prebid.js 8.8.0 → 8.9.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 (297) hide show
  1. package/dist/1plusXRtdProvider.js +1 -1
  2. package/dist/33acrossBidAdapter.js +1 -1
  3. package/dist/33acrossIdSystem.js +1 -1
  4. package/dist/adWMGAnalyticsAdapter.js +1 -1
  5. package/dist/adagioBidAdapter.js +1 -1
  6. package/dist/adbookpspBidAdapter.js +1 -1
  7. package/dist/adgenerationBidAdapter.js +1 -1
  8. package/dist/adkernelAdnAnalyticsAdapter.js +1 -1
  9. package/dist/adlooxAdServerVideo.js +1 -1
  10. package/dist/adlooxRtdProvider.js +1 -1
  11. package/dist/admixerIdSystem.js +1 -1
  12. package/dist/adnuntiusRtdProvider.js +1 -1
  13. package/dist/adpartnerBidAdapter.js +1 -1
  14. package/dist/adqueryBidAdapter.js +1 -1
  15. package/dist/adqueryIdSystem.js +1 -1
  16. package/dist/adrelevantisBidAdapter.js +1 -1
  17. package/dist/adriverIdSystem.js +1 -1
  18. package/dist/adtelligentIdSystem.js +1 -1
  19. package/dist/adtrgtmeBidAdapter.js +1 -1
  20. package/dist/adxcgAnalyticsAdapter.js +1 -1
  21. package/dist/adxcgBidAdapter.js +1 -1
  22. package/dist/adxpremiumAnalyticsAdapter.js +1 -1
  23. package/dist/adyoulikeBidAdapter.js +1 -1
  24. package/dist/aidemBidAdapter.js +1 -1
  25. package/dist/ajaBidAdapter.js +1 -1
  26. package/dist/akamaiDapRtdProvider.js +1 -1
  27. package/dist/alkimiBidAdapter.js +1 -1
  28. package/dist/amxBidAdapter.js +1 -1
  29. package/dist/amxIdSystem.js +1 -1
  30. package/dist/analyticsAdapter.js +1 -1
  31. package/dist/appierAnalyticsAdapter.js +1 -1
  32. package/dist/appnexusBidAdapter.js +1 -1
  33. package/dist/asoBidAdapter.js +1 -1
  34. package/dist/atsAnalyticsAdapter.js +1 -1
  35. package/dist/automatadBidAdapter.js +1 -1
  36. package/dist/axonixBidAdapter.js +1 -1
  37. package/dist/bidglassBidAdapter.js +1 -1
  38. package/dist/bidwatchAnalyticsAdapter.js +1 -1
  39. package/dist/big-richmediaBidAdapter.js +1 -1
  40. package/dist/bridBidAdapter.js +1 -1
  41. package/dist/bridgewellBidAdapter.js +1 -1
  42. package/dist/brightMountainMediaBidAdapter.js +1 -1
  43. package/dist/brightcomSSPBidAdapter.js +1 -1
  44. package/dist/britepoolIdSystem.js +1 -1
  45. package/dist/browsiRtdProvider.js +1 -1
  46. package/dist/byDataAnalyticsAdapter.js +1 -1
  47. package/dist/carodaBidAdapter.js +1 -1
  48. package/dist/categoryTranslation.js +1 -1
  49. package/dist/chtnwBidAdapter.js +1 -1
  50. package/dist/colossussspBidAdapter.js +1 -1
  51. package/dist/concertAnalyticsAdapter.js +1 -1
  52. package/dist/concertBidAdapter.js +1 -1
  53. package/dist/connectIdSystem.js +1 -1
  54. package/dist/connectadBidAdapter.js +1 -1
  55. package/dist/consumableBidAdapter.js +1 -1
  56. package/dist/conversantAnalyticsAdapter.js +1 -1
  57. package/dist/conversantBidAdapter.js +1 -1
  58. package/dist/craftBidAdapter.js +1 -1
  59. package/dist/criteoBidAdapter.js +1 -1
  60. package/dist/criteoIdSystem.js +1 -1
  61. package/dist/currency.js +1 -1
  62. package/dist/cwireBidAdapter.js +1 -1
  63. package/dist/dacIdSystem.js +1 -1
  64. package/dist/dailyhuntBidAdapter.js +1 -1
  65. package/dist/datablocksBidAdapter.js +1 -1
  66. package/dist/dgkeywordRtdProvider.js +1 -1
  67. package/dist/dmdIdSystem.js +1 -1
  68. package/dist/dspxBidAdapter.js +1 -1
  69. package/dist/eplanningAnalyticsAdapter.js +1 -1
  70. package/dist/eplanningBidAdapter.js +1 -1
  71. package/dist/euidIdSystem.js +1 -1
  72. package/dist/fabrickIdSystem.js +1 -1
  73. package/dist/feedadBidAdapter.js +1 -1
  74. package/dist/finativeBidAdapter.js +1 -1
  75. package/dist/fintezaAnalyticsAdapter.js +1 -1
  76. package/dist/fledgeForGpt.js +1 -1
  77. package/dist/freewheel-sspBidAdapter.js +1 -1
  78. package/dist/genericAnalyticsAdapter.js +1 -1
  79. package/dist/geoedgeRtdProvider.js +1 -1
  80. package/dist/gmosspBidAdapter.js +1 -1
  81. package/dist/gnetBidAdapter.js +1 -1
  82. package/dist/goldbachBidAdapter.js +1 -1
  83. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  84. package/dist/greenbidsRtdProvider.js +1 -1
  85. package/dist/gridBidAdapter.js +1 -1
  86. package/dist/growthCodeAnalyticsAdapter.js +1 -1
  87. package/dist/growthCodeIdSystem.js +1 -1
  88. package/dist/growthCodeRtdProvider.js +1 -1
  89. package/dist/gumgumBidAdapter.js +1 -1
  90. package/dist/h12mediaBidAdapter.js +1 -1
  91. package/dist/hadronAnalyticsAdapter.js +1 -1
  92. package/dist/hadronIdSystem.js +1 -1
  93. package/dist/hadronRtdProvider.js +1 -1
  94. package/dist/hypelabBidAdapter.js +1 -1
  95. package/dist/iasRtdProvider.js +1 -1
  96. package/dist/id5AnalyticsAdapter.js +1 -1
  97. package/dist/id5IdSystem.js +1 -1
  98. package/dist/idImportLibrary.js +1 -1
  99. package/dist/identityLinkIdSystem.js +1 -1
  100. package/dist/imRtdProvider.js +1 -1
  101. package/dist/imdsBidAdapter.js +1 -1
  102. package/dist/impactifyBidAdapter.js +1 -1
  103. package/dist/improvedigitalBidAdapter.js +1 -1
  104. package/dist/imuIdSystem.js +1 -1
  105. package/dist/insticatorBidAdapter.js +1 -1
  106. package/dist/intentIqIdSystem.js +1 -1
  107. package/dist/invisiblyAnalyticsAdapter.js +1 -1
  108. package/dist/ixBidAdapter.js +1 -1
  109. package/dist/jixieBidAdapter.js +1 -1
  110. package/dist/justpremiumBidAdapter.js +1 -1
  111. package/dist/jwplayerRtdProvider.js +1 -1
  112. package/dist/kargoAnalyticsAdapter.js +1 -1
  113. package/dist/kargoBidAdapter.js +1 -1
  114. package/dist/kinessoIdSystem.js +1 -1
  115. package/dist/konduitAnalyticsAdapter.js +1 -1
  116. package/dist/konduitWrapper.js +1 -1
  117. package/dist/kueezBidAdapter.js +1 -1
  118. package/dist/kueezRtbBidAdapter.js +1 -1
  119. package/dist/kulturemediaBidAdapter.js +1 -1
  120. package/dist/lassoBidAdapter.js +1 -1
  121. package/dist/lifestreetBidAdapter.js +1 -1
  122. package/dist/limelightDigitalBidAdapter.js +1 -1
  123. package/dist/liveIntentAnalyticsAdapter.js +1 -1
  124. package/dist/liveIntentIdSystem.js +1 -1
  125. package/dist/livewrappedAnalyticsAdapter.js +1 -1
  126. package/dist/logicadBidAdapter.js +1 -1
  127. package/dist/loglyliftBidAdapter.js +1 -1
  128. package/dist/lotamePanoramaIdSystem.js +1 -1
  129. package/dist/luponmediaBidAdapter.js +1 -1
  130. package/dist/magniteAnalyticsAdapter.js +1 -1
  131. package/dist/malltvAnalyticsAdapter.js +1 -1
  132. package/dist/mantisBidAdapter.js +1 -1
  133. package/dist/marsmediaAnalyticsAdapter.js +1 -1
  134. package/dist/marsmediaBidAdapter.js +1 -1
  135. package/dist/mediafuseBidAdapter.js +1 -1
  136. package/dist/medianetAnalyticsAdapter.js +1 -1
  137. package/dist/mediasquareBidAdapter.js +1 -1
  138. package/dist/merkleIdSystem.js +1 -1
  139. package/dist/mgidBidAdapter.js +1 -1
  140. package/dist/mgidRtdProvider.js +1 -1
  141. package/dist/minutemediaBidAdapter.js +1 -1
  142. package/dist/minutemediaplusBidAdapter.js +1 -1
  143. package/dist/mspa.js +1 -1
  144. package/dist/mwOpenLinkIdSystem.js +1 -1
  145. package/dist/naveggIdSystem.js +1 -1
  146. package/dist/neuwoRtdProvider.js +1 -1
  147. package/dist/nexx360BidAdapter.js +1 -1
  148. package/dist/not-for-prod/prebid.js +240 -240
  149. package/dist/novatiqIdSystem.js +1 -1
  150. package/dist/oguryBidAdapter.js +1 -1
  151. package/dist/onetagBidAdapter.js +1 -1
  152. package/dist/ooloAnalyticsAdapter.js +1 -1
  153. package/dist/open8BidAdapter.js +1 -1
  154. package/dist/operaadsIdSystem.js +1 -1
  155. package/dist/optidigitalBidAdapter.js +1 -1
  156. package/dist/optimeraRtdProvider.js +1 -1
  157. package/dist/outbrainBidAdapter.js +1 -1
  158. package/dist/oxxionAnalyticsAdapter.js +1 -1
  159. package/dist/oxxionRtdProvider.js +1 -1
  160. package/dist/parrableIdSystem.js +1 -1
  161. package/dist/pixfutureBidAdapter.js +1 -1
  162. package/dist/prebid-core.js +2 -2
  163. package/dist/prebidServerBidAdapter.js +1 -1
  164. package/dist/prebidmanagerAnalyticsAdapter.js +1 -1
  165. package/dist/priceFloors.js +1 -1
  166. package/dist/prismaBidAdapter.js +1 -1
  167. package/dist/pubgeniusBidAdapter.js +1 -1
  168. package/dist/publinkIdSystem.js +1 -1
  169. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  170. package/dist/pubmaticBidAdapter.js +1 -1
  171. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  172. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  173. package/dist/pxyzBidAdapter.js +1 -1
  174. package/dist/quantcastBidAdapter.js +1 -1
  175. package/dist/qwarryBidAdapter.js +1 -1
  176. package/dist/readpeakBidAdapter.js +1 -1
  177. package/dist/reconciliationRtdProvider.js +1 -1
  178. package/dist/relaidoBidAdapter.js +1 -1
  179. package/dist/relevadRtdProvider.js +1 -1
  180. package/dist/retailspotBidAdapter.js +1 -1
  181. package/dist/rhythmoneBidAdapter.js +1 -1
  182. package/dist/riseBidAdapter.js +1 -1
  183. package/dist/rivrAnalyticsAdapter.js +1 -1
  184. package/dist/roxotAnalyticsAdapter.js +1 -1
  185. package/dist/rubiconBidAdapter.js +1 -1
  186. package/dist/scaleableAnalyticsAdapter.js +1 -1
  187. package/dist/seedingAllianceBidAdapter.js +1 -1
  188. package/dist/seedtagBidAdapter.js +1 -1
  189. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  190. package/dist/sharethroughBidAdapter.js +1 -1
  191. package/dist/shinezBidAdapter.js +1 -1
  192. package/dist/silverpushBidAdapter.js +1 -1
  193. package/dist/sirdataRtdProvider.js +1 -1
  194. package/dist/slimcutBidAdapter.js +1 -1
  195. package/dist/smaatoBidAdapter.js +1 -1
  196. package/dist/smartadserverBidAdapter.js +1 -1
  197. package/dist/smartxBidAdapter.js +1 -1
  198. package/dist/smartyadsBidAdapter.js +1 -1
  199. package/dist/smilewantedBidAdapter.js +1 -1
  200. package/dist/sonobiAnalyticsAdapter.js +1 -1
  201. package/dist/sonobiBidAdapter.js +1 -1
  202. package/dist/sovrnAnalyticsAdapter.js +1 -1
  203. package/dist/sovrnBidAdapter.js +1 -1
  204. package/dist/sspBCBidAdapter.js +1 -1
  205. package/dist/staqAnalyticsAdapter.js +1 -1
  206. package/dist/stvBidAdapter.js +1 -1
  207. package/dist/sublimeBidAdapter.js +1 -1
  208. package/dist/taboolaBidAdapter.js +1 -1
  209. package/dist/talkadsBidAdapter.js +1 -1
  210. package/dist/tapadIdSystem.js +1 -1
  211. package/dist/targetVideoBidAdapter.js +1 -1
  212. package/dist/teadsBidAdapter.js +1 -1
  213. package/dist/teadsIdSystem.js +1 -1
  214. package/dist/terceptAnalyticsAdapter.js +1 -1
  215. package/dist/timeoutRtdProvider.js +1 -1
  216. package/dist/trionBidAdapter.js +1 -1
  217. package/dist/tripleliftBidAdapter.js +1 -1
  218. package/dist/ttdBidAdapter.js +1 -1
  219. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  220. package/dist/uid2IdSystem.js +1 -1
  221. package/dist/underdogmediaBidAdapter.js +1 -1
  222. package/dist/undertoneBidAdapter.js +1 -1
  223. package/dist/unifiedIdSystem.js +1 -1
  224. package/dist/verizonMediaIdSystem.js +1 -1
  225. package/dist/vidazooBidAdapter.js +1 -1
  226. package/dist/videobyteBidAdapter.js +1 -1
  227. package/dist/visxBidAdapter.js +1 -1
  228. package/dist/vrtcalBidAdapter.js +1 -1
  229. package/dist/vuukleBidAdapter.js +1 -1
  230. package/dist/weboramaRtdProvider.js +1 -1
  231. package/dist/widespaceBidAdapter.js +1 -1
  232. package/dist/winrBidAdapter.js +1 -1
  233. package/dist/yahoosspBidAdapter.js +1 -1
  234. package/dist/yieldmoBidAdapter.js +1 -1
  235. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  236. package/dist/yuktamediaAnalyticsAdapter.js +1 -1
  237. package/dist/zeta_global_sspAnalyticsAdapter.js +1 -1
  238. package/dist/zeta_global_sspBidAdapter.js +1 -1
  239. package/integrationExamples/gpt/growthcode.html +3 -12
  240. package/libraries/mspa/activityControls.js +3 -1
  241. package/modules/adagioBidAdapter.js +11 -3
  242. package/modules/concertBidAdapter.js +1 -0
  243. package/modules/fledgeForGpt.js +17 -4
  244. package/modules/fledgeForGpt.md +17 -10
  245. package/modules/geoedgeRtdProvider.js +1 -5
  246. package/modules/growthCodeAnalyticsAdapter.js +7 -4
  247. package/modules/growthCodeAnalyticsAdapter.md +1 -7
  248. package/modules/nexx360BidAdapter.js +7 -1
  249. package/modules/prebidServerBidAdapter/index.js +8 -2
  250. package/modules/pubmaticAnalyticsAdapter.js +16 -3
  251. package/package.json +1 -1
  252. package/src/adapters/bidderFactory.js +14 -5
  253. package/src/ajax.js +126 -87
  254. package/src/constants.json +8 -2
  255. package/src/native.js +12 -1
  256. package/test/mocks/xhr.js +256 -9
  257. package/test/spec/libraries/mspa/activityControls_spec.js +6 -0
  258. package/test/spec/modules/adagioBidAdapter_spec.js +15 -0
  259. package/test/spec/modules/adlooxAdServerVideo_spec.js +1 -5
  260. package/test/spec/modules/adlooxRtdProvider_spec.js +1 -5
  261. package/test/spec/modules/categoryTranslation_spec.js +7 -9
  262. package/test/spec/modules/concertBidAdapter_spec.js +1 -1
  263. package/test/spec/modules/conversantAnalyticsAdapter_spec.js +3 -5
  264. package/test/spec/modules/currency_spec.js +2 -2
  265. package/test/spec/modules/dmdIdSystem_spec.js +2 -2
  266. package/test/spec/modules/euidIdSystem_spec.js +10 -9
  267. package/test/spec/modules/feedadBidAdapter_spec.js +1 -1
  268. package/test/spec/modules/fledge_spec.js +117 -43
  269. package/test/spec/modules/geoedgeRtdProvider_spec.js +11 -18
  270. package/test/spec/modules/greenbidsRtdProvider_spec.js +3 -12
  271. package/test/spec/modules/growthCodeAnalyticsAdapter_spec.js +0 -1
  272. package/test/spec/modules/hadronIdSystem_spec.js +1 -1
  273. package/test/spec/modules/id5AnalyticsAdapter_spec.js +1 -7
  274. package/test/spec/modules/id5IdSystem_spec.js +25 -25
  275. package/test/spec/modules/identityLinkIdSystem_spec.js +0 -2
  276. package/test/spec/modules/intentIqIdSystem_spec.js +0 -2
  277. package/test/spec/modules/invisiblyAnalyticsAdapter_spec.js +2 -6
  278. package/test/spec/modules/jwplayerRtdProvider_spec.js +26 -28
  279. package/test/spec/modules/magniteAnalyticsAdapter_spec.js +2 -2
  280. package/test/spec/modules/mgidRtdProvider_spec.js +1 -5
  281. package/test/spec/modules/nexx360BidAdapter_spec.js +48 -1
  282. package/test/spec/modules/oguryBidAdapter_spec.js +5 -19
  283. package/test/spec/modules/ooloAnalyticsAdapter_spec.js +1 -1
  284. package/test/spec/modules/prebidServerBidAdapter_spec.js +33 -2
  285. package/test/spec/modules/priceFloors_spec.js +33 -37
  286. package/test/spec/modules/publinkIdSystem_spec.js +1 -1
  287. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +102 -18
  288. package/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +2 -8
  289. package/test/spec/modules/teadsIdSystem_spec.js +1 -1
  290. package/test/spec/modules/uid2IdSystem_helpers.js +5 -5
  291. package/test/spec/modules/uid2IdSystem_spec.js +24 -24
  292. package/test/spec/modules/userId_spec.js +1 -1
  293. package/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js +2 -5
  294. package/test/spec/native_spec.js +15 -2
  295. package/test/spec/unit/core/ajax_spec.js +403 -0
  296. package/test/spec/unit/core/bidderFactory_spec.js +80 -8
  297. package/test/spec/videoCache_spec.js +4 -4
@@ -862,6 +862,9 @@ describe('validate native', function () {
862
862
  }]
863
863
  },
864
864
  address: {},
865
+ privacyLink: {
866
+ required: true
867
+ }
865
868
  },
866
869
  },
867
870
  };
@@ -917,6 +920,7 @@ describe('validate native', function () {
917
920
  type: 9,
918
921
  }
919
922
  });
923
+ expect(ortb.privacy).to.equal(1);
920
924
  });
921
925
 
922
926
  ['bogusKey', 'clickUrl', 'privacyLink'].forEach(nativeKey => {
@@ -1024,11 +1028,14 @@ describe('validate native', function () {
1024
1028
  expect(oldNativeRequest.sponsoredBy).to.include({
1025
1029
  required: true,
1026
1030
  len: 25
1027
- })
1031
+ });
1028
1032
  expect(oldNativeRequest.body).to.include({
1029
1033
  required: true,
1030
1034
  len: 140
1031
- })
1035
+ });
1036
+ expect(oldNativeRequest.privacyLink).to.include({
1037
+ required: false
1038
+ });
1032
1039
  });
1033
1040
 
1034
1041
  if (FEATURES.NATIVE) {
@@ -1199,6 +1206,12 @@ describe('legacyPropertiesToOrtbNative', () => {
1199
1206
  expect(native.jstracker).to.eql('some-markupsome-other-markup');
1200
1207
  })
1201
1208
  });
1209
+ describe('privacylink', () => {
1210
+ it('should convert privacyLink to privacy', () => {
1211
+ const native = legacyPropertiesToOrtbNative({privacyLink: 'https:/my-privacy-link.com'});
1212
+ expect(native.privacy).to.eql('https:/my-privacy-link.com');
1213
+ })
1214
+ })
1202
1215
  });
1203
1216
 
1204
1217
  describe('fireImpressionTrackers', () => {
@@ -0,0 +1,403 @@
1
+ import {dep, attachCallbacks, fetcherFactory, toFetchRequest} from '../../../../src/ajax.js';
2
+ import {config} from 'src/config.js';
3
+ import {server} from '../../../mocks/xhr.js';
4
+ import {sandbox} from 'sinon';
5
+
6
+ const EXAMPLE_URL = 'https://www.example.com';
7
+
8
+ describe('fetcherFactory', () => {
9
+ let clock;
10
+
11
+ beforeEach(() => {
12
+ clock = sinon.useFakeTimers();
13
+ server.autoTimeout = true;
14
+ });
15
+
16
+ afterEach(() => {
17
+ clock.runAll();
18
+ clock.restore();
19
+ config.resetConfig();
20
+ });
21
+
22
+ Object.entries({
23
+ 'URL': EXAMPLE_URL,
24
+ 'request object': new Request(EXAMPLE_URL)
25
+ }).forEach(([t, resource]) => {
26
+ it(`times out after timeout when fetching ${t}`, (done) => {
27
+ const fetch = fetcherFactory(1000);
28
+ const resp = fetch(resource);
29
+ clock.tick(900);
30
+ expect(server.requests[0].fetch.request.signal.aborted).to.be.false;
31
+ clock.tick(100);
32
+ expect(server.requests[0].fetch.request.signal.aborted).to.be.true;
33
+ resp.catch(() => done());
34
+ });
35
+ });
36
+
37
+ it('does not timeout after it completes', () => {
38
+ const fetch = fetcherFactory(1000);
39
+ const resp = fetch(EXAMPLE_URL);
40
+ server.requests[0].respond();
41
+ return resp.then(() => {
42
+ clock.tick(2000);
43
+ expect(server.requests[0].fetch.request.signal.aborted).to.be.false;
44
+ });
45
+ });
46
+
47
+ Object.entries({
48
+ 'disableAjaxTimeout is set'() {
49
+ const fetcher = fetcherFactory(1000);
50
+ config.setConfig({disableAjaxTimeout: true});
51
+ return fetcher;
52
+ },
53
+ 'timeout is null'() {
54
+ return fetcherFactory(null);
55
+ },
56
+ }).forEach(([t, mkFetcher]) => {
57
+ it(`does not timeout if ${t}`, (done) => {
58
+ const fetch = mkFetcher();
59
+ const pm = fetch(EXAMPLE_URL);
60
+ clock.tick(2000);
61
+ server.requests[0].respond();
62
+ pm.then(() => done());
63
+ });
64
+ });
65
+
66
+ Object.entries({
67
+ 'local URL': ['/local.html', window.origin],
68
+ 'remote URL': [EXAMPLE_URL + '/remote.html', EXAMPLE_URL],
69
+ 'request with local URL': [new Request('/local.html'), window.origin],
70
+ 'request with remote URL': [new Request(EXAMPLE_URL + '/remote.html'), EXAMPLE_URL]
71
+ }).forEach(([t, [resource, expectedOrigin]]) => {
72
+ describe(`using ${t}`, () => {
73
+ it('calls request, passing origin', () => {
74
+ const request = sinon.stub();
75
+ const fetch = fetcherFactory(1000, {request});
76
+ fetch(resource);
77
+ sinon.assert.calledWith(request, expectedOrigin);
78
+ });
79
+
80
+ Object.entries({
81
+ success: 'respond',
82
+ error: 'error'
83
+ }).forEach(([t, method]) => {
84
+ it(`calls done on ${t}, passing origin`, () => {
85
+ const done = sinon.stub();
86
+ const fetch = fetcherFactory(1000, {done});
87
+ const req = fetch(resource).catch(() => null).then(() => {
88
+ sinon.assert.calledWith(done, expectedOrigin);
89
+ });
90
+ server.requests[0][method]();
91
+ return req;
92
+ });
93
+ });
94
+ });
95
+ });
96
+ });
97
+
98
+ describe('toFetchRequest', () => {
99
+ Object.entries({
100
+ 'simple POST': {
101
+ url: EXAMPLE_URL,
102
+ data: 'data',
103
+ expect: {
104
+ request: {
105
+ url: EXAMPLE_URL + '/',
106
+ method: 'POST',
107
+ },
108
+ text: 'data',
109
+ headers: {
110
+ 'content-type': 'text/plain'
111
+ }
112
+ }
113
+ },
114
+ 'POST with headers': {
115
+ url: EXAMPLE_URL,
116
+ data: '{"json": "body"}',
117
+ options: {
118
+ contentType: 'application/json',
119
+ customHeaders: {
120
+ 'x-custom': 'value'
121
+ }
122
+ },
123
+ expect: {
124
+ request: {
125
+ url: EXAMPLE_URL + '/',
126
+ method: 'POST',
127
+ },
128
+ text: '{"json": "body"}',
129
+ headers: {
130
+ 'content-type': 'application/json',
131
+ 'X-Custom': 'value'
132
+ }
133
+ }
134
+ },
135
+ 'simple GET': {
136
+ url: EXAMPLE_URL,
137
+ data: {p1: 'v1', p2: 'v2'},
138
+ options: {
139
+ method: 'GET',
140
+ },
141
+ expect: {
142
+ request: {
143
+ url: EXAMPLE_URL + '/?p1=v1&p2=v2',
144
+ method: 'GET'
145
+ },
146
+ text: '',
147
+ headers: {
148
+ 'content-type': 'text/plain'
149
+ }
150
+ }
151
+ },
152
+ 'GET with credentials': {
153
+ url: EXAMPLE_URL,
154
+ data: null,
155
+ options: {
156
+ method: 'GET',
157
+ withCredentials: true,
158
+ },
159
+ expect: {
160
+ request: {
161
+ url: EXAMPLE_URL + '/',
162
+ method: 'GET',
163
+ credentials: 'include'
164
+ },
165
+ text: '',
166
+ headers: {
167
+ 'content-type': 'text/plain'
168
+ }
169
+ }
170
+ }
171
+ }).forEach(([t, {url, data, options, expect: {request, text, headers}}]) => {
172
+ it(`can build ${t}`, () => {
173
+ const req = toFetchRequest(url, data, options);
174
+ return req.text().then(body => {
175
+ Object.entries(request).forEach(([prop, val]) => {
176
+ expect(req[prop]).to.eql(val);
177
+ });
178
+ const hdr = new Headers(headers);
179
+ Array.from(req.headers.entries()).forEach(([name, val]) => {
180
+ expect(hdr.get(name)).to.eql(val);
181
+ });
182
+ expect(body).to.eql(text);
183
+ });
184
+ });
185
+ });
186
+
187
+ describe('browsingTopics', () => {
188
+ Object.entries({
189
+ 'browsingTopics = true': [{browsingTopics: true}, true],
190
+ 'browsingTopics = false': [{browsingTopics: false}, false],
191
+ 'browsingTopics is undef': [{}, false]
192
+ }).forEach(([t, [opts, shouldBeSet]]) => {
193
+ describe(`when options has ${t}`, () => {
194
+ const sandbox = sinon.createSandbox();
195
+ afterEach(() => {
196
+ sandbox.restore();
197
+ });
198
+
199
+ it(`should ${!shouldBeSet ? 'not ' : ''}be set when in a secure context`, () => {
200
+ sandbox.stub(window, 'isSecureContext').get(() => true);
201
+ toFetchRequest(EXAMPLE_URL, null, opts);
202
+ sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {browsingTopics: shouldBeSet ? true : undefined});
203
+ });
204
+ it(`should not be set when not in a secure context`, () => {
205
+ sandbox.stub(window, 'isSecureContext').get(() => false);
206
+ toFetchRequest(EXAMPLE_URL, null, opts);
207
+ sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {browsingTopics: undefined});
208
+ });
209
+ })
210
+ })
211
+ })
212
+ });
213
+
214
+ describe('attachCallbacks', () => {
215
+ const sampleHeaders = new Headers({
216
+ 'x-1': 'v1',
217
+ 'x-2': 'v2'
218
+ });
219
+
220
+ function responseFactory(body, props) {
221
+ props = Object.assign({headers: sampleHeaders, url: EXAMPLE_URL}, props);
222
+ return function () {
223
+ return {
224
+ response: Object.defineProperties(new Response(body, props), {
225
+ url: {
226
+ get: () => props.url
227
+ }
228
+ }),
229
+ body: body || ''
230
+ };
231
+ };
232
+ }
233
+
234
+ function expectNullXHR(response) {
235
+ return new Promise((resolve, reject) => {
236
+ attachCallbacks(Promise.resolve(response), {
237
+ success: () => {
238
+ reject(new Error('should not succeed'));
239
+ },
240
+ error(statusText, xhr) {
241
+ expect(statusText).to.eql('');
242
+ sinon.assert.match(xhr, {
243
+ readyState: XMLHttpRequest.DONE,
244
+ status: 0,
245
+ statusText: '',
246
+ responseText: '',
247
+ response: '',
248
+ responseXML: null
249
+ });
250
+ expect(xhr.getResponseHeader('any')).to.be.null;
251
+ resolve();
252
+ }
253
+ });
254
+ });
255
+ }
256
+
257
+ it('runs error callback on rejections', () => {
258
+ return expectNullXHR(Promise.reject(new Error()));
259
+ });
260
+
261
+ Object.entries({
262
+ '2xx response': {
263
+ success: true,
264
+ makeResponse: responseFactory('body', {status: 200, statusText: 'OK'})
265
+ },
266
+ '2xx response with no body': {
267
+ success: true,
268
+ makeResponse: responseFactory(null, {status: 204, statusText: 'No content'})
269
+ },
270
+ '2xx response with XML': {
271
+ success: true,
272
+ xml: true,
273
+ makeResponse: responseFactory('<?xml><root><tag /></root>', {
274
+ status: 200,
275
+ statusText: 'OK',
276
+ headers: {'content-type': 'application/xml;charset=UTF8'}
277
+ })
278
+ },
279
+ '2xx response with HTML': {
280
+ success: true,
281
+ xml: true,
282
+ makeResponse: responseFactory('<html lang="en"><p></p></html>', {
283
+ status: 200,
284
+ statusText: 'OK',
285
+ headers: {'content-type': 'text/html;charset=UTF-8'}
286
+ })
287
+ },
288
+ '304 response': {
289
+ success: true,
290
+ makeResponse: responseFactory(null, {status: 304, statusText: 'Moved permanently'})
291
+ },
292
+ '4xx response': {
293
+ success: false,
294
+ makeResponse: responseFactory('body', {status: 400, statusText: 'Invalid request'})
295
+ },
296
+ '5xx response': {
297
+ success: false,
298
+ makeResponse: responseFactory('body', {status: 503, statusText: 'Gateway error'})
299
+ },
300
+ '4xx response with XML': {
301
+ success: false,
302
+ xml: true,
303
+ makeResponse: responseFactory('<?xml><root></root>', {
304
+ status: 404,
305
+ statusText: 'Not found',
306
+ headers: {
307
+ 'content-type': 'application/xml'
308
+ }
309
+ })
310
+ }
311
+ }).forEach(([t, {success, makeResponse, xml}]) => {
312
+ const cbType = success ? 'success' : 'error';
313
+
314
+ describe(`for ${t}`, () => {
315
+ let response, body;
316
+ beforeEach(() => {
317
+ ({response, body} = makeResponse());
318
+ });
319
+
320
+ function checkXHR(xhr) {
321
+ sinon.assert.match(xhr, {
322
+ readyState: XMLHttpRequest.DONE,
323
+ status: response.status,
324
+ statusText: response.statusText,
325
+ responseType: '',
326
+ responseURL: response.url,
327
+ response: body,
328
+ responseText: body,
329
+ });
330
+ if (xml) {
331
+ expect(xhr.responseXML.querySelectorAll('*').length > 0).to.be.true;
332
+ } else {
333
+ expect(xhr.responseXML).to.not.exist;
334
+ }
335
+ Array.from(response.headers.entries()).forEach(([name, value]) => {
336
+ expect(xhr.getResponseHeader(name)).to.eql(value);
337
+ });
338
+ expect(xhr.getResponseHeader('$$missing-header')).to.be.null;
339
+ }
340
+
341
+ it(`runs ${cbType} callback`, (done) => {
342
+ attachCallbacks(Promise.resolve(response), {
343
+ success(payload, xhr) {
344
+ expect(success).to.be.true;
345
+ expect(payload).to.eql(body);
346
+ checkXHR(xhr);
347
+ done();
348
+ },
349
+ error(statusText, xhr) {
350
+ expect(success).to.be.false;
351
+ expect(statusText).to.eql(response.statusText);
352
+ checkXHR(xhr);
353
+ done();
354
+ }
355
+ });
356
+ });
357
+
358
+ it(`runs error callback if body cannot be retrieved`, () => {
359
+ response.text = () => Promise.reject(new Error());
360
+ return expectNullXHR(response);
361
+ });
362
+
363
+ if (success) {
364
+ it('accepts a single function as success callback', (done) => {
365
+ attachCallbacks(Promise.resolve(response), function (payload, xhr) {
366
+ expect(payload).to.eql(body);
367
+ checkXHR(xhr);
368
+ done();
369
+ })
370
+ })
371
+ }
372
+ });
373
+ });
374
+
375
+ describe('callback exceptions', () => {
376
+ Object.entries({
377
+ success: responseFactory(null, {status: 204}),
378
+ error: responseFactory('', {status: 400}),
379
+ }).forEach(([cbType, makeResponse]) => {
380
+ it(`do not choke ${cbType} callbacks`, () => {
381
+ const {response} = makeResponse();
382
+ return new Promise((resolve) => {
383
+ const result = {success: false, error: false};
384
+ attachCallbacks(Promise.resolve(response), {
385
+ success() {
386
+ result.success = true;
387
+ throw new Error();
388
+ },
389
+ error() {
390
+ result.error = true;
391
+ throw new Error();
392
+ }
393
+ });
394
+ setTimeout(() => resolve(result), 20);
395
+ }).then(result => {
396
+ Object.entries(result).forEach(([typ, ran]) => {
397
+ expect(ran).to.be[typ === cbType ? 'true' : 'false']
398
+ })
399
+ });
400
+ });
401
+ });
402
+ });
403
+ });
@@ -14,7 +14,7 @@ import {bidderSettings} from '../../../../src/bidderSettings.js';
14
14
  import {decorateAdUnitsWithNativeParams} from '../../../../src/native.js';
15
15
  import * as activityRules from 'src/activities/rules.js';
16
16
  import {MODULE_TYPE_BIDDER} from '../../../../src/activities/modules.js';
17
- import {ACTIVITY_TRANSMIT_TID} from '../../../../src/activities/activities.js';
17
+ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../../../../src/activities/activities.js';
18
18
 
19
19
  const CODE = 'sampleBidder';
20
20
  const MOCK_BIDS_REQUEST = {
@@ -319,7 +319,7 @@ describe('bidders created by newBidder', function () {
319
319
  expect(ajaxStub.calledOnce).to.equal(true);
320
320
  expect(ajaxStub.firstCall.args[0]).to.equal(url);
321
321
  expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data));
322
- expect(ajaxStub.firstCall.args[3]).to.deep.equal({
322
+ sinon.assert.match(ajaxStub.firstCall.args[3], {
323
323
  method: 'POST',
324
324
  contentType: 'text/plain',
325
325
  withCredentials: true
@@ -344,11 +344,11 @@ describe('bidders created by newBidder', function () {
344
344
  expect(ajaxStub.calledOnce).to.equal(true);
345
345
  expect(ajaxStub.firstCall.args[0]).to.equal(url);
346
346
  expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data));
347
- expect(ajaxStub.firstCall.args[3]).to.deep.equal({
347
+ sinon.assert.match(ajaxStub.firstCall.args[3], {
348
348
  method: 'POST',
349
349
  contentType: 'application/json',
350
350
  withCredentials: true
351
- });
351
+ })
352
352
  });
353
353
 
354
354
  it('should make the appropriate GET request', function () {
@@ -367,10 +367,10 @@ describe('bidders created by newBidder', function () {
367
367
  expect(ajaxStub.calledOnce).to.equal(true);
368
368
  expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`);
369
369
  expect(ajaxStub.firstCall.args[2]).to.be.undefined;
370
- expect(ajaxStub.firstCall.args[3]).to.deep.equal({
370
+ sinon.assert.match(ajaxStub.firstCall.args[3], {
371
371
  method: 'GET',
372
372
  withCredentials: true
373
- });
373
+ })
374
374
  });
375
375
 
376
376
  it('should make the appropriate GET request when options are passed', function () {
@@ -391,10 +391,10 @@ describe('bidders created by newBidder', function () {
391
391
  expect(ajaxStub.calledOnce).to.equal(true);
392
392
  expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`);
393
393
  expect(ajaxStub.firstCall.args[2]).to.be.undefined;
394
- expect(ajaxStub.firstCall.args[3]).to.deep.equal({
394
+ sinon.assert.match(ajaxStub.firstCall.args[3], {
395
395
  method: 'GET',
396
396
  withCredentials: false
397
- });
397
+ })
398
398
  });
399
399
 
400
400
  it('should make multiple calls if the spec returns them', function () {
@@ -420,6 +420,78 @@ describe('bidders created by newBidder', function () {
420
420
  expect(ajaxStub.calledTwice).to.equal(true);
421
421
  });
422
422
 
423
+ describe('browsingTopics ajax option', () => {
424
+ let transmitUfpdAllowed, bidder;
425
+ beforeEach(() => {
426
+ activityRules.isActivityAllowed.reset();
427
+ activityRules.isActivityAllowed.callsFake((activity) => activity === ACTIVITY_TRANSMIT_UFPD ? transmitUfpdAllowed : true);
428
+ bidder = newBidder(spec);
429
+ spec.isBidRequestValid.returns(true);
430
+ });
431
+
432
+ it(`should be set to false when adapter sets browsingTopics = false`, () => {
433
+ transmitUfpdAllowed = true;
434
+ spec.buildRequests.returns([
435
+ {
436
+ method: 'GET',
437
+ url: 'url',
438
+ options: {
439
+ browsingTopics: false
440
+ }
441
+ }
442
+ ]);
443
+ bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback);
444
+ sinon.assert.calledWith(ajaxStub, 'url', sinon.match.any, sinon.match.any, sinon.match({
445
+ browsingTopics: false
446
+ }));
447
+ });
448
+
449
+ Object.entries({
450
+ 'allowed': true,
451
+ 'not allowed': false
452
+ }).forEach(([t, allow]) => {
453
+ it(`should be set to ${allow} when transmitUfpd is ${t}`, () => {
454
+ transmitUfpdAllowed = allow;
455
+ spec.buildRequests.returns([
456
+ {
457
+ method: 'GET',
458
+ url: '1',
459
+ },
460
+ {
461
+ method: 'POST',
462
+ url: '2',
463
+ data: {}
464
+ },
465
+ {
466
+ method: 'GET',
467
+ url: '3',
468
+ options: {
469
+ browsingTopics: true
470
+ }
471
+ },
472
+ {
473
+ method: 'POST',
474
+ url: '4',
475
+ data: {},
476
+ options: {
477
+ browsingTopics: true
478
+ }
479
+ }
480
+ ]);
481
+ bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback);
482
+ ['1', '2', '3', '4'].forEach(url => {
483
+ sinon.assert.calledWith(
484
+ ajaxStub,
485
+ url,
486
+ sinon.match.any,
487
+ sinon.match.any,
488
+ sinon.match({browsingTopics: allow})
489
+ );
490
+ });
491
+ });
492
+ });
493
+ });
494
+
423
495
  it('should not add bids for each placement code if no requests are given', function () {
424
496
  const bidder = newBidder(spec);
425
497
 
@@ -174,7 +174,7 @@ describe('The video cache', function () {
174
174
  const request = server.requests[0];
175
175
  request.method.should.equal('POST');
176
176
  request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache');
177
- request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8');
177
+ request.requestHeaders['Content-Type'].should.equal('text/plain');
178
178
  let payload = {
179
179
  puts: [{
180
180
  type: 'xml',
@@ -224,7 +224,7 @@ describe('The video cache', function () {
224
224
  const request = server.requests[0];
225
225
  request.method.should.equal('POST');
226
226
  request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache');
227
- request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8');
227
+ request.requestHeaders['Content-Type'].should.equal('text/plain');
228
228
  let payload = {
229
229
  puts: [{
230
230
  type: 'xml',
@@ -295,7 +295,7 @@ describe('The video cache', function () {
295
295
  const request = server.requests[0];
296
296
  request.method.should.equal('POST');
297
297
  request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache');
298
- request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8');
298
+ request.requestHeaders['Content-Type'].should.equal('text/plain');
299
299
  let payload = {
300
300
  puts: [{
301
301
  type: 'xml',
@@ -356,7 +356,7 @@ describe('The video cache', function () {
356
356
  const request = server.requests[0];
357
357
  request.method.should.equal('POST');
358
358
  request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache');
359
- request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8');
359
+ request.requestHeaders['Content-Type'].should.equal('text/plain');
360
360
 
361
361
  JSON.parse(request.requestBody).should.deep.equal({
362
362
  puts: [{