From 76bf4fa6bb71efe33202aab864f74bbc20dcba6b Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Thu, 9 Jul 2026 20:20:18 +0200 Subject: [PATCH 1/5] no ab group in browserblacklist --- modules/intentIqIdSystem.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/intentIqIdSystem.ts b/modules/intentIqIdSystem.ts index 0a5d1b3cb7f..7de2f514c2b 100644 --- a/modules/intentIqIdSystem.ts +++ b/modules/intentIqIdSystem.ts @@ -35,8 +35,6 @@ import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUti import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; import { setKeyValueOn } from '../libraries/gptUtils/gptUtils.js'; -import type { IdProviderSpec } from './userId/spec'; - export type IntentIqIdSystemModuleName = 'intentIqId'; @@ -492,11 +490,16 @@ export const intentIqIdSubmodule = { const cmpData = getCmpData(); const gdprDetected = cmpData.gdprString; firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage) as string); - actualABGroup = defineABTestingGroup(configParams, partnerData?.terminationCause); - if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); 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; setGamReporting(gamObjectReference, gamParameterName, actualABGroup, isBlacklisted); @@ -709,10 +712,13 @@ export const intentIqIdSubmodule = { if ('tc' in respJson) { partnerData.terminationCause = respJson.tc; - actualABGroup = defineABTestingGroup(configParams, respJson.tc,); - if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); - if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); + if (!isBlacklisted) { + actualABGroup = defineABTestingGroup(configParams, respJson.tc); + + if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); + if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); + } } if ('isOptedOut' in respJson) { if (respJson.isOptedOut !== firstPartyData.isOptedOut) { From 98f0b61c314afb3d961f43f4329594b9cd1c521d Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Thu, 9 Jul 2026 20:29:07 +0200 Subject: [PATCH 2/5] fix lint issues --- libraries/intentIqUtils/cryptionUtils.ts | 2 +- libraries/intentIqUtils/defineABTestingGroupUtils.ts | 12 +++++++++++- libraries/intentIqUtils/detectBrowserUtils.ts | 2 +- libraries/intentIqUtils/gamPredictionReport.ts | 2 +- libraries/intentIqUtils/getCmpData.ts | 2 +- libraries/intentIqUtils/getRefferer.ts | 2 +- libraries/intentIqUtils/getSyncKey.ts | 2 +- libraries/intentIqUtils/getUnitPosition.ts | 2 +- libraries/intentIqUtils/intentIqConfig.ts | 2 +- libraries/intentIqUtils/storageUtils.ts | 2 +- libraries/intentIqUtils/urlUtils.ts | 2 +- modules/intentIqAnalyticsAdapter.ts | 3 +-- modules/intentIqIdSystem.ts | 11 +---------- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/libraries/intentIqUtils/cryptionUtils.ts b/libraries/intentIqUtils/cryptionUtils.ts index d70dc8309f3..335d38a27e8 100644 --- a/libraries/intentIqUtils/cryptionUtils.ts +++ b/libraries/intentIqUtils/cryptionUtils.ts @@ -28,4 +28,4 @@ export function decryptData(encryptedText: string, key: number = 42): string { out += String.fromCharCode(Number(parts[i]) ^ key); } return out; -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/defineABTestingGroupUtils.ts b/libraries/intentIqUtils/defineABTestingGroupUtils.ts index 11fddb51ef1..c9765d6d7a2 100644 --- a/libraries/intentIqUtils/defineABTestingGroupUtils.ts +++ b/libraries/intentIqUtils/defineABTestingGroupUtils.ts @@ -13,6 +13,16 @@ interface ABTestingConfig { group?: string; } + +/** + * 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'; + /** * Fix percentage if provided some incorrect data * clampPct(150) => 100 @@ -85,4 +95,4 @@ export function defineABTestingGroup( return IIQServerConfigurationSource(tc, configObject.abPercentage); } } -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/detectBrowserUtils.ts b/libraries/intentIqUtils/detectBrowserUtils.ts index 49a9385fb95..58b584da458 100644 --- a/libraries/intentIqUtils/detectBrowserUtils.ts +++ b/libraries/intentIqUtils/detectBrowserUtils.ts @@ -95,4 +95,4 @@ export function detectBrowserFromUserAgentData( } return 'unknown'; -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/gamPredictionReport.ts b/libraries/intentIqUtils/gamPredictionReport.ts index 79f072c9350..9e631635e56 100644 --- a/libraries/intentIqUtils/gamPredictionReport.ts +++ b/libraries/intentIqUtils/gamPredictionReport.ts @@ -118,4 +118,4 @@ export function gamPredictionReport( } catch (error) { logError('Failed to subscribe to GAM: ' + error); } -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/getCmpData.ts b/libraries/intentIqUtils/getCmpData.ts index 3e062219ca8..9ff45a1f4ca 100644 --- a/libraries/intentIqUtils/getCmpData.ts +++ b/libraries/intentIqUtils/getCmpData.ts @@ -46,4 +46,4 @@ export function areCmpValuesEqual(a: unknown, b: unknown): boolean { if (aValid !== bValid) return false; return a === b; -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/getRefferer.ts b/libraries/intentIqUtils/getRefferer.ts index 20a68607f3a..5280cfb08fe 100644 --- a/libraries/intentIqUtils/getRefferer.ts +++ b/libraries/intentIqUtils/getRefferer.ts @@ -79,4 +79,4 @@ export function isDomainIncluded(fullUrl: string, domainName: string): boolean { logError(`Invalid URL provided: ${error}`); return false; } -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/getSyncKey.ts b/libraries/intentIqUtils/getSyncKey.ts index d3f74da48f2..9f39a8f26bd 100644 --- a/libraries/intentIqUtils/getSyncKey.ts +++ b/libraries/intentIqUtils/getSyncKey.ts @@ -1 +1 @@ -export const SYNC_KEY = (partner: number): string => `_iiq_sync_${partner}`; \ No newline at end of file +export const SYNC_KEY = (partner: number): string => `_iiq_sync_${partner}`; diff --git a/libraries/intentIqUtils/getUnitPosition.ts b/libraries/intentIqUtils/getUnitPosition.ts index 976794b6ed7..e59617def60 100644 --- a/libraries/intentIqUtils/getUnitPosition.ts +++ b/libraries/intentIqUtils/getUnitPosition.ts @@ -24,4 +24,4 @@ export function getUnitPosition( return typeof pos === 'number' ? pos : undefined; } -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/intentIqConfig.ts b/libraries/intentIqUtils/intentIqConfig.ts index 8b4c9ed04f4..408735a64ba 100644 --- a/libraries/intentIqUtils/intentIqConfig.ts +++ b/libraries/intentIqUtils/intentIqConfig.ts @@ -56,4 +56,4 @@ export const reportingServerAddress = ( const host = buildServerAddress('reports', region); return `${host}/report`; -}; \ No newline at end of file +}; diff --git a/libraries/intentIqUtils/storageUtils.ts b/libraries/intentIqUtils/storageUtils.ts index 3d0a90baa39..f8be07c39ae 100644 --- a/libraries/intentIqUtils/storageUtils.ts +++ b/libraries/intentIqUtils/storageUtils.ts @@ -179,4 +179,4 @@ export function tryParse(data: string): T | null { logError(err); return null; } -} \ No newline at end of file +} diff --git a/libraries/intentIqUtils/urlUtils.ts b/libraries/intentIqUtils/urlUtils.ts index b1a815597fc..7cf5f56cc75 100644 --- a/libraries/intentIqUtils/urlUtils.ts +++ b/libraries/intentIqUtils/urlUtils.ts @@ -19,4 +19,4 @@ export function appendSPData( } return `${url}&spd=${spdParam}`; -} \ No newline at end of file +} diff --git a/modules/intentIqAnalyticsAdapter.ts b/modules/intentIqAnalyticsAdapter.ts index 024e60b05ee..22e5f0be96f 100644 --- a/modules/intentIqAnalyticsAdapter.ts +++ b/modules/intentIqAnalyticsAdapter.ts @@ -16,9 +16,8 @@ import { import { reportingServerAddress } from '../libraries/intentIqUtils/intentIqConfig.js'; import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js'; import { gamPredictionReport } from '../libraries/intentIqUtils/gamPredictionReport.js'; -import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js'; +import { defineABTestingGroup, IntentIqABConfigSource } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js'; import { getGlobal } from '../src/prebidGlobal.js'; -import { IntentIqABConfigSource } from './intentIqIdSystem.js'; /** * Payload passed to `window.intentIqAnalyticsAdapter_.reportExternalWin()`. diff --git a/modules/intentIqIdSystem.ts b/modules/intentIqIdSystem.ts index 7de2f514c2b..8aea3dc4179 100644 --- a/modules/intentIqIdSystem.ts +++ b/modules/intentIqIdSystem.ts @@ -32,21 +32,12 @@ import { SYNC_KEY } from '../libraries/intentIqUtils/getSyncKey.ts'; import { getIiqServerAddress, iiqPixelServerAddress } from '../libraries/intentIqUtils/intentIqConfig.ts'; import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.ts'; import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.ts'; -import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; +import { defineABTestingGroup, IntentIqABConfigSource } from '../libraries/intentIqUtils/defineABTestingGroupUtils.ts'; import { setKeyValueOn } from '../libraries/gptUtils/gptUtils.js'; export type IntentIqIdSystemModuleName = 'intentIqId'; -/** - * 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'; - export interface IntentIqIdSystemParams { /** * Partner ID assigned by IntentIQ. Required. From 3a2576e603c827ebeb1c8de933327b94846a2e33 Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Fri, 10 Jul 2026 21:10:52 +0200 Subject: [PATCH 3/5] remove isblacklisted logic from request --- libraries/intentIqUtils/defineABTestingGroupUtils.ts | 10 ---------- modules/intentIqIdSystem.ts | 1 + 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/libraries/intentIqUtils/defineABTestingGroupUtils.ts b/libraries/intentIqUtils/defineABTestingGroupUtils.ts index c6870fbea57..44abec10171 100644 --- a/libraries/intentIqUtils/defineABTestingGroupUtils.ts +++ b/libraries/intentIqUtils/defineABTestingGroupUtils.ts @@ -22,16 +22,6 @@ interface ABTestingConfig { group?: string; } - -/** - * 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'; - /** * Fix percentage if provided some incorrect data * clampPct(150) => 100 diff --git a/modules/intentIqIdSystem.ts b/modules/intentIqIdSystem.ts index ecb28219345..dfa3d81ecf8 100644 --- a/modules/intentIqIdSystem.ts +++ b/modules/intentIqIdSystem.ts @@ -699,6 +699,7 @@ export const intentIqIdSubmodule = { if (!isBlacklisted) { actualABGroup = defineABTestingGroup(configParams, respJson.tc); + actualABGroup = defineABTestingGroup(configParams, respJson.tc,); if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); From a8d7b95c2a915123646819fde7ea1a61aab8ddb3 Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Fri, 10 Jul 2026 21:12:43 +0200 Subject: [PATCH 4/5] small fix --- modules/intentIqIdSystem.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/intentIqIdSystem.ts b/modules/intentIqIdSystem.ts index dfa3d81ecf8..f5b40ded326 100644 --- a/modules/intentIqIdSystem.ts +++ b/modules/intentIqIdSystem.ts @@ -696,14 +696,10 @@ export const intentIqIdSubmodule = { if ('tc' in respJson) { partnerData.terminationCause = respJson.tc; + actualABGroup = defineABTestingGroup(configParams, respJson.tc,); - if (!isBlacklisted) { - actualABGroup = defineABTestingGroup(configParams, respJson.tc); - actualABGroup = defineABTestingGroup(configParams, respJson.tc,); - - if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); - if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); - } + if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, actualABGroup); + if (groupChanged) groupChanged(actualABGroup, partnerData?.terminationCause); } if ('isOptedOut' in respJson) { if (respJson.isOptedOut !== firstPartyData.isOptedOut) { From 4d42855f9ac9996edaaefe2711ecf1c8dd9f4aaa Mon Sep 17 00:00:00 2001 From: Eyvaz Ahmadzada Date: Fri, 10 Jul 2026 21:14:22 +0200 Subject: [PATCH 5/5] add tests --- test/spec/modules/intentIqIdSystem_spec.js | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 279fd9cc5bb..c1e01ef9439 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -2029,6 +2029,79 @@ describe('IntentIQ tests', function () { 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 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 = {