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

prebid / Prebid.js / 17551159456

08 Sep 2025 12:42PM UTC coverage: 96.247% (-0.006%) from 96.253%
17551159456

push

github

web-flow
fix double invoke (#13856)

39727 of 48861 branches covered (81.31%)

1 of 1 new or added line in 1 file covered. (100.0%)

172 existing lines in 29 files now uncovered.

197550 of 205253 relevant lines covered (96.25%)

124.84 hits per line

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

99.05
/test/spec/modules/programmaticXBidAdapter_spec.js
1
import {expect} from 'chai';
2
import {
3
  spec as adapter,
4
  createDomain,
5
  storage
6
} from 'modules/programmaticXBidAdapter';
7
import * as utils from 'src/utils.js';
8
import {version} from 'package.json';
9
import {useFakeTimers} from 'sinon';
10
import {BANNER, VIDEO} from '../../../src/mediaTypes.js'
11
import {config} from '../../../src/config.js';
12
import {
13
  hashCode,
14
  extractPID,
15
  extractCID,
16
  extractSubDomain,
17
  getStorageItem,
18
  setStorageItem,
19
  tryParseJSON,
20
  getUniqueDealId,
21
} from '../../../libraries/vidazooUtils/bidderUtils.js';
22
import {getGlobal} from '../../../src/prebidGlobal.js';
23

24
export const TEST_ID_SYSTEMS = ['criteoId', 'id5id', 'netId', 'tdid', 'pubProvidedId', 'intentIqId', 'liveIntentId'];
1✔
25

26
const SUB_DOMAIN = 'exchange';
1✔
27

28
const BID = {
1✔
29
  'bidId': '2d52001cabd527',
30
  'adUnitCode': 'div-gpt-ad-12345-0',
31
  'params': {
32
    'subDomain': SUB_DOMAIN,
33
    'cId': '59db6b3b4ffaa70004f45cdc',
34
    'pId': '59ac17c192832d0011283fe3',
35
    'bidFloor': 0.1,
36
    'ext': {
37
      'param1': 'loremipsum',
38
      'param2': 'dolorsitamet'
39
    },
40
    'placementId': 'testBanner'
41
  },
42
  'placementCode': 'div-gpt-ad-1460505748561-0',
43
  'sizes': [[300, 250], [300, 600]],
44
  'bidderRequestId': '1fdb5ff1b6eaa7',
45
  'bidRequestsCount': 4,
46
  'bidderRequestsCount': 3,
47
  'bidderWinsCount': 1,
48
  'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a',
49
  'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc',
50
  'mediaTypes': [BANNER],
51
  'ortb2Imp': {
52
    'ext': {
53
      'gpid': '0123456789',
54
      'tid': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf'
55
    }
56
  }
57
};
58

59
const VIDEO_BID = {
1✔
60
  'bidId': '2d52001cabd527',
61
  'adUnitCode': '63550ad1ff6642d368cba59dh5884270560',
62
  'bidderRequestId': '12a8ae9ada9c13',
63
  'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee',
64
  'bidRequestsCount': 4,
65
  'bidderRequestsCount': 3,
66
  'bidderWinsCount': 1,
67
  'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc',
68
  'params': {
69
    'subDomain': SUB_DOMAIN,
70
    'cId': '635509f7ff6642d368cb9837',
71
    'pId': '59ac17c192832d0011283fe3',
72
    'bidFloor': 0.1,
73
    'placementId': 'testBanner'
74
  },
75
  'sizes': [[545, 307]],
76
  'mediaTypes': {
77
    'video': {
78
      'playerSize': [[545, 307]],
79
      'context': 'instream',
80
      'mimes': [
81
        'video/mp4',
82
        'application/javascript'
83
      ],
84
      'protocols': [2, 3, 5, 6],
85
      'maxduration': 60,
86
      'minduration': 0,
87
      'startdelay': 0,
88
      'linearity': 1,
89
      'api': [2],
90
      'placement': 1
91
    }
92
  },
93
  'ortb2Imp': {
94
    'ext': {
95
      'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee'
96
    }
97
  }
98
}
99

100
const ORTB2_DEVICE = {
1✔
101
  sua: {
102
    'source': 2,
103
    'platform': {
104
      'brand': 'Android',
105
      'version': ['8', '0', '0']
106
    },
107
    'browsers': [
108
      {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']},
109
      {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']},
110
      {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']}
111
    ],
112
    'mobile': 1,
113
    'model': 'SM-G955U',
114
    'bitness': '64',
115
    'architecture': ''
116
  },
117
  w: 980,
118
  h: 1720,
119
  dnt: 0,
120
  ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1',
121
  language: 'en',
122
  devicetype: 1,
123
  make: 'Apple',
124
  model: 'iPhone 12 Pro Max',
125
  os: 'iOS',
126
  osv: '17.4',
127
  ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'},
128
};
129

130
const BIDDER_REQUEST = {
1✔
131
  'gdprConsent': {
132
    'consentString': 'consent_string',
133
    'gdprApplies': true
134
  },
135
  'gppString': 'gpp_string',
136
  'gppSid': [7],
137
  'uspConsent': 'consent_string',
138
  'refererInfo': {
139
    'page': 'https://www.greatsite.com',
140
    'ref': 'https://www.somereferrer.com'
141
  },
142
  'ortb2': {
143
    'site': {
144
      'content': {
145
        'language': 'en'
146
      }
147
    },
148
    'regs': {
149
      'gpp': 'gpp_string',
150
      'gpp_sid': [7],
151
      'coppa': 0
152
    },
153
    'device': ORTB2_DEVICE,
154
  }
155
};
156

157
const SERVER_RESPONSE = {
1✔
158
  body: {
159
    cid: 'testcid123',
160
    results: [{
161
      'ad': '<iframe>console.log("hello world")</iframe>',
162
      'price': 0.8,
163
      'creativeId': '12610997325162499419',
164
      'exp': 30,
165
      'width': 300,
166
      'height': 250,
167
      'advertiserDomains': ['securepubads.g.doubleclick.net'],
168
      'cookies': [{
169
        'src': 'https://sync.com',
170
        'type': 'iframe'
171
      }, {
172
        'src': 'https://sync.com',
173
        'type': 'img'
174
      }]
175
    }]
176
  }
177
};
178

179
const VIDEO_SERVER_RESPONSE = {
1✔
180
  body: {
181
    'cid': '635509f7ff6642d368cb9837',
182
    'results': [{
183
      'ad': '<VAST version=\"3.0\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"></VAST>',
184
      'advertiserDomains': ['programmaticx.ai'],
185
      'exp': 60,
186
      'width': 545,
187
      'height': 307,
188
      'mediaType': 'video',
189
      'creativeId': '12610997325162499419',
190
      'price': 2,
191
      'cookies': []
192
    }]
193
  }
194
};
195

196
const ORTB2_OBJ = {
1✔
197
  "device": ORTB2_DEVICE,
198
  "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]},
199
  "site": {"content": {"language": "en"}
200
  }
201
};
202

203
const REQUEST = {
1✔
204
  data: {
205
    width: 300,
206
    height: 250,
207
    bidId: '2d52001cabd527'
208
  }
209
};
210

211
function getTopWindowQueryParams() {
212
  try {
2✔
213
    const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true});
2✔
214
    return parsedUrl.search;
2✔
215
  } catch (e) {
UNCOV
216
    return '';
×
217
  }
218
}
219

220
describe('programmaticXBidAdapter', function () {
1✔
221
  before(() => config.resetConfig());
1✔
222
  after(() => config.resetConfig());
1✔
223

224
  describe('validtae spec', function () {
1✔
225
    it('exists and is a function', function () {
1✔
226
      expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function');
1✔
227
    });
228

229
    it('exists and is a function', function () {
1✔
230
      expect(adapter.buildRequests).to.exist.and.to.be.a('function');
1✔
231
    });
232

233
    it('exists and is a function', function () {
1✔
234
      expect(adapter.interpretResponse).to.exist.and.to.be.a('function');
1✔
235
    });
236

237
    it('exists and is a function', function () {
1✔
238
      expect(adapter.getUserSyncs).to.exist.and.to.be.a('function');
1✔
239
    });
240

241
    it('exists and is a string', function () {
1✔
242
      expect(adapter.code).to.exist.and.to.be.a('string');
1✔
243
    });
244

245
    it('exists and contains media types', function () {
1✔
246
      expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2);
1✔
247
      expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]);
1✔
248
    });
249
  });
250

251
  describe('validate bid requests', function () {
1✔
252
    it('should require cId', function () {
1✔
253
      const isValid = adapter.isBidRequestValid({
1✔
254
        params: {
255
          pId: 'pid'
256
        }
257
      });
258
      expect(isValid).to.be.false;
1✔
259
    });
260

261
    it('should require pId', function () {
1✔
262
      const isValid = adapter.isBidRequestValid({
1✔
263
        params: {
264
          cId: 'cid'
265
        }
266
      });
267
      expect(isValid).to.be.false;
1✔
268
    });
269

270
    it('should validate correctly', function () {
1✔
271
      const isValid = adapter.isBidRequestValid({
1✔
272
        params: {
273
          cId: 'cid',
274
          pId: 'pid'
275
        }
276
      });
277
      expect(isValid).to.be.true;
1✔
278
    });
279
  });
280

281
  describe('build requests', function () {
1✔
282
    let sandbox;
283
    before(function () {
1✔
284
      getGlobal().bidderSettings = {
1✔
285
        programmaticX: {
286
          storageAllowed: true
287
        }
288
      };
289
      sandbox = sinon.createSandbox();
1✔
290
      sandbox.stub(Date, 'now').returns(1000);
1✔
291
    });
292

293
    it('should build video request', function () {
1✔
294
      const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page);
1✔
295
      config.setConfig({
1✔
296
        bidderTimeout: 3000
297
      });
298
      const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST);
1✔
299
      expect(requests).to.have.length(1);
1✔
300
      expect(requests[0]).to.deep.equal({
1✔
301
        method: 'POST',
302
        url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`,
303
        data: {
304
          adUnitCode: '63550ad1ff6642d368cba59dh5884270560',
305
          bidFloor: 0.1,
306
          bidId: '2d52001cabd527',
307
          bidderVersion: adapter.version,
308
          bidderRequestId: '12a8ae9ada9c13',
309
          cb: 1000,
310
          gdpr: 1,
311
          gdprConsent: 'consent_string',
312
          usPrivacy: 'consent_string',
313
          gppString: 'gpp_string',
314
          gppSid: [7],
315
          prebidVersion: version,
316
          transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee',
317
          bidRequestsCount: 4,
318
          bidderRequestsCount: 3,
319
          bidderWinsCount: 1,
320
          bidderTimeout: 3000,
321
          publisherId: '59ac17c192832d0011283fe3',
322
          url: 'https%3A%2F%2Fwww.greatsite.com',
323
          referrer: 'https://www.somereferrer.com',
324
          res: `${window.top.screen.width}x${window.top.screen.height}`,
325
          schain: VIDEO_BID.schain,
326
          sizes: ['545x307'],
327
          sua: {
328
            'source': 2,
329
            'platform': {
330
              'brand': 'Android',
331
              'version': ['8', '0', '0']
332
            },
333
            'browsers': [
334
              {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']},
335
              {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']},
336
              {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']}
337
            ],
338
            'mobile': 1,
339
            'model': 'SM-G955U',
340
            'bitness': '64',
341
            'architecture': ''
342
          },
343
          device: ORTB2_DEVICE,
344
          uniqueDealId: `${hashUrl}_${Date.now().toString()}`,
345
          uqs: getTopWindowQueryParams(),
346
          mediaTypes: {
347
            video: {
348
              api: [2],
349
              context: 'instream',
350
              linearity: 1,
351
              maxduration: 60,
352
              mimes: [
353
                'video/mp4',
354
                'application/javascript'
355
              ],
356
              minduration: 0,
357
              placement: 1,
358
              playerSize: [[545, 307]],
359
              protocols: [2, 3, 5, 6],
360
              startdelay: 0
361
            }
362
          },
363
          gpid: '',
364
          cat: [],
365
          contentLang: 'en',
366
          contentData: [],
367
          isStorageAllowed: true,
368
          pagecat: [],
369
          ortb2Imp: VIDEO_BID.ortb2Imp,
370
          ortb2: ORTB2_OBJ,
371
          placementId: "testBanner",
372
          userData: [],
373
          coppa: 0
374
        }
375
      });
376
    });
377

378
    it('should build banner request for each size', function () {
1✔
379
      const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page);
1✔
380
      config.setConfig({
1✔
381
        bidderTimeout: 3000
382
      });
383
      const requests = adapter.buildRequests([BID], BIDDER_REQUEST);
1✔
384
      expect(requests).to.have.length(1);
1✔
385
      expect(requests[0]).to.deep.equal({
1✔
386
        method: 'POST',
387
        url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`,
388
        data: {
389
          gdprConsent: 'consent_string',
390
          gdpr: 1,
391
          gppString: 'gpp_string',
392
          gppSid: [7],
393
          usPrivacy: 'consent_string',
394
          transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf',
395
          bidRequestsCount: 4,
396
          bidderRequestsCount: 3,
397
          bidderWinsCount: 1,
398
          bidderTimeout: 3000,
399
          bidderRequestId: '1fdb5ff1b6eaa7',
400
          sizes: ['300x250', '300x600'],
401
          sua: {
402
            'source': 2,
403
            'platform': {
404
              'brand': 'Android',
405
              'version': ['8', '0', '0']
406
            },
407
            'browsers': [
408
              {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']},
409
              {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']},
410
              {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']}
411
            ],
412
            'mobile': 1,
413
            'model': 'SM-G955U',
414
            'bitness': '64',
415
            'architecture': ''
416
          },
417
          device: ORTB2_DEVICE,
418
          url: 'https%3A%2F%2Fwww.greatsite.com',
419
          referrer: 'https://www.somereferrer.com',
420
          cb: 1000,
421
          bidFloor: 0.1,
422
          bidId: '2d52001cabd527',
423
          adUnitCode: 'div-gpt-ad-12345-0',
424
          publisherId: '59ac17c192832d0011283fe3',
425
          uniqueDealId: `${hashUrl}_${Date.now().toString()}`,
426
          bidderVersion: adapter.version,
427
          prebidVersion: version,
428
          schain: BID.schain,
429
          res: `${window.top.screen.width}x${window.top.screen.height}`,
430
          mediaTypes: [BANNER],
431
          gpid: '0123456789',
432
          uqs: getTopWindowQueryParams(),
433
          'ext.param1': 'loremipsum',
434
          'ext.param2': 'dolorsitamet',
435
          cat: [],
436
          contentLang: 'en',
437
          contentData: [],
438
          isStorageAllowed: true,
439
          pagecat: [],
440
          ortb2Imp: BID.ortb2Imp,
441
          ortb2: ORTB2_OBJ,
442
          placementId: "testBanner",
443
          userData: [],
444
          coppa: 0
445
        }
446
      });
447
    });
448

449
    after(function () {
1✔
450
      getGlobal().bidderSettings = {};
1✔
451
      sandbox.restore();
1✔
452
    });
453
  });
454
  describe('getUserSyncs', function () {
1✔
455
    it('should have valid user sync with iframeEnabled', function () {
1✔
456
      const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]);
1✔
457

458
      expect(result).to.deep.equal([{
1✔
459
        type: 'iframe',
460
        url: 'https://sync.programmaticx.ai/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0'
461
      }]);
462
    });
463

464
    it('should have valid user sync with cid on response', function () {
1✔
465
      const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]);
1✔
466
      expect(result).to.deep.equal([{
1✔
467
        type: 'iframe',
468
        url: 'https://sync.programmaticx.ai/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0'
469
      }]);
470
    });
471

472
    it('should have valid user sync with pixelEnabled', function () {
1✔
473
      const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]);
1✔
474

475
      expect(result).to.deep.equal([{
1✔
476
        'url': 'https://sync.programmaticx.ai/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0',
477
        'type': 'image'
478
      }]);
479
    });
480

481
    it('should have valid user sync with coppa 1 on response', function () {
1✔
482
      config.setConfig({
1✔
483
        coppa: 1
484
      });
485
      const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]);
1✔
486
      expect(result).to.deep.equal([{
1✔
487
        type: 'iframe',
488
        url: 'https://sync.programmaticx.ai/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1'
489
      }]);
490
    });
491

492
    it('should generate url with consent data', function () {
1✔
493
      const gdprConsent = {
1✔
494
        gdprApplies: true,
495
        consentString: 'consent_string'
496
      };
497
      const uspConsent = 'usp_string';
1✔
498
      const gppConsent = {
1✔
499
        gppString: 'gpp_string',
500
        applicableSections: [7]
501
      }
502

503
      const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent);
1✔
504

505
      expect(result).to.deep.equal([{
1✔
506
        'url': 'https://sync.programmaticx.ai/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7',
507
        'type': 'image'
508
      }]);
509
    });
510
  });
511

512
  describe('interpret response', function () {
1✔
513
    it('should return empty array when there is no response', function () {
1✔
514
      const responses = adapter.interpretResponse(null);
1✔
515
      expect(responses).to.be.empty;
1✔
516
    });
517

518
    it('should return empty array when there is no ad', function () {
1✔
519
      const responses = adapter.interpretResponse({price: 1, ad: ''});
1✔
520
      expect(responses).to.be.empty;
1✔
521
    });
522

523
    it('should return empty array when there is no price', function () {
1✔
524
      const responses = adapter.interpretResponse({price: null, ad: 'great ad'});
1✔
525
      expect(responses).to.be.empty;
1✔
526
    });
527

528
    it('should return an array of interpreted banner responses', function () {
1✔
529
      const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST);
1✔
530
      expect(responses).to.have.length(1);
1✔
531
      expect(responses[0]).to.deep.equal({
1✔
532
        requestId: '2d52001cabd527',
533
        cpm: 0.8,
534
        width: 300,
535
        height: 250,
536
        creativeId: '12610997325162499419',
537
        currency: 'USD',
538
        netRevenue: true,
539
        ttl: 30,
540
        ad: '<iframe>console.log("hello world")</iframe>',
541
        meta: {
542
          advertiserDomains: ['securepubads.g.doubleclick.net']
543
        }
544
      });
545
    });
546

547
    it('should get meta from response metaData', function () {
1✔
548
      const serverResponse = utils.deepClone(SERVER_RESPONSE);
1✔
549
      serverResponse.body.results[0].metaData = {
1✔
550
        advertiserDomains: ['programmaticx.ai'],
551
        agencyName: 'Agency Name',
552
      };
553
      const responses = adapter.interpretResponse(serverResponse, REQUEST);
1✔
554
      expect(responses[0].meta).to.deep.equal({
1✔
555
        advertiserDomains: ['programmaticx.ai'],
556
        agencyName: 'Agency Name'
557
      });
558
    });
559

560
    it('should return an array of interpreted video responses', function () {
1✔
561
      const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST);
1✔
562
      expect(responses).to.have.length(1);
1✔
563
      expect(responses[0]).to.deep.equal({
1✔
564
        requestId: '2d52001cabd527',
565
        cpm: 2,
566
        width: 545,
567
        height: 307,
568
        mediaType: 'video',
569
        creativeId: '12610997325162499419',
570
        currency: 'USD',
571
        netRevenue: true,
572
        ttl: 60,
573
        vastXml: '<VAST version=\"3.0\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"></VAST>',
574
        meta: {
575
          advertiserDomains: ['programmaticx.ai']
576
        }
577
      });
578
    });
579

580
    it('should take default TTL', function () {
1✔
581
      const serverResponse = utils.deepClone(SERVER_RESPONSE);
1✔
582
      delete serverResponse.body.results[0].exp;
1✔
583
      const responses = adapter.interpretResponse(serverResponse, REQUEST);
1✔
584
      expect(responses).to.have.length(1);
1✔
585
      expect(responses[0].ttl).to.equal(300);
1✔
586
    });
587
  });
588

589
  describe('user id system', function () {
1✔
590
    TEST_ID_SYSTEMS.forEach((idSystemProvider) => {
1✔
591
      const id = Date.now().toString();
7✔
592
      const bid = utils.deepClone(BID);
7✔
593

594
      const userId = (function () {
7✔
595
        switch (idSystemProvider) {
7!
596
          case 'lipb':
UNCOV
597
            return {lipbid: id};
×
598
          case 'id5id':
599
            return {uid: id};
1✔
600
          default:
601
            return id;
6✔
602
        }
603
      })();
604

605
      bid.userId = {
7✔
606
        [idSystemProvider]: userId
607
      };
608

609
      it(`should include 'uid.${idSystemProvider}' in request params`, function () {
7✔
610
        const requests = adapter.buildRequests([bid], BIDDER_REQUEST);
7✔
611
        expect(requests[0].data[`uid.${idSystemProvider}`]).to.equal(id);
7✔
612
      });
613
    });
614
    // testing bid.userIdAsEids handling
615
    it("should include user ids from bid.userIdAsEids (length=1)", function() {
1✔
616
      const bid = utils.deepClone(BID);
1✔
617
      bid.userIdAsEids = [
1✔
618
        {
619
          "source": "audigent.com",
620
          "uids": [{"id": "fakeidi6j6dlc6e"}]
621
        }
622
      ]
623
      const requests = adapter.buildRequests([bid], BIDDER_REQUEST);
1✔
624
      expect(requests[0].data['uid.audigent.com']).to.equal("fakeidi6j6dlc6e");
1✔
625
    })
626
    it("should include user ids from bid.userIdAsEids (length=2)", function() {
1✔
627
      const bid = utils.deepClone(BID);
1✔
628
      bid.userIdAsEids = [
1✔
629
        {
630
          "source": "audigent.com",
631
          "uids": [{"id": "fakeidi6j6dlc6e"}]
632
        },
633
        {
634
          "source": "rwdcntrl.net",
635
          "uids": [{"id": "fakeid6f35197d5c", "atype": 1}]
636
        }
637
      ]
638
      const requests = adapter.buildRequests([bid], BIDDER_REQUEST);
1✔
639
      expect(requests[0].data['uid.audigent.com']).to.equal("fakeidi6j6dlc6e");
1✔
640
      expect(requests[0].data['uid.rwdcntrl.net']).to.equal("fakeid6f35197d5c");
1✔
641
    })
642
    // testing user.ext.eid handling
643
    it("should include user ids from user.ext.eid (length=1)", function() {
1✔
644
      const bid = utils.deepClone(BID);
1✔
645
      bid.user = {
1✔
646
        ext: {
647
          eids: [
648
            {
649
              "source": "pubcid.org",
650
              "uids": [{"id": "fakeid8888dlc6e"}]
651
            }
652
          ]
653
        }
654
      }
655
      const requests = adapter.buildRequests([bid], BIDDER_REQUEST);
1✔
656
      expect(requests[0].data['uid.pubcid.org']).to.equal("fakeid8888dlc6e");
1✔
657
    })
658
    it("should include user ids from user.ext.eid (length=2)", function() {
1✔
659
      const bid = utils.deepClone(BID);
1✔
660
      bid.user = {
1✔
661
        ext: {
662
          eids: [
663
            {
664
              "source": "pubcid.org",
665
              "uids": [{"id": "fakeid8888dlc6e"}]
666
            },
667
            {
668
              "source": "adserver.org",
669
              "uids": [{"id": "fakeid495ff1"}]
670
            }
671
          ]
672
        }
673
      }
674
      const requests = adapter.buildRequests([bid], BIDDER_REQUEST);
1✔
675
      expect(requests[0].data['uid.pubcid.org']).to.equal("fakeid8888dlc6e");
1✔
676
      expect(requests[0].data['uid.adserver.org']).to.equal("fakeid495ff1");
1✔
677
    })
678
  });
679

680
  describe('alternate param names extractors', function () {
1✔
681
    it('should return undefined when param not supported', function () {
1✔
682
      const cid = extractCID({'c_id': '1'});
1✔
683
      const pid = extractPID({'p_id': '1'});
1✔
684
      const subDomain = extractSubDomain({'sub_domain': 'prebid'});
1✔
685
      expect(cid).to.be.undefined;
1✔
686
      expect(pid).to.be.undefined;
1✔
687
      expect(subDomain).to.be.undefined;
1✔
688
    });
689

690
    it('should return value when param supported', function () {
1✔
691
      const cid = extractCID({'cID': '1'});
1✔
692
      const pid = extractPID({'Pid': '2'});
1✔
693
      const subDomain = extractSubDomain({'subDOMAIN': 'prebid'});
1✔
694
      expect(cid).to.be.equal('1');
1✔
695
      expect(pid).to.be.equal('2');
1✔
696
      expect(subDomain).to.be.equal('prebid');
1✔
697
    });
698
  });
699

700
  describe('unique deal id', function () {
1✔
701
    before(function () {
1✔
702
      getGlobal().bidderSettings = {
1✔
703
        programmaticX: {
704
          storageAllowed: true
705
        }
706
      };
707
    });
708
    after(function () {
1✔
709
      getGlobal().bidderSettings = {};
1✔
710
    });
711
    const key = 'myKey';
1✔
712
    let uniqueDealId;
713
    beforeEach(() => {
1✔
714
      uniqueDealId = getUniqueDealId(storage, key, 0);
2✔
715
    })
716

717
    it('should get current unique deal id', function (done) {
1✔
718
      // waiting some time so `now` will become past
719
      setTimeout(() => {
1✔
720
        const current = getUniqueDealId(storage, key);
1✔
721
        expect(current).to.be.equal(uniqueDealId);
1✔
722
        done();
1✔
723
      }, 200);
724
    });
725

726
    it('should get new unique deal id on expiration', function (done) {
1✔
727
      setTimeout(() => {
1✔
728
        const current = getUniqueDealId(storage, key, 100);
1✔
729
        expect(current).to.not.be.equal(uniqueDealId);
1✔
730
        done();
1✔
731
      }, 200)
732
    });
733
  });
734

735
  describe('storage utils', function () {
1✔
736
    before(function () {
1✔
737
      getGlobal().bidderSettings = {
1✔
738
        programmaticX: {
739
          storageAllowed: true
740
        }
741
      };
742
    });
743
    after(function () {
1✔
744
      getGlobal().bidderSettings = {};
1✔
745
    });
746
    it('should get value from storage with create param', function () {
1✔
747
      const now = Date.now();
1✔
748
      const clock = useFakeTimers({
1✔
749
        shouldAdvanceTime: true,
750
        now
751
      });
752
      setStorageItem(storage, 'myKey', 2020);
1✔
753
      const {value, created} = getStorageItem(storage, 'myKey');
1✔
754
      expect(created).to.be.equal(now);
1✔
755
      expect(value).to.be.equal(2020);
1✔
756
      expect(typeof value).to.be.equal('number');
1✔
757
      expect(typeof created).to.be.equal('number');
1✔
758
      clock.restore();
1✔
759
    });
760

761
    it('should get external stored value', function () {
1✔
762
      const value = 'superman'
1✔
763
      window.localStorage.setItem('myExternalKey', value);
1✔
764
      const item = getStorageItem(storage, 'myExternalKey');
1✔
765
      expect(item).to.be.equal(value);
1✔
766
    });
767

768
    it('should parse JSON value', function () {
1✔
769
      const data = JSON.stringify({event: 'send'});
1✔
770
      const {event} = tryParseJSON(data);
1✔
771
      expect(event).to.be.equal('send');
1✔
772
    });
773

774
    it('should get original value on parse fail', function () {
1✔
775
      const value = 21;
1✔
776
      const parsed = tryParseJSON(value);
1✔
777
      expect(typeof parsed).to.be.equal('number');
1✔
778
      expect(parsed).to.be.equal(value);
1✔
779
    });
780
  });
781

782
  describe('createDomain test', function() {
1✔
783
    it('should return correct domain', function () {
1✔
784
      const responses = createDomain();
1✔
785
      expect(responses).to.be.equal('https://exchange.programmaticx.ai');
1✔
786
    });
787
  })
788
});
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