• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / #305

27 Jun 2025 12:10PM UTC coverage: 90.409% (-0.01%) from 90.422%
#305

push

travis-ci

prebidjs-release
Prebid 9.52.0 release

43449 of 54513 branches covered (79.7%)

64185 of 70994 relevant lines covered (90.41%)

174.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.95
/modules/cwireBidAdapter.js
1
import { registerBidder } from "../src/adapters/bidderFactory.js";
1✔
2
import { getStorageManager } from "../src/storageManager.js";
3
import { BANNER } from "../src/mediaTypes.js";
4
import {
5
  generateUUID,
6
  getParameterByName,
7
  isNumber,
8
  logError,
9
  logInfo,
10
} from "../src/utils.js";
11
import { getBoundingClientRect } from "../libraries/boundingClientRect/boundingClientRect.js";
12
import { hasPurpose1Consent } from "../src/utils/gdpr.js";
13
import { sendBeacon } from "../src/ajax.js";
14
import { isAutoplayEnabled } from "../libraries/autoplayDetection/autoplay.js";
15

16
/**
17
 * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
18
 * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
19
 * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse
20
 */
21

22
// ------------------------------------
23
const BIDDER_CODE = "cwire";
1✔
24
const CWID_KEY = "cw_cwid";
1✔
25

26
export const BID_ENDPOINT = "https://prebid.cwi.re/v1/bid";
1✔
27
export const EVENT_ENDPOINT = "https://prebid.cwi.re/v1/event";
1✔
28
export const GVL_ID = 1081;
1✔
29

30
/**
31
 * Allows limiting ad impressions per site render. Unique per prebid instance ID.
32
 */
33
export const pageViewId = generateUUID();
1✔
34

35
export const storage = getStorageManager({ bidderCode: BIDDER_CODE });
1✔
36

37
/**
38
 * Retrieve dimensions and CSS max height/width from a given slot and attach the properties to the bidRequest.
39
 * @param bid
40
 * @returns {*&{cwExt: {dimensions: {width: number, height: number}, style: {maxWidth: number, maxHeight: number}}}}
41
 */
42
function slotDimensions(bid) {
43
  let adUnitCode = bid.adUnitCode;
14✔
44
  let slotEl = document.getElementById(adUnitCode);
14✔
45

46
  if (slotEl) {
14✔
47
    logInfo(`Slot element found: ${adUnitCode}`);
3✔
48
    const { width: slotW, height: slotH } = getBoundingClientRect(slotEl);
3✔
49
    const cssMaxW = slotEl.style?.maxWidth;
3✔
50
    const cssMaxH = slotEl.style?.maxHeight;
3✔
51
    logInfo(`Slot dimensions (w/h): ${slotW} / ${slotH}`);
3✔
52
    logInfo(`Slot Styles (maxW/maxH): ${cssMaxW} / ${cssMaxH}`);
3✔
53

54
    bid = {
3✔
55
      ...bid,
56
      cwExt: {
57
        dimensions: {
58
          width: slotW,
59
          height: slotH,
60
        },
61
        style: {
62
          ...(cssMaxW && {
4✔
63
            maxWidth: cssMaxW,
64
          }),
65
          ...(cssMaxH && {
4✔
66
            maxHeight: cssMaxH,
67
          }),
68
        },
69
      },
70
    };
71
  }
72
  return bid;
14✔
73
}
74

75
/**
76
 * Extracts feature flags from a comma-separated url parameter `cwfeatures`.
77
 *
78
 * @returns *[]
79
 */
80
function getFeatureFlags() {
81
  let ffParam = getParameterByName("cwfeatures");
14✔
82
  if (ffParam) {
14✔
83
    return ffParam.split(",");
4✔
84
  }
85
  return [];
10✔
86
}
87

88
function getRefGroups() {
89
  const groups = getParameterByName("cwgroups");
14✔
90
  if (groups) {
14✔
91
    return groups.split(",");
4✔
92
  }
93
  return [];
10✔
94
}
95

96
function getBidFloor(bid) {
97
  if (typeof bid.getFloor !== "function") {
14✔
98
    return {};
13✔
99
  }
100

101
  let floor = bid.getFloor({
1✔
102
    currency: "USD",
103
    mediaType: "*",
104
    size: "*",
105
  });
106

107
  return floor;
1✔
108
}
109

110
/**
111
 * Returns the downlink speed of the connection in Mbps or an empty string if not available.
112
 */
113
function getConnectionDownLink(nav) {
114
  return nav && nav.connection && nav.connection.downlink >= 0
14!
115
    ? nav.connection.downlink.toString()
116
    : "";
117
}
118

119
/**
120
 * Reads the CWID from local storage.
121
 */
122
function getCwid() {
123
  return storage.localStorageIsEnabled()
14✔
124
    ? storage.getDataFromLocalStorage(CWID_KEY)
125
    : null;
126
}
127

128
function hasCwid() {
129
  return (
1✔
130
    storage.localStorageIsEnabled() && storage.getDataFromLocalStorage(CWID_KEY)
2✔
131
  );
132
}
133

134
/**
135
 * Store the CWID to local storage.
136
 */
137
function updateCwid(cwid) {
138
  if (storage.localStorageIsEnabled()) {
×
139
    storage.setDataInLocalStorage(CWID_KEY, cwid);
×
140
  } else {
141
    logInfo(`Could not set CWID ${cwid} in localstorage`);
×
142
  }
143
}
144

145
/**
146
 * Extract and collect cwire specific extensions.
147
 */
148
function getCwExtension() {
149
  const cwId = getCwid();
14✔
150
  const cwCreative = getParameterByName("cwcreative");
14✔
151
  const cwGroups = getRefGroups();
14✔
152
  const cwFeatures = getFeatureFlags();
14✔
153
  // Enable debug flag by passing ?cwdebug=true as url parameter.
154
  // Note: pbjs_debug=true enables it on prebid level
155
  // More info: https://docs.prebid.org/troubleshooting/troubleshooting-guide.html#turn-on-prebidjs-debug-messages
156
  const debug = getParameterByName("cwdebug");
14✔
157

158
  return {
14✔
159
    ...(cwId && {
21✔
160
      cwid: cwId,
161
    }),
162
    ...(cwGroups.length > 0 && {
18✔
163
      refgroups: cwGroups,
164
    }),
165
    ...(cwFeatures.length > 0 && {
18✔
166
      featureFlags: cwFeatures,
167
    }),
168
    ...(cwCreative && {
18✔
169
      cwcreative: cwCreative,
170
    }),
171
    ...(debug && {
18✔
172
      debug: true,
173
    }),
174
  };
175
}
176

177
export const spec = {
1✔
178
  code: BIDDER_CODE,
179
  gvlid: GVL_ID,
180
  supportedMediaTypes: [BANNER],
181

182
  /**
183
   * Determines whether the given bid request is valid.
184
   *
185
   * @param {BidRequest} bid The bid params to validate.
186
   * @return boolean True if this is a valid bid, and false otherwise.
187
   */
188
  isBidRequestValid: function (bid) {
189
    if (!bid.params?.domainId || !isNumber(bid.params.domainId)) {
3!
190
      logError("domainId not provided or not a number");
3✔
191
      if (!bid.params?.placementId || !isNumber(bid.params.placementId)) {
3✔
192
        logError("placementId not provided or not a number");
1✔
193
        return false;
1✔
194
      }
195

196
      if (!bid.params?.pageId || !isNumber(bid.params.pageId)) {
2!
197
        logError("pageId not provided or not a number");
×
198
        return false;
×
199
      }
200
      return true;
2✔
201
    }
202
    return true;
×
203
  },
204

205
  /**
206
   * Make a server request from the list of BidRequests.
207
   *
208
   * @param {validBidRequests[]} validBidRequests An array of bids.
209
   * @return ServerRequest Info describing the request to the server.
210
   */
211
  buildRequests: function (validBidRequests, bidderRequest) {
212
    // There are more fields on the refererInfo object
213
    let referrer = bidderRequest?.refererInfo?.page;
14✔
214

215
    // process bid requests
216
    let processed = validBidRequests
14✔
217
      .map((bid) => slotDimensions(bid))
14✔
218
      .map((bid) => {
219
        const bidFloor = getBidFloor(bid);
14✔
220
        return {
14✔
221
          ...bid,
222
          params: {
223
            ...bid.params,
224
            floor: bidFloor,
225
          },
226
        };
227
      })
228
      .map((bid) => {
229
        const autoplayEnabled = isAutoplayEnabled();
14✔
230
        return {
14✔
231
          ...bid,
232
          params: {
233
            ...bid.params,
234
            autoplay: autoplayEnabled,
235
          },
236
        };
237
      })
238
      // Flattens the pageId, domainId and placement Id for backwards compatibility.
239
      .map((bid) => ({
14✔
240
        ...bid,
241
        pageId: bid.params?.pageId,
242
        domainId: bid.params?.domainId,
243
        placementId: bid.params?.placementId,
244
      }));
245

246
    const extensions = getCwExtension();
14✔
247
    const payload = {
14✔
248
      slots: processed,
249
      httpRef: referrer,
250
      // TODO: Verify whether the auctionId and the usage of pageViewId make sense.
251
      pageViewId: pageViewId,
252
      networkBandwidth: getConnectionDownLink(window.navigator),
253
      sdk: {
254
        version: "$prebid.version$",
255
      },
256
      ...extensions,
257
    };
258
    const payloadString = JSON.stringify(payload);
14✔
259
    return {
14✔
260
      method: "POST",
261
      url: BID_ENDPOINT,
262
      data: payloadString,
263
    };
264
  },
265
  /**
266
   * Unpack the response from the server into a list of bids.
267
   *
268
   * @param {ServerResponse} serverResponse A successful response from the server.
269
   * @return {Bid[]} An array of bids which were nested inside the server.
270
   */
271
  interpretResponse: function (serverResponse, bidRequest) {
272
    if (!hasCwid()) {
1!
273
      const cwid = serverResponse.body?.cwid;
×
274
      if (cwid) {
×
275
        updateCwid(cwid);
×
276
      }
277
    }
278

279
    // Rename `html` response property to `ad` as used by prebid.
280
    const bids = serverResponse.body?.bids.map(({ html, ...rest }) => ({
1✔
281
      ...rest,
282
      ad: html,
283
    }));
284
    return bids || [];
1!
285
  },
286

287
  onBidWon: function (bid) {
288
    logInfo(`Bid won.`);
×
289
    const event = {
×
290
      type: "BID_WON",
291
      payload: {
292
        bid: bid,
293
      },
294
    };
295
    sendBeacon(EVENT_ENDPOINT, JSON.stringify(event));
×
296
  },
297

298
  onBidderError: function (error, bidderRequest) {
299
    logInfo(`Bidder error: ${error}`);
×
300
    const event = {
×
301
      type: "BID_ERROR",
302
      payload: {
303
        error: error,
304
        bidderRequest: bidderRequest,
305
      },
306
    };
307
    sendBeacon(EVENT_ENDPOINT, JSON.stringify(event));
×
308
  },
309

310
  getUserSyncs: function (
311
    syncOptions,
312
    serverResponses,
313
    gdprConsent,
314
    uspConsent
315
  ) {
316
    logInfo(
4✔
317
      "Collecting user-syncs: ",
318
      JSON.stringify({ syncOptions, gdprConsent, uspConsent, serverResponses })
319
    );
320

321
    const syncs = [];
4✔
322
    if (hasPurpose1Consent(gdprConsent) && gdprConsent.consentString) {
4✔
323
      logInfo("GDPR purpose 1 consent was given, adding user-syncs");
3✔
324
      let type = syncOptions.pixelEnabled
3✔
325
        ? "image"
326
        : null ?? syncOptions.iframeEnabled
6✔
327
        ? "iframe"
328
        : null;
329
      if (type) {
3✔
330
        syncs.push({
2✔
331
          type: type,
332
          url: `https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID&gdpr=${
333
            gdprConsent.gdprApplies ? 1 : 0
2✔
334
          }&gdpr_consent=${gdprConsent.consentString}`,
335
        });
336
      }
337
    }
338
    logInfo("Collected user-syncs: ", JSON.stringify({ syncs }));
4✔
339
    return syncs;
4✔
340
  },
341
};
342
registerBidder(spec);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc