Skip to content

Commit

Permalink
Add support for Publisher Common ID Module (#5871)
Browse files Browse the repository at this point in the history
- New user id value to be sent to STR Ad Server as `pubcid` of the bid request object

Story: [#175125639](https://www.pivotaltracker.com/story/show/175125639)
  • Loading branch information
madma authored Oct 27, 2020
1 parent 894c1d7 commit 6f531d5
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 54 deletions.
6 changes: 6 additions & 0 deletions modules/sharethroughBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export const sharethroughAdapterSpec = {
query.ttduid = bidRequest.userId.tdid;
}

if (bidRequest.userId && bidRequest.userId.pubcid) {
query.pubcid = bidRequest.userId.pubcid;
} else if (bidRequest.crumbs && bidRequest.crumbs.pubcid) {
query.pubcid = bidRequest.crumbs.pubcid;
}

if (bidRequest.schain) {
query.schain = JSON.stringify(bidRequest.schain);
}
Expand Down
127 changes: 73 additions & 54 deletions test/spec/modules/sharethroughBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ const bidRequests = [
params: {
pkey: 'aaaa1111'
},
userId: { tdid: 'fake-tdid' }
userId: {
tdid: 'fake-tdid',
pubcid: 'fake-pubcid'
},
crumbs: {
pubcid: 'fake-pubcid-in-crumbs-obj'
}
},
{
bidder: 'sharethrough',
Expand All @@ -33,8 +39,8 @@ const bidRequests = [
pkey: 'cccc3333',
iframe: true,
iframeSize: [500, 500]
},
},
}
}
];

const prebidRequests = [
Expand Down Expand Up @@ -98,7 +104,7 @@ const prebidRequests = [
skipIframeBusting: false,
sizes: [[300, 250], [300, 300], [250, 250], [600, 50]]
}
},
}
];

const bidderResponse = {
Expand All @@ -121,12 +127,12 @@ const bidderResponse = {
};

const setUserAgent = (uaString) => {
window.navigator['__defineGetter__']('userAgent', function () {
window.navigator['__defineGetter__']('userAgent', function() {
return uaString;
});
};

describe('sharethrough internal spec', function () {
describe('sharethrough internal spec', function() {
let windowSpy, windowTopSpy;

beforeEach(function() {
Expand All @@ -141,7 +147,7 @@ describe('sharethrough internal spec', function () {
window.top.STR = undefined;
});

describe('we cannot access top level document', function () {
describe('we cannot access top level document', function() {
beforeEach(function() {
window.lockedInFrame = true;
});
Expand All @@ -150,27 +156,27 @@ describe('sharethrough internal spec', function () {
window.lockedInFrame = false;
});

it('appends sfp.js to the safeframe', function () {
it('appends sfp.js to the safeframe', function() {
sharethroughInternal.handleIframe();
expect(windowSpy.calledOnce).to.be.true;
});

it('does not append anything if sfp.js is already loaded in the safeframe', function () {
it('does not append anything if sfp.js is already loaded in the safeframe', function() {
window.STR = { Tag: true };
sharethroughInternal.handleIframe();
expect(windowSpy.notCalled).to.be.true;
expect(windowTopSpy.notCalled).to.be.true;
});
});

describe('we are able to bust out of the iframe', function () {
it('appends sfp.js to window.top', function () {
describe('we are able to bust out of the iframe', function() {
it('appends sfp.js to window.top', function() {
sharethroughInternal.handleIframe();
expect(windowSpy.calledOnce).to.be.true;
expect(windowTopSpy.calledOnce).to.be.true;
});

it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function () {
it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function() {
window.top.STR = { Tag: true };
sharethroughInternal.handleIframe();
expect(windowSpy.calledOnce).to.be.true;
Expand All @@ -179,15 +185,15 @@ describe('sharethrough internal spec', function () {
});
});

describe('sharethrough adapter spec', function () {
describe('.code', function () {
it('should return a bidder code of sharethrough', function () {
describe('sharethrough adapter spec', function() {
describe('.code', function() {
it('should return a bidder code of sharethrough', function() {
expect(spec.code).to.eql('sharethrough');
});
});

describe('.isBidRequestValid', function () {
it('should return false if req has no pkey', function () {
describe('.isBidRequestValid', function() {
it('should return false if req has no pkey', function() {
const invalidBidRequest = {
bidder: 'sharethrough',
params: {
Expand All @@ -197,7 +203,7 @@ describe('sharethrough adapter spec', function () {
expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false);
});

it('should return false if req has wrong bidder code', function () {
it('should return false if req has wrong bidder code', function() {
const invalidBidRequest = {
bidder: 'notSharethrough',
params: {
Expand All @@ -207,22 +213,22 @@ describe('sharethrough adapter spec', function () {
expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false);
});

it('should return true if req is correct', function () {
it('should return true if req is correct', function() {
expect(spec.isBidRequestValid(bidRequests[0])).to.eq(true);
expect(spec.isBidRequestValid(bidRequests[1])).to.eq(true);
})
});
});

describe('.buildRequests', function () {
it('should return an array of requests', function () {
describe('.buildRequests', function() {
it('should return an array of requests', function() {
const builtBidRequests = spec.buildRequests(bidRequests);

expect(builtBidRequests[0].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1');
expect(builtBidRequests[1].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1');
expect(builtBidRequests[0].method).to.eq('GET');
});

it('should set the instant_play_capable parameter correctly based on browser userAgent string', function () {
it('should set the instant_play_capable parameter correctly based on browser userAgent string', function() {
setUserAgent('Android Chrome/60');
let builtBidRequests = spec.buildRequests(bidRequests);
expect(builtBidRequests[0].data.instant_play_capable).to.be.true;
Expand Down Expand Up @@ -252,51 +258,64 @@ describe('sharethrough adapter spec', function () {
const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('http:');
const bidRequest = spec.buildRequests(bidRequests, null)[0];
expect(bidRequest.data.secure).to.be.false;
stub.restore()
stub.restore();
});

it('should set the secure parameter to true when the protocol is https', function() {
const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('https:');
const bidRequest = spec.buildRequests(bidRequests, null)[0];
expect(bidRequest.data.secure).to.be.true;
stub.restore()
stub.restore();
});

it('should set the secure parameter to true when the protocol is neither http or https', function() {
const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('about:');
const bidRequest = spec.buildRequests(bidRequests, null)[0];
expect(bidRequest.data.secure).to.be.true;
stub.restore()
stub.restore();
});

it('should add ccpa parameter if uspConsent is present', function () {
it('should add ccpa parameter if uspConsent is present', function() {
const uspConsent = '1YNN';
const bidderRequest = { uspConsent: uspConsent };
const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(bidRequest.data.us_privacy).to.eq(uspConsent);
});

it('should add consent parameters if gdprConsent is present', function () {
it('should add consent parameters if gdprConsent is present', function() {
const gdprConsent = { consentString: 'consent_string123', gdprApplies: true };
const bidderRequest = { gdprConsent: gdprConsent };
const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(bidRequest.data.consent_required).to.eq(true);
expect(bidRequest.data.consent_string).to.eq('consent_string123');
});

it('should handle gdprConsent is present but values are undefined case', function () {
it('should handle gdprConsent is present but values are undefined case', function() {
const gdprConsent = { consent_string: undefined, gdprApplies: undefined };
const bidderRequest = { gdprConsent: gdprConsent };
const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(bidRequest.data).to.not.include.any.keys('consent_string')
expect(bidRequest.data).to.not.include.any.keys('consent_string');
});

it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function () {
it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function() {
const bidRequest = spec.buildRequests(bidRequests)[0];
expect(bidRequest.data.ttduid).to.eq('fake-tdid');
});

it('should add Sharethrough specific parameters', function () {
it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' +
' userId object of the bidrequest', function() {
const bidRequest = spec.buildRequests(bidRequests)[0];
expect(bidRequest.data.pubcid).to.eq('fake-pubcid');
});

it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' +
' crumbs object of the bidrequest', function() {
const bidRequest = spec.buildRequests(bidRequests)[0];
delete bidRequest.userId;
expect(bidRequest.data.pubcid).to.eq('fake-pubcid');
});

it('should add Sharethrough specific parameters', function() {
const builtBidRequests = spec.buildRequests(bidRequests);
expect(builtBidRequests[0]).to.deep.include({
strData: {
Expand Down Expand Up @@ -346,8 +365,8 @@ describe('sharethrough adapter spec', function () {
});
});

describe('.interpretResponse', function () {
it('returns a correctly parsed out response', function () {
describe('.interpretResponse', function() {
it('returns a correctly parsed out response', function() {
expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.include(
{
width: 1,
Expand All @@ -357,11 +376,11 @@ describe('sharethrough adapter spec', function () {
dealId: 'aDealId',
currency: 'USD',
netRevenue: true,
ttl: 360,
ttl: 360
});
});

it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function () {
it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function() {
expect(spec.interpretResponse(bidderResponse, prebidRequests[1])[0]).to.include(
{
width: 300,
Expand All @@ -371,11 +390,11 @@ describe('sharethrough adapter spec', function () {
dealId: 'aDealId',
currency: 'USD',
netRevenue: true,
ttl: 360,
ttl: 360
});
});

it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function () {
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function() {
expect(spec.interpretResponse(bidderResponse, prebidRequests[2])[0]).to.include(
{
width: 500,
Expand All @@ -385,11 +404,11 @@ describe('sharethrough adapter spec', function () {
dealId: 'aDealId',
currency: 'USD',
netRevenue: true,
ttl: 360,
ttl: 360
});
});

it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function () {
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function() {
expect(spec.interpretResponse(bidderResponse, prebidRequests[3])[0]).to.include(
{
width: 0,
Expand All @@ -399,11 +418,11 @@ describe('sharethrough adapter spec', function () {
dealId: 'aDealId',
currency: 'USD',
netRevenue: true,
ttl: 360,
ttl: 360
});
});

it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function () {
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function() {
expect(spec.interpretResponse(bidderResponse, prebidRequests[4])[0]).to.include(
{
width: 300,
Expand All @@ -413,26 +432,26 @@ describe('sharethrough adapter spec', function () {
dealId: 'aDealId',
currency: 'USD',
netRevenue: true,
ttl: 360,
ttl: 360
});
});

it('returns a blank array if there are no creatives', function () {
it('returns a blank array if there are no creatives', function() {
const bidResponse = { body: { creatives: [] } };
expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
});

it('returns a blank array if body object is empty', function () {
it('returns a blank array if body object is empty', function() {
const bidResponse = { body: {} };
expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
});

it('returns a blank array if body is null', function () {
it('returns a blank array if body is null', function() {
const bidResponse = { body: null };
expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
});

it('correctly generates ad markup when skipIframeBusting is false', function () {
it('correctly generates ad markup when skipIframeBusting is false', function() {
const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[0])[0].ad;
let resp = null;

Expand All @@ -447,7 +466,7 @@ describe('sharethrough adapter spec', function () {
expect(adMarkup).to.match(/handleIframe/);
});

it('correctly generates ad markup when skipIframeBusting is true', function () {
it('correctly generates ad markup when skipIframeBusting is true', function() {
const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[1])[0].ad;
let resp = null;

Expand All @@ -461,11 +480,11 @@ describe('sharethrough adapter spec', function () {
});
});

describe('.getUserSyncs', function () {
describe('.getUserSyncs', function() {
const cookieSyncs = ['cookieUrl1', 'cookieUrl2', 'cookieUrl3'];
const serverResponses = [{ body: { cookieSyncUrls: cookieSyncs } }];

it('returns an array of correctly formatted user syncs', function () {
it('returns an array of correctly formatted user syncs', function() {
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, null, 'fake-privacy-signal');
expect(syncArray).to.deep.equal([
{ type: 'image', url: 'cookieUrl1&us_privacy=fake-privacy-signal' },
Expand All @@ -474,22 +493,22 @@ describe('sharethrough adapter spec', function () {
);
});

it('returns an empty array if serverResponses is empty', function () {
it('returns an empty array if serverResponses is empty', function() {
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, []);
expect(syncArray).to.be.an('array').that.is.empty;
});

it('returns an empty array if the body is null', function () {
it('returns an empty array if the body is null', function() {
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: null }]);
expect(syncArray).to.be.an('array').that.is.empty;
});

it('returns an empty array if the body.cookieSyncUrls is missing', function () {
it('returns an empty array if the body.cookieSyncUrls is missing', function() {
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: { creatives: ['creative'] } }]);
expect(syncArray).to.be.an('array').that.is.empty;
});

it('returns an empty array if pixels are not enabled', function () {
it('returns an empty array if pixels are not enabled', function() {
const syncArray = spec.getUserSyncs({ pixelEnabled: false }, serverResponses);
expect(syncArray).to.be.an('array').that.is.empty;
});
Expand Down

0 comments on commit 6f531d5

Please sign in to comment.