Skip to content

Add support for Publisher Common ID Module in Sharethrough adapter #5871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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