From c867e7d10916416f5d072029b62e3743d1affcac Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Mon, 11 May 2020 10:38:13 -0400 Subject: [PATCH] parrableIdSystem: Consume US Privacy consent data (#5197) * Add unit coverage for parrableIdSystem getId callback * PBID-14: Pass uspString to Parrable as us_privacy query parameter * PBID-14: Simplify parrableIdSystem us_privacy test * PBID-14: Only send us_privacy to Parrable when a value exists * Remove path check from parrableIdSystem url test * Add missing extension to mock xhr import --- modules/parrableIdSystem.js | 12 +++- test/spec/modules/parrableIdSystem_spec.js | 83 +++++++++++++++++++--- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index cc569a79062..ba5fae81952 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -9,6 +9,7 @@ import * as utils from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getRefererInfo } from '../src/refererDetection.js'; +import { uspDataHandler } from '../src/adapterManager.js'; const PARRABLE_URL = 'https://h.parrable.com/prebid'; @@ -24,10 +25,11 @@ function isValidConfig(configParams) { return true; } -function fetchId(configParams, consentData, currentStoredId) { +function fetchId(configParams, currentStoredId) { if (!isValidConfig(configParams)) return; const refererInfo = getRefererInfo(); + const uspString = uspDataHandler.getConsentData(); const data = { eid: currentStoredId || null, @@ -40,6 +42,10 @@ function fetchId(configParams, consentData, currentStoredId) { _rand: Math.random() }; + if (uspString) { + searchParams.us_privacy = uspString; + } + const options = { method: 'GET', withCredentials: true @@ -88,8 +94,8 @@ export const parrableIdSubmodule = { * @param {ConsentData} [consentData] * @returns {function(callback:function)} */ - getId(configParams, consentData, currentStoredId) { - return fetchId(configParams, consentData, currentStoredId); + getId(configParams, gdprConsentData, currentStoredId) { + return fetchId(configParams, currentStoredId); } }; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 93415126a0a..662f63f6638 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -1,15 +1,19 @@ import { expect } from 'chai'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; +import { newStorageManager } from 'src/storageManager.js'; +import { getRefererInfo } from 'src/refererDetection.js'; +import { uspDataHandler } from 'src/adapterManager.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; -import { newStorageManager } from 'src/storageManager.js'; +import { server } from 'test/mocks/xhr.js'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const P_COOKIE_NAME = '_parrable_eid'; -const P_COOKIE_VALUE = '01.1563917337.test-eid'; +const P_COOKIE_EID = '01.1563917337.test-eid'; +const P_XHR_EID = '01.1588030911.test-new-eid' const P_CONFIG_MOCK = { name: 'parrableId', params: { @@ -31,6 +35,7 @@ describe('Parrable ID System', function() { } } } + function getAdUnitMock(code = 'adUnit-code') { return { code, @@ -46,33 +51,91 @@ describe('Parrable ID System', function() { }; } + describe('parrableIdSystem.getId()', function() { + let callbackSpy = sinon.spy(); + + beforeEach(function() { + callbackSpy.resetHistory(); + }); + + it('returns a callback used to refresh the ID', function() { + let getIdResponse = parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ); + expect(getIdResponse.callback).to.be.a('function'); + }); + + it('callback creates xhr to Parrable that synchronizes the ID', function() { + let getIdCallback = parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ).callback; + + getIdCallback(callbackSpy); + + let request = server.requests[0]; + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(queryParams.data)); + + expect(request.url).to.contain('h.parrable.com'); + expect(queryParams).to.not.have.property('us_privacy'); + expect(data).to.deep.equal({ + eid: P_COOKIE_EID, + trackers: P_CONFIG_MOCK.params.partner.split(','), + url: getRefererInfo().referer + }); + + server.requests[0].respond(200, + { 'Content-Type': 'text/plain' }, + JSON.stringify({ eid: P_XHR_EID }) + ); + + expect(callbackSpy.calledWith(P_XHR_EID)).to.be.true; + }); + + it('passes the uspString to Parrable', function() { + let uspString = '1YNN'; + uspDataHandler.setConsentData(uspString); + parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ).callback(callbackSpy); + expect(server.requests[0].url).to.contain('us_privacy=' + uspString); + }); + }); + describe('Parrable ID in Bid Request', function() { let adUnits; beforeEach(function() { adUnits = [getAdUnitMock()]; - }); - - it('should append parrableid to bid request', function(done) { // simulate existing browser local storage values storage.setCookie( P_COOKIE_NAME, - P_COOKIE_VALUE, + P_COOKIE_EID, (new Date(Date.now() + 5000).toUTCString()) ); - setSubmoduleRegistry([parrableIdSubmodule]); init(config); config.setConfig(getConfigMock()); + }); + + afterEach(function() { + storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); + }); + it('provides the parrableid in the bid request', function(done) { requestBidsHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(P_COOKIE_VALUE); + expect(bid.userId.parrableid).to.equal(P_COOKIE_EID); }); }); - storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); done(); }, { adUnits }); });