prebid.js 6.4.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/modules/adyoulikeBidAdapter.js +13 -9
  2. package/modules/bliinkBidAdapter.js +1 -1
  3. package/modules/consentManagement.js +7 -1
  4. package/modules/docereeBidAdapter.js +10 -1
  5. package/modules/docereeBidAdapter.md +2 -0
  6. package/modules/gptPreAuction.js +55 -7
  7. package/modules/ixBidAdapter.js +29 -12
  8. package/modules/limelightDigitalBidAdapter.js +2 -1
  9. package/modules/livewrappedAnalyticsAdapter.js +3 -1
  10. package/modules/loglyliftBidAdapter.js +79 -0
  11. package/modules/loglyliftBidAdapter.md +55 -0
  12. package/modules/optimeraRtdProvider.js +8 -1
  13. package/modules/ozoneBidAdapter.js +21 -64
  14. package/modules/yieldmoBidAdapter.js +23 -5
  15. package/modules/zeta_global_sspAnalyticsAdapter.js +97 -0
  16. package/modules/zeta_global_sspAnalyticsAdapter.md +24 -0
  17. package/package.json +1 -1
  18. package/src/config.js +27 -3
  19. package/src/prebid.js +2 -0
  20. package/src/utils.js +12 -1
  21. package/test/spec/config_spec.js +279 -0
  22. package/test/spec/modules/adyoulikeBidAdapter_spec.js +49 -0
  23. package/test/spec/modules/consentManagement_spec.js +20 -0
  24. package/test/spec/modules/docereeBidAdapter_spec.js +9 -1
  25. package/test/spec/modules/gptPreAuction_spec.js +177 -2
  26. package/test/spec/modules/ixBidAdapter_spec.js +104 -62
  27. package/test/spec/modules/limelightDigitalBidAdapter_spec.js +75 -17
  28. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +22 -0
  29. package/test/spec/modules/loglyliftBidAdapter_spec.js +172 -0
  30. package/test/spec/modules/optimeraRtdProvider_spec.js +14 -1
  31. package/test/spec/modules/ozoneBidAdapter_spec.js +43 -31
  32. package/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js +427 -0
package/src/utils.js CHANGED
@@ -1272,7 +1272,18 @@ export function mergeDeep(target, ...sources) {
1272
1272
  if (!target[key]) {
1273
1273
  Object.assign(target, { [key]: source[key] });
1274
1274
  } else if (isArray(target[key])) {
1275
- target[key] = target[key].concat(source[key]);
1275
+ source[key].forEach(obj => {
1276
+ let addItFlag = 1;
1277
+ for (let i = 0; i < target[key].length; i++) {
1278
+ if (deepEqual(target[key][i], obj)) {
1279
+ addItFlag = 0;
1280
+ break;
1281
+ }
1282
+ }
1283
+ if (addItFlag) {
1284
+ target[key].push(obj);
1285
+ }
1286
+ });
1276
1287
  }
1277
1288
  } else {
1278
1289
  Object.assign(target, { [key]: source[key] });
@@ -7,8 +7,10 @@ const utils = require('src/utils');
7
7
  let getConfig;
8
8
  let setConfig;
9
9
  let readConfig;
10
+ let mergeConfig;
10
11
  let getBidderConfig;
11
12
  let setBidderConfig;
13
+ let mergeBidderConfig;
12
14
  let setDefaults;
13
15
 
14
16
  describe('config API', function () {
@@ -19,8 +21,10 @@ describe('config API', function () {
19
21
  getConfig = config.getConfig;
20
22
  setConfig = config.setConfig;
21
23
  readConfig = config.readConfig;
24
+ mergeConfig = config.mergeConfig;
22
25
  getBidderConfig = config.getBidderConfig;
23
26
  setBidderConfig = config.setBidderConfig;
27
+ mergeBidderConfig = config.mergeBidderConfig;
24
28
  setDefaults = config.setDefaults;
25
29
  logErrorSpy = sinon.spy(utils, 'logError');
26
30
  logWarnSpy = sinon.spy(utils, 'logWarn');
@@ -345,4 +349,279 @@ describe('config API', function () {
345
349
  const warning = 'Auction Options given an incorrect param: testing';
346
350
  assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
347
351
  });
352
+
353
+ it('should merge input with existing global config', function () {
354
+ const obj = {
355
+ ortb2: {
356
+ site: {
357
+ name: 'example',
358
+ domain: 'page.example.com',
359
+ cat: ['IAB2'],
360
+ sectioncat: ['IAB2-2'],
361
+ pagecat: ['IAB2-2']
362
+ }
363
+ }
364
+ };
365
+ setConfig({ ortb2: {
366
+ user: {
367
+ ext: {
368
+ data: {
369
+ registered: true,
370
+ interests: ['cars']
371
+ }
372
+ }
373
+ }
374
+ }
375
+ });
376
+ mergeConfig(obj);
377
+ const expected = {
378
+ site: {
379
+ name: 'example',
380
+ domain: 'page.example.com',
381
+ cat: ['IAB2'],
382
+ sectioncat: ['IAB2-2'],
383
+ pagecat: ['IAB2-2']
384
+ },
385
+ user: {
386
+ ext: {
387
+ data: {
388
+ registered: true,
389
+ interests: ['cars']
390
+ }
391
+ }
392
+ }
393
+ }
394
+ expect(getConfig('ortb2')).to.deep.equal(expected);
395
+ });
396
+
397
+ it('input should take precedence over existing config if keys are the same', function() {
398
+ const input = {
399
+ ortb2: {
400
+ user: {
401
+ ext: {
402
+ data: {
403
+ registered: true,
404
+ interests: ['cars']
405
+ }
406
+ }
407
+ }
408
+ }
409
+ }
410
+ setConfig({ ortb2: {
411
+ user: {
412
+ ext: {
413
+ data: {
414
+ registered: false
415
+ }
416
+ }
417
+ }
418
+ }});
419
+ mergeConfig(input);
420
+ const expected = {
421
+ user: {
422
+ ext: {
423
+ data: {
424
+ registered: true,
425
+ interests: ['cars']
426
+ }
427
+ }
428
+ }
429
+ }
430
+ expect(getConfig('ortb2')).to.deep.equal(expected);
431
+ });
432
+
433
+ it('should log error for a non-object value passed in', function () {
434
+ mergeConfig('invalid object');
435
+ expect(logErrorSpy.calledOnce).to.equal(true);
436
+ const error = 'mergeConfig input must be an object';
437
+ assert.ok(logErrorSpy.calledWith(error), 'expected error was logged');
438
+ });
439
+
440
+ it('should merge input with existing bidder config', function () {
441
+ const input = {
442
+ bidders: ['rubicon', 'appnexus'],
443
+ config: {
444
+ ortb2: {
445
+ site: {
446
+ name: 'example',
447
+ domain: 'page.example.com',
448
+ cat: ['IAB2'],
449
+ sectioncat: ['IAB2-2'],
450
+ pagecat: ['IAB2-2']
451
+ },
452
+ user: {
453
+ ext: {
454
+ ssp: 'magnite',
455
+ data: {
456
+ registered: false,
457
+ interests: ['sports']
458
+ }
459
+ }
460
+ }
461
+ }
462
+ }
463
+ };
464
+ setBidderConfig({
465
+ bidders: ['rubicon'],
466
+ config: {
467
+ ortb2: {
468
+ user: {
469
+ ext: {
470
+ data: {
471
+ registered: true,
472
+ interests: ['cars']
473
+ }
474
+ }
475
+ }
476
+ }
477
+ }
478
+ });
479
+ mergeBidderConfig(input);
480
+ const expected = {
481
+ rubicon: {
482
+ ortb2: {
483
+ site: {
484
+ name: 'example',
485
+ domain: 'page.example.com',
486
+ cat: ['IAB2'],
487
+ sectioncat: ['IAB2-2'],
488
+ pagecat: ['IAB2-2']
489
+ },
490
+ user: {
491
+ ext: {
492
+ ssp: 'magnite',
493
+ data: {
494
+ registered: false,
495
+ interests: ['cars', 'sports']
496
+ }
497
+ }
498
+ }
499
+ }
500
+ },
501
+ appnexus: {
502
+ ortb2: {
503
+ site: {
504
+ name: 'example',
505
+ domain: 'page.example.com',
506
+ cat: ['IAB2'],
507
+ sectioncat: ['IAB2-2'],
508
+ pagecat: ['IAB2-2']
509
+ },
510
+ user: {
511
+ ext: {
512
+ ssp: 'magnite',
513
+ data: {
514
+ registered: false,
515
+ interests: ['sports']
516
+ }
517
+ }
518
+ }
519
+ }
520
+ }
521
+ }
522
+ expect(getBidderConfig()).to.deep.equal(expected);
523
+ });
524
+
525
+ it('should log error for a non-object value passed in', function () {
526
+ mergeBidderConfig('invalid object');
527
+ expect(logErrorSpy.calledOnce).to.equal(true);
528
+ const error = 'setBidderConfig bidder options must be an object';
529
+ assert.ok(logErrorSpy.calledWith(error), 'expected error was logged');
530
+ });
531
+
532
+ it('should log error for empty bidders array', function () {
533
+ mergeBidderConfig({
534
+ bidders: [],
535
+ config: {
536
+ ortb2: {
537
+ site: {
538
+ name: 'example',
539
+ domain: 'page.example.com',
540
+ cat: ['IAB2'],
541
+ sectioncat: ['IAB2-2'],
542
+ pagecat: ['IAB2-2']
543
+ }
544
+ }
545
+ }
546
+ });
547
+ expect(logErrorSpy.calledOnce).to.equal(true);
548
+ const error = 'setBidderConfig bidder options must contain a bidders list with at least 1 bidder';
549
+ assert.ok(logErrorSpy.calledWith(error), 'expected error was logged');
550
+ });
551
+
552
+ it('should log error for nonexistent config object', function () {
553
+ mergeBidderConfig({
554
+ bidders: ['appnexus']
555
+ });
556
+ expect(logErrorSpy.calledOnce).to.equal(true);
557
+ const error = 'setBidderConfig bidder options must contain a config object';
558
+ assert.ok(logErrorSpy.calledWith(error), 'expected error was logged');
559
+ });
560
+
561
+ it('should merge without array duplication', function() {
562
+ const userObj1 = {
563
+ name: 'www.dataprovider1.com',
564
+ ext: { taxonomyname: 'iab_audience_taxonomy' },
565
+ segment: [{
566
+ id: '1776'
567
+ }]
568
+ };
569
+
570
+ const userObj2 = {
571
+ name: 'www.dataprovider2.com',
572
+ ext: { taxonomyname: 'iab_audience_taxonomy' },
573
+ segment: [{
574
+ id: '1914'
575
+ }]
576
+ };
577
+
578
+ const siteObj1 = {
579
+ name: 'www.dataprovider3.com',
580
+ ext: {
581
+ taxonomyname: 'iab_audience_taxonomy'
582
+ },
583
+ segment: [
584
+ {
585
+ id: '1812'
586
+ },
587
+ {
588
+ id: '1955'
589
+ }
590
+ ]
591
+ }
592
+
593
+ setConfig({
594
+ ortb2: {
595
+ user: {
596
+ data: [userObj1, userObj2]
597
+ },
598
+ site: {
599
+ content: {
600
+ data: [siteObj1]
601
+ }
602
+ }
603
+ }
604
+ });
605
+
606
+ const rtd = {
607
+ ortb2: {
608
+ user: {
609
+ data: [userObj1]
610
+ },
611
+ site: {
612
+ content: {
613
+ data: [siteObj1]
614
+ }
615
+ }
616
+ }
617
+ };
618
+ mergeConfig(rtd);
619
+
620
+ let ortb2Config = getConfig('ortb2');
621
+
622
+ expect(ortb2Config.user.data).to.deep.include.members([userObj1, userObj2]);
623
+ expect(ortb2Config.site.content.data).to.deep.include.members([siteObj1]);
624
+ expect(ortb2Config.user.data).to.have.lengthOf(2);
625
+ expect(ortb2Config.site.content.data).to.have.lengthOf(1);
626
+ });
348
627
  });
@@ -95,6 +95,40 @@ describe('Adyoulike Adapter', function () {
95
95
  }
96
96
  ];
97
97
 
98
+ const bidRequestWithMultipleMediatype = [
99
+ {
100
+ 'bidId': 'bid_id_0',
101
+ 'bidder': 'adyoulike',
102
+ 'placementCode': 'adunit/hb-0',
103
+ 'params': {
104
+ 'placement': 'placement_0'
105
+ },
106
+ 'sizes': '300x250',
107
+ 'mediaTypes': {
108
+ 'banner': {
109
+ 'sizes': ['640x480']
110
+ },
111
+ 'video': {
112
+ 'playerSize': [640, 480],
113
+ 'context': 'outstream'
114
+ },
115
+ 'native': {
116
+ 'image': {
117
+ 'required': true,
118
+ },
119
+ 'title': {
120
+ 'required': true,
121
+ 'len': 80
122
+ },
123
+ 'cta': {
124
+ 'required': false
125
+ },
126
+ }
127
+ },
128
+ 'transactionId': 'bid_id_0_transaction_id'
129
+ }
130
+ ];
131
+
98
132
  const bidRequestWithNativeImageType = [
99
133
  {
100
134
  'bidId': 'bid_id_0',
@@ -647,6 +681,21 @@ describe('Adyoulike Adapter', function () {
647
681
  expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id');
648
682
  });
649
683
 
684
+ it('sends bid request to endpoint with single placement multiple mediatype', function () {
685
+ canonicalQuery.restore();
686
+ const request = spec.buildRequests(bidRequestWithMultipleMediatype, bidderRequest);
687
+ const payload = JSON.parse(request.data);
688
+
689
+ expect(request.url).to.contain(getEndpoint());
690
+ expect(request.method).to.equal('POST');
691
+
692
+ expect(request.url).to.not.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl));
693
+ expect(payload.Version).to.equal('1.0');
694
+ expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0');
695
+ expect(payload.PageRefreshed).to.equal(false);
696
+ expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id');
697
+ });
698
+
650
699
  it('sends bid request to endpoint with multiple placements', function () {
651
700
  const request = spec.buildRequests(bidRequestMultiPlacements, bidderRequest);
652
701
  const payload = JSON.parse(request.data);
@@ -715,6 +715,26 @@ describe('consentManagement', function () {
715
715
  expect(consent).to.be.null;
716
716
  });
717
717
 
718
+ it('allows the auction when CMP is unresponsive', (done) => {
719
+ setConsentConfig({
720
+ cmpApi: 'iab',
721
+ timeout: 10,
722
+ defaultGdprScope: true
723
+ });
724
+
725
+ requestBidsHook(() => {
726
+ didHookReturn = true;
727
+ }, {});
728
+
729
+ setTimeout(() => {
730
+ expect(didHookReturn).to.be.true;
731
+ const consent = gdprDataHandler.getConsentData();
732
+ expect(consent.gdprApplies).to.be.true;
733
+ expect(consent.consentString).to.be.undefined;
734
+ done();
735
+ }, 20);
736
+ });
737
+
718
738
  it('It still considers it a valid cmp response if gdprApplies is not a boolean', function () {
719
739
  // gdprApplies is undefined, should just still store consent response but use whatever defaultGdprScope was
720
740
  let testConsentData = {
@@ -31,6 +31,8 @@ describe('BidlabBidAdapter', function () {
31
31
  bidder: 'doceree',
32
32
  params: {
33
33
  placementId: 'DOC_7jm9j5eqkl0xvc5w',
34
+ gdpr: '1',
35
+ gdprConsent: 'CPQfU1jPQfU1jG0AAAENAwCAAAAAAAAAAAAAAAAAAAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g'
34
36
  }
35
37
  };
36
38
 
@@ -44,6 +46,12 @@ describe('BidlabBidAdapter', function () {
44
46
  });
45
47
  });
46
48
 
49
+ describe('isGdprConsentPresent', function () {
50
+ it('Should return true if gdpr consent is present', function () {
51
+ expect(spec.isGdprConsentPresent(bid)).to.be.true;
52
+ });
53
+ });
54
+
47
55
  describe('buildRequests', function () {
48
56
  let serverRequest = spec.buildRequests([bid]);
49
57
  serverRequest = serverRequest[0]
@@ -56,7 +64,7 @@ describe('BidlabBidAdapter', function () {
56
64
  expect(serverRequest.method).to.equal('GET');
57
65
  });
58
66
  it('Returns valid URL', function () {
59
- expect(serverRequest.url).to.equal('https://bidder.doceree.com/v1/adrequest?id=DOC_7jm9j5eqkl0xvc5w&pubRequestedURL=undefined&loggedInUser=JTdCJTIyZ2VuZGVyJTIyJTNBJTIyJTIyJTJDJTIyZW1haWwlMjIlM0ElMjIlMjIlMkMlMjJoYXNoZWRFbWFpbCUyMiUzQSUyMiUyMiUyQyUyMmZpcnN0TmFtZSUyMiUzQSUyMiUyMiUyQyUyMmxhc3ROYW1lJTIyJTNBJTIyJTIyJTJDJTIybnBpJTIyJTNBJTIyJTIyJTJDJTIyaGFzaGVkTlBJJTIyJTNBJTIyJTIyJTJDJTIyY2l0eSUyMiUzQSUyMiUyMiUyQyUyMnppcENvZGUlMjIlM0ElMjIlMjIlMkMlMjJzcGVjaWFsaXphdGlvbiUyMiUzQSUyMiUyMiU3RA%3D%3D&prebidjs=true&requestId=testing&');
67
+ expect(serverRequest.url).to.equal('https://bidder.doceree.com/v1/adrequest?id=DOC_7jm9j5eqkl0xvc5w&pubRequestedURL=undefined&loggedInUser=JTdCJTIyZ2VuZGVyJTIyJTNBJTIyJTIyJTJDJTIyZW1haWwlMjIlM0ElMjIlMjIlMkMlMjJoYXNoZWRFbWFpbCUyMiUzQSUyMiUyMiUyQyUyMmZpcnN0TmFtZSUyMiUzQSUyMiUyMiUyQyUyMmxhc3ROYW1lJTIyJTNBJTIyJTIyJTJDJTIybnBpJTIyJTNBJTIyJTIyJTJDJTIyaGFzaGVkTlBJJTIyJTNBJTIyJTIyJTJDJTIyY2l0eSUyMiUzQSUyMiUyMiUyQyUyMnppcENvZGUlMjIlM0ElMjIlMjIlMkMlMjJzcGVjaWFsaXphdGlvbiUyMiUzQSUyMiUyMiU3RA%3D%3D&prebidjs=true&requestId=testing&gdpr=1&gdpr_consent=CPQfU1jPQfU1jG0AAAENAwCAAAAAAAAAAAAAAAAAAAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g&');
60
68
  });
61
69
  });
62
70
  describe('interpretResponse', function () {
@@ -20,7 +20,9 @@ describe('GPT pre-auction module', () => {
20
20
  const testSlots = [
21
21
  makeSlot({ code: 'slotCode1', divId: 'div1' }),
22
22
  makeSlot({ code: 'slotCode2', divId: 'div2' }),
23
- makeSlot({ code: 'slotCode3', divId: 'div3' })
23
+ makeSlot({ code: 'slotCode3', divId: 'div3' }),
24
+ makeSlot({ code: 'slotCode4', divId: 'div4' }),
25
+ makeSlot({ code: 'slotCode4', divId: 'div5' })
24
26
  ];
25
27
 
26
28
  describe('appendPbAdSlot', () => {
@@ -172,7 +174,9 @@ describe('GPT pre-auction module', () => {
172
174
  expect(_currentConfig).to.deep.equal({
173
175
  enabled: true,
174
176
  customGptSlotMatching: false,
175
- customPbAdSlot: false
177
+ customPbAdSlot: false,
178
+ customPreAuction: false,
179
+ useDefaultPreAuction: false
176
180
  });
177
181
  });
178
182
  });
@@ -266,5 +270,176 @@ describe('GPT pre-auction module', () => {
266
270
  runMakeBidRequests(testAdUnits);
267
271
  expect(returnedAdUnits).to.deep.equal(expectedAdUnits);
268
272
  });
273
+
274
+ it('should use the passed customPreAuction logic', () => {
275
+ let counter = 0;
276
+ config.setConfig({
277
+ gptPreAuction: {
278
+ enabled: true,
279
+ customPreAuction: (adUnit, slotName) => {
280
+ counter += 1;
281
+ return `${adUnit.code}-${slotName || counter}`;
282
+ }
283
+ }
284
+ });
285
+ const testAdUnits = [
286
+ {
287
+ code: 'adUnit1',
288
+ ortb2Imp: { ext: { data: { pbadslot: '12345' } } }
289
+ },
290
+ {
291
+ code: 'adUnit2',
292
+ },
293
+ {
294
+ code: 'slotCode3',
295
+ },
296
+ {
297
+ code: 'div4',
298
+ }
299
+ ];
300
+
301
+ // all slots should be passed in same time and have slot-${index}
302
+ const expectedAdUnits = [{
303
+ code: 'adUnit1',
304
+ ortb2Imp: {
305
+ ext: {
306
+ // no slotname match so uses adUnit.code-counter
307
+ data: {
308
+ pbadslot: 'adUnit1-1'
309
+ },
310
+ gpid: 'adUnit1-1'
311
+ }
312
+ }
313
+ },
314
+ // second adunit
315
+ {
316
+ code: 'adUnit2',
317
+ ortb2Imp: {
318
+ ext: {
319
+ // no slotname match so uses adUnit.code-counter
320
+ data: {
321
+ pbadslot: 'adUnit2-2'
322
+ },
323
+ gpid: 'adUnit2-2'
324
+ }
325
+ }
326
+ }, {
327
+ code: 'slotCode3',
328
+ ortb2Imp: {
329
+ ext: {
330
+ // slotname found, so uses code + slotname (which is same)
331
+ data: {
332
+ pbadslot: 'slotCode3-slotCode3',
333
+ adserver: {
334
+ name: 'gam',
335
+ adslot: 'slotCode3'
336
+ }
337
+ },
338
+ gpid: 'slotCode3-slotCode3'
339
+ }
340
+ }
341
+ }, {
342
+ code: 'div4',
343
+ ortb2Imp: {
344
+ ext: {
345
+ // slotname found, so uses code + slotname
346
+ data: {
347
+ pbadslot: 'div4-slotCode4',
348
+ adserver: {
349
+ name: 'gam',
350
+ adslot: 'slotCode4'
351
+ }
352
+ },
353
+ gpid: 'div4-slotCode4'
354
+ }
355
+ }
356
+ }];
357
+
358
+ window.googletag.pubads().setSlots(testSlots);
359
+ runMakeBidRequests(testAdUnits);
360
+ expect(returnedAdUnits).to.deep.equal(expectedAdUnits);
361
+ });
362
+
363
+ it('should use useDefaultPreAuction logic', () => {
364
+ config.setConfig({
365
+ gptPreAuction: {
366
+ enabled: true,
367
+ useDefaultPreAuction: true
368
+ }
369
+ });
370
+ const testAdUnits = [
371
+ // First adUnit should use the preset pbadslot
372
+ {
373
+ code: 'adUnit1',
374
+ ortb2Imp: { ext: { data: { pbadslot: '12345' } } }
375
+ },
376
+ // Second adUnit should not match a gam slot, so no slot set
377
+ {
378
+ code: 'adUnit2',
379
+ },
380
+ // third adunit matches a single slot so uses it
381
+ {
382
+ code: 'slotCode3',
383
+ },
384
+ // fourth adunit matches multiple slots so combination
385
+ {
386
+ code: 'div4',
387
+ }
388
+ ];
389
+
390
+ const expectedAdUnits = [{
391
+ code: 'adUnit1',
392
+ ortb2Imp: {
393
+ ext: {
394
+ data: {
395
+ pbadslot: '12345'
396
+ },
397
+ gpid: '12345'
398
+ }
399
+ }
400
+ },
401
+ // second adunit
402
+ {
403
+ code: 'adUnit2',
404
+ ortb2Imp: {
405
+ ext: {
406
+ data: {
407
+ },
408
+ }
409
+ }
410
+ }, {
411
+ code: 'slotCode3',
412
+ ortb2Imp: {
413
+ ext: {
414
+ data: {
415
+ pbadslot: 'slotCode3',
416
+ adserver: {
417
+ name: 'gam',
418
+ adslot: 'slotCode3'
419
+ }
420
+ },
421
+ gpid: 'slotCode3'
422
+ }
423
+ }
424
+ }, {
425
+ code: 'div4',
426
+ ortb2Imp: {
427
+ ext: {
428
+ data: {
429
+ pbadslot: 'slotCode4#div4',
430
+ adserver: {
431
+ name: 'gam',
432
+ adslot: 'slotCode4'
433
+ }
434
+ },
435
+ gpid: 'slotCode4#div4'
436
+ }
437
+ }
438
+ }];
439
+
440
+ window.googletag.pubads().setSlots(testSlots);
441
+ runMakeBidRequests(testAdUnits);
442
+ expect(returnedAdUnits).to.deep.equal(expectedAdUnits);
443
+ });
269
444
  });
270
445
  });