prebid.js 5.17.0 → 6.0.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 (145) hide show
  1. package/.babelrc.js +3 -6
  2. package/README.md +3 -1
  3. package/browsers.json +1 -8
  4. package/integrationExamples/gpt/akamaidap_segments_example.html +132 -0
  5. package/modules/.submodules.json +1 -0
  6. package/modules/adfBidAdapter.js +21 -16
  7. package/modules/adgenerationBidAdapter.js +28 -4
  8. package/modules/adkernelBidAdapter.js +2 -1
  9. package/modules/admixerBidAdapter.js +11 -0
  10. package/modules/adtelligentBidAdapter.js +2 -1
  11. package/modules/airgridRtdProvider.js +1 -1
  12. package/modules/akamaiDapRtdProvider.js +474 -0
  13. package/modules/akamaiDapRtdProvider.md +47 -0
  14. package/modules/aolBidAdapter.js +2 -1
  15. package/modules/appnexusBidAdapter.js +5 -3
  16. package/modules/atsAnalyticsAdapter.js +67 -46
  17. package/modules/atsAnalyticsAdapter.md +1 -0
  18. package/modules/betweenBidAdapter.js +20 -3
  19. package/modules/bliinkBidAdapter.js +58 -32
  20. package/modules/bliinkBidAdapter.md +29 -6
  21. package/modules/browsiRtdProvider.js +106 -18
  22. package/modules/cleanioRtdProvider.js +192 -0
  23. package/modules/cleanioRtdProvider.md +59 -0
  24. package/modules/codefuelBidAdapter.js +183 -0
  25. package/modules/codefuelBidAdapter.md +111 -0
  26. package/modules/connectIdSystem.js +104 -0
  27. package/modules/connectIdSystem.md +33 -0
  28. package/modules/cwireBidAdapter.js +272 -0
  29. package/modules/cwireBidAdapter.md +43 -0
  30. package/modules/deepintentBidAdapter.js +106 -9
  31. package/modules/deepintentBidAdapter.md +36 -1
  32. package/modules/deltaprojectsBidAdapter.js +252 -0
  33. package/modules/deltaprojectsBidAdapter.md +32 -0
  34. package/modules/dgkeywordRtdProvider.js +0 -1
  35. package/modules/engageyaBidAdapter.js +157 -0
  36. package/modules/gridBidAdapter.js +1 -0
  37. package/modules/gumgumBidAdapter.js +8 -0
  38. package/modules/inskinBidAdapter.js +7 -3
  39. package/modules/ixBidAdapter.js +8 -1
  40. package/modules/jixieBidAdapter.js +8 -2
  41. package/modules/justpremiumBidAdapter.js +6 -1
  42. package/modules/limelightDigitalBidAdapter.js +22 -2
  43. package/modules/livewrappedAnalyticsAdapter.js +53 -3
  44. package/modules/mediakeysBidAdapter.js +2 -1
  45. package/modules/multibid/index.js +3 -3
  46. package/modules/nativoBidAdapter.js +6 -2
  47. package/modules/nextMillenniumBidAdapter.js +12 -3
  48. package/modules/oguryBidAdapter.js +36 -7
  49. package/modules/openxBidAdapter.js +34 -22
  50. package/modules/operaadsBidAdapter.js +21 -1
  51. package/modules/otmBidAdapter.js +146 -0
  52. package/modules/otmBidAdapter.md +27 -26
  53. package/modules/outbrainBidAdapter.js +5 -0
  54. package/modules/pixfutureBidAdapter.js +24 -4
  55. package/modules/pixfutureBidAdapter.md +127 -0
  56. package/modules/playwireBidAdapter.md +61 -0
  57. package/modules/prebidServerBidAdapter/index.js +1 -1
  58. package/modules/proxistoreBidAdapter.js +4 -6
  59. package/modules/publinkIdSystem.js +11 -6
  60. package/modules/pubmaticBidAdapter.js +9 -0
  61. package/modules/pubmaticBidAdapter.md +1 -1
  62. package/modules/rtdModule/index.js +2 -2
  63. package/modules/sonobiBidAdapter.js +7 -0
  64. package/modules/sortableBidAdapter.js +1 -0
  65. package/modules/talkadsBidAdapter.js +129 -0
  66. package/modules/talkadsBidAdapter.md +60 -0
  67. package/modules/teadsBidAdapter.js +3 -0
  68. package/modules/tripleliftBidAdapter.js +22 -5
  69. package/modules/trustxBidAdapter.js +8 -6
  70. package/modules/undertoneBidAdapter.js +9 -5
  71. package/modules/undertoneBidAdapter.md +5 -1
  72. package/modules/unicornBidAdapter.js +3 -3
  73. package/modules/userId/eids.js +18 -0
  74. package/modules/userId/eids.md +7 -0
  75. package/modules/userId/userId.md +12 -0
  76. package/modules/ventesBidAdapter.js +370 -0
  77. package/modules/ventesBidAdapter.md +94 -0
  78. package/modules/videobyteBidAdapter.js +13 -6
  79. package/modules/videobyteBidAdapter.md +49 -0
  80. package/modules/visxBidAdapter.js +15 -22
  81. package/modules/yahoosspBidAdapter.js +637 -0
  82. package/modules/yahoosspBidAdapter.md +795 -0
  83. package/modules/yieldlabBidAdapter.js +48 -3
  84. package/modules/yieldlabBidAdapter.md +16 -1
  85. package/modules/yieldmoSyntheticInventoryModule.js +46 -0
  86. package/modules/yieldmoSyntheticInventoryModule.md +68 -0
  87. package/package.json +1 -1
  88. package/src/adapterManager.js +5 -0
  89. package/src/adapters/bidderFactory.js +4 -3
  90. package/src/auction.js +11 -11
  91. package/src/constants.json +1 -0
  92. package/src/secureCreatives.js +6 -7
  93. package/src/targeting.js +11 -9
  94. package/test/spec/modules/adfBidAdapter_spec.js +83 -29
  95. package/test/spec/modules/adgenerationBidAdapter_spec.js +121 -50
  96. package/test/spec/modules/adtelligentBidAdapter_spec.js +1 -0
  97. package/test/spec/modules/akamaiDapRtdProvider_spec.js +246 -0
  98. package/test/spec/modules/appnexusBidAdapter_spec.js +2 -1
  99. package/test/spec/modules/atsAnalyticsAdapter_spec.js +42 -9
  100. package/test/spec/modules/betweenBidAdapter_spec.js +41 -0
  101. package/test/spec/modules/bliinkBidAdapter_spec.js +87 -36
  102. package/test/spec/modules/browsiRtdProvider_spec.js +62 -7
  103. package/test/spec/modules/cleanioRtdProvider_spec.js +188 -0
  104. package/test/spec/modules/codefuelBidAdapter_spec.js +316 -0
  105. package/test/spec/modules/connectIdSystem_spec.js +189 -0
  106. package/test/spec/modules/cwireBidAdapter_spec.js +246 -0
  107. package/test/spec/modules/deepintentBidAdapter_spec.js +153 -3
  108. package/test/spec/modules/deltaprojectsBidAdapter_spec.js +399 -0
  109. package/test/spec/modules/engageyaBidAdapter_spec.js +286 -0
  110. package/test/spec/modules/gumgumBidAdapter_spec.js +5 -1
  111. package/test/spec/modules/ixBidAdapter_spec.js +13 -3
  112. package/test/spec/modules/jixieBidAdapter_spec.js +13 -11
  113. package/test/spec/modules/justpremiumBidAdapter_spec.js +9 -2
  114. package/test/spec/modules/limelightDigitalBidAdapter_spec.js +155 -1
  115. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +67 -12
  116. package/test/spec/modules/multibid_spec.js +31 -31
  117. package/test/spec/modules/nextMillenniumBidAdapter_spec.js +13 -1
  118. package/test/spec/modules/oguryBidAdapter_spec.js +125 -37
  119. package/test/spec/modules/openxBidAdapter_spec.js +85 -13
  120. package/test/spec/modules/operaadsBidAdapter_spec.js +38 -6
  121. package/test/spec/modules/otmBidAdapter_spec.js +67 -0
  122. package/test/spec/modules/outbrainBidAdapter_spec.js +18 -0
  123. package/test/spec/modules/publinkIdSystem_spec.js +6 -6
  124. package/test/spec/modules/pubmaticBidAdapter_spec.js +39 -1
  125. package/test/spec/modules/sonobiBidAdapter_spec.js +34 -1
  126. package/test/spec/modules/sortableBidAdapter_spec.js +11 -0
  127. package/test/spec/modules/talkadsBidAdapter_spec.js +231 -0
  128. package/test/spec/modules/teadsBidAdapter_spec.js +132 -0
  129. package/test/spec/modules/tripleliftBidAdapter_spec.js +128 -0
  130. package/test/spec/modules/trustxBidAdapter_spec.js +3 -3
  131. package/test/spec/modules/undertoneBidAdapter_spec.js +52 -0
  132. package/test/spec/modules/unicornBidAdapter_spec.js +4 -4
  133. package/test/spec/modules/ventesBidAdapter_spec.js +845 -0
  134. package/test/spec/modules/videobyteBidAdapter_spec.js +2 -2
  135. package/test/spec/modules/visxBidAdapter_spec.js +48 -4
  136. package/test/spec/modules/yahoosspBidAdapter_spec.js +1332 -0
  137. package/test/spec/modules/yieldlabBidAdapter_spec.js +65 -1
  138. package/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js +89 -0
  139. package/test/spec/unit/core/adapterManager_spec.js +32 -0
  140. package/test/spec/unit/core/bidderFactory_spec.js +61 -1
  141. package/test/spec/unit/pbjs_api_spec.js +37 -2
  142. package/test/spec/unit/secureCreatives_spec.js +54 -25
  143. package/wdio.conf.js +1 -1
  144. package/modules/turktelekomBidAdapter.md +0 -49
  145. package/yarn.lock +0 -13122
@@ -0,0 +1,1332 @@
1
+ import { expect } from 'chai';
2
+ import { config } from 'src/config.js';
3
+ import { BANNER, VIDEO } from 'src/mediaTypes.js';
4
+ import { spec } from 'modules/yahoosspBidAdapter.js';
5
+
6
+ const DEFAULT_BID_ID = '84ab500420319d';
7
+ const DEFAULT_BID_DCN = '2093845709823475';
8
+ const DEFAULT_BID_POS = 'header';
9
+ const DEFAULT_PUBID = 'PubId';
10
+ const DEFAULT_AD_UNIT_CODE = '/19968336/header-bid-tag-1';
11
+ const DEFAULT_AD_UNIT_TYPE = 'banner';
12
+ const DEFAULT_PARAMS_BID_OVERRIDE = {};
13
+ const DEFAULT_VIDEO_CONTEXT = 'instream';
14
+ const ADAPTER_VERSION = '1.0.1';
15
+ const PREBID_VERSION = '$prebid.version$';
16
+ const INTEGRATION_METHOD = 'prebid.js';
17
+
18
+ // Utility functions
19
+ const generateBidRequest = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode}) => {
20
+ const bidRequest = {
21
+ adUnitCode,
22
+ auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917',
23
+ bidId,
24
+ bidderRequestsCount: 1,
25
+ bidder: 'yahoossp',
26
+ bidderRequestId: '7101db09af0db2',
27
+ bidderWinsCount: 0,
28
+ mediaTypes: {},
29
+ params: {
30
+ bidOverride: bidOverrideObject
31
+ },
32
+ src: 'client',
33
+ transactionId: '5b17b67d-7704-4732-8cc9-5b1723e9bcf9'
34
+ };
35
+
36
+ const bannerObj = {
37
+ sizes: [[300, 250], [300, 600]]
38
+ };
39
+
40
+ const videoObject = {
41
+ context: videoContext,
42
+ playerSize: [[300, 250]],
43
+ mimes: ['video/mp4', 'application/javascript'],
44
+ api: [2]
45
+ };
46
+
47
+ if (videoContext === 'outstream') {
48
+ bidRequest.renderer = undefined
49
+ };
50
+
51
+ if (adUnitType === 'banner' || adUnitType === 'dap-o2' || adUnitType === 'dap-up') {
52
+ bidRequest.mediaTypes.banner = bannerObj;
53
+ bidRequest.sizes = [[300, 250], [300, 600]];
54
+ } else if (adUnitType === 'video') {
55
+ bidRequest.mediaTypes.video = videoObject;
56
+ } else if (adUnitType === 'multi-format') {
57
+ bidRequest.mediaTypes.banner = bannerObj;
58
+ bidRequest.sizes = [[300, 250], [300, 600]];
59
+ bidRequest.mediaTypes.video = videoObject;
60
+ } else if (adUnitType === 'native') {
61
+ bidRequest.mediaTypes.native = {a: 123, b: 456};
62
+ }
63
+
64
+ if (pubIdMode === true) {
65
+ bidRequest.params.pubId = DEFAULT_PUBID;
66
+ } else {
67
+ bidRequest.params.dcn = DEFAULT_BID_DCN;
68
+ bidRequest.params.pos = pos || DEFAULT_BID_POS;
69
+ };
70
+
71
+ return bidRequest;
72
+ }
73
+
74
+ let generateBidderRequest = (bidRequestArray, adUnitCode) => {
75
+ const bidderRequest = {
76
+ adUnitCode: adUnitCode || 'default-adUnitCode',
77
+ auctionId: 'd4c83a3b-18e4-4208-b98b-63848449c7aa',
78
+ auctionStart: new Date().getTime(),
79
+ bidderCode: 'yahoossp',
80
+ bidderRequestId: '112f1c7c5d399a',
81
+ bids: bidRequestArray,
82
+ refererInfo: {
83
+ referer: 'https://publisher-test.com',
84
+ reachedTop: true,
85
+ isAmp: false,
86
+ numIframes: 0,
87
+ stack: ['https://publisher-test.com'],
88
+ },
89
+ gdprConsent: {
90
+ consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA',
91
+ vendorData: {},
92
+ gdprApplies: true
93
+ },
94
+ start: new Date().getTime(),
95
+ timeout: 1000,
96
+ };
97
+
98
+ return bidderRequest;
99
+ };
100
+
101
+ const generateBuildRequestMock = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode}) => {
102
+ const bidRequestConfig = {
103
+ bidId: bidId || DEFAULT_BID_ID,
104
+ pos: pos || DEFAULT_BID_POS,
105
+ adUnitCode: adUnitCode || DEFAULT_AD_UNIT_CODE,
106
+ adUnitType: adUnitType || DEFAULT_AD_UNIT_TYPE,
107
+ bidOverrideObject: bidOverrideObject || DEFAULT_PARAMS_BID_OVERRIDE,
108
+ videoContext: videoContext || DEFAULT_VIDEO_CONTEXT,
109
+ pubIdMode: pubIdMode || false
110
+ };
111
+ const bidRequest = generateBidRequest(bidRequestConfig);
112
+ const validBidRequests = [bidRequest];
113
+ const bidderRequest = generateBidderRequest(validBidRequests, adUnitCode);
114
+
115
+ return { bidRequest, validBidRequests, bidderRequest }
116
+ };
117
+
118
+ const generateAdmPayload = (admPayloadType) => {
119
+ let ADM_PAYLOAD;
120
+ switch (admPayloadType) {
121
+ case 'banner':
122
+ ADM_PAYLOAD = '<script>logInfo(\'ad\'); AdPlacement </script>'; // banner
123
+ break;
124
+ case 'video':
125
+ ADM_PAYLOAD = '<VAST></VAST>'; // VAST xml
126
+ break;
127
+ case 'multi-format':
128
+ const admPayloads = [
129
+ '<script>logInfo(\'ad\'); AdPlacement </script>', // banner
130
+ '<VAST></VAST>' // VAST xml
131
+ ]
132
+ const random = Math.floor(Math.random() * admPayloads.length);
133
+ ADM_PAYLOAD = admPayloads[random]
134
+ break;
135
+ case 'dap-o2':
136
+ ADM_PAYLOAD = '<script>o2playerSettings</script>'; // O2 player
137
+ break;
138
+ case 'dap-up':
139
+ ADM_PAYLOAD = '<script>YAHOO.VideoPlatform.VideoPlayer</script>'; // Unified Player
140
+ break;
141
+ };
142
+ return ADM_PAYLOAD;
143
+ };
144
+
145
+ const generateResponseMock = (admPayloadType, vastVersion, videoContext) => {
146
+ const bidResponse = {
147
+ id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9',
148
+ impid: '274395c06a24e5',
149
+ adm: generateAdmPayload(admPayloadType),
150
+ price: 1,
151
+ w: 300,
152
+ h: 250,
153
+ crid: 'ssp-placement-name',
154
+ adomain: ['advertiser-domain.com']
155
+ };
156
+
157
+ if (vastVersion === 'vast') {
158
+ bidResponse.nurl = 'https://yahoo.com?event=adAttempt';
159
+ };
160
+
161
+ const serverResponse = {
162
+ body: {
163
+ id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9',
164
+ seatbid: [{ bid: [ bidResponse ], seat: 13107 }]
165
+ }
166
+ };
167
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType, videoContext: videoContext});
168
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
169
+
170
+ return {serverResponse, data, bidderRequest};
171
+ }
172
+
173
+ // Unit tests
174
+ describe('YahooSSP Bid Adapter:', () => {
175
+ it('PLACEHOLDER TO PASS GULP', () => {
176
+ const obj = {};
177
+ expect(obj).to.be.an('object');
178
+ });
179
+
180
+ describe('getUserSyncs()', () => {
181
+ const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true';
182
+ const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true';
183
+ const IFRAME_TWO_URL = 'http://image-iframe-two.com/foo/bar?1234&baz=true';
184
+
185
+ let serverResponses = [];
186
+ beforeEach(() => {
187
+ serverResponses[0] = {
188
+ body: {
189
+ ext: {
190
+ pixels: `<script>document.write('<iframe src="${IFRAME_ONE_URL}"></iframe>` +
191
+ `<img src="${IMAGE_PIXEL_URL}"></iframe>` +
192
+ `<iframe src="${IFRAME_TWO_URL}"></iframe>');</script>`
193
+ }
194
+ }
195
+ }
196
+ });
197
+
198
+ after(() => {
199
+ serverResponses = undefined;
200
+ });
201
+
202
+ it('for only iframe enabled syncs', () => {
203
+ let syncOptions = {
204
+ iframeEnabled: true,
205
+ pixelEnabled: false
206
+ };
207
+ let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses);
208
+ expect(pixelsObjects.length).to.equal(2);
209
+ expect(pixelsObjects).to.deep.equal(
210
+ [
211
+ {type: 'iframe', 'url': IFRAME_ONE_URL},
212
+ {type: 'iframe', 'url': IFRAME_TWO_URL}
213
+ ]
214
+ )
215
+ });
216
+
217
+ it('for only pixel enabled syncs', () => {
218
+ let syncOptions = {
219
+ iframeEnabled: false,
220
+ pixelEnabled: true
221
+ };
222
+ let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses);
223
+ expect(pixelsObjects.length).to.equal(1);
224
+ expect(pixelsObjects).to.deep.equal(
225
+ [
226
+ {type: 'image', 'url': IMAGE_PIXEL_URL}
227
+ ]
228
+ )
229
+ });
230
+
231
+ it('for both pixel and iframe enabled syncs', () => {
232
+ let syncOptions = {
233
+ iframeEnabled: true,
234
+ pixelEnabled: true
235
+ };
236
+ let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses);
237
+ expect(pixelsObjects.length).to.equal(3);
238
+ expect(pixelsObjects).to.deep.equal(
239
+ [
240
+ {type: 'iframe', 'url': IFRAME_ONE_URL},
241
+ {type: 'image', 'url': IMAGE_PIXEL_URL},
242
+ {type: 'iframe', 'url': IFRAME_TWO_URL}
243
+ ]
244
+ )
245
+ });
246
+ });
247
+
248
+ // Validate Bid Requests
249
+ describe('isBidRequestValid()', () => {
250
+ const INVALID_INPUT = [
251
+ {},
252
+ {params: {}},
253
+ {params: {dcn: '2c9d2b50015a5aa95b70a9b0b5b10012'}},
254
+ {params: {dcn: 1234, pos: 'header'}},
255
+ {params: {dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', pos: 1234}},
256
+ {params: {dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', pos: ''}},
257
+ {params: {dcn: '', pos: 'header'}},
258
+ ];
259
+
260
+ INVALID_INPUT.forEach(input => {
261
+ it(`should determine that the bid is INVALID for the input ${JSON.stringify(input)}`, () => {
262
+ expect(spec.isBidRequestValid(input)).to.be.false;
263
+ });
264
+ });
265
+
266
+ it('should determine that the bid is VALID if dcn and pos are present on the params object', () => {
267
+ const validBid = {
268
+ params: {
269
+ dcn: '2c9d2b50015a5aa95b70a9b0b5b10012',
270
+ pos: 'header'
271
+ }
272
+ };
273
+ expect(spec.isBidRequestValid(validBid)).to.be.true;
274
+ });
275
+
276
+ it('should mark bid as VALID if bid.params.testing.e2etest = "true" (dcn & pos not required)', () => {
277
+ const validBid = {
278
+ params: {
279
+ dcn: 8888,
280
+ pos: 9999,
281
+ testing: {
282
+ e2etest: true
283
+ }
284
+ }
285
+ };
286
+ expect(spec.isBidRequestValid(validBid)).to.be.true;
287
+ });
288
+
289
+ it('should mark bid ad VALID if pubId exists instead of dcn & pos', () => {
290
+ const validBid = {
291
+ params: {
292
+ pubId: DEFAULT_PUBID
293
+ }
294
+ };
295
+ expect(spec.isBidRequestValid(validBid)).to.be.true;
296
+ });
297
+ });
298
+
299
+ describe('Price Floor module support:', () => {
300
+ it('should get bidfloor from getFloor method', () => {
301
+ const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({});
302
+ bidRequest.params.bidOverride = {cur: 'EUR'};
303
+ bidRequest.getFloor = (floorObj) => {
304
+ return {
305
+ floor: bidRequest.floors.values[floorObj.mediaType + '|640x480'],
306
+ currency: floorObj.currency,
307
+ mediaType: floorObj.mediaType
308
+ }
309
+ };
310
+ bidRequest.floors = {
311
+ currency: 'EUR',
312
+ values: {
313
+ 'banner|640x480': 5.55
314
+ }
315
+ };
316
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
317
+ expect(data.cur).to.deep.equal(['EUR']);
318
+ expect(data.imp[0].bidfloor).is.a('number');
319
+ expect(data.imp[0].bidfloor).to.equal(5.55);
320
+ });
321
+ });
322
+
323
+ describe('Schain module support:', () => {
324
+ it('should send Global or Bidder specific schain', function () {
325
+ const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({});
326
+ const globalSchain = {
327
+ ver: '1.0',
328
+ complete: 1,
329
+ nodes: [{
330
+ asi: 'some-platform.com',
331
+ sid: '111111',
332
+ rid: bidRequest.bidId,
333
+ hp: 1
334
+ }]
335
+ };
336
+ bidRequest.schain = globalSchain;
337
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
338
+ const schain = data.source.ext.schain;
339
+ expect(schain.nodes.length).to.equal(1);
340
+ expect(schain).to.equal(globalSchain);
341
+ });
342
+ });
343
+
344
+ describe('First party data module - "Site" support (ortb2):', () => {
345
+ // Should not allow invalid "site" data types
346
+ const INVALID_ORTB2_TYPES = [ null, [], 123, 'unsupportedKeyName', true, false, undefined ];
347
+ INVALID_ORTB2_TYPES.forEach(param => {
348
+ const ortb2 = { site: param }
349
+ config.setConfig({ortb2});
350
+ it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => {
351
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
352
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
353
+ expect(data.site[param]).to.be.undefined;
354
+ });
355
+ });
356
+
357
+ // Should add valid "site" params
358
+ const VALID_SITE_STRINGS = ['name', 'domain', 'page', 'ref', 'keywords', 'search'];
359
+ const VALID_SITE_ARRAYS = ['cat', 'sectioncat', 'pagecat'];
360
+
361
+ VALID_SITE_STRINGS.forEach(param => {
362
+ it(`should allow supported site keys to be added bid-request: ${JSON.stringify(param)}`, () => {
363
+ const ortb2 = {
364
+ site: {
365
+ [param]: 'something'
366
+ }
367
+ };
368
+ config.setConfig({ortb2});
369
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
370
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
371
+ expect(data.site[param]).to.exist;
372
+ expect(data.site[param]).to.be.a('string');
373
+ expect(data.site[param]).to.be.equal(ortb2.site[param]);
374
+ });
375
+ });
376
+
377
+ VALID_SITE_ARRAYS.forEach(param => {
378
+ it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => {
379
+ const ortb2 = {
380
+ site: {
381
+ [param]: ['something']
382
+ }
383
+ };
384
+ config.setConfig({ortb2});
385
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
386
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
387
+ expect(data.site[param]).to.exist;
388
+ expect(data.site[param]).to.be.a('array');
389
+ expect(data.site[param]).to.be.equal(ortb2.site[param]);
390
+ });
391
+ });
392
+
393
+ // Should not allow invalid "site.content" data types
394
+ INVALID_ORTB2_TYPES.forEach(param => {
395
+ it(`should determine that the ortb2.site.content key is invalid and should not be added to bid-request: ${JSON.stringify(param)}`, () => {
396
+ const ortb2 = {
397
+ site: {
398
+ content: param
399
+ }
400
+ };
401
+ config.setConfig({ortb2});
402
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
403
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
404
+ expect(data.site.content).to.be.undefined;
405
+ });
406
+ });
407
+
408
+ // Should not allow invalid "site.content" keys
409
+ it(`should not allow invalid ortb2.site.content object keys to be added to bid-request: {custom object}`, () => {
410
+ const ortb2 = {
411
+ site: {
412
+ content: {
413
+ fake: 'news',
414
+ unreal: 'param',
415
+ counterfit: 'data'
416
+ }
417
+ }
418
+ };
419
+ config.setConfig({ortb2});
420
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
421
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
422
+ expect(data.site.content).to.be.a('object');
423
+ });
424
+
425
+ // Should append valid "site.content" keys
426
+ const VALID_CONTENT_STRINGS = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language'];
427
+ VALID_CONTENT_STRINGS.forEach(param => {
428
+ it(`should determine that the ortb2.site String key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => {
429
+ const ortb2 = {
430
+ site: {
431
+ content: {
432
+ [param]: 'something'
433
+ }
434
+ }
435
+ };
436
+ config.setConfig({ortb2});
437
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
438
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
439
+ expect(data.site.content[param]).to.exist;
440
+ expect(data.site.content[param]).to.be.a('string');
441
+ expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]);
442
+ });
443
+ });
444
+
445
+ const VALID_CONTENT_NUMBERS = ['episode', 'prodq', 'context', 'livestream', 'len'];
446
+ VALID_CONTENT_NUMBERS.forEach(param => {
447
+ it(`should determine that the ortb2.site.content Number key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => {
448
+ const ortb2 = {
449
+ site: {
450
+ content: {
451
+ [param]: 1234
452
+ }
453
+ }
454
+ };
455
+ config.setConfig({ortb2});
456
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
457
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
458
+ expect(data.site.content[param]).to.be.a('number');
459
+ expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]);
460
+ });
461
+ });
462
+
463
+ const VALID_CONTENT_ARRAYS = ['cat'];
464
+ VALID_CONTENT_ARRAYS.forEach(param => {
465
+ it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => {
466
+ const ortb2 = {
467
+ site: {
468
+ content: {
469
+ [param]: ['something', 'something-else']
470
+ }
471
+ }
472
+ };
473
+ config.setConfig({ortb2});
474
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
475
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
476
+ expect(data.site.content[param]).to.be.a('array');
477
+ expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]);
478
+ });
479
+ });
480
+
481
+ const VALID_CONTENT_OBJECTS = ['ext'];
482
+ VALID_CONTENT_OBJECTS.forEach(param => {
483
+ it(`should determine that the ortb2.site.content Object key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => {
484
+ const ortb2 = {
485
+ site: {
486
+ content: {
487
+ [param]: {a: '123', b: '456'}
488
+ }
489
+ }
490
+ };
491
+ config.setConfig({ortb2});
492
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
493
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
494
+ expect(data.site.content[param]).to.be.a('object');
495
+ expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]);
496
+ config.setConfig({ortb2: {}});
497
+ });
498
+ });
499
+ });
500
+
501
+ describe('First party data module - "User" support (ortb2):', () => {
502
+ // Global ortb2.user validations
503
+ // Should not allow invalid "user" data types
504
+ const INVALID_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ];
505
+ INVALID_ORTB2_TYPES.forEach(param => {
506
+ const ortb2 = { user: param }
507
+ config.setConfig({ortb2});
508
+ it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => {
509
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
510
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
511
+ expect(data.user[param]).to.be.undefined;
512
+ });
513
+ });
514
+
515
+ // Should add valid "user" params
516
+ const VALID_USER_STRINGS = ['id', 'buyeruid', 'gender', 'keywords', 'customdata'];
517
+ VALID_USER_STRINGS.forEach(param => {
518
+ it(`should allow supported user string keys to be added bid-request: ${JSON.stringify(param)}`, () => {
519
+ const ortb2 = {
520
+ user: {
521
+ [param]: 'something'
522
+ }
523
+ };
524
+ config.setConfig({ortb2});
525
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
526
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
527
+ expect(data.user[param]).to.exist;
528
+ expect(data.user[param]).to.be.a('string');
529
+ expect(data.user[param]).to.be.equal(ortb2.user[param]);
530
+ });
531
+ });
532
+
533
+ const VALID_USER_NUMBERS = ['yob'];
534
+ VALID_USER_NUMBERS.forEach(param => {
535
+ it(`should allow supported user number keys to be added bid-request: ${JSON.stringify(param)}`, () => {
536
+ const ortb2 = {
537
+ user: {
538
+ [param]: 1982
539
+ }
540
+ };
541
+ config.setConfig({ortb2});
542
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
543
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
544
+ expect(data.user[param]).to.exist;
545
+ expect(data.user[param]).to.be.a('number');
546
+ expect(data.user[param]).to.be.equal(ortb2.user[param]);
547
+ });
548
+ });
549
+
550
+ const VALID_USER_ARRAYS = ['data'];
551
+ VALID_USER_ARRAYS.forEach(param => {
552
+ it(`should allow supported user Array keys to be added to the bid-request: ${JSON.stringify(param)}`, () => {
553
+ const ortb2 = {
554
+ user: {
555
+ [param]: ['something']
556
+ }
557
+ };
558
+ config.setConfig({ortb2});
559
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
560
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
561
+ expect(data.user[param]).to.exist;
562
+ expect(data.user[param]).to.be.a('array');
563
+ expect(data.user[param]).to.be.equal(ortb2.user[param]);
564
+ });
565
+ });
566
+
567
+ const VALID_USER_OBJECTS = ['ext'];
568
+ VALID_USER_OBJECTS.forEach(param => {
569
+ it(`should allow supported user extObject keys to be added to the bid-request: ${JSON.stringify(param)}`, () => {
570
+ const ortb2 = {
571
+ user: {
572
+ [param]: {a: '123', b: '456'}
573
+ }
574
+ };
575
+ config.setConfig({ortb2});
576
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
577
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
578
+ expect(data.user[param]).to.be.a('object');
579
+ expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}});
580
+ config.setConfig({ortb2: {}});
581
+ });
582
+ });
583
+
584
+ // Should allow valid user.data && site.content.data
585
+ const VALID_USER_DATA_STRINGS = ['id', 'name'];
586
+ VALID_USER_DATA_STRINGS.forEach(param => {
587
+ it(`should allow supported user.data & site.content.data strings to be added to the bid-request: ${JSON.stringify(param)}`, () => {
588
+ const ortb2 = {
589
+ user: {
590
+ data: [{[param]: 'string'}]
591
+ },
592
+ site: {
593
+ content: {
594
+ data: [{[param]: 'string'}]
595
+ }
596
+ }
597
+ };
598
+ config.setConfig({ortb2});
599
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
600
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
601
+ expect(data.user.data[0][param]).to.exist;
602
+ expect(data.user.data[0][param]).to.be.a('string');
603
+ expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]);
604
+ expect(data.site.content.data[0][param]).to.exist;
605
+ expect(data.site.content.data[0][param]).to.be.a('string');
606
+ expect(data.site.content.data[0][param]).to.be.equal(ortb2.site.content.data[0][param]);
607
+ });
608
+ });
609
+
610
+ const VALID_USER_DATA_ARRAYS = ['segment']
611
+ VALID_USER_DATA_ARRAYS.forEach(param => {
612
+ it(`should allow supported user data arrays to be added to the bid-request: ${JSON.stringify(param)}`, () => {
613
+ const ortb2 = {
614
+ user: {
615
+ data: [{[param]: [{id: 1}]}]
616
+ }
617
+ };
618
+ config.setConfig({ortb2});
619
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
620
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
621
+ expect(data.user.data[0][param]).to.exist;
622
+ expect(data.user.data[0][param]).to.be.a('array');
623
+ expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]);
624
+ });
625
+ });
626
+
627
+ const VALID_USER_DATA_OBJECTS = ['ext'];
628
+ VALID_USER_DATA_OBJECTS.forEach(param => {
629
+ it(`should allow supported user data objects to be added to the bid-request: ${JSON.stringify(param)}`, () => {
630
+ const ortb2 = {
631
+ user: {
632
+ data: [{[param]: {id: 'ext'}}]
633
+ }
634
+ };
635
+ config.setConfig({ortb2});
636
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
637
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
638
+ expect(data.user.data[0][param]).to.exist;
639
+ expect(data.user.data[0][param]).to.be.a('object');
640
+ expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]);
641
+ config.setConfig({ortb2: {}});
642
+ });
643
+ });
644
+
645
+ // adUnit.ortb2Imp.ext.data
646
+ it(`should allow adUnit.ortb2Imp.ext.data object to be added to the bid-request`, () => {
647
+ let { validBidRequests, bidderRequest } = generateBuildRequestMock({})
648
+ validBidRequests[0].ortb2Imp = {
649
+ ext: {
650
+ data: {
651
+ pbadslot: 'homepage-top-rect',
652
+ adUnitSpecificAttribute: '123'
653
+ }
654
+ }
655
+ };
656
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
657
+ expect(data.imp[0].ext.data).to.deep.equal(validBidRequests[0].ortb2Imp.ext.data);
658
+ });
659
+ });
660
+
661
+ describe('e2etest mode support:', () => {
662
+ it(`should override DCN & POS when params.testing.e2etest = "true".`, () => {
663
+ const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({});
664
+ const params = {
665
+ dcn: '1234',
666
+ pos: '5678',
667
+ testing: {
668
+ e2etest: true
669
+ }
670
+ }
671
+ bidRequest.params = params;
672
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
673
+ expect(data.site.id).to.be.equal('8a969516017a7a396ec539d97f540011');
674
+ expect(data.imp[0].tagid).to.be.equal('8a969978017a7aaabab4ab0bc01a0009');
675
+ expect(data.imp[0].ext.e2eTestMode).to.be.true;
676
+ });
677
+ });
678
+
679
+ describe('GDPR & Consent:', () => {
680
+ it('should return request objects that do not send cookies if purpose 1 consent is not provided', () => {
681
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
682
+ bidderRequest.gdprConsent = {
683
+ consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA',
684
+ apiVersion: 2,
685
+ vendorData: {
686
+ purpose: {
687
+ consents: {
688
+ '1': false
689
+ }
690
+ }
691
+ },
692
+ gdprApplies: true
693
+ };
694
+ const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options;
695
+ expect(options.withCredentials).to.be.false;
696
+ });
697
+ });
698
+
699
+ describe('Endpoint & Impression Request Mode:', () => {
700
+ it('should route request to config override endpoint', () => {
701
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
702
+ const testOverrideEndpoint = 'http://foo.bar.baz.com/bidderRequest';
703
+ config.setConfig({
704
+ yahoossp: {
705
+ endpoint: testOverrideEndpoint
706
+ }
707
+ });
708
+ const response = spec.buildRequests(validBidRequests, bidderRequest)[0];
709
+ expect(response).to.deep.include(
710
+ {
711
+ method: 'POST',
712
+ url: testOverrideEndpoint
713
+ });
714
+ });
715
+
716
+ it('should route request to /bidRequest endpoint when dcn & pos present', () => {
717
+ config.setConfig({
718
+ yahoossp: {}
719
+ });
720
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
721
+ const response = spec.buildRequests(validBidRequests, bidderRequest);
722
+ expect(response[0]).to.deep.include({
723
+ method: 'POST',
724
+ url: 'https://c2shb.pubgw.yahoo.com/bidRequest'
725
+ });
726
+ });
727
+
728
+ it('should route request to /partners endpoint when pubId is present', () => {
729
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true});
730
+ const response = spec.buildRequests(validBidRequests, bidderRequest);
731
+ expect(response[0]).to.deep.include({
732
+ method: 'POST',
733
+ url: 'https://c2shb.pubgw.yahoo.com/admax/bid/partners/PBJS'
734
+ });
735
+ });
736
+
737
+ it('should return a single request object for single request mode', () => {
738
+ let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({});
739
+ const BID_ID_2 = '84ab50xxxxx';
740
+ const BID_POS_2 = 'footer';
741
+ const AD_UNIT_CODE_2 = 'test-ad-unit-code-123';
742
+ const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2});
743
+ validBidRequests = [bidRequest, bidRequest2];
744
+ bidderRequest.bids = validBidRequests;
745
+
746
+ config.setConfig({
747
+ yahoossp: {
748
+ singleRequestMode: true
749
+ }
750
+ });
751
+
752
+ const data = spec.buildRequests(validBidRequests, bidderRequest).data;
753
+ expect(data.imp).to.be.an('array').with.lengthOf(2);
754
+
755
+ expect(data.imp[0]).to.deep.include({
756
+ id: DEFAULT_BID_ID,
757
+ ext: {
758
+ pos: DEFAULT_BID_POS,
759
+ dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE
760
+ }
761
+ });
762
+
763
+ expect(data.imp[1]).to.deep.include({
764
+ id: BID_ID_2,
765
+ ext: {
766
+ pos: BID_POS_2,
767
+ dfp_ad_unit_code: AD_UNIT_CODE_2
768
+ }
769
+ });
770
+ });
771
+ });
772
+
773
+ describe('Validate request filtering:', () => {
774
+ it('should not return request when no bids are present', function () {
775
+ let request = spec.buildRequests([]);
776
+ expect(request).to.be.undefined;
777
+ });
778
+
779
+ it('buildRequests(): should return an array with the correct amount of request objects', () => {
780
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
781
+ const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest;
782
+ expect(response.bids).to.be.an('array').to.have.lengthOf(1);
783
+ });
784
+ });
785
+
786
+ describe('Request Headers validation:', () => {
787
+ it('should return request objects with the relevant custom headers and content type declaration', () => {
788
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
789
+ const options = spec.buildRequests(validBidRequests, bidderRequest).options;
790
+ expect(options).to.deep.equal(
791
+ {
792
+ contentType: 'application/json',
793
+ customHeaders: {
794
+ 'x-openrtb-version': '2.5'
795
+ },
796
+ withCredentials: true
797
+ });
798
+ });
799
+ });
800
+
801
+ describe('Request Payload oRTB bid validation:', () => {
802
+ it('should generate a valid openRTB bid-request object in the data field', () => {
803
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
804
+ const data = spec.buildRequests(validBidRequests, bidderRequest).data;
805
+ expect(data.site).to.deep.equal({
806
+ id: bidderRequest.bids[0].params.dcn,
807
+ page: bidderRequest.refererInfo.referer
808
+ });
809
+
810
+ expect(data.device).to.deep.equal({
811
+ dnt: 0,
812
+ ua: navigator.userAgent,
813
+ ip: undefined
814
+ });
815
+
816
+ expect(data.regs).to.deep.equal({
817
+ ext: {
818
+ 'us_privacy': '',
819
+ gdpr: 1
820
+ }
821
+ });
822
+
823
+ expect(data.source).to.deep.equal({
824
+ ext: {
825
+ hb: 1,
826
+ adapterver: ADAPTER_VERSION,
827
+ prebidver: PREBID_VERSION,
828
+ integration: {
829
+ name: INTEGRATION_METHOD,
830
+ ver: PREBID_VERSION
831
+ }
832
+ },
833
+ fd: 1
834
+ });
835
+
836
+ expect(data.user).to.deep.equal({
837
+ ext: {
838
+ consent: bidderRequest.gdprConsent.consentString,
839
+ eids: []
840
+ }
841
+ });
842
+
843
+ expect(data.cur).to.deep.equal(['USD']);
844
+ });
845
+
846
+ it('should generate a valid openRTB imp.ext object in the bid-request', () => {
847
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
848
+ const bid = validBidRequests[0];
849
+ const data = spec.buildRequests(validBidRequests, bidderRequest).data;
850
+ expect(data.imp[0].ext).to.deep.equal({
851
+ pos: bid.params.pos,
852
+ dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE
853
+ });
854
+ });
855
+
856
+ it('should use siteId value as site.id in the outbound bid-request when using "pubId" integration mode', () => {
857
+ let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true});
858
+ validBidRequests[0].params.siteId = '1234567';
859
+ const data = spec.buildRequests(validBidRequests, bidderRequest).data;
860
+ expect(data.site.id).to.equal('1234567');
861
+ });
862
+
863
+ it('should use placementId value as imp.tagid in the outbound bid-request when using "pubId" integration mode', () => {
864
+ let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true});
865
+ validBidRequests[0].params.placementId = 'header-300x250';
866
+ const data = spec.buildRequests(validBidRequests, bidderRequest).data;
867
+ expect(data.imp[0].tagid).to.deep.equal('header-300x250');
868
+ });
869
+ });
870
+
871
+ describe('Request Payload oRTB bid.imp validation:', () => {
872
+ // Validate Banner imp imp when yahoossp.mode=undefined
873
+ it('should generate a valid "Banner" imp object', () => {
874
+ config.setConfig({
875
+ yahoossp: {}
876
+ });
877
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
878
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
879
+ expect(data.imp[0].video).to.not.exist;
880
+ expect(data.imp[0].banner).to.deep.equal({
881
+ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'],
882
+ format: [{w: 300, h: 250}, {w: 300, h: 600}]
883
+ });
884
+ });
885
+
886
+ // Validate Banner imp when yahoossp.mode="banner"
887
+ it('should generate a valid "Banner" imp object', () => {
888
+ config.setConfig({
889
+ yahoossp: { mode: 'banner' }
890
+ });
891
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({});
892
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
893
+ expect(data.imp[0].video).to.not.exist;
894
+ expect(data.imp[0].banner).to.deep.equal({
895
+ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'],
896
+ format: [{w: 300, h: 250}, {w: 300, h: 600}]
897
+ });
898
+ });
899
+
900
+ // Validate Video imp
901
+ it('should generate a valid "Video" only imp object', () => {
902
+ config.setConfig({
903
+ yahoossp: { mode: 'video' }
904
+ });
905
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'});
906
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
907
+ expect(data.imp[0].banner).to.not.exist;
908
+ expect(data.imp[0].video).to.deep.equal({
909
+ mimes: ['video/mp4', 'application/javascript'],
910
+ w: 300,
911
+ h: 250,
912
+ api: [2],
913
+ protocols: [2, 5],
914
+ startdelay: 0,
915
+ linearity: 1,
916
+ maxbitrate: undefined,
917
+ maxduration: undefined,
918
+ minduration: undefined,
919
+ delivery: undefined,
920
+ pos: undefined,
921
+ playbackmethod: undefined,
922
+ rewarded: undefined,
923
+ placement: undefined
924
+ });
925
+ });
926
+
927
+ // Validate multi-format Video+banner imp
928
+ it('should generate a valid multi-format "Video + Banner" imp object', () => {
929
+ config.setConfig({
930
+ yahoossp: { mode: 'all' }
931
+ });
932
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'multi-format'});
933
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
934
+ expect(data.imp[0].banner).to.deep.equal({
935
+ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'],
936
+ format: [{w: 300, h: 250}, {w: 300, h: 600}]
937
+ });
938
+ expect(data.imp[0].video).to.deep.equal({
939
+ mimes: ['video/mp4', 'application/javascript'],
940
+ w: 300,
941
+ h: 250,
942
+ api: [2],
943
+ protocols: [2, 5],
944
+ startdelay: 0,
945
+ linearity: 1,
946
+ maxbitrate: undefined,
947
+ maxduration: undefined,
948
+ minduration: undefined,
949
+ delivery: undefined,
950
+ pos: undefined,
951
+ playbackmethod: undefined,
952
+ rewarded: undefined,
953
+ placement: undefined
954
+ });
955
+ });
956
+
957
+ // Validate Key-Value Pairs
958
+ it('should generate supported String, Number, Array of Strings, Array of Numbers key-value pairs and append to imp.ext.kvs', () => {
959
+ let { validBidRequests, bidderRequest } = generateBuildRequestMock({})
960
+ validBidRequests[0].params.kvp = {
961
+ key1: 'String',
962
+ key2: 123456,
963
+ key3: ['String', 'String', 'String'],
964
+ key4: [1, 2, 3],
965
+ invalidKey1: true,
966
+ invalidKey2: null,
967
+ invalidKey3: ['string', 1234],
968
+ invalidKey4: {a: 1, b: 2},
969
+ invalidKey5: undefined
970
+ };
971
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
972
+
973
+ expect(data.imp[0].ext.kvs).to.deep.equal({
974
+ key1: 'String',
975
+ key2: 123456,
976
+ key3: ['String', 'String', 'String'],
977
+ key4: [1, 2, 3]
978
+ });
979
+ });
980
+ });
981
+
982
+ describe('Multiple adUnit validations:', () => {
983
+ // Multiple banner adUnits
984
+ it('should generate multiple bid-requests for each adUnit - 2 banner only', () => {
985
+ config.setConfig({
986
+ yahoossp: { mode: 'banner' }
987
+ });
988
+
989
+ const BID_ID_2 = '84ab50xxxxx';
990
+ const BID_POS_2 = 'footer';
991
+ const AD_UNIT_CODE_2 = 'test-ad-unit-code-123';
992
+ const BID_ID_3 = '84ab50yyyyy';
993
+ const BID_POS_3 = 'hero';
994
+ const AD_UNIT_CODE_3 = 'video-ad-unit';
995
+
996
+ let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); // banner
997
+ const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2}); // banner
998
+ const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3, adUnitType: 'video'}); // video (should be filtered)
999
+ validBidRequests = [bidRequest, bidRequest2, bidRequest3];
1000
+ bidderRequest.bids = validBidRequests;
1001
+
1002
+ const response = spec.buildRequests(validBidRequests, bidderRequest)
1003
+ expect(response).to.be.a('array');
1004
+ expect(response.length).to.equal(2);
1005
+ response.forEach((obj) => {
1006
+ expect(obj.data.imp[0].video).to.not.exist
1007
+ expect(obj.data.imp[0].banner).to.deep.equal({
1008
+ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'],
1009
+ format: [{w: 300, h: 250}, {w: 300, h: 600}]
1010
+ });
1011
+ });
1012
+ });
1013
+
1014
+ // Multiple video adUnits
1015
+ it('should generate multiple bid-requests for each adUnit - 2 video only', () => {
1016
+ config.setConfig({
1017
+ yahoossp: { mode: 'video' }
1018
+ });
1019
+ const BID_ID_2 = '84ab50xxxxx';
1020
+ const BID_POS_2 = 'footer';
1021
+ const AD_UNIT_CODE_2 = 'test-ad-unit-code-123';
1022
+ const BID_ID_3 = '84ab50yyyyy';
1023
+ const BID_POS_3 = 'hero';
1024
+ const AD_UNIT_CODE_3 = 'video-ad-unit';
1025
+
1026
+ let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); // video
1027
+ const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video
1028
+ const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3}); // banner (should be filtered)
1029
+ validBidRequests = [bidRequest, bidRequest2, bidRequest3];
1030
+ bidderRequest.bids = validBidRequests;
1031
+
1032
+ const response = spec.buildRequests(validBidRequests, bidderRequest)
1033
+ expect(response).to.be.a('array');
1034
+ expect(response.length).to.equal(2);
1035
+ response.forEach((obj) => {
1036
+ expect(obj.data.imp[0].banner).to.not.exist
1037
+ expect(obj.data.imp[0].video).to.deep.equal({
1038
+ mimes: ['video/mp4', 'application/javascript'],
1039
+ w: 300,
1040
+ h: 250,
1041
+ api: [2],
1042
+ protocols: [2, 5],
1043
+ startdelay: 0,
1044
+ linearity: 1,
1045
+ maxbitrate: undefined,
1046
+ maxduration: undefined,
1047
+ minduration: undefined,
1048
+ delivery: undefined,
1049
+ pos: undefined,
1050
+ playbackmethod: undefined,
1051
+ rewarded: undefined,
1052
+ placement: undefined
1053
+ });
1054
+ });
1055
+ });
1056
+ // Mixed adUnits 1-banner, 1-video, 1-native (should filter out native)
1057
+ it('should generate multiple bid-requests for both "video & banner" adUnits', () => {
1058
+ config.setConfig({
1059
+ yahoossp: { mode: 'all' }
1060
+ });
1061
+ const BID_ID_2 = '84ab50xxxxx';
1062
+ const BID_POS_2 = 'footer';
1063
+ const AD_UNIT_CODE_2 = 'video-ad-unit';
1064
+ const BID_ID_3 = '84ab50yyyyy';
1065
+ const BID_POS_3 = 'hero';
1066
+ const AD_UNIT_CODE_3 = 'native-ad-unit';
1067
+
1068
+ let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'banner'}); // banner
1069
+ const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video
1070
+ const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3, adUnitType: 'native'}); // native (should be filtered)
1071
+ validBidRequests = [bidRequest, bidRequest2, bidRequest3];
1072
+ bidderRequest.bids = validBidRequests;
1073
+
1074
+ const response = spec.buildRequests(validBidRequests, bidderRequest);
1075
+ expect(response).to.be.a('array');
1076
+ expect(response.length).to.equal(2);
1077
+ response.forEach((obj) => {
1078
+ expect(obj.data.imp[0].native).to.not.exist;
1079
+ });
1080
+
1081
+ const data1 = response[0].data;
1082
+ expect(data1.imp[0].video).to.not.exist;
1083
+ expect(data1.imp[0].banner).to.deep.equal({
1084
+ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'],
1085
+ format: [{w: 300, h: 250}, {w: 300, h: 600}]
1086
+ });
1087
+
1088
+ const data2 = response[1].data;
1089
+ expect(data2.imp[0].banner).to.not.exist;
1090
+ expect(data2.imp[0].video).to.deep.equal({
1091
+ mimes: ['video/mp4', 'application/javascript'],
1092
+ w: 300,
1093
+ h: 250,
1094
+ api: [2],
1095
+ protocols: [2, 5],
1096
+ startdelay: 0,
1097
+ linearity: 1,
1098
+ maxbitrate: undefined,
1099
+ maxduration: undefined,
1100
+ minduration: undefined,
1101
+ delivery: undefined,
1102
+ pos: undefined,
1103
+ playbackmethod: undefined,
1104
+ rewarded: undefined,
1105
+ placement: undefined
1106
+ });
1107
+ });
1108
+ });
1109
+
1110
+ describe('Video params firstlook & bidOverride validations:', () => {
1111
+ it('should first look at params.bidOverride for video placement data', () => {
1112
+ config.setConfig({
1113
+ yahoossp: { mode: 'video' }
1114
+ });
1115
+ const bidOverride = {
1116
+ imp: {
1117
+ video: {
1118
+ mimes: ['video/mp4'],
1119
+ w: 400,
1120
+ h: 350,
1121
+ api: [1],
1122
+ protocols: [1, 3],
1123
+ startdelay: 0,
1124
+ linearity: 1,
1125
+ maxbitrate: 400000,
1126
+ maxduration: 3600,
1127
+ minduration: 1500,
1128
+ delivery: 1,
1129
+ pos: 123456,
1130
+ playbackmethod: 1,
1131
+ rewarded: 1,
1132
+ placement: 1
1133
+ }
1134
+ }
1135
+ }
1136
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride});
1137
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
1138
+ expect(data.imp[0].video).to.deep.equal(bidOverride.imp.video);
1139
+ });
1140
+
1141
+ it('should second look at bid.mediaTypes.video for video placement data', () => {
1142
+ config.setConfig({
1143
+ yahoossp: { mode: 'video' }
1144
+ });
1145
+ let { bidRequest, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'});
1146
+ bidRequest.mediaTypes.video = {
1147
+ mimes: ['video/mp4'],
1148
+ playerSize: [400, 350],
1149
+ api: [1],
1150
+ protocols: [1, 3],
1151
+ startdelay: 0,
1152
+ linearity: 1,
1153
+ maxbitrate: 400000,
1154
+ maxduration: 3600,
1155
+ minduration: 1500,
1156
+ delivery: 1,
1157
+ pos: 123456,
1158
+ playbackmethod: 1,
1159
+ placement: 1
1160
+ }
1161
+ const validBidRequests = [bidRequest];
1162
+ bidderRequest.bids = validBidRequests;
1163
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
1164
+ expect(data.imp[0].video).to.deep.equal({
1165
+ mimes: ['video/mp4'],
1166
+ w: 400,
1167
+ h: 350,
1168
+ api: [1],
1169
+ protocols: [1, 3],
1170
+ startdelay: 0,
1171
+ linearity: 1,
1172
+ maxbitrate: 400000,
1173
+ maxduration: 3600,
1174
+ minduration: 1500,
1175
+ delivery: 1,
1176
+ pos: 123456,
1177
+ playbackmethod: 1,
1178
+ placement: 1,
1179
+ rewarded: undefined
1180
+ });
1181
+ });
1182
+
1183
+ it('should use params.bidOverride.device.ip override', () => {
1184
+ config.setConfig({
1185
+ yahoossp: { mode: 'all' }
1186
+ });
1187
+ const bidOverride = {
1188
+ device: {
1189
+ ip: '1.2.3.4'
1190
+ }
1191
+ }
1192
+ const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride});
1193
+ const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data;
1194
+ expect(data.device.ip).to.deep.equal(bidOverride.device.ip);
1195
+ });
1196
+ });
1197
+ // #endregion buildRequests():
1198
+
1199
+ describe('interpretResponse()', () => {
1200
+ describe('for mediaTypes: "banner"', () => {
1201
+ it('should insert banner payload into response[0].ad', () => {
1202
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1203
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1204
+ expect(response[0].ad).to.equal('<script>logInfo(\'ad\'); AdPlacement </script>');
1205
+ expect(response[0].mediaType).to.equal('banner');
1206
+ })
1207
+ });
1208
+
1209
+ describe('for mediaTypes: "video"', () => {
1210
+ it('should insert video VPAID payload into vastXml', () => {
1211
+ const { serverResponse, bidderRequest } = generateResponseMock('video');
1212
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1213
+ expect(response[0].ad).to.be.undefined;
1214
+ expect(response[0].vastXml).to.equal('<VAST></VAST>');
1215
+ expect(response[0].mediaType).to.equal('video');
1216
+ })
1217
+
1218
+ it('should insert video VAST win notification into vastUrl', () => {
1219
+ const { serverResponse, bidderRequest } = generateResponseMock('video', 'vast');
1220
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1221
+ expect(response[0].ad).to.be.undefined;
1222
+ expect(response[0].vastUrl).to.equal('https://yahoo.com?event=adAttempt');
1223
+ expect(response[0].vastXml).to.equal('<VAST></VAST>');
1224
+ expect(response[0].mediaType).to.equal('video');
1225
+ })
1226
+
1227
+ it('should insert video DAP O2 Player into ad', () => {
1228
+ const { serverResponse, bidderRequest } = generateResponseMock('dap-o2', 'vpaid');
1229
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1230
+ expect(response[0].ad).to.equal('<script>o2playerSettings</script>');
1231
+ expect(response[0].vastUrl).to.be.undefined;
1232
+ expect(response[0].vastXml).to.be.undefined;
1233
+ expect(response[0].mediaType).to.equal('banner');
1234
+ });
1235
+
1236
+ it('should insert video DAP Unified Player into ad', () => {
1237
+ const { serverResponse, bidderRequest } = generateResponseMock('dap-up', 'vpaid');
1238
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1239
+ expect(response[0].ad).to.equal('<script>YAHOO.VideoPlatform.VideoPlayer</script>');
1240
+ expect(response[0].vastUrl).to.be.undefined;
1241
+ expect(response[0].vastXml).to.be.undefined;
1242
+ expect(response[0].mediaType).to.equal('banner');
1243
+ })
1244
+ });
1245
+
1246
+ describe('Support Advertiser domains', () => {
1247
+ it('should append bid-response adomain to meta.advertiserDomains', () => {
1248
+ const { serverResponse, bidderRequest } = generateResponseMock('video', 'vpaid');
1249
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1250
+ expect(response[0].meta.advertiserDomains).to.be.a('array');
1251
+ expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com');
1252
+ })
1253
+ });
1254
+
1255
+ describe('bid response Ad ID / Creative ID', () => {
1256
+ it('should use adId if it exists in the bid-response', () => {
1257
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1258
+ const adId = 'bid-response-adId';
1259
+ serverResponse.body.seatbid[0].bid[0].adId = adId;
1260
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1261
+ expect(response[0].adId).to.equal(adId);
1262
+ });
1263
+
1264
+ it('should use impid if adId does not exist in the bid-response', () => {
1265
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1266
+ const impid = '25b6c429c1f52f';
1267
+ serverResponse.body.seatbid[0].bid[0].impid = impid;
1268
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1269
+ expect(response[0].adId).to.equal(impid);
1270
+ });
1271
+
1272
+ it('should use crid if adId & impid do not exist in the bid-response', () => {
1273
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1274
+ const crid = 'passback-12579';
1275
+ serverResponse.body.seatbid[0].bid[0].impid = undefined;
1276
+ serverResponse.body.seatbid[0].bid[0].crid = crid;
1277
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1278
+ expect(response[0].adId).to.equal(crid);
1279
+ });
1280
+ });
1281
+
1282
+ describe('Time To Live (ttl)', () => {
1283
+ const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined];
1284
+ UNSUPPORTED_TTL_FORMATS.forEach(param => {
1285
+ it('should not allow unsupported global yahoossp.ttl formats and default to 300', () => {
1286
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1287
+ config.setConfig({
1288
+ yahoossp: { ttl: param }
1289
+ });
1290
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1291
+ expect(response[0].ttl).to.equal(300);
1292
+ });
1293
+
1294
+ it('should not allow unsupported params.ttl formats and default to 300', () => {
1295
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1296
+ bidderRequest.bids[0].params.ttl = param;
1297
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1298
+ expect(response[0].ttl).to.equal(300);
1299
+ });
1300
+ });
1301
+
1302
+ const UNSUPPORTED_TTL_VALUES = [-1, 3601];
1303
+ UNSUPPORTED_TTL_VALUES.forEach(param => {
1304
+ it('should not allow invalid global yahoossp.ttl values 3600 < ttl < 0 and default to 300', () => {
1305
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1306
+ config.setConfig({
1307
+ yahoossp: { ttl: param }
1308
+ });
1309
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1310
+ expect(response[0].ttl).to.equal(300);
1311
+ });
1312
+
1313
+ it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => {
1314
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1315
+ bidderRequest.bids[0].params.ttl = param;
1316
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1317
+ expect(response[0].ttl).to.equal(300);
1318
+ });
1319
+ });
1320
+
1321
+ it('should give presedence to Gloabl ttl over params.ttl ', () => {
1322
+ const { serverResponse, bidderRequest } = generateResponseMock('banner');
1323
+ config.setConfig({
1324
+ yahoossp: { ttl: 500 }
1325
+ });
1326
+ bidderRequest.bids[0].params.ttl = 400;
1327
+ const response = spec.interpretResponse(serverResponse, {bidderRequest});
1328
+ expect(response[0].ttl).to.equal(500);
1329
+ });
1330
+ });
1331
+ });
1332
+ });