diff --git a/integrationExamples/gpt/adloox.html b/integrationExamples/gpt/adloox.html index e8920cf2ee1..fd61267479d 100644 --- a/integrationExamples/gpt/adloox.html +++ b/integrationExamples/gpt/adloox.html @@ -161,7 +161,11 @@ }, rubicon: { singleRequest: true - } + }, + // RTD module honors pageUrl for referrer detection and + // the analytics module uses this for the 'pageurl' macro + // N.B. set this to a non-example.com URL to see the video + //pageUrl: 'https://yourdomain.com/some/path/to/content.html' }); pbjs.enableAnalytics({ provider: 'adloox', diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index 2f4fd9334d2..1091b87a22d 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -11,6 +11,7 @@ import {auctionManager} from '../src/auctionManager.js'; import {AUCTION_COMPLETED} from '../src/auction.js'; import CONSTANTS from '../src/constants.json'; import {find} from '../src/polyfill.js'; +import {getRefererInfo} from '../src/refererDetection.js'; import { deepAccess, getGptSlotInfoForAdUnitCode, @@ -58,6 +59,10 @@ MACRO['targetelt'] = function(b, c) { MACRO['creatype'] = function(b, c) { return b.mediaType == 'video' ? ADLOOX_MEDIATYPE.VIDEO : ADLOOX_MEDIATYPE.DISPLAY; }; +MACRO['pageurl'] = function(b, c) { + const refererInfo = getRefererInfo(); + return (refererInfo.canonicalUrl || refererInfo.referer || '').substr(0, 300).split(/[?#]/)[0]; +}; MACRO['pbadslot'] = function(b, c) { const adUnit = find(auctionManager.getAdUnits(), a => b.adUnitCode === a.code); return deepAccess(adUnit, 'ortb2Imp.ext.data.pbadslot') || getGptSlotInfoForAdUnitCode(b.adUnitCode).gptSlot || b.adUnitCode; diff --git a/modules/adlooxAnalyticsAdapter.md b/modules/adlooxAnalyticsAdapter.md index c4618a2e3aa..e21261d0b8d 100644 --- a/modules/adlooxAnalyticsAdapter.md +++ b/modules/adlooxAnalyticsAdapter.md @@ -127,6 +127,7 @@ The following macros are available * `%%pbadslot%%`: [Prebid Ad Slot](https://docs.prebid.org/features/pbAdSlot.html) returns [`AdUnit.code`](https://docs.prebid.org/features/pbAdSlot.html) if set otherwise returns [`AdUnit.code`](https://docs.prebid.org/dev-docs/adunit-reference.html#adunit) * it is recommended you read the [Prebid Ad Slot section in the Adloox RTD Provider documentation](./adlooxRtdProvider.md#prebid-ad-slot) + * `%%pageurl%%`: [`canonicalUrl`](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-Page-URL) from the [`refererInfo` object](https://docs.prebid.org/dev-docs/bidder-adaptor.html#referrers) otherwise uses `referer` ### Functions diff --git a/modules/adlooxRtdProvider.js b/modules/adlooxRtdProvider.js index edd040faee2..bb8334ec8fe 100644 --- a/modules/adlooxRtdProvider.js +++ b/modules/adlooxRtdProvider.js @@ -16,6 +16,7 @@ import {config as _config} from '../src/config.js'; import {submodule} from '../src/hook.js'; import {ajax} from '../src/ajax.js'; import {getGlobal} from '../src/prebidGlobal.js'; +import {getRefererInfo} from '../src/refererDetection.js'; import { _each, deepAccess, @@ -297,6 +298,7 @@ function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { } } + const refererInfo = getRefererInfo(); const args = [ [ 'v', `pbjs-${getGlobal().version}` ], [ 'c', config.params.clientid ], @@ -305,7 +307,7 @@ function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { [ 'imp', config.params.imps ], [ 'fc_ip', config.params.freqcap_ip ], [ 'fc_ipua', config.params.freqcap_ipua ], - [ 'pn', document.location.pathname ] + [ 'pn', (refererInfo.canonicalUrl || refererInfo.referer || '').substr(0, 300).split(/[?#]/)[0] ] ]; if (!adUnits.length) { diff --git a/modules/adrinoBidAdapter.js b/modules/adrinoBidAdapter.js new file mode 100644 index 00000000000..4520066c3e7 --- /dev/null +++ b/modules/adrinoBidAdapter.js @@ -0,0 +1,74 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {triggerPixel} from '../src/utils.js'; +import {NATIVE} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'adrino'; +const REQUEST_METHOD = 'POST'; +const BIDDER_HOST = 'https://prd-prebid-bidder.adrino.io'; +const GVLID = 1072; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [NATIVE], + + isBidRequestValid: function (bid) { + return !!(bid.bidId) && + !!(bid.params) && + !!(bid.params.hash) && + (typeof bid.params.hash === 'string') && + !!(bid.mediaTypes) && + Object.keys(bid.mediaTypes).includes(NATIVE) && + (bid.bidder === BIDDER_CODE); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + const bidRequests = []; + + for (let i = 0; i < validBidRequests.length; i++) { + let requestData = { + bidId: validBidRequests[i].bidId, + nativeParams: validBidRequests[i].nativeParams, + placementHash: validBidRequests[i].params.hash, + referer: bidderRequest.refererInfo.referer, + userAgent: navigator.userAgent, + } + + if (bidderRequest && bidderRequest.gdprConsent) { + requestData.gdprConsent = { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + } + } + + bidRequests.push({ + method: REQUEST_METHOD, + url: BIDDER_HOST + '/bidder/bid/', + data: requestData, + options: { + contentType: 'application/json', + withCredentials: false, + } + }); + } + + return bidRequests; + }, + + interpretResponse: function (serverResponse, bidRequest) { + const response = serverResponse.body; + const bidResponses = []; + if (!response.noAd) { + bidResponses.push(response); + } + return bidResponses; + }, + + onBidWon: function (bid) { + if (bid['requestId']) { + triggerPixel(BIDDER_HOST + '/bidder/won/' + bid['requestId']); + } + } +}; + +registerBidder(spec); diff --git a/modules/adrinoBidAdapter.md b/modules/adrinoBidAdapter.md new file mode 100644 index 00000000000..5ec63a72736 --- /dev/null +++ b/modules/adrinoBidAdapter.md @@ -0,0 +1,45 @@ +# Overview + +``` +Module Name: Adrino Bidder Adapter +Module Type: Bidder Adapter +Maintainer: dev@adrino.pl +``` + +# Description + +Module connects to Adrino bidder to fetch bids. Only native format is supported. + +# Test Parameters + +``` +var adUnits = [ + code: '/12345678/prebid_native_example_1', + mediaTypes: { + native: { + image: { + required: true, + sizes: [[300, 210],[300,150],[140,100]] + }, + title: { + required: true + }, + sponsoredBy: { + required: false + }, + body: { + required: false + }, + icon: { + required: false + } + } + }, + bids: [{ + bidder: 'adrino', + params: { + hash: 'abcdef123456' + } + }] +]; +``` diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 88c643b484c..65e0d6e92eb 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -287,6 +287,7 @@ export function requestBidsHook(fn, reqBidsConfigObj) { if (!includes(Object.keys(cmpCallMap), userCMP)) { logWarn(`CMP framework (${userCMP}) is not a supported framework. Aborting consentManagement module and resuming auction.`); + gdprDataHandler.setConsentData(null); return hookConfig.nextFn.apply(hookConfig.context, hookConfig.args); } @@ -450,6 +451,7 @@ function exitModule(errMsg, hookConfig, extraArgs) { nextFn.apply(context, args); } else { logError(errMsg + ' Canceling auction as per consentManagement config.', extraArgs); + gdprDataHandler.setConsentData(null); if (typeof hookConfig.bidsBackHandler === 'function') { hookConfig.bidsBackHandler(); } else { @@ -469,7 +471,7 @@ export function resetConsentData() { consentData = undefined; userCMP = undefined; cmpVersion = 0; - gdprDataHandler.setConsentData(null); + gdprDataHandler.reset(); } /** @@ -507,6 +509,7 @@ export function setConsentConfig(config) { gdprScope = config.defaultGdprScope === true; logInfo('consentManagement module has been activated...'); + gdprDataHandler.enable(); if (userCMP === 'static') { if (isPlainObject(config.consentData)) { diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index 4a4c4ae0a55..75462221403 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -186,6 +186,7 @@ export function requestBidsHook(fn, reqBidsConfigObj) { if (!uspCallMap[consentAPI]) { logWarn(`USP framework (${consentAPI}) is not a supported framework. Aborting consentManagement module and resuming auction.`); + uspDataHandler.setConsentData(null); return hookConfig.nextFn.apply(hookConfig.context, hookConfig.args); } @@ -276,6 +277,7 @@ function exitModule(errMsg, hookConfig, extraArgs) { if (errMsg) { logWarn(errMsg + ' Resuming auction without consent data as per consentManagement config.', extraArgs); + uspDataHandler.setConsentData(null) // let core know that no consent data is available } nextFn.apply(context, args); } @@ -287,7 +289,7 @@ function exitModule(errMsg, hookConfig, extraArgs) { export function resetConsentData() { consentData = undefined; consentAPI = undefined; - uspDataHandler.setConsentData(null); + uspDataHandler.reset(); } /** @@ -315,6 +317,7 @@ export function setConsentConfig(config) { } logInfo('USPAPI consentManagement module has been activated...'); + uspDataHandler.enable(); if (consentAPI === 'static') { if (isPlainObject(config.consentData) && isPlainObject(config.consentData.getUSPData)) { diff --git a/package-lock.json b/package-lock.json index e7f349bd307..4c9bab3ea20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4775,13 +4775,13 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz", - "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.0.tgz", + "integrity": "sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001312", - "electron-to-chromium": "^1.4.71", + "caniuse-lite": "^1.0.30001313", + "electron-to-chromium": "^1.4.76", "escalade": "^3.1.1", "node-releases": "^2.0.2", "picocolors": "^1.0.0" @@ -5354,9 +5354,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001312", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", - "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", + "version": "1.0.30001314", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001314.tgz", + "integrity": "sha512-0zaSO+TnCHtHJIbpLroX7nsD+vYuOVjl3uzFbJO1wMVbuveJA0RK2WcQA9ZUIOiO0/ArMiMgHJLxfEZhQiC0kw==", "dev": true, "funding": { "type": "opencollective", @@ -7687,9 +7687,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.75", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz", - "integrity": "sha512-LxgUNeu3BVU7sXaKjUDD9xivocQLxFtq6wgERrutdY/yIOps3ODOZExK1jg8DTEg4U8TUCb5MLGeWFOYuxjF3Q==", + "version": "1.4.78", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.78.tgz", + "integrity": "sha512-o61+D/Lx7j/E0LIin/efOqeHpXhwi1TaQco9vUcRmr91m25SfZY6L5hWJDv/r+6kNjboFKgBw1LbfM0lbhuK6Q==", "dev": true }, "node_modules/emoji-regex": { @@ -7895,22 +7895,20 @@ } }, "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.57", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.57.tgz", + "integrity": "sha512-L7cCNoPwTkAp7IBHxrKLsh7NKiVFkcdxlP9vbVw9QUvb7gF0Mz9bEBN0WY9xqdTjGF907EMT/iG013vnbqwu1Q==", "dev": true, + "hasInstallScript": true, "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/es5-ext/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, "node_modules/es5-shim": { "version": "4.6.5", "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.5.tgz", @@ -12831,9 +12829,9 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", "dev": true }, "node_modules/http-proxy": { @@ -18252,9 +18250,9 @@ } }, "node_modules/postcss": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.7.tgz", - "integrity": "sha512-L9Ye3r6hkkCeOETQX6iOaWZgjp3LL6Lpqm6EtgbKrgqGGteRMNb9vzBfRL96YOSu8o7x3MfIH9Mo5cPJFGrW6A==", + "version": "8.4.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.8.tgz", + "integrity": "sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==", "dev": true, "optional": true, "dependencies": { @@ -18479,14 +18477,14 @@ } }, "node_modules/puppeteer-core": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.4.1.tgz", - "integrity": "sha512-AgRIWgIkUXXnbvoRhyveZnyoEYr3wTunSk2/evOfWvFs65GUzsrxnUTUSLgPM4MRshCQmRABW7qE1hDN1AD7nA==", + "version": "13.5.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.5.1.tgz", + "integrity": "sha512-dobVqWjV34ilyfQHR3BBnCYaekBYTi5MgegEYBRYd3s3uFy8jUpZEEWbaFjG9ETm+LGzR5Lmr0aF6LLuHtiuCg==", "dev": true, "dependencies": { "cross-fetch": "3.1.5", "debug": "4.3.3", - "devtools-protocol": "0.0.960912", + "devtools-protocol": "0.0.969999", "extract-zip": "2.0.1", "https-proxy-agent": "5.0.0", "pkg-dir": "4.2.0", @@ -18514,9 +18512,9 @@ } }, "node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.960912", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.960912.tgz", - "integrity": "sha512-I3hWmV9rWHbdnUdmMKHF2NuYutIM2kXz2mdXW8ha7TbRlGTVs+PF+PsB5QWvpCek4Fy9B+msiispCfwlhG5Sqg==", + "version": "0.0.969999", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.969999.tgz", + "integrity": "sha512-6GfzuDWU0OFAuOvBokXpXPLxjOJ5DZ157Ue3sGQQM3LgAamb8m0R0ruSfN0DDu+XG5XJgT50i6zZ/0o8RglreQ==", "dev": true }, "node_modules/puppeteer-core/node_modules/https-proxy-agent": { @@ -19433,9 +19431,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -21552,9 +21550,9 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", + "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", @@ -23327,7 +23325,6 @@ } }, "plugins/eslint": { - "name": "eslint-plugin-prebid", "version": "1.0.0", "dev": true, "license": "Apache-2.0" @@ -26953,13 +26950,13 @@ "dev": true }, "browserslist": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz", - "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.0.tgz", + "integrity": "sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001312", - "electron-to-chromium": "^1.4.71", + "caniuse-lite": "^1.0.30001313", + "electron-to-chromium": "^1.4.76", "escalade": "^3.1.1", "node-releases": "^2.0.2", "picocolors": "^1.0.0" @@ -27394,9 +27391,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001312", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", - "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", + "version": "1.0.30001314", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001314.tgz", + "integrity": "sha512-0zaSO+TnCHtHJIbpLroX7nsD+vYuOVjl3uzFbJO1wMVbuveJA0RK2WcQA9ZUIOiO0/ArMiMgHJLxfEZhQiC0kw==", "dev": true }, "caseless": { @@ -29225,9 +29222,9 @@ } }, "electron-to-chromium": { - "version": "1.4.75", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz", - "integrity": "sha512-LxgUNeu3BVU7sXaKjUDD9xivocQLxFtq6wgERrutdY/yIOps3ODOZExK1jg8DTEg4U8TUCb5MLGeWFOYuxjF3Q==", + "version": "1.4.78", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.78.tgz", + "integrity": "sha512-o61+D/Lx7j/E0LIin/efOqeHpXhwi1TaQco9vUcRmr91m25SfZY6L5hWJDv/r+6kNjboFKgBw1LbfM0lbhuK6Q==", "dev": true }, "emoji-regex": { @@ -29397,22 +29394,14 @@ } }, "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.57", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.57.tgz", + "integrity": "sha512-L7cCNoPwTkAp7IBHxrKLsh7NKiVFkcdxlP9vbVw9QUvb7gF0Mz9bEBN0WY9xqdTjGF907EMT/iG013vnbqwu1Q==", "dev": true, "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - }, - "dependencies": { - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - } + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" } }, "es5-shim": { @@ -33387,9 +33376,9 @@ } }, "http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", "dev": true }, "http-proxy": { @@ -37616,9 +37605,9 @@ "dev": true }, "postcss": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.7.tgz", - "integrity": "sha512-L9Ye3r6hkkCeOETQX6iOaWZgjp3LL6Lpqm6EtgbKrgqGGteRMNb9vzBfRL96YOSu8o7x3MfIH9Mo5cPJFGrW6A==", + "version": "8.4.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.8.tgz", + "integrity": "sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==", "dev": true, "optional": true, "requires": { @@ -37794,14 +37783,14 @@ "dev": true }, "puppeteer-core": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.4.1.tgz", - "integrity": "sha512-AgRIWgIkUXXnbvoRhyveZnyoEYr3wTunSk2/evOfWvFs65GUzsrxnUTUSLgPM4MRshCQmRABW7qE1hDN1AD7nA==", + "version": "13.5.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.5.1.tgz", + "integrity": "sha512-dobVqWjV34ilyfQHR3BBnCYaekBYTi5MgegEYBRYd3s3uFy8jUpZEEWbaFjG9ETm+LGzR5Lmr0aF6LLuHtiuCg==", "dev": true, "requires": { "cross-fetch": "3.1.5", "debug": "4.3.3", - "devtools-protocol": "0.0.960912", + "devtools-protocol": "0.0.969999", "extract-zip": "2.0.1", "https-proxy-agent": "5.0.0", "pkg-dir": "4.2.0", @@ -37823,9 +37812,9 @@ } }, "devtools-protocol": { - "version": "0.0.960912", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.960912.tgz", - "integrity": "sha512-I3hWmV9rWHbdnUdmMKHF2NuYutIM2kXz2mdXW8ha7TbRlGTVs+PF+PsB5QWvpCek4Fy9B+msiispCfwlhG5Sqg==", + "version": "0.0.969999", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.969999.tgz", + "integrity": "sha512-6GfzuDWU0OFAuOvBokXpXPLxjOJ5DZ157Ue3sGQQM3LgAamb8m0R0ruSfN0DDu+XG5XJgT50i6zZ/0o8RglreQ==", "dev": true }, "https-proxy-agent": { @@ -38545,9 +38534,9 @@ } }, "rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -40272,9 +40261,9 @@ "dev": true }, "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", + "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", "dev": true, "requires": { "@types/json5": "^0.0.29", diff --git a/src/adapterManager.js b/src/adapterManager.js index 89da900ae0a..a72b5daf6bf 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -20,7 +20,6 @@ import { logWarn, shuffle, timestamp, - isStr } from './utils.js'; import {processAdUnitsForLabels} from './sizeMapping.js'; import { decorateAdUnitsWithNativeParams, nativeAdapters } from './native.js'; @@ -31,6 +30,7 @@ import { hook } from './hook.js'; import {includes, find} from './polyfill.js'; import { adunitCounter } from './adUnits.js'; import { getRefererInfo } from './refererDetection.js'; +import {GdprConsentHandler, UspConsentHandler} from './consentHandler.js'; var CONSTANTS = require('./constants.json'); var events = require('./events.js'); @@ -141,47 +141,8 @@ function getAdUnitCopyForClientAdapters(adUnits) { return adUnitsClientCopy; } -export let gdprDataHandler = { - consentData: null, - generatedTime: null, - setConsentData: function(consentInfo, time = timestamp()) { - gdprDataHandler.consentData = consentInfo; - gdprDataHandler.generatedTime = time; - }, - getConsentData: function() { - return gdprDataHandler.consentData; - }, - getConsentMeta: function() { - if (gdprDataHandler.consentData && gdprDataHandler.consentData.vendorData && gdprDataHandler.generatedTime) { - return { - gdprApplies: gdprDataHandler.consentData.gdprApplies, - consentStringSize: (isStr(gdprDataHandler.consentData.vendorData.tcString)) ? gdprDataHandler.consentData.vendorData.tcString.length : 0, - generatedAt: gdprDataHandler.generatedTime, - apiVersion: gdprDataHandler.consentData.apiVersion - }; - } - } -}; - -export let uspDataHandler = { - consentData: null, - generatedTime: null, - setConsentData: function(consentInfo, time = timestamp()) { - uspDataHandler.consentData = consentInfo; - uspDataHandler.generatedTime = time; - }, - getConsentData: function() { - return uspDataHandler.consentData; - }, - getConsentMeta: function() { - if (uspDataHandler.consentData && uspDataHandler.generatedTime) { - return { - usp: uspDataHandler.consentData, - generatedAt: uspDataHandler.generatedTime - } - } - } -}; +export let gdprDataHandler = new GdprConsentHandler(); +export let uspDataHandler = new UspConsentHandler(); export let coppaDataHandler = { getCoppa: function() { diff --git a/src/consentHandler.js b/src/consentHandler.js new file mode 100644 index 00000000000..0df9e6fcb3b --- /dev/null +++ b/src/consentHandler.js @@ -0,0 +1,98 @@ +import {isStr, timestamp} from './utils.js'; + +export class ConsentHandler { + #enabled; + #data; + #promise; + #resolve; + #ready; + generatedTime; + + constructor() { + this.reset(); + } + + /** + * reset this handler (mainly for tests) + */ + reset() { + this.#promise = new Promise((resolve) => { + this.#resolve = (data) => { + this.#ready = true; + this.#data = data; + resolve(data); + }; + }); + this.#enabled = false; + this.#data = null; + this.#ready = false; + this.generatedTime = null; + } + + /** + * Enable this consent handler. This should be called by the relevant consent management module + * on initialization. + */ + enable() { + this.#enabled = true; + } + + /** + * @returns {boolean} true if the related consent management module is enabled. + */ + get enabled() { + return this.#enabled; + } + + /** + * @returns {boolean} true if consent data has been resolved (it may be `null` if the resolution failed). + */ + get ready() { + return this.#ready; + } + + /** + * @returns a promise than resolves to the consent data, or null if no consent data is available + */ + get promise() { + if (!this.#enabled) { + this.#resolve(null); + } + return this.#promise; + } + + setConsentData(data, time = timestamp()) { + this.generatedTime = time; + this.#resolve(data); + } + + getConsentData() { + return this.#data; + } +} + +export class UspConsentHandler extends ConsentHandler { + getConsentMeta() { + const consentData = this.getConsentData(); + if (consentData && this.generatedTime) { + return { + usp: consentData, + generatedAt: this.generatedTime + }; + } + } +} + +export class GdprConsentHandler extends ConsentHandler { + getConsentMeta() { + const consentData = this.getConsentData(); + if (consentData && consentData.vendorData && this.generatedTime) { + return { + gdprApplies: consentData.gdprApplies, + consentStringSize: (isStr(consentData.vendorData.tcString)) ? consentData.vendorData.tcString.length : 0, + generatedAt: this.generatedTime, + apiVersion: consentData.apiVersion + } + } + } +} diff --git a/test/spec/modules/adrinoBidAdapter_spec.js b/test/spec/modules/adrinoBidAdapter_spec.js new file mode 100644 index 00000000000..52b2796e6db --- /dev/null +++ b/test/spec/modules/adrinoBidAdapter_spec.js @@ -0,0 +1,204 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adrinoBidAdapter.js'; +import * as utils from '../../../src/utils'; + +describe('adrinoBidAdapter', function () { + describe('isBidRequestValid', function () { + const validBid = { + bidder: 'adrino', + params: { + hash: 'abcdef123456' + }, + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true, + sizes: [[300, 150], [300, 210]] + } + } + }, + adUnitCode: 'adunit-code', + bidId: '12345678901234', + bidderRequestId: '98765432109876', + auctionId: '01234567891234', + }; + + it('should return true when all mandatory parameters are there', function () { + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should return false when there are no params', function () { + const bid = { ...validBid }; + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when unsupported media type is requested', function () { + const bid = { ...validBid }; + bid.mediaTypes = { banner: { sizes: [[300, 250]] } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when hash is not a string', function () { + const bid = { ...validBid }; + bid.params.hash = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequest = { + bidder: 'adrino', + params: { + hash: 'abcdef123456' + }, + mediaTypes: { + native: { + title: { + required: true + }, + image: { + required: true, + sizes: [[300, 150], [300, 210]] + } + } + }, + adUnitCode: 'adunit-code', + bidId: '12345678901234', + bidderRequestId: '98765432109876', + auctionId: '01234567891234', + }; + + it('should build the request correctly with gdpr', function () { + const result = spec.buildRequests( + [ bidRequest ], + { gdprConsent: { gdprApplies: true, consentString: 'abc123' }, refererInfo: { referer: 'http://example.com/' } } + ); + expect(result.length).to.equal(1); + expect(result[0].method).to.equal('POST'); + expect(result[0].url).to.equal('https://prd-prebid-bidder.adrino.io/bidder/bid/'); + expect(result[0].data.bidId).to.equal('12345678901234'); + expect(result[0].data.placementHash).to.equal('abcdef123456'); + expect(result[0].data.referer).to.equal('http://example.com/'); + expect(result[0].data.userAgent).to.equal(navigator.userAgent); + expect(result[0].data).to.have.property('nativeParams'); + expect(result[0].data).to.have.property('gdprConsent'); + }); + + it('should build the request correctly without gdpr', function () { + const result = spec.buildRequests( + [ bidRequest ], + { refererInfo: { referer: 'http://example.com/' } } + ); + expect(result.length).to.equal(1); + expect(result[0].method).to.equal('POST'); + expect(result[0].url).to.equal('https://prd-prebid-bidder.adrino.io/bidder/bid/'); + expect(result[0].data.bidId).to.equal('12345678901234'); + expect(result[0].data.placementHash).to.equal('abcdef123456'); + expect(result[0].data.referer).to.equal('http://example.com/'); + expect(result[0].data.userAgent).to.equal(navigator.userAgent); + expect(result[0].data).to.have.property('nativeParams'); + expect(result[0].data).not.to.have.property('gdprConsent'); + }); + }); + + describe('interpretResponse', function () { + it('should interpret the response correctly', function () { + const response = { + requestId: '31662c69728811', + mediaType: 'native', + cpm: 0.53, + currency: 'PLN', + creativeId: '859115', + netRevenue: true, + ttl: 600, + width: 1, + height: 1, + noAd: false, + testAd: false, + native: { + title: 'Ad Title', + body: 'Ad Body', + image: { + url: 'http://emisja.contentstream.pl/_/getImageII/?vid=17180728299&typ=cs_300_150&element=IMAGE&scale=1&prefix=adart&nc=1643878278955', + height: 150, + width: 300 + }, + clickUrl: 'http://emisja.contentstream.pl/_/ctr2/?u=https%3A%2F%2Fonline.efortuna.pl%2Fpage%3Fkey%3Dej0xMzUzMTM1NiZsPTE1Mjc1MzY1JnA9NTMyOTA%253D&e=znU3tABN8K4N391dmUxYfte5G9tBaDXELJVo1_-kvaTJH2XwWRw77fmfL2YjcEmrbqRQ3M0GcJ0vPWcLtZlsrf8dWrAEHNoZKAC6JMnZF_65IYhTPbQIJ-zn3ac9TU7gEZftFKksH1al7rMuieleVv9r6_DtrOk_oZcYAe4rMRQM-TiWvivJRPBchAAblE0cqyG7rCunJFpal43sxlYm4GvcBJaYHzErn5PXjEzNbd3xHjkdiap-xU9y6BbfkUZ1xIMS8QZLvwNrTXMFCSfSRN2tgVfEj7KyGdLCITHSaFtuIKT2iW2pxC7f2RtPHnzsEPXH0SgAfhA3OxZ5jkQjOZy0PsO7MiCv3sJai5ezUAOjFgayU91ZhI0Y9r2YpB1tTGIjnO23wot8PvRENlThHQ%3D%3D&ref=https%3A%2F%2Fbox.adrino.cloud%2Ftmielcarz%2Fadrino_prebid%2Ftest_page3.html%3Fpbjs_debug%3Dtrue', + privacyLink: 'https://adrino.pl/wp-content/uploads/2021/01/POLITYKA-PRYWATNOS%CC%81CI-Adrino-Mobile.pdf', + impressionTrackers: [ + 'https://prd-impression-tracker-producer.adrino.io/impression/eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ7XCJpbXByZXNzaW9uSWRcIjpcIjMxNjYyYzY5NzI4ODExXCIsXCJkYXRlXCI6WzIwMjIsMiwzXSxcInBsYWNlbWVudEhhc2hcIjpcIjk0NTVjMDQxYzlkMTI1ZmIwNDE4MWVhMGVlZTJmMmFlXCIsXCJjYW1wYWlnbklkXCI6MTc5MjUsXCJhZHZlcnRpc2VtZW50SWRcIjo5MjA3OSxcInZpc3VhbGlzYXRpb25JZFwiOjg1OTExNSxcImNwbVwiOjUzLjB9IiwiZXhwIjoxNjQzOTE2MjUxLCJpYXQiOjE2NDM5MTU2NTF9.0Y_HvInGl6Xo5xP6rDLC8lzQRGvy-wKe0blk1o8ebWyVRFiUY1JGLUeE0k3sCsPNxgdHAv-o6EcbogpUuqlMJA' + ] + } + }; + + const serverResponse = { + body: response + }; + + const result = spec.interpretResponse(serverResponse, {}); + expect(result.length).to.equal(1); + expect(result[0]).to.equal(response); + }); + + it('should return empty array of responses', function () { + const response = { + requestId: '31662c69728811', + noAd: true, + testAd: false + }; + + const serverResponse = { + body: response + }; + + const result = spec.interpretResponse(serverResponse, {}); + expect(result.length).to.equal(0); + }); + }); + + describe('onBidWon', function () { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function() { + utils.triggerPixel.restore(); + }); + + it('should trigger pixel', function () { + const response = { + requestId: '31662c69728811', + mediaType: 'native', + cpm: 0.53, + currency: 'PLN', + creativeId: '859115', + netRevenue: true, + ttl: 600, + width: 1, + height: 1, + noAd: false, + testAd: false, + native: { + title: 'Ad Title', + body: 'Ad Body', + image: { + url: 'http://emisja.contentstream.pl/_/getImageII/?vid=17180728299&typ=cs_300_150&element=IMAGE&scale=1&prefix=adart&nc=1643878278955', + height: 150, + width: 300 + }, + clickUrl: 'http://emisja.contentstream.pl/_/ctr2/?u=https%3A%2F%2Fonline.efortuna.pl%2Fpage%3Fkey%3Dej0xMzUzMTM1NiZsPTE1Mjc1MzY1JnA9NTMyOTA%253D&e=znU3tABN8K4N391dmUxYfte5G9tBaDXELJVo1_-kvaTJH2XwWRw77fmfL2YjcEmrbqRQ3M0GcJ0vPWcLtZlsrf8dWrAEHNoZKAC6JMnZF_65IYhTPbQIJ-zn3ac9TU7gEZftFKksH1al7rMuieleVv9r6_DtrOk_oZcYAe4rMRQM-TiWvivJRPBchAAblE0cqyG7rCunJFpal43sxlYm4GvcBJaYHzErn5PXjEzNbd3xHjkdiap-xU9y6BbfkUZ1xIMS8QZLvwNrTXMFCSfSRN2tgVfEj7KyGdLCITHSaFtuIKT2iW2pxC7f2RtPHnzsEPXH0SgAfhA3OxZ5jkQjOZy0PsO7MiCv3sJai5ezUAOjFgayU91ZhI0Y9r2YpB1tTGIjnO23wot8PvRENlThHQ%3D%3D&ref=https%3A%2F%2Fbox.adrino.cloud%2Ftmielcarz%2Fadrino_prebid%2Ftest_page3.html%3Fpbjs_debug%3Dtrue', + privacyLink: 'https://adrino.pl/wp-content/uploads/2021/01/POLITYKA-PRYWATNOS%CC%81CI-Adrino-Mobile.pdf', + impressionTrackers: [ + 'https://prd-impression-tracker-producer.adrino.io/impression/eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ7XCJpbXByZXNzaW9uSWRcIjpcIjMxNjYyYzY5NzI4ODExXCIsXCJkYXRlXCI6WzIwMjIsMiwzXSxcInBsYWNlbWVudEhhc2hcIjpcIjk0NTVjMDQxYzlkMTI1ZmIwNDE4MWVhMGVlZTJmMmFlXCIsXCJjYW1wYWlnbklkXCI6MTc5MjUsXCJhZHZlcnRpc2VtZW50SWRcIjo5MjA3OSxcInZpc3VhbGlzYXRpb25JZFwiOjg1OTExNSxcImNwbVwiOjUzLjB9IiwiZXhwIjoxNjQzOTE2MjUxLCJpYXQiOjE2NDM5MTU2NTF9.0Y_HvInGl6Xo5xP6rDLC8lzQRGvy-wKe0blk1o8ebWyVRFiUY1JGLUeE0k3sCsPNxgdHAv-o6EcbogpUuqlMJA' + ] + } + }; + + spec.onBidWon(response); + expect(utils.triggerPixel.callCount).to.equal(1) + }); + }); +}); diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index c3ee30afc7d..6dc46192128 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -8,9 +8,9 @@ import { } from 'modules/consentManagementUsp.js'; import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; -import { uspDataHandler } from 'src/adapterManager.js'; +import {gdprDataHandler, uspDataHandler} from 'src/adapterManager.js'; +import 'src/prebid.js'; -let assert = require('chai').assert; let expect = require('chai').expect; function createIFrameMarker() { @@ -97,6 +97,21 @@ describe('consentManagement', function () { expect(consentAPI).to.be.equal('daa'); expect(consentTimeout).to.be.equal(7500); }); + + it('should enable uspDataHandler', () => { + setConsentConfig({usp: {cmpApi: 'daa', timeout: 7500}}); + expect(uspDataHandler.enabled).to.be.true; + }); + + it('should call setConsentData(null) on invalid CMP api', () => { + setConsentConfig({usp: {cmpApi: 'invalid'}}); + let hookRan = false; + requestBidsHook(() => { + hookRan = true; + }, {}); + expect(hookRan).to.be.true; + expect(uspDataHandler.ready).to.be.true; + }); }); describe('static consent string setConsentConfig value', () => { @@ -226,6 +241,32 @@ describe('consentManagement', function () { expect(consent).to.equal(testConsentData.uspString); sinon.assert.called(uspStub); }); + + it('should call uspDataHandler.setConsentData(null) on error', () => { + let hookRan = false; + uspStub = sinon.stub(window, '__uspapi').callsFake((...args) => { + args[2](null, false); + }); + requestBidsHook(() => { + hookRan = true; + }, {}); + expect(hookRan).to.be.true; + expect(uspDataHandler.ready).to.be.true; + expect(uspDataHandler.getConsentData()).to.equal(null); + }); + + it('should call uspDataHandler.setConsentData(null) on timeout', (done) => { + setConsentConfig({usp: {timeout: 10}}); + let hookRan = false; + uspStub = sinon.stub(window, '__uspapi').callsFake(() => {}); + requestBidsHook(() => { hookRan = true; }, {}); + setTimeout(() => { + expect(hookRan).to.be.true; + expect(uspDataHandler.ready).to.be.true; + expect(uspDataHandler.getConsentData()).to.equal(null); + done(); + }, 20) + }); }); describe('USPAPI workflow for iframed page', function () { diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 3b027f0845c..d95af454818 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -132,6 +132,11 @@ describe('consentManagement', function () { }); expect(gdprScope).to.be.equal(false); }); + + it('should enable gdprDataHandler', () => { + setConsentConfig({gdpr: {}}); + expect(gdprDataHandler.enabled).to.be.true; + }); }); describe('static consent string setConsentConfig value', () => { @@ -326,6 +331,14 @@ describe('consentManagement', function () { expect(consent).to.be.null; }); + it('should call gpdrDataHandler.setConsentData() when unknown CMP api is used', () => { + setConsentConfig({gdpr: {cmpApi: 'invalid'}}); + let hookRan = false; + requestBidsHook(() => { hookRan = true; }, {}); + expect(hookRan).to.be.true; + expect(gdprDataHandler.ready).to.be.true; + }) + it('should throw proper errors when CMP is not found', function () { setConsentConfig(goodConfigWithCancelAuction); @@ -337,6 +350,7 @@ describe('consentManagement', function () { sinon.assert.calledTwice(utils.logError); expect(didHookReturn).to.be.false; expect(consent).to.be.null; + expect(gdprDataHandler.ready).to.be.true; }); }); @@ -748,6 +762,7 @@ describe('consentManagement', function () { expect(didHookReturn).to.be.false; expect(bidsBackHandlerReturn).to.be.true; expect(consent).to.be.null; + expect(gdprDataHandler.ready).to.be.true; }); it('allows the auction when CMP is unresponsive', (done) => { @@ -766,6 +781,7 @@ describe('consentManagement', function () { const consent = gdprDataHandler.getConsentData(); expect(consent.gdprApplies).to.be.true; expect(consent.consentString).to.be.undefined; + expect(gdprDataHandler.ready).to.be.true; done(); }, 20); }); diff --git a/test/spec/unit/core/consentHandler_spec.js b/test/spec/unit/core/consentHandler_spec.js new file mode 100644 index 00000000000..bee5a2d9522 --- /dev/null +++ b/test/spec/unit/core/consentHandler_spec.js @@ -0,0 +1,41 @@ +import {ConsentHandler} from '../../../../src/consentHandler.js'; + +describe('Consent data handler', () => { + let handler; + beforeEach(() => { + handler = new ConsentHandler(); + }) + + it('should be disabled, return null data on init', () => { + expect(handler.enabled).to.be.false; + expect(handler.getConsentData()).to.equal(null); + }) + + it('should resolve promise to null when disabled', () => { + return handler.promise.then((data) => { + expect(data).to.equal(null); + }); + }); + + it('should return data after setConsentData', () => { + const data = {consent: 'string'}; + handler.enable(); + handler.setConsentData(data); + expect(handler.getConsentData()).to.equal(data); + }); + + it('should resolve .promise to data after setConsentData', (done) => { + let actual = null; + const data = {consent: 'string'}; + handler.enable(); + handler.promise.then((d) => actual = d); + setTimeout(() => { + expect(actual).to.equal(null); + handler.setConsentData(data); + setTimeout(() => { + expect(actual).to.equal(data); + done(); + }) + }) + }); +})