• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 19122650799

06 Nov 2025 02:17AM UTC coverage: 96.228% (-0.008%) from 96.236%
19122650799

push

github

web-flow
Medianet Analytics Adapter: pass ext from Prebid Server Response and Prebid 10 Updates (#13825)

* Pass ext values from Prebid Server bid response to bidderRequest and use pbjs.getUserIds instead of bid.userid

* Test cases added to verify Prebid Server ext response for an Auction

* Store Prebid Server Ext Fields in pbsExt and test case updated

* fix(lint): resolve linting issues causing pipeline failure

52738 of 64615 branches covered (81.62%)

58 of 58 new or added lines in 3 files covered. (100.0%)

104 existing lines in 13 files now uncovered.

201791 of 209700 relevant lines covered (96.23%)

124.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.39
/test/spec/modules/intentIqAnalyticsAdapter_spec.js
1
import { expect } from 'chai';
2
import iiqAnalyticsAnalyticsAdapter from 'modules/intentIqAnalyticsAdapter.js';
3
import * as utils from 'src/utils.js';
4
import { server } from 'test/mocks/xhr.js';
5
import { config } from 'src/config.js';
6
import { EVENTS } from 'src/constants.js';
7
import * as events from 'src/events.js';
8
import { getStorageManager } from 'src/storageManager.js';
9
import sinon from 'sinon';
10
import { REPORTER_ID, preparePayload, restoreReportList } from '../../../modules/intentIqAnalyticsAdapter.js';
11
import { FIRST_PARTY_KEY, PREBID, VERSION } from '../../../libraries/intentIqConstants/intentIqConstants.js';
12
import * as detectBrowserUtils from '../../../libraries/intentIqUtils/detectBrowserUtils.js';
13
import { getReferrer, appendVrrefAndFui } from '../../../libraries/intentIqUtils/getRefferer.js';
14
import { gppDataHandler, uspDataHandler, gdprDataHandler } from '../../../src/consentHandler.js';
15

16
const partner = 10;
1✔
17
const defaultData = '{"pcid":"f961ffb1-a0e1-4696-a9d2-a21d815bd344", "group": "A"}';
1✔
18
const version = VERSION;
1✔
19
const REPORT_ENDPOINT = 'https://reports.intentiq.com/report';
1✔
20
const REPORT_ENDPOINT_GDPR = 'https://reports-gdpr.intentiq.com/report';
1✔
21
const REPORT_SERVER_ADDRESS = 'https://test-reports.intentiq.com/report';
1✔
22

23
const storage = getStorageManager({ moduleType: 'analytics', moduleName: 'iiqAnalytics' });
1✔
24

25
const randomVal = () => Math.floor(Math.random() * 100000) + 1
28✔
26

27
const getUserConfig = () => [
58✔
28
  {
29
    'name': 'intentIqId',
30
    'params': {
31
      'partner': partner,
32
      'unpack': null,
33
      'manualWinReportEnabled': false,
34
    },
35
    'storage': {
36
      'type': 'html5',
37
      'name': 'intentIqId',
38
      'expires': 60,
39
      'refreshInSeconds': 14400
40
    }
41
  }
42
];
43

44
const getUserConfigWithReportingServerAddress = () => [
1✔
45
  {
46
    'name': 'intentIqId',
47
    'params': {
48
      'partner': partner,
49
      'unpack': null,
50
    },
51
    'storage': {
52
      'type': 'html5',
53
      'name': 'intentIqId',
54
      'expires': 60,
55
      'refreshInSeconds': 14400
56
    }
57
  }
58
];
59

60
const getWonRequest = () => ({
28✔
61
  'bidderCode': 'pubmatic',
62
  'width': 728,
63
  'height': 90,
64
  'statusMessage': 'Bid available',
65
  'adId': '23caeb34c55da51',
66
  'requestId': '87615b45ca4973',
67
  'transactionId': '5e69fd76-8c86-496a-85ce-41ae55787a50',
68
  'auctionId': '0cbd3a43-ff45-47b8-b002-16d3946b23bf-' + randomVal(),
69
  'mediaType': 'banner',
70
  'source': 'client',
71
  'cpm': 5,
72
  'currency': 'USD',
73
  'ttl': 300,
74
  'referrer': '',
75
  'adapterCode': 'pubmatic',
76
  'originalCpm': 5,
77
  'originalCurrency': 'USD',
78
  'responseTimestamp': 1669644710345,
79
  'requestTimestamp': 1669644710109,
80
  'bidder': 'testbidder',
81
  'timeToRespond': 236,
82
  'pbLg': '5.00',
83
  'pbMg': '5.00',
84
  'pbHg': '5.00',
85
  'pbAg': '5.00',
86
  'pbDg': '5.00',
87
  'pbCg': '',
88
  'size': '728x90',
89
  'status': 'rendered'
90
});
91

92
const enableAnalyticWithSpecialOptions = (options) => {
1✔
93
  iiqAnalyticsAnalyticsAdapter.disableAnalytics()
11✔
94
  iiqAnalyticsAnalyticsAdapter.enableAnalytics({
11✔
95
    provider: 'iiqAnalytics',
96
    options
97
  })
98
}
99

100
describe('IntentIQ tests all', function () {
1✔
101
  let logErrorStub;
102
  let getWindowSelfStub;
103
  let getWindowTopStub;
104
  let getWindowLocationStub;
105
  let detectBrowserStub;
106

107
  beforeEach(function () {
1✔
108
    logErrorStub = sinon.stub(utils, 'logError');
40✔
109
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(getUserConfig());
40✔
110
    sinon.stub(events, 'getEvents').returns([]);
40✔
111
    iiqAnalyticsAnalyticsAdapter.enableAnalytics({
40✔
112
      provider: 'iiqAnalytics',
113
    });
114
    iiqAnalyticsAnalyticsAdapter.initOptions = {
40✔
115
      lsValueInitialized: false,
116
      partner: null,
117
      fpid: null,
118
      userGroup: null,
119
      currentGroup: null,
120
      dataInLs: null,
121
      eidl: null,
122
      lsIdsInitialized: false,
123
      manualWinReportEnabled: false,
124
      domainName: null
125
    };
126
    if (iiqAnalyticsAnalyticsAdapter.track.restore) {
40!
UNCOV
127
      iiqAnalyticsAnalyticsAdapter.track.restore();
×
128
    }
129
    sinon.spy(iiqAnalyticsAnalyticsAdapter, 'track');
40✔
130
  });
131

132
  afterEach(function () {
1✔
133
    logErrorStub.restore();
40✔
134
    if (getWindowSelfStub) getWindowSelfStub.restore();
40✔
135
    if (getWindowTopStub) getWindowTopStub.restore();
40✔
136
    if (getWindowLocationStub) getWindowLocationStub.restore();
40✔
137
    if (detectBrowserStub) detectBrowserStub.restore();
40✔
138
    config.getConfig.restore();
40✔
139
    events.getEvents.restore();
40✔
140
    iiqAnalyticsAnalyticsAdapter.disableAnalytics();
40✔
141
    if (iiqAnalyticsAnalyticsAdapter.track.restore) {
40✔
142
      iiqAnalyticsAnalyticsAdapter.track.restore();
40✔
143
    }
144
    localStorage.clear();
40✔
145
    server.reset();
40✔
146
  });
147

148
  it('should send POST request with payload in request body if reportMethod is POST', function () {
1✔
149
    enableAnalyticWithSpecialOptions({
1✔
150
      reportMethod: 'POST'
151
    })
152
    const [userConfig] = getUserConfig();
1✔
153
    const wonRequest = getWonRequest();
1✔
154

155
    config.getConfig.restore();
1✔
156
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns([userConfig]);
1✔
157

158
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
159

160
    events.emit(EVENTS.BID_WON, wonRequest);
1✔
161

162
    const request = server.requests[0];
1✔
163
    restoreReportList();
1✔
164

165
    const expectedData = preparePayload(wonRequest);
1✔
166
    const expectedPayload = `["${btoa(JSON.stringify(expectedData))}"]`;
1✔
167

168
    expect(request.method).to.equal('POST');
1✔
169
    expect(request.requestBody).to.equal(expectedPayload);
1✔
170
  });
171

172
  it('should send GET request with payload in query string if reportMethod is NOT provided', function () {
1✔
173
    const [userConfig] = getUserConfig();
1✔
174
    const wonRequest = getWonRequest();
1✔
175
    config.getConfig.restore();
1✔
176
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns([userConfig]);
1✔
177

178
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
179
    events.emit(EVENTS.BID_WON, wonRequest);
1✔
180

181
    const request = server.requests[0];
1✔
182

183
    expect(request.method).to.equal('GET');
1✔
184

185
    const url = new URL(request.url);
1✔
186
    const payloadEncoded = url.searchParams.get('payload');
1✔
187
    const decoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
188

189
    restoreReportList();
1✔
190
    const expected = preparePayload(wonRequest);
1✔
191

192
    expect(decoded.partnerId).to.equal(expected.partnerId);
1✔
193
    expect(decoded.adType).to.equal(expected.adType);
1✔
194
    expect(decoded.prebidAuctionId).to.equal(expected.prebidAuctionId);
1✔
195
  });
196

197
  it('IIQ Analytical Adapter bid win report', function () {
1✔
198
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
199
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876' });
1✔
200
    const expectedVrref = getWindowLocationStub().href;
1✔
201
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
202

203
    expect(server.requests.length).to.be.above(0);
1✔
204
    const request = server.requests[0];
1✔
205
    const parsedUrl = new URL(request.url);
1✔
206
    const vrref = parsedUrl.searchParams.get('vrref');
1✔
207
    expect(request.url).to.contain(REPORT_ENDPOINT + '?pid=' + partner + '&mct=1');
1✔
208
    expect(request.url).to.contain(`&jsver=${version}`);
1✔
209
    expect(`&vrref=${decodeURIComponent(vrref)}`).to.contain(`&vrref=${expectedVrref}`);
1✔
210
    expect(request.url).to.contain('&payload=');
1✔
211
    expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
1✔
212
  });
213

214
  it('should include adType in payload when present in BID_WON event', function () {
1✔
215
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
216
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876/' });
1✔
217
    const bidWonEvent = { ...getWonRequest(), mediaType: 'video' };
1✔
218

219
    events.emit(EVENTS.BID_WON, bidWonEvent);
1✔
220

221
    const request = server.requests[0];
1✔
222
    const urlParams = new URL(request.url);
1✔
223
    const payloadEncoded = urlParams.searchParams.get('payload');
1✔
224
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
225

226
    expect(server.requests.length).to.be.above(0);
1✔
227
    expect(payloadDecoded).to.have.property('adType', bidWonEvent.mediaType);
1✔
228
  });
229

230
  it('should include adType in payload when present in reportExternalWin event', function () {
1✔
231
    enableAnalyticWithSpecialOptions({ manualWinReportEnabled: true })
1✔
232
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876/' });
1✔
233
    const externalWinEvent = { cpm: 1, currency: 'USD', adType: 'banner' };
1✔
234
    const [userConfig] = getUserConfig();
1✔
235
    config.getConfig.restore();
1✔
236
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns([userConfig]);
1✔
237

238
    const partnerId = userConfig.params.partner;
1✔
239

240
    events.emit(EVENTS.BID_REQUESTED);
1✔
241

242
    window[`intentIqAnalyticsAdapter_${partnerId}`].reportExternalWin(externalWinEvent);
1✔
243

244
    const request = server.requests[0];
1✔
245
    const urlParams = new URL(request.url);
1✔
246
    const payloadEncoded = urlParams.searchParams.get('payload');
1✔
247
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
248

249
    expect(server.requests.length).to.be.above(0);
1✔
250
    expect(payloadDecoded).to.have.property('adType', externalWinEvent.adType);
1✔
251
  });
252

253
  it('should send report to report-gdpr address if gdpr is detected', function () {
1✔
254
    const gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns({ gppString: '{"key1":"value1","key2":"value2"}' });
1✔
255
    const uspStub = sinon.stub(uspDataHandler, 'getConsentData').returns('1NYN');
1✔
256
    const gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns({ consentString: 'gdprConsent' });
1✔
257

258
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
259

260
    expect(server.requests.length).to.be.above(0);
1✔
261
    const request = server.requests[0];
1✔
262

263
    expect(request.url).to.contain(REPORT_ENDPOINT_GDPR);
1✔
264
    gppStub.restore();
1✔
265
    uspStub.restore();
1✔
266
    gdprStub.restore();
1✔
267
  });
268

269
  it('should initialize with default configurations', function () {
1✔
270
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized).to.be.false;
1✔
271
  });
272

273
  it('should handle BID_WON event with group configuration from local storage', function () {
1✔
274
    localStorage.setItem(FIRST_PARTY_KEY, '{"pcid":"testpcid", "group": "B"}');
1✔
275
    const expectedVrref = encodeURIComponent('http://localhost:9876/');
1✔
276

277
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
278

279
    expect(server.requests.length).to.be.above(0);
1✔
280
    const request = server.requests[0];
1✔
281
    expect(request.url).to.contain('https://reports.intentiq.com/report?pid=' + partner + '&mct=1');
1✔
282
    expect(request.url).to.contain(`&jsver=${version}`);
1✔
283
    expect(request.url).to.contain(`&vrref=${expectedVrref}`);
1✔
284
    expect(request.url).to.contain('iiqid=testpcid');
1✔
285
  });
286

287
  it('should handle BID_WON event with default group configuration', function () {
1✔
288
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
289
    const defaultDataObj = JSON.parse(defaultData)
1✔
290
    const wonRequest = getWonRequest();
1✔
291

292
    events.emit(EVENTS.BID_WON, wonRequest);
1✔
293

294
    expect(server.requests.length).to.be.above(0);
1✔
295
    const request = server.requests[0];
1✔
296
    restoreReportList()
1✔
297
    const dataToSend = preparePayload(wonRequest);
1✔
298
    const base64String = btoa(JSON.stringify(dataToSend));
1✔
299
    const payload = encodeURIComponent(JSON.stringify([base64String]));
1✔
300
    const expectedUrl = appendVrrefAndFui(REPORT_ENDPOINT +
1✔
301
      `?pid=${partner}&mct=1&iiqid=${defaultDataObj.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&uh=&gdpr=0`, iiqAnalyticsAnalyticsAdapter.initOptions.domainName
302
    );
303
    const urlWithPayload = expectedUrl + `&payload=${payload}`;
1✔
304

305
    expect(request.url).to.equal(urlWithPayload);
1✔
306
    expect(dataToSend.pcid).to.equal(defaultDataObj.pcid)
1✔
307
  });
308

309
  it('should send CMP data in report if available', function () {
1✔
310
    const uspData = '1NYN';
1✔
311
    const gppData = { gppString: '{"key1":"value1","key2":"value2"}' };
1✔
312
    const gdprData = { consentString: 'gdprConsent' };
1✔
313

314
    const gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(gppData);
1✔
315
    const uspStub = sinon.stub(uspDataHandler, 'getConsentData').returns(uspData);
1✔
316
    const gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns(gdprData);
1✔
317

318
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876/' });
1✔
319

320
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
321

322
    expect(server.requests.length).to.be.above(0);
1✔
323
    const request = server.requests[0];
1✔
324

325
    expect(request.url).to.contain(`&us_privacy=${encodeURIComponent(uspData)}`);
1✔
326
    expect(request.url).to.contain(`&gpp=${encodeURIComponent(gppData.gppString)}`);
1✔
327
    expect(request.url).to.contain(`&gdpr_consent=${encodeURIComponent(gdprData.consentString)}`);
1✔
328
    expect(request.url).to.contain(`&gdpr=1`);
1✔
329
    gppStub.restore();
1✔
330
    uspStub.restore();
1✔
331
    gdprStub.restore();
1✔
332
  });
333

334
  it('should not send request if manualWinReportEnabled is true', function () {
1✔
335
    iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = true;
1✔
336
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
337
    expect(server.requests.length).to.equal(1);
1✔
338
  });
339

340
  it('should read data from local storage', function () {
1✔
341
    localStorage.setItem(FIRST_PARTY_KEY, '{"group": "A"}');
1✔
342
    localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, '{"data":"testpcid", "eidl": 10}');
1✔
343
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
344
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.dataInLs).to.equal('testpcid');
1✔
345
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.eidl).to.equal(10);
1✔
346
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup).to.equal('A');
1✔
347
  });
348

349
  it('should handle initialization values from local storage', function () {
1✔
350
    localStorage.setItem(FIRST_PARTY_KEY, '{"pcid":"testpcid", "group": "B"}');
1✔
351
    localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, '{"data":"testpcid"}');
1✔
352
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
353
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup).to.equal('B');
1✔
354
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.fpid).to.be.not.null;
1✔
355
  });
356

357
  it('should handle reportExternalWin', function () {
1✔
358
    events.emit(EVENTS.BID_REQUESTED);
1✔
359
    iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = false;
1✔
360
    localStorage.setItem(FIRST_PARTY_KEY, '{"pcid":"testpcid", "group": "B"}');
1✔
361
    localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, '{"data":"testpcid"}');
1✔
362
    expect(window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin).to.be.a('function');
1✔
363
    expect(window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ cpm: 1, currency: 'USD' })).to.equal(false);
1✔
364
  });
365

366
  it('should return window.location.href when window.self === window.top', function () {
1✔
367
    // Stub helper functions
368
    getWindowSelfStub = sinon.stub(utils, 'getWindowSelf').returns(window);
1✔
369
    getWindowTopStub = sinon.stub(utils, 'getWindowTop').returns(window);
1✔
370
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876/' });
1✔
371

372
    const referrer = getReferrer();
1✔
373
    expect(referrer).to.equal('http://localhost:9876/');
1✔
374
  });
375

376
  it('should return window.top.location.href when window.self !== window.top and access is successful', function () {
1✔
377
    // Stub helper functions to simulate iframe
378
    getWindowSelfStub = sinon.stub(utils, 'getWindowSelf').returns({});
1✔
379
    getWindowTopStub = sinon.stub(utils, 'getWindowTop').returns({ location: { href: 'http://example.com/' } });
1✔
380

381
    const referrer = getReferrer();
1✔
382

383
    expect(referrer).to.equal('http://example.com/');
1✔
384
  });
385

386
  it('should return an empty string and log an error when accessing window.top.location.href throws an error', function () {
1✔
387
    // Stub helper functions to simulate error
388
    getWindowSelfStub = sinon.stub(utils, 'getWindowSelf').returns({});
1✔
389
    getWindowTopStub = sinon.stub(utils, 'getWindowTop').throws(new Error('Access denied'));
1✔
390

391
    const referrer = getReferrer();
1✔
392
    expect(referrer).to.equal('');
1✔
393
    expect(logErrorStub.calledOnce).to.be.true;
1✔
394
    expect(logErrorStub.firstCall.args[0]).to.contain('Error accessing location: Error: Access denied');
1✔
395
  });
396

397
  it('should not send request if the browser is in blacklist (chrome)', function () {
1✔
398
    const USERID_CONFIG_BROWSER = [...getUserConfig()];
1✔
399
    USERID_CONFIG_BROWSER[0].params.browserBlackList = 'ChrOmE';
1✔
400

401
    config.getConfig.restore();
1✔
402
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG_BROWSER);
1✔
403
    detectBrowserStub = sinon.stub(detectBrowserUtils, 'detectBrowser').returns('chrome');
1✔
404

405
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
406
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
407

408
    expect(server.requests.length).to.equal(0);
1✔
409
  });
410

411
  it('should send request if the browser is not in blacklist (safari)', function () {
1✔
412
    const USERID_CONFIG_BROWSER = [...getUserConfig()];
1✔
413
    USERID_CONFIG_BROWSER[0].params.browserBlackList = 'chrome,firefox';
1✔
414

415
    config.getConfig.restore();
1✔
416
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG_BROWSER);
1✔
417
    detectBrowserStub = sinon.stub(detectBrowserUtils, 'detectBrowser').returns('safari');
1✔
418

419
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
420
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
421

422
    expect(server.requests.length).to.be.above(0);
1✔
423
    const request = server.requests[0];
1✔
424
    expect(request.url).to.contain(`https://reports.intentiq.com/report?pid=${partner}&mct=1`);
1✔
425
    expect(request.url).to.contain(`&jsver=${version}`);
1✔
426
    expect(request.url).to.contain(`&vrref=${encodeURIComponent('http://localhost:9876/')}`);
1✔
427
    expect(request.url).to.contain('&payload=');
1✔
428
    expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
1✔
429
  });
430

431
  it('should send request in reportingServerAddress no gdpr', function () {
1✔
432
    const USERID_CONFIG_BROWSER = [...getUserConfigWithReportingServerAddress()];
1✔
433
    USERID_CONFIG_BROWSER[0].params.browserBlackList = 'chrome,firefox';
1✔
434

435
    config.getConfig.restore();
1✔
436
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG_BROWSER);
1✔
437
    detectBrowserStub = sinon.stub(detectBrowserUtils, 'detectBrowser').returns('safari');
1✔
438
    enableAnalyticWithSpecialOptions({ reportingServerAddress: REPORT_SERVER_ADDRESS })
1✔
439

440
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
441
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
442

443
    expect(server.requests.length).to.be.above(0);
1✔
444
    const request = server.requests[0];
1✔
445
    expect(request.url).to.contain(REPORT_SERVER_ADDRESS);
1✔
446
  });
447

448
  it('should include source parameter in report URL', function () {
1✔
449
    localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify(defaultData));
1✔
450

451
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
452
    const request = server.requests[0];
1✔
453

454
    expect(server.requests.length).to.be.above(0);
1✔
455
    expect(request.url).to.include(`&source=${PREBID}`);
1✔
456
  });
457

458
  it('should use correct key if siloEnabled is true', function () {
1✔
459
    const siloEnabled = true;
1✔
460
    const USERID_CONFIG = [...getUserConfig()];
1✔
461
    USERID_CONFIG[0].params.siloEnabled = siloEnabled;
1✔
462

463
    config.getConfig.restore();
1✔
464
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG);
1✔
465

466
    localStorage.setItem(FIRST_PARTY_KEY, `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + partner : ''}`);
1!
467
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
468

469
    expect(server.requests.length).to.be.above(0);
1✔
470
    const request = server.requests[0];
1✔
471
    expect(request.url).to.contain(REPORT_ENDPOINT + '?pid=' + partner + '&mct=1');
1✔
472
  });
473

474
  it('should send additionalParams in report if valid and small enough', function () {
1✔
475
    const userConfig = getUserConfig();
1✔
476
    userConfig[0].params.additionalParams = [{
1✔
477
      parameterName: 'general',
478
      parameterValue: 'Lee',
479
      destination: [0, 0, 1]
480
    }];
481

482
    config.getConfig.restore();
1✔
483
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(userConfig);
1✔
484

485
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
486
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
487

488
    const request = server.requests[0];
1✔
489
    expect(request.url).to.include('general=Lee');
1✔
490
  });
491

492
  it('should not send additionalParams in report if value is too large', function () {
1✔
493
    const longVal = 'x'.repeat(5000000);
1✔
494
    const userConfig = getUserConfig();
1✔
495
    userConfig[0].params.additionalParams = [{
1✔
496
      parameterName: 'general',
497
      parameterValue: longVal,
498
      destination: [0, 0, 1]
499
    }];
500

501
    config.getConfig.restore();
1✔
502
    sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns(userConfig);
1✔
503

504
    localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
505
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
506

507
    const request = server.requests[0];
1✔
508
    expect(request.url).not.to.include('general');
1✔
509
  });
510
  it('should include spd parameter from LS in report URL', function () {
1✔
511
    const spdObject = { foo: 'bar', value: 42 };
1✔
512
    const expectedSpdEncoded = encodeURIComponent(JSON.stringify(spdObject));
1✔
513

514
    localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({ ...defaultData, spd: spdObject }));
1✔
515
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876/' });
1✔
516

517
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
518

519
    const request = server.requests[0];
1✔
520

521
    expect(server.requests.length).to.be.above(0);
1✔
522
    expect(request.url).to.include(`&spd=${expectedSpdEncoded}`);
1✔
523
  });
524

525
  it('should include spd parameter string from LS in report URL', function () {
1✔
526
    const spdObject = 'server provided data';
1✔
527
    const expectedSpdEncoded = encodeURIComponent(spdObject);
1✔
528

529
    localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({ ...defaultData, spd: spdObject }));
1✔
530
    getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({ href: 'http://localhost:9876/' });
1✔
531

532
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
533

534
    const request = server.requests[0];
1✔
535

536
    expect(server.requests.length).to.be.above(0);
1✔
537
    expect(request.url).to.include(`&spd=${expectedSpdEncoded}`);
1✔
538
  });
539

540
  describe('GAM prediction reporting', function () {
1✔
541
    function createMockGAM() {
542
      const listeners = {};
2✔
543
      return {
2✔
544
        cmd: [],
545
        pubads: () => ({
1✔
546
          addEventListener: (name, cb) => {
547
            listeners[name] = cb;
1✔
548
          }
549
        }),
550
        _listeners: listeners
551
      };
552
    }
553

554
    function withConfigGamPredict(gamObj) {
555
      const [userConfig] = getUserConfig();
2✔
556
      userConfig.params.gamObjectReference = gamObj;
2✔
557
      userConfig.params.gamPredictReporting = true;
2✔
558
      config.getConfig.restore();
2✔
559
      sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns([userConfig]);
2✔
560
    }
561

562
    it('should subscribe to GAM and send report on slotRenderEnded without prior bidWon', function () {
1✔
563
      const gam = createMockGAM();
1✔
564
      withConfigGamPredict(gam);
1✔
565

566
      // enable subscription by LS flag
567
      localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ gpr: true }));
1✔
568
      localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
569

570
      // provide recent auctionEnd with matching bid to enrich payload
571
      events.getEvents.restore();
1✔
572
      sinon.stub(events, 'getEvents').returns([
1✔
573
        {
574
          eventType: 'auctionEnd', args: {
575
            auctionId: 'auc-1',
576
            adUnitCodes: ['ad-unit-1'],
577
            bidsReceived: [{ bidder: 'pubmatic', adUnitCode: 'ad-unit-1', cpm: 1, currency: 'USD', originalCpm: 1, originalCurrency: 'USD', status: 'rendered' }]
578
          }
579
        }
580
      ]);
581

582
      // trigger adapter to subscribe
583
      events.emit(EVENTS.BID_REQUESTED);
1✔
584

585
      // execute GAM cmd to register listener
586
      gam.cmd.forEach(fn => fn());
1✔
587

588
      // simulate slotRenderEnded
589
      const slot = {
1✔
590
        getSlotElementId: () => 'ad-unit-1',
1✔
591
        getAdUnitPath: () => '/123/foo',
1✔
592
        getTargetingKeys: () => ['hb_bidder', 'hb_adid'],
1✔
593
        getTargeting: (k) => k === 'hb_bidder' ? ['pubmatic'] : k === 'hb_adid' ? ['ad123'] : []
2!
594
      };
595
      if (gam._listeners['slotRenderEnded']) {
1✔
596
        gam._listeners['slotRenderEnded']({ isEmpty: false, slot });
1✔
597
      }
598

599
      expect(server.requests.length).to.be.above(0);
1✔
600
    });
601

602
    it('should NOT send report if a matching bidWon already exists', function () {
1✔
603
      const gam = createMockGAM();
1✔
604
      withConfigGamPredict(gam);
1✔
605

606
      localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ gpr: true }));
1✔
607
      localStorage.setItem(FIRST_PARTY_KEY, defaultData);
1✔
608

609
      // provide prior bidWon matching placementId and hb_adid
610
      events.getEvents.restore();
1✔
611
      sinon.stub(events, 'getEvents').returns([
1✔
612
        { eventType: 'bidWon', args: { adId: 'ad123' }, id: 'ad-unit-1' }
613
      ]);
614

615
      events.emit(EVENTS.BID_REQUESTED);
1✔
616
      gam.cmd.forEach(fn => fn());
1✔
617

618
      const slot = {
1✔
UNCOV
619
        getSlotElementId: () => 'ad-unit-1',
×
UNCOV
620
        getAdUnitPath: () => '/123/foo',
×
UNCOV
621
        getTargetingKeys: () => ['hb_bidder', 'hb_adid'],
×
UNCOV
622
        getTargeting: (k) => k === 'hb_bidder' ? ['pubmatic'] : k === 'hb_adid' ? ['ad123'] : []
×
623
      };
624

625
      const initialRequests = server.requests.length;
1✔
626
      if (gam._listeners['slotRenderEnded']) {
1!
UNCOV
627
        gam._listeners['slotRenderEnded']({ isEmpty: false, slot });
×
628
      }
629
      expect(server.requests.length).to.equal(initialRequests);
1✔
630
    });
631
  });
632

633
  const testCasesVrref = [
1✔
634
    {
635
      description: 'domainName matches window.top.location.href',
636
      getWindowSelf: {},
637
      getWindowTop: { location: { href: 'http://example.com/page' } },
638
      getWindowLocation: { href: 'http://example.com/page' },
639
      domainName: 'example.com',
640
      expectedVrref: encodeURIComponent('http://example.com/page'),
641
      shouldContainFui: false
642
    },
643
    {
644
      description: 'domainName does not match window.top.location.href',
645
      getWindowSelf: {},
646
      getWindowTop: { location: { href: 'http://anotherdomain.com/page' } },
647
      getWindowLocation: { href: 'http://anotherdomain.com/page' },
648
      domainName: 'example.com',
649
      expectedVrref: encodeURIComponent('example.com'),
650
      shouldContainFui: false
651
    },
652
    {
653
      description: 'domainName is missing, only fui=1 is returned',
654
      getWindowSelf: {},
655
      getWindowTop: { location: { href: '' } },
656
      getWindowLocation: { href: '' },
657
      domainName: null,
658
      expectedVrref: '',
659
      shouldContainFui: true
660
    },
661
    {
662
      description: 'domainName is missing',
663
      getWindowSelf: {},
664
      getWindowTop: { location: { href: 'http://example.com/page' } },
665
      getWindowLocation: { href: 'http://example.com/page' },
666
      domainName: null,
667
      expectedVrref: encodeURIComponent('http://example.com/page'),
668
      shouldContainFui: false
669
    },
670
  ];
671

672
  testCasesVrref.forEach(({ description, getWindowSelf, getWindowTop, getWindowLocation, domainName, expectedVrref, shouldContainFui }) => {
4✔
673
    it(`should append correct vrref when ${description}`, function () {
4✔
674
      getWindowSelfStub = sinon.stub(utils, 'getWindowSelf').returns(getWindowSelf);
4✔
675
      getWindowTopStub = sinon.stub(utils, 'getWindowTop').returns(getWindowTop);
4✔
676
      getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns(getWindowLocation);
4✔
677

678
      const url = 'https://reports.intentiq.com/report?pid=10';
4✔
679
      const modifiedUrl = appendVrrefAndFui(url, domainName);
4✔
680
      const urlObj = new URL(modifiedUrl);
4✔
681

682
      const vrref = encodeURIComponent(urlObj.searchParams.get('vrref') || '');
4✔
683
      const fui = urlObj.searchParams.get('fui');
4✔
684

685
      expect(vrref).to.equal(expectedVrref);
4✔
686
      expect(urlObj.searchParams.has('fui')).to.equal(shouldContainFui);
4✔
687
      if (shouldContainFui) {
4✔
688
        expect(fui).to.equal('1');
1✔
689
      }
690
    });
691
  });
692

693
  const adUnitConfigTests = [
1✔
694
    {
695
      adUnitConfig: 1,
696
      description: 'should extract adUnitCode first (adUnitConfig = 1)',
697
      event: { adUnitCode: 'adUnitCode-123', placementId: 'placementId-456' },
698
      expectedPlacementId: 'adUnitCode-123'
699
    },
700
    {
701
      adUnitConfig: 1,
702
      description: 'should extract placementId if there is no adUnitCode (adUnitConfig = 1)',
703
      event: { placementId: 'placementId-456' },
704
      expectedPlacementId: 'placementId-456'
705
    },
706
    {
707
      adUnitConfig: 2,
708
      description: 'should extract placementId first (adUnitConfig = 2)',
709
      event: { adUnitCode: 'adUnitCode-123', placementId: 'placementId-456' },
710
      expectedPlacementId: 'placementId-456'
711
    },
712
    {
713
      adUnitConfig: 2,
714
      description: 'should extract adUnitCode if there is no placementId (adUnitConfig = 2)',
715
      event: { adUnitCode: 'adUnitCode-123', },
716
      expectedPlacementId: 'adUnitCode-123'
717
    },
718
    {
719
      adUnitConfig: 3,
720
      description: 'should extract only adUnitCode (adUnitConfig = 3)',
721
      event: { adUnitCode: 'adUnitCode-123', placementId: 'placementId-456' },
722
      expectedPlacementId: 'adUnitCode-123'
723
    },
724
    {
725
      adUnitConfig: 4,
726
      description: 'should extract only placementId (adUnitConfig = 4)',
727
      event: { adUnitCode: 'adUnitCode-123', placementId: 'placementId-456' },
728
      expectedPlacementId: 'placementId-456'
729
    },
730
    {
731
      adUnitConfig: 1,
732
      description: 'should return empty placementId if neither adUnitCode or placementId exist',
733
      event: {},
734
      expectedPlacementId: ''
735
    },
736
    {
737
      adUnitConfig: 1,
738
      description: 'should extract placementId from params array if no top-level adUnitCode or placementId exist (adUnitConfig = 1)',
739
      event: {
740
        params: [{ someKey: 'value' }, { placementId: 'nested-placementId' }]
741
      },
742
      expectedPlacementId: 'nested-placementId'
743
    }
744
  ];
745

746
  adUnitConfigTests.forEach(({ adUnitConfig, description, event, expectedPlacementId }) => {
8✔
747
    it(description, function () {
8✔
748
      const [userConfig] = getUserConfig();
8✔
749
      enableAnalyticWithSpecialOptions({ adUnitConfig })
8✔
750

751
      config.getConfig.restore();
8✔
752
      sinon.stub(config, 'getConfig').withArgs('userSync.userIds').returns([userConfig]);
8✔
753

754
      const testEvent = { ...getWonRequest(), ...event };
8✔
755
      events.emit(EVENTS.BID_WON, testEvent);
8✔
756

757
      const request = server.requests[0];
8✔
758
      const urlParams = new URL(request.url);
8✔
759
      const encodedPayload = urlParams.searchParams.get('payload');
8✔
760
      const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0]));
8✔
761

762
      expect(server.requests.length).to.be.above(0);
8✔
763
      expect(encodedPayload).to.exist;
8✔
764
      expect(decodedPayload).to.have.property('placementId', expectedPlacementId);
8✔
765
    });
766
  });
767
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc