From 3a7cf3912a992830a24bdf87e156e677eb56caca Mon Sep 17 00:00:00 2001 From: Oleksii Petrychenko Date: Tue, 24 Feb 2026 20:02:01 +0200 Subject: [PATCH 1/4] AGT-803-Deprecated-GAM-methods-module --- modules/intentIqIdSystem.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 054afe82371..6d5dbf04bf2 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -208,9 +208,21 @@ export function setGamReporting(gamObjectReference, gamParameterName, userGroup, if (isBlacklisted) return; if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) { gamObjectReference.cmd.push(() => { - gamObjectReference - .pubads() - .setTargeting(gamParameterName, userGroup); + if (typeof gamObjectReference.setConfig === 'function') { + const currentConfig = typeof gamObjectReference.getConfig === 'function' + ? gamObjectReference.getConfig('targeting') + : {}; + const updatedConfig = { + targeting: { + ...(isPlainObject(currentConfig) ? currentConfig.targeting : {}), + [gamParameterName]: userGroup + } + } + gamObjectReference.setConfig(updatedConfig); + return; + } + const pubads = gamObjectReference?.pubads?.(); + if (pubads?.setTargeting) pubads.setTargeting(gamParameterName, userGroup); }); } } From 5a65757a86aafbac614acd5d829d12bc4984d87c Mon Sep 17 00:00:00 2001 From: Oleksii Petrychenko Date: Thu, 26 Feb 2026 11:36:49 +0200 Subject: [PATCH 2/4] AGT-803-Deprecated-GAM-methods-module --- modules/intentIqIdSystem.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 6d5dbf04bf2..c064719ae88 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -209,20 +209,15 @@ export function setGamReporting(gamObjectReference, gamParameterName, userGroup, if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) { gamObjectReference.cmd.push(() => { if (typeof gamObjectReference.setConfig === 'function') { - const currentConfig = typeof gamObjectReference.getConfig === 'function' - ? gamObjectReference.getConfig('targeting') - : {}; - const updatedConfig = { + gamObjectReference.setConfig({ targeting: { - ...(isPlainObject(currentConfig) ? currentConfig.targeting : {}), [gamParameterName]: userGroup } - } - gamObjectReference.setConfig(updatedConfig); + }); return; } - const pubads = gamObjectReference?.pubads?.(); - if (pubads?.setTargeting) pubads.setTargeting(gamParameterName, userGroup); + // Fallback in case an older version of Google Publisher Tag is used. + gamObjectReference?.pubads?.()?.setTargeting?.(gamParameterName, userGroup); }); } } From a238735df485dc8c1fa4ad6c0751aa63482c9f4d Mon Sep 17 00:00:00 2001 From: Oleksii Petrychenko Date: Mon, 2 Mar 2026 17:13:25 +0200 Subject: [PATCH 3/4] AGT-803-Deprecated-GAM-methods-module --- .../intentIqUtils/gamPredictionReport.js | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/libraries/intentIqUtils/gamPredictionReport.js b/libraries/intentIqUtils/gamPredictionReport.js index 2191ade6d35..e221483bb19 100644 --- a/libraries/intentIqUtils/gamPredictionReport.js +++ b/libraries/intentIqUtils/gamPredictionReport.js @@ -1,5 +1,5 @@ import { getEvents } from '../../src/events.js'; -import { logError } from '../../src/utils.js'; +import { isPlainObject, logError } from '../../src/utils.js'; export function gamPredictionReport (gamObjectReference, sendData) { try { @@ -7,11 +7,26 @@ export function gamPredictionReport (gamObjectReference, sendData) { const getSlotTargeting = (slot) => { const kvs = {}; try { - (slot.getTargetingKeys() || []).forEach((k) => { - kvs[k] = slot.getTargeting(k); - }); + if (typeof slot.getConfig === 'function') { + const current = slot.getConfig('targeting'); + const targeting = isPlainObject(current?.targeting) + ? current.targeting + : (isPlainObject(current) ? current : {}); + for (const k in targeting) { + const v = targeting[k]; + if (v == null) continue; + kvs[k] = Array.isArray(v) ? v : [typeof v === 'string' ? v : String(v)]; + } + return kvs; + } + // Fallback in case an older version of Google Publisher Tag is used. + if (typeof slot.getTargetingKeys === 'function' && typeof slot.getTargeting === 'function') { + (slot.getTargetingKeys() || []).forEach((k) => { + kvs[k] = slot.getTargeting(k); + }); + } } catch (e) { - logError('Failed to get targeting keys: ' + e); + logError('Failed to get slot targeting: ' + e); } return kvs; }; From 58c7ba0eb7a027cd803de48a5f9a6eb63679c102 Mon Sep 17 00:00:00 2001 From: Oleksii Petrychenko Date: Wed, 4 Mar 2026 16:36:17 +0200 Subject: [PATCH 4/4] AGT-803-Deprecated-GAM-methods-module --- .../intentIqUtils/gamPredictionReport.js | 5 +- .../libraries/gamPredictionReport_spec.js | 114 ++++++++++++++++++ test/spec/modules/intentIqIdSystem_spec.js | 42 ++++++- 3 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 test/spec/libraries/gamPredictionReport_spec.js diff --git a/libraries/intentIqUtils/gamPredictionReport.js b/libraries/intentIqUtils/gamPredictionReport.js index e221483bb19..69a81a8a5e6 100644 --- a/libraries/intentIqUtils/gamPredictionReport.js +++ b/libraries/intentIqUtils/gamPredictionReport.js @@ -3,7 +3,10 @@ import { isPlainObject, logError } from '../../src/utils.js'; export function gamPredictionReport (gamObjectReference, sendData) { try { - if (!gamObjectReference || !sendData) logError('Failed to get gamPredictionReport, required data is missed'); + if (!gamObjectReference || !sendData) { + logError('Failed to get gamPredictionReport, required data is missed'); + return + } const getSlotTargeting = (slot) => { const kvs = {}; try { diff --git a/test/spec/libraries/gamPredictionReport_spec.js b/test/spec/libraries/gamPredictionReport_spec.js new file mode 100644 index 00000000000..65690f9ccd3 --- /dev/null +++ b/test/spec/libraries/gamPredictionReport_spec.js @@ -0,0 +1,114 @@ +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 slot.getConfig flat object', () => { + const sendData = sinon.spy(); + const slot = { + getConfig: sinon.stub().withArgs('targeting').returns({ hb_bidder: ['flat'] }), + getSlotElementId: () => 'div-2', + getAdUnitPath: () => '/456' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal('flat'); + }); + + 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('coerces non-array targeting values to string arrays', () => { + const sendData = sinon.spy(); + const slot = { + getConfig: sinon.stub().withArgs('targeting').returns({ targeting: { hb_bidder: 42 } }), + getSlotElementId: () => 'div-4', + getAdUnitPath: () => '/101' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal('42'); + }); + + 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/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 18dd0452943..b3d3b083cb6 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -7,7 +7,8 @@ import { handleClientHints, firstPartyData as moduleFPD, isCMPStringTheSame, createPixelUrl, translateMetadata, - initializeGlobalIIQ + initializeGlobalIIQ, + setGamReporting } from '../../../modules/intentIqIdSystem.js'; import { storage, readData, storeData } from '../../../libraries/intentIqUtils/storageUtils.js'; import { gppDataHandler, uspDataHandler, gdprDataHandler } from '../../../src/consentHandler.js'; @@ -220,6 +221,45 @@ 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: [], + 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;