diff --git a/modules/yieldmoSyntheticInventoryModule.js b/modules/yieldmoSyntheticInventoryModule.js index bca778a7b43..1365ae4ec97 100644 --- a/modules/yieldmoSyntheticInventoryModule.js +++ b/modules/yieldmoSyntheticInventoryModule.js @@ -1,26 +1,50 @@ import { config } from '../src/config.js'; -import { isGptPubadsDefined } from '../src/utils.js'; +import { isGptPubadsDefined, isFn } from '../src/utils.js'; +import * as ajax from '../src/ajax.js' +import strIncludes from 'core-js-pure/features/string/includes.js'; export const MODULE_NAME = 'Yieldmo Synthetic Inventory Module'; +export const AD_SERVER_ENDPOINT = 'https://ads.yieldmo.com/v002/t_ads/ads'; +const USPAPI_VERSION = 1; + +let cmpVersion = 0; +let cmpResolved = false; export function init(config) { + checkSandbox(window); validateConfig(config); - if (!isGptPubadsDefined()) { - window.googletag = window.googletag || {}; - window.googletag.cmd = window.googletag.cmd || []; - } - - const googletag = window.googletag; - const containerName = 'ym_sim_container_' + config.placementId; - - googletag.cmd.push(() => { - if (window.document.body) { - googletagCmd(config, containerName, googletag); - } else { - window.document.addEventListener('DOMContentLoaded', () => googletagCmd(config, containerName, googletag)); + const consentData = () => { + const consentDataObj = {}; + return (api, result) => { + consentDataObj[api] = result; + if ('cmp' in consentDataObj && 'usp' in consentDataObj) { + if (!isGptPubadsDefined()) { + window.top.googletag = window.top.googletag || {}; + window.top.googletag.cmd = window.top.googletag.cmd || []; + } + ajax.ajaxBuilder()(`${AD_SERVER_ENDPOINT}?${serialize(collectData(config.placementId, consentDataObj))}`, { + success: (responceText, responseObj) => { + window.top.__ymAds = processResponse(responseObj); + const googletag = window.top.googletag; + googletag.cmd.push(() => { + if (window.top.document.body) { + googletagCmd(config, googletag); + } else { + window.top.document.addEventListener('DOMContentLoaded', () => googletagCmd(config, googletag)); + } + }); + }, + error: (message, err) => { + throw err; + } + }); + } } - }); + }; + const consentDataHandler = consentData(); + lookupIabConsent((a) => consentDataHandler('cmp', a), (e) => consentDataHandler('cmp', false)); + lookupUspConsent((a) => consentDataHandler('usp', a), (e) => consentDataHandler('usp', false)); } export function validateConfig(config) { @@ -32,10 +56,11 @@ export function validateConfig(config) { } } -function googletagCmd(config, containerName, googletag) { - const gamContainer = window.document.createElement('div'); +function googletagCmd(config, googletag) { + const gamContainer = window.top.document.createElement('div'); + const containerName = 'ym_sim_container_' + config.placementId; gamContainer.id = containerName; - window.document.body.appendChild(gamContainer); + window.top.document.body.appendChild(gamContainer); googletag.defineSlot(config.adUnitPath, [1, 1], containerName) .addService(googletag.pubads()) .setTargeting('ym_sim_p_id', config.placementId); @@ -43,4 +68,262 @@ function googletagCmd(config, containerName, googletag) { googletag.display(containerName); } +function collectData(placementId, consentDataObj) { + const timeStamp = new Date().getTime(); + const connection = window.navigator.connection || {}; + const description = Array.prototype.slice.call(document.getElementsByTagName('meta')) + .filter((meta) => meta.getAttribute('name') === 'description')[0]; + const pageDimensions = { + density: window.top.devicePixelRatio || 0, + height: window.top.screen.height || window.top.screen.availHeight || window.top.outerHeight || window.top.innerHeight || 481, + width: window.top.screen.width || window.top.screen.availWidth || window.top.outerWidth || window.top.innerWidth || 321, + }; + + return { + bust: timeStamp, + dnt: window.top.doNotTrack === '1' || window.top.navigator.doNotTrack === '1' || false, + pr: document.referrer || '', + _s: 1, + e: 4, + page_url: window.top.location.href, + p: placementId, + description: description ? description.content.substring(0, 1000) : '', + title: document.title, + scrd: pageDimensions.density, + h: pageDimensions.height, + w: pageDimensions.width, + pft: timeStamp, + ct: timeStamp, + connect: connection.effectiveType, + bwe: connection.downlink ? connection.downlink + 'Mb/sec' : '', + rtt: typeof connection.rtt !== 'undefined' ? String(connection.rtt) : undefined, + sd: connection.saveData, + us_privacy: (consentDataObj.usp && consentDataObj.usp.usPrivacy) || '', + cmp: (consentDataObj.cmp && consentDataObj.cmp.tcString) || '' + }; +} + +function serialize(dataObj) { + const str = []; + for (let p in dataObj) { + if (dataObj.hasOwnProperty(p) && (dataObj[p] || dataObj[p] === false)) { + str.push(encodeURIComponent(p) + '=' + encodeURIComponent(dataObj[p])); + } + } + return str.join('&'); +} + +function processResponse(res) { + let parsedResponseBody; + try { + parsedResponseBody = JSON.parse(res.responseText); + } catch (err) { + throw new Error(`${MODULE_NAME}: response is not valid JSON`); + } + if (res && res.status === 204) { + throw new Error(`${MODULE_NAME}: no content success status`); + } + if (parsedResponseBody.data && parsedResponseBody.data.length && parsedResponseBody.data[0].error_code) { + throw new Error(`${MODULE_NAME}: no ad, error_code: ${parsedResponseBody.data[0].error_code}`); + } + return parsedResponseBody; +} + +function checkSandbox(w) { + try { + return !w.top.document && w.top !== w && !w.frameElement; + } catch (e) { + throw new Error(`${MODULE_NAME}: module was placed in the sandbox iframe`); + } +} + +function lookupIabConsent(cmpSuccess, cmpError) { + function findCMP() { + let f = window; + let cmpFrame; + let cmpFunction; + + while (!cmpFrame) { + try { + if (isFn(f.__tcfapi)) { + cmpVersion = 2; + cmpFunction = f.__tcfapi; + cmpFrame = f; + continue; + } + } catch (e) { } + + try { + if (f.frames['__tcfapiLocator']) { + cmpVersion = 2; + cmpFrame = f; + continue; + } + } catch (e) { } + + if (f === window.top) break; + f = f.parent; + } + return { + cmpFrame, + cmpFunction + }; + } + + function cmpResponseCallback(tcfData, success) { + if (success) { + setTimeout(() => { + if (!cmpResolved) { + cmpSuccess(tcfData); + } + }, 3000); + if (tcfData.gdprApplies === false || tcfData.eventStatus === 'tcloaded' || tcfData.eventStatus === 'useractioncomplete') { + cmpSuccess(tcfData); + cmpResolved = true; + } + } else { + cmpError('CMP unable to register callback function. Please check CMP setup.'); + } + } + + let { cmpFrame, cmpFunction } = findCMP(); + + if (!cmpFrame) { + return cmpError('CMP not found.'); + } + + if (isFn(cmpFunction)) { + cmpFunction('addEventListener', cmpVersion, cmpResponseCallback); + } else { + callCmpWhileInIframe('addEventListener', cmpFrame, cmpResponseCallback); + } + + function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) { + let apiName = '__tcfapi'; + let callName = `${apiName}Call`; + let callId = Math.random() + ''; + let msg = { + [callName]: { + command: commandName, + version: cmpVersion, + parameter: undefined, + callId: callId + } + }; + + cmpFrame.postMessage(msg, '*'); + + window.addEventListener('message', readPostMessageResponse, false); + + function readPostMessageResponse(event) { + let cmpDataPkgName = `${apiName}Return`; + let json = (typeof event.data === 'string' && strIncludes(event.data, cmpDataPkgName)) ? JSON.parse(event.data) : event.data; + if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) { + let payload = json[cmpDataPkgName]; + + if (payload.callId === callId) { + moduleCallback(payload.returnValue, payload.success); + } + } + } + } +} + +function lookupUspConsent(uspSuccess, uspError) { + function findUsp() { + let f = window; + let uspapiFrame; + let uspapiFunction; + + while (!uspapiFrame) { + try { + if (isFn(f.__uspapi)) { + uspapiFunction = f.__uspapi; + uspapiFrame = f; + continue; + } + } catch (e) {} + + try { + if (f.frames['__uspapiLocator']) { + uspapiFrame = f; + continue; + } + } catch (e) {} + if (f === window.top) break; + f = f.parent; + } + return { + uspapiFrame, + uspapiFunction, + }; + } + + function handleUspApiResponseCallbacks() { + const uspResponse = {}; + + function afterEach() { + if (uspResponse.usPrivacy) { + uspSuccess(uspResponse); + } else { + uspError('Unable to get USP consent string.'); + } + } + + return { + consentDataCallback: (consentResponse, success) => { + if (success && consentResponse.uspString) { + uspResponse.usPrivacy = consentResponse.uspString; + } + afterEach(); + }, + }; + } + + let callbackHandler = handleUspApiResponseCallbacks(); + let { uspapiFrame, uspapiFunction } = findUsp(); + + if (!uspapiFrame) { + return uspError('USP CMP not found.'); + } + + if (isFn(uspapiFunction)) { + uspapiFunction( + 'getUSPData', + USPAPI_VERSION, + callbackHandler.consentDataCallback + ); + } else { + callUspApiWhileInIframe( + 'getUSPData', + uspapiFrame, + callbackHandler.consentDataCallback + ); + } + + function callUspApiWhileInIframe(commandName, uspapiFrame, moduleCallback) { + let callId = Math.random() + ''; + let msg = { + __uspapiCall: { + command: commandName, + version: USPAPI_VERSION, + callId: callId, + }, + }; + + uspapiFrame.postMessage(msg, '*'); + + window.addEventListener('message', readPostMessageResponse, false); + + function readPostMessageResponse(event) { + const res = event && event.data && event.data.__uspapiReturn; + if (res && res.callId) { + if (res.callId === callId) { + moduleCallback(res.returnValue, res.success); + } + } + } + } +} + config.getConfig('yieldmo_synthetic_inventory', config => init(config.yieldmo_synthetic_inventory)); diff --git a/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js b/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js index 55b4e7255f7..d75b358a40e 100644 --- a/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js +++ b/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import * as ajax from 'src/ajax.js'; import { init, MODULE_NAME, @@ -11,7 +12,7 @@ const mockedYmConfig = { }; const setGoogletag = () => { - window.googletag = { + window.top.googletag = { cmd: [], defineSlot: sinon.stub(), addService: sinon.stub(), @@ -20,12 +21,25 @@ const setGoogletag = () => { enableServices: sinon.stub(), display: sinon.stub(), }; - window.googletag.defineSlot.returns(window.googletag); - window.googletag.addService.returns(window.googletag); - window.googletag.pubads.returns({getSlots: sinon.stub()}); - return window.googletag; + window.top.googletag.defineSlot.returns(window.top.googletag); + window.top.googletag.addService.returns(window.top.googletag); + window.top.googletag.pubads.returns({getSlots: sinon.stub()}); + return window.top.googletag; } +const getQuearyParamsFromUrl = (url) => + [...new URL(url).searchParams] + .reduce( + (agg, param) => { + const [key, value] = param; + + agg[key] = value; + + return agg; + }, + {} + ); + describe('Yieldmo Synthetic Inventory Module', function() { let config = Object.assign({}, mockedYmConfig); let googletagBkp; @@ -39,18 +53,12 @@ describe('Yieldmo Synthetic Inventory Module', function() { window.googletag = googletagBkp; }); - it('should be enabled with valid required params', function() { - expect(function () { - init(mockedYmConfig); - }).not.to.throw() - }); - it('should throw an error if placementId is missed', function() { const {placementId, ...config} = mockedYmConfig; expect(function () { validateConfig(config); - }).throw(`${MODULE_NAME}: placementId required`) + }).throw(`${MODULE_NAME}: placementId required`); }); it('should throw an error if adUnitPath is missed', function() { @@ -58,32 +66,464 @@ describe('Yieldmo Synthetic Inventory Module', function() { expect(function () { validateConfig(config); - }).throw(`${MODULE_NAME}: adUnitPath required`) + }).throw(`${MODULE_NAME}: adUnitPath required`); + }); + + describe('Ajax ad request', () => { + let sandbox; + + const setAjaxStub = (cb) => { + const ajaxStub = sandbox.stub().callsFake(cb); + sandbox.stub(ajax, 'ajaxBuilder').callsFake(() => ajaxStub); + return ajaxStub; + } + + const responseData = { + data: [{ + ads: [{ + foo: 'bar', + }] + }] + }; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should open ad request to ad server', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => {}); + + init(mockedYmConfig); + + expect((new URL(ajaxStub.getCall(0).args[0])).host).to.be.equal('ads.yieldmo.com'); + }); + + it('should properly combine ad request query', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => {}); + // const title = 'Synthetic Inventory Test title'; + // const titleBackup = document.title; + // document.title = title; + const pageDimensions = { + scrd: String(window.top.devicePixelRatio || 0), + h: String(window.top.screen.height || window.screen.top.availHeight || window.top.outerHeight || window.top.innerHeight || 481), + w: String(window.top.screen.width || window.screen.top.availWidth || window.top.outerWidth || window.top.innerWidth || 321), + }; + const connection = window.navigator.connection || {}; + const connectToCompare = { + connect: String(connection.effectiveType), + bwe: String(connection.downlink ? connection.downlink + 'Mb/sec' : ''), + rtt: String(connection.rtt), + sd: String(connection.saveData), + } + + init(mockedYmConfig); + const queryParams = JSON.parse(JSON.stringify(getQuearyParamsFromUrl(ajaxStub.getCall(0).args[0]))); + const timeStamp = queryParams.bust; + + expect(queryParams).to.deep.equal({ + // title, + _s: '1', + dnt: 'false', + e: '4', + p: mockedYmConfig.placementId, + page_url: window.top.location.href, + pr: window.top.location.href, + bust: timeStamp, + pft: timeStamp, + ct: timeStamp, + ...connectToCompare, + ...pageDimensions + }); + + // document.title = titleBackup; + }); + + it('should send ad request to ad server', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => {}); + + init(mockedYmConfig); + + expect(ajaxStub.calledOnce).to.be.true; + }); + + it('should throw an error if can not parse response', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => { + callbackObj.success('', {responseText: '__invalid_JSON__', status: 200}); + }); + + expect(() => init(mockedYmConfig)).to.throw('Yieldmo Synthetic Inventory Module: response is not valid JSON'); + }); + + it('should throw an error if status is 204', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => { + callbackObj.success('', {status: 204, responseText: '{}'}); + }); + + expect(() => init(mockedYmConfig)).to.throw('Yieldmo Synthetic Inventory Module: no content success status'); + }); + + it('should throw an error if error_code present in the ad response', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => { + callbackObj.success('', {status: 200, responseText: '{"data": [{"error_code": "NOAD"}]}'}); + }); + + expect(() => init(mockedYmConfig)).to.throw('Yieldmo Synthetic Inventory Module: no ad, error_code: NOAD'); + }); + + it('should store ad response in window object', () => { + const ajaxStub = setAjaxStub((url, callbackObj) => { + callbackObj.success(JSON.stringify(responseData), {status: 200, responseText: JSON.stringify(responseData)}); + }); + + init(mockedYmConfig); + expect(window.top.__ymAds).to.deep.equal(responseData); + }); + + it('should add correct googletag.cmd', function() { + const containerName = 'ym_sim_container_' + mockedYmConfig.placementId; + const gtag = setGoogletag(); + + const ajaxStub = setAjaxStub((url, callbackObj) => { + callbackObj.success(JSON.stringify(responseData), {status: 200, responseText: '{"data": [{"ads": []}]}'}); + }); + + init(mockedYmConfig); + + expect(gtag.cmd.length).to.equal(1); + + gtag.cmd[0](); + + expect(gtag.addService.getCall(0)).to.not.be.null; + expect(gtag.setTargeting.getCall(0)).to.not.be.null; + expect(gtag.setTargeting.getCall(0).args[0]).to.exist.and.to.equal('ym_sim_p_id'); + expect(gtag.setTargeting.getCall(0).args[1]).to.exist.and.to.equal(mockedYmConfig.placementId); + expect(gtag.defineSlot.getCall(0)).to.not.be.null; + expect(gtag.enableServices.getCall(0)).to.not.be.null; + expect(gtag.display.getCall(0)).to.not.be.null; + expect(gtag.display.getCall(0).args[0]).to.exist.and.to.equal(containerName); + expect(gtag.pubads.getCall(0)).to.not.be.null; + + const gamContainerEl = window.top.document.getElementById(containerName); + expect(gamContainerEl).to.not.be.null; + + gamContainerEl.parentNode.removeChild(gamContainerEl); + }); + }); + + describe('lookupIabConsent', () => { + const callId = Math.random(); + const cmpFunction = sinon.stub(); + const originalXMLHttpRequest = window.XMLHttpRequest; + let requestMock = { + open: sinon.stub(), + send: sinon.stub(), + }; + let clock; + let postMessageStub; + let mathRandomStub; + let addEventListenerStub; + + beforeEach(() => { + postMessageStub = sinon.stub(window, 'postMessage'); + mathRandomStub = sinon.stub(Math, 'random'); + addEventListenerStub = sinon.stub(window, 'addEventListener'); + + window.XMLHttpRequest = function FakeXMLHttpRequest() { + this.open = requestMock.open; + this.send = requestMock.send; + this.setRequestHeader = () => {}; + }; + + clock = sinon.useFakeTimers(); + }); + + afterEach(() => { + window.XMLHttpRequest = originalXMLHttpRequest; + + postMessageStub.restore(); + mathRandomStub.restore(); + addEventListenerStub.restore(); + + cmpFunction.resetBehavior(); + cmpFunction.resetHistory(); + + requestMock.open.resetBehavior(); + requestMock.open.resetHistory(); + requestMock.send.resetBehavior(); + requestMock.send.resetHistory(); + + clock.restore(); + }); + + it('should get cmp function', () => { + window.__tcfapi = cmpFunction; + + init(mockedYmConfig); + + window.__tcfapi = undefined; + + expect(cmpFunction.calledOnceWith('addEventListener', 2)).to.be.true; + }); + + it('should call api without cmp consent if can not get it', () => { + cmpFunction.callsFake((e, version, callback) => { + callback(undefined, false); + }); + + window.__tcfapi = cmpFunction; + + init(mockedYmConfig); + + window.__tcfapi = undefined; + + expect(requestMock.open.calledOnce).to.be.true; + }); + + it('should add cmp consent string to ad server request params if gdprApplies is false', () => { + const tcfData = { gdprApplies: false, tcString: 'testTcString' }; + + cmpFunction.callsFake((e, version, callback) => { + callback(tcfData, true); + }); + + window.__tcfapi = cmpFunction; + + init(mockedYmConfig); + + window.__tcfapi = undefined; + + const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]); + + expect(queryParams.cmp).to.be.equal(tcfData.tcString); + }); + + it('should add cmp consent string to ad server request params if eventStatus is tcloaded', () => { + const tcfData = { eventStatus: 'tcloaded', tcString: 'testTcString' }; + + cmpFunction.callsFake((e, version, callback) => { + callback(tcfData, true); + }); + + window.__tcfapi = cmpFunction; + + init(mockedYmConfig); + + window.__tcfapi = undefined; + + const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]); + + expect(queryParams.cmp).to.be.equal(tcfData.tcString); + }); + + it('should add cmp consent string to ad server request params if eventStatus is useractioncomplete', () => { + const tcfData = { eventStatus: 'useractioncomplete', tcString: 'testTcString' }; + + cmpFunction.callsFake((e, version, callback) => { + callback(tcfData, true); + }); + + window.__tcfapi = cmpFunction; + + init(mockedYmConfig); + + window.__tcfapi = undefined; + + const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]); + + expect(queryParams.cmp).to.be.equal(tcfData.tcString); + }); + + it('should post message if cmp consent is loaded from another iframe', () => { + window.frames['__tcfapiLocator'] = 'cmpframe'; + + init(mockedYmConfig); + + window.frames['__tcfapiLocator'] = undefined; + + expect(window.postMessage.callCount).to.be.equal(1); + }); + + it('should add event listener for message event if usp consent is loaded from another iframe', () => { + window.frames['__tcfapiLocator'] = 'cmpframe'; + + init(mockedYmConfig); + + window.frames['__tcfapiLocator'] = undefined; + + expect(window.addEventListener.calledOnceWith('message')).to.be.true; + }); + + it('should add cmp consent string to ad server request params when called from iframe', () => { + const callId = Math.random(); + const tcfData = { gdprApplies: false, tcString: 'testTcString' }; + const cmpEvent = { + data: { + __tcfapiReturn: { + callId: `${callId}`, + returnValue: tcfData, + success: true, + } + }, + }; + + mathRandomStub.returns(callId); + addEventListenerStub.callsFake( + (e, callback) => { + callback(cmpEvent) + } + ); + + window.frames['__tcfapiLocator'] = 'cmpframe'; + + init(mockedYmConfig); + + window.frames['__tcfapiLocator'] = undefined; + + const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]); + + expect(queryParams.cmp).to.be.equal(tcfData.tcString); + }); }); - it('should add correct googletag.cmd', function() { - const containerName = 'ym_sim_container_' + mockedYmConfig.placementId; - const gtag = setGoogletag(); + describe('lookupUspConsent', () => { + const callId = Math.random(); + const uspFunction = sinon.stub(); + const originalXMLHttpRequest = window.XMLHttpRequest; + let requestMock = { + open: sinon.stub(), + send: sinon.stub(), + }; + let clock; + let postMessageStub; + let mathRandomStub; + let addEventListenerStub; + + beforeEach(() => { + postMessageStub = sinon.stub(window, 'postMessage'); + mathRandomStub = sinon.stub(Math, 'random'); + addEventListenerStub = sinon.stub(window, 'addEventListener'); + + window.XMLHttpRequest = function FakeXMLHttpRequest() { + this.open = requestMock.open; + this.send = requestMock.send; + this.setRequestHeader = () => {}; + }; + + clock = sinon.useFakeTimers(); + }); - init(mockedYmConfig); + afterEach(() => { + window.XMLHttpRequest = originalXMLHttpRequest; - expect(gtag.cmd.length).to.equal(1); + postMessageStub.restore(); + mathRandomStub.restore(); + addEventListenerStub.restore(); - gtag.cmd[0](); + uspFunction.resetBehavior(); + uspFunction.resetHistory(); + + requestMock.open.resetBehavior(); + requestMock.open.resetHistory(); + requestMock.send.resetBehavior(); + requestMock.send.resetHistory(); + + clock.restore(); + }); + + it('should get cmp function', () => { + window.__uspapi = uspFunction; + + init(mockedYmConfig); + + window.__uspapi = undefined; + + expect(uspFunction.calledOnceWith('getUSPData', 1)).to.be.true; + }); + + it('should call api without usp consent if can not get it', () => { + uspFunction.callsFake((e, version, callback) => { + callback(undefined, false); + }); + + window.__uspapi = uspFunction; + + init(mockedYmConfig); + + window.__uspapi = undefined; + + expect(requestMock.open.calledOnce).to.be.true; + }); + + it('should add usp consent string to ad server request params', () => { + const uspData = { uspString: 'testUspString' }; + + uspFunction.callsFake((e, version, callback) => { + callback(uspData, true); + }); + + window.__uspapi = uspFunction; + + init(mockedYmConfig); + + window.__uspapi = undefined; + + const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]); + + expect(queryParams.us_privacy).to.be.equal(uspData.uspString); + }); + + it('should post message if usp consent is loaded from another iframe', () => { + window.frames['__uspapiLocator'] = 'uspframe'; + + init(mockedYmConfig); + + window.frames['__uspapiLocator'] = undefined; + + expect(window.postMessage.callCount).to.be.equal(1); + }); + + it('should add event listener for message event if usp consent is loaded from another iframe', () => { + window.frames['__uspapiLocator'] = 'uspframe'; + + init(mockedYmConfig); + + window.frames['__uspapiLocator'] = undefined; + + expect(window.addEventListener.calledOnceWith('message')).to.be.true; + }); + + it('should add usp consent string to ad server request params when called from iframe', () => { + const uspData = { uspString: 'testUspString' }; + const uspEvent = { + data: { + __uspapiReturn: { + callId: `${callId}`, + returnValue: uspData, + success: true, + } + }, + }; + + mathRandomStub.returns(callId); + addEventListenerStub.callsFake( + (e, callback) => { + callback(uspEvent) + } + ); + + window.frames['__uspapiLocator'] = 'cmpframe'; + + init(mockedYmConfig); - expect(gtag.addService.getCall(0)).to.not.be.null; - expect(gtag.setTargeting.getCall(0)).to.not.be.null; - expect(gtag.setTargeting.getCall(0).args[0]).to.exist.and.to.equal('ym_sim_p_id'); - expect(gtag.setTargeting.getCall(0).args[1]).to.exist.and.to.equal(mockedYmConfig.placementId); - expect(gtag.defineSlot.getCall(0)).to.not.be.null; - expect(gtag.enableServices.getCall(0)).to.not.be.null; - expect(gtag.display.getCall(0)).to.not.be.null; - expect(gtag.display.getCall(0).args[0]).to.exist.and.to.equal(containerName); - expect(gtag.pubads.getCall(0)).to.not.be.null; + window.frames['__uspapiLocator'] = undefined; - const gamContainerEl = window.document.getElementById(containerName); - expect(gamContainerEl).to.not.be.null; + const queryParams = getQuearyParamsFromUrl(requestMock.open.getCall(0).args[1]); - gamContainerEl.parentNode.removeChild(gamContainerEl); + expect(queryParams.us_privacy).to.be.equal(uspData.uspString); + }); }); });