prebid.js 9.53.2 → 9.53.4

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 (229) hide show
  1. package/dist/33acrossAnalyticsAdapter.js +1 -1
  2. package/dist/33acrossBidAdapter.js +1 -1
  3. package/dist/33acrossIdSystem.js +1 -1
  4. package/dist/BTBidAdapter.js +1 -1
  5. package/dist/adagioAnalyticsAdapter.js +1 -1
  6. package/dist/adagioBidAdapter.js +1 -1
  7. package/dist/adagioRtdProvider.js +1 -1
  8. package/dist/adagioUtils.js +1 -1
  9. package/dist/addefendBidAdapter.js +1 -1
  10. package/dist/adgenerationBidAdapter.js +1 -1
  11. package/dist/adlooxRtdProvider.js +1 -1
  12. package/dist/adqueryBidAdapter.js +1 -1
  13. package/dist/adrelevantisBidAdapter.js +1 -1
  14. package/dist/adstirBidAdapter.js +1 -1
  15. package/dist/adtrgtmeBidAdapter.js +1 -1
  16. package/dist/adxcgAnalyticsAdapter.js +1 -1
  17. package/dist/adxcgBidAdapter.js +1 -1
  18. package/dist/adyoulikeBidAdapter.js +1 -1
  19. package/dist/agmaAnalyticsAdapter.js +1 -1
  20. package/dist/ajaBidAdapter.js +1 -1
  21. package/dist/amxBidAdapter.js +1 -1
  22. package/dist/amxIdSystem.js +1 -1
  23. package/dist/aniviewBidAdapter.js +1 -1
  24. package/dist/appierAnalyticsAdapter.js +1 -1
  25. package/dist/appnexusBidAdapter.js +1 -1
  26. package/dist/asoBidAdapter.js +1 -1
  27. package/dist/axonixBidAdapter.js +1 -1
  28. package/dist/beopBidAdapter.js +1 -1
  29. package/dist/bidderTimeoutUtils.js +1 -0
  30. package/dist/bidglassBidAdapter.js +1 -1
  31. package/dist/big-richmediaBidAdapter.js +1 -1
  32. package/dist/bitmediaBidAdapter.js +1 -1
  33. package/dist/bridBidAdapter.js +1 -1
  34. package/dist/bridgeuppBidAdapter.js +1 -1
  35. package/dist/bridgewellBidAdapter.js +1 -1
  36. package/dist/brightMountainMediaBidAdapter.js +1 -1
  37. package/dist/carodaBidAdapter.js +1 -1
  38. package/dist/chtnwBidAdapter.js +1 -1
  39. package/dist/chunk-core.js +1 -1
  40. package/dist/concertBidAdapter.js +1 -1
  41. package/dist/connectadBidAdapter.js +1 -1
  42. package/dist/consumableBidAdapter.js +1 -1
  43. package/dist/contxtfulBidAdapter.js +1 -1
  44. package/dist/conversantAnalyticsAdapter.js +1 -1
  45. package/dist/conversantBidAdapter.js +1 -1
  46. package/dist/craftBidAdapter.js +1 -1
  47. package/dist/criteoBidAdapter.js +1 -1
  48. package/dist/cwireBidAdapter.js +1 -1
  49. package/dist/dailymotionBidAdapter.js +1 -1
  50. package/dist/debugging-standalone.js +1 -1
  51. package/dist/dependencies.json +10 -1
  52. package/dist/dspxBidAdapter.js +1 -1
  53. package/dist/dxkultureBidAdapter.js +1 -1
  54. package/dist/eplanningBidAdapter.js +1 -1
  55. package/dist/equativBidAdapter.js +1 -1
  56. package/dist/eskimiBidAdapter.js +1 -1
  57. package/dist/euidIdSystem.js +1 -1
  58. package/dist/exadsBidAdapter.js +1 -1
  59. package/dist/excoBidAdapter.js +1 -1
  60. package/dist/feedadBidAdapter.js +1 -1
  61. package/dist/finativeBidAdapter.js +1 -1
  62. package/dist/freewheel-sspBidAdapter.js +1 -1
  63. package/dist/fwsspBidAdapter.js +1 -1
  64. package/dist/gmosspBidAdapter.js +1 -1
  65. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  66. package/dist/greenbidsBidAdapter.js +1 -1
  67. package/dist/greenbidsRtdProvider.js +1 -1
  68. package/dist/gridBidAdapter.js +1 -1
  69. package/dist/gumgumBidAdapter.js +1 -1
  70. package/dist/h12mediaBidAdapter.js +1 -1
  71. package/dist/hypelabBidAdapter.js +1 -1
  72. package/dist/id5AnalyticsAdapter.js +1 -1
  73. package/dist/id5IdSystem.js +1 -1
  74. package/dist/imdsBidAdapter.js +1 -1
  75. package/dist/improvedigitalBidAdapter.js +1 -1
  76. package/dist/inmobiBidAdapter.js +1 -1
  77. package/dist/insticatorBidAdapter.js +1 -1
  78. package/dist/intentIqAnalyticsAdapter.js +1 -1
  79. package/dist/ixBidAdapter.js +1 -1
  80. package/dist/jixieBidAdapter.js +1 -1
  81. package/dist/justpremiumBidAdapter.js +1 -1
  82. package/dist/kargoBidAdapter.js +1 -1
  83. package/dist/kimberliteBidAdapter.js +1 -1
  84. package/dist/konduitAnalyticsAdapter.js +1 -1
  85. package/dist/kueezBidAdapter.js +1 -1
  86. package/dist/lassoBidAdapter.js +1 -1
  87. package/dist/lifestreetBidAdapter.js +1 -1
  88. package/dist/liveIntentId.js +1 -1
  89. package/dist/logicadBidAdapter.js +1 -1
  90. package/dist/loglyliftBidAdapter.js +1 -1
  91. package/dist/luceadBidAdapter.js +1 -1
  92. package/dist/mabidderBidAdapter.js +1 -1
  93. package/dist/madsenseBidAdapter.js +1 -1
  94. package/dist/magniteAnalyticsAdapter.js +1 -1
  95. package/dist/malltvAnalyticsAdapter.js +1 -1
  96. package/dist/marsmediaBidAdapter.js +1 -1
  97. package/dist/mediafuseBidAdapter.js +1 -1
  98. package/dist/medianetBidAdapter.js +1 -1
  99. package/dist/medianetUtils.js +1 -1
  100. package/dist/mediasquareBidAdapter.js +1 -1
  101. package/dist/mgidBidAdapter.js +1 -1
  102. package/dist/missenaBidAdapter.js +1 -1
  103. package/dist/mobilefuseBidAdapter.js +1 -1
  104. package/dist/nextMillenniumBidAdapter.js +1 -1
  105. package/dist/nexx360Utils.js +1 -1
  106. package/dist/nobidAnalyticsAdapter.js +1 -1
  107. package/dist/nobidBidAdapter.js +1 -1
  108. package/dist/nodalsAiRtdProvider.js +1 -1
  109. package/dist/not-for-prod/prebid.js +178 -175
  110. package/dist/objectGuard.js +1 -1
  111. package/dist/oguryBidAdapter.js +1 -1
  112. package/dist/onetagBidAdapter.js +1 -1
  113. package/dist/ooloAnalyticsAdapter.js +1 -1
  114. package/dist/openxBidAdapter.js +1 -1
  115. package/dist/optableRtdProvider.js +1 -1
  116. package/dist/optidigitalBidAdapter.js +1 -1
  117. package/dist/orbidderBidAdapter.js +1 -1
  118. package/dist/outbrainBidAdapter.js +1 -1
  119. package/dist/pixfutureBidAdapter.js +1 -1
  120. package/dist/publinkIdSystem.js +1 -1
  121. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  122. package/dist/pubmaticBidAdapter.js +1 -1
  123. package/dist/pubmaticIdSystem.js +1 -1
  124. package/dist/pubmaticRtdProvider.js +1 -1
  125. package/dist/pubmaticUtils.js +1 -0
  126. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  127. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  128. package/dist/pxyzBidAdapter.js +1 -1
  129. package/dist/quantcastBidAdapter.js +1 -1
  130. package/dist/readpeakBidAdapter.js +1 -1
  131. package/dist/relaidoBidAdapter.js +1 -1
  132. package/dist/retailspotBidAdapter.js +1 -1
  133. package/dist/rhythmoneBidAdapter.js +1 -1
  134. package/dist/riseUtils.js +1 -1
  135. package/dist/rtdModule.js +1 -1
  136. package/dist/rubiconBidAdapter.js +1 -1
  137. package/dist/seedingAllianceBidAdapter.js +1 -1
  138. package/dist/seedtagBidAdapter.js +1 -1
  139. package/dist/sevioBidAdapter.js +1 -0
  140. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  141. package/dist/sharethroughBidAdapter.js +1 -1
  142. package/dist/showheroes-bsBidAdapter.js +1 -1
  143. package/dist/smaatoBidAdapter.js +1 -1
  144. package/dist/smartadserverBidAdapter.js +1 -1
  145. package/dist/smartxBidAdapter.js +1 -1
  146. package/dist/smilewantedBidAdapter.js +1 -1
  147. package/dist/snigelBidAdapter.js +1 -1
  148. package/dist/sonobiBidAdapter.js +1 -1
  149. package/dist/sovrnBidAdapter.js +1 -1
  150. package/dist/sparteoBidAdapter.js +1 -1
  151. package/dist/sspBCBidAdapter.js +1 -1
  152. package/dist/stvBidAdapter.js +1 -1
  153. package/dist/sublimeBidAdapter.js +1 -1
  154. package/dist/taboolaBidAdapter.js +1 -1
  155. package/dist/tappxBidAdapter.js +1 -1
  156. package/dist/targetVideoBidAdapter.js +1 -1
  157. package/dist/teadsBidAdapter.js +1 -1
  158. package/dist/terceptAnalyticsAdapter.js +1 -1
  159. package/dist/themoneytizerBidAdapter.js +1 -1
  160. package/dist/timeoutRtdProvider.js +1 -1
  161. package/dist/trionBidAdapter.js +1 -1
  162. package/dist/tripleliftBidAdapter.js +1 -1
  163. package/dist/ttdBidAdapter.js +1 -1
  164. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  165. package/dist/uid2IdSystem.js +1 -1
  166. package/dist/underdogmediaBidAdapter.js +1 -1
  167. package/dist/undertoneBidAdapter.js +1 -1
  168. package/dist/unrulyBidAdapter.js +1 -1
  169. package/dist/userId.js +1 -1
  170. package/dist/vidazooUtils.js +1 -1
  171. package/dist/videobyteBidAdapter.js +1 -1
  172. package/dist/visxBidAdapter.js +1 -1
  173. package/dist/vuukleBidAdapter.js +1 -1
  174. package/dist/widespaceBidAdapter.js +1 -1
  175. package/dist/winrBidAdapter.js +1 -1
  176. package/dist/yahooAdsBidAdapter.js +1 -1
  177. package/dist/yandexBidAdapter.js +1 -1
  178. package/dist/yieldmoBidAdapter.js +1 -1
  179. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  180. package/integrationExamples/gpt/pubmaticRtdProvider_Example.html +161 -0
  181. package/libraries/bidderTimeoutUtils/bidderTimeoutUtils.js +119 -0
  182. package/libraries/objectGuard/objectGuard.js +170 -48
  183. package/libraries/objectGuard/ortbGuard.js +33 -43
  184. package/libraries/pubmaticUtils/plugins/dynamicTimeout.js +209 -0
  185. package/libraries/pubmaticUtils/plugins/floorProvider.js +168 -0
  186. package/libraries/pubmaticUtils/plugins/pluginManager.js +106 -0
  187. package/libraries/pubmaticUtils/plugins/unifiedPricingRule.js +375 -0
  188. package/libraries/pubmaticUtils/pubmaticUtils.js +76 -0
  189. package/modules/adagioAnalyticsAdapter.js +6 -1
  190. package/modules/adagioBidAdapter.js +12 -5
  191. package/modules/adagioRtdProvider.js +41 -35
  192. package/modules/fwsspBidAdapter.js +134 -69
  193. package/modules/fwsspBidAdapter.md +121 -26
  194. package/modules/optableRtdProvider.js +33 -12
  195. package/modules/pubmaticAnalyticsAdapter.js +315 -587
  196. package/modules/pubmaticBidAdapter.js +71 -8
  197. package/modules/pubmaticIdSystem.js +4 -4
  198. package/modules/pubmaticRtdProvider.js +105 -613
  199. package/modules/rtdModule/index.js +23 -6
  200. package/modules/sevioBidAdapter.js +413 -0
  201. package/modules/sevioBidAdapter.md +29 -0
  202. package/modules/sparteoBidAdapter.js +122 -10
  203. package/modules/timeoutRtdProvider.js +2 -105
  204. package/modules/ttdBidAdapter.js +0 -5
  205. package/modules/userId/eids.js +1 -1
  206. package/modules/userId/index.js +32 -1
  207. package/package.json +1 -1
  208. package/src/auction.js +3 -0
  209. package/test/spec/activities/objectGuard_spec.js +189 -32
  210. package/test/spec/activities/ortbGuard_spec.js +10 -15
  211. package/test/spec/libraries/bidderTimeoutUtils/bidderTimeoutUtils_spec.js +213 -0
  212. package/test/spec/libraries/pubmaticUtils/plugins/dynamicTimeout_spec.js +746 -0
  213. package/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js +184 -0
  214. package/test/spec/libraries/pubmaticUtils/plugins/pluginManager_spec.js +489 -0
  215. package/test/spec/libraries/pubmaticUtils/plugins/unifiedPricingRule_spec.js +359 -0
  216. package/test/spec/libraries/pubmaticUtils/pubmaticUtils_spec.js +236 -0
  217. package/test/spec/modules/adagioAnalyticsAdapter_spec.js +94 -24
  218. package/test/spec/modules/adagioRtdProvider_spec.js +17 -17
  219. package/test/spec/modules/fwsspBidAdapter_spec.js +513 -78
  220. package/test/spec/modules/optableRtdProvider_spec.js +55 -5
  221. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +634 -916
  222. package/test/spec/modules/pubmaticBidAdapter_spec.js +260 -1
  223. package/test/spec/modules/pubmaticRtdProvider_spec.js +252 -1505
  224. package/test/spec/modules/realTimeDataModule_spec.js +58 -8
  225. package/test/spec/modules/sevioBidAdapter_spec.js +513 -0
  226. package/test/spec/modules/sparteoBidAdapter_spec.js +528 -43
  227. package/test/spec/modules/timeoutRtdProvider_spec.js +1 -201
  228. package/test/spec/modules/ttdBidAdapter_spec.js +0 -33
  229. package/test/spec/modules/userId_spec.js +115 -1
@@ -7,13 +7,14 @@ import 'src/prebid.js';
7
7
  import {attachRealTimeDataProvider, onDataDeletionRequest} from 'modules/rtdModule/index.js';
8
8
  import {GDPR_GVLIDS} from '../../../src/consentHandler.js';
9
9
  import {MODULE_TYPE_RTD} from '../../../src/activities/modules.js';
10
-
11
- const getBidRequestDataSpy = sinon.spy();
10
+ import {registerActivityControl} from '../../../src/activities/rules.js';
11
+ import {ACTIVITY_ENRICH_UFPD, ACTIVITY_TRANSMIT_EIDS} from '../../../src/activities/activities.js';
12
12
 
13
13
  describe('Real time module', function () {
14
14
  let eventHandlers;
15
15
  let sandbox;
16
16
  let validSM, validSMWait, invalidSM, failureSM, nonConfSM, conf;
17
+ let getBidRequestDataStub;
17
18
 
18
19
  function mockEmitEvent(event, ...args) {
19
20
  (eventHandlers[event] || []).forEach((h) => h(...args));
@@ -22,6 +23,8 @@ describe('Real time module', function () {
22
23
  before(() => {
23
24
  eventHandlers = {};
24
25
  sandbox = sinon.createSandbox();
26
+ getBidRequestDataStub = sinon.stub();
27
+
25
28
  sandbox.stub(events, 'on').callsFake((event, handler) => {
26
29
  if (!eventHandlers.hasOwnProperty(event)) {
27
30
  eventHandlers[event] = [];
@@ -41,7 +44,7 @@ describe('Real time module', function () {
41
44
  getTargetingData: (adUnitsCodes) => {
42
45
  return {'ad2': {'key': 'validSM'}}
43
46
  },
44
- getBidRequestData: getBidRequestDataSpy
47
+ getBidRequestData: getBidRequestDataStub
45
48
  };
46
49
 
47
50
  validSMWait = {
@@ -50,7 +53,7 @@ describe('Real time module', function () {
50
53
  getTargetingData: (adUnitsCodes) => {
51
54
  return {'ad1': {'key': 'validSMWait'}}
52
55
  },
53
- getBidRequestData: getBidRequestDataSpy
56
+ getBidRequestData: getBidRequestDataStub
54
57
  };
55
58
 
56
59
  invalidSM = {
@@ -110,18 +113,27 @@ describe('Real time module', function () {
110
113
  })
111
114
 
112
115
  describe('', () => {
113
- let PROVIDERS, _detachers;
116
+ let PROVIDERS, _detachers, rules;
114
117
 
115
118
  beforeEach(function () {
116
119
  PROVIDERS = [validSM, invalidSM, failureSM, nonConfSM, validSMWait];
117
120
  _detachers = PROVIDERS.map(rtdModule.attachRealTimeDataProvider);
118
121
  rtdModule.init(config);
119
122
  config.setConfig(conf);
123
+ rules = [
124
+ registerActivityControl(ACTIVITY_TRANSMIT_EIDS, 'test', (params) => {
125
+ return {allow: false};
126
+ }),
127
+ registerActivityControl(ACTIVITY_ENRICH_UFPD, 'test', (params) => {
128
+ return {allow: false};
129
+ })
130
+ ]
120
131
  });
121
132
 
122
133
  afterEach(function () {
123
134
  _detachers.forEach((f) => f());
124
135
  config.resetConfig();
136
+ rules.forEach(rule => rule());
125
137
  });
126
138
 
127
139
  it('should use only valid modules', function () {
@@ -129,11 +141,49 @@ describe('Real time module', function () {
129
141
  });
130
142
 
131
143
  it('should be able to modify bid request', function (done) {
144
+ const request = {bidRequest: {}};
145
+ getBidRequestDataStub.callsFake((req) => {
146
+ req.foo = 'bar';
147
+ });
148
+ rtdModule.setBidRequestsData(() => {
149
+ assert(getBidRequestDataStub.calledTwice);
150
+ assert(getBidRequestDataStub.calledWith(sinon.match({bidRequest: {}})));
151
+ expect(request.foo).to.eql('bar');
152
+ done();
153
+ }, request)
154
+ });
155
+
156
+ it('should apply guard to modules, but not affect ortb2Fragments otherwise', (done) => {
157
+ const ortb2Fragments = {
158
+ global: {
159
+ user: {
160
+ eids: ['id']
161
+ }
162
+ },
163
+ bidder: {
164
+ bidderA: {
165
+ user: {
166
+ eids: ['bid']
167
+ }
168
+ }
169
+ }
170
+ };
171
+ const request = {ortb2Fragments};
172
+ getBidRequestDataStub.callsFake((req) => {
173
+ expect(req.ortb2Fragments.global.user.eids).to.not.exist;
174
+ expect(req.ortb2Fragments.bidder.bidderA.eids).to.not.exist;
175
+ req.ortb2Fragments.global.user.yob = 123;
176
+ req.ortb2Fragments.bidder.bidderB = {
177
+ user: {
178
+ yob: 123
179
+ }
180
+ };
181
+ });
132
182
  rtdModule.setBidRequestsData(() => {
133
- assert(getBidRequestDataSpy.calledTwice);
134
- assert(getBidRequestDataSpy.calledWith(sinon.match({bidRequest: {}})));
183
+ expect(request.ortb2Fragments.global.user.eids).to.eql(['id']);
184
+ expect(request.ortb2Fragments.bidder.bidderB?.user).to.not.exist;
135
185
  done();
136
- }, {bidRequest: {}})
186
+ }, request);
137
187
  });
138
188
 
139
189
  it('sould place targeting on adUnits', function (done) {
@@ -0,0 +1,513 @@
1
+ import { expect } from 'chai';
2
+ import { spec } from 'modules/sevioBidAdapter.js';
3
+ import { config } from 'src/config.js';
4
+ const ENDPOINT_URL = 'https://req.adx.ws/prebid';
5
+
6
+ describe('sevioBidAdapter', function () {
7
+ describe('isBidRequestValid', function () {
8
+ let bid = {
9
+ 'bidder': 'sevio',
10
+ 'params': {
11
+ zone: 'zoneId'
12
+ },
13
+ 'mediaTypes': {
14
+ 'banner': {
15
+ 'sizes': [[728, 90]]
16
+ }
17
+ },
18
+ 'adUnitCode': 'adunit-code',
19
+ 'bidId': '1234asdf1234',
20
+ 'bidderRequestId': '1234asdf1234asdf',
21
+ 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120'
22
+ };
23
+ it('should return true where required params found', function () {
24
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
25
+ });
26
+ });
27
+
28
+ describe('buildRequests', function () {
29
+ let bidRequests = [
30
+ {
31
+ 'bidder': 'sevio',
32
+ 'params': {
33
+ zone: 'zoneId'
34
+ },
35
+ 'mediaTypes': {
36
+ 'banner': {
37
+ 'sizes': [[728, 90]]
38
+ }
39
+ },
40
+ 'bidId': '3e16f4cbbca2b',
41
+ 'bidderRequestId': '2d0e47e3ddc744',
42
+ 'auctionId': 'fb56cc83-bc64-4c44-a9b8-34fec672b592',
43
+ },
44
+ {
45
+ 'bidder': 'sevio',
46
+ 'params': {
47
+ zone: 'zoneId'
48
+ },
49
+ 'mediaTypes': {
50
+ 'banner': {
51
+ 'sizes': [[728, 90]]
52
+ }
53
+ },
54
+ 'adUnitCode': 'adunit-sevio-2nd',
55
+ 'bidId': '3a7e104573c543"',
56
+ 'bidderRequestId': '250799bbf223c6',
57
+ 'auctionId': '0b29430c-b25f-487a-b90c-68697a01f4e6',
58
+ }
59
+ ];
60
+
61
+ let bidderRequests = {
62
+ 'refererInfo': {
63
+ 'numIframes': 0,
64
+ 'reachedTop': true,
65
+ 'referer': 'https://example.com',
66
+ 'stack': ['https://example.com']
67
+ }
68
+ };
69
+
70
+ const request = spec.buildRequests(bidRequests, bidderRequests);
71
+ it('sends bid request to our endpoint via POST', function () {
72
+ expect(request[0].method).to.equal('POST');
73
+ expect(request[1].method).to.equal('POST');
74
+ });
75
+ it('attaches source and version to endpoint URL as query params', function () {
76
+ expect(request[0].url).to.equal(ENDPOINT_URL);
77
+ expect(request[1].url).to.equal(ENDPOINT_URL);
78
+ });
79
+ });
80
+
81
+ describe('interpretResponse', function () {
82
+ let bidRequest = [
83
+ {
84
+ 'method': 'POST',
85
+ 'url': ENDPOINT_URL,
86
+ 'data': {
87
+ 'zone': 'zoneId',
88
+ 'width': '728',
89
+ 'height': '90',
90
+ 'bidId': 'bidId123',
91
+ 'referer': 'www.example.com'
92
+ }
93
+ }
94
+ ];
95
+ let serverResponse = {
96
+ body: {
97
+ "bids": [
98
+ {
99
+ "requestId": "3e16f4cbbca2b",
100
+ "cpm": 5.0,
101
+ "currency": "EUR",
102
+ "width": 728,
103
+ "height": 90,
104
+ "creativeId": "b38d1ea7-36ea-410a-801a-0673b8ed8201",
105
+ "ad": "<html lang='en'><h3>I am an ad</h3></html>",
106
+ "ttl": 300,
107
+ "netRevenue": false,
108
+ "mediaType": "BANNER",
109
+ "meta": {
110
+ "advertiserDomains": [
111
+ "none.com"
112
+ ]
113
+ }
114
+ }
115
+ ],
116
+ "userSyncs": [
117
+ {
118
+ "url": "https://example.com/dmp/profile/?pid=12718&sg=SEVIO_CGE",
119
+ "type": "image"
120
+ }
121
+ ]
122
+ }
123
+ };
124
+ it('should get the correct bid response', function () {
125
+ let expectedResponse = [{
126
+ 'requestId': '3e16f4cbbca2b',
127
+ 'cpm': 5.0,
128
+ 'width': 728,
129
+ 'height': 90,
130
+ 'creativeId': 'b38d1ea7-36ea-410a-801a-0673b8ed8201',
131
+ 'currency': 'EUR',
132
+ 'netRevenue': true,
133
+ 'ttl': 3000,
134
+ 'ad': '<html lang="en"><h3>I am an ad</h3></html>',
135
+ 'mediaType': 'banner',
136
+ 'meta': {'advertiserDomains': ['none.com']}
137
+ }];
138
+ let result = spec.interpretResponse(serverResponse, bidRequest[0]);
139
+
140
+ expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse));
141
+ });
142
+
143
+ it('should get the correct bid response for the native case', function () {
144
+ let expectedResponseNative = [{
145
+ requestId: '36e835f6cbfca38',
146
+ cpm: 5,
147
+ currency: 'EUR',
148
+ width: 1,
149
+ height: 1,
150
+ creativeId: '28cf46ce-fe57-4417-acd6-285db604aa30',
151
+ ad: '{"ver":"1.2","assets":[{"id":1,"img":{"type":3,"url":"https://delivery.targetblankdev.com/bc42a192-9413-458b-ad88-f93ce023eacb/native/assets/4336011f-2076-4122-acb9-60f0478311eb/28cf46ce-fe57-4417-acd6-285db604aa30/552e9483-5ba6-46ed-b014-c61e80d8f9d1.png"}},{"id":2,"title":{"text":"TestAdNative","len":12}},{"id":4,"data":{"type":2,"value":"Test Ad Native"}}],"link":{"url":"https://work.targetblankdev.com/ad-server-e?data=o4rIvHAEkHlT9_ItFWCiBfQDNUkLHzkjLF9uPkIArQZiqBg_bWdNjmhGDU96MmBAI3UURTDIZ4CuqYA90CazeB7gVUwZboKeJXp8MIMiLQEzxaUQh6qsFjBoVFbn6H0qq7neZUEX82NuPcgwNzsThJnING6uFzUUCrlgAGncJQc68DMldAFqxTrgsSHpAhyF00-LCUF1eblyoT03R6RWIEpBl1O85VE9MeRPV5BHDaIjYVT7wWUSLXa40_mr_tUpFST6oDwVEFldoYQruwm07gjxLLLjnymoj9QXUuSTgGYwPFwW6wqG0p67xaGuGNB8J08AUweUujghsXHf_iSbkqfhO1LilHa_YrZ0UXzZSjRRWOX_sPVLs6Wta4RsEl3KMKVsVlgSLV6j0Okbw2cP6GztzMbURlz2C3jX2veaOsKxvajdqU5U1VLPYaRBAp-RDhuGKTbBHTe83bqgvgwebcEzcqQk-gAAAAA&integrity=3Yj4qCKUgBQPCshcNy2FPHD3Upsj8M5GOQ8E4ORetqI"},"eventtrackers":[{"event":1,"method":1,"url":"https://work.targetblankdev.com/ad-server-e?data=gS4Wtf5CrSPsZHjTBW1mDkQ1TP6aDOWpxpBQrUEfS4u8zrPxIBN1RFHJR5HdEKIKSdLjXZojo-lwz87xbP-ABPgD90lpjBeL-KOVOgvvwBy92VYCLZPvbsgYxJd_BFSiiz2UvwathNDkSsWChylm6t8sbIF62Qe540dhb3T1cI_Ben_qkgqrobPSHbAyBRKsje_twgWYf2TJFKsKmQYq5zSwgCnZKpMgZ0nFqUitx7DPjiZrGTFZxZ66J3ArskkREs6N0nPy4H5y2zFNepzAorp-pLONDHWSFkbQNzqNZqZgUJ_8XracHjL5_VDDwmz392xnx6_Kf1a6ezDRJyfp3k7ZJoGA5U4Wx5z4S7SelueaXZYgnHv--skg7P3pIXc7veM6nfXQD-GDmC0sDdrRgFbJCwCHBdkvurEcFASxIiBOaH8FOu2quxAth0dEoEHFpwKd_bJdAcXZFfUt4URDy43hQAQAAAAA&integrity=fP4SzYcSbOv8RbHcTT5xsC0fmeftmjv51PV_8G7-Wy0"},{"event":2,"method":1,"url":"https://work.targetblankdev.com/ad-server-e?data=PMO9Lc4-g0OGvzRglK8_72bWOZumt1Hgvy-ifNC3VT5iJ3PEBt1FD96vxr8w_Oy4E0BMXXHlDABkXelqcS6a1HJTdR8u-BncqZ8lycFkrVg9wMFNiorbpVxzpM5lgaj-uUEH7oYreDCXD_qK_5OzQaJp3rHgXjtyUZEaXimv6Bgu-hBeUYimezBT5Ba9IJJ1YDMdgdY-pFIU4ND1-kQN11KYTwikW37IWX-q8zZMwM3m78KsgKnY_OkJzy-0JJUeKkmRv7awNoBBOmhjmY7qHbDcVcwG5GQp4b0zJTm9bg6zHxIIYKsYqdQzXUjqL94rQ1M113QrGW9p9U11W0fSpX3VbHL0EtSrnoAo8d9xTjQ2nc5OsJOlDbYXakVO_GEiGtqK1kMUtBkQzjctCB_TyatPj_f7GZ-Vjuema9bTQUwKybco4Gmfu32GpsDKlPL4j3sMahH1W55zTrjOl2f4SkVyrXpTTpWS8Ifxl6Gq-xvYm7vixStI6gAAAAA&integrity=hDyA0PinLzMdhwKbV6BOJVTUn3xP9UQSDqf6JebKFhQ"}]}',
152
+ ttl: 300,
153
+ netRevenue: false,
154
+ mediaType: 'NATIVE',
155
+ meta: {
156
+ advertiserDomains: "example.com"
157
+ },
158
+ bidder: 'sevio',
159
+ native: {
160
+ "image": "https://example.com/image.png",
161
+ "image_width": 0,
162
+ "image_height": 0,
163
+ "title": "TestAdNative",
164
+ "body": "Test Ad Native",
165
+ "clickUrl": "https://example.com/ad-server-e?data=rYe8nbAM5c5zq5NcGi0xXHqGPRSwg9NdOtXo8HW7MBdZO6niYSCmNsZqZDU6PDs9jVqmCux1S-phDpqQodyDvLfMMFomYzBMfo6O9A9Zbjy_tDB-cEUwMbeiifLkXixiYbfWReUMm4VCErRUggbh-aZvd9HEpiSKQVxdzmL7_zJh0ZxCBPz6p6ukHQr_OqNcSeJvXK0UnFgvOT460CvfsZRwiXJ7PlOyJIrKJcllKMbQCnXRvnuXgZ7md-JLuorEF1zr2mU_a-1KvEkuPjdRZXGhgx68IZ1X7nBah-bbh_a3RD5_-nfOs5Sgm-osdxAxqAP90YFhHJSFubBlOvVaGJCEvpwz2hQAkkFoumfx1DkXLTbwFQBgi_mnXZssXz9RPQ-uzes7Hrpv2vWvtXcQtZcXkDLVc8vno1KQnaGTdING9ASNDMb0FwRHQqLH18lRxiAvtWZuAAqL3y2K2OClxKESDwRfCQAAAAA&integrity=1q8zuOP0wR6HFL22B0EcXl8a1FhqB4dYakIdamrH4TM",
166
+ "impressionTrackers": [
167
+ "https://example.com/ad-server-e?data=Q0uIkM00KhPFH-kwwFyX0xng6t1ZzDT-7TFojIwp1kSUAZRMxWAwpkifMKIv5xVteKcn_TStODvcIk2DFNBsLH68EBXiXtzlSuqrkRNhRXCshvuIuEpi7p18OFtztv0p42_D-LqnD0qaeVQP_UJ7Vxbi2cchLD6WxswYGafQ6hbuIw9bDXbx_FFzlTd3v99mq5YzZSyr6A26sKRr4FQz7F-1nXlXqln7MVUEDtbkbumxw8FfzIZsP04u4bWFnMd0pWCAwmp4z0ZwAfsMWquUlOf2eZVls-9dwdssB6PxjmkLIp3TRwMwiT2aNALf0sIMCH1gkyTl12ircYgjX9urxSGx3e1GoTlPQvdQZM9_WQyct8MJYh_HCRF_ZDGsPUtGT8f9MkttjWZUG1aXboPbL1EntUzzjM8XMb5vHnu4fOuVkAFY6jF7y4JLnq07dKnxB3e2mxQCuVFqw0i6u9IFo5i4PmQAAAAA&integrity=2iKlABjSJ08PWsZwavEV4fvFabbRW3MN5EcXyBdg4VE"
168
+ ],
169
+ "viewableTrackers": [
170
+ "https://example.com/ad-server-e?data=yMc4kfll-AQy3mUXZIl1xA2JjMlgm73j3HoGmqofgXVcVe1Q3wS6GD9ic0upRjeat_rLEP_aNrBevQsEUulH9F9JzFYDrkQavrGlhmHbddFnAx4mDrFK1N50uWR4oFmhl-V1RZ6PMrNeCLSH5KV8nDRsl5bCYG3YNBu6A65w-VJZpxfavNSHZfhDkDRvxSM6cYlstqlgg-dXp6jYdFS8w2SXIb8KgrxPN18Zw4T6wCqd0OGTDcO2ylQzjsvFeRrdBkkIyLlvovkfnYOYaLsyoAOclOMNaoDwmOhTLqCZr6IPrieLP4VyrsussbkIhBBSNvVr7KwNpLptTj3JqX6dSazTTm3FSojqCp8o6PoE072QmX6xmMK_Mm1XIJq9jtCxRER2s9VLkaWyzksgDmFeHzrnHurmDQ52BxA6m4DYQ9_txrMfxy5kK5lb73Qls2bcLzF2oosqRRCg2SWXomwKSkOkovxM7kxh_eIhYcZyxRO0wq5fILlMXgAAAAA&integrity=9QYkbMgRLGjGxBY2sO3VeZqyR5CF2sJHkGvPp6V6AeM"
171
+ ],
172
+ "adTemplate": "<div class=\"sponsored-post\">\n <img src=\"##image##\" />\n <div class=\"content\">\n <h1>\n <a href=\"##clickUrl##\" target=\"_blank\" class=\"pb-click\">##title##</a>\n </h1>\n <p>##body##</p>\n <div class=\"attribution\">##title##</div>\n </div>\n </div>"
173
+ }
174
+ }];
175
+ let serverResponseNative = {
176
+ body: {
177
+ "bids": [
178
+ {
179
+ "requestId": "36e835f6cbfca38",
180
+ "cpm": 5.0,
181
+ "currency": "EUR",
182
+ "width": 1,
183
+ "height": 1,
184
+ "creativeId": "28cf46ce-fe57-4417-acd6-285db604aa30",
185
+ "ad": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"img\":{\"type\":3,\"url\":\"https://example.com/image.png\"}},{\"id\":2,\"title\":{\"text\":\"TestAdNative\",\"len\":12}},{\"id\":4,\"data\":{\"type\":2,\"value\":\"Test Ad Native\"}}],\"link\":{\"url\":\"https://example.com/ad-server-e?data=o4rIvHAEkHlT9_ItFWCiBfQDNUkLHzkjLF9uPkIArQZiqBg_bWdNjmhGDU96MmBAI3UURTDIZ4CuqYA90CazeB7gVUwZboKeJXp8MIMiLQEzxaUQh6qsFjBoVFbn6H0qq7neZUEX82NuPcgwNzsThJnING6uFzUUCrlgAGncJQc68DMldAFqxTrgsSHpAhyF00-LCUF1eblyoT03R6RWIEpBl1O85VE9MeRPV5BHDaIjYVT7wWUSLXa40_mr_tUpFST6oDwVEFldoYQruwm07gjxLLLjnymoj9QXUuSTgGYwPFwW6wqG0p67xaGuGNB8J08AUweUujghsXHf_iSbkqfhO1LilHa_YrZ0UXzZSjRRWOX_sPVLs6Wta4RsEl3KMKVsVlgSLV6j0Okbw2cP6GztzMbURlz2C3jX2veaOsKxvajdqU5U1VLPYaRBAp-RDhuGKTbBHTe83bqgvgwebcEzcqQk-gAAAAA&integrity=3Yj4qCKUgBQPCshcNy2FPHD3Upsj8M5GOQ8E4ORetqI\"},\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"https://example.com/ad-server-e?data=gS4Wtf5CrSPsZHjTBW1mDkQ1TP6aDOWpxpBQrUEfS4u8zrPxIBN1RFHJR5HdEKIKSdLjXZojo-lwz87xbP-ABPgD90lpjBeL-KOVOgvvwBy92VYCLZPvbsgYxJd_BFSiiz2UvwathNDkSsWChylm6t8sbIF62Qe540dhb3T1cI_Ben_qkgqrobPSHbAyBRKsje_twgWYf2TJFKsKmQYq5zSwgCnZKpMgZ0nFqUitx7DPjiZrGTFZxZ66J3ArskkREs6N0nPy4H5y2zFNepzAorp-pLONDHWSFkbQNzqNZqZgUJ_8XracHjL5_VDDwmz392xnx6_Kf1a6ezDRJyfp3k7ZJoGA5U4Wx5z4S7SelueaXZYgnHv--skg7P3pIXc7veM6nfXQD-GDmC0sDdrRgFbJCwCHBdkvurEcFASxIiBOaH8FOu2quxAth0dEoEHFpwKd_bJdAcXZFfUt4URDy43hQAQAAAAA&integrity=fP4SzYcSbOv8RbHcTT5xsC0fmeftmjv51PV_8G7-Wy0\"},{\"event\":2,\"method\":1,\"url\":\"https://example.com/ad-server-e?data=PMO9Lc4-g0OGvzRglK8_72bWOZumt1Hgvy-ifNC3VT5iJ3PEBt1FD96vxr8w_Oy4E0BMXXHlDABkXelqcS6a1HJTdR8u-BncqZ8lycFkrVg9wMFNiorbpVxzpM5lgaj-uUEH7oYreDCXD_qK_5OzQaJp3rHgXjtyUZEaXimv6Bgu-hBeUYimezBT5Ba9IJJ1YDMdgdY-pFIU4ND1-kQN11KYTwikW37IWX-q8zZMwM3m78KsgKnY_OkJzy-0JJUeKkmRv7awNoBBOmhjmY7qHbDcVcwG5GQp4b0zJTm9bg6zHxIIYKsYqdQzXUjqL94rQ1M113QrGW9p9U11W0fSpX3VbHL0EtSrnoAo8d9xTjQ2nc5OsJOlDbYXakVO_GEiGtqK1kMUtBkQzjctCB_TyatPj_f7GZ-Vjuema9bTQUwKybco4Gmfu32GpsDKlPL4j3sMahH1W55zTrjOl2f4SkVyrXpTTpWS8Ifxl6Gq-xvYm7vixStI6gAAAAA&integrity=hDyA0PinLzMdhwKbV6BOJVTUn3xP9UQSDqf6JebKFhQ\"}]}",
186
+ "ttl": 300,
187
+ "netRevenue": false,
188
+ "mediaType": "NATIVE",
189
+ "meta": {
190
+ "advertiserDomains": [
191
+ "example.com"
192
+ ]
193
+ }
194
+ }
195
+ ],
196
+ "userSyncs": [
197
+ {
198
+ "url": "https://dmp.adform.net/dmp/profile/?pid=12718&sg=SEVIO_CGE",
199
+ "type": "image"
200
+ }
201
+ ]
202
+ }
203
+ };
204
+
205
+ let result = spec.interpretResponse(serverResponseNative);
206
+ expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponseNative));
207
+ })
208
+
209
+ it('should use bidRequest.params.keywords when provided', function () {
210
+ const singleBidRequest = [
211
+ {
212
+ bidder: 'sevio',
213
+ params: {
214
+ zone: 'zoneId',
215
+ keywords: ['play', 'games']
216
+ },
217
+ mediaTypes: {
218
+ banner: { sizes: [[728, 90]] }
219
+ },
220
+ bidId: 'bid-kw',
221
+ bidderRequestId: 'br-kw',
222
+ auctionId: 'auc-kw'
223
+ }
224
+ ];
225
+ const bidderRequest = {
226
+ refererInfo: {
227
+ numIframes: 0,
228
+ reachedTop: true,
229
+ referer: 'https://example.com',
230
+ stack: ['https://example.com']
231
+ }
232
+ };
233
+
234
+ const requests = spec.buildRequests(singleBidRequest, bidderRequest);
235
+ expect(requests).to.be.an('array').that.is.not.empty;
236
+ expect(requests[0].data).to.have.property('keywords');
237
+ expect(requests[0].data.keywords).to.have.property('tokens');
238
+ expect(requests[0].data.keywords.tokens).to.deep.equal(['play', 'games']);
239
+ });
240
+ });
241
+
242
+ it('should prefer ortb2.site.keywords when present on bidderRequest', function () {
243
+ const singleBidRequest = [
244
+ {
245
+ bidder: 'sevio',
246
+ params: {
247
+ zone: 'zoneId'
248
+ },
249
+ mediaTypes: {
250
+ banner: { sizes: [[300, 250]] }
251
+ },
252
+ bidId: 'bid-kw-ortb',
253
+ bidderRequestId: 'br-kw-ortb',
254
+ auctionId: 'auc-kw-ortb'
255
+ }
256
+ ];
257
+ const bidderRequestWithOrtb = {
258
+ refererInfo: {
259
+ numIframes: 0,
260
+ reachedTop: true,
261
+ referer: 'https://example.com',
262
+ stack: ['https://example.com']
263
+ },
264
+ ortb2: {
265
+ site: {
266
+ keywords: ['keyword1', 'keyword2']
267
+ }
268
+ }
269
+ };
270
+
271
+ const requests = spec.buildRequests(singleBidRequest, bidderRequestWithOrtb);
272
+ expect(requests).to.be.an('array').that.is.not.empty;
273
+ expect(requests[0].data).to.have.property('keywords');
274
+ expect(requests[0].data.keywords).to.have.property('tokens');
275
+ expect(requests[0].data.keywords.tokens).to.deep.equal(['keyword1', 'keyword2']);
276
+ });
277
+
278
+ function mkBid(overrides) {
279
+ return Object.assign({
280
+ bidId: 'bid-1',
281
+ bidder: 'sevio',
282
+ params: { zone: 'zone-123', referenceId: 'ref-abc', keywords: ['k1', 'k2'] },
283
+ mediaTypes: { banner: { sizes: [[300, 250]] } },
284
+ refererInfo: { page: 'https://example.com/page', referer: 'https://referrer.example' },
285
+ userIdAsEids: []
286
+ }, overrides || {});
287
+ }
288
+
289
+ const baseBidderRequest = {
290
+ timeout: 1200,
291
+ refererInfo: { page: 'https://example.com/page', referer: 'https://referrer.example' },
292
+ gdprConsent: { consentString: 'TCF-STRING' },
293
+ uspConsent: { uspString: '1NYN' },
294
+ gppConsent: { consentString: 'GPP-STRING' },
295
+ ortb2: { device: {}, ext: {} }
296
+ };
297
+
298
+ describe('Sevio adapter helper coverage via buildRequests (JS)', () => {
299
+ let stubs = [];
300
+
301
+ afterEach(() => {
302
+ while (stubs.length) stubs.pop().restore();
303
+ document.title = '';
304
+ document.head.innerHTML = '';
305
+ try {
306
+ Object.defineProperty(navigator, 'connection', { value: undefined, configurable: true });
307
+ } catch (e) {}
308
+ });
309
+
310
+ it('getReferrerInfo → data.referer', () => {
311
+ const out = spec.buildRequests([mkBid()], baseBidderRequest);
312
+ expect(out).to.have.lengthOf(1);
313
+ expect(out[0].data.referer).to.equal('https://example.com/page');
314
+ });
315
+
316
+ it('getPageTitle prefers top.title; falls back to og:title (top document)', () => {
317
+ window.top.document.title = 'Doc Title';
318
+ let out = spec.buildRequests([mkBid()], baseBidderRequest);
319
+ expect(out[0].data.pageTitle).to.equal('Doc Title');
320
+
321
+ window.top.document.title = '';
322
+ const meta = window.top.document.createElement('meta');
323
+ meta.setAttribute('property', 'og:title');
324
+ meta.setAttribute('content', 'OG Title');
325
+ window.top.document.head.appendChild(meta);
326
+
327
+ out = spec.buildRequests([mkBid()], baseBidderRequest);
328
+ expect(out[0].data.pageTitle).to.equal('OG Title');
329
+
330
+ meta.remove();
331
+ });
332
+
333
+ it('getPageTitle cross-origin fallback (window.top throws) uses local document.*', function () {
334
+ document.title = 'Local Title';
335
+
336
+ // In jsdom, window.top === window; try to simulate cross-origin by throwing from getter.
337
+ let restored = false;
338
+ try {
339
+ const original = Object.getOwnPropertyDescriptor(window, 'top');
340
+ Object.defineProperty(window, 'top', {
341
+ configurable: true,
342
+ get() { throw new Error('cross-origin'); }
343
+ });
344
+ const out = spec.buildRequests([mkBid()], baseBidderRequest);
345
+ expect(out[0].data.pageTitle).to.equal('Local Title');
346
+ Object.defineProperty(window, 'top', original);
347
+ restored = true;
348
+ } catch (e) {
349
+ // Environment didn’t allow redefining window.top; skip this case
350
+ this.skip();
351
+ } finally {
352
+ if (!restored) {
353
+ try { Object.defineProperty(window, 'top', { value: window, configurable: true }); } catch (e) {}
354
+ }
355
+ }
356
+ });
357
+
358
+ it('computeTTFB via navigation entries (top.performance) and cached within call', () => {
359
+ const perfTop = window.top.performance;
360
+
361
+ const original = perfTop.getEntriesByType;
362
+ Object.defineProperty(perfTop, 'getEntriesByType', {
363
+ configurable: true, writable: true,
364
+ value: (type) => (type === 'navigation' ? [{ responseStart: 152, requestStart: 100 }] : [])
365
+ });
366
+
367
+ const out = spec.buildRequests([mkBid({ bidId: 'A' }), mkBid({ bidId: 'B' })], baseBidderRequest);
368
+ expect(out).to.have.lengthOf(2);
369
+ expect(out[0].data.timeToFirstByte).to.equal('52');
370
+ expect(out[1].data.timeToFirstByte).to.equal('52');
371
+
372
+ Object.defineProperty(perfTop, 'getEntriesByType', { configurable: true, writable: true, value: original });
373
+ });
374
+
375
+ it('computeTTFB falls back to top.performance.timing when no navigation entries', () => {
376
+ const perfTop = window.top.performance;
377
+ const originalGetEntries = perfTop.getEntriesByType;
378
+ const originalTimingDesc = Object.getOwnPropertyDescriptor(perfTop, 'timing');
379
+
380
+ Object.defineProperty(perfTop, 'getEntriesByType', {
381
+ configurable: true, writable: true, value: () => []
382
+ });
383
+
384
+ Object.defineProperty(perfTop, 'timing', {
385
+ configurable: true,
386
+ value: { responseStart: 250, requestStart: 200 }
387
+ });
388
+
389
+ const out = spec.buildRequests([mkBid()], baseBidderRequest);
390
+ expect(out[0].data.timeToFirstByte).to.equal('50');
391
+
392
+ Object.defineProperty(perfTop, 'getEntriesByType', {
393
+ configurable: true, writable: true, value: originalGetEntries
394
+ });
395
+ if (originalTimingDesc) {
396
+ Object.defineProperty(perfTop, 'timing', originalTimingDesc);
397
+ } else {
398
+ Object.defineProperty(perfTop, 'timing', { configurable: true, value: undefined });
399
+ }
400
+ });
401
+
402
+ it('handles multiple sizes correctly', function () {
403
+ const multiSizeBidRequests = [
404
+ {
405
+ bidder: 'sevio',
406
+ params: { zone: 'zoneId' },
407
+ mediaTypes: {
408
+ banner: {
409
+ sizes: [
410
+ [300, 250],
411
+ [728, 90],
412
+ [160, 600],
413
+ ]
414
+ }
415
+ },
416
+ bidId: 'multi123',
417
+ }
418
+ ];
419
+
420
+ const bidderRequests = {
421
+ refererInfo: {
422
+ numIframes: 0,
423
+ reachedTop: true,
424
+ referer: 'https://example.com',
425
+ stack: ['https://example.com']
426
+ }
427
+ };
428
+
429
+ const request = spec.buildRequests(multiSizeBidRequests, bidderRequests);
430
+ const sizes = request[0].data.ads[0].sizes;
431
+
432
+ expect(sizes).to.deep.equal([
433
+ { width: 300, height: 250 },
434
+ { width: 728, height: 90 },
435
+ { width: 160, height: 600 },
436
+ ]);
437
+ });
438
+ });
439
+
440
+ describe('currency handling', function () {
441
+ let bidRequests;
442
+ let bidderRequests;
443
+
444
+ beforeEach(function () {
445
+ bidRequests = [{
446
+ bidder: 'sevio',
447
+ params: { zone: 'zoneId' },
448
+ mediaTypes: { banner: { sizes: [[300, 250]] } },
449
+ bidId: '123'
450
+ }];
451
+
452
+ bidderRequests = {
453
+ refererInfo: {
454
+ referer: 'https://example.com',
455
+ page: 'https://example.com',
456
+ }
457
+ };
458
+ });
459
+
460
+ afterEach(function () {
461
+ if (typeof config.resetConfig === 'function') {
462
+ config.resetConfig();
463
+ } else if (typeof config.setConfig === 'function') {
464
+ config.setConfig({ currency: null });
465
+ }
466
+ });
467
+
468
+ it('includes EUR currency when EUR is set in prebid config', function () {
469
+ config.setConfig({
470
+ currency: {
471
+ adServerCurrency: 'EUR'
472
+ }
473
+ });
474
+
475
+ const req = spec.buildRequests(bidRequests, bidderRequests);
476
+ const payload = req[0].data;
477
+
478
+ expect(payload.currency).to.equal('EUR');
479
+ });
480
+
481
+ it('includes GBP currency when GBP is set in prebid config', function () {
482
+ config.setConfig({
483
+ currency: {
484
+ adServerCurrency: 'GBP'
485
+ }
486
+ });
487
+
488
+ const req = spec.buildRequests(bidRequests, bidderRequests);
489
+ const payload = req[0].data;
490
+
491
+ expect(payload.currency).to.equal('GBP');
492
+ });
493
+
494
+ it('does NOT include currency when no currency config is set', function () {
495
+ const req = spec.buildRequests(bidRequests, bidderRequests);
496
+ const payload = req[0].data;
497
+
498
+ expect(payload).to.not.have.property('currency');
499
+ });
500
+ it('parses comma-separated keywords string into tokens array', function () {
501
+ const singleBidRequest = [{
502
+ bidder: 'sevio',
503
+ params: { zone: 'zoneId', keywords: 'play, games, fun ' }, // string CSV
504
+ mediaTypes: { banner: { sizes: [[728, 90]] } },
505
+ bidId: 'bid-kw-str'
506
+ }];
507
+ const requests = spec.buildRequests(singleBidRequest, baseBidderRequest);
508
+ expect(requests).to.be.an('array').that.is.not.empty;
509
+ expect(requests[0].data).to.have.nested.property('keywords.tokens');
510
+ expect(requests[0].data.keywords.tokens).to.deep.equal(['play', 'games', 'fun']);
511
+ });
512
+ });
513
+ });