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

prebid / Prebid.js / 24470786840

15 Apr 2026 06:16PM UTC coverage: 96.369% (-0.01%) from 96.38%
24470786840

Pull #14731

github

7914b7
patmmccann
Core: bump IAB type package versions
Pull Request #14731: Core: bump IAB type package versions

42970 of 52887 branches covered (81.25%)

219443 of 227711 relevant lines covered (96.37%)

72.74 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
const REQUEST = {
1✔
203
  data: {
204
    width: 300,
205
    height: 250,
206
    bidId: '2d52001cabd527'
207
  }
208
};
209

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

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

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

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

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

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

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

244
    it('exists and is a number', function () {
1✔
245
      expect(adapter.gvlid).to.exist.and.to.be.a('number');
1✔
246
    })
247

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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