From 74c46230c904d0090ad69126fb1f9e2d6611f8d4 Mon Sep 17 00:00:00 2001 From: Eyvaz Date: Thu, 27 Aug 2026 19:00:07 +0200 Subject: [PATCH 1/2] remove gam related code and tests --- .../intentIqUtils/gamPredictionReport.ts | 121 ----------- modules/intentIqAnalyticsAdapter.md | 2 - modules/intentIqAnalyticsAdapter.ts | 59 +----- modules/intentIqIdSystem.md | 4 - modules/intentIqIdSystem.ts | 41 +--- .../libraries/gamPredictionReport_spec.js | 86 -------- .../modules/intentIqAnalyticsAdapter_spec.js | 110 ---------- test/spec/modules/intentIqIdSystem_spec.js | 200 +----------------- 8 files changed, 5 insertions(+), 618 deletions(-) delete mode 100644 libraries/intentIqUtils/gamPredictionReport.ts delete mode 100644 test/spec/libraries/gamPredictionReport_spec.js diff --git a/libraries/intentIqUtils/gamPredictionReport.ts b/libraries/intentIqUtils/gamPredictionReport.ts deleted file mode 100644 index 9e631635e56..00000000000 --- a/libraries/intentIqUtils/gamPredictionReport.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { getEvents } from '../../src/events.js'; -import { logError } from '../../src/utils.js'; -import { getSlotTargetingMap } from '../../src/utils/gptTargeting.js'; - -export function gamPredictionReport( - gamObjectReference: any, - sendData: (data: Record) => void -): void { - try { - if (!gamObjectReference || !sendData) { - logError('Failed to get gamPredictionReport, required data is missed'); - return; - } - - const getSlotTargeting = (slot: any): Record => { - try { - return getSlotTargetingMap(slot); - } catch (e) { - logError('Failed to get slot targeting: ' + e); - return {}; - } - }; - - const extractWinData = (gamEvent: any): Record | undefined => { - const slot = gamEvent.slot; - const targeting = getSlotTargeting(slot); - - const dataToSend: Record = { - placementId: slot.getSlotElementId && slot.getSlotElementId(), - adUnitPath: slot.getAdUnitPath && slot.getAdUnitPath(), - bidderCode: targeting.hb_bidder ? targeting.hb_bidder[0] : null, - biddingPlatformId: 5 - }; - - if (dataToSend.placementId) { - // TODO check auto subscription to prebid events - const bidWonEvents = getEvents().filter((ev: any) => ev.eventType === 'bidWon'); - if (bidWonEvents.length) { - for (let i = bidWonEvents.length - 1; i >= 0; i--) { - const element = bidWonEvents[i]; - if ( - dataToSend.placementId === element.id && - targeting.hb_adid && - targeting.hb_adid[0] === (element.args as any).adId - ) { - return; - } - } - } - - const endEvents = getEvents().filter((ev: any) => ev.eventType === 'auctionEnd'); - - if (endEvents.length) { - for (let i = endEvents.length - 1; i >= 0; i--) { - const element = endEvents[i]; - - if ((element.args as any)?.adUnitCodes?.includes(dataToSend.placementId)) { - const defineRelevantData = (bid: any): void => { - dataToSend.cpm = bid.cpm + 0.01; - dataToSend.currency = bid.currency; - dataToSend.originalCpm = bid.originalCpm; - dataToSend.originalCurrency = bid.originalCurrency; - dataToSend.status = bid.status; - dataToSend.prebidAuctionId = (element.args as any)?.auctionId; - - if (!dataToSend.bidderCode) { - dataToSend.bidderCode = 'GAM'; - } - }; - - if (dataToSend.bidderCode) { - const relevantBid = (element.args as any)?.bidsReceived.find( - (item: any) => - item.bidder === dataToSend.bidderCode && - item.adUnitCode === dataToSend.placementId - ); - - if (relevantBid) { - defineRelevantData(relevantBid); - break; - } - } else { - let highestBid = 0; - - (element.args as any)?.bidsReceived.forEach((bid: any) => { - if ( - bid.adUnitCode === dataToSend.placementId && - bid.cpm > highestBid - ) { - highestBid = bid.cpm; - defineRelevantData(bid); - } - }); - - break; - } - } - } - } - } - - return dataToSend; - }; - - gamObjectReference.cmd.push(() => { - gamObjectReference.pubads().addEventListener( - 'slotRenderEnded', - (event: any) => { - if (event.isEmpty) return; - - const data = extractWinData(event); - if (data) { - sendData(data); - } - } - ); - }); - } catch (error) { - logError('Failed to subscribe to GAM: ' + error); - } -} diff --git a/modules/intentIqAnalyticsAdapter.md b/modules/intentIqAnalyticsAdapter.md index 52434dabb43..ddd64efc340 100644 --- a/modules/intentIqAnalyticsAdapter.md +++ b/modules/intentIqAnalyticsAdapter.md @@ -26,11 +26,9 @@ No registration for this module is required. | options.reportMethod | Optional | String | Defines the HTTP method used to send the analytics report. If set to `"POST"`, the report payload will be sent in the body of the request. If set to `"GET"` (default), the payload will be included as a query parameter in the request URL. | `"GET"` | | options.reportingServerAddress | Optional | String | The base URL for the IntentIQ reporting server. If parameter is provided in `configParams`, it will be used. | `"https://domain.com"` | | options.adUnitConfig | Optional | Number | Determines how the `placementId` parameter is extracted in the report (default is 1). Possible values: 1 – adUnitCode first, 2 – placementId first, 3 – only adUnitCode, 4 – only placementId. | `1` | -| options.gamPredictReporting | Optional | Boolean | This variable controls whether the GAM prediction logic is enabled or disabled. The main purpose of this logic is to extract information from a rendered GAM slot when no Prebid bidWon event is available. In that case, we take the highest CPM from the current auction and add 0.01 to that value. | `false` | | options.ABTestingConfigurationSource | Optional | String | Determines how AB group will be defined. Possible values: `"IIQServer"` – group defined by IIQ server, `"percentage"` – generated group based on abPercentage, `"group"` – define group based on value provided by partner. | `IIQServer` | | options.abPercentage | Optional | Number | Percentage for A/B testing group. Default value is `95` | `95` | | options.group | Optional | String | Define group provided by partner, possible values: `"A"`, `"B"` | `"A"` | -| options.gamObjectReference | Optional | Object | This is a reference to the Google Ad Manager (GAM) object, which will be used to set targeting. If this parameter is not provided, the group reporting will not be configured. | `googletag` | | options.browserBlackList | Optional | String | This is the name of a browser that can be added to a blacklist. | `"chrome"` | | options.domainName | Optional | String | Specifies the domain of the page in which the IntentIQ object is currently running and serving the impression. This domain will be used later in the revenue reporting breakdown by domain. For example, cnn.com. It identifies the primary source of requests to the IntentIQ servers, even within nested web pages. | `"currentDomain.com"` | | options. additionalParams | Optional | Array | This parameter allows sending additional custom key-value parameters with specific destination logic (sync, VR, winreport). Each custom parameter is defined as an object in the array. | `[ { parameterName: “abc”, parameterValue: 123, destination: [1,1,0] } ]` | diff --git a/modules/intentIqAnalyticsAdapter.ts b/modules/intentIqAnalyticsAdapter.ts index aab97c97a41..794b5516831 100644 --- a/modules/intentIqAnalyticsAdapter.ts +++ b/modules/intentIqAnalyticsAdapter.ts @@ -1,4 +1,4 @@ -import { isPlainObject, logError, logInfo } from '../src/utils.js'; +import { logError, logInfo } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { ajax } from '../src/ajax.js'; @@ -15,7 +15,6 @@ import { } from '../libraries/intentIqConstants/intentIqConstants.ts'; import { reportingServerAddress } from '../libraries/intentIqUtils/intentIqConfig.ts'; import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.ts'; -import { gamPredictionReport } from '../libraries/intentIqUtils/gamPredictionReport.ts'; import { defineABTestingGroup, IntentIqABConfigSource } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; import { getGlobal } from '../src/prebidGlobal.js'; @@ -97,11 +96,6 @@ export interface IntentIqAnalyticsAdapterOptions { */ manualWinReportEnabled?: boolean; - /** - * Enable GAM predict-score reporting. Defaults to `false`. - */ - gamPredictReporting?: boolean; - /** * HTTP method used to send reports. Defaults to `'GET'`. */ @@ -163,11 +157,6 @@ export interface IntentIqAnalyticsAdapterOptions { */ siloEnabled?: boolean; - /** - * Reference to the GAM `googletag.pubads()` object for predict-score - * reporting. - */ - gamObjectReference?: Record; } const MODULE_NAME = 'iiqAnalytics' as const; @@ -177,9 +166,6 @@ const pbjs: any = getGlobal(); export const REPORTER_ID = Date.now() + '_' + getRandom(0, 1000); let globalName: string | undefined; let identityGlobalName: string | undefined; -let alreadySubscribedOnGAM = false; -let reportList: Record> = {}; -let cleanReportsID: ReturnType | undefined; let iiqConfig: any; const PARAMS_NAMES: Record = { @@ -259,10 +245,6 @@ const iiqAnalyticsAnalyticsAdapter: any = Object.assign(adapter({ url: DEFAULT_U bidWon(args); break; case BID_REQUESTED: { - if (!alreadySubscribedOnGAM && shouldSubscribeOnGAM()) { - alreadySubscribedOnGAM = true; - gamPredictionReport(iiqConfig?.gamObjectReference, bidWon); - } const fpdFromGlobalObject = (window as any)[identityGlobalName as string]?.firstPartyData; if (fpdFromGlobalObject) { const currentCmpData = getCmpData(); @@ -289,11 +271,10 @@ function initAdapterConfig(config: any): void { const options = config?.options || {}; iiqConfig = options; - const { manualWinReportEnabled, gamPredictReporting, reportMethod, reportingServerAddress, region, adUnitConfig, partner, ABTestingConfigurationSource, browserBlackList, domainName, additionalParams } = options; + const { manualWinReportEnabled, reportMethod, reportingServerAddress, region, adUnitConfig, partner, ABTestingConfigurationSource, browserBlackList, domainName, additionalParams } = options; iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = manualWinReportEnabled || false; iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod = parseReportingMethod(reportMethod); - iiqAnalyticsAnalyticsAdapter.initOptions.gamPredictReporting = typeof gamPredictReporting === 'boolean' ? gamPredictReporting : false; iiqAnalyticsAnalyticsAdapter.initOptions.reportingServerAddress = typeof reportingServerAddress === 'string' ? reportingServerAddress : ''; iiqAnalyticsAnalyticsAdapter.initOptions.region = typeof region === 'string' ? region : ''; iiqAnalyticsAnalyticsAdapter.initOptions.adUnitConfig = typeof adUnitConfig === 'number' ? adUnitConfig : 1; @@ -352,29 +333,13 @@ function receivePartnerData(): boolean | void { } } -function shouldSubscribeOnGAM(): boolean { - if (!iiqConfig?.gamObjectReference || !isPlainObject(iiqConfig.gamObjectReference)) return false; - const partnerData = (window as any)[identityGlobalName as string]?.partnerData; - - if (partnerData) { - return partnerData.gpr || (!('gpr' in partnerData) && iiqAnalyticsAnalyticsAdapter.initOptions.gamPredictReporting); - } - return false; -} - function shouldSendReport(isReportExternal?: boolean): boolean { return ( - (isReportExternal && - iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled && - !shouldSubscribeOnGAM()) || + (isReportExternal && iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled) || (!isReportExternal && !iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled) ); } -export function restoreReportList() { - reportList = {}; -} - function bidWon(args: any, isReportExternal?: boolean): boolean | void { if ( isNaN(iiqAnalyticsAnalyticsAdapter.initOptions.partner) @@ -476,21 +441,6 @@ export function preparePayload(data: any): Record | void { } prepareData(data, result); - if (shouldSubscribeOnGAM()) { - if (!reportList[result.placementId] || !reportList[result.placementId][result.prebidAuctionId]) { - reportList[result.placementId] = reportList[result.placementId] - ? { ...reportList[result.placementId], [result.prebidAuctionId]: 1 } - : { [result.prebidAuctionId]: 1 }; - cleanReportsID = setTimeout(() => { - if (cleanReportsID) clearTimeout(cleanReportsID); - restoreReportList(); - }, 1500); // clear object in 1.5 second after defining reporting list - } else { - logError('Duplication detected, report will be not sent'); - return; - } - } - fillEidsData(result); return result; @@ -661,9 +611,6 @@ iiqAnalyticsAnalyticsAdapter.originDisableAnalytics = iiqAnalyticsAnalyticsAdapt iiqAnalyticsAnalyticsAdapter.disableAnalytics = function(): void { globalName = undefined; identityGlobalName = undefined; - alreadySubscribedOnGAM = false; - reportList = {}; - cleanReportsID = undefined; iiqConfig = undefined; iiqAnalyticsAnalyticsAdapter.initOptions = getDefaultInitOptions(); iiqAnalyticsAnalyticsAdapter.originDisableAnalytics(); diff --git a/modules/intentIqIdSystem.md b/modules/intentIqIdSystem.md index ee002d90c0c..33a56ae2a55 100644 --- a/modules/intentIqIdSystem.md +++ b/modules/intentIqIdSystem.md @@ -44,8 +44,6 @@ Please find below list of parameters that could be used in configuring Intent IQ | params.timeoutInMillis | Optional | Number | This is the timeout in milliseconds, which defines the maximum duration before the callback is triggered. The default value is 500. | `450` | | params.browserBlackList | Optional | String | This is the name of a browser that can be added to a blacklist. | `"chrome"` | | params.domainName | Optional | String | Specifies the domain of the page in which the IntentIQ object is currently running and serving the impression. This domain will be used later in the revenue reporting breakdown by domain. For example, cnn.com. It identifies the primary source of requests to the IntentIQ servers, even within nested web pages. | `"currentDomain.com"` | -| params.gamObjectReference | Optional | Object | This is a reference to the Google Ad Manager (GAM) object, which will be used to set targeting. If this parameter is not provided, the group reporting will not be configured. | `googletag` | -| params.gamParameterName | Optional | String | The name of the targeting parameter that will be used to pass the group. If not specified, the default value is `intent_iq_group`. | `"intent_iq_group"` | | params.sourceMetaData | Optional | String | This metadata can be provided by the partner and will be included in the requests URL as a query parameter | `"123.123.123.123"` | | params.sourceMetaDataExternal | Optional | Number | This metadata can be provided by the partner and will be included in the requests URL as a query parameter | `123456` | | params.iiqServerAddress | Optional | String | The base URL for the IntentIQ API server. If parameter is provided in `configParams`, it will be used. | `"https://domain.com"` | @@ -77,8 +75,6 @@ pbjs.setConfig({ callback: (data) => {...}, // your logic here groupChanged: (group) => console.log('Group is', group), domainName: "currentDomain.com", - gamObjectReference: googletag, // Optional parameter - gamParameterName: "intent_iq_group", // Optional parameter sourceMetaData: "123.123.123.123", // Optional parameter sourceMetaDataExternal: 123456, // Optional parameter chTimeout: 10, // Optional parameter diff --git a/modules/intentIqIdSystem.ts b/modules/intentIqIdSystem.ts index 7b7158d6f44..34f6c89bed8 100644 --- a/modules/intentIqIdSystem.ts +++ b/modules/intentIqIdSystem.ts @@ -5,7 +5,7 @@ * @requires module:modules/userId */ -import { isNumber, isPlainObject, isStr, logError } from '../src/utils.js'; +import { isNumber, isStr, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { detectBrowser } from '../libraries/intentIqUtils/detectBrowserUtils.ts'; @@ -33,7 +33,6 @@ import { getIiqServerAddress, iiqPixelServerAddress } from '../libraries/intentI import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.ts'; import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.ts'; import { defineABTestingGroup, IntentIqABConfigSource } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; -import { setKeyValueOn } from '../libraries/gptUtils/gptUtils.js'; export type IntentIqIdSystemModuleName = 'intentIqId'; @@ -80,18 +79,6 @@ export interface IntentIqIdSystemParams { */ groupChanged?: (group: 'A' | 'B', terminationCause?: number) => void; - /** - * Reference to the GAM `googletag.pubads()` object for automatic targeting - * key injection. - */ - gamObjectReference?: Record; - - /** - * GAM targeting key used to pass the A/B group. Defaults to - * `'intent_iq_group'`. - */ - gamParameterName?: string; - /** * Percentage of users placed in the WITH_IIQ (group A) cohort. * Accepts 0–100; values outside that range are clamped. Defaults to 95. @@ -343,20 +330,6 @@ function sendSyncRequest(allowedStorage: any, url: string, partner: number, firs } } -/** - * Configures and updates A/B testing group in Google Ad Manager (GAM). - * - * @param {object} gamObjectReference - Reference to the GAM object, expected to have a `cmd` queue and `pubads()` API. - * @param {string} gamParameterName - The name of the GAM targeting parameter where the group value will be stored. - * @param {string} userGroup - The A/B testing group assigned to the user (e.g., 'A', 'B', or a custom value). - */ -export function setGamReporting(gamObjectReference: any, gamParameterName: string, userGroup: any, isBlacklisted = false): void { - if (isBlacklisted) return; - if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) { - setKeyValueOn(gamParameterName, userGroup, gamObjectReference); - } -} - /** * Processes raw client hints data into a structured format. * @param {object} clientHints - Raw client hints data @@ -455,8 +428,6 @@ export const intentIqIdSubmodule = { let callbackFired = false; let runtimeEids: any = { eids: [] }; - const gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined; - const gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group'; const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined; const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false; sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData as string) : ''; @@ -486,8 +457,6 @@ export const intentIqIdSubmodule = { } let newUser = false; - setGamReporting(gamObjectReference, gamParameterName, actualABGroup, isBlacklisted); - callbackTimeoutID = setTimeout(() => { firePartnerCallback(); }, configParams.timeoutInMillis || 500 @@ -698,7 +667,6 @@ export const intentIqIdSubmodule = { partnerData.terminationCause = respJson.tc; actualABGroup = defineABTestingGroup(configParams, respJson.tc,); - if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); } if ('isOptedOut' in respJson) { @@ -761,13 +729,6 @@ export const intentIqIdSubmodule = { } } - if ('gpr' in respJson) { - // GAM prediction reporting - partnerData.gpr = respJson.gpr; - } else { - delete partnerData.gpr; // remove prediction flag in case server doesn't provide it - } - if (respJson.data?.eids) { runtimeEids = respJson.data; callback(respJson.data.eids); diff --git a/test/spec/libraries/gamPredictionReport_spec.js b/test/spec/libraries/gamPredictionReport_spec.js deleted file mode 100644 index 46de582b844..00000000000 --- a/test/spec/libraries/gamPredictionReport_spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import { expect } from 'chai'; -import sinon from 'sinon'; -import * as events from 'src/events.js'; -import * as utils from 'src/utils.js'; -import { gamPredictionReport } from '../../../libraries/intentIqUtils/gamPredictionReport.js'; - -describe('gamPredictionReport', function () { - let getEventsStub; - let logErrorStub; - - beforeEach(() => { - getEventsStub = sinon.stub(events, 'getEvents').returns([]); - logErrorStub = sinon.stub(utils, 'logError'); - }); - - afterEach(() => { - getEventsStub.restore(); - logErrorStub.restore(); - }); - - function runWithSlot(slot, sendData) { - let handler; - const gamObjectReference = { - cmd: [], - pubads: () => ({ - addEventListener: (eventName, callback) => { - handler = callback; - } - }) - }; - - gamPredictionReport(gamObjectReference, sendData); - gamObjectReference.cmd.forEach((fn) => fn()); - handler({ isEmpty: false, slot }); - } - - it('reads targeting from slot.getConfig targeting wrapper', () => { - const sendData = sinon.spy(); - const slot = { - getConfig: sinon.stub().withArgs('targeting').returns({ targeting: { hb_bidder: ['test'] } }), - getTargetingKeys: sinon.stub().throws(new Error('deprecated')), - getTargeting: sinon.stub().throws(new Error('deprecated')), - getSlotElementId: () => 'div-1', - getAdUnitPath: () => '/123' - }; - - runWithSlot(slot, sendData); - - expect(sendData.calledOnce).to.equal(true); - expect(sendData.firstCall.args[0].bidderCode).to.equal('test'); - }); - - it('reads targeting from legacy slot.getTargeting APIs when getConfig is missing', () => { - const sendData = sinon.spy(); - const slot = { - getTargetingKeys: sinon.stub().returns(['hb_bidder']), - getTargeting: sinon.stub().withArgs('hb_bidder').returns(['legacy']), - getSlotElementId: () => 'div-3', - getAdUnitPath: () => '/789' - }; - - runWithSlot(slot, sendData); - - expect(sendData.calledOnce).to.equal(true); - expect(sendData.firstCall.args[0].bidderCode).to.equal('legacy'); - expect(slot.getTargetingKeys.calledOnce).to.equal(true); - expect(slot.getTargeting.calledOnce).to.equal(true); - }); - - it('logs and recovers when legacy targeting APIs throw', () => { - const sendData = sinon.spy(); - const slot = { - getTargetingKeys: sinon.stub().throws(new Error('legacy broken')), - getTargeting: sinon.stub(), - getSlotElementId: () => 'div-5', - getAdUnitPath: () => '/202' - }; - - runWithSlot(slot, sendData); - - expect(sendData.calledOnce).to.equal(true); - expect(sendData.firstCall.args[0].bidderCode).to.equal(null); - expect(logErrorStub.called).to.equal(true); - expect(logErrorStub.firstCall.args[0]).to.match(/Failed to get slot targeting/); - }); -}); diff --git a/test/spec/modules/intentIqAnalyticsAdapter_spec.js b/test/spec/modules/intentIqAnalyticsAdapter_spec.js index 1c8174e1a1a..dca38ab01a0 100644 --- a/test/spec/modules/intentIqAnalyticsAdapter_spec.js +++ b/test/spec/modules/intentIqAnalyticsAdapter_spec.js @@ -2,7 +2,6 @@ import { expect } from "chai"; import iiqAnalyticsAnalyticsAdapter, { REPORTER_ID, preparePayload, - restoreReportList, } from "modules/intentIqAnalyticsAdapter.js"; import * as utils from "src/utils.js"; import { server } from "test/mocks/xhr.js"; @@ -12,7 +11,6 @@ import * as events from "src/events.js"; import { getGlobal } from "../../../src/prebidGlobal.js"; import sinon from "sinon"; import { - FIRST_PARTY_KEY, PREBID, VERSION, WITHOUT_IIQ, @@ -224,7 +222,6 @@ describe("IntentIQ tests all", function () { events.emit(EVENTS.BID_WON, wonRequest); const request = server.requests[0]; - restoreReportList(); const expectedData = preparePayload(wonRequest); const expectedPayload = `["${btoa(JSON.stringify(expectedData))}"]`; @@ -246,7 +243,6 @@ describe("IntentIQ tests all", function () { const payloadEncoded = url.searchParams.get("payload"); const decoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0])); - restoreReportList(); const expected = preparePayload(wonRequest); expect(decoded.partnerId).to.equal(expected.partnerId); @@ -399,7 +395,6 @@ describe("IntentIQ tests all", function () { expect(server.requests.length).to.be.above(0); const request = server.requests[0]; - restoreReportList(); const dataToSend = preparePayload(wonRequest); const base64String = btoa(JSON.stringify(dataToSend)); const payload = encodeURIComponent(JSON.stringify([base64String])); @@ -670,8 +665,6 @@ describe("IntentIQ tests all", function () { expect(payloadDecoded).to.have.property("vrref"); expect(decodeURIComponent(payloadDecoded.vrref)).to.equal(domainName); - - restoreReportList(); }); it("should not send additionalParams in report if value is too large", function () { @@ -729,109 +722,6 @@ describe("IntentIQ tests all", function () { expect(request.url).to.include(`&spd=${expectedSpdEncoded}`); }); - describe("GAM prediction reporting", function () { - function createMockGAM() { - const listeners = {}; - return { - cmd: [], - pubads: () => ({ - addEventListener: (name, cb) => { - listeners[name] = cb; - }, - }), - _listeners: listeners, - }; - } - - it("should subscribe to GAM and send report on slotRenderEnded without prior bidWon", function () { - const gam = createMockGAM(); - - enableAnalyticWithSpecialOptions({ - gamObjectReference: gam - }); - - // enable subscription by LS flag - window[`iiq_identity_${partner}`].partnerData.gpr = true; - - // provide recent auctionEnd with matching bid to enrich payload - events.getEvents.restore(); - sinon.stub(events, "getEvents").returns([ - { - eventType: "auctionEnd", - args: { - auctionId: "auc-1", - adUnitCodes: ["ad-unit-1"], - bidsReceived: [ - { - bidder: "pubmatic", - adUnitCode: "ad-unit-1", - cpm: 1, - currency: "USD", - originalCpm: 1, - originalCurrency: "USD", - status: "rendered", - }, - ], - }, - }, - ]); - - // trigger adapter to subscribe - events.emit(EVENTS.BID_REQUESTED); - - // execute GAM cmd to register listener - gam.cmd.forEach((fn) => fn()); - - // simulate slotRenderEnded - const slot = { - getSlotElementId: () => "ad-unit-1", - getAdUnitPath: () => "/123/foo", - getTargetingKeys: () => ["hb_bidder", "hb_adid"], - getTargeting: (k) => - k === "hb_bidder" ? ["pubmatic"] : k === "hb_adid" ? ["ad123"] : [], - }; - if (gam._listeners["slotRenderEnded"]) { - gam._listeners["slotRenderEnded"]({ isEmpty: false, slot }); - } - - expect(server.requests.length).to.be.above(0); - }); - - it("should NOT send report if a matching bidWon already exists", function () { - const gam = createMockGAM(); - - localStorage.setItem( - FIRST_PARTY_KEY + "_" + partner, - JSON.stringify({ gpr: true }) - ); - - // provide prior bidWon matching placementId and hb_adid - events.getEvents.restore(); - sinon - .stub(events, "getEvents") - .returns([ - { eventType: "bidWon", args: { adId: "ad123" }, id: "ad-unit-1" }, - ]); - - events.emit(EVENTS.BID_REQUESTED); - gam.cmd.forEach((fn) => fn()); - - const slot = { - getSlotElementId: () => "ad-unit-1", - getAdUnitPath: () => "/123/foo", - getTargetingKeys: () => ["hb_bidder", "hb_adid"], - getTargeting: (k) => - k === "hb_bidder" ? ["pubmatic"] : k === "hb_adid" ? ["ad123"] : [], - }; - - const initialRequests = server.requests.length; - if (gam._listeners["slotRenderEnded"]) { - gam._listeners["slotRenderEnded"]({ isEmpty: false, slot }); - } - expect(server.requests.length).to.equal(initialRequests); - }); - }); - const testCasesVrref = [ { description: "domainName matches window.top.location.href", diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index e7a957b6dfc..45123258d46 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -7,8 +7,7 @@ import { handleClientHints, firstPartyData as moduleFPD, isCMPStringTheSame, createPixelUrl, translateMetadata, - initializeGlobalIIQ, - setGamReporting + initializeGlobalIIQ } from '../../../modules/intentIqIdSystem.js'; import { storage, readData, storeData } from '../../../libraries/intentIqUtils/storageUtils.js'; import { gppDataHandler, uspDataHandler, gdprDataHandler } from '../../../src/consentHandler.js'; @@ -105,24 +104,6 @@ async function waitForClientHints() { const testAPILink = 'https://new-test-api.intentiq.com'; const syncTestAPILink = 'https://new-test-sync.intentiq.com'; -const mockGAM = () => { - const targetingObject = {}; - return { - cmd: [], - pubads: () => ({ - setTargeting: (key, value) => { - targetingObject[key] = value; - }, - getTargeting: (key) => { - return [targetingObject[key]]; - }, - getTargetingKeys: () => { - return Object.keys(targetingObject); - } - }) - }; -}; - const regionCases = [ { name: 'no region (default)', region: undefined, expected: 'https://api.intentiq.com' }, { name: 'apac', region: 'apac', expected: 'https://api-apac.intentiq.com' }, @@ -220,46 +201,6 @@ describe('IntentIQ tests', function () { expect(submodule).to.be.undefined; }); - it('should use setConfig when available in setGamReporting', function () { - const setConfigSpy = sinon.spy(); - const pubadsSetTargetingSpy = sinon.spy(); - const mockGAM = { - cmd: [], - getConfig: sinon.stub(), - setConfig: setConfigSpy, - pubads: () => ({ - setTargeting: pubadsSetTargetingSpy - }) - }; - - setGamReporting(mockGAM, 'intent_iq_group', 'A'); - mockGAM.cmd.forEach((fn) => fn()); - - expect(setConfigSpy.calledOnce).to.equal(true); - expect(setConfigSpy.firstCall.args[0]).to.deep.equal({ - targeting: { - intent_iq_group: 'A' - } - }); - expect(pubadsSetTargetingSpy.called).to.equal(false); - }); - - it('should fall back to pubads.setTargeting when setConfig is missing', function () { - const pubadsSetTargetingSpy = sinon.spy(); - const mockGAM = { - cmd: [], - pubads: () => ({ - setTargeting: pubadsSetTargetingSpy - }) - }; - - setGamReporting(mockGAM, 'intent_iq_group', 'B'); - mockGAM.cmd.forEach((fn) => fn()); - - expect(pubadsSetTargetingSpy.calledOnce).to.equal(true); - expect(pubadsSetTargetingSpy.firstCall.args).to.deep.equal(['intent_iq_group', 'B']); - }); - it('should not save data in cookie if relevant type not set', async function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -395,145 +336,6 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should set GAM targeting to B initially and update to A after server response', async function () { - const callBackSpy = sinon.spy(); - const mockGamObject = mockGAM(); - const expectedGamParameterName = 'intent_iq_group'; - defaultConfigParams.params.abPercentage = 0; // "B" provided percentage by user - - const originalPubads = mockGamObject.pubads; - const setTargetingSpy = sinon.spy(); - mockGamObject.pubads = function () { - const obj = { ...originalPubads.apply(this, arguments) }; - const originalSetTargeting = obj.setTargeting; - obj.setTargeting = function (...args) { - setTargetingSpy(...args); - return originalSetTargeting.apply(this, args); - }; - return obj; - }; - - defaultConfigParams.params.gamObjectReference = mockGamObject; - - const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - mockGamObject.cmd.forEach(cb => cb()); - mockGamObject.cmd = []; - - const groupBeforeResponse = mockGamObject.pubads().getTargeting(expectedGamParameterName); - - request.respond(200, responseHeader, JSON.stringify({ tc: 20 })); - - mockGamObject.cmd.forEach(cb => cb()); - mockGamObject.cmd = []; - - const groupAfterResponse = mockGamObject.pubads().getTargeting(expectedGamParameterName); - - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39'); - expect(groupBeforeResponse).to.deep.equal([WITHOUT_IIQ]); - expect(groupAfterResponse).to.deep.equal([WITH_IIQ]); - expect(setTargetingSpy.calledTwice).to.be.true; - }); - - it('should set GAM targeting to B when server tc=41', async () => { - window.localStorage.clear(); - const mockGam = mockGAM(); - defaultConfigParams.params.gamObjectReference = mockGam; - defaultConfigParams.params.abPercentage = 100; - - const cb = intentIqIdSubmodule.getId(defaultConfigParams).callback; - cb(() => {}); - await waitForClientHints(); - - const req = server.requests[0]; - mockGam.cmd.forEach(fn => fn()); - const before = mockGam.pubads().getTargeting('intent_iq_group'); - - req.respond(200, responseHeader, JSON.stringify({ tc: 41 })); - mockGam.cmd.forEach(fn => fn()); - const after = mockGam.pubads().getTargeting('intent_iq_group'); - - expect(before).to.deep.equal([WITH_IIQ]); - expect(after).to.deep.equal([WITHOUT_IIQ]); - }); - - it('should read tc from LS and set relevant GAM group', async () => { - window.localStorage.clear(); - const storageKey = `${FIRST_PARTY_KEY}_${defaultConfigParams.params.partner}`; - localStorage.setItem(storageKey, JSON.stringify({ terminationCause: 41 })); - - const mockGam = mockGAM(); - defaultConfigParams.params.gamObjectReference = mockGam; - defaultConfigParams.params.abPercentage = 100; - - const cb = intentIqIdSubmodule.getId(defaultConfigParams).callback; - cb(() => {}); - await waitForClientHints(); - - mockGam.cmd.forEach(fn => fn()); - const group = mockGam.pubads().getTargeting('intent_iq_group'); - - expect(group).to.deep.equal([WITHOUT_IIQ]); - }); - - it('should use the provided gamParameterName from configParams', function () { - const callBackSpy = sinon.spy(); - const mockGamObject = mockGAM(); - const customParamName = 'custom_gam_param'; - - defaultConfigParams.params.gamObjectReference = mockGamObject; - defaultConfigParams.params.gamParameterName = customParamName; - - const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; - submoduleCallback(callBackSpy); - mockGamObject.cmd.forEach(cb => cb()); - const targetingKeys = mockGamObject.pubads().getTargetingKeys(); - - expect(targetingKeys).to.include(customParamName); - }); - - it('should NOT call GAM setTargeting when current browser is in browserBlackList', function () { - const usedBrowser = 'chrome'; - const gam = mockGAM(); - const pa = gam.pubads(); - sinon.stub(gam, 'pubads').returns(pa); - - const originalSetTargeting = pa.setTargeting; - let setTargetingCalls = 0; - pa.setTargeting = function (...args) { - setTargetingCalls++; - return originalSetTargeting.apply(this, args); - }; - - localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({ - pcid: 'pcid-1', - pcidDate: Date.now(), - isOptedOut: false, - date: Date.now(), - sCal: Date.now() - })); - - const cfg = { - params: { - partner, - gamObjectReference: gam, - gamParameterName: 'custom_gam_param', - browserBlackList: usedBrowser - } - }; - - intentIqIdSubmodule.getId(cfg); - gam.cmd.forEach(fn => fn()); - const currentBrowserLowerCase = detectBrowser(); - if (currentBrowserLowerCase === usedBrowser) { - expect(setTargetingCalls).to.equal(0); - expect(pa.getTargetingKeys()).to.not.include('custom_gam_param'); - } - }); - it('should not throw Uncaught TypeError when IntentIQ endpoint returns empty response', async function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; From 4f3b9a183a88350f1942cf1cf70fd07c70a28d5c Mon Sep 17 00:00:00 2001 From: Eyvaz Date: Thu, 3 Sep 2026 20:58:37 +0200 Subject: [PATCH 2/2] remove unused code according to partner config --- .../intentIqConstants/intentIqConstants.ts | 22 - .../defineABTestingGroupUtils.ts | 97 --- libraries/intentIqUtils/detectBrowserUtils.ts | 98 --- libraries/intentIqUtils/getSyncKey.ts | 1 - .../intentIqUtils/handleAdditionalParams.ts | 44 - modules/intentIqAnalyticsAdapter.ts | 282 +----- modules/intentIqIdSystem.ts | 233 +---- .../modules/intentIqAnalyticsAdapter_spec.js | 595 ++----------- test/spec/modules/intentIqIdSystem_spec.js | 824 +----------------- 9 files changed, 139 insertions(+), 2057 deletions(-) delete mode 100644 libraries/intentIqUtils/defineABTestingGroupUtils.ts delete mode 100644 libraries/intentIqUtils/detectBrowserUtils.ts delete mode 100644 libraries/intentIqUtils/getSyncKey.ts delete mode 100644 libraries/intentIqUtils/handleAdditionalParams.ts diff --git a/libraries/intentIqConstants/intentIqConstants.ts b/libraries/intentIqConstants/intentIqConstants.ts index d65cb3d5346..cae9e758616 100644 --- a/libraries/intentIqConstants/intentIqConstants.ts +++ b/libraries/intentIqConstants/intentIqConstants.ts @@ -4,9 +4,7 @@ export const SUPPORTED_TYPES = ["html5", "cookie"]; export const WITH_IIQ = "A"; export const WITHOUT_IIQ = "B"; -export const DEFAULT_PERCENTAGE = 95; export const CLIENT_HINTS_KEY = "_iiq_ch"; -export const EMPTY = "EMPTY"; export const GVLID = 1323; export const VERSION = 0.38; export const PREBID = "pbjs"; @@ -15,19 +13,6 @@ export const HOURS_72 = HOURS_24 * 3; export const INVALID_ID = "INVALID_ID"; -export const SYNC_REFRESH_MILL = 3600000; -export const META_DATA_CONSTANT = 256; - -export const MAX_REQUEST_LENGTH = { - // https://www.geeksforgeeks.org/maximum-length-of-a-url-in-different-browsers/ - chrome: 2097152, - safari: 80000, - opera: 2097152, - edge: 2048, - firefox: 65536, - ie: 2048, -}; - export const CH_KEYS = [ "brands", "mobile", @@ -39,10 +24,3 @@ export const CH_KEYS = [ "platformVersion", "fullVersionList", ]; - -export const AB_CONFIG_SOURCE = { - PERCENTAGE: "percentage", - GROUP: "group", - IIQ_SERVER: "IIQServer", - DISABLED: "disabled", -}; diff --git a/libraries/intentIqUtils/defineABTestingGroupUtils.ts b/libraries/intentIqUtils/defineABTestingGroupUtils.ts deleted file mode 100644 index 44abec10171..00000000000 --- a/libraries/intentIqUtils/defineABTestingGroupUtils.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - WITH_IIQ, - WITHOUT_IIQ, - DEFAULT_PERCENTAGE, - AB_CONFIG_SOURCE, -} from '../intentIqConstants/intentIqConstants.js'; - -/** - * A/B testing configuration source — controls how the test group is assigned. - * - `'percentage'` — random assignment based on `abPercentage` - * - `'group'` — fixed group supplied via the `group` param - * - `'IIQServer'` — server-driven assignment (default) - * - `'disabled'` — A/B testing disabled; always use IIQ - */ -export type IntentIqABConfigSource = 'percentage' | 'group' | 'IIQServer' | 'disabled'; - -type ABGroup = typeof WITH_IIQ | typeof WITHOUT_IIQ; - -interface ABTestingConfig { - ABTestingConfigurationSource?: string; - abPercentage?: number; - group?: string; -} - -/** - * Fix percentage if provided some incorrect data - * clampPct(150) => 100 - * clampPct(-5) => 0 - * clampPct('abc') => DEFAULT_PERCENTAGE - */ -function clampPct(val: unknown): number { - const n = Number(val); - if (!Number.isFinite(n)) return DEFAULT_PERCENTAGE; // fallback = 95 - return Math.max(0, Math.min(100, n)); -} - -/** - * Randomly assigns a user to group A or B based on the given percentage. - * Generates a random number (1–100) and compares it with the percentage. - * - * @param {number} pct The percentage threshold (0–100). - * @returns {string} Returns WITH_IIQ for Group A or WITHOUT_IIQ for Group B. - */ -function pickABByPercentage(pct?: number): ABGroup { - const percentageToUse = - typeof pct === 'number' ? pct : DEFAULT_PERCENTAGE; - const percentage = clampPct(percentageToUse); - const roll = Math.floor(Math.random() * 100) + 1; - return roll <= percentage ? WITH_IIQ : WITHOUT_IIQ; // A : B -} - -function configurationSourceGroupInitialization(group?: string): ABGroup { - return typeof group === 'string' && group.toUpperCase() === WITHOUT_IIQ - ? WITHOUT_IIQ - : WITH_IIQ; -} - -/** - * Determines the runtime A/B testing group without saving it to Local Storage. - * 1. If terminationCause (tc) exists: - * - tc = 41 → Group B (WITHOUT_IIQ) - * - any other value → Group A (WITH_IIQ) - * 2. Otherwise, assigns the group randomly based on DEFAULT_PERCENTAGE (default 95% for A, 5% for B). - * - * @param {number} [tc] The termination cause value returned by the server. - * @param {number} [abPercentage] A/B percentage provided by partner. - * @returns {string} The determined group: WITH_IIQ (A) or WITHOUT_IIQ (B). - */ -function IIQServerConfigurationSource(tc?: number, abPercentage?: number): ABGroup { - if (typeof tc === 'number' && Number.isFinite(tc)) { - return tc === 41 ? WITHOUT_IIQ : WITH_IIQ; - } - - return pickABByPercentage(abPercentage); -} - -export function defineABTestingGroup( - configObject: ABTestingConfig, - tc?: number -): ABGroup { - switch (configObject.ABTestingConfigurationSource) { - case AB_CONFIG_SOURCE.GROUP: - return configurationSourceGroupInitialization( - configObject.group - ); - - case AB_CONFIG_SOURCE.PERCENTAGE: - return pickABByPercentage(configObject.abPercentage); - - default: { - if (!configObject.ABTestingConfigurationSource) { - configObject.ABTestingConfigurationSource = AB_CONFIG_SOURCE.IIQ_SERVER; - } - return IIQServerConfigurationSource(tc, configObject.abPercentage); - } - } -} diff --git a/libraries/intentIqUtils/detectBrowserUtils.ts b/libraries/intentIqUtils/detectBrowserUtils.ts deleted file mode 100644 index 58b584da458..00000000000 --- a/libraries/intentIqUtils/detectBrowserUtils.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { logError } from '../../src/utils.js'; - -type BrowserName = - | 'chrome' - | 'edge' - | 'firefox' - | 'ie' - | 'opera' - | 'safari' - | 'unknown'; - -/** - * Detects the browser using either userAgent or userAgentData - * @return {string} The name of the detected browser or 'unknown' if unable to detect - */ -export function detectBrowser(): BrowserName { - try { - if (navigator?.userAgent) { - return detectBrowserFromUserAgent(navigator.userAgent); - } else if ((navigator as any)?.userAgentData) { - return detectBrowserFromUserAgentData((navigator as any)?.userAgentData); - } - } catch (error) { - logError('Error detecting browser:', error); - } - return 'unknown'; -} - -/** - * Detects the browser from the user agent string - * @param {string} userAgent - The user agent string from the browser - * @return {string} The name of the detected browser or 'unknown' if unable to detect - */ -export function detectBrowserFromUserAgent(userAgent: string): BrowserName { - const browserRegexPatterns: Record = { - opera: /Opera|OPR/, - edge: /Edg/, - chrome: /Chrome|CriOS/, - safari: /Safari/, - firefox: /Firefox/, - ie: /MSIE|Trident/, - }; - - // Check for Edge first - if (browserRegexPatterns.edge.test(userAgent)) { - return 'edge'; - } - - // Check for Opera next - if (browserRegexPatterns.opera.test(userAgent)) { - return 'opera'; - } - - // Check for Chrome first to avoid confusion with Safari - if (browserRegexPatterns.chrome.test(userAgent)) { - return 'chrome'; - } - - // Now we can safely check for Safari - if ( - browserRegexPatterns.safari.test(userAgent) && - !browserRegexPatterns.chrome.test(userAgent) - ) { - return 'safari'; - } - - // Check other browsers - for (const browser in browserRegexPatterns) { - if (browserRegexPatterns[browser].test(userAgent)) { - return browser as BrowserName; - } - } - - return 'unknown'; -} - -/** - * Detects the browser from the NavigatorUAData object - * @param {Object} userAgentData - The user agent data object from the browser - * @return {string} The name of the detected browser or 'unknown' if unable to detect - */ -export function detectBrowserFromUserAgentData( - userAgentData -): BrowserName { - const brandNames = userAgentData.brands.map(brand => brand.brand); - - if (brandNames.includes('Microsoft Edge')) { - return 'edge'; - } else if (brandNames.includes('Opera')) { - return 'opera'; - } else if ( - brandNames.some(brand => brand === 'Chromium' || brand === 'Google Chrome') - ) { - return 'chrome'; - } - - return 'unknown'; -} diff --git a/libraries/intentIqUtils/getSyncKey.ts b/libraries/intentIqUtils/getSyncKey.ts deleted file mode 100644 index 9f39a8f26bd..00000000000 --- a/libraries/intentIqUtils/getSyncKey.ts +++ /dev/null @@ -1 +0,0 @@ -export const SYNC_KEY = (partner: number): string => `_iiq_sync_${partner}`; diff --git a/libraries/intentIqUtils/handleAdditionalParams.ts b/libraries/intentIqUtils/handleAdditionalParams.ts deleted file mode 100644 index e4bfa14c84f..00000000000 --- a/libraries/intentIqUtils/handleAdditionalParams.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { MAX_REQUEST_LENGTH } from "../intentIqConstants/intentIqConstants.js"; - -/** - * Appends additional parameters to a URL if they are valid and applicable for the given request destination. - * - * @param {string} browser - The name of the current browser; used to look up the maximum URL length. - * @param {string} url - The base URL to which additional parameters may be appended. - * @param {(string|number)} requestTo - The destination identifier; used as an index to check if a parameter applies. - * @param {Array} additionalParams - An array of parameter objects to append. - * Each parameter object should have the following properties: - * - `parameterName` {string}: The name of the parameter. - * - `parameterValue` {*}: The value of the parameter. - * - `destination` {Object|Array}: An object or array indicating the applicable destinations. Sync = 0, VR = 1, reporting = 2 - * - * @return {string} The resulting URL with additional parameters appended if valid; otherwise, the original URL. - */ -export function handleAdditionalParams(browser, url, requestTo, additionalParams) { - let queryString = ''; - - if (!Array.isArray(additionalParams)) return url; - - for (let i = 0; i < additionalParams.length; i++) { - const param = additionalParams[i]; - - if ( - typeof param !== 'object' || - !param.parameterName || - !param.parameterValue || - !param.destination || - !Array.isArray(param.destination) - ) { - continue; - } - - if (param.destination[requestTo]) { - queryString += `&agp_${encodeURIComponent(param.parameterName)}=${param.parameterValue}`; - } - } - - const maxLength = MAX_REQUEST_LENGTH[browser] ?? 2048; - if ((url.length + queryString.length) > maxLength) return url; - - return url + queryString; -} diff --git a/modules/intentIqAnalyticsAdapter.ts b/modules/intentIqAnalyticsAdapter.ts index 794b5516831..4b246187101 100644 --- a/modules/intentIqAnalyticsAdapter.ts +++ b/modules/intentIqAnalyticsAdapter.ts @@ -2,20 +2,17 @@ import { logError, logInfo } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { ajax } from '../src/ajax.js'; -import { EVENTS } from '../src/constants.js'; -import { detectBrowser } from '../libraries/intentIqUtils/detectBrowserUtils.ts'; import { appendSPData } from '../libraries/intentIqUtils/urlUtils.ts'; -import { appendVrrefAndFui, getCurrentUrl, getRelevantRefferer } from '../libraries/intentIqUtils/getRefferer.ts'; -import { getCmpData, areCmpValuesEqual, isValidValue } from '../libraries/intentIqUtils/getCmpData.ts'; +import { appendVrrefAndFui, getCurrentUrl } from '../libraries/intentIqUtils/getRefferer.ts'; +import { getCmpData, isValidValue } from '../libraries/intentIqUtils/getCmpData.ts'; import { getUnitPosition } from '../libraries/intentIqUtils/getUnitPosition.ts'; import { VERSION, PREBID, - WITH_IIQ + WITH_IIQ, + WITHOUT_IIQ } from '../libraries/intentIqConstants/intentIqConstants.ts'; import { reportingServerAddress } from '../libraries/intentIqUtils/intentIqConfig.ts'; -import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.ts'; -import { defineABTestingGroup, IntentIqABConfigSource } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; import { getGlobal } from '../src/prebidGlobal.js'; /** @@ -90,73 +87,12 @@ export interface IntentIqAnalyticsAdapterOptions { partner: number; /** - * Set to `true` to allow manual win reporting via - * `window.intentIqAnalyticsAdapter_.reportExternalWin()`. - * Defaults to `false`. - */ - manualWinReportEnabled?: boolean; - - /** - * HTTP method used to send reports. Defaults to `'GET'`. - */ - reportMethod?: 'GET' | 'POST'; - - /** - * Override for the IntentIQ reporting server base URL. - */ - reportingServerAddress?: string; - - /** - * Geo-region routing hint for the reporting server. - */ - region?: string; - - /** - * Controls how the `placementId` field in reports is populated: - * 1 = adUnitCode then placementId (default) - * 2 = placementId then adUnitCode - * 3 = adUnitCode only - * 4 = placementId only - */ - adUnitConfig?: 1 | 2 | 3 | 4; - - /** - * Determines how the A/B test group is assigned. Defaults to `'IIQServer'`. - */ - ABTestingConfigurationSource?: IntentIqABConfigSource; - - /** - * Explicit A/B group override. Only used when - * `ABTestingConfigurationSource` is `'group'`. + * Explicit A/B group override. This build always assigns the A/B test + * group directly from `group` (equivalent to a fixed + * `ABTestingConfigurationSource: 'group'`), independent of the server + * termination cause. */ group?: 'A' | 'B'; - - /** - * Percentage of users placed in the WITH_IIQ cohort (0–100). Defaults to 95. - */ - abPercentage?: number; - - /** - * Comma-separated list of browser names (lowercase) excluded from reporting, - * e.g. `'chrome,safari'`. - */ - browserBlackList?: string; - - /** - * Publisher domain name appended to report URLs. - */ - domainName?: string; - - /** - * Freeform key-value pairs appended to every report URL. - */ - additionalParams?: Record; - - /** - * When `true`, first-party data is stored under a partner-specific key. - */ - siloEnabled?: boolean; - } const MODULE_NAME = 'iiqAnalytics' as const; @@ -211,10 +147,6 @@ const PARAMS_NAMES: Record = { const DEFAULT_URL = 'https://reports.intentiq.com/report'; -const getDataForDefineURL = () => { - return [iiqAnalyticsAnalyticsAdapter.initOptions.reportingServerAddress, iiqAnalyticsAnalyticsAdapter.initOptions.region]; -}; - const getDefaultInitOptions = () => { return { adapterConfigInitialized: false, @@ -224,69 +156,31 @@ const getDefaultInitOptions = () => { dataInLs: null, eidl: null, dataIdsInitialized: false, - manualWinReportEnabled: false, - domainName: null, - siloEnabled: false, - reportMethod: null, - abPercentage: null, - userPercentage: null, - abTestUuid: null, - additionalParams: null, - reportingServerAddress: '', - region: '' + abTestUuid: null }; }; const iiqAnalyticsAnalyticsAdapter: any = Object.assign(adapter({ url: DEFAULT_URL, analyticsType }), { initOptions: getDefaultInitOptions(), - track({ eventType, args }: { eventType: string; args: any }) { - switch (eventType) { - case BID_WON: - bidWon(args); - break; - case BID_REQUESTED: { - const fpdFromGlobalObject = (window as any)[identityGlobalName as string]?.firstPartyData; - if (fpdFromGlobalObject) { - const currentCmpData = getCmpData(); - const hasCmpMismatch = ['gdprString', 'gppString', 'uspString'].some((field: string) => - !areCmpValuesEqual(fpdFromGlobalObject[field], currentCmpData[field]) - ); - if (hasCmpMismatch) { - pbjs.refreshUserIds({ submoduleNames: ['intentIqId'] }); - } - } - break; - } - default: - break; - } + track() { + // Intentional no-op: this build fixes manualWinReportEnabled to true, so BID_WON + // reports are only sent via window.intentIqAnalyticsAdapter_.reportExternalWin(). + // Keeping this override in place prevents the base AnalyticsAdapter's default + // endpoint auto-send behavior for every tracked event. } }); -// Events needed -const { BID_WON, BID_REQUESTED } = EVENTS; - function initAdapterConfig(config: any): void { if (iiqAnalyticsAnalyticsAdapter.initOptions.adapterConfigInitialized) return; const options = config?.options || {}; iiqConfig = options; - const { manualWinReportEnabled, reportMethod, reportingServerAddress, region, adUnitConfig, partner, ABTestingConfigurationSource, browserBlackList, domainName, additionalParams } = options; - iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = - manualWinReportEnabled || false; - iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod = parseReportingMethod(reportMethod); - iiqAnalyticsAnalyticsAdapter.initOptions.reportingServerAddress = typeof reportingServerAddress === 'string' ? reportingServerAddress : ''; - iiqAnalyticsAnalyticsAdapter.initOptions.region = typeof region === 'string' ? region : ''; - iiqAnalyticsAnalyticsAdapter.initOptions.adUnitConfig = typeof adUnitConfig === 'number' ? adUnitConfig : 1; - iiqAnalyticsAnalyticsAdapter.initOptions.configSource = ABTestingConfigurationSource; - iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = defineABTestingGroup(options); + const { partner, group } = options; + // ABTestingConfigurationSource is fixed to 'group' for this build: the group is + // taken directly from `group`, independent of the server termination cause. + iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = + typeof group === 'string' && group.toUpperCase() === WITHOUT_IIQ ? WITHOUT_IIQ : WITH_IIQ; iiqAnalyticsAnalyticsAdapter.initOptions.idModuleConfigInitialized = true; - iiqAnalyticsAnalyticsAdapter.initOptions.browserBlackList = - typeof browserBlackList === 'string' - ? browserBlackList.toLowerCase() - : ''; - iiqAnalyticsAnalyticsAdapter.initOptions.domainName = domainName || ''; - iiqAnalyticsAnalyticsAdapter.initOptions.additionalParams = additionalParams || null; if (!partner) { logError('IIQ ANALYTICS -> partner ID is missing'); iiqAnalyticsAnalyticsAdapter.initOptions.partner = -1; @@ -321,76 +215,31 @@ function receivePartnerData(): boolean | void { iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = actualABGroup; } iiqAnalyticsAnalyticsAdapter.initOptions.clientHints = clientHints; - - const { abPercentage, userProvidedAbPercentage } = (window as any)[identityGlobalName as string]; - if (abPercentage !== undefined) { - iiqAnalyticsAnalyticsAdapter.initOptions.abPercentage = abPercentage; - } - iiqAnalyticsAnalyticsAdapter.initOptions.userPercentage = userProvidedAbPercentage; } catch (e) { logError(e); return false; } } -function shouldSendReport(isReportExternal?: boolean): boolean { - return ( - (isReportExternal && iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled) || - (!isReportExternal && !iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled) - ); -} - -function bidWon(args: any, isReportExternal?: boolean): boolean | void { - if ( - isNaN(iiqAnalyticsAnalyticsAdapter.initOptions.partner) - ) { +function bidWon(args: any): boolean | void { + if (isNaN(iiqAnalyticsAnalyticsAdapter.initOptions.partner)) { iiqAnalyticsAnalyticsAdapter.initOptions.partner = -1; } - const currentBrowserLowerCase = detectBrowser(); - if (iiqAnalyticsAnalyticsAdapter.initOptions.browserBlackList?.includes(currentBrowserLowerCase)) { - logError('IIQ ANALYTICS -> Browser is in blacklist!'); - return; - } - - if (shouldSendReport(isReportExternal)) { - const success = receivePartnerData(); - const preparedPayload = preparePayload(args); - if (!preparedPayload) return false; - if (success === false) { - preparedPayload[PARAMS_NAMES.terminationCause] = -1; - } - const { url, method, payload } = constructFullUrl(preparedPayload); - if (method === 'POST') { - ajax(url, undefined, payload, { - method, - contentType: 'application/x-www-form-urlencoded' - }); - } else { - ajax(url, undefined, null, { method }); - } - logInfo('IIQ ANALYTICS -> BID WON'); - return true; + const success = receivePartnerData(); + const preparedPayload = preparePayload(args); + if (!preparedPayload) return false; + if (success === false) { + preparedPayload[PARAMS_NAMES.terminationCause] = -1; } - return false; -} - -function parseReportingMethod(reportMethod: unknown): 'GET' | 'POST' { - if (typeof reportMethod === 'string') { - switch (reportMethod.toUpperCase()) { - case 'GET': - return 'GET'; - case 'POST': - return 'POST'; - default: - return 'GET'; - } - } - return 'GET'; + const { url } = constructFullUrl(preparedPayload); + ajax(url, undefined, null, { method: 'GET' }); + logInfo('IIQ ANALYTICS -> BID WON'); + return true; } function defineGlobalVariableName(): void { function reportExternalWin(args: any): boolean | void { - return bidWon(args, true); + return bidWon(args); } const partnerId = iiqConfig?.partner || 0; @@ -409,7 +258,7 @@ export function preparePayload(data: any): Record | void { const fullUrl = getCurrentUrl(); result[PARAMS_NAMES.partnerId] = iiqAnalyticsAnalyticsAdapter.initOptions.partner; result[PARAMS_NAMES.prebidVersion] = prebidVersion; - result[PARAMS_NAMES.referrer] = getRelevantRefferer(iiqAnalyticsAnalyticsAdapter.initOptions.domainName, fullUrl); + result[PARAMS_NAMES.referrer] = encodeURIComponent(fullUrl); result[PARAMS_NAMES.terminationCause] = iiqAnalyticsAnalyticsAdapter.initOptions.terminationCause; result[PARAMS_NAMES.clientType] = iiqAnalyticsAnalyticsAdapter.initOptions.clientType; result[PARAMS_NAMES.siteId] = iiqAnalyticsAnalyticsAdapter.initOptions.siteId; @@ -430,15 +279,8 @@ export function preparePayload(data: any): Record | void { if (iiqAnalyticsAnalyticsAdapter.initOptions.fpid?.pid) { result[PARAMS_NAMES.profile] = encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pid); } - if (iiqAnalyticsAnalyticsAdapter.initOptions.configSource) { - result[PARAMS_NAMES.ABTestingConfigurationSource] = iiqAnalyticsAnalyticsAdapter.initOptions.configSource; - } - if (iiqAnalyticsAnalyticsAdapter.initOptions.abPercentage !== null) { - result[PARAMS_NAMES.abPercentage] = iiqAnalyticsAnalyticsAdapter.initOptions.abPercentage; - } - if (iiqAnalyticsAnalyticsAdapter.initOptions.userPercentage !== undefined && iiqAnalyticsAnalyticsAdapter.initOptions.userPercentage !== null) { - result[PARAMS_NAMES.userPercentage] = iiqAnalyticsAnalyticsAdapter.initOptions.userPercentage; - } + // ABTestingConfigurationSource is fixed to 'group' for this build. + result[PARAMS_NAMES.ABTestingConfigurationSource] = 'group'; prepareData(data, result); fillEidsData(result); @@ -470,41 +312,13 @@ function prepareData(data: any, result: Record): void { const pos = getUnitPosition(pbjs, data.adUnitCode); if (typeof pos === 'number') result.pos = pos; } - if (data.size) { - result.size = data.size; - } - if (typeof data.pos === 'number') { - result.pos = data.pos; - } else if (data.adUnitCode) { - const pos = getUnitPosition(pbjs, data.adUnitCode); - if (typeof pos === 'number') result.pos = pos; - } result.prebidAuctionId = data.auctionId || data.prebidAuctionId; if (adTypeValue) result[PARAMS_NAMES.adType] = adTypeValue; - switch (iiqAnalyticsAnalyticsAdapter.initOptions.adUnitConfig) { - case 1: - // adUnitCode or placementId - result.placementId = data.adUnitCode || extractPlacementId(data) || ''; - break; - case 2: - // placementId or adUnitCode - result.placementId = extractPlacementId(data) || data.adUnitCode || ''; - break; - case 3: - // Only adUnitCode - result.placementId = data.adUnitCode || ''; - break; - case 4: - // Only placementId - result.placementId = extractPlacementId(data) || ''; - break; - default: - // Default (like in case #1) - result.placementId = data.adUnitCode || extractPlacementId(data) || ''; - } + // adUnitConfig is fixed to the default (adUnitCode, falling back to placementId). + result.placementId = data.adUnitCode || extractPlacementId(data) || ''; result.biddingPlatformId = data.biddingPlatformId || 1; @@ -539,18 +353,15 @@ function getDefaultDataObject(): Record { }; } -function constructFullUrl(data: Record): any { +function constructFullUrl(data: Record): { url: string } { const report: string[] = []; - const reportMethod = iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod; const partnerData = (window as any)[identityGlobalName as string]?.partnerData; - const currentBrowserLowerCase = detectBrowser(); const partnerAuctionId = data?.partnerAuctionId; const encodedData = btoa(JSON.stringify(data)); report.push(encodedData); const cmpData = getCmpData(); - const [reportEndpoint, region] = getDataForDefineURL(); - const baseUrl = reportingServerAddress(reportEndpoint, region); + const baseUrl = reportingServerAddress(); let url = baseUrl + @@ -581,22 +392,9 @@ function constructFullUrl(data: Record): any { (cmpData.gdprApplies && isValidValue(cmpData.tcfApiVersion) ? '&tcfv=' + encodeURIComponent(cmpData.tcfApiVersion as string) : ''); url = appendSPData(url, partnerData); - url = appendVrrefAndFui(url, iiqAnalyticsAnalyticsAdapter.initOptions.domainName); + url = appendVrrefAndFui(url); + url += '&payload=' + encodeURIComponent(JSON.stringify(report)); - if (reportMethod !== 'POST') { - url += '&payload=' + encodeURIComponent(JSON.stringify(report)); - } - - url = handleAdditionalParams( - currentBrowserLowerCase, - url, - 2, - iiqAnalyticsAnalyticsAdapter.initOptions.additionalParams - ); - - if (reportMethod === 'POST') { - return { url, method: 'POST', payload: JSON.stringify(report) }; - } return { url }; } diff --git a/modules/intentIqIdSystem.ts b/modules/intentIqIdSystem.ts index 34f6c89bed8..c3e72e23d95 100644 --- a/modules/intentIqIdSystem.ts +++ b/modules/intentIqIdSystem.ts @@ -5,10 +5,9 @@ * @requires module:modules/userId */ -import { isNumber, isStr, logError } from '../src/utils.js'; +import { logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import { detectBrowser } from '../libraries/intentIqUtils/detectBrowserUtils.ts'; import { appendSPData } from '../libraries/intentIqUtils/urlUtils.ts'; import { isCHSupported } from '../libraries/intentIqUtils/chUtils.ts'; import { appendVrrefAndFui } from '../libraries/intentIqUtils/getRefferer.ts'; @@ -25,14 +24,11 @@ import { CLIENT_HINTS_KEY, FIRST_PARTY_KEY, GVLID, - VERSION, INVALID_ID, SYNC_REFRESH_MILL, META_DATA_CONSTANT, PREBID, - HOURS_72, CH_KEYS, DEFAULT_PERCENTAGE, WITH_IIQ + VERSION, INVALID_ID, PREBID, + HOURS_72, CH_KEYS, WITH_IIQ, WITHOUT_IIQ } from '../libraries/intentIqConstants/intentIqConstants.ts'; -import { SYNC_KEY } from '../libraries/intentIqUtils/getSyncKey.ts'; -import { getIiqServerAddress, iiqPixelServerAddress } from '../libraries/intentIqUtils/intentIqConfig.ts'; -import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.ts'; +import { getIiqServerAddress } from '../libraries/intentIqUtils/intentIqConfig.ts'; import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.ts'; -import { defineABTestingGroup, IntentIqABConfigSource } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; export type IntentIqIdSystemModuleName = 'intentIqId'; @@ -44,8 +40,8 @@ export interface IntentIqIdSystemParams { /** * Invoked when the identity lookup completes or times out. - * Receives the resolved EID payload or an empty string when the browser is - * blacklisted or the user is opted out. + * Receives the resolved EID payload or an empty string when the user is + * opted out. */ callback?: (data: { eids: unknown[] } | string) => void; @@ -56,65 +52,13 @@ export interface IntentIqIdSystemParams { timeoutInMillis?: number; /** - * Comma-separated list of browser names (lowercase) that should be - * excluded from identity resolution, e.g. `'chrome,safari'`. - */ - browserBlackList?: string; - - /** - * Publisher domain name, used to build the referrer URL parameter. - */ - domainName?: string; - - /** - * When `true`, first-party data is stored under a partner-specific key so - * multiple IntentIQ configurations on the same page do not collide. - */ - siloEnabled?: boolean; - - /** - * Called whenever the resolved A/B group changes. - * Receives the new group (`'A'` | `'B'`) and the server termination-cause - * code when available. - */ - groupChanged?: (group: 'A' | 'B', terminationCause?: number) => void; - - /** - * Percentage of users placed in the WITH_IIQ (group A) cohort. - * Accepts 0–100; values outside that range are clamped. Defaults to 95. - * Only used when `ABTestingConfigurationSource` is `'percentage'` or - * `'IIQServer'` (no prior server termination cause). - */ - abPercentage?: number; - - /** - * Determines how the A/B test group is assigned. Defaults to `'IIQServer'`. - */ - ABTestingConfigurationSource?: IntentIqABConfigSource; - - /** - * Explicit A/B group override. Only used when - * `ABTestingConfigurationSource` is `'group'`. + * Explicit A/B group override. This build always assigns the A/B test + * group directly from `group` (equivalent to a fixed + * `ABTestingConfigurationSource: 'group'`), independent of the server + * termination cause. */ group?: 'A' | 'B'; - /** - * Human-readable metadata tag describing the integration source - * (e.g. `'prebid'`, `'amp'`). Translated to a numeric code internally. - */ - sourceMetaData?: string; - - /** - * Numeric metadata code for the integration source when a specific - * override is required. - */ - sourceMetaDataExternal?: number; - - /** - * Freeform key-value pairs appended to every pixel request. - */ - additionalParams?: Record; - /** * Timeout in milliseconds for fetching Client Hints before falling back * to an empty string. Defaults to 10 ms. @@ -131,12 +75,7 @@ export interface IntentIqIdSystemParams { * by the IntentIQ server. */ partnerClientIdType?: number; - - /** - * Partner-supplied Advertiser ID - */ - pai?: string; -} +}; declare module './userId/spec' { interface UserId { @@ -165,8 +104,6 @@ const encoderCH: Record = { wow64: 7, fullVersionList: 8 }; -let sourceMetaData: number | string | undefined; -let sourceMetaDataExternal: number | undefined; let globalName = ''; let FIRST_PARTY_KEY_FINAL = FIRST_PARTY_KEY; @@ -180,12 +117,6 @@ let partnerData: any; let clientHints: string | null | undefined; let actualABGroup: IntentIqIdSystemParams['group'] | undefined; -function getEffectiveAbPercentage(abPercentage: unknown): number { - const n = Number(abPercentage); - if (!Number.isFinite(n)) return DEFAULT_PERCENTAGE; - return Math.max(0, Math.min(100, n)); -} - /** * Generate standard UUID string * @return {string} @@ -200,11 +131,6 @@ function generateGUID(): string { return guid; } -function addUniquenessToUrl(url: string): string { - url += '&tsrnd=' + Math.floor(Math.random() * 1000) + '_' + new Date().getTime(); - return url; -} - function appendFirstPartyData(url: string, firstPartyData: any, partnerData: any): string { url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : ''; url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : ''; @@ -249,31 +175,6 @@ function appendCounters(url: string): string { return url; } -/** - * Translate and validate sourceMetaData - */ -export function translateMetadata(data: string): number { - try { - const d = data.split('.'); - return ( - ((+d[0] * META_DATA_CONSTANT + +d[1]) * META_DATA_CONSTANT + +d[2]) * META_DATA_CONSTANT + - +d[3] - ); - } catch (e) { - return NaN; - } -} - -/** - * Add sourceMetaData to URL if valid - */ -function addMetaData(url: string, data: unknown): string { - if (typeof data !== 'number' || isNaN(data)) { - return url; - } - return url + '&fbp=' + data; -} - export function initializeGlobalIIQ(partnerId: number): boolean { if (!globalName || !(window as any)[globalName]) { globalName = `iiq_identity_${partnerId}`; @@ -283,53 +184,6 @@ export function initializeGlobalIIQ(partnerId: number): boolean { return false; } -export function createPixelUrl(firstPartyData: any, clientHints: string, configParams: any, partnerData: any, cmpData: any): string { - const browser = detectBrowser(); - - let url = iiqPixelServerAddress(configParams); - url += '/profiles_engine/ProfilesEngineServlet?at=20&mi=10&secure=1'; - url += '&dpi=' + configParams.partner; - url = appendFirstPartyData(url, firstPartyData, partnerData); - url = appendPartnersFirstParty(url, configParams); - url = addUniquenessToUrl(url); - url += partnerData?.clientType ? '&idtype=' + partnerData.clientType : ''; - url += VERSION ? '&jsver=' + VERSION : ''; - if (clientHints) url += '&uh=' + encodeURIComponent(clientHints); - url = appendVrrefAndFui(url, configParams.domainName); - url = appendCMPData(url, cmpData); - url = addMetaData(url, sourceMetaDataExternal || sourceMetaData); - url = handleAdditionalParams(browser, url, 0, configParams.additionalParams); - url = appendSPData(url, partnerData); - url += '&source=' + PREBID; - url += actualABGroup ? '&testGroup=' + encodeURIComponent(actualABGroup) : ''; - if (isNumber(configParams.abPercentage)) { - url += '&testPercentage=' + encodeURIComponent(getEffectiveAbPercentage(configParams.abPercentage)); - } - url += '&isInTestGroup=' + (actualABGroup === WITH_IIQ); - return url; -} - -function sendSyncRequest(allowedStorage: any, url: string, partner: number, firstPartyData: any, newUser: boolean): void { - const lastSyncDate: any = Number(readData(SYNC_KEY(partner) || '', allowedStorage)) || false; - const lastSyncElapsedTime = Date.now() - lastSyncDate; - - if (firstPartyData.isOptedOut) { - const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL; - if (newUser || needToDoSync) { - ajax(url, () => { - }, undefined, { method: 'GET', withCredentials: true }); - if (firstPartyData?.date) { - firstPartyData.date = Date.now(); - storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); - } - } - } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) { - storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage); - ajax(url, () => { - }, undefined, { method: 'GET', withCredentials: true }); - } -} - /** * Processes raw client hints data into a structured format. * @param {object} clientHints - Raw client hints data @@ -428,11 +282,6 @@ export const intentIqIdSubmodule = { let callbackFired = false; let runtimeEids: any = { eids: [] }; - const groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined; - const siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false; - sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData as string) : ''; - sourceMetaDataExternal = isNumber(configParams.sourceMetaDataExternal) ? configParams.sourceMetaDataExternal : undefined; - const additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined; const chTimeout = Number(configParams?.chTimeout) >= 0 ? Number(configParams.chTimeout) : 10; PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`; @@ -441,21 +290,15 @@ export const intentIqIdSubmodule = { let rrttStrtTime = 0; let shouldCallServer = false; - FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`; const cmpData = getCmpData(); const gdprDetected = cmpData.gdprString; firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage) as string); - const currentBrowserLowerCase = detectBrowser(); - const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : ''; - const isBlacklisted = browserBlackList?.includes(currentBrowserLowerCase); - if (!isBlacklisted) { - actualABGroup = defineABTestingGroup(configParams, partnerData?.terminationCause); - if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); - } else { - actualABGroup = undefined; - } - let newUser = false; + // ABTestingConfigurationSource is fixed to 'group' for this build: the group + // is taken directly from configParams.group, independent of the server tc. + actualABGroup = typeof configParams.group === 'string' && configParams.group.toUpperCase() === WITHOUT_IIQ + ? WITHOUT_IIQ + : WITH_IIQ; callbackTimeoutID = setTimeout(() => { firePartnerCallback(); @@ -473,7 +316,6 @@ export const intentIqIdSubmodule = { // when opted out, pcid/pcidDate are not persisted to device, so the runtime // value is regenerated each session without overwriting persisted fields. firstPartyData = firstPartyData ? { ...firstPartyData, ...newObj } : newObj; - newUser = true; storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); } else if (!firstPartyData.pcidDate) { firstPartyData.pcidDate = Date.now(); @@ -546,8 +388,6 @@ export const intentIqIdSubmodule = { (window as any)[globalName].firstPartyData = firstPartyData; (window as any)[globalName].clientHints = clientHints; (window as any)[globalName].actualABGroup = actualABGroup; - (window as any)[globalName].abPercentage = getEffectiveAbPercentage(configParams.abPercentage); - (window as any)[globalName].userProvidedAbPercentage = configParams.abPercentage; } } @@ -579,29 +419,6 @@ export const intentIqIdSubmodule = { firePartnerCallback(); } - function buildAndSendPixel(ch: string): void { - const url = createPixelUrl(firstPartyData, ch, configParams, partnerData, cmpData); - sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser); - } - - // Check if current browser is in blacklist - if (isBlacklisted) { - logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.'); - if (configParams.callback) configParams.callback(''); - - if (chSupported) { - if (clientHints) { - buildAndSendPixel(clientHints); - } else { - waitOnCH(chTimeout) - .then((ch: any) => buildAndSendPixel(ch || '')); - } - } else { - buildAndSendPixel(''); - } - return; - } - if (!shouldCallServer) { firePartnerCallback(); updateCountersAndStore(runtimeEids, allowedStorage, partnerData); @@ -612,28 +429,22 @@ export const intentIqIdSubmodule = { // use protocol relative urls for http or https let url = `${getIiqServerAddress(configParams as any)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; - url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; url = appendFirstPartyData(url, firstPartyData, partnerData); url = appendPartnersFirstParty(url, configParams); url += (partnerData.cttl) ? '&cttl=' + encodeURIComponent(partnerData.cttl) : ''; url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : ''; url = appendCMPData(url, cmpData); - url += '&japs=' + encodeURIComponent(configParams.siloEnabled === true); + url += '&japs=false'; url = appendCounters(url); url += VERSION ? '&jsver=' + VERSION : ''; url += actualABGroup ? '&testGroup=' + encodeURIComponent(actualABGroup) : ''; - url = addMetaData(url, sourceMetaDataExternal || sourceMetaData); - if (isNumber(configParams.abPercentage)) { - url += '&testPercentage=' + encodeURIComponent(getEffectiveAbPercentage(configParams.abPercentage)); - } - url = handleAdditionalParams(currentBrowserLowerCase, url, 1, additionalParams); url = appendSPData(url, partnerData); url += '&source=' + PREBID; - url += '&ABTestingConfigurationSource=' + configParams.ABTestingConfigurationSource; + url += '&ABTestingConfigurationSource=group'; url += '&abtg=' + encodeURIComponent(actualABGroup as string); // Add vrref and fui to the URL - url = appendVrrefAndFui(url, configParams.domainName); + url = appendVrrefAndFui(url); const storeFirstPartyData = (): void => { partnerData.eidl = runtimeEids?.eids?.length || -1; @@ -664,10 +475,10 @@ export const intentIqIdSubmodule = { } else partnerData.cttl = HOURS_72; if ('tc' in respJson) { + // ABTestingConfigurationSource is fixed to 'group' for this build, so the + // group never depends on the server's termination cause — only store it + // for downstream reporting (e.g. the analytics adapter). partnerData.terminationCause = respJson.tc; - actualABGroup = defineABTestingGroup(configParams, respJson.tc,); - - if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); } if ('isOptedOut' in respJson) { if (respJson.isOptedOut !== firstPartyData.isOptedOut) { diff --git a/test/spec/modules/intentIqAnalyticsAdapter_spec.js b/test/spec/modules/intentIqAnalyticsAdapter_spec.js index dca38ab01a0..a4fb5965b2f 100644 --- a/test/spec/modules/intentIqAnalyticsAdapter_spec.js +++ b/test/spec/modules/intentIqAnalyticsAdapter_spec.js @@ -5,7 +5,6 @@ import iiqAnalyticsAnalyticsAdapter, { } from "modules/intentIqAnalyticsAdapter.js"; import * as utils from "src/utils.js"; import { server } from "test/mocks/xhr.js"; -import { config } from "src/config.js"; import { EVENTS } from "src/constants.js"; import * as events from "src/events.js"; import { getGlobal } from "../../../src/prebidGlobal.js"; @@ -14,9 +13,7 @@ import { PREBID, VERSION, WITHOUT_IIQ, - AB_CONFIG_SOURCE, } from "../../../libraries/intentIqConstants/intentIqConstants.js"; -import * as detectBrowserUtils from "../../../libraries/intentIqUtils/detectBrowserUtils.js"; import { getCurrentUrl, appendVrrefAndFui, @@ -27,8 +24,6 @@ import { gdprDataHandler, } from "../../../src/consentHandler.js"; -let getConfigStub; -let userIdConfigForTest; const partner = 10; const identityName = `iiq_identity_${partner}`; const defaultIdentityObject = { @@ -64,58 +59,17 @@ const defaultIdentityObject = { 8: '"Chromium";v="142.0.7444.60", "Google Chrome";v="142.0.7444.60", "Not_A Brand";v="99.0.0.0"', }), }; -const regionCases = [ - { - name: 'default (no region)', - region: undefined, - expectedEndpoint: 'https://reports.intentiq.com/report' - }, - { - name: 'apac', - region: 'apac', - expectedEndpoint: 'https://reports-apac.intentiq.com/report' - }, - { - name: 'emea', - region: 'emea', - expectedEndpoint: 'https://reports-emea.intentiq.com/report' - }, - { - name: 'gdpr', - region: 'gdpr', - expectedEndpoint: 'https://reports-gdpr.intentiq.com/report' - } -]; const version = VERSION; const REPORT_ENDPOINT = "https://reports.intentiq.com/report"; -const REPORT_SERVER_ADDRESS = "https://test-reports.intentiq.com/report"; - const randomVal = () => Math.floor(Math.random() * 100000) + 1; const getDefaultConfig = () => { return { partner, - manualWinReportEnabled: false, }; }; -const getUserConfigWithReportingServerAddress = () => [ - { - 'name': 'intentIqId', - 'params': { - 'partner': partner, - 'unpack': null, - }, - 'storage': { - 'type': 'html5', - 'name': 'intentIqId', - 'expires': 60, - 'refreshInSeconds': 14400 - } - } -]; - const getWonRequest = () => ({ bidderCode: "pubmatic", width: 728, @@ -158,33 +112,18 @@ const enableAnalyticWithSpecialOptions = (receivedOptions) => { }); }; +const reportWin = (data) => window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin(data); + describe("IntentIQ tests all", function () { let logErrorStub; let getWindowSelfStub; let getWindowTopStub; let getWindowLocationStub; - let detectBrowserStub; beforeEach(function () { logErrorStub = sinon.stub(utils, "logError"); sinon.stub(events, "getEvents").returns([]); - if (config.getConfig && config.getConfig.restore) { - config.getConfig.restore(); - } - - iiqAnalyticsAnalyticsAdapter.initOptions = { - lsValueInitialized: false, - partner: null, - fpid: null, - userGroup: null, - currentGroup: null, - dataInLs: null, - eidl: null, - lsIdsInitialized: false, - manualWinReportEnabled: false, - domainName: null, - }; iiqAnalyticsAnalyticsAdapter.enableAnalytics({ provider: "iiqAnalytics", options: getDefaultConfig() @@ -198,11 +137,9 @@ describe("IntentIQ tests all", function () { afterEach(function () { logErrorStub.restore(); - if (getConfigStub && getConfigStub.restore) getConfigStub.restore(); if (getWindowSelfStub) getWindowSelfStub.restore(); if (getWindowTopStub) getWindowTopStub.restore(); if (getWindowLocationStub) getWindowLocationStub.restore(); - if (detectBrowserStub) detectBrowserStub.restore(); events.getEvents.restore(); iiqAnalyticsAnalyticsAdapter.disableAnalytics(); if (iiqAnalyticsAnalyticsAdapter.track.restore) { @@ -210,30 +147,18 @@ describe("IntentIQ tests all", function () { } localStorage.clear(); server.reset(); - delete window[`iiq_identity_${partner}`]; + delete window[identityName]; }); - it("should send POST request with payload in request body if reportMethod is POST", function () { - enableAnalyticWithSpecialOptions({ - reportMethod: "POST", - }); - const wonRequest = getWonRequest(); - - events.emit(EVENTS.BID_WON, wonRequest); - - const request = server.requests[0]; - - const expectedData = preparePayload(wonRequest); - const expectedPayload = `["${btoa(JSON.stringify(expectedData))}"]`; - - expect(request.method).to.equal("POST"); - expect(request.requestBody).to.equal(expectedPayload); + it("should not send any request on BID_WON event (reporting is manual-only)", function () { + events.emit(EVENTS.BID_WON, getWonRequest()); + expect(server.requests.length).to.equal(0); }); - it("should send GET request with payload in query string if reportMethod is NOT provided", function () { + it("should send GET request with payload in query string when reporting a win", function () { const wonRequest = getWonRequest(); - events.emit(EVENTS.BID_WON, wonRequest); + reportWin(wonRequest); const request = server.requests[0]; @@ -255,7 +180,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876" }); const expectedVrref = getWindowLocationStub().href; - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests.length).to.be.above(0); const request = server.requests[0]; @@ -274,13 +199,13 @@ describe("IntentIQ tests all", function () { ); }); - it("should include adType in payload when present in BID_WON event", function () { + it("should include adType in payload when reporting a win", function () { getWindowLocationStub = sinon .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876/" }); const bidWonEvent = { ...getWonRequest(), mediaType: "video" }; - events.emit(EVENTS.BID_WON, bidWonEvent); + reportWin(bidWonEvent); const request = server.requests[0]; const urlParams = new URL(request.url); @@ -291,38 +216,14 @@ describe("IntentIQ tests all", function () { expect(payloadDecoded).to.have.property("adType", bidWonEvent.mediaType); }); - it("should include adType in payload when present in reportExternalWin event", function () { - enableAnalyticWithSpecialOptions({ manualWinReportEnabled: true }); - getWindowLocationStub = sinon - .stub(utils, "getWindowLocation") - .returns({ href: "http://localhost:9876/" }); - const externalWinEvent = { cpm: 1, currency: "USD", adType: "banner" }; - - events.emit(EVENTS.BID_REQUESTED); - - window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin( - externalWinEvent - ); - - const request = server.requests[0]; - const urlParams = new URL(request.url); - const payloadEncoded = urlParams.searchParams.get("payload"); - const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0])); - - expect(server.requests.length).to.be.above(0); - expect(payloadDecoded).to.have.property("adType", externalWinEvent.adType); - }); - - it("should get pos from pbjs.adUnits when BID_WON has no pos", function () { + it("should get pos from pbjs.adUnits when there is no pos on the win event", function () { const pbjs = getGlobal(); const prevAdUnits = pbjs.adUnits; pbjs.adUnits = Array.isArray(pbjs.adUnits) ? pbjs.adUnits : []; pbjs.adUnits.push({ code: "myVideoAdUnit", mediaTypes: { video: { pos: 777 } } }); - enableAnalyticWithSpecialOptions({ manualWinReportEnabled: false }); - - events.emit(EVENTS.BID_WON, { + reportWin({ ...getWonRequest(), adUnitCode: "myVideoAdUnit", mediaType: "video" @@ -338,11 +239,9 @@ describe("IntentIQ tests all", function () { }); it("should get pos from reportExternalWin when present", function () { - enableAnalyticWithSpecialOptions({ manualWinReportEnabled: true }); - const winPos = 999; - window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ + reportWin({ adUnitCode: "myVideoAdUnit", bidderCode: "appnexus", cpm: 1.5, @@ -361,37 +260,13 @@ describe("IntentIQ tests all", function () { expect(payloadDecoded.pos).to.equal(winPos); }); - it("should initialize with default configurations", function () { - expect(iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized).to.be - .false; - }); - - it("should handle BID_WON event with group configuration from local storage", function () { - window[`iiq_identity_${partner}`].firstPartyData = { - ...window[`iiq_identity_${partner}`].firstPartyData, - group: "B", - }; - - const expectedVrref = encodeURIComponent("http://localhost:9876/"); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - expect(server.requests.length).to.be.above(0); - const request = server.requests[0]; - expect(request.url).to.contain( - "https://reports.intentiq.com/report?pid=" + partner + "&mct=1" - ); - expect(request.url).to.contain(`&jsver=${version}`); - expect(request.url).to.contain(`&vrref=${expectedVrref}`); - }); - - it("should handle BID_WON event with default group configuration", function () { + it("should report a win with default group configuration", function () { const spdData = "server provided data"; const expectedSpdEncoded = encodeURIComponent(spdData); window[identityName].partnerData.spd = spdData; const wonRequest = getWonRequest(); - events.emit(EVENTS.BID_WON, wonRequest); + reportWin(wonRequest); expect(server.requests.length).to.be.above(0); const request = server.requests[0]; @@ -400,8 +275,7 @@ describe("IntentIQ tests all", function () { const payload = encodeURIComponent(JSON.stringify([base64String])); const expectedUrl = appendVrrefAndFui( REPORT_ENDPOINT + - `?pid=${partner}&mct=1&iiqid=${defaultIdentityObject.firstPartyData.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&uh=${encodeURIComponent(window[identityName].clientHints)}&gdpr=0&spd=${expectedSpdEncoded}`, - iiqAnalyticsAnalyticsAdapter.initOptions.domainName + `?pid=${partner}&mct=1&iiqid=${defaultIdentityObject.firstPartyData.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&uh=${encodeURIComponent(window[identityName].clientHints)}&gdpr=0&spd=${expectedSpdEncoded}` ); const urlWithPayload = expectedUrl + `&payload=${payload}`; @@ -428,7 +302,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876/" }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests.length).to.be.above(0); const request = server.requests[0]; @@ -461,7 +335,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876/" }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests.length).to.be.above(0); const request = server.requests[0]; @@ -474,50 +348,26 @@ describe("IntentIQ tests all", function () { gdprStub.restore(); }); - regionCases.forEach(({ name, region, expectedEndpoint }) => { - it(`should send request to region-specific report endpoint when region is "${name}"`, function () { - userIdConfigForTest = getUserConfigWithReportingServerAddress(); - getConfigStub = sinon.stub(config, "getConfig"); - getConfigStub.withArgs("userSync.userIds").callsFake(() => userIdConfigForTest); - - enableAnalyticWithSpecialOptions({ region }); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - expect(server.requests.length).to.be.above(0); - const request = server.requests[0]; - expect(request.url).to.contain(expectedEndpoint); - }); - }); - - it("should not send request if manualWinReportEnabled is true", function () { - iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = true; - events.emit(EVENTS.BID_WON, getWonRequest()); - expect(server.requests.length).to.equal(0); - }); - it("should handle initialization values from local storage", function () { - window[`iiq_identity_${partner}`].actualABGroup = WITHOUT_IIQ; + window[identityName].actualABGroup = WITHOUT_IIQ; - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup).to.equal( WITHOUT_IIQ ); expect(iiqAnalyticsAnalyticsAdapter.initOptions.fpid).to.be.not.null; }); - it("should handle reportExternalWin", function () { - events.emit(EVENTS.BID_REQUESTED); - iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = false; + it("should always report an external win regardless of any manualWinReportEnabled config", function () { expect( window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin ).to.be.a("function"); - expect( - window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ - cpm: 1, - currency: "USD", - }) - ).to.equal(false); + const result = reportWin({ + cpm: 1, + currency: "USD", + }); + expect(result).to.equal(true); + expect(server.requests.length).to.be.above(0); }); it("should return window.location.href when window.self === window.top", function () { @@ -559,133 +409,14 @@ describe("IntentIQ tests all", function () { ); }); - it("should not send request if the browser is in blacklist (chrome)", function () { - enableAnalyticWithSpecialOptions({ - browserBlackList: "ChrOmE" - }); - detectBrowserStub = sinon - .stub(detectBrowserUtils, "detectBrowser") - .returns("chrome"); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - expect(server.requests.length).to.equal(0); - }); - - it("should send request if the browser is not in blacklist (safari)", function () { - enableAnalyticWithSpecialOptions({ - browserBlackList: "chrome,firefox" - }); - - detectBrowserStub = sinon - .stub(detectBrowserUtils, "detectBrowser") - .returns("safari"); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - expect(server.requests.length).to.be.above(0); - const request = server.requests[0]; - expect(request.url).to.contain( - `https://reports.intentiq.com/report?pid=${partner}&mct=1` - ); - expect(request.url).to.contain(`&jsver=${version}`); - expect(request.url).to.contain( - `&vrref=${encodeURIComponent("http://localhost:9876/")}` - ); - expect(request.url).to.contain("&payload="); - expect(request.url).to.contain( - "iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344" - ); - }); - - it("should send request in reportingServerAddress no gdpr", function () { - detectBrowserStub = sinon - .stub(detectBrowserUtils, "detectBrowser") - .returns("safari"); - enableAnalyticWithSpecialOptions({ - reportingServerAddress: REPORT_SERVER_ADDRESS, - browserBlackList: "chrome,firefox" - }); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - expect(server.requests.length).to.be.above(0); - const request = server.requests[0]; - expect(request.url).to.contain(REPORT_SERVER_ADDRESS); - }); - it("should include source parameter in report URL", function () { - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); const request = server.requests[0]; expect(server.requests.length).to.be.above(0); expect(request.url).to.include(`&source=${PREBID}`); }); - it("should send additionalParams in report if valid and small enough", function () { - enableAnalyticWithSpecialOptions({ - additionalParams: [ - { - parameterName: "general", - parameterValue: "Lee", - destination: [0, 0, 1], - }, - ] - }); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - const request = server.requests[0]; - expect(request.url).to.include("general=Lee"); - }); - - it("should include domainName in both query and payload when fullUrl is empty (cross-origin)", function () { - const domainName = "mydomain-frame.com"; - - enableAnalyticWithSpecialOptions({ domainName }); - - getWindowTopStub = sinon.stub(utils, "getWindowTop").throws(new Error("cross-origin")); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - const request = server.requests[0]; - - // Query contain vrref=domainName - const parsedUrl = new URL(request.url); - const vrrefParam = parsedUrl.searchParams.get("vrref"); - - // Payload contain vrref=domainName - const payloadEncoded = parsedUrl.searchParams.get("payload"); - const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0])); - - expect(server.requests.length).to.be.above(0); - expect(vrrefParam).to.not.equal(null); - expect(decodeURIComponent(vrrefParam)).to.equal(domainName); - expect(parsedUrl.searchParams.get("fui")).to.equal("1"); - - expect(payloadDecoded).to.have.property("vrref"); - expect(decodeURIComponent(payloadDecoded.vrref)).to.equal(domainName); - }); - - it("should not send additionalParams in report if value is too large", function () { - const longVal = "x".repeat(5000000); - - enableAnalyticWithSpecialOptions({ - additionalParams: [ - { - parameterName: "general", - parameterValue: longVal, - destination: [0, 0, 1], - }, - ] - }); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - const request = server.requests[0]; - expect(request.url).not.to.include("general"); - }); - it("should include spd parameter from LS in report URL", function () { const spdObject = { foo: "bar", value: 42 }; const expectedSpdEncoded = encodeURIComponent(JSON.stringify(spdObject)); @@ -697,7 +428,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876/" }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); const request = server.requests[0]; @@ -714,7 +445,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876/" }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); const request = server.requests[0]; @@ -800,56 +531,26 @@ describe("IntentIQ tests all", function () { } ); - const adUnitConfigTests = [ + const placementIdTests = [ { - adUnitConfig: 1, - description: "should extract adUnitCode first (adUnitConfig = 1)", + description: "should extract adUnitCode when present", event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" }, expectedPlacementId: "adUnitCode-123", }, { - adUnitConfig: 1, - description: - "should extract placementId if there is no adUnitCode (adUnitConfig = 1)", + description: "should fall back to placementId when there is no adUnitCode", event: { placementId: "placementId-456" }, expectedPlacementId: "placementId-456", }, { - adUnitConfig: 2, - description: "should extract placementId first (adUnitConfig = 2)", - event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" }, - expectedPlacementId: "placementId-456", - }, - { - adUnitConfig: 2, - description: - "should extract adUnitCode if there is no placementId (adUnitConfig = 2)", - event: { adUnitCode: "adUnitCode-123" }, - expectedPlacementId: "adUnitCode-123", - }, - { - adUnitConfig: 3, - description: "should extract only adUnitCode (adUnitConfig = 3)", - event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" }, - expectedPlacementId: "adUnitCode-123", - }, - { - adUnitConfig: 4, - description: "should extract only placementId (adUnitConfig = 4)", - event: { adUnitCode: "adUnitCode-123", placementId: "placementId-456" }, - expectedPlacementId: "placementId-456", - }, - { - adUnitConfig: 1, description: - "should return empty placementId if neither adUnitCode or placementId exist", + "should return empty placementId if neither adUnitCode nor placementId exist", event: {}, expectedPlacementId: "", }, { - adUnitConfig: 1, description: - "should extract placementId from params array if no top-level adUnitCode or placementId exist (adUnitConfig = 1)", + "should extract placementId from nested params array if no top-level adUnitCode or placementId exist", event: { params: [{ someKey: "value" }, { placementId: "nested-placementId" }], }, @@ -857,34 +558,26 @@ describe("IntentIQ tests all", function () { }, ]; - adUnitConfigTests.forEach( - ({ adUnitConfig, description, event, expectedPlacementId }) => { - it(description, function () { - enableAnalyticWithSpecialOptions({ adUnitConfig }); + placementIdTests.forEach(({ description, event, expectedPlacementId }) => { + it(description, function () { + const testEvent = { ...getWonRequest(), ...event }; + reportWin(testEvent); - const testEvent = { ...getWonRequest(), ...event }; - events.emit(EVENTS.BID_WON, testEvent); - - const request = server.requests[0]; - const urlParams = new URL(request.url); - const encodedPayload = urlParams.searchParams.get("payload"); - const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0])); - - expect(server.requests.length).to.be.above(0); - expect(encodedPayload).to.exist; - expect(decodedPayload).to.have.property( - "placementId", - expectedPlacementId - ); - }); - } - ); + const request = server.requests[0]; + const urlParams = new URL(request.url); + const encodedPayload = urlParams.searchParams.get("payload"); + const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0])); - it("should include ABTestingConfigurationSource in payload when provided", function () { - const ABTestingConfigurationSource = "percentage"; - enableAnalyticWithSpecialOptions({ ABTestingConfigurationSource }); + expect(server.requests.length).to.be.above(0); + expect(decodedPayload).to.have.property( + "placementId", + expectedPlacementId + ); + }); + }); - events.emit(EVENTS.BID_WON, getWonRequest()); + it("should always include ABTestingConfigurationSource as 'group' in payload", function () { + reportWin(getWonRequest()); const request = server.requests[0]; const urlParams = new URL(request.url); @@ -894,35 +587,20 @@ describe("IntentIQ tests all", function () { expect(server.requests.length).to.be.above(0); expect(decodedPayload).to.have.property( "ABTestingConfigurationSource", - ABTestingConfigurationSource + "group" ); }); - it("should not include ABTestingConfigurationSource in payload when not provided", function () { - enableAnalyticWithSpecialOptions({}); - - events.emit(EVENTS.BID_WON, getWonRequest()); - - const request = server.requests[0]; - const urlParams = new URL(request.url); - const encodedPayload = urlParams.searchParams.get("payload"); - const decodedPayload = JSON.parse(atob(JSON.parse(encodedPayload)[0])); - - expect(server.requests.length).to.be.above(0); - expect(decodedPayload).to.not.have.property("ABTestingConfigurationSource"); - }); - - it("should use group from provided options when ABTestingConfigurationSource is 'group'", function () { + it("should use group provided by partner options in the payload", function () { const providedGroup = WITHOUT_IIQ; // Ensure actualABGroup is not set so group from options is used - delete window[`iiq_identity_${partner}`].actualABGroup; + delete window[identityName].actualABGroup; enableAnalyticWithSpecialOptions({ group: providedGroup, - ABTestingConfigurationSource: AB_CONFIG_SOURCE.GROUP, }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); const request = server.requests[0]; const urlParams = new URL(request.url); @@ -934,14 +612,10 @@ describe("IntentIQ tests all", function () { expect(decodedPayload).to.have.property("abGroup", providedGroup); }); - it("should include partnerAuctionId in query params and payload if provided by partner (GET)", function () { + it("should include partnerAuctionId in query params and payload if provided by partner", function () { const partnerAuctionId = "TEST-PAUCID-123"; - enableAnalyticWithSpecialOptions({ - manualWinReportEnabled: true, - reportMethod: "GET" - }); - window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ + reportWin({ cpm: 1, currency: "USD", adType: "banner", @@ -961,145 +635,6 @@ describe("IntentIQ tests all", function () { expect(payloadDecoded.partnerAuctionId).to.equal(partnerAuctionId); }); - it("should include partnerAuctionId in query params and payload if provided by partner (POST)", function () { - const partnerAuctionId = "TEST-PAUCID-123"; - enableAnalyticWithSpecialOptions({ - manualWinReportEnabled: true, - reportMethod: "POST" - }); - - window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ - cpm: 1, - currency: "USD", - adType: "banner", - partnerAuctionId - }); - - const request = server.requests[0]; - const url = new URL(request.url); - const paucidParam = url.searchParams.get("paucid"); - const bodyArray = JSON.parse(request.requestBody); - const payloadDecoded = JSON.parse(atob(bodyArray[0])); - - expect(request.requestBody).to.be.a('string'); - expect(JSON.parse(paucidParam)).to.deep.equal([partnerAuctionId]); - expect(payloadDecoded.partnerAuctionId).to.equal(partnerAuctionId); - }); - - it('should include abPercentage and userPercentage in payload when set in global identity object', function () { - window[identityName].abPercentage = 70; - window[identityName].userProvidedAbPercentage = 70; - events.emit(EVENTS.BID_WON, getWonRequest()); - - const request = server.requests[0]; - const url = new URL(request.url); - const decoded = JSON.parse(atob(JSON.parse(url.searchParams.get('payload'))[0])); - - expect(decoded.abPercentage).to.equal(70); - expect(decoded.userPercentage).to.equal(70); - }); - - it('should include abPercentage but not userPercentage in payload when abPercentage is set but user did not provide it', function () { - window[identityName].abPercentage = 95; - window[identityName].userProvidedAbPercentage = undefined; - events.emit(EVENTS.BID_WON, getWonRequest()); - - const request = server.requests[0]; - const url = new URL(request.url); - const decoded = JSON.parse(atob(JSON.parse(url.searchParams.get('payload'))[0])); - - expect(decoded.abPercentage).to.equal(95); - expect(decoded).to.not.have.property('userPercentage'); - }); - - describe('BID_REQUESTED CMP mismatch detection', function () { - let refreshUserIdsStub; - let gppStub, uspStub, gdprStub; - - beforeEach(function () { - getGlobal().refreshUserIds = sinon.stub(); - refreshUserIdsStub = getGlobal().refreshUserIds; - gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); - uspStub = sinon.stub(uspDataHandler, 'getConsentData').returns(null); - gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns(null); - }); - - afterEach(function () { - delete getGlobal().refreshUserIds; - gppStub.restore(); - uspStub.restore(); - gdprStub.restore(); - }); - - it('should call refreshUserIds with intentIqId when gdprString changes', function () { - window[identityName].firstPartyData.gdprString = 'oldConsent'; - gdprStub.returns({ consentString: 'newConsent', gdprApplies: true }); - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.calledOnce).to.be.true; - expect(refreshUserIdsStub.calledWith({ submoduleNames: ['intentIqId'] })).to.be.true; - }); - - it('should call refreshUserIds when uspString changes from valid to another valid value', function () { - window[identityName].firstPartyData.uspString = '1YNN'; - uspStub.returns('1NNN'); - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.calledOnce).to.be.true; - }); - - it('should not call refreshUserIds when CMP data matches stored firstPartyData', function () { - window[identityName].firstPartyData.gdprString = 'sameConsent'; - window[identityName].firstPartyData.gppString = null; - window[identityName].firstPartyData.uspString = null; - gdprStub.returns({ consentString: 'sameConsent', gdprApplies: true }); - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.called).to.be.false; - }); - - it('should not call refreshUserIds when null and empty string are compared (both invalid)', function () { - window[identityName].firstPartyData.gdprString = null; - window[identityName].firstPartyData.gppString = null; - window[identityName].firstPartyData.uspString = null; - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.called).to.be.false; - }); - - it('should not call refreshUserIds when stored value is "undefined" string and current is null (both invalid)', function () { - window[identityName].firstPartyData.gdprString = ''; - window[identityName].firstPartyData.gppString = 'undefined'; - window[identityName].firstPartyData.uspString = 'undefined'; - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.called).to.be.false; - }); - - it('should call refreshUserIds when valid gdprString is replaced by null (invalid)', function () { - window[identityName].firstPartyData.gdprString = 'validConsent'; - window[identityName].firstPartyData.gppString = null; - window[identityName].firstPartyData.uspString = null; - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.calledOnce).to.be.true; - }); - - it('should not call refreshUserIds when firstPartyData is absent in global object', function () { - delete window[identityName].firstPartyData; - - events.emit(EVENTS.BID_REQUESTED); - - expect(refreshUserIdsStub.called).to.be.false; - }); - }); - describe('constructFullUrl CMP isValidValue filtering', function () { let gppStub, uspStub, gdprStub; @@ -1114,7 +649,7 @@ describe("IntentIQ tests all", function () { gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns(null); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests[0].url).to.not.include('us_privacy'); }); @@ -1124,7 +659,7 @@ describe("IntentIQ tests all", function () { gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns(null); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests[0].url).to.not.include('us_privacy'); }); @@ -1134,7 +669,7 @@ describe("IntentIQ tests all", function () { gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns(null); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests[0].url).to.not.include('&gpp='); }); @@ -1144,7 +679,7 @@ describe("IntentIQ tests all", function () { gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns(null); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests[0].url).to.not.include('gdpr_consent'); }); @@ -1154,7 +689,7 @@ describe("IntentIQ tests all", function () { gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns({ consentString: 'undefined', gdprApplies: false }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests[0].url).to.not.include('gdpr_consent'); }); @@ -1165,7 +700,7 @@ describe("IntentIQ tests all", function () { gppStub = sinon.stub(gppDataHandler, 'getConsentData').returns(null); gdprStub = sinon.stub(gdprDataHandler, 'getConsentData').returns({ consentString, gdprApplies: true }); - events.emit(EVENTS.BID_WON, getWonRequest()); + reportWin(getWonRequest()); expect(server.requests[0].url).to.include(`gdpr_consent=${encodeURIComponent(consentString)}`); expect(server.requests[0].url).to.include('gdpr=1'); diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 45123258d46..26eda9da704 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -6,25 +6,21 @@ import { intentIqIdSubmodule, handleClientHints, firstPartyData as moduleFPD, - isCMPStringTheSame, createPixelUrl, translateMetadata, + isCMPStringTheSame, initializeGlobalIIQ } from '../../../modules/intentIqIdSystem.js'; import { storage, readData, storeData } from '../../../libraries/intentIqUtils/storageUtils.js'; import { gppDataHandler, uspDataHandler, gdprDataHandler } from '../../../src/consentHandler.js'; import { clearAllCookies } from '../../helpers/cookies.js'; -import { detectBrowser, detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/intentIqUtils/detectBrowserUtils.js'; -import { CLIENT_HINTS_KEY, FIRST_PARTY_KEY, PREBID, WITH_IIQ, WITHOUT_IIQ } from '../../../libraries/intentIqConstants/intentIqConstants.js'; +import { CLIENT_HINTS_KEY, FIRST_PARTY_KEY, PREBID } from '../../../libraries/intentIqConstants/intentIqConstants.js'; import { decryptData } from '../../../libraries/intentIqUtils/cryptionUtils.js'; const partner = 10; -const pai = '11'; const partnerClientId = '12'; const partnerClientIdType = 0; -const sourceMetaData = '1.1.1.1'; const defaultConfigParams = { params: { partner } }; -const paiConfigParams = { params: { partner, pai } }; const pcidConfigParams = { params: { partner, partnerClientIdType, partnerClientId } }; -const allConfigParams = { params: { partner, pai, partnerClientIdType, partnerClientId, sourceMetaData } }; +const allConfigParams = { params: { partner, partnerClientIdType, partnerClientId } }; const responseHeader = { 'Content-Type': 'application/json' }; export const testClientHints = { @@ -103,7 +99,6 @@ async function waitForClientHints() { } const testAPILink = 'https://new-test-api.intentiq.com'; -const syncTestAPILink = 'https://new-test-sync.intentiq.com'; const regionCases = [ { name: 'no region (default)', region: undefined, expected: 'https://api.intentiq.com' }, { name: 'apac', region: 'apac', expected: 'https://api-apac.intentiq.com' }, @@ -111,13 +106,6 @@ const regionCases = [ { name: 'gdpr', region: 'gdpr', expected: 'https://api-gdpr.intentiq.com' } ]; -const syncRegionCases = [ - { name: 'default', region: undefined, expected: 'https://sync.intentiq.com' }, - { name: 'apac', region: 'apac', expected: 'https://sync-apac.intentiq.com' }, - { name: 'emea', region: 'emea', expected: 'https://sync-emea.intentiq.com' }, - { name: 'gdpr', region: 'gdpr', expected: 'https://sync-gdpr.intentiq.com' }, -]; - describe('IntentIQ tests', function () { this.timeout(10000); let sandbox; @@ -224,7 +212,7 @@ describe('IntentIQ tests', function () { await waitForClientHints(); const request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -259,24 +247,6 @@ describe('IntentIQ tests', function () { expect(intentIqIdSubmodule.decode(undefined)).to.equal(undefined); }); - it('should send AT=20 request and send source in it', async function () { - const usedBrowser = 'chrome'; - intentIqIdSubmodule.getId({ - params: { - partner: 10, - browserBlackList: usedBrowser - } - }); - const currentBrowserLowerCase = detectBrowser(); - - if (currentBrowserLowerCase === usedBrowser) { - await waitForClientHints(); - const at20request = server.requests[0]; - expect(at20request.url).to.contain(`&source=${PREBID}`); - expect(at20request.url).to.contain(`at=20`); - } - }); - it('should send at=39 request and send source in it', async function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -287,22 +257,6 @@ describe('IntentIQ tests', function () { expect(request.url).to.contain(`&source=${PREBID}`); }); - it('should call the IntentIQ endpoint with only partner, pai', async function () { - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId(paiConfigParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - - const request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); - request.respond( - 200, - responseHeader, - JSON.stringify({}) - ); - expect(callBackSpy.calledOnce).to.be.true; - }); - it('should call the IntentIQ endpoint with only partner, pcid', async function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(pcidConfigParams).callback; @@ -320,22 +274,6 @@ describe('IntentIQ tests', function () { expect(callBackSpy.calledOnce).to.be.true; }); - it('should call the IntentIQ endpoint with partner, pcid, pai', async function () { - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); - expect(request.url).to.contain('&pcid=12'); - request.respond( - 200, - responseHeader, - JSON.stringify({}) - ); - expect(callBackSpy.calledOnce).to.be.true; - }); - it('should not throw Uncaught TypeError when IntentIQ endpoint returns empty response', async function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -372,7 +310,7 @@ describe('IntentIQ tests', function () { submoduleCallback(callBackSpy); await waitForClientHints(); const request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -388,7 +326,7 @@ describe('IntentIQ tests', function () { submoduleCallback(callBackSpy); await waitForClientHints(); const request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); request.respond( 200, responseHeader, @@ -405,7 +343,7 @@ describe('IntentIQ tests', function () { submoduleCallback(callBackSpy); await waitForClientHints(); const request = server.requests[0]; - expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&pai=11&iiqidtype=2&iiqpcid='); + expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid='); expect(request.url).to.contain('cttl=' + testLSValue.cttl); expect(request.url).to.contain('rrtt=' + testLSValue.rrtt); request.respond( @@ -423,16 +361,6 @@ describe('IntentIQ tests', function () { expect(returnedValue.id).to.deep.equal(JSON.parse(decryptData(testLSValueWithData.data)).eids); }); - it('should handle browser blacklisting', function () { - const configParamsWithBlacklist = { - params: { partner: partner, browserBlackList: 'chrome' } - }; - sinon.stub(navigator, 'userAgent').value('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'); - const submoduleCallback = intentIqIdSubmodule.getId(configParamsWithBlacklist); - expect(logErrorStub.calledOnce).to.be.true; - expect(submoduleCallback).to.be.undefined; - }); - it('should handle invalid JSON in readData', async function () { localStorage.setItem('_iiq_fdata_' + partner, 'invalid_json'); const callBackSpy = sinon.spy(); @@ -451,44 +379,6 @@ describe('IntentIQ tests', function () { expect(logErrorStub.called).to.be.true; }); - it('should send AT=20 request and send spd in it', async function () { - const spdValue = { foo: 'bar', value: 42 }; - const encodedSpd = encodeURIComponent(JSON.stringify(spdValue)); - localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ pcid: '123', spd: spdValue })); - - intentIqIdSubmodule.getId({ - params: { - partner: 10, - browserBlackList: 'chrome' - } - }); - - await waitForClientHints(); - - const at20request = server.requests[0]; - expect(at20request.url).to.contain(`&spd=${encodedSpd}`); - expect(at20request.url).to.contain(`at=20`); - }); - - it('should send AT=20 request and send spd string in it ', async function () { - const spdValue = 'server provided data'; - const encodedSpd = encodeURIComponent(spdValue); - localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ pcid: '123', spd: spdValue })); - - intentIqIdSubmodule.getId({ - params: { - partner: 10, - browserBlackList: 'chrome' - } - }); - - await waitForClientHints(); - - const at20request = server.requests[0]; - expect(at20request.url).to.contain(`&spd=${encodedSpd}`); - expect(at20request.url).to.contain(`at=20`); - }); - it('should send spd from firstPartyData in localStorage in at=39 request', async function () { const spdValue = { foo: 'bar', value: 42 }; const encodedSpd = encodeURIComponent(JSON.stringify(spdValue)); @@ -544,59 +434,7 @@ describe('IntentIQ tests', function () { expect(parsedLs.spd).to.deep.equal(spdValue); }); - describe('detectBrowserFromUserAgent', function () { - it('should detect Chrome browser', function () { - const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'; - const result = detectBrowserFromUserAgent(userAgent); - expect(result).to.equal('chrome'); - }); - - it('should detect Safari browser', function () { - const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15'; - const result = detectBrowserFromUserAgent(userAgent); - expect(result).to.equal('safari'); - }); - - it('should detect Firefox browser', function () { - const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'; - const result = detectBrowserFromUserAgent(userAgent); - expect(result).to.equal('firefox'); - }); - }); - - describe('detectBrowserFromUserAgentData', function () { - it('should detect Microsoft Edge browser', function () { - const userAgentData = { - brands: [ - { brand: 'Microsoft Edge', version: '91' }, - { brand: 'Chromium', version: '91' } - ] - }; - const result = detectBrowserFromUserAgentData(userAgentData); - expect(result).to.equal('edge'); - }); - - it('should detect Chrome browser', function () { - const userAgentData = { - brands: [ - { brand: 'Google Chrome', version: '91' }, - { brand: 'Chromium', version: '91' } - ] - }; - const result = detectBrowserFromUserAgentData(userAgentData); - expect(result).to.equal('chrome'); - }); - - it('should return unknown for unrecognized user agent data', function () { - const userAgentData = { - brands: [ - { brand: 'Unknown Browser', version: '1.0' } - ] - }; - const result = detectBrowserFromUserAgentData(userAgentData); - expect(result).to.equal('unknown'); - }); - + describe('server-call gating based on FPD/consent state', function () { it("Should call the server for new partner if FPD has been updated by other partner, and 72 hours have not yet passed.", async () => { const allowedStorage = ['html5']; const newPartnerId = 12345; @@ -935,26 +773,6 @@ describe('IntentIQ tests', function () { expect(request.url).to.contain(testAPILink); }); - it('should make request to correct address with iiqPixelServerAddress parameter', async function() { - const callbackConfigParams = { - params: { - partner: partner, - pai, - partnerClientIdType, - partnerClientId, - browserBlackList: 'Chrome', - iiqPixelServerAddress: syncTestAPILink, - callback: () => {} - } - }; - - intentIqIdSubmodule.getId({ ...callbackConfigParams }); - await waitForClientHints(); - - const request = server.requests[0]; - expect(request.url).to.contain(syncTestAPILink); - }); - regionCases.forEach(({ name, region, expected }) => { it(`should use region-specific api endpoint when region is "${name}"`, async function () { mockConsentHandlers(uspData, gppData, gdprData); // gdprApplies = true @@ -975,36 +793,6 @@ describe('IntentIQ tests', function () { expect(request.url).to.contain(expected); }); }); - - syncRegionCases.forEach(({ name, region, expected }) => { - it(`should use region-specific sync endpoint when region is "${name}"`, async function () { - let wasCallbackCalled = false; - - const callbackConfigParams = { - params: { - partner, - pai, - partnerClientIdType, - partnerClientId, - browserBlackList: 'Chrome', - region, - callback: () => { - wasCallbackCalled = true; - } - } - }; - - mockConsentHandlers(uspData, gppData, gdprData); - - intentIqIdSubmodule.getId(callbackConfigParams); - - await waitForClientHints(); - - const request = server.requests[0]; - expect(request.url).to.contain(expected); - expect(wasCallbackCalled).to.equal(true); - }); - }); }); it('should get and save client hints to storage', async () => { @@ -1021,77 +809,6 @@ describe('IntentIQ tests', function () { expect(savedClientHints).to.equal(expectedClientHints); }); - it('should add clientHints to the URL if provided', function () { - const firstPartyData = {}; - const clientHints = 'exampleClientHints'; - const configParams = { partner: 'testPartner', domainName: 'example.com' }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData); - - expect(url).to.include(`&uh=${encodeURIComponent(clientHints)}`); - }); - - it('should not add clientHints to the URL if not provided', function () { - const firstPartyData = {}; - const configParams = { partner: 'testPartner', domainName: 'example.com' }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, undefined, configParams, partnerData, cmpData); - - expect(url).to.not.include('&uh='); - }); - - it('should include testPercentage with configured abPercentage in pixel URL', function () { - const firstPartyData = {}; - const configParams = { partner: 'testPartner', domainName: 'example.com', abPercentage: 70 }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, undefined, configParams, partnerData, cmpData); - - expect(url).to.include('&testPercentage=70'); - }); - - it('should not include testPercentage when abPercentage is not configured in pixel URL', function () { - const firstPartyData = {}; - const configParams = { partner: 'testPartner', domainName: 'example.com' }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, undefined, configParams, partnerData, cmpData); - - expect(url).to.not.include('testPercentage'); - }); - - it('should include testPercentage=0 when abPercentage is explicitly 0 in pixel URL', function () { - const firstPartyData = {}; - const configParams = { partner: 'testPartner', domainName: 'example.com', abPercentage: 0 }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, undefined, configParams, partnerData, cmpData); - - expect(url).to.include('&testPercentage=0'); - }); - - it('should clamp abPercentage out of range in pixel URL', function () { - const firstPartyData = {}; - const configParams = { partner: 'testPartner', domainName: 'example.com', abPercentage: 150 }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, undefined, configParams, partnerData, cmpData); - - expect(url).to.include('&testPercentage=100'); - }); - - it('should include isInTestGroup in pixel URL', function () { - const firstPartyData = {}; - const configParams = { partner: 'testPartner', domainName: 'example.com', abPercentage: 100 }; - const partnerData = {}; - const cmpData = {}; - const url = createPixelUrl(firstPartyData, undefined, configParams, partnerData, cmpData); - - expect(url).to.include('&isInTestGroup='); - }); - it('should sends uh from LS immediately and later updates LS with fresh CH', async () => { localStorage.setItem(CLIENT_HINTS_KEY, 'OLD_CH_VALUE'); const callBackSpy = sinon.spy(); @@ -1161,86 +878,6 @@ describe('IntentIQ tests', function () { uadStub.restore(); }); - it('blacklist: should use uh from LS immediately and updates LS when CH resolves', async () => { - localStorage.setItem(CLIENT_HINTS_KEY, 'OLD_CH_VALUE'); - const { resolve, stub } = stubCHDeferred(); // getHighEntropyValues returns pending promise - const blk = detectBrowser(); - const cfg = { params: { ...defaultConfigParams.params, browserBlackList: blk, chTimeout: 50 } }; - - intentIqIdSubmodule.getId(cfg); - - const firstReq = server.requests[0]; - expect(firstReq).to.exist; - expect(firstReq.url).to.include('at=20'); - expect(firstReq.url).to.include('&uh=OLD_CH_VALUE'); - expect(server.requests.length).to.equal(1); - - // Now deliver fresh CH from browser and wait for background handlers - resolve(testClientHints); - await waitForClientHints(); - - // LS updated, network not re-fired - const expectedFresh = handleClientHints(testClientHints); - expect(readData(CLIENT_HINTS_KEY, ['html5'])).to.equal(expectedFresh); - expect(server.requests.length).to.equal(1); - - stub.restore(); - }); - - it('blacklist: should send sync with uh when CH supported and ready', async () => { - localStorage.removeItem(CLIENT_HINTS_KEY); - const expectedCH = handleClientHints(testClientHints); - - let uadStub = sinon.stub(navigator, 'userAgentData').value({ - getHighEntropyValues: async () => testClientHints - }); - - const blk = detectBrowser(); - const cfg = { - params: { - ...defaultConfigParams.params, - browserBlackList: blk, - chTimeout: 300 - } - }; - - intentIqIdSubmodule.getId(cfg); - await waitForClientHints(); - - const req = server.requests[0]; - expect(req).to.exist; - expect(req.url).to.include('at=20'); - expect(req.url).to.include(`&uh=${encodeURIComponent(expectedCH)}`); - expect(readData(CLIENT_HINTS_KEY, ['html5'])).to.equal(expectedCH); - - uadStub.restore(); - }); - - it('blacklist: sends sync with uh when CH supported and ready', async () => { - const expectedCH = handleClientHints(testClientHints); - Object.defineProperty(navigator, 'userAgentData', { - value: { getHighEntropyValues: async () => testClientHints }, - configurable: true - }); - const blk = detectBrowser(); - const cfg = { - params: { - ...defaultConfigParams.params, - browserBlackList: blk, - chTimeout: 300 - } - }; - - intentIqIdSubmodule.getId(cfg); - await waitForClientHints(); - - const req = server.requests[0]; - expect(req).to.exist; - expect(req.url).to.include('at=20'); - expect(req.url).to.include(`&uh=${encodeURIComponent(expectedCH)}`); - expect(readData(CLIENT_HINTS_KEY, ['html5'])).to.equal(expectedCH); - }); - it('should return true if CMP strings are the same', function () { const fpData = { gdprString: '123', gppString: '456', uspString: '789' }; const cmpData = { gdprString: '123', gppString: '456', uspString: '789' }; @@ -1332,190 +969,23 @@ describe('IntentIQ tests', function () { expect(isCMPStringTheSame(fpData, cmpData)).to.be.false; }); - describe('appendCMPData via createPixelUrl', function () { - const baseParams = { partner: 'testPartner', domainName: 'example.com' }; - - it('should not include us_privacy in URL when uspString is null', function () { - const cmpData = { uspString: null, gppString: null, gdprApplies: false, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.not.include('us_privacy'); - }); - - it('should not include us_privacy in URL when uspString is the string "undefined"', function () { - const cmpData = { uspString: 'undefined', gppString: null, gdprApplies: false, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.not.include('us_privacy'); - }); - - it('should include us_privacy in URL when uspString is a valid string', function () { - const cmpData = { uspString: '1NYN', gppString: null, gdprApplies: false, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.include(`&us_privacy=${encodeURIComponent('1NYN')}`); - }); - - it('should not include gpp in URL when gppString is null', function () { - const cmpData = { uspString: null, gppString: null, gdprApplies: false, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.not.include('&gpp='); - }); - - it('should not include gpp in URL when gppString is the string "undefined"', function () { - const cmpData = { uspString: null, gppString: 'undefined', gdprApplies: false, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.not.include('&gpp='); - }); - - it('should include gdpr=1 without gdpr_consent when gdprApplies is true and gdprString is null', function () { - const cmpData = { uspString: null, gppString: null, gdprApplies: true, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.include('&gdpr=1'); - expect(url).to.not.include('gdpr_consent'); - }); - - it('should include gdpr=1 without gdpr_consent when gdprApplies is true and gdprString is "undefined"', function () { - const cmpData = { uspString: null, gppString: null, gdprApplies: true, gdprString: 'undefined' }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.include('&gdpr=1'); - expect(url).to.not.include('gdpr_consent'); - }); - - it('should include gdpr=1 and gdpr_consent when gdprApplies is true and gdprString is valid', function () { - const cmpData = { uspString: null, gppString: null, gdprApplies: true, gdprString: 'validConsent' }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.include('&gdpr=1'); - expect(url).to.include(`&gdpr_consent=${encodeURIComponent('validConsent')}`); - }); - - it('should include gdpr=0 and no gdpr_consent when gdprApplies is false', function () { - const cmpData = { uspString: null, gppString: null, gdprApplies: false, gdprString: null }; - const url = createPixelUrl({}, undefined, baseParams, {}, cmpData); - expect(url).to.include('&gdpr=0'); - expect(url).to.not.include('gdpr_consent'); - }); - }); - it('should run callback from params', async () => { let wasCallbackCalled = false; const callbackConfigParams = { params: { partner: partner, - pai, partnerClientIdType, partnerClientId, - browserBlackList: 'Chrome', callback: () => { wasCallbackCalled = true; } } }; - await intentIqIdSubmodule.getId(callbackConfigParams); - expect(wasCallbackCalled).to.equal(true); - }); - - it('should send sourceMetaData in AT=39 if it exists in configParams', async function () { - const translatedMetaDataValue = translateMetadata(sourceMetaData); - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId(allConfigParams).callback; - submoduleCallback(callBackSpy); + intentIqIdSubmodule.getId(callbackConfigParams); await waitForClientHints(); - - const request = server.requests[0]; - - expect(request.url).to.include('?at=39'); - expect(request.url).to.include(`fbp=${translatedMetaDataValue}`); - }); - - it('should NOT send sourceMetaData and sourceMetaDataExternal in AT=39 if it is undefined', async function () { - const callBackSpy = sinon.spy(); - const configParams = { params: { ...allConfigParams.params, sourceMetaData: undefined } }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - - const request = server.requests[0]; - - expect(request.url).to.include('?at=39'); - expect(request.url).not.to.include('fbp='); - }); - - it('should NOT send sourceMetaData in AT=39 if value is NAN', async function () { - const callBackSpy = sinon.spy(); - const configParams = { params: { ...allConfigParams.params, sourceMetaData: NaN } }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=39'); - expect(request.url).not.to.include('fbp='); - }); - - it('should send sourceMetaData in AT=20 if it exists in configParams', async function () { - const translatedMetaDataValue = translateMetadata(sourceMetaData); - const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome' } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=20'); - expect(request.url).to.include(`fbp=${translatedMetaDataValue}`); - }); - - it('should NOT send sourceMetaData in AT=20 if value is NAN', async function () { - const configParams = { params: { ...allConfigParams.params, sourceMetaData: NaN, browserBlackList: 'chrome' } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=20'); - expect(request.url).to.not.include('&fbp='); - }); - - it('should send pcid and idtype in AT=20 if it provided in config', async function () { - const partnerClientId = 'partnerClientId 123'; - const partnerClientIdType = 0; - const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=20'); - expect(request.url).to.include(`&pcid=${encodeURIComponent(partnerClientId)}`); - expect(request.url).to.include(`&idtype=${partnerClientIdType}`); - }); - - it('should NOT send pcid and idtype in AT=20 if partnerClientId is NOT a string', async function () { - const partnerClientId = 123; - const partnerClientIdType = 0; - const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=20'); - expect(request.url).not.to.include(`&pcid=`); - expect(request.url).not.to.include(`&idtype=`); - }); - - it('should NOT send pcid and idtype in AT=20 if partnerClientIdType is NOT a number', async function () { - const partnerClientId = 'partnerClientId 123'; - const partnerClientIdType = 'wrong'; - const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=20'); - expect(request.url).not.to.include(`&pcid=`); - expect(request.url).not.to.include(`&idtype=`); + expect(wasCallbackCalled).to.equal(true); }); - it('should send partnerClientId and partnerClientIdType in AT=39 if it provided in config', async function () { const partnerClientId = 'partnerClientId 123'; const partnerClientIdType = 0; @@ -1563,44 +1033,6 @@ describe('IntentIQ tests', function () { expect(request.url).not.to.include(`&pcid=${partnerClientId}`); expect(request.url).not.to.include(`&idtype=${partnerClientIdType}`); }); - - it('should NOT send sourceMetaData in AT=20 if sourceMetaDataExternal provided', async function () { - const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', sourceMetaDataExternal: 123 } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.include('?at=20'); - expect(request.url).to.include('&fbp=123'); - }); - - it('should store first party data under the silo key when siloEnabled is true', async function () { - const configParams = { params: { ...allConfigParams.params, siloEnabled: true } }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const expectedKey = FIRST_PARTY_KEY + '_p_' + configParams.params.partner; - const storedData = localStorage.getItem(expectedKey); - const parsed = JSON.parse(storedData); - - expect(storedData).to.be.a('string'); - expect(localStorage.getItem(FIRST_PARTY_KEY)).to.be.null; - expect(parsed).to.have.property('pcid'); - }); - - it('should send siloEnabled value in the request', async function () { - const callBackSpy = sinon.spy(); - const configParams = { params: { ...allConfigParams.params, siloEnabled: true } }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - - const request = server.requests[0]; - - expect(request.url).to.contain(`&japs=${configParams.params.siloEnabled}`); - }); - it('should increment callCount when valid eids are returned', async function () { const firstPartyDataKey = '_iiq_fdata_' + partner; const partnerData = { callCount: 0, failCount: 0, noDataCounter: 0 }; @@ -1659,158 +1091,13 @@ describe('IntentIQ tests', function () { expect(updatedData.noDataCounter).to.equal(1); }); - it('should send additional parameters in sync request due to configuration', async function () { - const configParams = { - params: { - ...defaultConfigParams.params, - browserBlackList: 'chrome', - additionalParams: [{ - parameterName: 'general', - parameterValue: 'Lee', - destination: [1, 0, 0] - }] - } - }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - const syncRequest = server.requests[0]; - - expect(syncRequest.url).to.include('general=Lee'); - }); - it('should send additionalParams in VR request', async function () { - const configParams = { - params: { - ...defaultConfigParams.params, - additionalParams: [{ - parameterName: 'general', - parameterValue: 'Lee', - destination: [0, 1, 0] - }] - } - }; - - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const vrRequest = server.requests[0]; - - expect(vrRequest.url).to.include('general=Lee'); - }); - - it('should not send additionalParams in case it is not an array', async function () { - const configParams = { - params: { - ...defaultConfigParams.params, - additionalParams: { - parameterName: 'general', - parameterValue: 'Lee', - destination: [0, 1, 0] - } - } - }; - - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const vrRequest = server.requests[0]; - - expect(vrRequest.url).not.to.include('general='); - }); - - it('should not send additionalParams in case request url is too long', async function () { - const longValue = 'x'.repeat(5000000); // simulate long parameter - const configParams = { - params: { - ...defaultConfigParams.params, - additionalParams: [{ - parameterName: 'general', - parameterValue: longValue, - destination: [0, 1, 0] - }] - } - }; - - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const vrRequest = server.requests[0]; - - expect(vrRequest.url).not.to.include('general='); - }); - - it('should call groupChanged with "withoutIIQ" when terminationCause is 41', async function () { - const groupChangedSpy = sinon.spy(); - const callBackSpy = sinon.spy(); - const configParams = { - params: { - ...defaultConfigParams.params, - groupChanged: groupChangedSpy - } - }; - - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - - const request = server.requests[0]; - request.respond( - 200, - responseHeader, - JSON.stringify({ - tc: 41, - isOptedOut: false, - data: { eids: [] } - }) - ); - - expect(callBackSpy.calledOnce).to.be.true; - expect(groupChangedSpy.calledWith(WITHOUT_IIQ)).to.be.true; - }); - - it('should call groupChanged with "withIIQ" when terminationCause is NOT 41', async function () { - const groupChangedSpy = sinon.spy(); - const callBackSpy = sinon.spy(); - const configParams = { - params: { - ...defaultConfigParams.params, - groupChanged: groupChangedSpy - } - }; - - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - - const request = server.requests[0]; - request.respond( - 200, - responseHeader, - JSON.stringify({ - tc: 35, - isOptedOut: false, - data: { eids: [] } - }) - ); - - expect(callBackSpy.calledOnce).to.be.true; - expect(groupChangedSpy.calledWith(WITH_IIQ)).to.be.true; - }); - it('should use group provided by partner', async function () { - const groupChangedSpy = sinon.spy(); const callBackSpy = sinon.spy(); const usedGroup = 'B'; - const ABTestingConfigurationSource = 'group'; const configParams = { params: { ...defaultConfigParams.params, - ABTestingConfigurationSource, - group: usedGroup, - groupChanged: groupChangedSpy + group: usedGroup } }; @@ -1825,95 +1112,8 @@ describe('IntentIQ tests', function () { ); expect(request.url).to.contain(`abtg=${usedGroup}`); - expect(request.url).to.contain(`ABTestingConfigurationSource=${ABTestingConfigurationSource}`); + expect(request.url).to.contain('ABTestingConfigurationSource=group'); expect(request.url).to.contain(`testGroup=${usedGroup}`); expect(callBackSpy.calledOnce).to.be.true; - expect(groupChangedSpy.calledWith(usedGroup)).to.be.true; - }); - - it('should NOT call groupChanged when the current browser is blacklisted', async function () { - const groupChangedSpy = sinon.spy(); - const blk = detectBrowser(); - const configParams = { - params: { - ...defaultConfigParams.params, - browserBlackList: blk, - groupChanged: groupChangedSpy - } - }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - - expect(groupChangedSpy.called).to.be.false; - }); - - it('should not mark a test group on the sync pixel when the current browser is blacklisted', async function () { - const blk = detectBrowser(); - const configParams = { - params: { - ...defaultConfigParams.params, - browserBlackList: blk - } - }; - - intentIqIdSubmodule.getId(configParams); - await waitForClientHints(); - - const pixelRequest = server.requests[0]; - expect(pixelRequest).to.exist; - expect(pixelRequest.url).to.include('at=20'); - expect(pixelRequest.url).to.not.include('testGroup='); - expect(pixelRequest.url).to.include('isInTestGroup=false'); - }); - - it('should include testPercentage with configured abPercentage in AT=39 URL', async function () { - const callBackSpy = sinon.spy(); - const configParams = { - params: { ...defaultConfigParams.params, abPercentage: 70 } - }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.contain('testPercentage=70'); - }); - - it('should not include testPercentage in AT=39 URL when abPercentage is not configured', async function () { - const callBackSpy = sinon.spy(); - const configParams = { params: { partner } }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.not.contain('testPercentage'); - }); - - it('should include testPercentage=0 when abPercentage is explicitly 0 in AT=39 URL', async function () { - const callBackSpy = sinon.spy(); - const configParams = { - params: { ...defaultConfigParams.params, abPercentage: 0 } - }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.contain('testPercentage=0'); - }); - - it('should clamp abPercentage out of range in AT=39 URL', async function () { - const callBackSpy = sinon.spy(); - const configParams = { - params: { ...defaultConfigParams.params, abPercentage: 150 } - }; - const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.contain('testPercentage=100'); }); });