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

prebid / Prebid.js / 21359463030

26 Jan 2026 01:31PM UTC coverage: 96.218% (+0.003%) from 96.215%
21359463030

push

github

web-flow
Core: adding ima params to local cache request (#14312)

* Core: adding ima params to local cache request

* retrieving ima params

* usp data handler

41687 of 51254 branches covered (81.33%)

6 of 15 new or added lines in 1 file covered. (40.0%)

53 existing lines in 6 files now uncovered.

208851 of 217061 relevant lines covered (96.22%)

71.61 hits per line

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

98.15
/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 { getGlobal } from "../../../src/prebidGlobal.js";
9
import sinon from "sinon";
10
import {
11
  REPORTER_ID,
12
  preparePayload,
13
  restoreReportList,
14
} from "../../../modules/intentIqAnalyticsAdapter.js";
15
import {
16
  FIRST_PARTY_KEY,
17
  PREBID,
18
  VERSION,
19
  WITHOUT_IIQ,
20
  WITH_IIQ,
21
  AB_CONFIG_SOURCE,
22
} from "../../../libraries/intentIqConstants/intentIqConstants.js";
23
import * as detectBrowserUtils from "../../../libraries/intentIqUtils/detectBrowserUtils.js";
24
import {
25
  getCurrentUrl,
26
  appendVrrefAndFui,
27
} from "../../../libraries/intentIqUtils/getRefferer.js";
28
import {
29
  gppDataHandler,
30
  uspDataHandler,
31
  gdprDataHandler,
32
} from "../../../src/consentHandler.js";
33

34
let getConfigStub;
35
let userIdConfigForTest;
36
const partner = 10;
1✔
37
const identityName = `iiq_identity_${partner}`
1✔
38
const defaultIdentityObject = {
1✔
39
  firstPartyData: {
40
    pcid: "f961ffb1-a0e1-4696-a9d2-a21d815bd344",
41
    pcidDate: 1762527405808,
42
    uspString: "undefined",
43
    gppString: "undefined",
44
    gdprString: "",
45
    date: Date.now(),
46
    sCal: Date.now() - 36000,
47
    isOptedOut: false,
48
    pid: "profile",
49
    dbsaved: "true"
50
  },
51
  partnerData: {
52
    abTestUuid: "abTestUuid",
53
    adserverDeviceType: 1,
54
    clientType: 2,
55
    cttl: 43200000,
56
    date: Date.now(),
57
    profile: "profile",
58
    wsrvcll: true,
59
  },
60
  clientHints: JSON.stringify({
61
    0: '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
62
    1: "?0",
63
    2: '"macOS"',
64
    3: '"arm"',
65
    4: '"64"',
66
    6: '"15.6.1"',
67
    7: "?0",
68
    8: '"Chromium";v="142.0.7444.60", "Google Chrome";v="142.0.7444.60", "Not_A Brand";v="99.0.0.0"',
69
  }),
70
};
71
const regionCases = [
1✔
72
  {
73
    name: 'default (no region)',
74
    region: undefined,
75
    expectedEndpoint: 'https://reports.intentiq.com/report'
76
  },
77
  {
78
    name: 'apac',
79
    region: 'apac',
80
    expectedEndpoint: 'https://reports-apac.intentiq.com/report'
81
  },
82
  {
83
    name: 'emea',
84
    region: 'emea',
85
    expectedEndpoint: 'https://reports-emea.intentiq.com/report'
86
  },
87
  {
88
    name: 'gdpr',
89
    region: 'gdpr',
90
    expectedEndpoint: 'https://reports-gdpr.intentiq.com/report'
91
  }
92
]
93
const version = VERSION;
1✔
94
const REPORT_ENDPOINT = "https://reports.intentiq.com/report";
1✔
95
const REPORT_ENDPOINT_GDPR = "https://reports-gdpr.intentiq.com/report";
1✔
96
const REPORT_SERVER_ADDRESS = "https://test-reports.intentiq.com/report";
1✔
97

98
const randomVal = () => Math.floor(Math.random() * 100000) + 1;
34✔
99

100
const getDefaultConfig = () => {
1✔
101
  return {
73✔
102
    partner,
103
    manualWinReportEnabled: false,
104
  }
105
}
106

107
const getUserConfigWithReportingServerAddress = () => [
4✔
108
  {
109
    'name': 'intentIqId',
110
    'params': {
111
      'partner': partner,
112
      'unpack': null,
113
    },
114
    'storage': {
115
      'type': 'html5',
116
      'name': 'intentIqId',
117
      'expires': 60,
118
      'refreshInSeconds': 14400
119
    }
120
  }
121
];
122

123
const getWonRequest = () => ({
34✔
124
  bidderCode: "pubmatic",
125
  width: 728,
126
  height: 90,
127
  statusMessage: "Bid available",
128
  adId: "23caeb34c55da51",
129
  requestId: "87615b45ca4973",
130
  transactionId: "5e69fd76-8c86-496a-85ce-41ae55787a50",
131
  auctionId: "0cbd3a43-ff45-47b8-b002-16d3946b23bf-" + randomVal(),
132
  mediaType: "banner",
133
  source: "client",
134
  cpm: 5,
135
  currency: "USD",
136
  ttl: 300,
137
  referrer: "",
138
  adapterCode: "pubmatic",
139
  originalCpm: 5,
140
  originalCurrency: "USD",
141
  responseTimestamp: 1669644710345,
142
  requestTimestamp: 1669644710109,
143
  bidder: "testbidder",
144
  timeToRespond: 236,
145
  pbLg: "5.00",
146
  pbMg: "5.00",
147
  pbHg: "5.00",
148
  pbAg: "5.00",
149
  pbDg: "5.00",
150
  pbCg: "",
151
  size: "728x90",
152
  status: "rendered",
153
});
154

155
const enableAnalyticWithSpecialOptions = (receivedOptions) => {
1✔
156
  iiqAnalyticsAnalyticsAdapter.disableAnalytics();
26✔
157
  iiqAnalyticsAnalyticsAdapter.enableAnalytics({
26✔
158
    provider: "iiqAnalytics",
159
    options: {
160
      ...getDefaultConfig(),
161
      ...receivedOptions
162
    },
163
  });
164
};
165

166
describe("IntentIQ tests all", function () {
1✔
167
  let logErrorStub;
168
  let getWindowSelfStub;
169
  let getWindowTopStub;
170
  let getWindowLocationStub;
171
  let detectBrowserStub;
172

173
  beforeEach(function () {
1✔
174
    logErrorStub = sinon.stub(utils, "logError");
47✔
175
    sinon.stub(events, "getEvents").returns([]);
47✔
176

177
    if (config.getConfig && config.getConfig.restore) {
47!
UNCOV
178
      config.getConfig.restore();
×
179
    }
180

181
    iiqAnalyticsAnalyticsAdapter.initOptions = {
47✔
182
      lsValueInitialized: false,
183
      partner: null,
184
      fpid: null,
185
      userGroup: null,
186
      currentGroup: null,
187
      dataInLs: null,
188
      eidl: null,
189
      lsIdsInitialized: false,
190
      manualWinReportEnabled: false,
191
      domainName: null,
192
    };
193
    iiqAnalyticsAnalyticsAdapter.enableAnalytics({
47✔
194
      provider: "iiqAnalytics",
195
      options: getDefaultConfig()
196
    });
197
    if (iiqAnalyticsAnalyticsAdapter.track.restore) {
47!
UNCOV
198
      iiqAnalyticsAnalyticsAdapter.track.restore();
×
199
    }
200
    sinon.spy(iiqAnalyticsAnalyticsAdapter, "track");
47✔
201
    window[identityName] = utils.deepClone(defaultIdentityObject);
47✔
202
  });
203

204
  afterEach(function () {
1✔
205
    logErrorStub.restore();
47✔
206
    if (getConfigStub && getConfigStub.restore) getConfigStub.restore();
47✔
207
    if (getWindowSelfStub) getWindowSelfStub.restore();
47✔
208
    if (getWindowTopStub) getWindowTopStub.restore();
47✔
209
    if (getWindowLocationStub) getWindowLocationStub.restore();
47✔
210
    if (detectBrowserStub) detectBrowserStub.restore();
47✔
211
    events.getEvents.restore();
47✔
212
    iiqAnalyticsAnalyticsAdapter.disableAnalytics();
47✔
213
    if (iiqAnalyticsAnalyticsAdapter.track.restore) {
47✔
214
      iiqAnalyticsAnalyticsAdapter.track.restore();
47✔
215
    }
216
    localStorage.clear();
47✔
217
    server.reset();
47✔
218
    delete window[`iiq_identity_${partner}`]
47✔
219
  });
220

221
  it("should send POST request with payload in request body if reportMethod is POST", function () {
1✔
222
    enableAnalyticWithSpecialOptions({
1✔
223
      reportMethod: "POST",
224
    });
225
    const wonRequest = getWonRequest();
1✔
226

227
    events.emit(EVENTS.BID_WON, wonRequest);
1✔
228

229
    const request = server.requests[0];
1✔
230
    restoreReportList();
1✔
231

232
    const expectedData = preparePayload(wonRequest);
1✔
233
    const expectedPayload = `["${btoa(JSON.stringify(expectedData))}"]`;
1✔
234

235
    expect(request.method).to.equal("POST");
1✔
236
    expect(request.requestBody).to.equal(expectedPayload);
1✔
237
  });
238

239
  it("should send GET request with payload in query string if reportMethod is NOT provided", function () {
1✔
240
    const wonRequest = getWonRequest();
1✔
241

242
    events.emit(EVENTS.BID_WON, wonRequest);
1✔
243

244
    const request = server.requests[0];
1✔
245

246
    expect(request.method).to.equal("GET");
1✔
247

248
    const url = new URL(request.url);
1✔
249
    const payloadEncoded = url.searchParams.get("payload");
1✔
250
    const decoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
251

252
    restoreReportList();
1✔
253
    const expected = preparePayload(wonRequest);
1✔
254

255
    expect(decoded.partnerId).to.equal(expected.partnerId);
1✔
256
    expect(decoded.adType).to.equal(expected.adType);
1✔
257
    expect(decoded.prebidAuctionId).to.equal(expected.prebidAuctionId);
1✔
258
  });
259

260
  it("IIQ Analytical Adapter bid win report", function () {
1✔
261
    getWindowLocationStub = sinon
1✔
262
      .stub(utils, "getWindowLocation")
263
      .returns({ href: "http://localhost:9876" });
264
    const expectedVrref = getWindowLocationStub().href;
1✔
265
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
266

267
    expect(server.requests.length).to.be.above(0);
1✔
268
    const request = server.requests[0];
1✔
269
    const parsedUrl = new URL(request.url);
1✔
270
    const vrref = parsedUrl.searchParams.get("vrref");
1✔
271
    expect(request.url).to.contain(
1✔
272
      REPORT_ENDPOINT + "?pid=" + partner + "&mct=1"
273
    );
274
    expect(request.url).to.contain(`&jsver=${version}`);
1✔
275
    expect(`&vrref=${decodeURIComponent(vrref)}`).to.contain(
1✔
276
      `&vrref=${expectedVrref}`
277
    );
278
    expect(request.url).to.contain("&payload=");
1✔
279
    expect(request.url).to.contain(
1✔
280
      "iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344"
281
    );
282
  });
283

284
  it("should include adType in payload when present in BID_WON event", function () {
1✔
285
    getWindowLocationStub = sinon
1✔
286
      .stub(utils, "getWindowLocation")
287
      .returns({ href: "http://localhost:9876/" });
288
    const bidWonEvent = { ...getWonRequest(), mediaType: "video" };
1✔
289

290
    events.emit(EVENTS.BID_WON, bidWonEvent);
1✔
291

292
    const request = server.requests[0];
1✔
293
    const urlParams = new URL(request.url);
1✔
294
    const payloadEncoded = urlParams.searchParams.get("payload");
1✔
295
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
296

297
    expect(server.requests.length).to.be.above(0);
1✔
298
    expect(payloadDecoded).to.have.property("adType", bidWonEvent.mediaType);
1✔
299
  });
300

301
  it("should include adType in payload when present in reportExternalWin event", function () {
1✔
302
    enableAnalyticWithSpecialOptions({ manualWinReportEnabled: true });
1✔
303
    getWindowLocationStub = sinon
1✔
304
      .stub(utils, "getWindowLocation")
305
      .returns({ href: "http://localhost:9876/" });
306
    const externalWinEvent = { cpm: 1, currency: "USD", adType: "banner" };
1✔
307

308
    events.emit(EVENTS.BID_REQUESTED);
1✔
309

310
    window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin(
1✔
311
      externalWinEvent
312
    );
313

314
    const request = server.requests[0];
1✔
315
    const urlParams = new URL(request.url);
1✔
316
    const payloadEncoded = urlParams.searchParams.get("payload");
1✔
317
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
318

319
    expect(server.requests.length).to.be.above(0);
1✔
320
    expect(payloadDecoded).to.have.property("adType", externalWinEvent.adType);
1✔
321
  });
322

323
  it("should get pos from pbjs.adUnits when BID_WON has no pos", function () {
1✔
324
    const pbjs = getGlobal();
1✔
325
    const prevAdUnits = pbjs.adUnits;
1✔
326

327
    pbjs.adUnits = Array.isArray(pbjs.adUnits) ? pbjs.adUnits : [];
1!
328
    pbjs.adUnits.push({ code: "myVideoAdUnit", mediaTypes: { video: { pos: 777 } } });
1✔
329

330
    enableAnalyticWithSpecialOptions({ manualWinReportEnabled: false });
1✔
331

332
    events.emit(EVENTS.BID_WON, {
1✔
333
      ...getWonRequest(),
334
      adUnitCode: "myVideoAdUnit",
335
      mediaType: "video"
336
    });
337

338
    const request = server.requests[0];
1✔
339
    const payloadEncoded = new URL(request.url).searchParams.get("payload");
1✔
340
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
341

342
    expect(payloadDecoded.pos).to.equal(777);
1✔
343

344
    pbjs.adUnits = prevAdUnits;
1✔
345
  });
346

347
  it("should get pos from reportExternalWin when present", function () {
1✔
348
    enableAnalyticWithSpecialOptions({ manualWinReportEnabled: true });
1✔
349

350
    const winPos = 999;
1✔
351

352
    window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({
1✔
353
      adUnitCode: "myVideoAdUnit",
354
      bidderCode: "appnexus",
355
      cpm: 1.5,
356
      currency: "USD",
357
      mediaType: "video",
358
      size: "300x250",
359
      status: "rendered",
360
      auctionId: "auc123",
361
      pos: winPos
362
    });
363

364
    const request = server.requests[0];
1✔
365
    const payloadEncoded = new URL(request.url).searchParams.get("payload");
1✔
366
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
367

368
    expect(payloadDecoded.pos).to.equal(winPos);
1✔
369
  });
370

371
  it("should initialize with default configurations", function () {
1✔
372
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized).to.be
1✔
373
      .false;
374
  });
375

376
  it("should handle BID_WON event with group configuration from local storage", function () {
1✔
377
    window[`iiq_identity_${partner}`].firstPartyData = {
1✔
378
      ...window[`iiq_identity_${partner}`].firstPartyData,
379
      group: "B",
380
    };
381

382
    const expectedVrref = encodeURIComponent("http://localhost:9876/");
1✔
383

384
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
385

386
    expect(server.requests.length).to.be.above(0);
1✔
387
    const request = server.requests[0];
1✔
388
    expect(request.url).to.contain(
1✔
389
      "https://reports.intentiq.com/report?pid=" + partner + "&mct=1"
390
    );
391
    expect(request.url).to.contain(`&jsver=${version}`);
1✔
392
    expect(request.url).to.contain(`&vrref=${expectedVrref}`);
1✔
393
  });
394

395
  it("should handle BID_WON event with default group configuration", function () {
1✔
396
    const spdData = "server provided data";
1✔
397
    const expectedSpdEncoded = encodeURIComponent(spdData);
1✔
398
    window[identityName].partnerData.spd = spdData;
1✔
399
    const wonRequest = getWonRequest();
1✔
400

401
    events.emit(EVENTS.BID_WON, wonRequest);
1✔
402

403
    expect(server.requests.length).to.be.above(0);
1✔
404
    const request = server.requests[0];
1✔
405
    restoreReportList();
1✔
406
    const dataToSend = preparePayload(wonRequest);
1✔
407
    const base64String = btoa(JSON.stringify(dataToSend));
1✔
408
    const payload = encodeURIComponent(JSON.stringify([base64String]));
1✔
409
    const expectedUrl = appendVrrefAndFui(
1✔
410
      REPORT_ENDPOINT +
411
      `?pid=${partner}&mct=1&iiqid=${defaultIdentityObject.firstPartyData.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&uh=${encodeURIComponent(window[identityName].clientHints)}&gdpr=0&spd=${expectedSpdEncoded}`,
412
      iiqAnalyticsAnalyticsAdapter.initOptions.domainName
413
    );
414
    const urlWithPayload = expectedUrl + `&payload=${payload}`;
1✔
415

416
    expect(request.url).to.equal(urlWithPayload);
1✔
417
    expect(dataToSend.pcid).to.equal(defaultIdentityObject.firstPartyData.pcid);
1✔
418
  });
419

420
  it("should send CMP data in report if available", function () {
1✔
421
    const uspData = "1NYN";
1✔
422
    const gppData = { gppString: '{"key1":"value1","key2":"value2"}' };
1✔
423
    const gdprData = { consentString: "gdprConsent" };
1✔
424

425
    const gppStub = sinon
1✔
426
      .stub(gppDataHandler, "getConsentData")
427
      .returns(gppData);
428
    const uspStub = sinon
1✔
429
      .stub(uspDataHandler, "getConsentData")
430
      .returns(uspData);
431
    const gdprStub = sinon
1✔
432
      .stub(gdprDataHandler, "getConsentData")
433
      .returns(gdprData);
434

435
    getWindowLocationStub = sinon
1✔
436
      .stub(utils, "getWindowLocation")
437
      .returns({ href: "http://localhost:9876/" });
438

439
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
440

441
    expect(server.requests.length).to.be.above(0);
1✔
442
    const request = server.requests[0];
1✔
443

444
    expect(request.url).to.contain(
1✔
445
      `&us_privacy=${encodeURIComponent(uspData)}`
446
    );
447
    expect(request.url).to.contain(
1✔
448
      `&gpp=${encodeURIComponent(gppData.gppString)}`
449
    );
450
    expect(request.url).to.contain(
1✔
451
      `&gdpr_consent=${encodeURIComponent(gdprData.consentString)}`
452
    );
453
    expect(request.url).to.contain(`&gdpr=1`);
1✔
454
    gppStub.restore();
1✔
455
    uspStub.restore();
1✔
456
    gdprStub.restore();
1✔
457
  });
458

459
  regionCases.forEach(({ name, region, expectedEndpoint }) => {
4✔
460
    it(`should send request to region-specific report endpoint when region is "${name}"`, function () {
4✔
461
      userIdConfigForTest = getUserConfigWithReportingServerAddress();
4✔
462
      getConfigStub = sinon.stub(config, "getConfig");
4✔
463
      getConfigStub.withArgs("userSync.userIds").callsFake(() => userIdConfigForTest);
4✔
464

465
      enableAnalyticWithSpecialOptions({ region });
4✔
466

467
      events.emit(EVENTS.BID_WON, getWonRequest());
4✔
468

469
      expect(server.requests.length).to.be.above(0);
4✔
470
      const request = server.requests[0];
4✔
471
      expect(request.url).to.contain(expectedEndpoint);
4✔
472
    });
473
  });
474

475
  it("should not send request if manualWinReportEnabled is true", function () {
1✔
476
    iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = true;
1✔
477
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
478
    expect(server.requests.length).to.equal(0);
1✔
479
  });
480

481
  it("should handle initialization values from local storage", function () {
1✔
482
    window[`iiq_identity_${partner}`].actualABGroup = WITHOUT_IIQ;
1✔
483

484
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
485
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup).to.equal(
1✔
486
      WITHOUT_IIQ
487
    );
488
    expect(iiqAnalyticsAnalyticsAdapter.initOptions.fpid).to.be.not.null;
1✔
489
  });
490

491
  it("should handle reportExternalWin", function () {
1✔
492
    events.emit(EVENTS.BID_REQUESTED);
1✔
493
    iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = false;
1✔
494
    expect(
1✔
495
      window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin
496
    ).to.be.a("function");
497
    expect(
1✔
498
      window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({
499
        cpm: 1,
500
        currency: "USD",
501
      })
502
    ).to.equal(false);
503
  });
504

505
  it("should return window.location.href when window.self === window.top", function () {
1✔
506
    // Stub helper functions
507
    getWindowSelfStub = sinon.stub(utils, "getWindowSelf").returns(window);
1✔
508
    getWindowTopStub = sinon.stub(utils, "getWindowTop").returns(window);
1✔
509
    getWindowLocationStub = sinon
1✔
510
      .stub(utils, "getWindowLocation")
511
      .returns({ href: "http://localhost:9876/" });
512

513
    const referrer = getCurrentUrl();
1✔
514
    expect(referrer).to.equal("http://localhost:9876/");
1✔
515
  });
516

517
  it("should return window.top.location.href when window.self !== window.top and access is successful", function () {
1✔
518
    // Stub helper functions to simulate iframe
519
    getWindowSelfStub = sinon.stub(utils, "getWindowSelf").returns({});
1✔
520
    getWindowTopStub = sinon
1✔
521
      .stub(utils, "getWindowTop")
522
      .returns({ location: { href: "http://example.com/" } });
523

524
    const referrer = getCurrentUrl();
1✔
525

526
    expect(referrer).to.equal("http://example.com/");
1✔
527
  });
528

529
  it("should return an empty string and log an error when accessing window.top.location.href throws an error", function () {
1✔
530
    // Stub helper functions to simulate error
531
    getWindowSelfStub = sinon.stub(utils, "getWindowSelf").returns({});
1✔
532
    getWindowTopStub = sinon
1✔
533
      .stub(utils, "getWindowTop")
534
      .throws(new Error("Access denied"));
535

536
    const referrer = getCurrentUrl();
1✔
537
    expect(referrer).to.equal("");
1✔
538
    expect(logErrorStub.calledOnce).to.be.true;
1✔
539
    expect(logErrorStub.firstCall.args[0]).to.contain(
1✔
540
      "Error accessing location: Error: Access denied"
541
    );
542
  });
543

544
  it("should not send request if the browser is in blacklist (chrome)", function () {
1✔
545
    enableAnalyticWithSpecialOptions({
1✔
546
      browserBlackList: "ChrOmE"
547
    })
548
    detectBrowserStub = sinon
1✔
549
      .stub(detectBrowserUtils, "detectBrowser")
550
      .returns("chrome");
551

552
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
553

554
    expect(server.requests.length).to.equal(0);
1✔
555
  });
556

557
  it("should send request if the browser is not in blacklist (safari)", function () {
1✔
558
    enableAnalyticWithSpecialOptions({
1✔
559
      browserBlackList: "chrome,firefox"
560
    })
561

562
    detectBrowserStub = sinon
1✔
563
      .stub(detectBrowserUtils, "detectBrowser")
564
      .returns("safari");
565

566
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
567

568
    expect(server.requests.length).to.be.above(0);
1✔
569
    const request = server.requests[0];
1✔
570
    expect(request.url).to.contain(
1✔
571
      `https://reports.intentiq.com/report?pid=${partner}&mct=1`
572
    );
573
    expect(request.url).to.contain(`&jsver=${version}`);
1✔
574
    expect(request.url).to.contain(
1✔
575
      `&vrref=${encodeURIComponent("http://localhost:9876/")}`
576
    );
577
    expect(request.url).to.contain("&payload=");
1✔
578
    expect(request.url).to.contain(
1✔
579
      "iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344"
580
    );
581
  });
582

583
  it("should send request in reportingServerAddress no gdpr", function () {
1✔
584
    detectBrowserStub = sinon
1✔
585
      .stub(detectBrowserUtils, "detectBrowser")
586
      .returns("safari");
587
    enableAnalyticWithSpecialOptions({
1✔
588
      reportingServerAddress: REPORT_SERVER_ADDRESS,
589
      browserBlackList: "chrome,firefox"
590
    });
591

592
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
593

594
    expect(server.requests.length).to.be.above(0);
1✔
595
    const request = server.requests[0];
1✔
596
    expect(request.url).to.contain(REPORT_SERVER_ADDRESS);
1✔
597
  });
598

599
  it("should include source parameter in report URL", function () {
1✔
600
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
601
    const request = server.requests[0];
1✔
602

603
    expect(server.requests.length).to.be.above(0);
1✔
604
    expect(request.url).to.include(`&source=${PREBID}`);
1✔
605
  });
606

607
  it("should send additionalParams in report if valid and small enough", function () {
1✔
608
    enableAnalyticWithSpecialOptions({
1✔
609
      additionalParams: [
610
        {
611
          parameterName: "general",
612
          parameterValue: "Lee",
613
          destination: [0, 0, 1],
614
        },
615
      ]
616
    })
617

618
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
619

620
    const request = server.requests[0];
1✔
621
    expect(request.url).to.include("general=Lee");
1✔
622
  });
623

624
  it("should include domainName in both query and payload when fullUrl is empty (cross-origin)", function () {
1✔
625
    const domainName = "mydomain-frame.com";
1✔
626

627
    enableAnalyticWithSpecialOptions({ domainName });
1✔
628

629
    getWindowTopStub = sinon.stub(utils, "getWindowTop").throws(new Error("cross-origin"));
1✔
630

631
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
632

633
    const request = server.requests[0];
1✔
634

635
    // Query contain vrref=domainName
636
    const parsedUrl = new URL(request.url);
1✔
637
    const vrrefParam = parsedUrl.searchParams.get("vrref");
1✔
638

639
    // Payload contain vrref=domainName
640
    const payloadEncoded = parsedUrl.searchParams.get("payload");
1✔
641
    const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0]));
1✔
642

643
    expect(server.requests.length).to.be.above(0);
1✔
644
    expect(vrrefParam).to.not.equal(null);
1✔
645
    expect(decodeURIComponent(vrrefParam)).to.equal(domainName);
1✔
646
    expect(parsedUrl.searchParams.get("fui")).to.equal("1");
1✔
647

648
    expect(payloadDecoded).to.have.property("vrref");
1✔
649
    expect(decodeURIComponent(payloadDecoded.vrref)).to.equal(domainName);
1✔
650

651
    restoreReportList();
1✔
652
  });
653

654
  it("should not send additionalParams in report if value is too large", function () {
1✔
655
    const longVal = "x".repeat(5000000);
1✔
656

657
    enableAnalyticWithSpecialOptions({
1✔
658
      additionalParams: [
659
        {
660
          parameterName: "general",
661
          parameterValue: longVal,
662
          destination: [0, 0, 1],
663
        },
664
      ]
665
    })
666

667
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
668

669
    const request = server.requests[0];
1✔
670
    expect(request.url).not.to.include("general");
1✔
671
  });
672

673
  it("should include spd parameter from LS in report URL", function () {
1✔
674
    const spdObject = { foo: "bar", value: 42 };
1✔
675
    const expectedSpdEncoded = encodeURIComponent(JSON.stringify(spdObject));
1✔
676
    window[identityName].firstPartyData.spd =
1✔
677
      JSON.stringify(spdObject);
678
    window[identityName].partnerData.spd = spdObject;
1✔
679

680
    getWindowLocationStub = sinon
1✔
681
      .stub(utils, "getWindowLocation")
682
      .returns({ href: "http://localhost:9876/" });
683

684
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
685

686
    const request = server.requests[0];
1✔
687

688
    expect(server.requests.length).to.be.above(0);
1✔
689
    expect(request.url).to.include(`&spd=${expectedSpdEncoded}`);
1✔
690
  });
691

692
  it("should include spd parameter string from LS in report URL", function () {
1✔
693
    const spdData = "server provided data";
1✔
694
    const expectedSpdEncoded = encodeURIComponent(spdData);
1✔
695
    window[identityName].partnerData.spd = spdData;
1✔
696

697
    getWindowLocationStub = sinon
1✔
698
      .stub(utils, "getWindowLocation")
699
      .returns({ href: "http://localhost:9876/" });
700

701
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
702

703
    const request = server.requests[0];
1✔
704

705
    expect(server.requests.length).to.be.above(0);
1✔
706
    expect(request.url).to.include(`&spd=${expectedSpdEncoded}`);
1✔
707
  });
708

709
  describe("GAM prediction reporting", function () {
1✔
710
    function createMockGAM() {
711
      const listeners = {};
2✔
712
      return {
2✔
713
        cmd: [],
714
        pubads: () => ({
1✔
715
          addEventListener: (name, cb) => {
716
            listeners[name] = cb;
1✔
717
          },
718
        }),
719
        _listeners: listeners,
720
      };
721
    }
722

723
    it("should subscribe to GAM and send report on slotRenderEnded without prior bidWon", function () {
1✔
724
      const gam = createMockGAM();
1✔
725

726
      enableAnalyticWithSpecialOptions({
1✔
727
        gamObjectReference: gam
728
      })
729

730
      // enable subscription by LS flag
731
      window[`iiq_identity_${partner}`].partnerData.gpr = true;
1✔
732

733
      // provide recent auctionEnd with matching bid to enrich payload
734
      events.getEvents.restore();
1✔
735
      sinon.stub(events, "getEvents").returns([
1✔
736
        {
737
          eventType: "auctionEnd",
738
          args: {
739
            auctionId: "auc-1",
740
            adUnitCodes: ["ad-unit-1"],
741
            bidsReceived: [
742
              {
743
                bidder: "pubmatic",
744
                adUnitCode: "ad-unit-1",
745
                cpm: 1,
746
                currency: "USD",
747
                originalCpm: 1,
748
                originalCurrency: "USD",
749
                status: "rendered",
750
              },
751
            ],
752
          },
753
        },
754
      ]);
755

756
      // trigger adapter to subscribe
757
      events.emit(EVENTS.BID_REQUESTED);
1✔
758

759
      // execute GAM cmd to register listener
760
      gam.cmd.forEach((fn) => fn());
1✔
761

762
      // simulate slotRenderEnded
763
      const slot = {
1✔
764
        getSlotElementId: () => "ad-unit-1",
1✔
765
        getAdUnitPath: () => "/123/foo",
1✔
766
        getTargetingKeys: () => ["hb_bidder", "hb_adid"],
1✔
767
        getTargeting: (k) =>
768
          k === "hb_bidder" ? ["pubmatic"] : k === "hb_adid" ? ["ad123"] : [],
2!
769
      };
770
      if (gam._listeners["slotRenderEnded"]) {
1✔
771
        gam._listeners["slotRenderEnded"]({ isEmpty: false, slot });
1✔
772
      }
773

774
      expect(server.requests.length).to.be.above(0);
1✔
775
    });
776

777
    it("should NOT send report if a matching bidWon already exists", function () {
1✔
778
      const gam = createMockGAM();
1✔
779

780
      localStorage.setItem(
1✔
781
        FIRST_PARTY_KEY + "_" + partner,
782
        JSON.stringify({ gpr: true })
783
      );
784

785
      // provide prior bidWon matching placementId and hb_adid
786
      events.getEvents.restore();
1✔
787
      sinon
1✔
788
        .stub(events, "getEvents")
789
        .returns([
790
          { eventType: "bidWon", args: { adId: "ad123" }, id: "ad-unit-1" },
791
        ]);
792

793
      events.emit(EVENTS.BID_REQUESTED);
1✔
794
      gam.cmd.forEach((fn) => fn());
1✔
795

796
      const slot = {
1✔
UNCOV
797
        getSlotElementId: () => "ad-unit-1",
×
UNCOV
798
        getAdUnitPath: () => "/123/foo",
×
UNCOV
799
        getTargetingKeys: () => ["hb_bidder", "hb_adid"],
×
800
        getTargeting: (k) =>
UNCOV
801
          k === "hb_bidder" ? ["pubmatic"] : k === "hb_adid" ? ["ad123"] : [],
×
802
      };
803

804
      const initialRequests = server.requests.length;
1✔
805
      if (gam._listeners["slotRenderEnded"]) {
1!
UNCOV
806
        gam._listeners["slotRenderEnded"]({ isEmpty: false, slot });
×
807
      }
808
      expect(server.requests.length).to.equal(initialRequests);
1✔
809
    });
810
  });
811

812
  const testCasesVrref = [
1✔
813
    {
814
      description: "domainName matches window.top.location.href",
815
      getWindowSelf: {},
816
      getWindowTop: { location: { href: "http://example.com/page" } },
817
      getWindowLocation: { href: "http://example.com/page" },
818
      domainName: "example.com",
819
      expectedVrref: encodeURIComponent("http://example.com/page"),
820
      shouldContainFui: false,
821
    },
822
    {
823
      description: "domainName does not match window.top.location.href",
824
      getWindowSelf: {},
825
      getWindowTop: { location: { href: "http://anotherdomain.com/page" } },
826
      getWindowLocation: { href: "http://anotherdomain.com/page" },
827
      domainName: "example.com",
828
      expectedVrref: encodeURIComponent("example.com"),
829
      shouldContainFui: false,
830
    },
831
    {
832
      description: "domainName is missing, only fui=1 is returned",
833
      getWindowSelf: {},
834
      getWindowTop: { location: { href: "" } },
835
      getWindowLocation: { href: "" },
836
      domainName: null,
837
      expectedVrref: "",
838
      shouldContainFui: true,
839
    },
840
    {
841
      description: "domainName is missing",
842
      getWindowSelf: {},
843
      getWindowTop: { location: { href: "http://example.com/page" } },
844
      getWindowLocation: { href: "http://example.com/page" },
845
      domainName: null,
846
      expectedVrref: encodeURIComponent("http://example.com/page"),
847
      shouldContainFui: false,
848
    },
849
  ];
850

851
  testCasesVrref.forEach(
1✔
852
    ({
853
      description,
854
      getWindowSelf,
855
      getWindowTop,
856
      getWindowLocation,
857
      domainName,
858
      expectedVrref,
859
      shouldContainFui,
860
    }) => {
4✔
861
      it(`should append correct vrref when ${description}`, function () {
4✔
862
        getWindowSelfStub = sinon
4✔
863
          .stub(utils, "getWindowSelf")
864
          .returns(getWindowSelf);
865
        getWindowTopStub = sinon
4✔
866
          .stub(utils, "getWindowTop")
867
          .returns(getWindowTop);
868
        getWindowLocationStub = sinon
4✔
869
          .stub(utils, "getWindowLocation")
870
          .returns(getWindowLocation);
871

872
        const url = "https://reports.intentiq.com/report?pid=10";
4✔
873
        const modifiedUrl = appendVrrefAndFui(url, domainName);
4✔
874
        const urlObj = new URL(modifiedUrl);
4✔
875

876
        const vrref = encodeURIComponent(
4✔
877
          urlObj.searchParams.get("vrref") || ""
5✔
878
        );
879
        const fui = urlObj.searchParams.get("fui");
4✔
880

881
        expect(vrref).to.equal(expectedVrref);
4✔
882
        expect(urlObj.searchParams.has("fui")).to.equal(shouldContainFui);
4✔
883
        if (shouldContainFui) {
4✔
884
          expect(fui).to.equal("1");
1✔
885
        }
886
      });
887
    }
888
  );
889

890
  const adUnitConfigTests = [
1✔
891
    {
892
      adUnitConfig: 1,
893
      description: "should extract adUnitCode first (adUnitConfig = 1)",
894
      event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" },
895
      expectedPlacementId: "adUnitCode-123",
896
    },
897
    {
898
      adUnitConfig: 1,
899
      description:
900
        "should extract placementId if there is no adUnitCode (adUnitConfig = 1)",
901
      event: { placementId: "placementId-456" },
902
      expectedPlacementId: "placementId-456",
903
    },
904
    {
905
      adUnitConfig: 2,
906
      description: "should extract placementId first (adUnitConfig = 2)",
907
      event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" },
908
      expectedPlacementId: "placementId-456",
909
    },
910
    {
911
      adUnitConfig: 2,
912
      description:
913
        "should extract adUnitCode if there is no placementId (adUnitConfig = 2)",
914
      event: { adUnitCode: "adUnitCode-123" },
915
      expectedPlacementId: "adUnitCode-123",
916
    },
917
    {
918
      adUnitConfig: 3,
919
      description: "should extract only adUnitCode (adUnitConfig = 3)",
920
      event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" },
921
      expectedPlacementId: "adUnitCode-123",
922
    },
923
    {
924
      adUnitConfig: 4,
925
      description: "should extract only placementId (adUnitConfig = 4)",
926
      event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" },
927
      expectedPlacementId: "placementId-456",
928
    },
929
    {
930
      adUnitConfig: 1,
931
      description:
932
        "should return empty placementId if neither adUnitCode or placementId exist",
933
      event: {},
934
      expectedPlacementId: "",
935
    },
936
    {
937
      adUnitConfig: 1,
938
      description:
939
        "should extract placementId from params array if no top-level adUnitCode or placementId exist (adUnitConfig = 1)",
940
      event: {
941
        params: [{ someKey: "value" }, { placementId: "nested-placementId" }],
942
      },
943
      expectedPlacementId: "nested-placementId",
944
    },
945
  ];
946

947
  adUnitConfigTests.forEach(
1✔
948
    ({ adUnitConfig, description, event, expectedPlacementId }) => {
8✔
949
      it(description, function () {
8✔
950
        enableAnalyticWithSpecialOptions({ adUnitConfig });
8✔
951

952
        const testEvent = { ...getWonRequest(), ...event };
8✔
953
        events.emit(EVENTS.BID_WON, testEvent);
8✔
954

955
        const request = server.requests[0];
8✔
956
        const urlParams = new URL(request.url);
8✔
957
        const encodedPayload = urlParams.searchParams.get("payload");
8✔
958
        const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0]));
8✔
959

960
        expect(server.requests.length).to.be.above(0);
8✔
961
        expect(encodedPayload).to.exist;
8✔
962
        expect(decodedPayload).to.have.property(
8✔
963
          "placementId",
964
          expectedPlacementId
965
        );
966
      });
967
    }
968
  );
969

970
  it("should include ABTestingConfigurationSource in payload when provided", function () {
1✔
971
    const ABTestingConfigurationSource = "percentage";
1✔
972
    enableAnalyticWithSpecialOptions({ ABTestingConfigurationSource });
1✔
973

974
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
975

976
    const request = server.requests[0];
1✔
977
    const urlParams = new URL(request.url);
1✔
978
    const encodedPayload = urlParams.searchParams.get("payload");
1✔
979
    const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0]));
1✔
980

981
    expect(server.requests.length).to.be.above(0);
1✔
982
    expect(decodedPayload).to.have.property(
1✔
983
      "ABTestingConfigurationSource",
984
      ABTestingConfigurationSource
985
    );
986
  });
987

988
  it("should not include ABTestingConfigurationSource in payload when not provided", function () {
1✔
989
    enableAnalyticWithSpecialOptions({});
1✔
990

991
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
992

993
    const request = server.requests[0];
1✔
994
    const urlParams = new URL(request.url);
1✔
995
    const encodedPayload = urlParams.searchParams.get("payload");
1✔
996
    const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0]));
1✔
997

998
    expect(server.requests.length).to.be.above(0);
1✔
999
    expect(decodedPayload).to.not.have.property("ABTestingConfigurationSource");
1✔
1000
  });
1001

1002
  it("should use group from provided options when ABTestingConfigurationSource is 'group'", function () {
1✔
1003
    const providedGroup = WITHOUT_IIQ;
1✔
1004
    // Ensure actualABGroup is not set so group from options is used
1005
    delete window[`iiq_identity_${partner}`].actualABGroup;
1✔
1006

1007
    enableAnalyticWithSpecialOptions({
1✔
1008
      group: providedGroup,
1009
      ABTestingConfigurationSource: AB_CONFIG_SOURCE.GROUP,
1010
    });
1011

1012
    events.emit(EVENTS.BID_WON, getWonRequest());
1✔
1013

1014
    const request = server.requests[0];
1✔
1015
    const urlParams = new URL(request.url);
1✔
1016
    const encodedPayload = urlParams.searchParams.get("payload");
1✔
1017
    const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0]));
1✔
1018

1019
    expect(server.requests.length).to.be.above(0);
1✔
1020
    // Verify that the group from options is used in the payload
1021
    expect(decodedPayload).to.have.property("abGroup", providedGroup);
1✔
1022
  });
1023
});
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