mk9-prebid
Version:
Header Bidding Management Library
1,174 lines (1,018 loc) • 42.8 kB
JavaScript
import {spec} from 'modules/smaatoBidAdapter.js';
import * as utils from 'src/utils.js';
import {config} from 'src/config.js';
import {createEidsArray} from 'modules/userId/eids.js';
const ADTYPE_IMG = 'Img';
const ADTYPE_RICHMEDIA = 'Richmedia';
const ADTYPE_VIDEO = 'Video';
const REFERRER = 'http://example.com/page.html'
const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU'
const AUCTION_ID = '6653';
const defaultBidderRequest = {
gdprConsent: {
consentString: CONSENT_STRING,
gdprApplies: true
},
uspConsent: 'uspConsentString',
refererInfo: {
referer: REFERRER,
},
timeout: 1200,
auctionId: AUCTION_ID
};
const BANNER_PREBID_MEDIATYPE = {
sizes: [[300, 50]]
}
const singleBannerBidRequest = {
bidder: 'smaato',
params: {
publisherId: 'publisherId',
adspaceId: 'adspaceId'
},
mediaTypes: {
banner: BANNER_PREBID_MEDIATYPE
},
adUnitCode: '/19968336/header-bid-tag-0',
transactionId: 'transactionId',
sizes: [[300, 50]],
bidId: 'bidId',
bidderRequestId: 'bidderRequestId',
src: 'client',
bidRequestsCount: 1,
bidderRequestsCount: 1,
bidderWinsCount: 0
};
const extractPayloadOfFirstAndOnlyRequest = (reqs) => {
expect(reqs).to.have.length(1);
return JSON.parse(reqs[0].data);
}
describe('smaatoBidAdapterTest', () => {
describe('isBidRequestValid', () => {
it('is invalid, when params object is not present', () => {
expect(spec.isBidRequestValid({})).to.be.false;
});
it('is invalid, when params object is empty', () => {
expect(spec.isBidRequestValid({params: {}})).to.be.false;
});
it('is invalid, when publisherId is present but of wrong type', () => {
expect(spec.isBidRequestValid({params: {publisherId: 123}})).to.be.false;
});
describe('for ad pod / long form video requests', () => {
const ADPOD = {video: {context: 'adpod'}}
it('is invalid, when adbreakId is missing', () => {
expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123'}})).to.be.false;
});
it('is invalid, when adbreakId is present but of wrong type', () => {
expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123', adbreakId: 456}})).to.be.false;
});
it('is valid, when required params are present', () => {
expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123', adbreakId: '456'}})).to.be.true;
});
it('is invalid, when forbidden adspaceId param is present', () => {
expect(spec.isBidRequestValid({
mediaTypes: ADPOD,
params: {publisherId: '123', adbreakId: '456', adspaceId: '42'}
})).to.be.false;
});
});
describe('for non adpod requests', () => {
it('is invalid, when adspaceId is missing', () => {
expect(spec.isBidRequestValid({params: {publisherId: '123'}})).to.be.false;
});
it('is invalid, when adspaceId is present but of wrong type', () => {
expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: 456}})).to.be.false;
});
it('is valid, when required params are present for minimal request', () => {
expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456'}})).to.be.true;
});
it('is invalid, when forbidden adbreakId param is present', () => {
expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456', adbreakId: '42'}})).to.be.false;
});
});
});
describe('buildRequests', () => {
const BANNER_OPENRTB_IMP = {
w: 300,
h: 50,
format: [
{
h: 50,
w: 300
}
]
}
describe('common', () => {
const MINIMAL_BIDDER_REQUEST = {
refererInfo: {
referer: REFERRER,
}
};
it('auction type is 1 (first price auction)', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.at).to.be.equal(1);
})
it('currency is US dollar', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.cur).to.be.deep.equal(['USD']);
})
it('can override endpoint', () => {
const overridenEndpoint = 'https://prebid/bidder';
const updatedBidRequest = utils.deepClone(singleBannerBidRequest);
utils.deepSetValue(updatedBidRequest, 'params.endpoint', overridenEndpoint);
const reqs = spec.buildRequests([updatedBidRequest], defaultBidderRequest);
expect(reqs).to.have.length(1);
expect(reqs[0].url).to.equal(overridenEndpoint);
});
it('sends correct imp', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp).to.deep.equal([
{
id: 'bidId',
banner: BANNER_OPENRTB_IMP,
tagid: 'adspaceId',
}
]);
});
it('sends bidfloor when configured', () => {
const singleBannerBidRequestWithFloor = Object.assign({}, singleBannerBidRequest);
singleBannerBidRequestWithFloor.getFloor = function(arg) {
if (arg.currency === 'USD' &&
arg.mediaType === 'banner' &&
JSON.stringify(arg.size) === JSON.stringify([300, 50])) {
return {
currency: 'USD',
floor: 0.123
}
}
}
const reqs = spec.buildRequests([singleBannerBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.equal(0.123);
});
it('bidfloor uses catch-all when multiple sizes', () => {
const singleBannerMultipleSizesBidRequestWithFloor = Object.assign({}, singleBannerBidRequest, {
mediaTypes: {
banner: {
sizes: [[320, 50], [320, 250]]
}
}
});
singleBannerMultipleSizesBidRequestWithFloor.getFloor = function(arg) {
if (arg.size === '*') {
return {
currency: 'USD',
floor: 0.101
}
}
}
const reqs = spec.buildRequests([singleBannerMultipleSizesBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.equal(0.101);
});
it('sends undefined bidfloor when not a function', () => {
const singleBannerBidRequestWithFloor = Object.assign({}, singleBannerBidRequest);
singleBannerBidRequestWithFloor.getFloor = 0
const reqs = spec.buildRequests([singleBannerBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.undefined
});
it('sends undefined bidfloor when invalid', () => {
const singleBannerBidRequestWithFloor = Object.assign({}, singleBannerBidRequest);
singleBannerBidRequestWithFloor.getFloor = function() {
return undefined;
}
const reqs = spec.buildRequests([singleBannerBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.undefined
});
it('sends undefined bidfloor when not a number', () => {
const singleBannerBidRequestWithFloor = Object.assign({}, singleBannerBidRequest);
singleBannerBidRequestWithFloor.getFloor = function() {
return {
currency: 'USD',
}
}
const reqs = spec.buildRequests([singleBannerBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.undefined;
});
it('sends undefined bidfloor when wrong currency', () => {
const singleBannerBidRequestWithFloor = Object.assign({}, singleBannerBidRequest);
singleBannerBidRequestWithFloor.getFloor = function() {
return {
currency: 'EUR',
floor: 0.123
}
}
const reqs = spec.buildRequests([singleBannerBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.undefined;
});
it('sends correct site', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.id).to.exist.and.to.be.a('string');
expect(req.site.domain).to.exist.and.to.be.a('string');
expect(req.site.page).to.exist.and.to.be.a('string');
expect(req.site.ref).to.equal(REFERRER);
expect(req.site.publisher.id).to.equal('publisherId');
})
it('sends gdpr applies if exists', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.regs.ext.gdpr).to.equal(1);
expect(req.user.ext.consent).to.equal(CONSENT_STRING);
});
it('sends no gdpr applies if no gdpr exists', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], MINIMAL_BIDDER_REQUEST);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.regs.ext.gdpr).to.not.exist;
expect(req.user.ext.consent).to.not.exist;
});
it('sends us_privacy if exists', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.regs.ext.us_privacy).to.equal('uspConsentString');
});
it('sends tmax', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.tmax).to.equal(1200);
});
it('sends no us_privacy if no us_privacy exists', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], MINIMAL_BIDDER_REQUEST);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.regs.ext.us_privacy).to.not.exist;
});
it('sends first party data', () => {
this.sandbox = sinon.sandbox.create()
this.sandbox.stub(config, 'getConfig').callsFake(key => {
const config = {
ortb2: {
site: {
keywords: 'power tools,drills'
},
user: {
keywords: 'a,b',
gender: 'M',
yob: 1984
}
}
};
return utils.deepAccess(config, key);
});
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.user.gender).to.equal('M');
expect(req.user.yob).to.equal(1984);
expect(req.user.keywords).to.eql('a,b');
expect(req.user.ext.consent).to.equal(CONSENT_STRING);
expect(req.site.keywords).to.eql('power tools,drills');
expect(req.site.publisher.id).to.equal('publisherId');
this.sandbox.restore();
});
it('has no user ids', () => {
const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.user.ext.eids).to.not.exist;
});
});
describe('multiple requests', () => {
it('build individual server request for each bid request', () => {
const bidRequest1 = utils.deepClone(singleBannerBidRequest);
const bidRequest1BidId = '1111';
utils.deepSetValue(bidRequest1, 'bidId', bidRequest1BidId);
const bidRequest2 = utils.deepClone(singleBannerBidRequest);
const bidRequest2BidId = '2222';
utils.deepSetValue(bidRequest2, 'bidId', bidRequest2BidId);
const reqs = spec.buildRequests([bidRequest1, bidRequest2], defaultBidderRequest);
expect(reqs).to.have.length(2);
expect(JSON.parse(reqs[0].data).imp[0].id).to.be.equal(bidRequest1BidId);
expect(JSON.parse(reqs[1].data).imp[0].id).to.be.equal(bidRequest2BidId);
});
});
describe('buildRequests for video imps', () => {
const VIDEO_OUTSTREAM_PREBID_MEDIATYPE = {
context: 'outstream',
playerSize: [[768, 1024]],
mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'],
minduration: 5,
maxduration: 30,
startdelay: 0,
linearity: 1,
protocols: [7],
skip: 1,
skipmin: 5,
api: [7],
ext: {rewarded: 0}
};
const VIDEO_OUTSTREAM_OPENRTB_IMP = {
mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'],
minduration: 5,
startdelay: 0,
linearity: 1,
h: 1024,
maxduration: 30,
skip: 1,
protocols: [7],
ext: {
rewarded: 0
},
skipmin: 5,
api: [7],
w: 768
};
const singleVideoBidRequest = {
bidder: 'smaato',
params: {
publisherId: 'publisherId',
adspaceId: 'adspaceId'
},
mediaTypes: {
video: VIDEO_OUTSTREAM_PREBID_MEDIATYPE
},
adUnitCode: '/19968336/header-bid-tag-0',
transactionId: 'transactionId',
sizes: [[300, 50]],
bidId: 'bidId',
bidderRequestId: 'bidderRequestId',
src: 'client',
bidRequestsCount: 1,
bidderRequestsCount: 1,
bidderWinsCount: 0
};
it('sends correct video imps', () => {
const reqs = spec.buildRequests([singleVideoBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].id).to.be.equal('bidId');
expect(req.imp[0].tagid).to.be.equal('adspaceId');
expect(req.imp[0].bidfloor).to.be.undefined;
expect(req.imp[0].video).to.deep.equal(VIDEO_OUTSTREAM_OPENRTB_IMP);
});
it('sends bidfloor when configured', () => {
const singleVideoBidRequestWithFloor = Object.assign({}, singleVideoBidRequest);
singleVideoBidRequestWithFloor.getFloor = function(arg) {
if (arg.currency === 'USD' &&
arg.mediaType === 'video' &&
JSON.stringify(arg.size) === JSON.stringify([768, 1024])) {
return {
currency: 'USD',
floor: 0.456
}
}
}
const reqs = spec.buildRequests([singleVideoBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.equal(0.456);
});
it('splits multi format bid requests', () => {
const combinedBannerAndVideoBidRequest = {
bidder: 'smaato',
params: {
publisherId: 'publisherId',
adspaceId: 'adspaceId'
},
mediaTypes: {
banner: BANNER_PREBID_MEDIATYPE,
video: VIDEO_OUTSTREAM_PREBID_MEDIATYPE
},
adUnitCode: '/19968336/header-bid-tag-0',
transactionId: 'transactionId',
sizes: [[300, 50]],
bidId: 'bidId',
bidderRequestId: 'bidderRequestId',
src: 'client',
bidRequestsCount: 1,
bidderRequestsCount: 1,
bidderWinsCount: 0
};
const reqs = spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest);
expect(reqs).to.have.length(2);
expect(JSON.parse(reqs[0].data).imp[0].banner).to.deep.equal(BANNER_OPENRTB_IMP);
expect(JSON.parse(reqs[0].data).imp[0].video).to.not.exist;
expect(JSON.parse(reqs[1].data).imp[0].banner).to.not.exist;
expect(JSON.parse(reqs[1].data).imp[0].video).to.deep.equal(VIDEO_OUTSTREAM_OPENRTB_IMP);
});
describe('ad pod / long form video', () => {
describe('required parameters with requireExactDuration false', () => {
const ADBREAK_ID = 'adbreakId';
const ADPOD = 'adpod';
const BID_ID = '4331';
const W = 640;
const H = 480;
const ADPOD_DURATION = 300;
const DURATION_RANGE = [15, 30];
const longFormVideoBidRequest = {
params: {
publisherId: 'publisherId',
adbreakId: ADBREAK_ID,
},
mediaTypes: {
video: {
context: ADPOD,
playerSize: [[W, H]],
adPodDurationSec: ADPOD_DURATION,
durationRangeSec: DURATION_RANGE,
requireExactDuration: false
}
},
bidId: BID_ID
};
it('sends required fields', () => {
const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.id).to.be.equal(AUCTION_ID);
expect(req.imp.length).to.be.equal(ADPOD_DURATION / DURATION_RANGE[0]);
expect(req.imp[0].id).to.be.equal(BID_ID);
expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID);
expect(req.imp[0].bidfloor).to.be.undefined;
expect(req.imp[0].video.ext.context).to.be.equal(ADPOD);
expect(req.imp[0].video.w).to.be.equal(W);
expect(req.imp[0].video.h).to.be.equal(H);
expect(req.imp[0].video.maxduration).to.be.equal(DURATION_RANGE[1]);
expect(req.imp[0].video.sequence).to.be.equal(1);
expect(req.imp[1].id).to.be.equal(BID_ID);
expect(req.imp[1].tagid).to.be.equal(ADBREAK_ID);
expect(req.imp[1].bidfloor).to.be.undefined;
expect(req.imp[1].video.ext.context).to.be.equal(ADPOD);
expect(req.imp[1].video.w).to.be.equal(W);
expect(req.imp[1].video.h).to.be.equal(H);
expect(req.imp[1].video.maxduration).to.be.equal(DURATION_RANGE[1]);
expect(req.imp[1].video.sequence).to.be.equal(2);
});
it('sends bidfloor when configured', () => {
const longFormVideoBidRequestWithFloor = Object.assign({}, longFormVideoBidRequest);
longFormVideoBidRequestWithFloor.getFloor = function(arg) {
if (arg.currency === 'USD' &&
arg.mediaType === 'video' &&
JSON.stringify(arg.size) === JSON.stringify([640, 480])) {
return {
currency: 'USD',
floor: 0.789
}
}
}
const reqs = spec.buildRequests([longFormVideoBidRequestWithFloor], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].bidfloor).to.be.equal(0.789);
expect(req.imp[1].bidfloor).to.be.equal(0.789);
});
it('sends brand category exclusion as true when config is set to true', () => {
config.setConfig({adpod: {brandCategoryExclusion: true}});
const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(true);
});
it('sends brand category exclusion as false when config is set to false', () => {
config.setConfig({adpod: {brandCategoryExclusion: false}});
const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(false);
});
it('sends brand category exclusion as false when config is not set', () => {
const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(false);
});
});
describe('required parameters with requireExactDuration true', () => {
const ADBREAK_ID = 'adbreakId';
const ADPOD = 'adpod';
const BID_ID = '4331';
const W = 640;
const H = 480;
const ADPOD_DURATION = 5;
const DURATION_RANGE = [5, 15, 25];
const longFormVideoBidRequest = {
params: {
publisherId: 'publisherId',
adbreakId: ADBREAK_ID,
},
mediaTypes: {
video: {
context: ADPOD,
playerSize: [[W, H]],
adPodDurationSec: ADPOD_DURATION,
durationRangeSec: DURATION_RANGE,
requireExactDuration: true
}
},
bidId: BID_ID
};
it('sends required fields', () => {
const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.id).to.be.equal(AUCTION_ID);
expect(req.imp.length).to.be.equal(DURATION_RANGE.length);
expect(req.imp[0].id).to.be.equal(BID_ID);
expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID);
expect(req.imp[0].video.ext.context).to.be.equal(ADPOD);
expect(req.imp[0].video.w).to.be.equal(W);
expect(req.imp[0].video.h).to.be.equal(H);
expect(req.imp[0].video.minduration).to.be.equal(DURATION_RANGE[0]);
expect(req.imp[0].video.maxduration).to.be.equal(DURATION_RANGE[0]);
expect(req.imp[0].video.sequence).to.be.equal(1);
expect(req.imp[1].id).to.be.equal(BID_ID);
expect(req.imp[1].tagid).to.be.equal(ADBREAK_ID);
expect(req.imp[1].video.ext.context).to.be.equal(ADPOD);
expect(req.imp[1].video.w).to.be.equal(W);
expect(req.imp[1].video.h).to.be.equal(H);
expect(req.imp[1].video.minduration).to.be.equal(DURATION_RANGE[1]);
expect(req.imp[1].video.maxduration).to.be.equal(DURATION_RANGE[1]);
expect(req.imp[1].video.sequence).to.be.equal(2);
expect(req.imp[2].id).to.be.equal(BID_ID);
expect(req.imp[2].tagid).to.be.equal(ADBREAK_ID);
expect(req.imp[2].video.ext.context).to.be.equal(ADPOD);
expect(req.imp[2].video.w).to.be.equal(W);
expect(req.imp[2].video.h).to.be.equal(H);
expect(req.imp[2].video.minduration).to.be.equal(DURATION_RANGE[2]);
expect(req.imp[2].video.maxduration).to.be.equal(DURATION_RANGE[2]);
expect(req.imp[2].video.sequence).to.be.equal(3);
});
});
describe('forwarding of optional parameters', () => {
const MIMES = ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'];
const STARTDELAY = 0;
const LINEARITY = 1;
const SKIP = 1;
const PROTOCOLS = [7];
const SKIPMIN = 5;
const API = [7];
const validBasicAdpodBidRequest = {
params: {
publisherId: 'publisherId',
adbreakId: 'adbreakId',
},
mediaTypes: {
video: {
context: 'adpod',
playerSize: [640, 480],
adPodDurationSec: 300,
durationRangeSec: [15, 30],
mimes: MIMES,
startdelay: STARTDELAY,
linearity: LINEARITY,
skip: SKIP,
protocols: PROTOCOLS,
skipmin: SKIPMIN,
api: API
}
},
bidId: 'bidId'
};
it('sends general video fields when they are present', () => {
const reqs = spec.buildRequests([validBasicAdpodBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].video.mimes).to.eql(MIMES);
expect(req.imp[0].video.startdelay).to.be.equal(STARTDELAY);
expect(req.imp[0].video.linearity).to.be.equal(LINEARITY);
expect(req.imp[0].video.skip).to.be.equal(SKIP);
expect(req.imp[0].video.protocols).to.eql(PROTOCOLS);
expect(req.imp[0].video.skipmin).to.be.equal(SKIPMIN);
expect(req.imp[0].video.api).to.eql(API);
});
it('sends series name when parameter is present', () => {
const SERIES_NAME = 'foo'
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.tvSeriesName = SERIES_NAME;
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.series).to.be.equal(SERIES_NAME);
});
it('sends episode name when parameter is present', () => {
const EPISODE_NAME = 'foo'
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.tvEpisodeName = EPISODE_NAME;
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.title).to.be.equal(EPISODE_NAME);
});
it('sends season number as string when parameter is present', () => {
const SEASON_NUMBER_AS_NUMBER_IN_PREBID_REQUEST = 42
const SEASON_NUMBER_AS_STRING_IN_OUTGOING_REQUEST = '42'
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.tvSeasonNumber = SEASON_NUMBER_AS_NUMBER_IN_PREBID_REQUEST;
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.season).to.be.equal(SEASON_NUMBER_AS_STRING_IN_OUTGOING_REQUEST);
});
it('sends episode number when parameter is present', () => {
const EPISODE_NUMBER = 42
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.tvEpisodeNumber = EPISODE_NUMBER;
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.episode).to.be.equal(EPISODE_NUMBER);
});
it('sends content length when parameter is present', () => {
const LENGTH = 42
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.contentLengthSec = LENGTH;
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.len).to.be.equal(LENGTH);
});
it('sends livestream as 1 when content mode parameter is live', () => {
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.contentMode = 'live';
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.livestream).to.be.equal(1);
});
it('sends livestream as 0 when content mode parameter is on-demand', () => {
const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest);
adpodRequestWithParameter.mediaTypes.video.contentMode = 'on-demand';
const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.site.content.livestream).to.be.equal(0);
});
it("doesn't send any optional parameters when none are present", () => {
const reqs = spec.buildRequests([validBasicAdpodBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.imp[0].video.ext.requireExactDuration).to.not.exist;
expect(req.site.content).to.not.exist;
});
});
});
});
describe('in-app requests', () => {
const LOCATION = {
lat: 33.3,
lon: -88.8
}
const DEVICE_ID = 'aDeviceId'
const inAppBidRequestWithoutAppParams = {
bidder: 'smaato',
params: {
publisherId: 'publisherId',
adspaceId: 'adspaceId'
},
mediaTypes: {
banner: BANNER_PREBID_MEDIATYPE
},
adUnitCode: '/19968336/header-bid-tag-0',
transactionId: 'transactionId',
sizes: [[300, 50]],
bidId: 'bidId',
bidderRequestId: 'bidderRequestId',
src: 'client',
bidRequestsCount: 1,
bidderRequestsCount: 1,
bidderWinsCount: 0
};
it('when geo and ifa info present, then add both to device object', () => {
const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams);
inAppBidRequest.params.app = {ifa: DEVICE_ID, geo: LOCATION};
const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.device.geo).to.deep.equal(LOCATION);
expect(req.device.ifa).to.equal(DEVICE_ID);
});
it('when ifa is present but geo is missing, then add only ifa to device object', () => {
const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams);
inAppBidRequest.params.app = {ifa: DEVICE_ID};
const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.device.geo).to.not.exist;
expect(req.device.ifa).to.equal(DEVICE_ID);
});
it('when geo is present but ifa is missing, then add only geo to device object', () => {
const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams);
inAppBidRequest.params.app = {geo: LOCATION};
const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.device.geo).to.deep.equal(LOCATION);
expect(req.device.ifa).to.not.exist;
});
it('when app param does not exist, then add no specific device info', () => {
const reqs = spec.buildRequests([inAppBidRequestWithoutAppParams], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.device.geo).to.not.exist;
expect(req.device.ifa).to.not.exist;
});
});
describe('user ids in requests', () => {
it('user ids are added to user.ext.eids', () => {
const userIdBidRequest = {
bidder: 'smaato',
params: {
publisherId: 'publisherId',
adspaceId: 'adspaceId'
},
mediaTypes: {
banner: BANNER_PREBID_MEDIATYPE
},
adUnitCode: '/19968336/header-bid-tag-0',
transactionId: 'transactionId',
sizes: [[300, 50]],
bidId: 'bidId',
bidderRequestId: 'bidderRequestId',
src: 'client',
bidRequestsCount: 1,
bidderRequestsCount: 1,
bidderWinsCount: 0,
userId: {
criteoId: '123456',
tdid: '89145'
},
userIdAsEids: createEidsArray({
criteoId: '123456',
tdid: '89145'
})
};
const reqs = spec.buildRequests([userIdBidRequest], defaultBidderRequest);
const req = extractPayloadOfFirstAndOnlyRequest(reqs);
expect(req.user.ext.eids).to.exist;
expect(req.user.ext.eids).to.have.length(2);
});
});
});
describe('interpretResponse', () => {
function buildBidRequest(payloadAsJsObj = {imp: [{}]}) {
return {
method: 'POST',
url: 'https://prebid.ad.smaato.net/oapi/prebid',
data: JSON.stringify(payloadAsJsObj)
}
}
const buildOpenRtbBidResponse = (adType) => {
let adm = '';
switch (adType) {
case ADTYPE_IMG:
adm = JSON.stringify({
image: {
img: {
url: 'https://prebid/static/ad.jpg',
w: 320,
h: 50,
ctaurl: 'https://prebid/track/ctaurl'
},
impressiontrackers: [
'https://prebid/track/imp/1',
'https://prebid/track/imp/2'
],
clicktrackers: [
'https://prebid/track/click/1'
]
}
});
break;
case ADTYPE_RICHMEDIA:
adm = JSON.stringify({
richmedia: {
mediadata: {
content: '<div><h3>RICHMEDIA CONTENT</h3></div>',
w: 800,
h: 600
},
impressiontrackers: [
'https://prebid/track/imp/1',
'https://prebid/track/imp/2'
],
clicktrackers: [
'https://prebid/track/click/1'
]
}
});
break;
case ADTYPE_VIDEO:
adm = '<VAST version="2.0"></VAST>';
break;
default:
throw Error('Invalid AdType');
}
return {
body: {
bidid: '04db8629-179d-4bcd-acce-e54722969006',
cur: 'USD',
ext: {},
id: '5ebea288-f13a-4754-be6d-4ade66c68877',
seatbid: [
{
bid: [
{
'adm': adm,
'adomain': [
'smaato.com'
],
'bidderName': 'smaato',
'cid': 'CM6523',
'crid': 'CR69381',
'dealid': '12345',
'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd',
'impid': '226416e6e6bf41',
'iurl': 'https://prebid/iurl',
'nurl': 'https://prebid/nurl',
'price': 0.01,
'w': 350,
'h': 50
}
],
seat: 'CM6523'
}
],
},
headers: {
get: function (header) {
if (header === 'X-SMT-ADTYPE') {
return adType;
}
}
}
};
};
it('returns empty array on no bid responses', () => {
const response_with_empty_body = {body: {}};
const bids = spec.interpretResponse(response_with_empty_body, buildBidRequest());
expect(bids).to.be.empty;
});
describe('non ad pod', () => {
it('single image reponse', () => {
const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), buildBidRequest());
expect(bids).to.deep.equal([
{
requestId: '226416e6e6bf41',
cpm: 0.01,
width: 350,
height: 50,
ad: '<div style="cursor:pointer" onclick="fetch(decodeURIComponent(\'https%3A%2F%2Fprebid%2Ftrack%2Fclick%2F1\'), {cache: \'no-cache\'});;window.open(decodeURIComponent(\'https%3A%2F%2Fprebid%2Ftrack%2Fctaurl\'));"><img src="https://prebid/static/ad.jpg" width="320" height="50"/><img src="https://prebid/track/imp/1" alt="" width="0" height="0"/><img src="https://prebid/track/imp/2" alt="" width="0" height="0"/></div>',
ttl: 300,
creativeId: 'CR69381',
dealId: '12345',
netRevenue: true,
currency: 'USD',
mediaType: 'banner',
meta: {
advertiserDomains: ['smaato.com'],
agencyId: 'CM6523',
networkName: 'smaato',
mediaType: 'banner'
}
}
]);
});
it('single richmedia reponse', () => {
const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), buildBidRequest());
expect(bids).to.deep.equal([
{
requestId: '226416e6e6bf41',
cpm: 0.01,
width: 350,
height: 50,
ad: '<div onclick="fetch(decodeURIComponent(\'https%3A%2F%2Fprebid%2Ftrack%2Fclick%2F1\'), {cache: \'no-cache\'});"><div><h3>RICHMEDIA CONTENT</h3></div><img src="https://prebid/track/imp/1" alt="" width="0" height="0"/><img src="https://prebid/track/imp/2" alt="" width="0" height="0"/></div>',
ttl: 300,
creativeId: 'CR69381',
dealId: '12345',
netRevenue: true,
currency: 'USD',
mediaType: 'banner',
meta: {
advertiserDomains: ['smaato.com'],
agencyId: 'CM6523',
networkName: 'smaato',
mediaType: 'banner'
}
}
]);
});
it('single video response', () => {
const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_VIDEO), buildBidRequest());
expect(bids).to.deep.equal([
{
requestId: '226416e6e6bf41',
cpm: 0.01,
width: 350,
height: 50,
vastXml: '<VAST version="2.0"></VAST>',
ttl: 300,
creativeId: 'CR69381',
dealId: '12345',
netRevenue: true,
currency: 'USD',
mediaType: 'video',
meta: {
advertiserDomains: ['smaato.com'],
agencyId: 'CM6523',
networkName: 'smaato',
mediaType: 'video'
}
}
]);
});
it('ignores bid response with invalid ad type', () => {
const serverResponse = buildOpenRtbBidResponse(ADTYPE_IMG);
serverResponse.headers.get = (header) => {
if (header === 'X-SMT-ADTYPE') {
return undefined;
}
};
const bids = spec.interpretResponse(serverResponse, buildBidRequest());
expect(bids).to.be.empty;
});
});
describe('ad pod', () => {
const bidRequestWithAdpodContext = buildBidRequest({imp: [{video: {ext: {context: 'adpod'}}}]});
const PRIMARY_CAT_ID = 1337
const serverResponse = {
body: {
bidid: '04db8629-179d-4bcd-acce-e54722969006',
cur: 'USD',
ext: {},
id: '5ebea288-f13a-4754-be6d-4ade66c68877',
seatbid: [
{
bid: [
{
adm: '<VAST version="2.0"></VAST>',
adomain: [
'smaato.com'
],
bidderName: 'smaato',
cid: 'CM6523',
crid: 'CR69381',
dealid: '12345',
id: '6906aae8-7f74-4edd-9a4f-f49379a3cadd',
impid: '226416e6e6bf41',
iurl: 'https://prebid/iurl',
nurl: 'https://prebid/nurl',
price: 0.01,
w: 350,
h: 50,
cat: [PRIMARY_CAT_ID],
ext: {
duration: 42
}
}
],
seat: 'CM6523'
}
]
},
headers: {get: () => undefined}
};
it('sets required values for adpod bid from server response', () => {
const bids = spec.interpretResponse(serverResponse, bidRequestWithAdpodContext);
expect(bids).to.deep.equal([
{
requestId: '226416e6e6bf41',
cpm: 0.01,
width: 350,
height: 50,
vastXml: '<VAST version="2.0"></VAST>',
ttl: 300,
creativeId: 'CR69381',
dealId: '12345',
netRevenue: true,
currency: 'USD',
mediaType: 'video',
video: {
context: 'adpod',
durationSeconds: 42
},
meta: {
advertiserDomains: ['smaato.com'],
agencyId: 'CM6523',
networkName: 'smaato',
mediaType: 'video'
}
}
]);
});
it('sets primary category id in case of enabled brand category exclusion', () => {
config.setConfig({adpod: {brandCategoryExclusion: true}});
const bids = spec.interpretResponse(serverResponse, bidRequestWithAdpodContext)
expect(bids[0].meta.primaryCatId).to.be.equal(PRIMARY_CAT_ID)
})
});
it('uses correct TTL when expire header exists', () => {
const clock = sinon.useFakeTimers();
clock.tick(2000);
const resp = buildOpenRtbBidResponse(ADTYPE_IMG);
resp.headers.get = (header) => {
if (header === 'X-SMT-ADTYPE') {
return ADTYPE_IMG;
}
if (header === 'X-SMT-Expires') {
return 2000 + (400 * 1000);
}
};
const bids = spec.interpretResponse(resp, buildBidRequest());
expect(bids[0].ttl).to.equal(400);
clock.restore();
});
it('uses net revenue flag send from server', () => {
const resp = buildOpenRtbBidResponse(ADTYPE_IMG);
resp.body.seatbid[0].bid[0].ext = {net: false};
const bids = spec.interpretResponse(resp, buildBidRequest());
expect(bids[0].netRevenue).to.equal(false);
});
});
describe('getUserSyncs', () => {
it('returns no pixels', () => {
expect(spec.getUserSyncs()).to.be.empty
})
})
});