From 3b0f8713676fbe380e4705d9c2c72716bb61b2f2 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 22 Apr 2021 14:56:16 +0200 Subject: [PATCH 01/10] Add tpcSupport, read it from xxhr and store it in compound cookie --- modules/parrableIdSystem.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 826dd9a933a..15ebd6ed593 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -53,6 +53,10 @@ function serializeParrableId(parrableId) { if (parrableId.ccpaOptout) { components.push('ccpaOptout:1'); } + if (parrableId.tpcSupport !== null) { + const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0' + components.push(tpcSupportComponent); + } return components.join(','); } @@ -190,10 +194,12 @@ function fetchId(configParams, gdprConsentData) { const eid = (parrableId) ? parrableId.eid : null; const refererInfo = getRefererInfo(); + const tpcSupport = (parrableId) ? parrableId.tpcSupport : null + const tpcUntil = (parrableId) ? parrableId.tpcUntil : null const uspString = uspDataHandler.getConsentData(); const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; - const partners = configParams.partners || configParams.partner + const partners = configParams.partners || configParams.partner; const trackers = typeof partners === 'string' ? partners.split(',') : partners; @@ -203,9 +209,13 @@ function fetchId(configParams, gdprConsentData) { trackers, url: refererInfo.referer, prebidVersion: '$prebid.version$', - isIframe: utils.inIframe() + isIframe: utils.inIframe(), }; + if (Date.now() < tpcUntil) { + data.tpcSupport = tpcSupport; + } + const searchParams = { data: encodeBase64UrlSafe(btoa(JSON.stringify(data))), gdpr: gdprApplies ? 1 : 0, @@ -242,6 +252,10 @@ function fetchId(configParams, gdprConsentData) { if (responseObj.ibaOptout === true) { newParrableId.ibaOptout = true; } + if (responseObj.tpcSupport !== null) { + newParrableId.tpcSupport = responseObj.tpcSupport; + newParrableId.tpcUntil = new Date(Date.now() + responseObj.tpcSupportTtl); + } } } catch (error) { utils.logError(error); From c04ffdf3fa14aaad5d0bd3f8ae4805929a32993d Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 22 Apr 2021 15:10:51 +0200 Subject: [PATCH 02/10] Parse a cookie boolean number (0, 1) as a boolean value --- modules/parrableIdSystem.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 15ebd6ed593..40b75adc66a 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -34,8 +34,8 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); - // unpack a value of 1 as true - parrableId[pair[0]] = +pair[1] === 1 ? true : pair[1]; + // unpack a value of 0 or 1 as boolean + parrableId[pair[0]] = typeof pair[1] === 'number' ? Boolean(pair[1]) : pair[1]; }); return parrableId; From 235f13d5be5b24f34f1743f39158c31c6680be05 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 22 Apr 2021 17:30:40 +0200 Subject: [PATCH 03/10] Improve conditions --- modules/parrableIdSystem.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 40b75adc66a..88f4e6d4fe1 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -35,7 +35,7 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); // unpack a value of 0 or 1 as boolean - parrableId[pair[0]] = typeof pair[1] === 'number' ? Boolean(pair[1]) : pair[1]; + parrableId[pair[0]] = (pair[1] !== null && !isNaN(pair[1])) ? Boolean(+pair[1]) : pair[1]; }); return parrableId; @@ -53,8 +53,8 @@ function serializeParrableId(parrableId) { if (parrableId.ccpaOptout) { components.push('ccpaOptout:1'); } - if (parrableId.tpcSupport !== null) { - const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0' + if (parrableId.tpcSupport !== undefined) { + const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0'; components.push(tpcSupportComponent); } @@ -252,7 +252,7 @@ function fetchId(configParams, gdprConsentData) { if (responseObj.ibaOptout === true) { newParrableId.ibaOptout = true; } - if (responseObj.tpcSupport !== null) { + if (responseObj.tpcSupport !== undefined) { newParrableId.tpcSupport = responseObj.tpcSupport; newParrableId.tpcUntil = new Date(Date.now() + responseObj.tpcSupportTtl); } From 8e0a8a10c0bc0460aefc125160dcd71cec036d4a Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 23 Apr 2021 15:13:58 +0200 Subject: [PATCH 04/10] Add tests --- modules/parrableIdSystem.js | 4 +- test/spec/modules/parrableIdSystem_spec.js | 142 ++++++++++++++++++++- 2 files changed, 141 insertions(+), 5 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 88f4e6d4fe1..2523d1d735e 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -35,7 +35,7 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); // unpack a value of 0 or 1 as boolean - parrableId[pair[0]] = (pair[1] !== null && !isNaN(pair[1])) ? Boolean(+pair[1]) : pair[1]; + parrableId[pair[0]] = (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) ? Boolean(+pair[1]) : pair[1]; }); return parrableId; @@ -254,7 +254,7 @@ function fetchId(configParams, gdprConsentData) { } if (responseObj.tpcSupport !== undefined) { newParrableId.tpcSupport = responseObj.tpcSupport; - newParrableId.tpcUntil = new Date(Date.now() + responseObj.tpcSupportTtl); + newParrableId.tpcUntil = Date.now() + responseObj.tpcSupportTtl; } } } catch (error) { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 3f517a66c68..a8ce5afed19 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -21,6 +21,7 @@ const P_CONFIG_MOCK = { partners: 'parrable_test_partner_123,parrable_test_partner_456' } }; +const RESPONSE_HEADERS = { 'Content-Type': 'application/json' }; function getConfigMock() { return { @@ -57,6 +58,10 @@ function serializeParrableId(parrableId) { if (parrableId.ccpaOptout) { str += ',ccpaOptout:1'; } + if (parrableId.tpcSupport !== undefined) { + const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0' + str += `,${tpcSupportComponent}`; + } return str; } @@ -68,6 +73,7 @@ function writeParrableCookie(parrableId) { (new Date(Date.now() + 5000).toUTCString()), 'lax' ); + console.log(storage.getCookie(P_COOKIE_NAME)); } function removeParrableCookie() { @@ -83,7 +89,7 @@ function decodeBase64UrlSafe(encBase64) { return encBase64.replace(/[-_.]/g, (m) => DEC[m]); } -describe('Parrable ID System', function() { +describe.only('Parrable ID System', function() { describe('parrableIdSystem.getId()', function() { describe('response callback function', function() { let logErrorStub; @@ -125,7 +131,6 @@ describe('Parrable ID System', function() { { 'Content-Type': 'text/plain' }, JSON.stringify({ eid: P_XHR_EID }) ); - expect(callbackSpy.lastCall.lastArg).to.deep.equal({ eid: P_XHR_EID }); @@ -242,6 +247,137 @@ describe('Parrable ID System', function() { }) }); }); + + describe('third party cookie support', function () { + let logErrorStub; + let callbackSpy = sinon.spy(); + + beforeEach(function() { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + callbackSpy.resetHistory(); + }); + + afterEach(function() { + logErrorStub.restore(); + }); + + describe.only('when getting tpcSupport from XHR response', function () { + let request; + let dateNowStub; + const dateNowMock = 1; + const tpcSupportTtl = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + afterEach(() => { + server.resetHistory(); + }); + + after(() => { + server.respond(); + dateNowStub.restore(); + }); + + it('should pass tpcSupport: true to the callback', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: true, tpcSupportTtl }) + ); + + expect(callbackSpy.lastCall.args[0]).to.deep.equal({ + eid: P_XHR_EID, + tpcSupport: true, + tpcUntil: dateNowMock + tpcSupportTtl + }); + }); + + it('should pass tpcSupport: false to the callback', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + ); + + expect(callbackSpy.lastCall.args[0]).to.deep.equal({ + eid: P_XHR_EID, + tpcSupport: false, + tpcUntil: dateNowMock + tpcSupportTtl + }); + }); + + it('should not pass tpcSupport to the callback', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID }) + ); + + expect(callbackSpy.lastCall.args[0]).to.deep.equal({ + eid: P_XHR_EID + }); + }); + }); + + describe('when getting tpcSupport from compound cookie', function () { + let dateNowStub; + const dateNowMock = 1; + const tpcSupportTtl = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + afterEach(() => { + removeParrableCookie(); + }); + + it('should send tpcSupport: true', function () { + writeParrableCookie({ eid: P_COOKIE_EID, tpcSupport: true, tpcUntil: dateNowMock + tpcSupportTtl }); + const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); + + expect(getIdResult.id).to.deep.equal({ + eid: P_COOKIE_EID, + tpcSupport: true, + tpcUntil: dateNowMock + tpcSupportTtl + }); + }); + + it('should send tpcSupport: false in the xhr', function () { + writeParrableCookie({ eid: P_COOKIE_EID, tpcSupport: false, tpcUntil: dateNowMock + tpcSupportTtl }); + const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); + + expect(getIdResult.id).to.deep.equal({ + eid: P_COOKIE_EID, + tpcSupport: false, + tpcUntil: dateNowMock + tpcSupportTtl + }); + }); + + it('should not send tpcSupport in the xhr when not found', function () { + writeParrableCookie({ eid: P_COOKIE_EID }); + const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); + + expect(getIdResult.id).to.not.have.property('tpcSupport'); + }); + }); + }); }); describe('parrableIdSystem.decode()', function() { @@ -529,7 +665,7 @@ describe('Parrable ID System', function() { }); }); - describe('partners parsing', () => { + describe('partners parsing', function () { let callbackSpy = sinon.spy(); const partnersTestCase = [ From 320b68b5fb397b587e18c3341fb1bc71beaf43af Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 23 Apr 2021 18:00:27 +0200 Subject: [PATCH 05/10] Tests passing --- gulpfile.js | 2 +- modules/parrableIdSystem.js | 13 ++++++++++--- test/spec/modules/parrableIdSystem_spec.js | 18 +++++++----------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index ac8b8c2dcd5..b538668c74f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -383,7 +383,7 @@ gulp.task('build-bundle-dev', gulp.series(makeDevpackPkg, gulpBundle.bind(null, gulp.task('build-bundle-prod', gulp.series(makeWebpackPkg, gulpBundle.bind(null, false))); // public tasks (dependencies are needed for each task since they can be ran on their own) -gulp.task('test', gulp.series(clean, lint, test)); +gulp.task('test', gulp.series(clean, test)); gulp.task('test-coverage', gulp.series(clean, testCoverage)); gulp.task(viewCoverage); diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 2523d1d735e..5bd7194a096 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -34,8 +34,13 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); - // unpack a value of 0 or 1 as boolean - parrableId[pair[0]] = (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) ? Boolean(+pair[1]) : pair[1]; + if (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) { // unpack a value of 0 or 1 as boolean + parrableId[pair[0]] = Boolean(+pair[1]); + } else if (!isNaN(pair[1])) { // convert to number if is a number + parrableId[pair[0]] = +pair[1] + } else { + parrableId[pair[0]] = pair[1] + } }); return parrableId; @@ -55,7 +60,9 @@ function serializeParrableId(parrableId) { } if (parrableId.tpcSupport !== undefined) { const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0'; + const tpcUntil = `tpcUntil:${parrableId.tpcUntil}`; components.push(tpcSupportComponent); + components.push(tpcUntil); } return components.join(','); @@ -195,7 +202,7 @@ function fetchId(configParams, gdprConsentData) { const eid = (parrableId) ? parrableId.eid : null; const refererInfo = getRefererInfo(); const tpcSupport = (parrableId) ? parrableId.tpcSupport : null - const tpcUntil = (parrableId) ? parrableId.tpcUntil : null + const tpcUntil = (parrableId) ? +parrableId.tpcUntil : null const uspString = uspDataHandler.getConsentData(); const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index a8ce5afed19..d8659795b7b 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -59,8 +59,9 @@ function serializeParrableId(parrableId) { str += ',ccpaOptout:1'; } if (parrableId.tpcSupport !== undefined) { - const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0' + const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0'; str += `,${tpcSupportComponent}`; + str += `,tpcUntil:${parrableId.tpcUntil}`; } return str; } @@ -73,7 +74,6 @@ function writeParrableCookie(parrableId) { (new Date(Date.now() + 5000).toUTCString()), 'lax' ); - console.log(storage.getCookie(P_COOKIE_NAME)); } function removeParrableCookie() { @@ -258,13 +258,14 @@ describe.only('Parrable ID System', function() { afterEach(function () { callbackSpy.resetHistory(); + removeParrableCookie(); }); afterEach(function() { logErrorStub.restore(); }); - describe.only('when getting tpcSupport from XHR response', function () { + describe('when getting tpcSupport from XHR response', function () { let request; let dateNowStub; const dateNowMock = 1; @@ -274,12 +275,7 @@ describe.only('Parrable ID System', function() { dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); }); - afterEach(() => { - server.resetHistory(); - }); - after(() => { - server.respond(); dateNowStub.restore(); }); @@ -337,15 +333,15 @@ describe.only('Parrable ID System', function() { describe('when getting tpcSupport from compound cookie', function () { let dateNowStub; - const dateNowMock = 1; + const dateNowMock = Date.now(); const tpcSupportTtl = 1; before(() => { dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); }); - afterEach(() => { - removeParrableCookie(); + after(() => { + dateNowStub.restore(); }); it('should send tpcSupport: true', function () { From 925d0a45dfb6d2aac4c72edad9316c6ec39070a9 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 27 Apr 2021 14:17:54 +0200 Subject: [PATCH 06/10] Read the cookie splitting parrableId and params (tpc, tpcUntil) --- modules/parrableIdSystem.js | 50 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 5bd7194a096..17b26b328d3 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -46,21 +46,21 @@ function deserializeParrableId(parrableIdStr) { return parrableId; } -function serializeParrableId(parrableId) { +function serializeParrableId(parrableIdAndParams) { let components = []; - if (parrableId.eid) { - components.push('eid:' + parrableId.eid); + if (parrableIdAndParams.eid) { + components.push('eid:' + parrableIdAndParams.eid); } - if (parrableId.ibaOptout) { + if (parrableIdAndParams.ibaOptout) { components.push('ibaOptout:1'); } - if (parrableId.ccpaOptout) { + if (parrableIdAndParams.ccpaOptout) { components.push('ccpaOptout:1'); } - if (parrableId.tpcSupport !== undefined) { - const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0'; - const tpcUntil = `tpcUntil:${parrableId.tpcUntil}`; + if (parrableIdAndParams.tpcSupport !== undefined) { + const tpcSupportComponent = parrableIdAndParams.tpcSupport === true ? 'tpc:1' : 'tpc:0'; + const tpcUntil = `tpcUntil:${parrableIdAndParams.tpcUntil}`; components.push(tpcSupportComponent); components.push(tpcUntil); } @@ -95,14 +95,21 @@ function encodeBase64UrlSafe(base64) { function readCookie() { const parrableIdStr = storage.getCookie(PARRABLE_COOKIE_NAME); if (parrableIdStr) { - return deserializeParrableId(decodeURIComponent(parrableIdStr)); + const parsedCookie = deserializeParrableId(decodeURIComponent(parrableIdStr)); + const { tpc, tpcUntil, ...parrableId } = parsedCookie; + let { eid, ibaOptout, ccpaOptout, ...params } = parsedCookie; + + if (Date.now() >= tpcUntil) { + params.tpc = undefined; + } + return { parrableId, params }; } return null; } -function writeCookie(parrableId) { - if (parrableId) { - const parrableIdStr = encodeURIComponent(serializeParrableId(parrableId)); +function writeCookie(parrableIdAndParams) { + if (parrableIdAndParams) { + const parrableIdStr = encodeURIComponent(serializeParrableId(parrableIdAndParams)); storage.setCookie(PARRABLE_COOKIE_NAME, parrableIdStr, getExpirationDate(), 'lax'); } } @@ -189,7 +196,7 @@ function shouldFilterImpression(configParams, parrableId) { function fetchId(configParams, gdprConsentData) { if (!isValidConfig(configParams)) return; - let parrableId = readCookie(); + let { parrableId, params } = readCookie() || {}; if (!parrableId) { parrableId = readLegacyCookies(); migrateLegacyCookies(parrableId); @@ -199,10 +206,9 @@ function fetchId(configParams, gdprConsentData) { return null; } - const eid = (parrableId) ? parrableId.eid : null; + const eid = parrableId ? parrableId.eid : null; const refererInfo = getRefererInfo(); - const tpcSupport = (parrableId) ? parrableId.tpcSupport : null - const tpcUntil = (parrableId) ? +parrableId.tpcUntil : null + const tpcSupport = params ? params.tpc : null const uspString = uspDataHandler.getConsentData(); const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; @@ -217,12 +223,9 @@ function fetchId(configParams, gdprConsentData) { url: refererInfo.referer, prebidVersion: '$prebid.version$', isIframe: utils.inIframe(), + tpcSupport }; - if (Date.now() < tpcUntil) { - data.tpcSupport = tpcSupport; - } - const searchParams = { data: encodeBase64UrlSafe(btoa(JSON.stringify(data))), gdpr: gdprApplies ? 1 : 0, @@ -246,6 +249,7 @@ function fetchId(configParams, gdprConsentData) { const callbacks = { success: response => { let newParrableId = parrableId ? utils.deepClone(parrableId) : {}; + let newParams = {}; if (response) { try { let responseObj = JSON.parse(response); @@ -260,15 +264,15 @@ function fetchId(configParams, gdprConsentData) { newParrableId.ibaOptout = true; } if (responseObj.tpcSupport !== undefined) { - newParrableId.tpcSupport = responseObj.tpcSupport; - newParrableId.tpcUntil = Date.now() + responseObj.tpcSupportTtl; + newParams.tpcSupport = responseObj.tpcSupport; + newParams.tpcUntil = Math.floor((Date.now() + responseObj.tpcSupportTtl) / 1000); } } } catch (error) { utils.logError(error); cb(); } - writeCookie(newParrableId); + writeCookie({ ...newParrableId, ...newParams }); cb(newParrableId); } else { utils.logError('parrableId: ID fetch returned an empty result'); From e2273fc499e2cd42e49ca23e6d6bd7b5450baed0 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 27 Apr 2021 14:18:11 +0200 Subject: [PATCH 07/10] Adapt tests --- test/spec/modules/parrableIdSystem_spec.js | 75 ++++------------------ 1 file changed, 14 insertions(+), 61 deletions(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index d8659795b7b..53d21e045a9 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -89,7 +89,7 @@ function decodeBase64UrlSafe(encBase64) { return encBase64.replace(/[-_.]/g, (m) => DEC[m]); } -describe.only('Parrable ID System', function() { +describe('Parrable ID System', function() { describe('parrableIdSystem.getId()', function() { describe('response callback function', function() { let logErrorStub; @@ -268,7 +268,7 @@ describe.only('Parrable ID System', function() { describe('when getting tpcSupport from XHR response', function () { let request; let dateNowStub; - const dateNowMock = 1; + const dateNowMock = Date.now(); const tpcSupportTtl = 1; before(() => { @@ -279,7 +279,7 @@ describe.only('Parrable ID System', function() { dateNowStub.restore(); }); - it('should pass tpcSupport: true to the callback', function () { + it('should set tpcSupport: true and tpcUntil in the cookie', function () { let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); callback(callbackSpy); request = server.requests[0]; @@ -290,14 +290,12 @@ describe.only('Parrable ID System', function() { JSON.stringify({ eid: P_XHR_EID, tpcSupport: true, tpcSupportTtl }) ); - expect(callbackSpy.lastCall.args[0]).to.deep.equal({ - eid: P_XHR_EID, - tpcSupport: true, - tpcUntil: dateNowMock + tpcSupportTtl - }); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:1,tpcUntil:' + Math.floor((dateNowMock + tpcSupportTtl) / 1000)) + ); }); - it('should pass tpcSupport: false to the callback', function () { + it('should set tpcSupport: false and tpcUntil in the cookie', function () { let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); callback(callbackSpy); request = server.requests[0]; @@ -307,14 +305,12 @@ describe.only('Parrable ID System', function() { JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) ); - expect(callbackSpy.lastCall.args[0]).to.deep.equal({ - eid: P_XHR_EID, - tpcSupport: false, - tpcUntil: dateNowMock + tpcSupportTtl - }); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock + tpcSupportTtl) / 1000)) + ); }); - it('should not pass tpcSupport to the callback', function () { + it('should not set tpcSupport in the cookie', function () { let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); callback(callbackSpy); request = server.requests[0]; @@ -325,52 +321,9 @@ describe.only('Parrable ID System', function() { JSON.stringify({ eid: P_XHR_EID }) ); - expect(callbackSpy.lastCall.args[0]).to.deep.equal({ - eid: P_XHR_EID - }); - }); - }); - - describe('when getting tpcSupport from compound cookie', function () { - let dateNowStub; - const dateNowMock = Date.now(); - const tpcSupportTtl = 1; - - before(() => { - dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); - }); - - after(() => { - dateNowStub.restore(); - }); - - it('should send tpcSupport: true', function () { - writeParrableCookie({ eid: P_COOKIE_EID, tpcSupport: true, tpcUntil: dateNowMock + tpcSupportTtl }); - const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); - - expect(getIdResult.id).to.deep.equal({ - eid: P_COOKIE_EID, - tpcSupport: true, - tpcUntil: dateNowMock + tpcSupportTtl - }); - }); - - it('should send tpcSupport: false in the xhr', function () { - writeParrableCookie({ eid: P_COOKIE_EID, tpcSupport: false, tpcUntil: dateNowMock + tpcSupportTtl }); - const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); - - expect(getIdResult.id).to.deep.equal({ - eid: P_COOKIE_EID, - tpcSupport: false, - tpcUntil: dateNowMock + tpcSupportTtl - }); - }); - - it('should not send tpcSupport in the xhr when not found', function () { - writeParrableCookie({ eid: P_COOKIE_EID }); - const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK); - - expect(getIdResult.id).to.not.have.property('tpcSupport'); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID) + ); }); }); }); From 1fed201339fc0605b724546df4599d9f4d981ede Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 27 Apr 2021 14:18:25 +0200 Subject: [PATCH 08/10] Revert linting in test task --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index b538668c74f..ac8b8c2dcd5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -383,7 +383,7 @@ gulp.task('build-bundle-dev', gulp.series(makeDevpackPkg, gulpBundle.bind(null, gulp.task('build-bundle-prod', gulp.series(makeWebpackPkg, gulpBundle.bind(null, false))); // public tasks (dependencies are needed for each task since they can be ran on their own) -gulp.task('test', gulp.series(clean, test)); +gulp.task('test', gulp.series(clean, lint, test)); gulp.task('test-coverage', gulp.series(clean, testCoverage)); gulp.task(viewCoverage); From 925cdd292a891f7796d1b6456f943f2a2665411c Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 28 Apr 2021 15:41:42 +0200 Subject: [PATCH 09/10] Convert Date.now to seconds on reading cookie --- modules/parrableIdSystem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 17b26b328d3..7cabbf33e00 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -99,7 +99,7 @@ function readCookie() { const { tpc, tpcUntil, ...parrableId } = parsedCookie; let { eid, ibaOptout, ccpaOptout, ...params } = parsedCookie; - if (Date.now() >= tpcUntil) { + if ((Date.now() / 1000) >= tpcUntil) { params.tpc = undefined; } return { parrableId, params }; From 0a91ca20fb1459a76ed5b7e4e8f5bdee0604367f Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 29 Apr 2021 12:44:18 +0200 Subject: [PATCH 10/10] Add tests --- modules/parrableIdSystem.js | 6 +- test/spec/modules/parrableIdSystem_spec.js | 67 ++++++++++++++++++++-- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 7cabbf33e00..ed7563d7e7c 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -193,6 +193,10 @@ function shouldFilterImpression(configParams, parrableId) { return isBlocked() || !isAllowed(); } +function epochFromTtl(ttl) { + return Math.trunc((Date.now() / 1000) + ttl); +} + function fetchId(configParams, gdprConsentData) { if (!isValidConfig(configParams)) return; @@ -265,7 +269,7 @@ function fetchId(configParams, gdprConsentData) { } if (responseObj.tpcSupport !== undefined) { newParams.tpcSupport = responseObj.tpcSupport; - newParams.tpcUntil = Math.floor((Date.now() + responseObj.tpcSupportTtl) / 1000); + newParams.tpcUntil = epochFromTtl(responseObj.tpcSupportTtl); } } } catch (error) { diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 53d21e045a9..bbd0d3546ff 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -58,8 +58,8 @@ function serializeParrableId(parrableId) { if (parrableId.ccpaOptout) { str += ',ccpaOptout:1'; } - if (parrableId.tpcSupport !== undefined) { - const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0'; + if (parrableId.tpc !== undefined) { + const tpcSupportComponent = parrableId.tpc === true ? 'tpc:1' : 'tpc:0'; str += `,${tpcSupportComponent}`; str += `,tpcUntil:${parrableId.tpcUntil}`; } @@ -248,7 +248,7 @@ describe('Parrable ID System', function() { }); }); - describe('third party cookie support', function () { + describe('third party cookie support status', function () { let logErrorStub; let callbackSpy = sinon.spy(); @@ -291,7 +291,7 @@ describe('Parrable ID System', function() { ); expect(storage.getCookie(P_COOKIE_NAME)).to.equal( - encodeURIComponent('eid:' + P_XHR_EID + ',tpc:1,tpcUntil:' + Math.floor((dateNowMock + tpcSupportTtl) / 1000)) + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:1,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) ); }); @@ -306,7 +306,7 @@ describe('Parrable ID System', function() { ); expect(storage.getCookie(P_COOKIE_NAME)).to.equal( - encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock + tpcSupportTtl) / 1000)) + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) ); }); @@ -326,6 +326,63 @@ describe('Parrable ID System', function() { ); }); }); + + describe('when getting tpcSupport from cookie', function () { + let request; + let dateNowStub; + const dateNowMock = Date.now(); + const tpcSupportTtl = dateNowMock; + const tpcUntilExpired = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + after(() => { + dateNowStub.restore(); + }); + + it('should send tpcSupport in the XHR', function () { + writeParrableCookie({ + eid: P_COOKIE_EID, + tpc: true, + tpcUntil: (dateNowMock / 1000) + 1 + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.tpcSupport).to.equal(true); + }); + + it('should unset tpcSupport from cookie when tpcUntil reached', function () { + writeParrableCookie({ + eid: P_COOKIE_EID, + tpcSupport: true, + tpcUntil: tpcUntilExpired + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + ); + + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.tpcSupport).to.equal(undefined); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + }); }); });