prebid.js 7.18.0 → 7.20.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 (313) hide show
  1. package/dist/33acrossBidAdapter.js +1 -1
  2. package/dist/33acrossIdSystem.js +1 -1
  3. package/dist/adWMGAnalyticsAdapter.js +1 -1
  4. package/dist/adagioAnalyticsAdapter.js +1 -1
  5. package/dist/adagioBidAdapter.js +1 -1
  6. package/dist/adbookpspBidAdapter.js +1 -1
  7. package/dist/adfBidAdapter.js +1 -1
  8. package/dist/adgenerationBidAdapter.js +1 -1
  9. package/dist/adkernelAdnAnalyticsAdapter.js +1 -1
  10. package/dist/adkernelBidAdapter.js +1 -1
  11. package/dist/adlooxAdServerVideo.js +1 -1
  12. package/dist/adlooxAnalyticsAdapter.js +1 -1
  13. package/dist/adlooxRtdProvider.js +1 -1
  14. package/dist/adomikAnalyticsAdapter.js +1 -1
  15. package/dist/adqueryIdSystem.js +1 -1
  16. package/dist/adrelevantisBidAdapter.js +1 -1
  17. package/dist/adtrgtmeBidAdapter.js +1 -1
  18. package/dist/adxcgAnalyticsAdapter.js +1 -1
  19. package/dist/adxcgBidAdapter.js +1 -1
  20. package/dist/adxpremiumAnalyticsAdapter.js +1 -1
  21. package/dist/ajaBidAdapter.js +1 -1
  22. package/dist/amxBidAdapter.js +1 -1
  23. package/dist/amxIdSystem.js +1 -1
  24. package/dist/aolBidAdapter.js +1 -1
  25. package/dist/appierAnalyticsAdapter.js +1 -1
  26. package/dist/appnexusBidAdapter.js +1 -1
  27. package/dist/asoBidAdapter.js +1 -1
  28. package/dist/atsAnalyticsAdapter.js +1 -1
  29. package/dist/axonixBidAdapter.js +1 -1
  30. package/dist/{andBeyondMediaBidAdapter.js → beyondmediaBidAdapter.js} +1 -1
  31. package/dist/bidViewability.js +1 -1
  32. package/dist/bidglassBidAdapter.js +1 -1
  33. package/dist/bidwatchAnalyticsAdapter.js +1 -1
  34. package/dist/big-richmediaBidAdapter.js +1 -1
  35. package/dist/bridgewellBidAdapter.js +1 -1
  36. package/dist/brightMountainMediaBidAdapter.js +1 -1
  37. package/dist/byDataAnalyticsAdapter.js +1 -1
  38. package/dist/carodaBidAdapter.js +1 -1
  39. package/dist/categoryTranslation.js +1 -1
  40. package/dist/cleanioRtdProvider.js +1 -1
  41. package/dist/colossussspBidAdapter.js +1 -1
  42. package/dist/concertAnalyticsAdapter.js +1 -1
  43. package/dist/concertBidAdapter.js +1 -1
  44. package/dist/connectIdSystem.js +1 -1
  45. package/dist/connectadBidAdapter.js +1 -1
  46. package/dist/consentManagement.js +1 -1
  47. package/dist/consentManagementUsp.js +1 -1
  48. package/dist/consumableBidAdapter.js +1 -1
  49. package/dist/conversantAnalyticsAdapter.js +1 -0
  50. package/dist/conversantBidAdapter.js +1 -1
  51. package/dist/craftBidAdapter.js +1 -1
  52. package/dist/criteoBidAdapter.js +1 -1
  53. package/dist/currency.js +1 -1
  54. package/dist/datablocksAnalyticsAdapter.js +1 -1
  55. package/dist/dchain.js +1 -1
  56. package/dist/debugging-standalone.js +1 -1
  57. package/dist/debugging.js +1 -1
  58. package/dist/dependencies.json +9 -0
  59. package/dist/dfpAdServerVideo.js +1 -1
  60. package/dist/dgkeywordRtdProvider.js +1 -1
  61. package/dist/dianomiBidAdapter.js +1 -1
  62. package/dist/dspxBidAdapter.js +1 -1
  63. package/dist/enrichmentFpdModule.js +1 -1
  64. package/dist/eplanningAnalyticsAdapter.js +1 -1
  65. package/dist/eplanningBidAdapter.js +1 -1
  66. package/dist/finativeBidAdapter.js +1 -1
  67. package/dist/fintezaAnalyticsAdapter.js +1 -1
  68. package/dist/fpd.js +1 -0
  69. package/dist/ftrackIdSystem.js +1 -1
  70. package/dist/gdprEnforcement.js +1 -1
  71. package/dist/glimpseBidAdapter.js +1 -1
  72. package/dist/gmosspBidAdapter.js +1 -1
  73. package/dist/goldbachBidAdapter.js +1 -1
  74. package/dist/googleAnalyticsAdapter.js +1 -1
  75. package/dist/gridBidAdapter.js +1 -1
  76. package/dist/gridNMBidAdapter.js +1 -1
  77. package/dist/gumgumBidAdapter.js +1 -1
  78. package/dist/h12mediaBidAdapter.js +1 -1
  79. package/dist/hadronAnalyticsAdapter.js +1 -1
  80. package/dist/id5AnalyticsAdapter.js +1 -1
  81. package/dist/id5IdSystem.js +1 -1
  82. package/dist/improvedigitalBidAdapter.js +1 -1
  83. package/dist/inmarBidAdapter.js +1 -1
  84. package/dist/insticatorBidAdapter.js +1 -1
  85. package/dist/invisiblyAnalyticsAdapter.js +1 -1
  86. package/dist/ixBidAdapter.js +1 -1
  87. package/dist/justpremiumBidAdapter.js +1 -1
  88. package/dist/kargoAnalyticsAdapter.js +1 -1
  89. package/dist/kinessoIdSystem.js +1 -1
  90. package/dist/konduitAnalyticsAdapter.js +1 -1
  91. package/dist/kueezBidAdapter.js +1 -1
  92. package/dist/lassoBidAdapter.js +1 -1
  93. package/dist/lifestreetBidAdapter.js +1 -1
  94. package/dist/liveIntentAnalyticsAdapter.js +1 -1
  95. package/dist/liveIntentIdSystem.js +1 -1
  96. package/dist/livewrappedAnalyticsAdapter.js +1 -1
  97. package/dist/liveyieldAnalyticsAdapter.js +1 -1
  98. package/dist/logicadBidAdapter.js +1 -1
  99. package/dist/loglyliftBidAdapter.js +1 -1
  100. package/dist/lotamePanoramaIdSystem.js +1 -1
  101. package/dist/magniteAnalyticsAdapter.js +1 -0
  102. package/dist/malltvAnalyticsAdapter.js +1 -1
  103. package/dist/marsmediaAnalyticsAdapter.js +1 -1
  104. package/dist/marsmediaBidAdapter.js +1 -1
  105. package/dist/mass.js +1 -1
  106. package/dist/mediafuseBidAdapter.js +1 -1
  107. package/dist/mediakeysBidAdapter.js +1 -1
  108. package/dist/medianetAnalyticsAdapter.js +1 -1
  109. package/dist/mediasquareBidAdapter.js +1 -1
  110. package/dist/mgidBidAdapter.js +1 -1
  111. package/dist/minutemediaBidAdapter.js +1 -1
  112. package/dist/multibid.js +1 -1
  113. package/dist/nativoBidAdapter.js +1 -1
  114. package/dist/not-for-prod/prebid.js +198 -194
  115. package/dist/oguryBidAdapter.js +1 -1
  116. package/dist/onetagBidAdapter.js +1 -1
  117. package/dist/ooloAnalyticsAdapter.js +1 -1
  118. package/dist/openxAnalyticsAdapter.js +1 -1
  119. package/dist/optimonAnalyticsAdapter.js +1 -1
  120. package/dist/outbrainBidAdapter.js +1 -1
  121. package/dist/parrableIdSystem.js +1 -1
  122. package/dist/pianoDmpAnalyticsAdapter.js +1 -1
  123. package/dist/pixfutureBidAdapter.js +1 -1
  124. package/dist/prebid-core.js +2 -2
  125. package/dist/prebidServerBidAdapter.js +1 -1
  126. package/dist/prebidmanagerAnalyticsAdapter.js +1 -1
  127. package/dist/priceFloors.js +1 -1
  128. package/dist/publinkIdSystem.js +1 -1
  129. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  130. package/dist/pubmaticBidAdapter.js +1 -1
  131. package/dist/pubperfAnalyticsAdapter.js +1 -1
  132. package/dist/pubstackAnalyticsAdapter.js +1 -1
  133. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  134. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  135. package/dist/pulsepointAnalyticsAdapter.js +1 -1
  136. package/dist/pxyzBidAdapter.js +1 -1
  137. package/dist/quantcastBidAdapter.js +1 -1
  138. package/dist/quantcastIdSystem.js +1 -1
  139. package/dist/readpeakBidAdapter.js +1 -1
  140. package/dist/realvuAnalyticsAdapter.js +1 -1
  141. package/dist/relaidoBidAdapter.js +1 -1
  142. package/dist/relevantAnalyticsAdapter.js +1 -1
  143. package/dist/rhythmoneBidAdapter.js +1 -1
  144. package/dist/riseBidAdapter.js +1 -1
  145. package/dist/rivrAnalyticsAdapter.js +1 -1
  146. package/dist/roxotAnalyticsAdapter.js +1 -1
  147. package/dist/rtdModule.js +1 -1
  148. package/dist/rubiconAnalyticsAdapter.js +1 -1
  149. package/dist/rubiconBidAdapter.js +1 -1
  150. package/dist/s2sTesting.js +1 -1
  151. package/dist/scaleableAnalyticsAdapter.js +1 -1
  152. package/dist/schain.js +1 -1
  153. package/dist/seedingAllianceBidAdapter.js +1 -1
  154. package/dist/seedtagBidAdapter.js +1 -1
  155. package/dist/sharedIdSystem.js +1 -1
  156. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  157. package/dist/sharethroughBidAdapter.js +1 -1
  158. package/dist/shinezBidAdapter.js +1 -1
  159. package/dist/sigmoidAnalyticsAdapter.js +1 -1
  160. package/dist/smaatoBidAdapter.js +1 -1
  161. package/dist/smartadserverBidAdapter.js +1 -1
  162. package/dist/smartxBidAdapter.js +1 -1
  163. package/dist/smilewantedBidAdapter.js +1 -1
  164. package/dist/sonobiAnalyticsAdapter.js +1 -1
  165. package/dist/sonobiBidAdapter.js +1 -1
  166. package/dist/sovrnAnalyticsAdapter.js +1 -1
  167. package/dist/sovrnBidAdapter.js +1 -1
  168. package/dist/sspBCBidAdapter.js +1 -1
  169. package/dist/staqAnalyticsAdapter.js +1 -1
  170. package/dist/sublimeBidAdapter.js +1 -1
  171. package/dist/synacormediaBidAdapter.js +1 -1
  172. package/dist/taboolaBidAdapter.js +1 -1
  173. package/dist/tapadIdSystem.js +1 -1
  174. package/dist/targetVideoBidAdapter.js +1 -1
  175. package/dist/teadsBidAdapter.js +1 -1
  176. package/dist/teadsIdSystem.js +1 -0
  177. package/dist/terceptAnalyticsAdapter.js +1 -1
  178. package/dist/trionBidAdapter.js +1 -1
  179. package/dist/tripleliftBidAdapter.js +1 -1
  180. package/dist/ttdBidAdapter.js +1 -1
  181. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  182. package/dist/underdogmediaBidAdapter.js +1 -1
  183. package/dist/undertoneBidAdapter.js +1 -1
  184. package/dist/userId.js +1 -1
  185. package/dist/vidazooBidAdapter.js +1 -1
  186. package/dist/videobyteBidAdapter.js +1 -1
  187. package/dist/visxBidAdapter.js +1 -1
  188. package/dist/vrtcalBidAdapter.js +1 -1
  189. package/dist/vuukleBidAdapter.js +1 -1
  190. package/dist/weboramaRtdProvider.js +1 -1
  191. package/dist/widespaceBidAdapter.js +1 -1
  192. package/dist/winrBidAdapter.js +1 -1
  193. package/dist/yahoosspBidAdapter.js +1 -1
  194. package/dist/yandexBidAdapter.js +1 -1
  195. package/dist/yieldmoBidAdapter.js +1 -1
  196. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  197. package/dist/yuktamediaAnalyticsAdapter.js +1 -1
  198. package/dist/zeta_global_sspAnalyticsAdapter.js +1 -1
  199. package/libraries/fpd/sua.js +98 -0
  200. package/modules/.submodules.json +1 -0
  201. package/modules/adfBidAdapter.js +1 -1
  202. package/modules/adkernelAdnBidAdapter.js +1 -0
  203. package/modules/adkernelBidAdapter.js +2 -2
  204. package/modules/adqueryIdSystem.js +8 -8
  205. package/modules/adxcgBidAdapter.js +1 -1
  206. package/modules/amxBidAdapter.js +1 -1
  207. package/modules/aolBidAdapter.js +25 -2
  208. package/modules/appnexusBidAdapter.js +19 -2
  209. package/modules/{andBeyondMediaBidAdapter.js → beyondmediaBidAdapter.js} +0 -0
  210. package/modules/{andBeyondMediaBidAdapter.md → beyondmediaBidAdapter.md} +1 -1
  211. package/modules/categoryTranslation.js +4 -4
  212. package/modules/cleanioRtdProvider.js +3 -4
  213. package/modules/colossussspBidAdapter.js +2 -1
  214. package/modules/connectIdSystem.js +16 -1
  215. package/modules/conversantAnalyticsAdapter.js +560 -0
  216. package/modules/conversantAnalyticsAdapter.md +47 -0
  217. package/modules/criteoBidAdapter.js +1 -1
  218. package/modules/currency.js +11 -11
  219. package/modules/dchain.js +2 -2
  220. package/modules/debugging/bidInterceptor.js +2 -2
  221. package/modules/debugging/debugging.js +10 -2
  222. package/modules/debugging/legacy.js +2 -2
  223. package/modules/dgkeywordRtdProvider.js +21 -3
  224. package/modules/dianomiBidAdapter.js +1 -1
  225. package/modules/enrichmentFpdModule.js +33 -14
  226. package/modules/eplanningBidAdapter.js +48 -2
  227. package/modules/finativeBidAdapter.js +1 -1
  228. package/modules/gridBidAdapter.js +187 -117
  229. package/modules/improvedigitalBidAdapter.js +2 -2
  230. package/modules/ixBidAdapter.js +94 -23
  231. package/modules/liveIntentIdSystem.js +39 -5
  232. package/modules/lotamePanoramaIdSystem.js +10 -6
  233. package/modules/magniteAnalyticsAdapter.js +902 -0
  234. package/modules/magniteAnalyticsAdapter.md +18 -0
  235. package/modules/mass.js +2 -2
  236. package/modules/mediakeysBidAdapter.js +6 -1
  237. package/modules/mediakeysBidAdapter.md +50 -40
  238. package/modules/multibid/index.js +5 -5
  239. package/modules/nativoBidAdapter.js +1 -1
  240. package/modules/pixfutureBidAdapter.js +46 -15
  241. package/modules/prebidServerBidAdapter/index.js +54 -19
  242. package/modules/priceFloors.js +8 -22
  243. package/modules/readpeakBidAdapter.js +1 -1
  244. package/modules/rubiconAnalyticsAdapter.js +14 -9
  245. package/modules/rubiconBidAdapter.js +2 -3
  246. package/modules/seedingAllianceBidAdapter.js +1 -1
  247. package/modules/sharethroughBidAdapter.js +1 -1
  248. package/modules/taboolaBidAdapter.js +5 -5
  249. package/modules/talkadsBidAdapter.js +1 -0
  250. package/modules/teadsIdSystem.js +234 -0
  251. package/modules/teadsIdSystem.md +22 -0
  252. package/modules/truereachBidAdapter.js +1 -0
  253. package/modules/ttdBidAdapter.js +1 -1
  254. package/modules/userId/index.js +21 -14
  255. package/modules/vrtcalBidAdapter.js +7 -1
  256. package/modules/vuukleBidAdapter.js +34 -3
  257. package/modules/yahoosspBidAdapter.js +4 -2
  258. package/modules/yandexBidAdapter.js +31 -8
  259. package/package.json +2 -2
  260. package/src/adapterManager.js +2 -4
  261. package/src/adapters/bidderFactory.js +13 -11
  262. package/src/auction.js +140 -69
  263. package/src/constants.json +8 -0
  264. package/src/cpmBucketManager.js +22 -3
  265. package/src/native.js +1 -5
  266. package/src/prebid.js +5 -6
  267. package/src/targeting.js +31 -14
  268. package/test/fixtures/fixtures.js +2 -1
  269. package/test/spec/auctionmanager_spec.js +109 -21
  270. package/test/spec/cpmBucketManager_spec.js +239 -177
  271. package/test/spec/fpd/sua_spec.js +244 -0
  272. package/test/spec/modules/adfBidAdapter_spec.js +2 -3
  273. package/test/spec/modules/adqueryIdSystem_spec.js +0 -2
  274. package/test/spec/modules/adxcgBidAdapter_spec.js +2 -3
  275. package/test/spec/modules/amxBidAdapter_spec.js +3 -2
  276. package/test/spec/modules/aolBidAdapter_spec.js +42 -9
  277. package/test/spec/modules/appnexusBidAdapter_spec.js +24 -0
  278. package/test/spec/modules/{andBeyondMediaBidAdapter_spec.js → beyondmediaBidAdapter_spec.js} +1 -1
  279. package/test/spec/modules/colossussspBidAdapter_spec.js +4 -2
  280. package/test/spec/modules/connectIdSystem_spec.js +20 -0
  281. package/test/spec/modules/conversantAnalyticsAdapter_spec.js +963 -0
  282. package/test/spec/modules/currency_spec.js +14 -6
  283. package/test/spec/modules/debugging_mod_spec.js +9 -0
  284. package/test/spec/modules/dgkeywordRtdProvider_spec.js +83 -1
  285. package/test/spec/modules/dianomiBidAdapter_spec.js +2 -3
  286. package/test/spec/modules/enrichmentFpdModule_spec.js +50 -7
  287. package/test/spec/modules/eplanningBidAdapter_spec.js +285 -1
  288. package/test/spec/modules/fpdModule_spec.js +9 -0
  289. package/test/spec/modules/gridBidAdapter_spec.js +87 -31
  290. package/test/spec/modules/ixBidAdapter_spec.js +221 -2
  291. package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +62 -8
  292. package/test/spec/modules/liveIntentIdSystem_spec.js +62 -8
  293. package/test/spec/modules/lotamePanoramaIdSystem_spec.js +0 -1
  294. package/test/spec/modules/magniteAnalyticsAdapter_spec.js +1942 -0
  295. package/test/spec/modules/mediakeysBidAdapter_spec.js +20 -3
  296. package/test/spec/modules/nativoBidAdapter_spec.js +13 -2
  297. package/test/spec/modules/prebidServerBidAdapter_spec.js +202 -15
  298. package/test/spec/modules/priceFloors_spec.js +5 -5
  299. package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +2 -3
  300. package/test/spec/modules/seedtagBidAdapter_spec.js +15 -18
  301. package/test/spec/modules/sharethroughBidAdapter_spec.js +2 -1
  302. package/test/spec/modules/taboolaBidAdapter_spec.js +112 -13
  303. package/test/spec/modules/teadsIdSystem_spec.js +271 -0
  304. package/test/spec/modules/ttdBidAdapter_spec.js +3 -3
  305. package/test/spec/modules/userId_spec.js +39 -4
  306. package/test/spec/modules/vrtcalBidAdapter_spec.js +12 -1
  307. package/test/spec/modules/vuukleBidAdapter_spec.js +87 -0
  308. package/test/spec/modules/yahoosspBidAdapter_spec.js +48 -1
  309. package/test/spec/modules/yandexBidAdapter_spec.js +8 -5
  310. package/test/spec/native_spec.js +5 -0
  311. package/test/spec/unit/core/bidderFactory_spec.js +66 -11
  312. package/test/spec/unit/core/targeting_spec.js +28 -9
  313. package/test/spec/unit/pbjs_api_spec.js +48 -2
@@ -0,0 +1,1942 @@
1
+ import magniteAdapter, {
2
+ parseBidResponse,
3
+ getHostNameFromReferer,
4
+ storage,
5
+ rubiConf,
6
+ } from '../../../modules/magniteAnalyticsAdapter.js';
7
+ import CONSTANTS from 'src/constants.json';
8
+ import { config } from 'src/config.js';
9
+ import { server } from 'test/mocks/xhr.js';
10
+ import * as mockGpt from '../integration/faker/googletag.js';
11
+ import {
12
+ setConfig,
13
+ addBidResponseHook,
14
+ } from 'modules/currency.js';
15
+ import { getGlobal } from '../../../src/prebidGlobal.js';
16
+ import { deepAccess } from '../../../src/utils.js';
17
+
18
+ let events = require('src/events.js');
19
+ let utils = require('src/utils.js');
20
+
21
+ const {
22
+ EVENTS: {
23
+ AUCTION_INIT,
24
+ AUCTION_END,
25
+ BID_REQUESTED,
26
+ BID_RESPONSE,
27
+ BIDDER_DONE,
28
+ BID_WON,
29
+ BID_TIMEOUT,
30
+ BILLABLE_EVENT
31
+ }
32
+ } = CONSTANTS;
33
+
34
+ const STUBBED_UUID = '12345678-1234-1234-1234-123456789abc';
35
+
36
+ // Mock Event Data
37
+ const MOCK = {
38
+ AUCTION_INIT: {
39
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
40
+ 'timestamp': 1658868383741,
41
+ 'adUnits': [
42
+ {
43
+ 'code': 'box',
44
+ 'mediaTypes': {
45
+ 'banner': {
46
+ 'sizes': [
47
+ [
48
+ 300,
49
+ 250
50
+ ]
51
+ ]
52
+ }
53
+ },
54
+ 'bids': [
55
+ {
56
+ 'bidder': 'rubicon',
57
+ 'params': {
58
+ 'accountId': 1001,
59
+ 'siteId': 267318,
60
+ 'zoneId': 1861698
61
+ }
62
+ }
63
+ ],
64
+ 'sizes': [
65
+ [
66
+ 300,
67
+ 250
68
+ ]
69
+ ],
70
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
71
+ 'ortb2Imp': {
72
+ 'ext': {
73
+ 'tid': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
74
+ 'data': {
75
+ 'adserver': {
76
+ 'name': 'gam',
77
+ 'adslot': '/1234567/prebid-slot'
78
+ },
79
+ 'pbadslot': '/1234567/prebid-slot'
80
+ },
81
+ 'gpid': '/1234567/prebid-slot'
82
+ }
83
+ }
84
+ }
85
+ ],
86
+ 'bidderRequests': [
87
+ {
88
+ 'bidderCode': 'rubicon',
89
+ 'bids': [
90
+ {
91
+ 'bidder': 'rubicon',
92
+ 'params': {
93
+ 'accountId': 1001,
94
+ 'siteId': 267318,
95
+ 'zoneId': 1861698,
96
+ },
97
+ 'adUnitCode': 'box',
98
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
99
+ 'bidId': '23fcd8cf4bf0d7',
100
+ 'src': 'client',
101
+ 'startTime': 1658868383748
102
+ }
103
+ ],
104
+ 'refererInfo': {
105
+ 'page': 'http://a-test-domain.com:8000/test_pages/sanity/TEMP/prebidTest.html?pbjs_debug=true',
106
+ },
107
+ }
108
+ ],
109
+ 'timeout': 3000,
110
+ 'config': {
111
+ 'accountId': 1001,
112
+ 'endpoint': 'https://pba-event-service-alb-dev.use1.fanops.net/event'
113
+ }
114
+ },
115
+ BID_REQUESTED: {
116
+ 'bidderCode': 'rubicon',
117
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
118
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
119
+ 'bids': [
120
+ {
121
+ 'bidder': 'rubicon',
122
+ 'params': {
123
+ 'accountId': 1001,
124
+ 'siteId': 267318,
125
+ 'zoneId': 1861698,
126
+ },
127
+ 'adUnitCode': 'box',
128
+ 'bidId': '23fcd8cf4bf0d7',
129
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
130
+ 'src': 'client',
131
+ }
132
+ ]
133
+ },
134
+ BID_RESPONSE: {
135
+ 'bidderCode': 'rubicon',
136
+ 'width': 300,
137
+ 'height': 250,
138
+ 'adId': '3c0b59947ced11',
139
+ 'requestId': '23fcd8cf4bf0d7',
140
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
141
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
142
+ 'mediaType': 'banner',
143
+ 'source': 'client',
144
+ 'currency': 'USD',
145
+ 'creativeId': '4954828',
146
+ 'cpm': 3.4,
147
+ 'ttl': 300,
148
+ 'netRevenue': true,
149
+ 'ad': '<html></html>',
150
+ 'bidder': 'rubicon',
151
+ 'adUnitCode': 'box',
152
+ 'timeToRespond': 271,
153
+ 'size': '300x250',
154
+ 'status': 'rendered',
155
+ getStatusCode: () => 1,
156
+ },
157
+ AUCTION_END: {
158
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
159
+ 'auctionEnd': 1658868384019,
160
+ },
161
+ BIDDER_DONE: {
162
+ 'bidderCode': 'rubicon',
163
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
164
+ 'bids': [
165
+ {
166
+ 'bidder': 'rubicon',
167
+ 'adUnitCode': 'box',
168
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
169
+ 'bidId': '23fcd8cf4bf0d7',
170
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
171
+ 'src': 'client',
172
+ }
173
+ ]
174
+ },
175
+ BID_WON: {
176
+ 'bidderCode': 'rubicon',
177
+ 'bidId': '23fcd8cf4bf0d7',
178
+ 'adId': '3c0b59947ced11',
179
+ 'requestId': '23fcd8cf4bf0d7',
180
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
181
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
182
+ 'mediaType': 'banner',
183
+ 'currency': 'USD',
184
+ 'cpm': 3.4,
185
+ 'ttl': 300,
186
+ 'bidder': 'rubicon',
187
+ 'adUnitCode': 'box',
188
+ 'status': 'rendered',
189
+ }
190
+ }
191
+
192
+ const ANALYTICS_MESSAGE = {
193
+ 'channel': 'web',
194
+ 'integration': 'pbjs',
195
+ 'referrerUri': 'http://a-test-domain.com:8000/test_pages/sanity/TEMP/prebidTest.html?pbjs_debug=true',
196
+ 'version': '$prebid.version$',
197
+ 'referrerHostname': 'a-test-domain.com',
198
+ 'timestamps': {
199
+ 'timeSincePageLoad': 500,
200
+ 'eventTime': 1519767014281,
201
+ 'prebidLoaded': magniteAdapter.MODULE_INITIALIZED_TIME
202
+ },
203
+ 'wrapper': {
204
+ 'name': '10000_fakewrapper_test'
205
+ },
206
+ 'session': {
207
+ 'id': '12345678-1234-1234-1234-123456789abc',
208
+ 'pvid': '12345678',
209
+ 'start': 1519767013781,
210
+ 'expires': 1519788613781
211
+ },
212
+ 'auctions': [
213
+ {
214
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
215
+ 'auctionStart': 1658868383741,
216
+ 'samplingFactor': 1,
217
+ 'clientTimeoutMillis': 3000,
218
+ 'accountId': 1001,
219
+ 'bidderOrder': [
220
+ 'rubicon'
221
+ ],
222
+ 'serverTimeoutMillis': 1000,
223
+ 'adUnits': [
224
+ {
225
+ 'adUnitCode': 'box',
226
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
227
+ 'mediaTypes': [
228
+ 'banner'
229
+ ],
230
+ 'dimensions': [
231
+ {
232
+ 'width': 300,
233
+ 'height': 250
234
+ }
235
+ ],
236
+ 'pbAdSlot': '/1234567/prebid-slot',
237
+ 'gpid': '/1234567/prebid-slot',
238
+ 'bids': [
239
+ {
240
+ 'bidder': 'rubicon',
241
+ 'bidId': '23fcd8cf4bf0d7',
242
+ 'source': 'client',
243
+ 'status': 'success',
244
+ 'clientLatencyMillis': 271,
245
+ 'bidResponse': {
246
+ 'bidPriceUSD': 3.4,
247
+ 'mediaType': 'banner',
248
+ 'dimensions': {
249
+ 'width': 300,
250
+ 'height': 250
251
+ }
252
+ }
253
+ }
254
+ ],
255
+ 'accountId': 1001,
256
+ 'siteId': 267318,
257
+ 'zoneId': 1861698,
258
+ 'status': 'success'
259
+ }
260
+ ],
261
+ 'auctionEnd': 1658868384019
262
+ }
263
+ ],
264
+ 'gamRenders': [
265
+ {
266
+ 'adSlot': 'box',
267
+ 'advertiserId': 1111,
268
+ 'creativeId': 2222,
269
+ 'lineItemId': 3333,
270
+ 'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
271
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a'
272
+ }
273
+ ],
274
+ 'bidsWon': [
275
+ {
276
+ 'bidder': 'rubicon',
277
+ 'bidId': '23fcd8cf4bf0d7',
278
+ 'source': 'client',
279
+ 'status': 'success',
280
+ 'clientLatencyMillis': 271,
281
+ 'bidResponse': {
282
+ 'bidPriceUSD': 3.4,
283
+ 'mediaType': 'banner',
284
+ 'dimensions': {
285
+ 'width': 300,
286
+ 'height': 250
287
+ }
288
+ },
289
+ 'sourceAuctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
290
+ 'renderAuctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
291
+ 'sourceTransactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
292
+ 'renderTransactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
293
+ 'transactionId': '7b10a106-89ea-4e19-bc51-9b2e970fc42a',
294
+ 'accountId': 1001,
295
+ 'siteId': 267318,
296
+ 'zoneId': 1861698,
297
+ 'mediaTypes': [
298
+ 'banner'
299
+ ],
300
+ 'adUnitCode': 'box'
301
+ }
302
+ ],
303
+ 'trigger': 'gam-delayed'
304
+ }
305
+
306
+ describe('magnite analytics adapter', function () {
307
+ let sandbox;
308
+ let clock;
309
+ let getDataFromLocalStorageStub, setDataInLocalStorageStub, localStorageIsEnabledStub;
310
+ let gptSlot0;
311
+ let gptSlotRenderEnded0;
312
+ beforeEach(function () {
313
+ mockGpt.enable();
314
+ gptSlot0 = mockGpt.makeSlot({ code: 'box' });
315
+ gptSlotRenderEnded0 = {
316
+ eventName: 'slotRenderEnded',
317
+ params: {
318
+ slot: gptSlot0,
319
+ isEmpty: false,
320
+ advertiserId: 1111,
321
+ sourceAgnosticCreativeId: 2222,
322
+ sourceAgnosticLineItemId: 3333
323
+ }
324
+ };
325
+ getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
326
+ setDataInLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage');
327
+ localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled');
328
+ sandbox = sinon.sandbox.create();
329
+
330
+ localStorageIsEnabledStub.returns(true);
331
+
332
+ sandbox.stub(events, 'getEvents').returns([]);
333
+
334
+ sandbox.stub(utils, 'generateUUID').returns(STUBBED_UUID);
335
+
336
+ clock = sandbox.useFakeTimers(1519767013781);
337
+
338
+ magniteAdapter.referrerHostname = '';
339
+
340
+ config.setConfig({
341
+ s2sConfig: {
342
+ timeout: 1000,
343
+ accountId: 10000,
344
+ },
345
+ rubicon: {
346
+ wrapperName: '10000_fakewrapper_test'
347
+ }
348
+ })
349
+ });
350
+
351
+ afterEach(function () {
352
+ sandbox.restore();
353
+ config.resetConfig();
354
+ mockGpt.enable();
355
+ getDataFromLocalStorageStub.restore();
356
+ setDataInLocalStorageStub.restore();
357
+ localStorageIsEnabledStub.restore();
358
+ magniteAdapter.disableAnalytics();
359
+ });
360
+
361
+ it('should require accountId', function () {
362
+ sandbox.stub(utils, 'logError');
363
+
364
+ magniteAdapter.enableAnalytics({
365
+ options: {
366
+ endpoint: '//localhost:9999/event'
367
+ }
368
+ });
369
+
370
+ expect(utils.logError.called).to.equal(true);
371
+ });
372
+
373
+ it('should require endpoint', function () {
374
+ sandbox.stub(utils, 'logError');
375
+
376
+ magniteAdapter.enableAnalytics({
377
+ options: {
378
+ accountId: 1001
379
+ }
380
+ });
381
+
382
+ expect(utils.logError.called).to.equal(true);
383
+ });
384
+
385
+ describe('config subscribe', function () {
386
+ it('should update the pvid if user asks', function () {
387
+ expect(utils.generateUUID.called).to.equal(false);
388
+ config.setConfig({ rubicon: { updatePageView: true } });
389
+ expect(utils.generateUUID.called).to.equal(true);
390
+ });
391
+ it('should merge in and preserve older set configs', function () {
392
+ config.setConfig({
393
+ rubicon: {
394
+ wrapperName: '1001_general',
395
+ int_type: 'dmpbjs',
396
+ fpkvs: {
397
+ source: 'fb'
398
+ },
399
+ updatePageView: true
400
+ }
401
+ });
402
+ expect(rubiConf).to.deep.equal({
403
+ analyticsEventDelay: 500,
404
+ analyticsBatchTimeout: 5000,
405
+ analyticsProcessDelay: 1,
406
+ dmBilling: {
407
+ enabled: false,
408
+ vendors: [],
409
+ waitForAuction: true
410
+ },
411
+ pvid: '12345678',
412
+ wrapperName: '1001_general',
413
+ int_type: 'dmpbjs',
414
+ fpkvs: {
415
+ source: 'fb'
416
+ },
417
+ updatePageView: true
418
+ });
419
+
420
+ // update it with stuff
421
+ config.setConfig({
422
+ rubicon: {
423
+ analyticsBatchTimeout: 3000,
424
+ fpkvs: {
425
+ link: 'email'
426
+ }
427
+ }
428
+ });
429
+ expect(rubiConf).to.deep.equal({
430
+ analyticsEventDelay: 500,
431
+ analyticsBatchTimeout: 3000,
432
+ analyticsProcessDelay: 1,
433
+ dmBilling: {
434
+ enabled: false,
435
+ vendors: [],
436
+ waitForAuction: true
437
+ },
438
+ pvid: '12345678',
439
+ wrapperName: '1001_general',
440
+ int_type: 'dmpbjs',
441
+ fpkvs: {
442
+ source: 'fb',
443
+ link: 'email'
444
+ },
445
+ updatePageView: true
446
+ });
447
+
448
+ // overwriting specific edge keys should update them
449
+ config.setConfig({
450
+ rubicon: {
451
+ fpkvs: {
452
+ link: 'iMessage',
453
+ source: 'twitter'
454
+ }
455
+ }
456
+ });
457
+ expect(rubiConf).to.deep.equal({
458
+ analyticsEventDelay: 500,
459
+ analyticsBatchTimeout: 3000,
460
+ analyticsProcessDelay: 1,
461
+ dmBilling: {
462
+ enabled: false,
463
+ vendors: [],
464
+ waitForAuction: true
465
+ },
466
+ pvid: '12345678',
467
+ wrapperName: '1001_general',
468
+ int_type: 'dmpbjs',
469
+ fpkvs: {
470
+ link: 'iMessage',
471
+ source: 'twitter'
472
+ },
473
+ updatePageView: true
474
+ });
475
+ });
476
+ });
477
+
478
+ describe('when handling events', function () {
479
+ function performStandardAuction({
480
+ gptEvents = [gptSlotRenderEnded0],
481
+ auctionId = MOCK.AUCTION_INIT.auctionId,
482
+ eventDelay = rubiConf.analyticsEventDelay,
483
+ sendBidWon = true
484
+ } = {}) {
485
+ events.emit(AUCTION_INIT, { ...MOCK.AUCTION_INIT, auctionId });
486
+ events.emit(BID_REQUESTED, { ...MOCK.BID_REQUESTED, auctionId });
487
+ events.emit(BID_RESPONSE, { ...MOCK.BID_RESPONSE, auctionId });
488
+ events.emit(BIDDER_DONE, { ...MOCK.BIDDER_DONE, auctionId });
489
+ events.emit(AUCTION_END, { ...MOCK.AUCTION_END, auctionId });
490
+
491
+ if (gptEvents && gptEvents.length) {
492
+ gptEvents.forEach(gptEvent => mockGpt.emitEvent(gptEvent.eventName, gptEvent.params));
493
+ }
494
+
495
+ if (sendBidWon) {
496
+ events.emit(BID_WON, { ...MOCK.BID_WON, auctionId });
497
+ }
498
+
499
+ if (eventDelay > 0) {
500
+ clock.tick(eventDelay);
501
+ }
502
+ }
503
+
504
+ beforeEach(function () {
505
+ magniteAdapter.enableAnalytics({
506
+ options: {
507
+ endpoint: '//localhost:9999/event',
508
+ accountId: 1001
509
+ }
510
+ });
511
+ config.setConfig({ rubicon: { updatePageView: true } });
512
+ });
513
+
514
+ it('should build a batched message from prebid events', function () {
515
+ performStandardAuction();
516
+
517
+ expect(server.requests.length).to.equal(1);
518
+ let request = server.requests[0];
519
+
520
+ expect(request.url).to.equal('//localhost:9999/event');
521
+
522
+ let message = JSON.parse(request.requestBody);
523
+
524
+ expect(message).to.deep.equal(ANALYTICS_MESSAGE);
525
+ });
526
+
527
+ it('should pass along bidderOrder correctly', function () {
528
+ const auctionInit = utils.deepClone(MOCK.AUCTION_INIT);
529
+
530
+ auctionInit.bidderRequests = auctionInit.bidderRequests.concat([
531
+ { bidderCode: 'pubmatic' },
532
+ { bidderCode: 'ix' },
533
+ { bidderCode: 'appnexus' }
534
+ ])
535
+
536
+ events.emit(AUCTION_INIT, auctionInit);
537
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
538
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
539
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
540
+ clock.tick(rubiConf.analyticsBatchTimeout + 1000);
541
+
542
+ let message = JSON.parse(server.requests[0].requestBody);
543
+ expect(message.auctions[0].bidderOrder).to.deep.equal([
544
+ 'rubicon',
545
+ 'pubmatic',
546
+ 'ix',
547
+ 'appnexus'
548
+ ]);
549
+ });
550
+
551
+ it('should pass along user ids', function () {
552
+ let auctionInit = utils.deepClone(MOCK.AUCTION_INIT);
553
+ auctionInit.bidderRequests[0].bids[0].userId = {
554
+ criteoId: 'sadfe4334',
555
+ lotamePanoramaId: 'asdf3gf4eg',
556
+ pubcid: 'dsfa4545-svgdfs5',
557
+ sharedId: { id1: 'asdf', id2: 'sadf4344' }
558
+ };
559
+
560
+ events.emit(AUCTION_INIT, auctionInit);
561
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
562
+ events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
563
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
564
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
565
+ clock.tick(rubiConf.analyticsBatchTimeout + 1000);
566
+
567
+ let message = JSON.parse(server.requests[0].requestBody);
568
+
569
+ expect(message.auctions[0].user).to.deep.equal({
570
+ ids: [
571
+ { provider: 'criteoId', 'hasId': true },
572
+ { provider: 'lotamePanoramaId', 'hasId': true },
573
+ { provider: 'pubcid', 'hasId': true },
574
+ { provider: 'sharedId', 'hasId': true },
575
+ ]
576
+ });
577
+ });
578
+
579
+ // A-Domain tests
580
+ [
581
+ { input: ['magnite.com'], expected: ['magnite.com'] },
582
+ { input: ['magnite.com', 'prebid.org'], expected: ['magnite.com', 'prebid.org'] },
583
+ { input: [123, 'prebid.org', false, true, [], 'magnite.com', {}], expected: ['prebid.org', 'magnite.com'] },
584
+ { input: 'not array', expected: undefined },
585
+ { input: [], expected: undefined },
586
+ ].forEach((test, index) => {
587
+ it(`should handle adomain correctly - #${index + 1}`, function () {
588
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
589
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
590
+
591
+ let bidResponse = utils.deepClone(MOCK.BID_RESPONSE);
592
+ bidResponse.meta = {
593
+ advertiserDomains: test.input
594
+ }
595
+
596
+ events.emit(BID_RESPONSE, bidResponse);
597
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
598
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
599
+ events.emit(BID_WON, MOCK.BID_WON);
600
+ clock.tick(rubiConf.analyticsBatchTimeout + 1000);
601
+
602
+ let message = JSON.parse(server.requests[0].requestBody);
603
+ expect(message.auctions[0].adUnits[0].bids[0].bidResponse.adomains).to.deep.equal(test.expected);
604
+ });
605
+ });
606
+
607
+ describe('with session handling', function () {
608
+ const expectedPvid = STUBBED_UUID.slice(0, 8);
609
+ beforeEach(function () {
610
+ config.setConfig({ rubicon: { updatePageView: true } });
611
+ });
612
+
613
+ it('should not log any session data if local storage is not enabled', function () {
614
+ localStorageIsEnabledStub.returns(false);
615
+
616
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
617
+ delete expectedMessage.session;
618
+ delete expectedMessage.fpkvs;
619
+
620
+ performStandardAuction();
621
+
622
+ expect(server.requests.length).to.equal(1);
623
+ let request = server.requests[0];
624
+
625
+ expect(request.url).to.equal('//localhost:9999/event');
626
+
627
+ let message = JSON.parse(request.requestBody);
628
+
629
+ expect(message).to.deep.equal(expectedMessage);
630
+ });
631
+
632
+ it('should should pass along custom rubicon kv and pvid when defined', function () {
633
+ config.setConfig({
634
+ rubicon: {
635
+ fpkvs: {
636
+ source: 'fb',
637
+ link: 'email'
638
+ }
639
+ }
640
+ });
641
+ performStandardAuction();
642
+ expect(server.requests.length).to.equal(1);
643
+ let request = server.requests[0];
644
+ let message = JSON.parse(request.requestBody);
645
+
646
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
647
+ expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8);
648
+ expectedMessage.fpkvs = [
649
+ { key: 'source', value: 'fb' },
650
+ { key: 'link', value: 'email' }
651
+ ]
652
+ expect(message).to.deep.equal(expectedMessage);
653
+ });
654
+
655
+ it('should convert kvs to strings before sending', function () {
656
+ config.setConfig({
657
+ rubicon: {
658
+ fpkvs: {
659
+ number: 24,
660
+ boolean: false,
661
+ string: 'hello',
662
+ array: ['one', 2, 'three'],
663
+ object: { one: 'two' }
664
+ }
665
+ }
666
+ });
667
+ performStandardAuction();
668
+ expect(server.requests.length).to.equal(1);
669
+ let request = server.requests[0];
670
+ let message = JSON.parse(request.requestBody);
671
+
672
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
673
+ expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8);
674
+ expectedMessage.fpkvs = [
675
+ { key: 'number', value: '24' },
676
+ { key: 'boolean', value: 'false' },
677
+ { key: 'string', value: 'hello' },
678
+ { key: 'array', value: 'one,2,three' },
679
+ { key: 'object', value: '[object Object]' }
680
+ ]
681
+ expect(message).to.deep.equal(expectedMessage);
682
+ });
683
+
684
+ it('should use the query utm param rubicon kv value and pass updated kv and pvid when defined', function () {
685
+ sandbox.stub(utils, 'getWindowLocation').returns({ 'search': '?utm_source=other', 'pbjs_debug': 'true' });
686
+
687
+ config.setConfig({
688
+ rubicon: {
689
+ fpkvs: {
690
+ source: 'fb',
691
+ link: 'email'
692
+ }
693
+ }
694
+ });
695
+ performStandardAuction();
696
+ expect(server.requests.length).to.equal(1);
697
+ let request = server.requests[0];
698
+ let message = JSON.parse(request.requestBody);
699
+
700
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
701
+ expectedMessage.session.pvid = STUBBED_UUID.slice(0, 8);
702
+ expectedMessage.fpkvs = [
703
+ { key: 'source', value: 'other' },
704
+ { key: 'link', value: 'email' }
705
+ ]
706
+
707
+ message.fpkvs.sort((left, right) => left.key < right.key);
708
+ expectedMessage.fpkvs.sort((left, right) => left.key < right.key);
709
+
710
+ expect(message).to.deep.equal(expectedMessage);
711
+ });
712
+
713
+ it('should pick up existing localStorage and use its values', function () {
714
+ // set some localStorage
715
+ let inputlocalStorage = {
716
+ id: '987654',
717
+ start: 1519767017881, // 15 mins before "now"
718
+ expires: 1519767039481, // six hours later
719
+ lastSeen: 1519766113781,
720
+ fpkvs: { source: 'tw' }
721
+ };
722
+ getDataFromLocalStorageStub.withArgs('mgniSession').returns(btoa(JSON.stringify(inputlocalStorage)));
723
+
724
+ config.setConfig({
725
+ rubicon: {
726
+ fpkvs: {
727
+ link: 'email' // should merge this with what is in the localStorage!
728
+ }
729
+ }
730
+ });
731
+ performStandardAuction();
732
+ expect(server.requests.length).to.equal(1);
733
+ let request = server.requests[0];
734
+ let message = JSON.parse(request.requestBody);
735
+
736
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
737
+ expectedMessage.session = {
738
+ id: '987654',
739
+ start: 1519767017881,
740
+ expires: 1519767039481,
741
+ pvid: expectedPvid
742
+ }
743
+ expectedMessage.fpkvs = [
744
+ { key: 'source', value: 'tw' },
745
+ { key: 'link', value: 'email' }
746
+ ]
747
+ expect(message).to.deep.equal(expectedMessage);
748
+
749
+ let calledWith;
750
+ try {
751
+ calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1]));
752
+ } catch (e) {
753
+ calledWith = {};
754
+ }
755
+
756
+ expect(calledWith).to.deep.equal({
757
+ id: '987654', // should have stayed same
758
+ start: 1519767017881, // should have stayed same
759
+ expires: 1519767039481, // should have stayed same
760
+ lastSeen: 1519767013781, // lastSeen updated to our auction init time
761
+ fpkvs: { source: 'tw', link: 'email' }, // link merged in
762
+ pvid: expectedPvid // new pvid stored
763
+ });
764
+ });
765
+
766
+ it('should overwrite matching localstorge value and use its remaining values', function () {
767
+ sandbox.stub(utils, 'getWindowLocation').returns({ 'search': '?utm_source=fb&utm_click=dog' });
768
+
769
+ // set some localStorage
770
+ let inputlocalStorage = {
771
+ id: '987654',
772
+ start: 1519766113781, // 15 mins before "now"
773
+ expires: 1519787713781, // six hours later
774
+ lastSeen: 1519766113781,
775
+ fpkvs: { source: 'tw', link: 'email' }
776
+ };
777
+ getDataFromLocalStorageStub.withArgs('mgniSession').returns(btoa(JSON.stringify(inputlocalStorage)));
778
+
779
+ config.setConfig({
780
+ rubicon: {
781
+ fpkvs: {
782
+ link: 'email' // should merge this with what is in the localStorage!
783
+ }
784
+ }
785
+ });
786
+ performStandardAuction();
787
+ expect(server.requests.length).to.equal(1);
788
+ let request = server.requests[0];
789
+ let message = JSON.parse(request.requestBody);
790
+
791
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
792
+ expectedMessage.session = {
793
+ id: '987654',
794
+ start: 1519766113781,
795
+ expires: 1519787713781,
796
+ pvid: expectedPvid
797
+ }
798
+ expectedMessage.fpkvs = [
799
+ { key: 'source', value: 'fb' },
800
+ { key: 'link', value: 'email' },
801
+ { key: 'click', value: 'dog' }
802
+ ]
803
+
804
+ message.fpkvs.sort((left, right) => left.key < right.key);
805
+ expectedMessage.fpkvs.sort((left, right) => left.key < right.key);
806
+
807
+ expect(message).to.deep.equal(expectedMessage);
808
+
809
+ let calledWith;
810
+ try {
811
+ calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1]));
812
+ } catch (e) {
813
+ calledWith = {};
814
+ }
815
+
816
+ expect(calledWith).to.deep.equal({
817
+ id: '987654', // should have stayed same
818
+ start: 1519766113781, // should have stayed same
819
+ expires: 1519787713781, // should have stayed same
820
+ lastSeen: 1519767013781, // lastSeen updated to our auction init time
821
+ fpkvs: { source: 'fb', link: 'email', click: 'dog' }, // link merged in
822
+ pvid: expectedPvid // new pvid stored
823
+ });
824
+ });
825
+
826
+ it('should throw out session if lastSeen > 30 mins ago and create new one', function () {
827
+ // set some localStorage
828
+ let inputlocalStorage = {
829
+ id: '987654',
830
+ start: 1519764313781, // 45 mins before "now"
831
+ expires: 1519785913781, // six hours later
832
+ lastSeen: 1519764313781, // 45 mins before "now"
833
+ fpkvs: { source: 'tw' }
834
+ };
835
+ getDataFromLocalStorageStub.withArgs('mgniSession').returns(btoa(JSON.stringify(inputlocalStorage)));
836
+
837
+ config.setConfig({
838
+ rubicon: {
839
+ fpkvs: {
840
+ link: 'email' // should merge this with what is in the localStorage!
841
+ }
842
+ }
843
+ });
844
+
845
+ performStandardAuction();
846
+ expect(server.requests.length).to.equal(1);
847
+ let request = server.requests[0];
848
+ let message = JSON.parse(request.requestBody);
849
+
850
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
851
+ // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid
852
+ expectedMessage.session.pvid = expectedPvid;
853
+
854
+ // the saved fpkvs should have been thrown out since session expired
855
+ expectedMessage.fpkvs = [
856
+ { key: 'link', value: 'email' }
857
+ ]
858
+ expect(message).to.deep.equal(expectedMessage);
859
+
860
+ let calledWith;
861
+ try {
862
+ calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1]));
863
+ } catch (e) {
864
+ calledWith = {};
865
+ }
866
+
867
+ expect(calledWith).to.deep.equal({
868
+ id: STUBBED_UUID, // should have generated not used input
869
+ start: 1519767013781, // updated to whenever auction init started
870
+ expires: 1519788613781, // 6 hours after start
871
+ lastSeen: 1519767013781, // lastSeen updated to our "now"
872
+ fpkvs: { link: 'email' }, // link merged in
873
+ pvid: expectedPvid // new pvid stored
874
+ });
875
+ });
876
+
877
+ it('should throw out session if past expires time and create new one', function () {
878
+ // set some localStorage
879
+ let inputlocalStorage = {
880
+ id: '987654',
881
+ start: 1519745353781, // 6 hours before "expires"
882
+ expires: 1519766953781, // little more than six hours ago
883
+ lastSeen: 1519767008781, // 5 seconds ago
884
+ fpkvs: { source: 'tw' }
885
+ };
886
+ getDataFromLocalStorageStub.withArgs('mgniSession').returns(btoa(JSON.stringify(inputlocalStorage)));
887
+
888
+ config.setConfig({
889
+ rubicon: {
890
+ fpkvs: {
891
+ link: 'email' // should merge this with what is in the localStorage!
892
+ }
893
+ }
894
+ });
895
+
896
+ performStandardAuction();
897
+ expect(server.requests.length).to.equal(1);
898
+ let request = server.requests[0];
899
+ let message = JSON.parse(request.requestBody);
900
+
901
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
902
+ // session should match what is already in ANALYTICS_MESSAGE, just need to add pvid
903
+ expectedMessage.session.pvid = expectedPvid;
904
+
905
+ // the saved fpkvs should have been thrown out since session expired
906
+ expectedMessage.fpkvs = [
907
+ { key: 'link', value: 'email' }
908
+ ]
909
+ expect(message).to.deep.equal(expectedMessage);
910
+
911
+ let calledWith;
912
+ try {
913
+ calledWith = JSON.parse(atob(setDataInLocalStorageStub.getCall(0).args[1]));
914
+ } catch (e) {
915
+ calledWith = {};
916
+ }
917
+
918
+ expect(calledWith).to.deep.equal({
919
+ id: STUBBED_UUID, // should have generated and not used same one
920
+ start: 1519767013781, // updated to whenever auction init started
921
+ expires: 1519788613781, // 6 hours after start
922
+ lastSeen: 1519767013781, // lastSeen updated to our "now"
923
+ fpkvs: { link: 'email' }, // link merged in
924
+ pvid: expectedPvid // new pvid stored
925
+ });
926
+ });
927
+ });
928
+
929
+ it('should send gam data if adunit has elementid ortb2 fields', function () {
930
+ // update auction init mock to have the elementids in the adunit
931
+ // and change adUnitCode to be hashes
932
+ let auctionInit = utils.deepClone(MOCK.AUCTION_INIT);
933
+ auctionInit.adUnits[0].ortb2Imp.ext.data.elementid = [gptSlot0.getSlotElementId()];
934
+ auctionInit.adUnits[0].code = '1a2b3c4d';
935
+
936
+ // bid request
937
+ let bidRequested = utils.deepClone(MOCK.BID_REQUESTED);
938
+ bidRequested.bids[0].adUnitCode = '1a2b3c4d';
939
+
940
+ // bid response
941
+ let bidResponse = utils.deepClone(MOCK.BID_RESPONSE);
942
+ bidResponse.adUnitCode = '1a2b3c4d';
943
+
944
+ // bidder done
945
+ let bidderDone = utils.deepClone(MOCK.BIDDER_DONE);
946
+ bidderDone.bids[0].adUnitCode = '1a2b3c4d';
947
+
948
+ // bidder done
949
+ let bidWon = utils.deepClone(MOCK.BID_WON);
950
+ bidWon.adUnitCode = '1a2b3c4d';
951
+
952
+ // Run auction
953
+ events.emit(AUCTION_INIT, auctionInit);
954
+ events.emit(BID_REQUESTED, bidRequested);
955
+ events.emit(BID_RESPONSE, bidResponse);
956
+ events.emit(BIDDER_DONE, bidderDone);
957
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
958
+
959
+ // emmit gpt events and bidWon
960
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
961
+
962
+ events.emit(BID_WON, bidWon);
963
+
964
+ // tick the event delay time plus processing delay
965
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
966
+
967
+ expect(server.requests.length).to.equal(1);
968
+ let request = server.requests[0];
969
+ let message = JSON.parse(request.requestBody);
970
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
971
+
972
+ // new adUnitCodes in payload
973
+ expectedMessage.auctions[0].adUnits[0].adUnitCode = '1a2b3c4d';
974
+ expectedMessage.bidsWon[0].adUnitCode = '1a2b3c4d';
975
+ expect(message).to.deep.equal(expectedMessage);
976
+ });
977
+
978
+ it('should delay the event call depending on analyticsEventDelay config', function () {
979
+ config.setConfig({
980
+ rubicon: {
981
+ analyticsEventDelay: 2000
982
+ }
983
+ });
984
+ performStandardAuction({ eventDelay: 0 });
985
+
986
+ // Should not be sent until delay
987
+ expect(server.requests.length).to.equal(0);
988
+
989
+ // tick the clock and it should fire
990
+ clock.tick(2000);
991
+
992
+ expect(server.requests.length).to.equal(1);
993
+ let request = server.requests[0];
994
+ let message = JSON.parse(request.requestBody);
995
+
996
+ // The timestamps should be changed from the default by (set eventDelay (2000) - eventDelay default (500))
997
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
998
+ expectedMessage.timestamps.eventTime = expectedMessage.timestamps.eventTime + 1500;
999
+ expectedMessage.timestamps.timeSincePageLoad = expectedMessage.timestamps.timeSincePageLoad + 1500;
1000
+
1001
+ expect(message).to.deep.equal(expectedMessage);
1002
+ });
1003
+
1004
+ ['seatBidId', 'pbsBidId'].forEach(pbsParam => {
1005
+ it(`should overwrite prebid bidId with incoming PBS ${pbsParam}`, function () {
1006
+ // bid response
1007
+ let seatBidResponse = utils.deepClone(MOCK.BID_RESPONSE);
1008
+ seatBidResponse[pbsParam] = 'abc-123-do-re-me';
1009
+
1010
+ // Run auction
1011
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
1012
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1013
+ events.emit(BID_RESPONSE, seatBidResponse);
1014
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1015
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1016
+
1017
+ // emmit gpt events and bidWon
1018
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1019
+
1020
+ events.emit(BID_WON, MOCK.BID_WON);
1021
+
1022
+ // tick the event delay time plus processing delay
1023
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1024
+
1025
+ expect(server.requests.length).to.equal(1);
1026
+ let request = server.requests[0];
1027
+ let message = JSON.parse(request.requestBody);
1028
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1029
+
1030
+ // new adUnitCodes in payload
1031
+ expectedMessage.auctions[0].adUnits[0].bids[0].bidId = 'abc-123-do-re-me';
1032
+ expectedMessage.auctions[0].adUnits[0].bids[0].oldBidId = '23fcd8cf4bf0d7';
1033
+ expectedMessage.bidsWon[0].bidId = 'abc-123-do-re-me';
1034
+ expect(message).to.deep.equal(expectedMessage);
1035
+ });
1036
+ });
1037
+
1038
+ [0, '0'].forEach(pbsParam => {
1039
+ it(`should generate new bidId if incoming pbsBidId is ${pbsParam}`, function () {
1040
+ // bid response
1041
+ let seatBidResponse = utils.deepClone(MOCK.BID_RESPONSE);
1042
+ seatBidResponse.pbsBidId = pbsParam;
1043
+
1044
+ // Run auction
1045
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
1046
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1047
+ events.emit(BID_RESPONSE, seatBidResponse);
1048
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1049
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1050
+
1051
+ // emmit gpt events and bidWon
1052
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1053
+
1054
+ events.emit(BID_WON, MOCK.BID_WON);
1055
+
1056
+ // tick the event delay time plus processing delay
1057
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1058
+
1059
+ expect(server.requests.length).to.equal(1);
1060
+ let request = server.requests[0];
1061
+ let message = JSON.parse(request.requestBody);
1062
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1063
+
1064
+ // new adUnitCodes in payload
1065
+ expectedMessage.auctions[0].adUnits[0].bids[0].bidId = STUBBED_UUID;
1066
+ expectedMessage.auctions[0].adUnits[0].bids[0].oldBidId = '23fcd8cf4bf0d7';
1067
+ expectedMessage.bidsWon[0].bidId = STUBBED_UUID;
1068
+ expect(message).to.deep.equal(expectedMessage);
1069
+ });
1070
+ });
1071
+
1072
+ it(`should pick highest cpm if more than one bidResponse comes in`, function () {
1073
+ // Run auction
1074
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
1075
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1076
+
1077
+ const bidResp = utils.deepClone(MOCK.BID_RESPONSE);
1078
+
1079
+ // emit some bid responses
1080
+ [1.0, 5.5, 0.1].forEach(cpm => {
1081
+ events.emit(BID_RESPONSE, { ...bidResp, cpm });
1082
+ });
1083
+
1084
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1085
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1086
+
1087
+ // emmit gpt events and bidWon
1088
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1089
+
1090
+ events.emit(BID_WON, MOCK.BID_WON);
1091
+
1092
+ // tick the event delay time plus processing delay
1093
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1094
+
1095
+ expect(server.requests.length).to.equal(1);
1096
+ let request = server.requests[0];
1097
+ let message = JSON.parse(request.requestBody);
1098
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1099
+
1100
+ // highest cpm in payload
1101
+ expectedMessage.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD = 5.5;
1102
+ expectedMessage.bidsWon[0].bidResponse.bidPriceUSD = 5.5;
1103
+ expect(message).to.deep.equal(expectedMessage);
1104
+ });
1105
+
1106
+ it('should send bid won events by themselves if emitted after auction pba payload is sent', function () {
1107
+ performStandardAuction({ sendBidWon: false });
1108
+
1109
+ // Now send bidWon
1110
+ events.emit(BID_WON, MOCK.BID_WON);
1111
+
1112
+ // tick the event delay time plus processing delay
1113
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1114
+
1115
+ // should see two server requests
1116
+ expect(server.requests.length).to.equal(2);
1117
+
1118
+ // first is normal analytics event without bidWon
1119
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1120
+ delete expectedMessage.bidsWon;
1121
+
1122
+ let message = JSON.parse(server.requests[0].requestBody);
1123
+ expect(message).to.deep.equal(expectedMessage);
1124
+
1125
+ // second is just a bidWon (remove gam and auction event)
1126
+ message = JSON.parse(server.requests[1].requestBody);
1127
+
1128
+ let expectedMessage2 = utils.deepClone(ANALYTICS_MESSAGE);
1129
+ delete expectedMessage2.auctions;
1130
+ delete expectedMessage2.gamRenders;
1131
+
1132
+ // second event should be event delay time after first one
1133
+ expectedMessage2.timestamps.eventTime = expectedMessage.timestamps.eventTime + rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay;
1134
+ expectedMessage2.timestamps.timeSincePageLoad = expectedMessage.timestamps.timeSincePageLoad + rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay;
1135
+
1136
+ // trigger is `batched-bidsWon`
1137
+ expectedMessage2.trigger = 'batched-bidsWon';
1138
+
1139
+ expect(message).to.deep.equal(expectedMessage2);
1140
+ });
1141
+
1142
+ it('should send gamRender events by themselves if emitted after auction pba payload is sent', function () {
1143
+ // dont send extra events and hit the batch timeout
1144
+ performStandardAuction({ gptEvents: [], sendBidWon: false, eventDelay: rubiConf.analyticsBatchTimeout });
1145
+
1146
+ // Now send gptEvent and bidWon
1147
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1148
+ events.emit(BID_WON, MOCK.BID_WON);
1149
+
1150
+ // tick the event delay time plus processing delay
1151
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1152
+
1153
+ // should see two server requests
1154
+ expect(server.requests.length).to.equal(2);
1155
+
1156
+ // first is normal analytics event without bidWon or gam
1157
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1158
+ delete expectedMessage.bidsWon;
1159
+ delete expectedMessage.gamRenders;
1160
+
1161
+ // timing changes a bit -> timestamps should be batchTimeout - event delay later
1162
+ const expectedExtraTime = rubiConf.analyticsBatchTimeout - rubiConf.analyticsEventDelay;
1163
+ expectedMessage.timestamps.eventTime = expectedMessage.timestamps.eventTime + expectedExtraTime;
1164
+ expectedMessage.timestamps.timeSincePageLoad = expectedMessage.timestamps.timeSincePageLoad + expectedExtraTime;
1165
+
1166
+ // since gam event did not fire, the trigger should be auctionEnd
1167
+ expectedMessage.trigger = 'auctionEnd';
1168
+
1169
+ let message = JSON.parse(server.requests[0].requestBody);
1170
+ expect(message).to.deep.equal(expectedMessage);
1171
+
1172
+ // second is gam and bid won
1173
+ message = JSON.parse(server.requests[1].requestBody);
1174
+
1175
+ let expectedMessage2 = utils.deepClone(ANALYTICS_MESSAGE);
1176
+ // second event should be event delay time after first one
1177
+ expectedMessage2.timestamps.eventTime = expectedMessage.timestamps.eventTime + rubiConf.analyticsEventDelay;
1178
+ expectedMessage2.timestamps.timeSincePageLoad = expectedMessage.timestamps.timeSincePageLoad + rubiConf.analyticsEventDelay;
1179
+ delete expectedMessage2.auctions;
1180
+
1181
+ // trigger should be `batched-bidsWon-gamRender`
1182
+ expectedMessage2.trigger = 'batched-bidsWon-gamRenders';
1183
+
1184
+ expect(message).to.deep.equal(expectedMessage2);
1185
+ });
1186
+
1187
+ it('should send all events solo if delay and batch set to 0', function () {
1188
+ const defaultDelay = rubiConf.analyticsEventDelay;
1189
+ config.setConfig({
1190
+ rubicon: {
1191
+ analyticsBatchTimeout: 0,
1192
+ analyticsEventDelay: 0,
1193
+ analyticsProcessDelay: 0
1194
+ }
1195
+ });
1196
+
1197
+ performStandardAuction({ eventDelay: 0 });
1198
+
1199
+ // should be 3 requests
1200
+ expect(server.requests.length).to.equal(3);
1201
+
1202
+ // grab expected 3 requests from default message
1203
+ let { auctions, gamRenders, bidsWon, ...rest } = utils.deepClone(ANALYTICS_MESSAGE);
1204
+
1205
+ // rest of payload should have timestamps changed to be - default eventDelay since we changed it to 0
1206
+ rest.timestamps.eventTime = rest.timestamps.eventTime - defaultDelay;
1207
+ rest.timestamps.timeSincePageLoad = rest.timestamps.timeSincePageLoad - defaultDelay;
1208
+
1209
+ // loop through and assert events fired in correct order with correct stuff
1210
+ [
1211
+ { expectedMessage: { auctions, ...rest }, trigger: 'solo-auction' },
1212
+ { expectedMessage: { gamRenders, ...rest }, trigger: 'solo-gam' },
1213
+ { expectedMessage: { bidsWon, ...rest }, trigger: 'solo-bidWon' },
1214
+ ].forEach((stuff, requestNum) => {
1215
+ let message = JSON.parse(server.requests[requestNum].requestBody);
1216
+ stuff.expectedMessage.trigger = stuff.trigger;
1217
+ expect(message).to.deep.equal(stuff.expectedMessage);
1218
+ });
1219
+ });
1220
+
1221
+ it(`should correctly mark bids as timed out`, function () {
1222
+ // Run auction (simulate bidder timed out in 1000 ms)
1223
+ const auctionStart = Date.now() - 1000;
1224
+ events.emit(AUCTION_INIT, { ...MOCK.AUCTION_INIT, timestamp: auctionStart });
1225
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1226
+
1227
+ // emit bid timeout
1228
+ events.emit(BID_TIMEOUT, [
1229
+ {
1230
+ auctionId: MOCK.AUCTION_INIT.auctionId,
1231
+ adUnitCode: MOCK.AUCTION_INIT.adUnits[0].code,
1232
+ bidId: MOCK.BID_REQUESTED.bids[0].bidId,
1233
+ transactionId: MOCK.AUCTION_INIT.adUnits[0].transactionId,
1234
+ }
1235
+ ]);
1236
+
1237
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1238
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1239
+
1240
+ // emmit gpt events and bidWon
1241
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1242
+
1243
+ // tick the event delay time plus processing delay
1244
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1245
+
1246
+ expect(server.requests.length).to.equal(1);
1247
+ let request = server.requests[0];
1248
+ let message = JSON.parse(request.requestBody);
1249
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1250
+
1251
+ // should see error time out bid
1252
+ expectedMessage.auctions[0].adUnits[0].bids[0].status = 'error';
1253
+ expectedMessage.auctions[0].adUnits[0].bids[0].error = {
1254
+ code: 'timeout-error',
1255
+ description: 'prebid.js timeout' // will help us diff if timeout was set by PBS or PBJS
1256
+ };
1257
+
1258
+ // should not see bidResponse or bidsWon
1259
+ delete expectedMessage.auctions[0].adUnits[0].bids[0].bidResponse;
1260
+ delete expectedMessage.bidsWon;
1261
+
1262
+ // adunit should be marked as error
1263
+ expectedMessage.auctions[0].adUnits[0].status = 'error';
1264
+
1265
+ // timed out in 1000 ms
1266
+ expectedMessage.auctions[0].adUnits[0].bids[0].clientLatencyMillis = 1000;
1267
+
1268
+ expectedMessage.auctions[0].auctionStart = auctionStart;
1269
+
1270
+ expect(message).to.deep.equal(expectedMessage);
1271
+ });
1272
+
1273
+ [
1274
+ { name: 'aupname', adUnitPath: 'adUnits.0.ortb2Imp.ext.data.aupname', eventPath: 'auctions.0.adUnits.0.pattern', input: '1234/mycoolsite/*&gpt_leaderboard&deviceType=mobile' },
1275
+ { name: 'gpid', adUnitPath: 'adUnits.0.ortb2Imp.ext.gpid', eventPath: 'auctions.0.adUnits.0.gpid', input: '1234/gpid/path' },
1276
+ { name: 'pbadslot', adUnitPath: 'adUnits.0.ortb2Imp.ext.data.pbadslot', eventPath: 'auctions.0.adUnits.0.pbAdSlot', input: '1234/pbadslot/path' }
1277
+ ].forEach(test => {
1278
+ it(`should correctly pass ${test.name}`, function () {
1279
+ // bid response
1280
+ let auctionInit = utils.deepClone(MOCK.AUCTION_INIT);
1281
+ utils.deepSetValue(auctionInit, test.adUnitPath, test.input);
1282
+
1283
+ // Run auction
1284
+ events.emit(AUCTION_INIT, auctionInit);
1285
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1286
+ events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
1287
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1288
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1289
+
1290
+ // emmit gpt events and bidWon
1291
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1292
+
1293
+ events.emit(BID_WON, MOCK.BID_WON);
1294
+
1295
+ // tick the event delay time plus processing delay
1296
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1297
+
1298
+ expect(server.requests.length).to.equal(1);
1299
+ let request = server.requests[0];
1300
+ let message = JSON.parse(request.requestBody);
1301
+
1302
+ // pattern in payload
1303
+ expect(deepAccess(message, test.eventPath)).to.equal(test.input);
1304
+ });
1305
+ });
1306
+
1307
+ it('should pass bidderDetail for multibid auctions', function () {
1308
+ let bidResponse = utils.deepClone(MOCK.BID_RESPONSE);
1309
+ bidResponse.targetingBidder = 'rubi2';
1310
+ bidResponse.originalRequestId = bidResponse.requestId;
1311
+ bidResponse.requestId = '1a2b3c4d5e6f7g8h9';
1312
+
1313
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
1314
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1315
+ events.emit(BID_RESPONSE, bidResponse);
1316
+ events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
1317
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1318
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1319
+
1320
+ // emmit gpt events and bidWon
1321
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1322
+
1323
+ let bidWon = utils.deepClone(MOCK.BID_WON);
1324
+ bidWon.bidId = bidWon.requestId = '1a2b3c4d5e6f7g8h9';
1325
+ bidWon.bidderDetail = 'rubi2';
1326
+ events.emit(BID_WON, bidWon);
1327
+
1328
+ // tick the event delay time plus processing delay
1329
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1330
+
1331
+ expect(server.requests.length).to.equal(1);
1332
+
1333
+ let message = JSON.parse(server.requests[0].requestBody);
1334
+
1335
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1336
+
1337
+ // expect an extra bid added
1338
+ expectedMessage.auctions[0].adUnits[0].bids.push({
1339
+ ...ANALYTICS_MESSAGE.auctions[0].adUnits[0].bids[0],
1340
+ bidderDetail: 'rubi2',
1341
+ bidId: '1a2b3c4d5e6f7g8h9'
1342
+ });
1343
+
1344
+ // bid won is our extra bid
1345
+ expectedMessage.bidsWon[0].bidderDetail = 'rubi2';
1346
+ expectedMessage.bidsWon[0].bidId = '1a2b3c4d5e6f7g8h9';
1347
+
1348
+ expect(message).to.deep.equal(expectedMessage);
1349
+ });
1350
+
1351
+ it('should pass bidderDetail for multibid auctions', function () {
1352
+ // Set the rates
1353
+ setConfig({
1354
+ adServerCurrency: 'JPY',
1355
+ rates: {
1356
+ USD: {
1357
+ JPY: 100
1358
+ }
1359
+ }
1360
+ });
1361
+
1362
+ // set our bid response to JPY
1363
+ let bidResponse = utils.deepClone(MOCK.BID_RESPONSE);
1364
+ bidResponse.currency = 'JPY';
1365
+ bidResponse.cpm = 100;
1366
+
1367
+ // Now add the bidResponse hook which hooks on the currenct conversion function onto the bid response
1368
+ let innerBid;
1369
+ addBidResponseHook(function (adCodeId, bid) {
1370
+ innerBid = bid;
1371
+ }, 'elementId', bidResponse);
1372
+
1373
+ // Use the rubi analytics parseBidResponse Function to get the resulting cpm from the bid response!
1374
+ const bidResponseObj = parseBidResponse(innerBid);
1375
+ expect(bidResponseObj).to.have.property('bidPriceUSD');
1376
+ expect(bidResponseObj.bidPriceUSD).to.equal(1.0);
1377
+ });
1378
+
1379
+ it('should use the integration type provided in the config instead of the default', () => {
1380
+ config.setConfig({
1381
+ rubicon: {
1382
+ int_type: 'testType'
1383
+ }
1384
+ })
1385
+
1386
+ performStandardAuction();
1387
+
1388
+ expect(server.requests.length).to.equal(1);
1389
+ const request = server.requests[0];
1390
+ const message = JSON.parse(request.requestBody);
1391
+ expect(message.integration).to.equal('testType');
1392
+ });
1393
+
1394
+ it('should correctly pass bid.source when is s2s', () => {
1395
+ // Run auction
1396
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
1397
+
1398
+ const bidReq = utils.deepClone(MOCK.BID_REQUESTED);
1399
+ bidReq.bids[0].src = 's2s';
1400
+
1401
+ events.emit(BID_REQUESTED, bidReq);
1402
+ events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
1403
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1404
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1405
+
1406
+ // emmit gpt events and bidWon
1407
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1408
+ events.emit(BID_WON, MOCK.BID_WON);
1409
+
1410
+ // tick the event delay time plus processing delay
1411
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1412
+
1413
+ expect(server.requests.length).to.equal(1);
1414
+ let request = server.requests[0];
1415
+ let message = JSON.parse(request.requestBody);
1416
+ let expectedMessage = utils.deepClone(ANALYTICS_MESSAGE);
1417
+
1418
+ // bid source should be 'server'
1419
+ expectedMessage.auctions[0].adUnits[0].bids[0].source = 'server';
1420
+ expectedMessage.bidsWon[0].source = 'server';
1421
+ expect(message).to.deep.equal(expectedMessage);
1422
+ });
1423
+
1424
+ describe('when handling bid caching', () => {
1425
+ let auctionInits, bidRequests, bidResponses, bidsWon;
1426
+ beforeEach(function () {
1427
+ // set timing stuff to 0 so we clearly know when things fire
1428
+ config.setConfig({
1429
+ useBidCache: true,
1430
+ rubicon: {
1431
+ analyticsEventDelay: 0,
1432
+ analyticsBatchTimeout: 0,
1433
+ analyticsProcessDelay: 0
1434
+ }
1435
+ });
1436
+
1437
+ // setup 3 auctions
1438
+ auctionInits = [
1439
+ { ...MOCK.AUCTION_INIT, auctionId: 'auctionId-1', adUnits: [{ ...MOCK.AUCTION_INIT.adUnits[0], transactionId: 'tid-1' }] },
1440
+ { ...MOCK.AUCTION_INIT, auctionId: 'auctionId-2', adUnits: [{ ...MOCK.AUCTION_INIT.adUnits[0], transactionId: 'tid-2' }] },
1441
+ { ...MOCK.AUCTION_INIT, auctionId: 'auctionId-3', adUnits: [{ ...MOCK.AUCTION_INIT.adUnits[0], transactionId: 'tid-3' }] }
1442
+ ];
1443
+ bidRequests = [
1444
+ { ...MOCK.BID_REQUESTED, auctionId: 'auctionId-1', bids: [{ ...MOCK.BID_REQUESTED.bids[0], bidId: 'bidId-1', transactionId: 'tid-1' }] },
1445
+ { ...MOCK.BID_REQUESTED, auctionId: 'auctionId-2', bids: [{ ...MOCK.BID_REQUESTED.bids[0], bidId: 'bidId-2', transactionId: 'tid-2' }] },
1446
+ { ...MOCK.BID_REQUESTED, auctionId: 'auctionId-3', bids: [{ ...MOCK.BID_REQUESTED.bids[0], bidId: 'bidId-3', transactionId: 'tid-3' }] }
1447
+ ];
1448
+ bidResponses = [
1449
+ { ...MOCK.BID_RESPONSE, auctionId: 'auctionId-1', transactionId: 'tid-1', requestId: 'bidId-1' },
1450
+ { ...MOCK.BID_RESPONSE, auctionId: 'auctionId-2', transactionId: 'tid-2', requestId: 'bidId-2' },
1451
+ { ...MOCK.BID_RESPONSE, auctionId: 'auctionId-3', transactionId: 'tid-3', requestId: 'bidId-3' },
1452
+ ];
1453
+ bidsWon = [
1454
+ { ...MOCK.BID_WON, auctionId: 'auctionId-1', transactionId: 'tid-1', bidId: 'bidId-1', requestId: 'bidId-1' },
1455
+ { ...MOCK.BID_WON, auctionId: 'auctionId-2', transactionId: 'tid-2', bidId: 'bidId-2', requestId: 'bidId-2' },
1456
+ { ...MOCK.BID_WON, auctionId: 'auctionId-3', transactionId: 'tid-3', bidId: 'bidId-3', requestId: 'bidId-3' },
1457
+ ];
1458
+ });
1459
+ function runBasicAuction(auctionNum) {
1460
+ events.emit(AUCTION_INIT, auctionInits[auctionNum]);
1461
+ events.emit(BID_REQUESTED, bidRequests[auctionNum]);
1462
+ events.emit(BID_RESPONSE, bidResponses[auctionNum]);
1463
+ events.emit(BIDDER_DONE, { ...MOCK.BIDDER_DONE, auctionId: auctionInits[auctionNum].auctionId });
1464
+ events.emit(AUCTION_END, { ...MOCK.AUCTION_END, auctionId: auctionInits[auctionNum].auctionId });
1465
+ }
1466
+ it('should select earliest auction to attach to', () => {
1467
+ // get 3 auctions pending to send events
1468
+ runBasicAuction(0);
1469
+ runBasicAuction(1);
1470
+ runBasicAuction(2);
1471
+
1472
+ // emmit a gptEvent should attach to first auction
1473
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1474
+
1475
+ // should be 4 requests so far (3 auctions + 1 gamRender)
1476
+ expect(server.requests.length).to.equal(4);
1477
+
1478
+ // 4th should be gamRender and should have Auciton # 1's id's
1479
+ const message = JSON.parse(server.requests[3].requestBody);
1480
+ const expectedMessage = {
1481
+ ...ANALYTICS_MESSAGE.gamRenders[0],
1482
+ auctionId: 'auctionId-1',
1483
+ transactionId: 'tid-1'
1484
+ };
1485
+ expect(message.gamRenders).to.deep.equal([expectedMessage]);
1486
+
1487
+ // emit bidWon from first auction
1488
+ events.emit(BID_WON, bidsWon[0]);
1489
+
1490
+ // another request which is bidWon
1491
+ expect(server.requests.length).to.equal(5);
1492
+ const message1 = JSON.parse(server.requests[4].requestBody);
1493
+ const expectedMessage1 = {
1494
+ ...ANALYTICS_MESSAGE.bidsWon[0],
1495
+ sourceAuctionId: 'auctionId-1',
1496
+ renderAuctionId: 'auctionId-1',
1497
+ sourceTransactionId: 'tid-1',
1498
+ renderTransactionId: 'tid-1',
1499
+ transactionId: 'tid-1',
1500
+ bidId: 'bidId-1',
1501
+ };
1502
+ expect(message1.bidsWon).to.deep.equal([expectedMessage1]);
1503
+ });
1504
+
1505
+ [
1506
+ { useBidCache: true, expectedRenderId: 3 },
1507
+ { useBidCache: false, expectedRenderId: 2 }
1508
+ ].forEach(test => {
1509
+ it(`should match bidWon to correct render auction if useBidCache is ${test.useBidCache}`, () => {
1510
+ config.setConfig({ useBidCache: test.useBidCache });
1511
+ // get 3 auctions pending to send events
1512
+ runBasicAuction(0);
1513
+ runBasicAuction(1);
1514
+ runBasicAuction(2);
1515
+
1516
+ // emmit 3 gpt Events, first two "empty"
1517
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, {
1518
+ slot: gptSlot0,
1519
+ isEmpty: true,
1520
+ });
1521
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, {
1522
+ slot: gptSlot0,
1523
+ isEmpty: true,
1524
+ });
1525
+ // last one is valid
1526
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1527
+
1528
+ // should be 6 requests so far (3 auctions + 3 gamRender)
1529
+ expect(server.requests.length).to.equal(6);
1530
+
1531
+ // 4th should be gamRender and should have Auciton # 1's id's
1532
+ const message = JSON.parse(server.requests[3].requestBody);
1533
+ const expectedMessage = {
1534
+ auctionId: 'auctionId-1',
1535
+ transactionId: 'tid-1',
1536
+ isSlotEmpty: true,
1537
+ adSlot: 'box'
1538
+ };
1539
+ expect(message.gamRenders).to.deep.equal([expectedMessage]);
1540
+
1541
+ // 5th should be gamRender and should have Auciton # 2's id's
1542
+ const message1 = JSON.parse(server.requests[4].requestBody);
1543
+ const expectedMessage1 = {
1544
+ auctionId: 'auctionId-2',
1545
+ transactionId: 'tid-2',
1546
+ isSlotEmpty: true,
1547
+ adSlot: 'box'
1548
+ };
1549
+ expect(message1.gamRenders).to.deep.equal([expectedMessage1]);
1550
+
1551
+ // 6th should be gamRender and should have Auciton # 3's id's
1552
+ const message2 = JSON.parse(server.requests[5].requestBody);
1553
+ const expectedMessage2 = {
1554
+ ...ANALYTICS_MESSAGE.gamRenders[0],
1555
+ auctionId: 'auctionId-3',
1556
+ transactionId: 'tid-3'
1557
+ };
1558
+ expect(message2.gamRenders).to.deep.equal([expectedMessage2]);
1559
+
1560
+ // emit bidWon from second auction
1561
+ // it should pick out render information from 3rd auction and source from 1st
1562
+ events.emit(BID_WON, bidsWon[1]);
1563
+
1564
+ // another request which is bidWon
1565
+ expect(server.requests.length).to.equal(7);
1566
+ const message3 = JSON.parse(server.requests[6].requestBody);
1567
+ const expectedMessage3 = {
1568
+ ...ANALYTICS_MESSAGE.bidsWon[0],
1569
+ sourceAuctionId: 'auctionId-2',
1570
+ renderAuctionId: `auctionId-${test.expectedRenderId}`,
1571
+ sourceTransactionId: 'tid-2',
1572
+ renderTransactionId: `tid-${test.expectedRenderId}`,
1573
+ transactionId: 'tid-2',
1574
+ bidId: 'bidId-2'
1575
+ };
1576
+ if (test.useBidCache) expectedMessage3.isCachedBid = true
1577
+ expect(message3.bidsWon).to.deep.equal([expectedMessage3]);
1578
+ });
1579
+ });
1580
+
1581
+ it('should still fire bidWon if no gam match found', () => {
1582
+ // get 3 auctions pending to send events
1583
+ runBasicAuction(0);
1584
+ runBasicAuction(1);
1585
+ runBasicAuction(2);
1586
+
1587
+ // emit bidWon from 3rd auction - it should still fire even though no associated gamRender found
1588
+ events.emit(BID_WON, bidsWon[2]);
1589
+
1590
+ // another request which is bidWon
1591
+ expect(server.requests.length).to.equal(4);
1592
+ const message1 = JSON.parse(server.requests[3].requestBody);
1593
+ const expectedMessage1 = {
1594
+ ...ANALYTICS_MESSAGE.bidsWon[0],
1595
+ sourceAuctionId: 'auctionId-3',
1596
+ renderAuctionId: 'auctionId-3',
1597
+ sourceTransactionId: 'tid-3',
1598
+ renderTransactionId: 'tid-3',
1599
+ transactionId: 'tid-3',
1600
+ bidId: 'bidId-3',
1601
+ };
1602
+ expect(message1.bidsWon).to.deep.equal([expectedMessage1]);
1603
+ });
1604
+ });
1605
+ });
1606
+
1607
+ describe('billing events integration', () => {
1608
+ beforeEach(function () {
1609
+ magniteAdapter.enableAnalytics({
1610
+ options: {
1611
+ endpoint: '//localhost:9999/event',
1612
+ accountId: 1001
1613
+ }
1614
+ });
1615
+ // default dmBilling
1616
+ config.setConfig({
1617
+ rubicon: {
1618
+ dmBilling: {
1619
+ enabled: false,
1620
+ vendors: [],
1621
+ waitForAuction: true
1622
+ }
1623
+ }
1624
+ })
1625
+ });
1626
+ afterEach(function () {
1627
+ magniteAdapter.disableAnalytics();
1628
+ });
1629
+ const basicBillingAuction = (billingEvents = []) => {
1630
+ events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
1631
+ events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
1632
+
1633
+ billingEvents.forEach(ev => events.emit(BILLABLE_EVENT, ev));
1634
+
1635
+ events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
1636
+ events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
1637
+ events.emit(AUCTION_END, MOCK.AUCTION_END);
1638
+
1639
+ mockGpt.emitEvent(gptSlotRenderEnded0.eventName, gptSlotRenderEnded0.params);
1640
+ events.emit(BID_WON, MOCK.BID_WON);
1641
+
1642
+ // tick the event delay time plus processing delay
1643
+ clock.tick(rubiConf.analyticsEventDelay + rubiConf.analyticsProcessDelay);
1644
+ }
1645
+ it('should ignore billing events when not enabled', () => {
1646
+ basicBillingAuction([{
1647
+ vendor: 'vendorName',
1648
+ type: 'auction',
1649
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1650
+ }]);
1651
+ expect(server.requests.length).to.equal(1);
1652
+ const request = server.requests[0];
1653
+ const message = JSON.parse(request.requestBody);
1654
+ expect(message.billableEvents).to.be.undefined;
1655
+ });
1656
+ it('should ignore billing events when enabled but vendor is not whitelisted', () => {
1657
+ // off by default
1658
+ config.setConfig({
1659
+ rubicon: {
1660
+ dmBilling: {
1661
+ enabled: true
1662
+ }
1663
+ }
1664
+ });
1665
+ basicBillingAuction([{
1666
+ vendor: 'vendorName',
1667
+ type: 'auction',
1668
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1669
+ }]);
1670
+ expect(server.requests.length).to.equal(1);
1671
+ const request = server.requests[0];
1672
+ const message = JSON.parse(request.requestBody);
1673
+ expect(message.billableEvents).to.be.undefined;
1674
+ });
1675
+ it('should ignore billing events if billingId is not defined or billingId is not a string', () => {
1676
+ // off by default
1677
+ config.setConfig({
1678
+ rubicon: {
1679
+ dmBilling: {
1680
+ enabled: true,
1681
+ vendors: ['vendorName']
1682
+ }
1683
+ }
1684
+ });
1685
+ basicBillingAuction([
1686
+ {
1687
+ vendor: 'vendorName',
1688
+ type: 'auction',
1689
+ },
1690
+ {
1691
+ vendor: 'vendorName',
1692
+ type: 'auction',
1693
+ billingId: true
1694
+ },
1695
+ {
1696
+ vendor: 'vendorName',
1697
+ type: 'auction',
1698
+ billingId: 1233434
1699
+ },
1700
+ {
1701
+ vendor: 'vendorName',
1702
+ type: 'auction',
1703
+ billingId: null
1704
+ }
1705
+ ]);
1706
+ expect(server.requests.length).to.equal(1);
1707
+ const request = server.requests[0];
1708
+ const message = JSON.parse(request.requestBody);
1709
+ expect(message.billableEvents).to.be.undefined;
1710
+ });
1711
+ it('should pass along billing event in same payload if same auctionId', () => {
1712
+ // off by default
1713
+ config.setConfig({
1714
+ rubicon: {
1715
+ dmBilling: {
1716
+ enabled: true,
1717
+ vendors: ['vendorName']
1718
+ }
1719
+ }
1720
+ });
1721
+ basicBillingAuction([{
1722
+ vendor: 'vendorName',
1723
+ type: 'pageView',
1724
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965',
1725
+ auctionId: MOCK.AUCTION_INIT.auctionId
1726
+ }]);
1727
+ expect(server.requests.length).to.equal(1);
1728
+ const request = server.requests[0];
1729
+ const message = JSON.parse(request.requestBody);
1730
+ expect(message).to.haveOwnProperty('auctions');
1731
+ expect(message.billableEvents).to.deep.equal([{
1732
+ accountId: 1001,
1733
+ vendor: 'vendorName',
1734
+ type: 'pageView',
1735
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965',
1736
+ auctionId: MOCK.AUCTION_INIT.auctionId
1737
+ }]);
1738
+ });
1739
+ it('should pass NOT pass along billing event in same payload if no auctionId', () => {
1740
+ // off by default
1741
+ config.setConfig({
1742
+ rubicon: {
1743
+ dmBilling: {
1744
+ enabled: true,
1745
+ vendors: ['vendorName']
1746
+ }
1747
+ }
1748
+ });
1749
+ basicBillingAuction([{
1750
+ vendor: 'vendorName',
1751
+ type: 'auction',
1752
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965',
1753
+ }]);
1754
+ expect(server.requests.length).to.equal(2);
1755
+
1756
+ // first is the billing event
1757
+ let message = JSON.parse(server.requests[0].requestBody);
1758
+ expect(message).to.not.haveOwnProperty('auctions');
1759
+ expect(message.billableEvents).to.deep.equal([{
1760
+ accountId: 1001,
1761
+ vendor: 'vendorName',
1762
+ type: 'auction',
1763
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1764
+ }]);
1765
+
1766
+ // second is auctions
1767
+ message = JSON.parse(server.requests[1].requestBody);
1768
+ expect(message).to.haveOwnProperty('auctions');
1769
+ expect(message).to.not.haveOwnProperty('billableEvents');
1770
+ });
1771
+ it('should pass along multiple billing events but filter out duplicates', () => {
1772
+ // off by default
1773
+ config.setConfig({
1774
+ rubicon: {
1775
+ dmBilling: {
1776
+ enabled: true,
1777
+ vendors: ['vendorName']
1778
+ }
1779
+ }
1780
+ });
1781
+ basicBillingAuction([
1782
+ {
1783
+ vendor: 'vendorName',
1784
+ type: 'auction',
1785
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965',
1786
+ auctionId: MOCK.AUCTION_INIT.auctionId
1787
+ },
1788
+ {
1789
+ vendor: 'vendorName',
1790
+ type: 'impression',
1791
+ billingId: '743db6e3-21f2-44d4-917f-cb3488c6076f',
1792
+ auctionId: MOCK.AUCTION_INIT.auctionId
1793
+ },
1794
+ {
1795
+ vendor: 'vendorName',
1796
+ type: 'auction',
1797
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965',
1798
+ auctionId: MOCK.AUCTION_INIT.auctionId
1799
+ }
1800
+ ]);
1801
+ expect(server.requests.length).to.equal(1);
1802
+ const request = server.requests[0];
1803
+ const message = JSON.parse(request.requestBody);
1804
+ expect(message).to.haveOwnProperty('auctions');
1805
+ expect(message.billableEvents).to.deep.equal([
1806
+ {
1807
+ accountId: 1001,
1808
+ vendor: 'vendorName',
1809
+ type: 'auction',
1810
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965',
1811
+ auctionId: MOCK.AUCTION_INIT.auctionId
1812
+ },
1813
+ {
1814
+ accountId: 1001,
1815
+ vendor: 'vendorName',
1816
+ type: 'impression',
1817
+ billingId: '743db6e3-21f2-44d4-917f-cb3488c6076f',
1818
+ auctionId: MOCK.AUCTION_INIT.auctionId
1819
+ }
1820
+ ]);
1821
+ });
1822
+ it('should pass along event right away if no pending auction', () => {
1823
+ // off by default
1824
+ config.setConfig({
1825
+ rubicon: {
1826
+ analyticsEventDelay: 0,
1827
+ dmBilling: {
1828
+ enabled: true,
1829
+ vendors: ['vendorName']
1830
+ }
1831
+ }
1832
+ });
1833
+
1834
+ events.emit(BILLABLE_EVENT, {
1835
+ vendor: 'vendorName',
1836
+ type: 'auction',
1837
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1838
+ });
1839
+ expect(server.requests.length).to.equal(1);
1840
+ const request = server.requests[0];
1841
+ const message = JSON.parse(request.requestBody);
1842
+ expect(message).to.not.haveOwnProperty('auctions');
1843
+ expect(message.billableEvents).to.deep.equal([
1844
+ {
1845
+ accountId: 1001,
1846
+ vendor: 'vendorName',
1847
+ type: 'auction',
1848
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1849
+ }
1850
+ ]);
1851
+ });
1852
+ it('should pass along event right away if pending auction but not waiting', () => {
1853
+ // off by default
1854
+ config.setConfig({
1855
+ rubicon: {
1856
+ dmBilling: {
1857
+ enabled: true,
1858
+ vendors: ['vendorName'],
1859
+ waitForAuction: false
1860
+ }
1861
+ }
1862
+ });
1863
+ // should fire right away, and then auction later
1864
+ basicBillingAuction([{
1865
+ vendor: 'vendorName',
1866
+ type: 'auction',
1867
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1868
+ }]);
1869
+ expect(server.requests.length).to.equal(2);
1870
+ const billingRequest = server.requests[0];
1871
+ const billingMessage = JSON.parse(billingRequest.requestBody);
1872
+ expect(billingMessage).to.not.haveOwnProperty('auctions');
1873
+ expect(billingMessage.billableEvents).to.deep.equal([
1874
+ {
1875
+ accountId: 1001,
1876
+ vendor: 'vendorName',
1877
+ type: 'auction',
1878
+ billingId: 'f8558d41-62de-4349-bc7b-2dbee1e69965'
1879
+ }
1880
+ ]);
1881
+ // auction event after
1882
+ const auctionRequest = server.requests[1];
1883
+ const auctionMessage = JSON.parse(auctionRequest.requestBody);
1884
+ // should not double pass events!
1885
+ expect(auctionMessage).to.not.haveOwnProperty('billableEvents');
1886
+ });
1887
+ });
1888
+
1889
+ it('getHostNameFromReferer correctly grabs hostname from an input URL', function () {
1890
+ let inputUrl = 'https://www.prebid.org/some/path?pbjs_debug=true';
1891
+ expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.org');
1892
+ inputUrl = 'https://www.prebid.com/some/path?pbjs_debug=true';
1893
+ expect(getHostNameFromReferer(inputUrl)).to.equal('www.prebid.com');
1894
+ inputUrl = 'https://prebid.org/some/path?pbjs_debug=true';
1895
+ expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org');
1896
+ inputUrl = 'http://xn--p8j9a0d9c9a.xn--q9jyb4c/';
1897
+ expect(typeof getHostNameFromReferer(inputUrl)).to.equal('string');
1898
+
1899
+ // not non-UTF char's in query / path which break if noDecodeWholeURL not set
1900
+ inputUrl = 'https://prebid.org/search_results/%95x%8Em%92%CA/?category=000';
1901
+ expect(getHostNameFromReferer(inputUrl)).to.equal('prebid.org');
1902
+ });
1903
+
1904
+ describe(`handle currency conversions`, () => {
1905
+ const origConvertCurrency = getGlobal().convertCurrency;
1906
+ afterEach(() => {
1907
+ if (origConvertCurrency != null) {
1908
+ getGlobal().convertCurrency = origConvertCurrency;
1909
+ } else {
1910
+ delete getGlobal().convertCurrency;
1911
+ }
1912
+ });
1913
+
1914
+ it(`should convert successfully`, () => {
1915
+ getGlobal().convertCurrency = () => 1.0;
1916
+ const bidCopy = utils.deepClone(MOCK.BID_RESPONSE);
1917
+ bidCopy.currency = 'JPY';
1918
+ bidCopy.cpm = 100;
1919
+
1920
+ const bidResponseObj = parseBidResponse(bidCopy);
1921
+ expect(bidResponseObj.conversionError).to.equal(undefined);
1922
+ expect(bidResponseObj.ogCurrency).to.equal(undefined);
1923
+ expect(bidResponseObj.ogPrice).to.equal(undefined);
1924
+ expect(bidResponseObj.bidPriceUSD).to.equal(1.0);
1925
+ });
1926
+
1927
+ it(`should catch error and set to zero with conversionError flag true`, () => {
1928
+ getGlobal().convertCurrency = () => {
1929
+ throw new Error('I am an error');
1930
+ };
1931
+ const bidCopy = utils.deepClone(MOCK.BID_RESPONSE);
1932
+ bidCopy.currency = 'JPY';
1933
+ bidCopy.cpm = 100;
1934
+
1935
+ const bidResponseObj = parseBidResponse(bidCopy);
1936
+ expect(bidResponseObj.conversionError).to.equal(true);
1937
+ expect(bidResponseObj.ogCurrency).to.equal('JPY');
1938
+ expect(bidResponseObj.ogPrice).to.equal(100);
1939
+ expect(bidResponseObj.bidPriceUSD).to.equal(0);
1940
+ });
1941
+ });
1942
+ });