prebid.js 5.19.0 → 5.20.3

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 (67) hide show
  1. package/.circleci/config.yml +4 -5
  2. package/modules/adxcgBidAdapter.js +311 -359
  3. package/modules/airgridRtdProvider.js +1 -1
  4. package/modules/appnexusBidAdapter.js +9 -3
  5. package/modules/atsAnalyticsAdapter.js +67 -46
  6. package/modules/atsAnalyticsAdapter.md +1 -0
  7. package/modules/betweenBidAdapter.js +2 -1
  8. package/modules/browsiRtdProvider.js +106 -18
  9. package/modules/cleanioRtdProvider.js +192 -0
  10. package/modules/cleanioRtdProvider.md +59 -0
  11. package/modules/deltaprojectsBidAdapter.js +252 -0
  12. package/modules/deltaprojectsBidAdapter.md +32 -0
  13. package/modules/gridBidAdapter.js +1 -0
  14. package/modules/ipromBidAdapter.js +79 -0
  15. package/modules/ixBidAdapter.js +7 -1
  16. package/modules/jixieBidAdapter.js +8 -2
  17. package/modules/justpremiumBidAdapter.js +6 -1
  18. package/modules/kinessoIdSystem.js +1 -1
  19. package/modules/livewrappedAnalyticsAdapter.js +5 -0
  20. package/modules/multibid/index.js +3 -3
  21. package/modules/nativoBidAdapter.js +5 -1
  22. package/modules/openxBidAdapter.js +1 -1
  23. package/modules/operaadsBidAdapter.js +21 -1
  24. package/modules/otmBidAdapter.js +146 -0
  25. package/modules/otmBidAdapter.md +27 -26
  26. package/modules/outbrainBidAdapter.js +5 -0
  27. package/modules/playwireBidAdapter.md +61 -0
  28. package/modules/pubmaticBidAdapter.js +1 -1
  29. package/modules/rtdModule/index.js +2 -2
  30. package/modules/sonobiBidAdapter.js +7 -0
  31. package/modules/sortableBidAdapter.js +1 -0
  32. package/modules/teadsBidAdapter.js +3 -0
  33. package/modules/trustxBidAdapter.js +8 -6
  34. package/modules/ventesBidAdapter.js +370 -0
  35. package/modules/ventesBidAdapter.md +94 -0
  36. package/modules/yahoosspBidAdapter.js +6 -6
  37. package/package.json +1 -1
  38. package/src/auction.js +11 -11
  39. package/src/prebid.js +20 -4
  40. package/src/targeting.js +8 -0
  41. package/test/fixtures/fixtures.js +2 -1
  42. package/test/spec/modules/adxcgBidAdapter_spec.js +827 -571
  43. package/test/spec/modules/appnexusBidAdapter_spec.js +16 -1
  44. package/test/spec/modules/atsAnalyticsAdapter_spec.js +42 -9
  45. package/test/spec/modules/browsiRtdProvider_spec.js +62 -7
  46. package/test/spec/modules/cleanioRtdProvider_spec.js +188 -0
  47. package/test/spec/modules/deltaprojectsBidAdapter_spec.js +399 -0
  48. package/test/spec/modules/eplanningBidAdapter_spec.js +8 -8
  49. package/test/spec/modules/ipromBidAdapter_spec.js +195 -0
  50. package/test/spec/modules/ixBidAdapter_spec.js +3 -3
  51. package/test/spec/modules/jixieBidAdapter_spec.js +13 -11
  52. package/test/spec/modules/justpremiumBidAdapter_spec.js +9 -2
  53. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +23 -4
  54. package/test/spec/modules/multibid_spec.js +31 -31
  55. package/test/spec/modules/openxBidAdapter_spec.js +0 -26
  56. package/test/spec/modules/operaadsBidAdapter_spec.js +38 -6
  57. package/test/spec/modules/otmBidAdapter_spec.js +67 -0
  58. package/test/spec/modules/outbrainBidAdapter_spec.js +18 -0
  59. package/test/spec/modules/pubmaticBidAdapter_spec.js +40 -0
  60. package/test/spec/modules/sonobiBidAdapter_spec.js +34 -1
  61. package/test/spec/modules/sortableBidAdapter_spec.js +11 -0
  62. package/test/spec/modules/teadsBidAdapter_spec.js +132 -0
  63. package/test/spec/modules/trustxBidAdapter_spec.js +3 -3
  64. package/test/spec/modules/ventesBidAdapter_spec.js +845 -0
  65. package/test/spec/unit/core/adapterManager_spec.js +7 -3
  66. package/test/spec/unit/core/targeting_spec.js +93 -0
  67. package/test/spec/unit/pbjs_api_spec.js +3 -1
@@ -1047,7 +1047,8 @@ describe('AppNexusAdapter', function () {
1047
1047
  'trackers': [
1048
1048
  {
1049
1049
  'impression_urls': [
1050
- 'https://lax1-ib.adnxs.com/impression'
1050
+ 'https://lax1-ib.adnxs.com/impression',
1051
+ 'https://www.test.com/tracker'
1051
1052
  ],
1052
1053
  'video_events': {}
1053
1054
  }
@@ -1334,6 +1335,20 @@ describe('AppNexusAdapter', function () {
1334
1335
  expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']);
1335
1336
  });
1336
1337
 
1338
+ it('should add brand id', function() {
1339
+ let responseBrandId = deepClone(response);
1340
+ responseBrandId.tags[0].ads[0].brand_id = 123;
1341
+
1342
+ let bidderRequest = {
1343
+ bids: [{
1344
+ bidId: '3db3773286ee59',
1345
+ adUnitCode: 'code'
1346
+ }]
1347
+ }
1348
+ let result = spec.interpretResponse({ body: responseBrandId }, {bidderRequest});
1349
+ expect(Object.keys(result[0].meta)).to.include.members(['brandId']);
1350
+ });
1351
+
1337
1352
  it('should add advertiserDomains', function() {
1338
1353
  let responseAdvertiserId = deepClone(response);
1339
1354
  responseAdvertiserId.tags[0].ads[0].adomain = ['123'];
@@ -12,11 +12,15 @@ let constants = require('src/constants.json');
12
12
 
13
13
  export const storage = getStorageManager();
14
14
  let sandbox;
15
+ let clock;
16
+ let now = new Date();
17
+
15
18
  describe('ats analytics adapter', function () {
16
19
  beforeEach(function () {
17
20
  sinon.stub(events, 'getEvents').returns([]);
18
21
  storage.setCookie('_lr_env_src_ats', 'true', 'Thu, 01 Jan 1970 00:00:01 GMT');
19
22
  sandbox = sinon.sandbox.create();
23
+ clock = sandbox.useFakeTimers(now.getTime());
20
24
  });
21
25
 
22
26
  afterEach(function () {
@@ -25,18 +29,20 @@ describe('ats analytics adapter', function () {
25
29
  atsAnalyticsAdapter.disableAnalytics();
26
30
  Math.random.restore();
27
31
  sandbox.restore();
32
+ clock.restore();
28
33
  });
29
34
 
30
35
  describe('track', function () {
31
36
  it('builds and sends request and response data', function () {
32
37
  sinon.stub(Math, 'random').returns(0.99);
33
- sinon.stub(atsAnalyticsAdapter, 'shouldFireRequest').returns(true);
34
38
  sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25');
35
- let now = new Date();
39
+
36
40
  now.setTime(now.getTime() + 3600000);
37
41
  storage.setCookie('_lr_env_src_ats', 'true', now.toUTCString());
38
42
  storage.setCookie('_lr_sampling_rate', '10', now.toUTCString());
39
43
 
44
+ this.timeout(2100);
45
+
40
46
  let initOptions = {
41
47
  pid: '10433394'
42
48
  };
@@ -62,7 +68,7 @@ describe('ats analytics adapter', function () {
62
68
  'refererInfo': {
63
69
  'referer': 'https://example.com/dev'
64
70
  },
65
- 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7',
71
+ 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7'
66
72
  };
67
73
  // prepare general auction - response
68
74
  let bidResponse = {
@@ -90,7 +96,7 @@ describe('ats analytics adapter', function () {
90
96
  let expectedAfterBid = {
91
97
  'Data': [{
92
98
  'has_envelope': true,
93
- 'adapter_version': 2,
99
+ 'adapter_version': 3,
94
100
  'bidder': 'appnexus',
95
101
  'bid_id': '30c77d079cdf17',
96
102
  'auction_id': 'a5b849e5-87d7-4205-8300-d063084fcfb7',
@@ -103,10 +109,30 @@ describe('ats analytics adapter', function () {
103
109
  'response_time_stamp': '2020-02-03T14:23:11.978Z',
104
110
  'currency': 'USD',
105
111
  'cpm': 0.5,
106
- 'net_revenue': true
112
+ 'net_revenue': true,
113
+ 'bid_won': true
107
114
  }]
108
115
  };
109
116
 
117
+ let wonRequest = {
118
+ 'adId': '2eddfdc0c791dc',
119
+ 'mediaType': 'banner',
120
+ 'requestId': '30c77d079cdf17',
121
+ 'cpm': 0.5,
122
+ 'creativeId': 29681110,
123
+ 'currency': 'USD',
124
+ 'netRevenue': true,
125
+ 'ttl': 300,
126
+ 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7',
127
+ 'statusMessage': 'Bid available',
128
+ 'responseTimestamp': 1633525319061,
129
+ 'requestTimestamp': 1633525319258,
130
+ 'bidder': 'appnexus',
131
+ 'adUnitCode': 'div-gpt-ad-1438287399331-0',
132
+ 'size': '300x250',
133
+ 'status': 'rendered'
134
+ };
135
+
110
136
  // lets simulate that some bidders timeout
111
137
  let bidTimeoutArgsV1 = [
112
138
  {
@@ -148,6 +174,14 @@ describe('ats analytics adapter', function () {
148
174
 
149
175
  // Step 5: Send auction end event
150
176
  events.emit(constants.EVENTS.AUCTION_END, {});
177
+ // Step 6: Send bid won event
178
+ events.emit(constants.EVENTS.BID_WON, wonRequest);
179
+
180
+ sandbox.stub($$PREBID_GLOBAL$$, 'getAllWinningBids').callsFake((key) => {
181
+ return [wonRequest]
182
+ });
183
+
184
+ clock.tick(2000);
151
185
 
152
186
  let requests = server.requests.filter(req => {
153
187
  return req.url.indexOf(analyticsUrl) > -1;
@@ -156,13 +190,12 @@ describe('ats analytics adapter', function () {
156
190
  expect(requests.length).to.equal(1);
157
191
 
158
192
  let realAfterBid = JSON.parse(requests[0].requestBody);
159
- // Step 6: assert real data after bid and expected data
193
+
194
+ // Step 7: assert real data after bid and expected data
160
195
  expect(realAfterBid['Data']).to.deep.equal(expectedAfterBid['Data']);
161
196
 
162
197
  // check that the publisher ID is configured via options
163
198
  expect(atsAnalyticsAdapter.context.pid).to.equal(initOptions.pid);
164
-
165
- atsAnalyticsAdapter.shouldFireRequest.restore();
166
199
  })
167
200
  it('check browser is safari', function () {
168
201
  sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25');
@@ -204,7 +237,7 @@ describe('ats analytics adapter', function () {
204
237
  sinon.stub(atsAnalyticsAdapter, 'getUserAgent').returns('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.25 (KHTML, like Gecko) Version/6.0 Safari/536.25');
205
238
  sinon.stub(Math, 'random').returns(0.99);
206
239
  // publisher can try to pass anything they want but we will set sampling rate to 100, which means we will have 1% of requests
207
- let result = atsAnalyticsAdapter.shouldFireRequest(10);
240
+ let result = atsAnalyticsAdapter.shouldFireRequest(8);
208
241
  expect(result).to.equal(true);
209
242
  })
210
243
  it('should not fire analytics request if math random is something other then 0.99', function () {
@@ -1,5 +1,6 @@
1
1
  import * as browsiRTD from '../../../modules/browsiRtdProvider.js';
2
2
  import {makeSlot} from '../integration/faker/googletag.js';
3
+ import * as utils from '../../../src/utils'
3
4
 
4
5
  describe('browsi Real time data sub module', function () {
5
6
  const conf = {
@@ -29,11 +30,11 @@ describe('browsi Real time data sub module', function () {
29
30
  });
30
31
 
31
32
  it('should match placement with ad unit', function () {
32
- const slot = makeSlot({code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1'});
33
+ const slot = makeSlot({code: '/123/abc', divId: 'browsiAd_1'});
33
34
 
34
- const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250']); // true
35
- const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true
36
- const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_Low']); // false
35
+ const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/123/abc']); // true
36
+ const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/123/abc', '/456/def']); // true
37
+ const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/123/def']); // false
37
38
  const test4 = browsiRTD.isIdMatchingAdUnit(slot, []); // true
38
39
 
39
40
  expect(test1).to.equal(true);
@@ -43,12 +44,12 @@ describe('browsi Real time data sub module', function () {
43
44
  });
44
45
 
45
46
  it('should return correct macro values', function () {
46
- const slot = makeSlot({code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1'});
47
+ const slot = makeSlot({code: '/123/abc', divId: 'browsiAd_1'});
47
48
 
48
49
  slot.setTargeting('test', ['test', 'value']);
49
50
  // slot getTargeting doesn't act like GPT so we can't expect real value
50
51
  const macroResult = browsiRTD.getMacroId({p: '<AD_UNIT>/<KEY_test>'}, slot);
51
- expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA');
52
+ expect(macroResult).to.equal('/123/abc/NA');
52
53
 
53
54
  const macroResultB = browsiRTD.getMacroId({}, slot);
54
55
  expect(macroResultB).to.equal('browsiAd_1');
@@ -72,7 +73,7 @@ describe('browsi Real time data sub module', function () {
72
73
  it('should return prediction from server', function () {
73
74
  makeSlot({code: 'hasPrediction', divId: 'hasPrediction'});
74
75
  const data = {
75
- p: {'hasPrediction': {p: 0.234}},
76
+ p: {'hasPrediction': {ps: {0: 0.234}}},
76
77
  kn: 'bv',
77
78
  pmd: undefined
78
79
  };
@@ -80,4 +81,58 @@ describe('browsi Real time data sub module', function () {
80
81
  expect(browsiRTD.browsiSubmodule.getTargetingData(['hasPrediction'])).to.eql({hasPrediction: {bv: '0.20'}});
81
82
  })
82
83
  })
84
+
85
+ describe('should return matching prediction', function () {
86
+ const predictions = {
87
+ 0: 0.123,
88
+ 1: 0.254,
89
+ 3: 0,
90
+ 4: 0.8
91
+ }
92
+ const singlePrediction = {
93
+ 0: 0.123
94
+ }
95
+ it('should return raw value if valid', function () {
96
+ expect(browsiRTD.getCurrentData(predictions, 0)).to.equal(0.123);
97
+ expect(browsiRTD.getCurrentData(predictions, 1)).to.equal(0.254);
98
+ })
99
+ it('should return 0 for prediction = 0', function () {
100
+ expect(browsiRTD.getCurrentData(predictions, 3)).to.equal(0);
101
+ })
102
+ it('should return -1 for invalid params', function () {
103
+ expect(browsiRTD.getCurrentData(null, 3)).to.equal(-1);
104
+ expect(browsiRTD.getCurrentData(predictions, null)).to.equal(-1);
105
+ })
106
+ it('should return prediction according to object keys length ', function () {
107
+ expect(browsiRTD.getCurrentData(singlePrediction, 0)).to.equal(0.123);
108
+ expect(browsiRTD.getCurrentData(singlePrediction, 1)).to.equal(-1);
109
+ expect(browsiRTD.getCurrentData(singlePrediction, 2)).to.equal(-1);
110
+ expect(browsiRTD.getCurrentData(predictions, 4)).to.equal(0.8);
111
+ expect(browsiRTD.getCurrentData(predictions, 5)).to.equal(0.8);
112
+ expect(browsiRTD.getCurrentData(predictions, 8)).to.equal(0.8);
113
+ })
114
+ })
115
+ describe('should set bid request data', function () {
116
+ const data = {
117
+ p: {
118
+ 'adUnit1': {ps: {0: 0.234}},
119
+ 'adUnit2': {ps: {0: 0.134}}},
120
+ kn: 'bv',
121
+ pmd: undefined
122
+ };
123
+ browsiRTD.setData(data);
124
+ const fakeAdUnits = [
125
+ {
126
+ code: 'adUnit1'
127
+ },
128
+ {
129
+ code: 'adUnit2'
130
+ }
131
+ ]
132
+ browsiRTD.browsiSubmodule.getBidRequestData({adUnits: fakeAdUnits}, () => {}, {}, null);
133
+ it('should set ad unit params with prediction values', function () {
134
+ expect(utils.deepAccess(fakeAdUnits[0], 'ortb2Imp.ext.data.browsi')).to.eql({bv: '0.20'});
135
+ expect(utils.deepAccess(fakeAdUnits[1], 'ortb2Imp.ext.data.browsi')).to.eql({bv: '0.10'});
136
+ })
137
+ })
83
138
  });
@@ -0,0 +1,188 @@
1
+ import * as utils from '../../../src/utils.js';
2
+ import * as hook from '../../../src/hook.js'
3
+
4
+ import { __TEST__ } from '../../../modules/cleanioRtdProvider.js';
5
+
6
+ const {
7
+ readConfig,
8
+ ConfigError,
9
+ pageInitStepPreloadScript,
10
+ pageInitStepProtectPage,
11
+ bidWrapStepAugmentHtml,
12
+ bidWrapStepProtectByWrapping,
13
+ beforeInit,
14
+ } = __TEST__;
15
+
16
+ sinon.assert.expose(chai.assert, { prefix: 'sinon' });
17
+
18
+ const fakeScriptURL = 'https://example.com/script.js';
19
+
20
+ function makeFakeBidResponse() {
21
+ return {
22
+ ad: '<body>hello ad</body>',
23
+ bidderCode: 'BIDDER',
24
+ creativeId: 'CREATIVE',
25
+ cpm: 1.23,
26
+ };
27
+ }
28
+
29
+ describe('clean.io RTD module', function () {
30
+ describe('readConfig()', function() {
31
+ it('should throw ConfigError on invalid configurations', function() {
32
+ expect(() => readConfig({})).to.throw(ConfigError);
33
+ expect(() => readConfig({ params: {} })).to.throw(ConfigError);
34
+ expect(() => readConfig({ params: { protectionMode: 'bids' } })).to.throw(ConfigError);
35
+ expect(() => readConfig({ params: { cdnUrl: 'abc' } })).to.throw(ConfigError);
36
+ expect(() => readConfig({ params: { cdnUrl: 'abc', protectionMode: 'bids' } })).to.throw(ConfigError);
37
+ expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: '123' } })).to.throw(ConfigError);
38
+ });
39
+
40
+ it('should accept valid configurations', function() {
41
+ expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } })).to.not.throw();
42
+ expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids' } })).to.not.throw();
43
+ expect(() => readConfig({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids-nowait' } })).to.not.throw();
44
+ });
45
+ });
46
+
47
+ describe('Module initialization step', function() {
48
+ let insertElementStub;
49
+ beforeEach(function() {
50
+ insertElementStub = sinon.stub(utils, 'insertElement');
51
+ });
52
+ afterEach(function() {
53
+ utils.insertElement.restore();
54
+ });
55
+
56
+ it('pageInitStepPreloadScript() should insert link/preload element', function() {
57
+ pageInitStepPreloadScript(fakeScriptURL);
58
+
59
+ sinon.assert.calledOnce(insertElementStub);
60
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'LINK'));
61
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.rel === 'preload'));
62
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.as === 'script'));
63
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.href === fakeScriptURL));
64
+ });
65
+
66
+ it('pageInitStepProtectPage() should insert script element', function() {
67
+ pageInitStepProtectPage(fakeScriptURL);
68
+
69
+ sinon.assert.calledOnce(insertElementStub);
70
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'SCRIPT'));
71
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.type === 'text/javascript'));
72
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.src === fakeScriptURL));
73
+ });
74
+ });
75
+
76
+ function ensurePrependToBidResponse(fakeBidResponse) {
77
+ expect(fakeBidResponse).to.have.own.property('ad').which.is.a('string');
78
+ expect(fakeBidResponse.ad).to.contain('<!-- pbad://creativeId=CREATIVE&bidderCode=BIDDER&cpm=1.23 -->');
79
+ }
80
+
81
+ function ensureWrapBidResponse(fakeBidResponse, scriptUrl) {
82
+ expect(fakeBidResponse).to.have.own.property('ad').which.is.a('string');
83
+ expect(fakeBidResponse.ad).to.contain(`src="${scriptUrl}"`);
84
+ expect(fakeBidResponse.ad).to.contain('agent.put(ad)');
85
+ }
86
+
87
+ describe('Bid processing step', function() {
88
+ it('bidWrapStepAugmentHtml() should prepend bid-specific information in a comment', function() {
89
+ const fakeBidResponse = makeFakeBidResponse();
90
+ bidWrapStepAugmentHtml(fakeBidResponse);
91
+ ensurePrependToBidResponse(fakeBidResponse);
92
+ });
93
+
94
+ it('bidWrapStepProtectByWrapping() should wrap payload into a script tag', function() {
95
+ const fakeBidResponse = makeFakeBidResponse();
96
+ bidWrapStepProtectByWrapping(fakeScriptURL, 0, fakeBidResponse);
97
+ ensureWrapBidResponse(fakeBidResponse, fakeScriptURL);
98
+ });
99
+ });
100
+
101
+ describe('Sumbodule execution', function() {
102
+ let submoduleStub;
103
+ let insertElementStub;
104
+ beforeEach(function () {
105
+ submoduleStub = sinon.stub(hook, 'submodule');
106
+ insertElementStub = sinon.stub(utils, 'insertElement');
107
+ });
108
+ afterEach(function () {
109
+ utils.insertElement.restore();
110
+ submoduleStub.restore();
111
+ });
112
+
113
+ function getModule() {
114
+ beforeInit();
115
+
116
+ expect(submoduleStub.calledOnceWith('realTimeData')).to.equal(true);
117
+
118
+ const registeredSubmoduleDefinition = submoduleStub.getCall(0).args[1];
119
+ expect(registeredSubmoduleDefinition).to.be.an('object');
120
+ expect(registeredSubmoduleDefinition).to.have.own.property('name', 'clean.io');
121
+ expect(registeredSubmoduleDefinition).to.have.own.property('init').that.is.a('function');
122
+ expect(registeredSubmoduleDefinition).to.have.own.property('onBidResponseEvent').that.is.a('function');
123
+
124
+ return registeredSubmoduleDefinition;
125
+ }
126
+
127
+ it('should register clean.io RTD submodule provider', function () {
128
+ getModule();
129
+ });
130
+
131
+ it('should refuse initialization with incorrect parameters', function () {
132
+ const { init } = getModule();
133
+ expect(init({ params: { cdnUrl: 'abc', protectionMode: 'full' } }, {})).to.equal(false); // too short distribution name
134
+ sinon.assert.notCalled(insertElementStub);
135
+ });
136
+
137
+ it('should iniitalize in full (page) protection mode', function () {
138
+ const { init, onBidResponseEvent } = getModule();
139
+ expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } }, {})).to.equal(true);
140
+ sinon.assert.calledOnce(insertElementStub);
141
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'SCRIPT'));
142
+
143
+ const fakeBidResponse = makeFakeBidResponse();
144
+ onBidResponseEvent(fakeBidResponse, {}, {});
145
+ ensurePrependToBidResponse(fakeBidResponse);
146
+ });
147
+
148
+ it('should iniitalize in bids (frame) protection mode', function () {
149
+ const { init, onBidResponseEvent } = getModule();
150
+ expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids' } }, {})).to.equal(true);
151
+ sinon.assert.calledOnce(insertElementStub);
152
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'LINK'));
153
+
154
+ const fakeBidResponse = makeFakeBidResponse();
155
+ onBidResponseEvent(fakeBidResponse, {}, {});
156
+ ensureWrapBidResponse(fakeBidResponse, 'https://abc1234567890.cloudfront.net/script.js');
157
+ });
158
+
159
+ it('should respect preload status in bids-nowait protection mode', function () {
160
+ const { init, onBidResponseEvent } = getModule();
161
+ expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'bids-nowait' } }, {})).to.equal(true);
162
+ sinon.assert.calledOnce(insertElementStub);
163
+ sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'LINK'));
164
+ const preloadLink = insertElementStub.getCall(0).args[0];
165
+ expect(preloadLink).to.have.property('onload').which.is.a('function');
166
+ expect(preloadLink).to.have.property('onerror').which.is.a('function');
167
+
168
+ const fakeBidResponse1 = makeFakeBidResponse();
169
+ onBidResponseEvent(fakeBidResponse1, {}, {});
170
+ ensurePrependToBidResponse(fakeBidResponse1);
171
+
172
+ // Simulate successful preloading
173
+ preloadLink.onload();
174
+
175
+ const fakeBidResponse2 = makeFakeBidResponse();
176
+ onBidResponseEvent(fakeBidResponse2, {}, {});
177
+ ensureWrapBidResponse(fakeBidResponse2, 'https://abc1234567890.cloudfront.net/script.js');
178
+
179
+ // Simulate error
180
+ preloadLink.onerror();
181
+
182
+ // Now we should fallback to just prepending
183
+ const fakeBidResponse3 = makeFakeBidResponse();
184
+ onBidResponseEvent(fakeBidResponse3, {}, {});
185
+ ensurePrependToBidResponse(fakeBidResponse3);
186
+ });
187
+ });
188
+ });