From 1ba68883a4aa0b6cc6a59e75d1ba07027589e4a0 Mon Sep 17 00:00:00 2001
From: Eddy Pechuzal <46331062+epechuzal@users.noreply.github.com>
Date: Tue, 7 Sep 2021 13:57:53 -0700
Subject: [PATCH] Sharethrough adapter: connect to OpenRTB endpoint (#7290)
---
modules/sharethroughBidAdapter.js | 427 ++++----
modules/sharethroughBidAdapter.md | 68 +-
.../modules/sharethroughBidAdapter_spec.js | 980 ++++++++----------
3 files changed, 704 insertions(+), 771 deletions(-)
diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js
index 967d8194607..24ab2530014 100644
--- a/modules/sharethroughBidAdapter.js
+++ b/modules/sharethroughBidAdapter.js
@@ -1,123 +1,190 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import * as utils from '../src/utils.js';
import { config } from '../src/config.js';
+import { BANNER, VIDEO } from '../src/mediaTypes.js';
+import { createEidsArray } from './userId/eids.js';
-const VERSION = '3.4.1';
+const VERSION = '4.0.0';
const BIDDER_CODE = 'sharethrough';
-const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1';
-const DEFAULT_SIZE = [1, 1];
+const SUPPLY_ID = 'WYu2BXv1';
+
+const STR_ENDPOINT = `https://btlr.sharethrough.com/universal/v1?supply_id=${SUPPLY_ID}`;
// this allows stubbing of utility function that is used internally by the sharethrough adapter
export const sharethroughInternal = {
- b64EncodeUnicode,
- handleIframe,
- isLockedInFrame,
- getProtocol
+ getProtocol,
};
export const sharethroughAdapterSpec = {
code: BIDDER_CODE,
+ supportedMediaTypes: [VIDEO, BANNER],
isBidRequestValid: bid => !!bid.params.pkey && bid.bidder === BIDDER_CODE,
buildRequests: (bidRequests, bidderRequest) => {
- return bidRequests.map(bidRequest => {
- let query = {
- placement_key: bidRequest.params.pkey,
- bidId: bidRequest.bidId,
- consent_required: false,
- instant_play_capable: canAutoPlayHTML5Video(),
- hbSource: 'prebid',
- hbVersion: '$prebid.version$',
- strVersion: VERSION,
- };
-
- const gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot');
- if (gpid) {
- query.gpid = gpid;
- }
-
- Object.assign(query, handleUniversalIds(bidRequest));
-
- const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0;
- query.secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1);
-
- if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) {
- query.consent_string = bidderRequest.gdprConsent.consentString;
- }
-
- if (bidderRequest && bidderRequest.gdprConsent) {
- query.consent_required = !!bidderRequest.gdprConsent.gdprApplies;
- }
-
- if (bidderRequest && bidderRequest.uspConsent) {
- query.us_privacy = bidderRequest.uspConsent
+ const timeout = config.getConfig('bidderTimeout');
+
+ const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0;
+ const secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1);
+
+ const req = {
+ id: utils.generateUUID(),
+ at: 1,
+ cur: ['USD'],
+ tmax: timeout,
+ site: {
+ domain: window.location.hostname,
+ page: window.location.href,
+ ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null,
+ },
+ user: {
+ ext: {
+ eids: userIdAsEids(bidRequests[0]),
+ },
+ },
+ device: {
+ ua: navigator.userAgent,
+ language: navigator.language,
+ js: 1,
+ dnt: navigator.doNotTrack === '1' ? 1 : 0,
+ h: window.screen.height,
+ w: window.screen.width,
+ },
+ regs: {
+ coppa: config.getConfig('coppa') === true ? 1 : 0,
+ ext: {},
+ },
+ source: {
+ ext: {
+ version: '$prebid.version$',
+ str: VERSION,
+ schain: bidRequests[0].schain,
+ },
+ },
+ bcat: bidRequests[0].params.bcat || [],
+ badv: bidRequests[0].params.badv || [],
+ test: 0,
+ };
+
+ if (bidderRequest.gdprConsent) {
+ const gdprApplies = bidderRequest.gdprConsent.gdprApplies === true;
+ req.regs.ext.gdpr = gdprApplies ? 1 : 0;
+ if (gdprApplies) {
+ req.user.ext.consent = bidderRequest.gdprConsent.consentString;
}
+ }
- if (config.getConfig('coppa') === true) {
- query.coppa = true
- }
+ if (bidderRequest.uspConsent) {
+ req.regs.ext.us_privacy = bidderRequest.uspConsent;
+ }
- if (bidRequest.schain) {
- query.schain = JSON.stringify(bidRequest.schain);
- }
+ const imps = bidRequests.map(bidReq => {
+ const impression = {};
- const floor = getFloor(bidRequest);
- if (floor) {
- query.bidfloor = floor;
+ const gpid = utils.deepAccess(bidReq, 'ortb2Imp.ext.data.pbadslot');
+ if (gpid) {
+ impression.ext = { gpid: gpid };
}
- if (bidRequest.params.badv) {
- query.badv = bidRequest.params.badv;
+ // if request is for video, we only support instream
+ if (bidReq.mediaTypes && bidReq.mediaTypes.video && bidReq.mediaTypes.video.context === 'outstream') {
+ // return null so we can easily remove this imp from the array of imps that we send to adserver
+ return null;
}
- if (bidRequest.params.bcat) {
- query.bcat = bidRequest.params.bcat;
+ if (bidReq.mediaTypes && bidReq.mediaTypes.video) {
+ const videoRequest = bidReq.mediaTypes.video;
+
+ // default playerSize, only change this if we know width and height are properly defined in the request
+ let [w, h] = [640, 360];
+ if (videoRequest.playerSize && videoRequest.playerSize[0] && videoRequest.playerSize[1]) {
+ [w, h] = videoRequest.playerSize;
+ }
+
+ impression.video = {
+ pos: nullish(videoRequest.pos, 0),
+ topframe: utils.inIframe() ? 0 : 1,
+ skip: nullish(videoRequest.skip, 0),
+ linearity: nullish(videoRequest.linearity, 1),
+ minduration: nullish(videoRequest.minduration, 5),
+ maxduration: nullish(videoRequest.maxduration, 60),
+ playbackmethod: videoRequest.playbackmethod || [2],
+ api: getVideoApi(videoRequest),
+ mimes: videoRequest.mimes || ['video/mp4'],
+ protocols: getVideoProtocols(videoRequest),
+ w,
+ h,
+ startdelay: nullish(videoRequest.startdelay, 0),
+ skipmin: nullish(videoRequest.skipmin, 0),
+ skipafter: nullish(videoRequest.skipafter, 0),
+ };
+
+ if (videoRequest.placement) impression.video.placement = videoRequest.placement;
+ if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery;
+ if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype;
+ if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad;
+ } else {
+ impression.banner = {
+ pos: utils.deepAccess(bidReq, 'mediaTypes.banner.pos', 0),
+ topframe: utils.inIframe() ? 0 : 1,
+ format: bidReq.sizes.map(size => ({ w: +size[0], h: +size[1] })),
+ };
}
- // Data that does not need to go to the server,
- // but we need as part of interpretResponse()
- const strData = {
- skipIframeBusting: bidRequest.params.iframe,
- iframeSize: bidRequest.params.iframeSize,
- sizes: bidRequest.sizes
- };
-
return {
- method: 'POST',
- url: STR_ENDPOINT,
- data: query,
- strData: strData
+ id: bidReq.bidId,
+ tagid: String(bidReq.params.pkey),
+ secure: secure ? 1 : 0,
+ bidfloor: getBidRequestFloor(bidReq),
+ ...impression,
};
- })
+ }).filter(imp => !!imp);
+
+ return {
+ method: 'POST',
+ url: STR_ENDPOINT,
+ data: {
+ ...req,
+ imp: imps,
+ },
+ bidRequests,
+ bidderRequest,
+ };
},
interpretResponse: ({ body }, req) => {
- if (!body || !body.creatives || !body.creatives.length) {
+ if (!body || !body.seatbid || body.seatbid.length === 0 || !body.seatbid[0].bid || body.seatbid[0].bid.length === 0) {
return [];
}
- const creative = body.creatives[0];
- let size = DEFAULT_SIZE;
- if (req.strData.iframeSize || req.strData.sizes.length) {
- size = req.strData.iframeSize
- ? req.strData.iframeSize
- : getLargestSize(req.strData.sizes);
- }
+ return body.seatbid[0].bid.map(bid => {
+ const request = matchRequest(bid.impid, req);
+
+ const response = {
+ requestId: bid.impid,
+ width: +bid.w,
+ height: +bid.h,
+ cpm: +bid.price,
+ creativeId: bid.crid,
+ dealId: bid.dealid || null,
+ mediaType: request.mediaTypes && request.mediaTypes.video ? VIDEO : BANNER,
+ currency: body.cur || 'USD',
+ netRevenue: true,
+ ttl: 360,
+ ad: bid.adm,
+ nurl: bid.nurl,
+ meta: {
+ advertiserDomains: bid.adomain || [],
+ },
+ };
- return [{
- requestId: req.data.bidId,
- width: size[0],
- height: size[1],
- cpm: creative.cpm,
- creativeId: creative.creative.creative_key,
- dealId: creative.creative.deal_id,
- currency: 'USD',
- netRevenue: true,
- ttl: 360,
- meta: { advertiserDomains: creative.creative && creative.creative.adomain ? creative.creative.adomain : [] },
- ad: generateAd(body, req)
- }];
+ if (response.mediaType === VIDEO) {
+ response.ttl = 3600;
+ response.vastXml = bid.adm;
+ }
+
+ return response;
+ });
},
getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => {
@@ -138,175 +205,77 @@ export const sharethroughAdapterSpec = {
},
// Empty implementation for prebid core to be able to find it
- onTimeout: (data) => {},
+ onTimeout: (data) => {
+ },
// Empty implementation for prebid core to be able to find it
- onBidWon: (bid) => {},
+ onBidWon: (bid) => {
+ },
// Empty implementation for prebid core to be able to find it
- onSetTargeting: (bid) => {}
+ onSetTargeting: (bid) => {
+ },
};
-function handleUniversalIds(bidRequest) {
- if (!bidRequest.userId) return {};
-
- const universalIds = {};
-
- const ttd = utils.deepAccess(bidRequest, 'userId.tdid');
- if (ttd) universalIds.ttduid = ttd;
-
- const pubc = utils.deepAccess(bidRequest, 'userId.pubcid') || utils.deepAccess(bidRequest, 'crumbs.pubcid');
- if (pubc) universalIds.pubcid = pubc;
-
- const idl = utils.deepAccess(bidRequest, 'userId.idl_env');
- if (idl) universalIds.idluid = idl;
-
- const id5 = utils.deepAccess(bidRequest, 'userId.id5id.uid');
- if (id5) {
- universalIds.id5uid = { id: id5 };
- const id5link = utils.deepAccess(bidRequest, 'userId.id5id.ext.linkType');
- if (id5link) universalIds.id5uid.linkType = id5link;
- }
-
- const lipb = utils.deepAccess(bidRequest, 'userId.lipb.lipbid');
- if (lipb) universalIds.liuid = lipb;
-
- return universalIds;
-}
-
-function getLargestSize(sizes) {
- function area(size) {
- return size[0] * size[1];
+function getVideoApi({ api }) {
+ let defaultValue = [2];
+ if (api && Array.isArray(api) && api.length > 0) {
+ return api;
+ } else {
+ return defaultValue;
}
-
- return sizes.reduce((prev, current) => {
- if (area(current) > area(prev)) {
- return current
- } else {
- return prev
- }
- });
}
-function generateAd(body, req) {
- const strRespId = `str_response_${req.data.bidId}`;
-
- let adMarkup = `
-
-
-
- `;
-
- if (req.strData.skipIframeBusting) {
- // Don't break out of iframe
- adMarkup = adMarkup + ``;
+function getVideoProtocols({ protocols }) {
+ let defaultValue = [2, 3, 5, 6, 7, 8];
+ if (protocols && Array.isArray(protocols) && protocols.length > 0) {
+ return protocols;
} else {
- // Add logic to the markup that detects whether or not in top level document is accessible
- // this logic will deploy sfp.js and/or iframe buster script(s) as appropriate
- adMarkup = adMarkup + `
-
- `;
+ return defaultValue;
}
-
- return adMarkup;
}
-function handleIframe () {
- // only load iframe buster JS if we can access the top level document
- // if we are 'locked in' to this frame then no point trying to bust out: we may as well render in the frame instead
- var iframeBusterLoaded = false;
- if (!window.lockedInFrame) {
- var sfpIframeBusterJs = document.createElement('script');
- sfpIframeBusterJs.src = 'https://native.sharethrough.com/assets/sfp-set-targeting.js';
- sfpIframeBusterJs.type = 'text/javascript';
- try {
- window.document.getElementsByTagName('body')[0].appendChild(sfpIframeBusterJs);
- iframeBusterLoaded = true;
- } catch (e) {
- utils.logError('Trouble writing frame buster script, error details:', e);
- }
- }
-
- var clientJsLoaded = (!iframeBusterLoaded) ? !!(window.STR && window.STR.Tag) : !!(window.top.STR && window.top.STR.Tag);
- if (!clientJsLoaded) {
- var sfpJs = document.createElement('script');
- sfpJs.src = 'https://native.sharethrough.com/assets/sfp.js';
- sfpJs.type = 'text/javascript';
-
- // only add sfp js to window.top if iframe busting successfully loaded; otherwise, add to iframe
- try {
- if (iframeBusterLoaded) {
- window.top.document.getElementsByTagName('body')[0].appendChild(sfpJs);
- } else {
- window.document.getElementsByTagName('body')[0].appendChild(sfpJs);
- }
- } catch (e) {
- utils.logError('Trouble writing sfp script, error details:', e);
+function getBidRequestFloor(bid) {
+ let floor = null;
+ if (typeof bid.getFloor === 'function') {
+ const floorInfo = bid.getFloor({
+ currency: 'USD',
+ mediaType: bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner',
+ size: bid.sizes.map(size => ({ w: size[0], h: size[1] })),
+ });
+ if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) {
+ floor = parseFloat(floorInfo.floor);
}
}
+ return floor !== null ? floor : bid.params.floor;
}
-// determines if we are capable of busting out of the iframe we are in
-// if we catch a DOMException when trying to access top-level document, it means we're stuck in the frame we're in
-function isLockedInFrame () {
- window.lockedInFrame = false;
- try {
- window.lockedInFrame = !window.top.document;
- } catch (e) {
- window.lockedInFrame = (e instanceof DOMException);
+function userIdAsEids(bidRequest) {
+ const eids = createEidsArray(utils.deepAccess(bidRequest, 'userId')) || [];
+
+ const flocData = utils.deepAccess(bidRequest, 'userId.flocId');
+ const isFlocIdValid = flocData && flocData.id && flocData.version;
+ if (isFlocIdValid) {
+ eids.push({
+ source: 'chrome.com',
+ uids: [{ id: flocData.id, atype: 1, ext: { ver: flocData.version } }],
+ });
}
-}
-// See https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
-function b64EncodeUnicode(str) {
- return btoa(
- encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
- function toSolidBytes(match, p1) {
- return String.fromCharCode('0x' + p1);
- }));
+ return eids;
}
-function canAutoPlayHTML5Video() {
- const userAgent = navigator.userAgent;
- if (!userAgent) return false;
-
- const isAndroid = /Android/i.test(userAgent);
- const isiOS = /iPhone|iPad|iPod/i.test(userAgent);
- const chromeVersion = parseInt((/Chrome\/([0-9]+)/.exec(userAgent) || [0, 0])[1]);
- const chromeiOSVersion = parseInt((/CriOS\/([0-9]+)/.exec(userAgent) || [0, 0])[1]);
- const safariVersion = parseInt((/Version\/([0-9]+)/.exec(userAgent) || [0, 0])[1]);
-
- if (
- (isAndroid && chromeVersion >= 53) ||
- (isiOS && (safariVersion >= 10 || chromeiOSVersion >= 53)) ||
- !(isAndroid || isiOS)
- ) {
- return true;
- } else {
- return false;
- }
+function getProtocol() {
+ return window.location.protocol;
}
-function getProtocol() {
- return document.location.protocol;
+function matchRequest(id, request) {
+ return request.bidRequests.find(bid => bid.bidId === id);
}
-function getFloor(bid) {
- if (utils.isFn(bid.getFloor)) {
- const floorInfo = bid.getFloor({
- currency: 'USD',
- mediaType: 'banner',
- size: bid.sizes.map(size => ({ w: size[0], h: size[1] }))
- });
- if (utils.isPlainObject(floorInfo) && !isNaN(floorInfo.floor) && floorInfo.currency === 'USD') {
- return parseFloat(floorInfo.floor);
- }
- }
- return null;
+// stub for ?? operator
+function nullish(input, def) {
+ return input === null || input === undefined ? def : input;
}
registerBidder(sharethroughAdapterSpec);
diff --git a/modules/sharethroughBidAdapter.md b/modules/sharethroughBidAdapter.md
index 396b8164577..218ef353d4e 100644
--- a/modules/sharethroughBidAdapter.md
+++ b/modules/sharethroughBidAdapter.md
@@ -23,22 +23,74 @@ Module that connects to Sharethrough's demand sources
// REQUIRED - The placement key
pkey: 'LuB3vxGGFrBZJa6tifXW4xgK',
- // OPTIONAL - Render Sharethrough creative in an iframe, defaults to false
- iframe: true,
-
- // OPTIONAL - If iframeSize is provided, we'll use this size for the iframe
- // otherwise we'll grab the largest size from the sizes array
- // This is ignored if iframe: false
- iframeSize: [250, 250],
-
// OPTIONAL - Blocked Advertiser Domains
badv: ['domain1.com', 'domain2.com'],
// OPTIONAL - Blocked Categories (IAB codes)
bcat: ['IAB1-1', 'IAB1-2'],
+
+ // OPTIONAL - default bid floor, if not specified in bid request (USD)
+ floor: 0.1,
}
}
]
}
];
```
+
+# Sample Instream Video Ad Unit: For Publishers
+```
+var adVideoAdUnits = [
+{
+ code: 'test-div-video',
+ mediaTypes: {
+ video: {
+ // CANNOT be 'outstream'
+ context: 'instream',
+ placement: 1,
+ delivery: 1,
+ companiontype: 'companion type',
+ companionad: 'companion ad',
+ // default values shown below this point
+ pos: 0,
+ skip: 0,
+ linearity: 1,
+ minduration: 5,
+ maxduration: 60,
+ playbackmethod: [2],
+ api: [2],
+ mimes: ['video/mp4'],
+ protocols: [2, 3, 5, 6, 7, 8],
+ playerSize: [640, 360],
+ startdelay: 0,
+ skipmin: 0,
+ skipafter: 0,
+ },
+ },
+ bids: [{
+ bidder: 'sharethrough',
+ params: {
+ pkey: 'pkey1'
+ }
+ }]
+}]
+```
+
+# Sample Banner Ad Unit: For Publishers
+```
+var adUnits = [
+{
+ code: 'test-div-video',
+ mediaTypes: {
+ banner: {
+ pos: 0, // default
+ },
+ },
+ bids: [{
+ bidder: 'sharethrough',
+ params: {
+ pkey: 'pkey1'
+ }
+ }]
+}]
+```
diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js
index 3e406e1af44..122c6cdbb39 100644
--- a/test/spec/modules/sharethroughBidAdapter_spec.js
+++ b/test/spec/modules/sharethroughBidAdapter_spec.js
@@ -1,247 +1,38 @@
import { expect } from 'chai';
import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js';
import { newBidder } from 'src/adapters/bidderFactory.js';
-import * as utils from '../../../src/utils.js';
import { config } from 'src/config';
+import * as utils from 'src/utils';
const spec = newBidder(sharethroughAdapterSpec).getSpec();
-const bidRequests = [
- {
- bidder: 'sharethrough',
- bidId: 'bidId1',
- sizes: [[600, 300]],
- placementCode: 'foo',
- params: {
- pkey: 'aaaa1111'
- },
- ortb2Imp: {
- ext: {
- data: {
- pbadslot: 'adslot-id-1'
- }
- }
- },
- userId: {
- tdid: 'fake-tdid',
- pubcid: 'fake-pubcid',
- idl_env: 'fake-identity-link',
- id5id: {
- uid: 'fake-id5id',
- ext: {
- linkType: 2
- }
- },
- lipb: {
- lipbid: 'fake-lipbid'
- }
- },
- crumbs: {
- pubcid: 'fake-pubcid-in-crumbs-obj'
- }
- },
- {
- bidder: 'sharethrough',
- bidId: 'bidId2',
- sizes: [[700, 400]],
- placementCode: 'bar',
- params: {
- pkey: 'bbbb2222',
- iframe: true
- }
- },
- {
- bidder: 'sharethrough',
- bidId: 'bidId3',
- sizes: [[700, 400]],
- placementCode: 'coconut',
- params: {
- pkey: 'cccc3333',
- iframe: true,
- iframeSize: [500, 500]
- }
- },
- {
- bidder: 'sharethrough',
- bidId: 'bidId4',
- sizes: [[700, 400]],
- placementCode: 'bar',
- params: {
- pkey: 'dddd4444',
- badv: ['domain1.com', 'domain2.com']
- }
- },
- {
- bidder: 'sharethrough',
- bidId: 'bidId5',
- sizes: [[700, 400]],
- placementCode: 'bar',
- params: {
- pkey: 'eeee5555',
- bcat: ['IAB1-1', 'IAB1-2']
- }
- },
-];
-
-const prebidRequests = [
- {
- method: 'POST',
- url: 'https://btlr.sharethrough.com/WYu2BXv1/v1',
- data: {
- bidId: 'bidId',
- placement_key: 'pKey'
- },
- strData: {
- skipIframeBusting: false,
- sizes: []
- }
- },
- {
- method: 'POST',
- url: 'https://btlr.sharethrough.com/WYu2BXv1/v1',
- data: {
- bidId: 'bidId',
- placement_key: 'pKey'
- },
- strData: {
- skipIframeBusting: true,
- sizes: [[300, 250], [300, 300], [250, 250], [600, 50]]
- }
- },
- {
- method: 'POST',
- url: 'https://btlr.sharethrough.com/WYu2BXv1/v1',
- data: {
- bidId: 'bidId',
- placement_key: 'pKey'
- },
- strData: {
- skipIframeBusting: true,
- iframeSize: [500, 500],
- sizes: [[300, 250], [300, 300], [250, 250], [600, 50]]
- }
- },
- {
- method: 'POST',
- url: 'https://btlr.sharethrough.com/WYu2BXv1/v1',
- data: {
- bidId: 'bidId',
- placement_key: 'pKey'
- },
- strData: {
- skipIframeBusting: false,
- sizes: [[0, 0]]
- }
- },
- {
- method: 'POST',
- url: 'https://btlr.sharethrough.com/WYu2BXv1/v1',
- data: {
- bidId: 'bidId',
- placement_key: 'pKey'
- },
- strData: {
- skipIframeBusting: false,
- sizes: [[300, 250], [300, 300], [250, 250], [600, 50]]
- }
- }
-];
-
-const bidderResponse = {
- body: {
- 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994',
- 'bidId': 'bidId1',
- 'version': 1,
- 'creatives': [{
- 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051',
- 'cpm': 12.34,
- 'creative': {
- 'deal_id': 'aDealId',
- 'creative_key': 'aCreativeId',
- 'title': '✓ à la mode'
- }
- }],
- 'stxUserId': ''
- },
- header: { get: (header) => header }
-};
-
-const setUserAgent = (uaString) => {
- window.navigator['__defineGetter__']('userAgent', function() {
- return uaString;
- });
-};
-
-describe('sharethrough internal spec', function() {
- let windowStub, windowTopStub;
- let stubbedReturn = [{
- appendChild: () => undefined
- }]
- beforeEach(function() {
- windowStub = sinon.stub(window.document, 'getElementsByTagName');
- windowTopStub = sinon.stub(window.top.document, 'getElementsByTagName');
- windowStub.withArgs('body').returns(stubbedReturn);
- windowTopStub.withArgs('body').returns(stubbedReturn);
- });
-
- afterEach(function() {
- windowStub.restore();
- windowTopStub.restore();
- window.STR = undefined;
- window.top.STR = undefined;
- });
-
- describe('we cannot access top level document', function() {
- beforeEach(function() {
- window.lockedInFrame = true;
- });
-
- afterEach(function() {
- window.lockedInFrame = false;
- });
- it('appends sfp.js to the safeframe', function() {
- sharethroughInternal.handleIframe();
- expect(windowStub.calledOnce).to.be.true;
- });
+describe('sharethrough adapter spec', function() {
+ let protocolStub;
+ let inIframeStub;
- it('does not append anything if sfp.js is already loaded in the safeframe', function() {
- window.STR = { Tag: true };
- sharethroughInternal.handleIframe();
- expect(windowStub.notCalled).to.be.true;
- expect(windowTopStub.notCalled).to.be.true;
- });
+ beforeEach(() => {
+ protocolStub = sinon.stub(sharethroughInternal, 'getProtocol').returns('https');
+ inIframeStub = sinon.stub(utils, 'inIframe').returns(false);
});
- describe('we are able to bust out of the iframe', function() {
- it('appends sfp.js to window.top', function() {
- sharethroughInternal.handleIframe();
- expect(windowStub.calledOnce).to.be.true;
- expect(windowTopStub.calledOnce).to.be.true;
- });
-
- 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(windowStub.calledOnce).to.be.true;
- expect(windowTopStub.notCalled).to.be.true;
- });
+ afterEach(() => {
+ protocolStub.restore();
+ inIframeStub.restore();
});
-});
-describe('sharethrough adapter spec', function() {
- describe('.code', function() {
+ describe('code', function() {
it('should return a bidder code of sharethrough', function() {
expect(spec.code).to.eql('sharethrough');
});
});
- describe('.isBidRequestValid', function() {
+ describe('isBidRequestValid', function() {
it('should return false if req has no pkey', function() {
const invalidBidRequest = {
bidder: 'sharethrough',
params: {
- notPKey: 'abc123'
- }
+ notPKey: 'abc123',
+ },
};
expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false);
});
@@ -250,383 +41,504 @@ describe('sharethrough adapter spec', function() {
const invalidBidRequest = {
bidder: 'notSharethrough',
params: {
- notPKey: 'abc123'
+ pkey: 'abc123',
}
};
expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false);
});
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);
+ const validBidRequest = {
+ bidder: 'sharethrough',
+ params: {
+ pkey: 'abc123',
+ },
+ };
+ expect(spec.isBidRequestValid(validBidRequest)).to.eq(true);
});
});
- describe('.buildRequests', function() {
- it('should return an array of requests', function() {
- const builtBidRequests = spec.buildRequests(bidRequests);
+ describe('open rtb', () => {
+ let bidRequests, bidderRequest;
+
+ beforeEach(() => {
+ config.setConfig({
+ bidderTimeout: 242,
+ coppa: true,
+ });
- 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('POST');
+ bidRequests = [
+ {
+ bidder: 'sharethrough',
+ bidId: 'bidId1',
+ sizes: [[300, 250], [300, 600]],
+ params: {
+ pkey: 'aaaa1111',
+ bcat: ['cat1', 'cat2'],
+ badv: ['adv1', 'adv2'],
+ },
+ mediaTypes: {
+ banner: {
+ pos: 1,
+ },
+ },
+ ortb2Imp: {
+ ext: {
+ data: {
+ pbadslot: 'universal-id',
+ },
+ },
+ },
+ userId: {
+ tdid: 'fake-tdid',
+ pubcid: 'fake-pubcid',
+ idl_env: 'fake-identity-link',
+ id5id: {
+ uid: 'fake-id5id',
+ ext: {
+ linkType: 2,
+ },
+ },
+ lipb: {
+ lipbid: 'fake-lipbid',
+ },
+ criteoId: 'fake-criteo',
+ britepoolid: 'fake-britepool',
+ intentIqId: 'fake-intentiq',
+ lotamePanoramaId: 'fake-lotame',
+ parrableId: {
+ eid: 'fake-parrable',
+ },
+ netId: 'fake-netid',
+ sharedid: {
+ id: 'fake-sharedid',
+ },
+ flocId: {
+ id: 'fake-flocid',
+ version: '42',
+ },
+ },
+ crumbs: {
+ pubcid: 'fake-pubcid-in-crumbs-obj',
+ },
+ schain: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [
+ {
+ asi: 'directseller.com',
+ sid: '00001',
+ rid: 'BidRequest1',
+ hp: 1,
+ },
+ ],
+ },
+ getFloor: () => ({ currency: 'USD', floor: 42 }),
+ },
+ {
+ bidder: 'sharethrough',
+ bidId: 'bidId2',
+ sizes: [[600, 300]],
+ params: {
+ pkey: 'bbbb2222',
+ },
+ mediaTypes: {
+ video: {
+ pos: 3,
+ skip: 1,
+ linearity: 0,
+ minduration: 10,
+ maxduration: 30,
+ playbackmethod: [1],
+ api: [3],
+ mimes: ['video/3gpp'],
+ protocols: [2, 3],
+ playerSize: [640, 480],
+ startdelay: 42,
+ skipmin: 10,
+ skipafter: 20,
+ placement: 1,
+ delivery: 1,
+ companiontype: 'companion type',
+ companionad: 'companion ad',
+ context: 'instream',
+ },
+ },
+ getFloor: () => ({ currency: 'USD', floor: 42 }),
+ },
+ ];
+
+ bidderRequest = {
+ refererInfo: {
+ referer: 'https://referer.com',
+ },
+ };
});
- 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;
+ describe('buildRequests', function() {
+ describe('top level object', () => {
+ it('should build openRTB request', () => {
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+
+ expect(builtRequest.method).to.equal('POST');
+ expect(builtRequest.url).not.to.be.undefined;
+ expect(builtRequest.options).to.be.undefined;
+ expect(builtRequest.bidderRequest).to.deep.equal(bidderRequest);
+
+ const openRtbReq = builtRequest.data;
+ expect(openRtbReq.id).not.to.be.undefined;
+ expect(openRtbReq.cur).to.deep.equal(['USD']);
+ expect(openRtbReq.tmax).to.equal(242);
+
+ expect(openRtbReq.site.domain).not.to.be.undefined;
+ expect(openRtbReq.site.page).not.to.be.undefined;
+ expect(openRtbReq.site.ref).to.equal('https://referer.com');
+
+ const expectedEids = {
+ 'liveramp.com': { id: 'fake-identity-link' },
+ 'id5-sync.com': { id: 'fake-id5id' },
+ 'pubcid.org': { id: 'fake-pubcid' },
+ 'adserver.org': { id: 'fake-tdid' },
+ 'criteo.com': { id: 'fake-criteo' },
+ 'britepool.com': { id: 'fake-britepool' },
+ 'liveintent.com': { id: 'fake-lipbid' },
+ 'intentiq.com': { id: 'fake-intentiq' },
+ 'crwdcntrl.net': { id: 'fake-lotame' },
+ 'parrable.com': { id: 'fake-parrable' },
+ 'netid.de': { id: 'fake-netid' },
+ 'chrome.com': { id: 'fake-flocid' },
+ };
+ expect(openRtbReq.user.ext.eids).to.be.an('array').that.have.length(Object.keys(expectedEids).length);
+ for (const eid of openRtbReq.user.ext.eids) {
+ expect(Object.keys(expectedEids)).to.include(eid.source);
+ expect(eid.uids[0].id).to.equal(expectedEids[eid.source].id);
+ expect(eid.uids[0].atype).to.be.ok;
+ }
- setUserAgent('iPhone Version/11');
- builtBidRequests = spec.buildRequests(bidRequests);
- expect(builtBidRequests[0].data.instant_play_capable).to.be.true;
+ expect(openRtbReq.device.ua).to.equal(navigator.userAgent);
+ expect(openRtbReq.regs.coppa).to.equal(1);
- setUserAgent('iPhone CriOS/60');
- builtBidRequests = spec.buildRequests(bidRequests);
- expect(builtBidRequests[0].data.instant_play_capable).to.be.true;
+ expect(openRtbReq.source.ext.version).not.to.be.undefined;
+ expect(openRtbReq.source.ext.str).not.to.be.undefined;
+ expect(openRtbReq.source.ext.schain).to.deep.equal(bidRequests[0].schain);
- setUserAgent('Android Chrome/50');
- builtBidRequests = spec.buildRequests(bidRequests);
- expect(builtBidRequests[0].data.instant_play_capable).to.be.false;
+ expect(openRtbReq.bcat).to.deep.equal(bidRequests[0].params.bcat);
+ expect(openRtbReq.badv).to.deep.equal(bidRequests[0].params.badv);
- setUserAgent('Android Chrome');
- builtBidRequests = spec.buildRequests(bidRequests);
- expect(builtBidRequests[0].data.instant_play_capable).to.be.false;
+ expect(openRtbReq.imp).to.have.length(2);
- setUserAgent(undefined);
- builtBidRequests = spec.buildRequests(bidRequests);
- expect(builtBidRequests[0].data.instant_play_capable).to.be.false;
- });
+ expect(openRtbReq.imp[0].id).to.equal('bidId1');
+ expect(openRtbReq.imp[0].tagid).to.equal('aaaa1111');
+ expect(openRtbReq.imp[0].secure).to.equal(1);
+ expect(openRtbReq.imp[0].bidfloor).to.equal(42);
- it('should set the secure parameter to false when the protocol is http', 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();
- });
+ expect(openRtbReq.imp[1].id).to.equal('bidId2');
+ expect(openRtbReq.imp[1].tagid).to.equal('bbbb2222');
+ expect(openRtbReq.imp[1].secure).to.equal(1);
+ expect(openRtbReq.imp[1].bidfloor).to.equal(42);
+ });
- 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();
- });
+ it('should have empty eid array if no id is provided', () => {
+ const openRtbReq = spec.buildRequests([bidRequests[1]], bidderRequest).data;
- 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();
- });
+ expect(openRtbReq.user.ext.eids).to.deep.equal([]);
+ });
+ });
- 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);
- });
+ describe('regulation', () => {
+ describe('gdpr', () => {
+ it('should populate request accordingly when gdpr applies', () => {
+ bidderRequest.gdprConsent = {
+ gdprApplies: true,
+ consentString: 'consent',
+ };
- 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');
- });
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- 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(openRtbReq.regs.ext.gdpr).to.equal(1);
+ expect(openRtbReq.user.ext.consent).to.equal('consent');
+ });
- 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 populate request accordingly when gdpr explicitly does not apply', () => {
+ bidderRequest.gdprConsent = {
+ gdprApplies: false,
+ };
- 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');
- });
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- 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 bidData = utils.deepClone(bidRequests);
- delete bidData[0].userId.pubcid;
+ expect(openRtbReq.regs.ext.gdpr).to.equal(0);
+ expect(openRtbReq.user.ext.consent).to.be.undefined;
+ });
+ });
- const bidRequest = spec.buildRequests(bidData)[0];
- expect(bidRequest.data.pubcid).to.eq('fake-pubcid-in-crumbs-obj');
- });
+ describe('US privacy', () => {
+ it('should populate request accordingly when us privacy applies', () => {
+ bidderRequest.uspConsent = 'consent';
- 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');
- });
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- it('should add the idluid parameter if a bid request contains a value for Identity Link from Live Ramp', function() {
- const bidRequest = spec.buildRequests(bidRequests)[0];
- expect(bidRequest.data.idluid).to.eq('fake-identity-link');
- });
+ expect(openRtbReq.regs.ext.us_privacy).to.equal('consent');
+ });
+ });
- it('should add the id5uid parameter if a bid request contains a value for ID5', function() {
- const bidRequest = spec.buildRequests(bidRequests)[0];
- expect(bidRequest.data.id5uid.id).to.eq('fake-id5id');
- expect(bidRequest.data.id5uid.linkType).to.eq(2);
- });
+ describe('coppa', () => {
+ it('should populate request accordingly when coppa does not apply', () => {
+ config.setConfig({ coppa: false });
- it('should add the liuid parameter if a bid request contains a value for LiveIntent ID', function() {
- const bidRequest = spec.buildRequests(bidRequests)[0];
- expect(bidRequest.data.liuid).to.eq('fake-lipbid');
- });
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- it('should add Sharethrough specific parameters', function() {
- const builtBidRequests = spec.buildRequests(bidRequests);
- expect(builtBidRequests[0]).to.deep.include({
- strData: {
- skipIframeBusting: undefined,
- iframeSize: undefined,
- sizes: [[600, 300]]
- }
+ expect(openRtbReq.regs.coppa).to.equal(0);
+ });
+ });
});
- });
-
- it('should add a supply chain parameter if schain is present', function() {
- // shallow copy of the first bidRequest obj, so we don't mutate
- const bidRequest = Object.assign({}, bidRequests[0]);
- bidRequest['schain'] = {
- ver: '1.0',
- complete: 1,
- nodes: [
- {
- asi: 'directseller.com',
- sid: '00001',
- rid: 'BidRequest1',
- hp: 1
- }
- ]
- };
- const builtBidRequest = spec.buildRequests([bidRequest])[0];
- expect(builtBidRequest.data.schain).to.eq(JSON.stringify(bidRequest.schain));
- });
-
- describe('gpid', () => {
- it('should include the gpid param if pbadslot is found in ortb2Imp in the bid request', () => {
- const bidRequest = spec.buildRequests(bidRequests)[0];
- expect(bidRequest.data.gpid).to.eq('adslot-id-1')
- });
+ describe('universal id', () => {
+ it('should include gpid when universal id is provided', () => {
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- it('should not include the gpid param if pbadslot is not found in ortb2Imp in the bid request', () => {
- const bidRequest = spec.buildRequests(bidRequests)[1];
- expect(bidRequest.data).to.not.include.any.keys('gpid');
+ expect(openRtbReq.imp[0].ext.gpid).to.equal('universal-id');
+ expect(openRtbReq.imp[1].ext).to.be.undefined;
+ });
});
- });
-
- it('should add badv if provided', () => {
- const builtBidRequest = spec.buildRequests([bidRequests[3]])[0];
-
- expect(builtBidRequest.data.badv).to.have.members(['domain1.com', 'domain2.com'])
- });
-
- it('should add bcat if provided', () => {
- const builtBidRequest = spec.buildRequests([bidRequests[4]])[0];
- expect(builtBidRequest.data.bcat).to.have.members(['IAB1-1', 'IAB1-2'])
- });
+ describe('secure flag', () => {
+ it('should be positive when protocol is https', () => {
+ protocolStub.returns('https');
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- it('should not add a supply chain parameter if schain is missing', function() {
- const bidRequest = spec.buildRequests(bidRequests)[0];
- expect(bidRequest.data).to.not.include.any.keys('schain');
- });
+ expect(openRtbReq.imp[0].secure).to.equal(1);
+ expect(openRtbReq.imp[1].secure).to.equal(1);
+ });
- it('should include the bidfloor parameter if it is present in the bid request', function() {
- const bidRequest = Object.assign({}, bidRequests[0]);
- bidRequest['getFloor'] = () => ({ currency: 'USD', floor: 0.5 });
- const builtBidRequest = spec.buildRequests([bidRequest])[0];
- expect(builtBidRequest.data.bidfloor).to.eq(0.5);
- });
+ it('should be negative when protocol is http', () => {
+ protocolStub.returns('http');
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- it('should not include the bidfloor parameter if it is missing in the bid request', function() {
- const bidRequest = Object.assign({}, bidRequests[0]);
- const builtBidRequest = spec.buildRequests([bidRequest])[0];
- expect(builtBidRequest.data).to.not.include.any.keys('bidfloor');
- });
+ expect(openRtbReq.imp[0].secure).to.equal(0);
+ expect(openRtbReq.imp[1].secure).to.equal(0);
+ });
- describe('coppa', function() {
- it('should add coppa to request if enabled', function() {
- config.setConfig({coppa: true});
- const bidRequest = Object.assign({}, bidRequests[0]);
- const builtBidRequest = spec.buildRequests([bidRequest])[0];
- expect(builtBidRequest.data.coppa).to.eq(true);
- });
+ it('should be positive when protocol is neither http nor https', () => {
+ protocolStub.returns('about');
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+ const openRtbReq = builtRequest.data;
- it('should not add coppa to request if disabled', function() {
- config.setConfig({coppa: false});
- const bidRequest = Object.assign({}, bidRequests[0]);
- const builtBidRequest = spec.buildRequests([bidRequest])[0];
- expect(builtBidRequest.data.coppa).to.be.undefined;
+ expect(openRtbReq.imp[0].secure).to.equal(1);
+ expect(openRtbReq.imp[1].secure).to.equal(1);
+ });
});
- it('should not add coppa to request if unknown value', function() {
- config.setConfig({coppa: 'something'});
- const bidRequest = Object.assign({}, bidRequests[0]);
- const builtBidRequest = spec.buildRequests([bidRequest])[0];
- expect(builtBidRequest.data.coppa).to.be.undefined;
- });
- });
- });
+ describe('banner imp', () => {
+ it('should generate open rtb banner imp', () => {
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
- describe('.interpretResponse', function() {
- it('returns a correctly parsed out response', function() {
- expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.deep.include(
- {
- width: 1,
- height: 1,
- cpm: 12.34,
- creativeId: 'aCreativeId',
- dealId: 'aDealId',
- currency: 'USD',
- netRevenue: true,
- ttl: 360,
- meta: { advertiserDomains: [] }
+ const bannerImp = builtRequest.data.imp[0].banner;
+ expect(bannerImp.pos).to.equal(1);
+ expect(bannerImp.topframe).to.equal(1);
+ expect(bannerImp.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]);
});
- });
- 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,
- height: 300,
- cpm: 12.34,
- creativeId: 'aCreativeId',
- dealId: 'aDealId',
- currency: 'USD',
- netRevenue: true,
- ttl: 360
- });
- });
+ it('should default to pos 0 if not provided', () => {
+ delete bidRequests[0].mediaTypes;
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
- 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,
- height: 500,
- cpm: 12.34,
- creativeId: 'aCreativeId',
- dealId: 'aDealId',
- currency: 'USD',
- netRevenue: true,
- ttl: 360
+ const bannerImp = builtRequest.data.imp[0].banner;
+ expect(bannerImp.pos).to.equal(0);
});
- });
+ });
- 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,
- height: 0,
- cpm: 12.34,
- creativeId: 'aCreativeId',
- dealId: 'aDealId',
- currency: 'USD',
- netRevenue: true,
- ttl: 360
+ describe('video imp', () => {
+ it('should generate open rtb video imp', () => {
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+
+ const videoImp = builtRequest.data.imp[1].video;
+ expect(videoImp.pos).to.equal(3);
+ expect(videoImp.topframe).to.equal(1);
+ expect(videoImp.skip).to.equal(1);
+ expect(videoImp.linearity).to.equal(0);
+ expect(videoImp.minduration).to.equal(10);
+ expect(videoImp.maxduration).to.equal(30);
+ expect(videoImp.playbackmethod).to.deep.equal([1]);
+ expect(videoImp.api).to.deep.equal([3]);
+ expect(videoImp.mimes).to.deep.equal(['video/3gpp']);
+ expect(videoImp.protocols).to.deep.equal([2, 3]);
+ expect(videoImp.w).to.equal(640);
+ expect(videoImp.h).to.equal(480);
+ expect(videoImp.startdelay).to.equal(42);
+ expect(videoImp.skipmin).to.equal(10);
+ expect(videoImp.skipafter).to.equal(20);
+ expect(videoImp.placement).to.equal(1);
+ expect(videoImp.delivery).to.equal(1);
+ expect(videoImp.companiontype).to.equal('companion type');
+ expect(videoImp.companionad).to.equal('companion ad');
});
- });
- 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,
- height: 300,
- cpm: 12.34,
- creativeId: 'aCreativeId',
- dealId: 'aDealId',
- currency: 'USD',
- netRevenue: true,
- ttl: 360
+ it('should set defaults if no value provided', () => {
+ delete bidRequests[1].mediaTypes.video.pos;
+ delete bidRequests[1].mediaTypes.video.skip;
+ delete bidRequests[1].mediaTypes.video.linearity;
+ delete bidRequests[1].mediaTypes.video.minduration;
+ delete bidRequests[1].mediaTypes.video.maxduration;
+ delete bidRequests[1].mediaTypes.video.playbackmethod;
+ delete bidRequests[1].mediaTypes.video.api;
+ delete bidRequests[1].mediaTypes.video.mimes;
+ delete bidRequests[1].mediaTypes.video.protocols;
+ delete bidRequests[1].mediaTypes.video.playerSize;
+ delete bidRequests[1].mediaTypes.video.startdelay;
+ delete bidRequests[1].mediaTypes.video.skipmin;
+ delete bidRequests[1].mediaTypes.video.skipafter;
+ delete bidRequests[1].mediaTypes.video.placement;
+ delete bidRequests[1].mediaTypes.video.delivery;
+ delete bidRequests[1].mediaTypes.video.companiontype;
+ delete bidRequests[1].mediaTypes.video.companionad;
+
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
+
+ const videoImp = builtRequest.data.imp[1].video;
+ expect(videoImp.pos).to.equal(0);
+ expect(videoImp.skip).to.equal(0);
+ expect(videoImp.linearity).to.equal(1);
+ expect(videoImp.minduration).to.equal(5);
+ expect(videoImp.maxduration).to.equal(60);
+ expect(videoImp.playbackmethod).to.deep.equal([2]);
+ expect(videoImp.api).to.deep.equal([2]);
+ expect(videoImp.mimes).to.deep.equal(['video/mp4']);
+ expect(videoImp.protocols).to.deep.equal([2, 3, 5, 6, 7, 8]);
+ expect(videoImp.w).to.equal(640);
+ expect(videoImp.h).to.equal(360);
+ expect(videoImp.startdelay).to.equal(0);
+ expect(videoImp.skipmin).to.equal(0);
+ expect(videoImp.skipafter).to.equal(0);
+ expect(videoImp.placement).to.be.undefined;
+ expect(videoImp.delivery).to.be.undefined;
+ expect(videoImp.companiontype).to.be.undefined;
+ expect(videoImp.companionad).to.be.undefined;
});
- });
- 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('should not return a video impression if context is outstream', () => {
+ bidRequests[1].mediaTypes.video.context = 'outstream';
+ const builtRequest = spec.buildRequests(bidRequests, bidderRequest);
- 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() {
- const bidResponse = { body: null };
- expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
+ const videoImp = builtRequest.data.imp[1];
+ expect(videoImp).to.be.undefined;
+ });
+ });
});
- it('correctly generates ad markup when skipIframeBusting is false', function() {
- const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[0])[0].ad;
- let resp = null;
+ describe('interpretResponse', function() {
+ let request;
+ let response;
+
+ beforeEach(() => {
+ request = spec.buildRequests(bidRequests, bidderRequest);
+ response = {
+ body: {
+ seatbid: [{
+ bid: [{
+ id: '123',
+ impid: 'bidId1',
+ w: 300,
+ h: 250,
+ price: 42,
+ crid: 'creative',
+ dealid: 'deal',
+ adomain: ['domain.com'],
+ adm: 'markup',
+ }, {
+ id: '456',
+ impid: 'bidId2',
+ w: 640,
+ h: 480,
+ price: 42,
+ adm: 'vastTag',
+ }],
+ }],
+ },
+ };
+ });
- expect(() => btoa(JSON.stringify(bidderResponse))).to.throw();
- expect(() => resp = sharethroughInternal.b64EncodeUnicode(JSON.stringify(bidderResponse))).not.to.throw();
- expect(adMarkup).to.match(
- /data-str-native-key="pKey" data-stx-response-name="str_response_bidId"/);
- expect(!!adMarkup.indexOf(resp)).to.eql(true);
+ describe('banner', () => {
+ it('should return a banner bid', () => {
+ const resp = spec.interpretResponse(response, request);
+
+ const bannerBid = resp[0];
+ expect(bannerBid.requestId).to.equal('bidId1');
+ expect(bannerBid.width).to.equal(300);
+ expect(bannerBid.height).to.equal(250);
+ expect(bannerBid.cpm).to.equal(42);
+ expect(bannerBid.creativeId).to.equal('creative');
+ expect(bannerBid.dealId).to.equal('deal');
+ expect(bannerBid.mediaType).to.equal('banner');
+ expect(bannerBid.currency).to.equal('USD');
+ expect(bannerBid.netRevenue).to.equal(true);
+ expect(bannerBid.ttl).to.equal(360);
+ expect(bannerBid.ad).to.equal('markup');
+ expect(bannerBid.meta.advertiserDomains).to.deep.equal(['domain.com']);
+ expect(bannerBid.vastXml).to.be.undefined;
+ });
+ });
- // insert functionality to autodetect whether or not in safeframe, and handle JS insertion
- expect(adMarkup).to.match(/isLockedInFrame/);
- expect(adMarkup).to.match(/handleIframe/);
+ describe('video', () => {
+ it('should return a video bid', () => {
+ const resp = spec.interpretResponse(response, request);
+
+ const bannerBid = resp[1];
+ expect(bannerBid.requestId).to.equal('bidId2');
+ expect(bannerBid.width).to.equal(640);
+ expect(bannerBid.height).to.equal(480);
+ expect(bannerBid.cpm).to.equal(42);
+ expect(bannerBid.creativeId).to.be.undefined;
+ expect(bannerBid.dealId).to.be.null;
+ expect(bannerBid.mediaType).to.equal('video');
+ expect(bannerBid.currency).to.equal('USD');
+ expect(bannerBid.netRevenue).to.equal(true);
+ expect(bannerBid.ttl).to.equal(3600);
+ expect(bannerBid.ad).to.equal('vastTag');
+ expect(bannerBid.meta.advertiserDomains).to.deep.equal([]);
+ expect(bannerBid.vastXml).to.equal('vastTag');
+ });
+ });
});
- it('correctly generates ad markup when skipIframeBusting is true', function() {
- const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[1])[0].ad;
- let resp = null;
-
- expect(() => btoa(JSON.stringify(bidderResponse))).to.throw();
- expect(() => resp = sharethroughInternal.b64EncodeUnicode(JSON.stringify(bidderResponse))).not.to.throw();
- expect(adMarkup).to.match(
- /data-str-native-key="pKey" data-stx-response-name="str_response_bidId"/);
- expect(!!adMarkup.indexOf(resp)).to.eql(true);
- expect(adMarkup).to.match(
- /