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

prebid / Prebid.js / 17956843100

23 Sep 2025 07:24PM UTC coverage: 96.236% (-0.004%) from 96.24%
17956843100

push

github

web-flow
FWSSP Adapter: update schain logic (#13925)

39884 of 49035 branches covered (81.34%)

24 of 24 new or added lines in 2 files covered. (100.0%)

126 existing lines in 11 files now uncovered.

198197 of 205949 relevant lines covered (96.24%)

124.84 hits per line

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

82.35
/modules/datablocksBidAdapter.js
1
import {deepAccess, getWinDimensions, getWindowTop, isEmpty, isGptPubadsDefined} from '../src/utils.js';
1✔
2
import {registerBidder} from '../src/adapters/bidderFactory.js';
3
import {config} from '../src/config.js';
4
import {BANNER, NATIVE} from '../src/mediaTypes.js';
5
import {getStorageManager} from '../src/storageManager.js';
6
import {ajax} from '../src/ajax.js';
7
import {convertOrtbRequestToProprietaryNative} from '../src/native.js';
8
import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
9
import {getExtraWinDimensions} from '../libraries/extraWinDimensions/extraWinDimensions.js';
10

11
export const storage = getStorageManager({bidderCode: 'datablocks'});
1✔
12

13
const NATIVE_ID_MAP = {};
1✔
14
const NATIVE_PARAMS = {
1✔
15
  title: {
16
    id: 1,
17
    name: 'title'
18
  },
19
  icon: {
20
    id: 2,
21
    type: 1,
22
    name: 'img'
23
  },
24
  image: {
25
    id: 3,
26
    type: 3,
27
    name: 'img'
28
  },
29
  body: {
30
    id: 4,
31
    name: 'data',
32
    type: 2
33
  },
34
  sponsoredBy: {
35
    id: 5,
36
    name: 'data',
37
    type: 1
38
  },
39
  cta: {
40
    id: 6,
41
    type: 12,
42
    name: 'data'
43
  },
44
  body2: {
45
    id: 7,
46
    name: 'data',
47
    type: 10
48
  },
49
  rating: {
50
    id: 8,
51
    name: 'data',
52
    type: 3
53
  },
54
  likes: {
55
    id: 9,
56
    name: 'data',
57
    type: 4
58
  },
59
  downloads: {
60
    id: 10,
61
    name: 'data',
62
    type: 5
63
  },
64
  displayUrl: {
65
    id: 11,
66
    name: 'data',
67
    type: 11
68
  },
69
  price: {
70
    id: 12,
71
    name: 'data',
72
    type: 6
73
  },
74
  salePrice: {
75
    id: 13,
76
    name: 'data',
77
    type: 7
78
  },
79
  address: {
80
    id: 14,
81
    name: 'data',
82
    type: 9
83
  },
84
  phone: {
85
    id: 15,
86
    name: 'data',
87
    type: 8
88
  }
89
};
90

91
Object.keys(NATIVE_PARAMS).forEach((key) => {
1✔
92
  NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key;
15✔
93
});
94

95
// DEFINE THE PREBID BIDDER SPEC
96
export const spec = {
1✔
97
  supportedMediaTypes: [BANNER, NATIVE],
98
  code: 'datablocks',
99

100
  // DATABLOCKS SCOPED OBJECT
101
  db_obj: {metrics_host: 'prebid.dblks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0},
102

103
  // STORE THE DATABLOCKS BUYERID IN STORAGE
104
  store_dbid: function(dbid) {
105
    let stored = false;
2✔
106

107
    // CREATE 1 YEAR EXPIRY DATE
108
    const d = new Date();
2✔
109
    d.setTime(Date.now() + (365 * 24 * 60 * 60 * 1000));
2✔
110

111
    // TRY TO STORE IN COOKIE
112
    if (storage.cookiesAreEnabled) {
2✔
113
      storage.setCookie('_db_dbid', dbid, d.toUTCString(), 'None', null);
2✔
114
      stored = true;
2✔
115
    }
116

117
    // TRY TO STORE IN LOCAL STORAGE
118
    if (storage.localStorageIsEnabled) {
2✔
119
      storage.setDataInLocalStorage('_db_dbid', dbid);
2✔
120
      stored = true;
2✔
121
    }
122

123
    return stored;
2✔
124
  },
125

126
  // FETCH DATABLOCKS BUYERID FROM STORAGE
127
  get_dbid: function() {
128
    let dbId = '';
3✔
129
    if (storage.cookiesAreEnabled) {
3✔
130
      dbId = storage.getCookie('_db_dbid') || '';
3✔
131
    }
132

133
    if (!dbId && storage.localStorageIsEnabled) {
3✔
134
      dbId = storage.getDataFromLocalStorage('_db_dbid') || '';
3✔
135
    }
136
    return dbId;
3✔
137
  },
138

139
  // STORE SYNCS IN STORAGE
140
  store_syncs: function(syncs) {
141
    if (storage.localStorageIsEnabled) {
1✔
142
      const syncObj = {};
1✔
143
      syncs.forEach(sync => {
1✔
144
        syncObj[sync.id] = sync.uid;
1✔
145
      });
146

147
      // FETCH EXISTING SYNCS AND MERGE NEW INTO STORAGE
148
      const storedSyncs = this.get_syncs();
1✔
149
      storage.setDataInLocalStorage('_db_syncs', JSON.stringify(Object.assign(storedSyncs, syncObj)));
1✔
150

151
      return true;
1✔
152
    }
153
  },
154

155
  // GET SYNCS FROM STORAGE
156
  get_syncs: function() {
157
    if (storage.localStorageIsEnabled) {
3!
158
      const syncData = storage.getDataFromLocalStorage('_db_syncs');
3✔
159
      if (syncData) {
3!
UNCOV
160
        return JSON.parse(syncData);
×
161
      } else {
162
        return {};
3✔
163
      }
164
    } else {
UNCOV
165
      return {};
×
166
    }
167
  },
168

169
  // ADD METRIC DATA TO THE METRICS RESPONSE QUEUE
170
  queue_metric: function(metric) {
UNCOV
171
    if (typeof metric === 'object') {
×
172
      // PUT METRICS IN THE QUEUE
UNCOV
173
      this.db_obj.metrics.push(metric);
×
174

175
      // RESET PREVIOUS TIMER
176
      if (this.db_obj.metrics_timer) {
×
UNCOV
177
        clearTimeout(this.db_obj.metrics_timer);
×
178
      }
179

180
      // SETUP THE TIMER TO FIRE BACK THE DATA
181
      const scope = this;
×
182
      this.db_obj.metrics_timer = setTimeout(function() {
×
UNCOV
183
        scope.send_metrics();
×
184
      }, this.db_obj.metrics_queue_time);
185

UNCOV
186
      return true;
×
187
    } else {
UNCOV
188
      return false;
×
189
    }
190
  },
191

192
  // POST CONSOLIDATED METRICS BACK TO SERVER
193
  send_metrics: function() {
194
    // POST TO SERVER
UNCOV
195
    ajax(`https://${this.db_obj.metrics_host}/a/pb/`, null, JSON.stringify(this.db_obj.metrics), {method: 'POST', withCredentials: true});
×
196

197
    // RESET THE QUEUE OF METRIC DATA
UNCOV
198
    this.db_obj.metrics = [];
×
199

UNCOV
200
    return true;
×
201
  },
202

203
  // GET BASIC CLIENT INFORMATION
204
  get_client_info: function () {
205
    const botTest = new BotClientTests();
2✔
206
    const win = getWindowTop();
2✔
207
    const windowDimensions = getWinDimensions();
2✔
208
    const extraDims = getExtraWinDimensions();
2✔
209
    return {
2✔
210
      'wiw': windowDimensions.innerWidth,
211
      'wih': windowDimensions.innerHeight,
212
      'saw': extraDims.screen.availWidth,
213
      'sah': extraDims.screen.availHeight,
214
      'scd': extraDims.screen.colorDepth,
215
      'sw': windowDimensions.screen.width,
216
      'sh': windowDimensions.screen.height,
217
      'whl': win.history.length,
218
      'wxo': win.pageXOffset,
219
      'wyo': win.pageYOffset,
220
      'wpr': win.devicePixelRatio,
221
      'is_bot': botTest.doTests(),
222
      'is_hid': win.document.hidden,
223
      'vs': win.document.visibilityState
224
    };
225
  },
226

227
  // LISTEN FOR GPT VIEWABILITY EVENTS
228
  get_viewability: function(bid) {
229
    // ONLY RUN ONCE IF PUBLISHER HAS OPTED IN
230
    if (!this.db_obj.vis_optout && !this.db_obj.vis_run) {
2✔
231
      this.db_obj.vis_run = true;
1✔
232

233
      // ADD GPT EVENT LISTENERS
234
      const scope = this;
1✔
235
      if (isGptPubadsDefined()) {
1✔
236
        if (typeof window['googletag'].pubads().addEventListener === 'function') {
1✔
237
          // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781
238
          window['googletag'].pubads().addEventListener('impressionViewable', function(event) {
1✔
UNCOV
239
            scope.queue_metric({type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()});
×
240
          });
241
          window['googletag'].pubads().addEventListener('slotRenderEnded', function(event) {
1✔
UNCOV
242
            scope.queue_metric({type: 'slot_render', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()});
×
243
          })
244
        }
245
      }
246
    }
247
  },
248

249
  // VALIDATE THE BID REQUEST
250
  isBidRequestValid: function(bid) {
251
    // SET GLOBAL VARS FROM BIDDER CONFIG
252
    this.db_obj.source_id = bid.params.source_id;
3✔
253
    if (bid.params.vis_optout) {
3✔
254
      this.db_obj.vis_optout = true;
1✔
255
    }
256

257
    return !!(bid.params.source_id && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native));
3!
258
  },
259

260
  // GENERATE THE RTB REQUEST
261
  buildRequests: function(validRequests, bidderRequest) {
262
    // convert Native ORTB definition to old-style prebid native definition
263
    validRequests = convertOrtbRequestToProprietaryNative(validRequests);
2✔
264

265
    // RETURN EMPTY IF THERE ARE NO VALID REQUESTS
266
    if (!validRequests.length) {
2✔
267
      return [];
1✔
268
    }
269

270
    // CONVERT PREBID NATIVE REQUEST OBJ INTO RTB OBJ
271
    function createNativeRequest(bid) {
272
      const assets = [];
1✔
273
      if (bid.nativeParams) {
1✔
274
        Object.keys(bid.nativeParams).forEach((key) => {
1✔
275
          if (NATIVE_PARAMS[key]) {
3✔
276
            const {name, type, id} = NATIVE_PARAMS[key];
3✔
277
            const assetObj = type ? {type} : {};
3✔
278
            let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key];
3✔
279
            if (len) {
3!
UNCOV
280
              assetObj.len = len;
×
281
            }
282
            if (aRatios && aRatios[0]) {
3!
283
              aRatios = aRatios[0];
×
284
              const wmin = aRatios.min_width || 0;
×
285
              const hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0;
×
UNCOV
286
              assetObj.wmin = wmin;
×
UNCOV
287
              assetObj.hmin = hmin;
×
288
            }
289
            if (sizes && sizes.length) {
3✔
290
              sizes = [].concat(...sizes);
1✔
291
              assetObj.w = sizes[0];
1✔
292
              assetObj.h = sizes[1];
1✔
293
            }
294
            const asset = {required: required ? 1 : 0, id};
3!
295
            asset[name] = assetObj;
3✔
296
            assets.push(asset);
3✔
297
          }
298
        });
299
      }
300
      return {
1✔
301
        ver: '1.2',
302
        request: {
303
          assets: assets,
304
          context: 1,
305
          plcmttype: 1,
306
          ver: '1.2'
307
        }
308
      }
309
    }
310
    const imps = [];
1✔
311
    // ITERATE THE VALID REQUESTS AND GENERATE IMP OBJECT
312
    validRequests.forEach(bidRequest => {
1✔
313
      // BUILD THE IMP OBJECT
314
      const imp = {
3✔
315
        id: bidRequest.bidId,
316
        tagid: bidRequest.params.tagid || bidRequest.adUnitCode,
6✔
317
        placement_id: bidRequest.params.placement_id || 0,
6✔
318
        secure: window.location.protocol === 'https:',
319
        ortb2: deepAccess(bidRequest, `ortb2Imp`) || {},
6✔
320
        floor: {}
321
      }
322

323
      // CHECK FOR FLOORS
324
      if (typeof bidRequest.getFloor === 'function') {
3!
UNCOV
325
        imp.floor = bidRequest.getFloor({
×
326
          currency: 'USD',
327
          mediaType: '*',
328
          size: '*'
329
        });
330
      }
331

332
      // BUILD THE SIZES
333
      if (deepAccess(bidRequest, `mediaTypes.banner`)) {
3✔
334
        const sizes = getAdUnitSizes(bidRequest);
2✔
335
        if (sizes.length) {
2✔
336
          imp.banner = {
2✔
337
            w: sizes[0][0],
338
            h: sizes[0][1],
339
            format: sizes.map(size => ({ w: size[0], h: size[1] }))
2✔
340
          };
341

342
          // ADD TO THE LIST OF IMP REQUESTS
343
          imps.push(imp);
2✔
344
        }
345
      } else if (deepAccess(bidRequest, `mediaTypes.native`)) {
1✔
346
        // ADD TO THE LIST OF IMP REQUESTS
347
        imp.native = createNativeRequest(bidRequest);
1✔
348
        imps.push(imp);
1✔
349
      }
350
    });
351

352
    // RETURN EMPTY IF THERE WERE NO PROPER ADUNIT REQUESTS TO BE MADE
353
    if (!imps.length) {
1!
UNCOV
354
      return [];
×
355
    }
356

357
    // GENERATE SITE OBJECT
358
    const site = {
1✔
359
      domain: window.location.host,
360
      // TODO: is 'page' the right value here?
361
      page: bidderRequest.refererInfo.page,
362
      schain: validRequests[0]?.ortb2?.source?.ext?.schain || {},
2✔
363
      ext: {
364
        p_domain: bidderRequest.refererInfo.domain,
365
        rt: bidderRequest.refererInfo.reachedTop,
366
        frames: bidderRequest.refererInfo.numIframes,
367
        stack: bidderRequest.refererInfo.stack,
368
        timeout: config.getConfig('bidderTimeout')
369
      },
370
    };
371

372
    // ADD REF URL IF FOUND
373
    if (self === top && document.referrer) {
1!
UNCOV
374
      site.ref = document.referrer;
×
375
    }
376

377
    // ADD META KEYWORDS IF FOUND
378
    const keywords = document.getElementsByTagName('meta')['keywords'];
1✔
379
    if (keywords && keywords.content) {
1!
UNCOV
380
      site.keywords = keywords.content;
×
381
    }
382

383
    // GENERATE DEVICE OBJECT
384
    const device = {
1✔
385
      ip: 'peer',
386
      ua: window.navigator.userAgent,
387
      js: 1,
388
      language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en',
2!
389
      buyerid: this.get_dbid() || 0,
2✔
390
      ext: {
391
        pb_eids: validRequests[0].userIdAsEids || {},
2✔
392
        syncs: this.get_syncs() || {},
1!
393
        coppa: config.getConfig('coppa') || 0,
2✔
394
        gdpr: bidderRequest.gdprConsent || {},
2✔
395
        usp: bidderRequest.uspConsent || {},
2✔
396
        client_info: this.get_client_info(),
397
        ortb2: bidderRequest.ortb2 || {}
2✔
398
      }
399
    };
400

401
    const sourceId = validRequests[0].params.source_id || 0;
1!
402
    const host = validRequests[0].params.host || 'prebid.dblks.net';
1!
403

404
    // RETURN WITH THE REQUEST AND PAYLOAD
405
    return {
1✔
406
      method: 'POST',
407
      url: `https://${host}/openrtb/?sid=${sourceId}`,
408
      data: {
409
        id: bidderRequest.bidderRequestId,
410
        imp: imps,
411
        site: site,
412
        device: device
413
      },
414
      options: {
415
        withCredentials: true
416
      }
417
    };
418
  },
419

420
  // INITIATE USER SYNCING
421
  getUserSyncs: function(options, rtbResponse, gdprConsent) {
422
    const syncs = [];
1✔
423
    const bidResponse = rtbResponse?.[0]?.body ?? null;
1!
424
    const scope = this;
1✔
425

426
    // LISTEN FOR SYNC DATA FROM IFRAME TYPE SYNC
427
    window.addEventListener('message', function (event) {
1✔
428
      if (event.data.sentinel && event.data.sentinel === 'dblks_syncData') {
2!
429
        // STORE FOUND SYNCS
UNCOV
430
        if (event.data.syncs) {
×
UNCOV
431
          scope.store_syncs(event.data.syncs);
×
432
        }
433
      }
434
    });
435

436
    // POPULATE GDPR INFORMATION
437
    const gdprData = {
1✔
438
      gdpr: 0,
439
      gdprConsent: ''
440
    }
441
    if (typeof gdprConsent === 'object') {
1✔
442
      if (typeof gdprConsent.gdprApplies === 'boolean') {
1!
443
        gdprData.gdpr = Number(gdprConsent.gdprApplies);
1✔
444
        gdprData.gdprConsent = gdprConsent.consentString;
1✔
445
      } else {
UNCOV
446
        gdprData.gdprConsent = gdprConsent.consentString;
×
447
      }
448
    }
449

450
    // EXTRACT BUYERID COOKIE VALUE FROM BID RESPONSE AND PUT INTO STORAGE
451
    let dbBuyerId = this.get_dbid() || '';
1✔
452
    if (bidResponse.ext && bidResponse.ext.buyerid) {
1✔
453
      dbBuyerId = bidResponse.ext.buyerid;
1✔
454
      this.store_dbid(dbBuyerId);
1✔
455
    }
456

457
    // EXTRACT USERSYNCS FROM BID RESPONSE
458
    if (bidResponse.ext && bidResponse.ext.syncs) {
1✔
459
      bidResponse.ext.syncs.forEach(sync => {
1✔
460
        if (checkValid(sync)) {
2✔
461
          syncs.push(addParams(sync));
2✔
462
        }
463
      })
464
    }
465

466
    // APPEND PARAMS TO SYNC URL
467
    function addParams(sync) {
468
      // PARSE THE URL
469
      try {
2✔
470
        const url = new URL(sync.url);
2✔
471
        const urlParams = {};
2✔
472
        for (const [key, value] of url.searchParams.entries()) {
2✔
UNCOV
473
          urlParams[key] = value;
×
474
        };
475

476
        // APPLY EXTRA VARS
477
        urlParams.gdpr = gdprData.gdpr;
2✔
478
        urlParams.gdprConsent = gdprData.gdprConsent;
2✔
479
        urlParams.bidid = bidResponse.bidid;
2✔
480
        urlParams.id = bidResponse.id;
2✔
481
        urlParams.uid = dbBuyerId;
2✔
482

483
        // REBUILD URL
484
        sync.url = `${url.origin}${url.pathname}?${Object.keys(urlParams).map(key => key + '=' + encodeURIComponent(urlParams[key])).join('&')}`;
10✔
485
      } catch (e) {};
486

487
      // RETURN THE REBUILT URL
488
      return sync;
2✔
489
    }
490

491
    // ENSURE THAT THE SYNC TYPE IS VALID AND HAS PERMISSION
492
    function checkValid(sync) {
493
      if (!sync.type || !sync.url) {
2!
UNCOV
494
        return false;
×
495
      }
496
      switch (sync.type) {
2!
497
        case 'iframe':
498
          return options.iframeEnabled;
1✔
499
        case 'image':
500
          return options.pixelEnabled;
1✔
501
        default:
UNCOV
502
          return false;
×
503
      }
504
    }
505
    return syncs;
1✔
506
  },
507

508
  // DATABLOCKS WON THE AUCTION - REPORT SUCCESS
509
  onBidWon: function(bid) {
510
    this.queue_metric({type: 'bid_won', source_id: bid.params[0].source_id, req_id: bid.requestId, slot_id: bid.adUnitCode, auction_id: bid.auctionId, size: bid.size, cpm: bid.cpm, pb: bid.adserverTargeting.hb_pb, rt: bid.timeToRespond, ttl: bid.ttl});
1✔
511
  },
512

513
  // TARGETING HAS BEEN SET
514
  onSetTargeting: function(bid) {
515
    // LISTEN FOR VIEWABILITY EVENTS
516
    this.get_viewability(bid);
1✔
517
  },
518

519
  // PARSE THE RTB RESPONSE AND RETURN FINAL RESULTS
520
  interpretResponse: function(rtbResponse, bidRequest) {
521
    // CONVERT NATIVE RTB RESPONSE INTO PREBID RESPONSE
522
    function parseNative(native) {
523
      const {assets, link, imptrackers, jstracker} = native;
1✔
524
      const result = {
1✔
525
        clickUrl: link.url,
526
        clickTrackers: link.clicktrackers || [],
2✔
527
        impressionTrackers: imptrackers || [],
1!
528
        javascriptTrackers: jstracker ? [jstracker] : []
1!
529
      };
530

531
      (assets || []).forEach((asset) => {
1!
532
        const {id, img, data, title} = asset;
3✔
533
        const key = NATIVE_ID_MAP[id];
3✔
534
        if (key) {
3✔
535
          if (!isEmpty(title)) {
3✔
536
            result.title = title.text
1✔
537
          } else if (!isEmpty(img)) {
2✔
538
            result[key] = {
1✔
539
              url: img.url,
540
              height: img.h,
541
              width: img.w
542
            }
543
          } else if (!isEmpty(data)) {
1✔
544
            result[key] = data.value;
1✔
545
          }
546
        }
547
      });
548

549
      return result;
1✔
550
    }
551

552
    const bids = [];
1✔
553
    const resBids = deepAccess(rtbResponse, 'body.seatbid') || [];
1!
554
    resBids.forEach(bid => {
1✔
555
      const resultItem = {requestId: bid.id, cpm: bid.price, creativeId: bid.crid, currency: bid.currency || 'USD', netRevenue: true, ttl: bid.ttl || 360, meta: {advertiserDomains: bid.adomain}};
3✔
556

557
      const mediaType = deepAccess(bid, 'ext.mtype') || '';
3!
558
      switch (mediaType) {
3!
559
        case 'banner':
560
          bids.push(Object.assign({}, resultItem, {mediaType: BANNER, width: bid.w, height: bid.h, ad: bid.adm}));
2✔
561
          break;
2✔
562

563
        case 'native':
564
          const nativeResult = JSON.parse(bid.adm);
1✔
565
          bids.push(Object.assign({}, resultItem, {mediaType: NATIVE, native: parseNative(nativeResult.native)}));
1✔
566
          break;
1✔
567

568
        default:
UNCOV
569
          break;
×
570
      }
571
    })
572

573
    return bids;
1✔
574
  }
575
};
576

577
// DETECT BOTS
578
export class BotClientTests {
579
  constructor() {
580
    this.tests = {
3✔
581
      headless_chrome: function() {
582
        if (self.navigator) {
3✔
583
          if (self.navigator.webdriver) {
3!
UNCOV
584
            return true;
×
585
          }
586
        }
587

588
        return false;
3✔
589
      },
590

591
      selenium: function () {
592
        let response = false;
3✔
593

594
        if (window && document) {
3✔
595
          const results = [
3✔
596
            'webdriver' in window,
597
            '_Selenium_IDE_Recorder' in window,
598
            'callSelenium' in window,
599
            '_selenium' in window,
600
            '__webdriver_script_fn' in document,
601
            '__driver_evaluate' in document,
602
            '__webdriver_evaluate' in document,
603
            '__selenium_evaluate' in document,
604
            '__fxdriver_evaluate' in document,
605
            '__driver_unwrapped' in document,
606
            '__webdriver_unwrapped' in document,
607
            '__selenium_unwrapped' in document,
608
            '__fxdriver_unwrapped' in document,
609
            '__webdriver_script_func' in document,
610
            document.documentElement.getAttribute('selenium') !== null,
611
            document.documentElement.getAttribute('webdriver') !== null,
612
            document.documentElement.getAttribute('driver') !== null
613
          ];
614

615
          results.forEach(result => {
3✔
616
            if (result === true) {
51!
UNCOV
617
              response = true;
×
618
            }
619
          })
620
        }
621

622
        return response;
3✔
623
      },
624
    }
625
  }
626
  doTests() {
627
    let response = false;
3✔
628
    for (const i of Object.keys(this.tests)) {
3✔
629
      if (this.tests[i]() === true) {
6!
UNCOV
630
        response = true;
×
631
      }
632
    }
633
    return response;
3✔
634
  }
635
}
636

637
// INIT OUR BIDDER WITH PREBID
638
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