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

prebid / Prebid.js / 18594521186

17 Oct 2025 01:40PM UTC coverage: 96.238% (-0.001%) from 96.239%
18594521186

push

github

web-flow
Semantiq RTD module: fix incorrect property name (#14027)

Co-authored-by: Alexandr Kim <alexandr.kim@audienzz.ch>

52437 of 64151 branches covered (81.74%)

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

401 existing lines in 53 files now uncovered.

200307 of 208138 relevant lines covered (96.24%)

125.2 hits per line

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

99.06
/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) {
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 is a number', function () {
1✔
246
      expect(adapter.gvlid).to.exist.and.to.be.a('number');
1✔
247
    })
248

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

509
      expect(result).to.deep.equal([{
1✔
510
        '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',
511
        'type': 'image'
512
      }]);
513
    });
514
  });
515

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

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

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

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

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

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

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

593
  describe('user id system', function () {
1✔
594
    TEST_ID_SYSTEMS.forEach((idSystemProvider) => {
1✔
595
      const id = Date.now().toString();
7✔
596
      const bid = utils.deepClone(BID);
7✔
597

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

609
      bid.userId = {
7✔
610
        [idSystemProvider]: userId
611
      };
612

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

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

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

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

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

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

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

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

772
    it('should parse JSON value', function () {
1✔
773
      const data = JSON.stringify({event: 'send'});
1✔
774
      const {event} = tryParseJSON(data);
1✔
775
      expect(event).to.be.equal('send');
1✔
776
    });
777

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

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