diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 1dc04a9c2b7..61eb327fd2c 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -26,6 +26,9 @@ Thank you for your pull request. Please make sure this PR is scoped to one chang
}
}
```
+
+Be sure to test the integration with your adserver using the [Hello World](/integrationExamples/gpt/hello_world.html) sample page.
+
- contact email of the adapter’s maintainer
- [ ] official adapter submission
diff --git a/CHANGELOG b/CHANGELOG
index c1a03f7ba63..22cced6a558 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+AOL Prebid 1.24.0
+----------------
+Updated to Prebid 0.25.0
+
+
AOL Prebid 1.23.0
----------------
Added passing key values feature.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3252375ac68..5856835f785 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,6 +31,8 @@ Before a Pull Request will be considered for merge:
- All new and existing tests must pass
- Added or modified code must have greater than 80% coverage
+If you are submitting an adapter, you can also use the [Hello World](integrationExamples/gpt/hello_world.html) example page to test integration with your server.
+
### Test Guidelines
When you are adding code to Prebid.js, or modifying code that isn't covered by an existing test, test the code according to these guidelines:
diff --git a/adapters.json b/adapters.json
index b6d1b41ad70..7d62a26c235 100644
--- a/adapters.json
+++ b/adapters.json
@@ -7,6 +7,7 @@
"adform",
"adkernel",
"admedia",
+ "adyoulike",
"bidfluence",
"vertamedia",
"aol",
@@ -14,6 +15,7 @@
"appnexusAst",
"beachfront",
"audienceNetwork",
+ "carambola",
"conversant",
"districtmDMX",
"fidelity",
@@ -66,8 +68,10 @@
"atomx",
"tapsense",
"trion",
+ "eplanning",
"prebidServer",
"adsupply",
+ "cox",
{
"aol": {
"alias": "onemobile"
@@ -153,5 +157,15 @@
"rhythmone": {
"supportedMediaTypes": ["video"]
}
+ },
+ {
+ "admixer": {
+ "supportedMediaTypes": ["video"]
+ }
+ },
+ {
+ "conversant": {
+ "supportedMediaTypes": ["video"]
+ }
}
]
diff --git a/gulpfile.js b/gulpfile.js
index e2511fd0dad..47ab725b4aa 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -46,7 +46,7 @@ gulp.task('clean', function () {
.pipe(clean());
});
-gulp.task('devpack', function () {
+gulp.task('devpack', ['clean'], function () {
webpackConfig.devtool = 'source-map';
const analyticsSources = helpers.getAnalyticsSources(analyticsDirectory);
return gulp.src([].concat(analyticsSources, 'src/prebid.js'))
@@ -201,12 +201,11 @@ gulp.task('watch', function () {
'src/**/*.js',
'test/spec/**/*.js',
'!test/spec/loaders/**/*.js'
- ], ['lint', 'webpack', 'devpack', 'test']);
+ ], ['clean', 'lint', 'webpack', 'devpack', 'test']);
gulp.watch([
'loaders/**/*.js',
'test/spec/loaders/**/*.js'
], ['lint', 'mocha']);
- gulp.watch(['integrationExamples/gpt/*.html'], ['test']);
connect.server({
https: argv.https,
port: port,
diff --git a/integrationExamples/gpt/.gitignore b/integrationExamples/gpt/.gitignore
deleted file mode 100644
index fb64ad0cc6e..00000000000
--- a/integrationExamples/gpt/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-pbjs_adblade_example_gpt.html
diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html
new file mode 100644
index 00000000000..0f5e24a301a
--- /dev/null
+++ b/integrationExamples/gpt/hello_world.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Prebid.js Test
+Div-1
+
+
+
+
+
diff --git a/package.json b/package.json
index c906d2459d8..4d3832e1fe8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "0.24.1",
+ "version": "0.25.0",
"description": "Header Bidding Management Library",
"main": "src/prebid.js",
"scripts": {
diff --git a/src/Renderer.js b/src/Renderer.js
index c4e926ca4d5..c81049fa1f6 100644
--- a/src/Renderer.js
+++ b/src/Renderer.js
@@ -2,19 +2,37 @@ import { loadScript } from 'src/adloader';
import * as utils from 'src/utils';
export function Renderer(options) {
- const { url, config, id, callback } = options;
+ const { url, config, id, callback, loaded } = options;
this.url = url;
this.config = config;
- this.callback = callback;
this.handlers = {};
this.id = id;
+ // a renderer may push to the command queue to delay rendering until the
+ // render function is loaded by loadScript, at which point the the command
+ // queue will be processed
+ this.loaded = loaded;
+ this.cmd = [];
+ this.push = func => {
+ if (typeof func !== 'function') {
+ utils.logError('Commands given to Renderer.push must be wrapped in a function');
+ return;
+ }
+ this.loaded ? func.call() : this.cmd.push(func);
+ };
+
+ // bidders may override this with the `callback` property given to `install`
+ this.callback = callback || (() => {
+ this.loaded = true;
+ this.process();
+ });
+
// we expect to load a renderer url once only so cache the request to load script
- loadScript(url, callback, true);
+ loadScript(url, this.callback, true);
}
-Renderer.install = function({ url, config, id, callback }) {
- return new Renderer({ url, config, id, callback });
+Renderer.install = function({ url, config, id, callback, loaded }) {
+ return new Renderer({ url, config, id, callback, loaded });
};
Renderer.prototype.getConfig = function() {
@@ -36,3 +54,17 @@ Renderer.prototype.handleVideoEvent = function({ id, eventName }) {
utils.logMessage(`Prebid Renderer event for id ${id} type ${eventName}`);
};
+
+/*
+ * Calls functions that were pushed to the command queue before the
+ * renderer was loaded by `loadScript`
+ */
+Renderer.prototype.process = function() {
+ while (this.cmd.length > 0) {
+ try {
+ this.cmd.shift().call();
+ } catch (error) {
+ utils.logError('Error processing Renderer command: ', error);
+ }
+ }
+};
diff --git a/src/adapters/adform.js b/src/adapters/adform.js
index 660c1c585b0..31eceba0b0d 100644
--- a/src/adapters/adform.js
+++ b/src/adapters/adform.js
@@ -10,11 +10,11 @@ function AdformAdapter() {
};
function _callBids(params) {
- var bid, _value, _key, i, j, k, l;
+ var bid, _value, _key, i, j, k, l, reqParams;
var bids = params.bids;
var request = [];
var callbackName = '_adf_' + utils.getUniqueIdentifierStr();
- var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'url', null ], [ 'tid', null ], [ 'callback', '$$PREBID_GLOBAL$$.' + callbackName ] ];
+ var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], ['fd', 1], [ 'url', null ], [ 'tid', null ], [ 'callback', '$$PREBID_GLOBAL$$.' + callbackName ] ];
for (i = 0, l = bids.length; i < l; i++) {
bid = bids[i];
@@ -28,7 +28,9 @@ function AdformAdapter() {
}
}
- request.push(formRequestUrl(bid.params));
+ reqParams = bid.params;
+ reqParams.transactionId = bid.transactionId;
+ request.push(formRequestUrl(reqParams));
}
request.unshift('//' + globalParams[0][1] + '/adx/?rp=4');
@@ -76,6 +78,7 @@ function AdformAdapter() {
bidObject.width = adItem.width;
bidObject.height = adItem.height;
bidObject.dealId = adItem.deal_id;
+ bidObject.transactionId = bid.transactionId;
bidmanager.addBidResponse(bid.placementCode, bidObject);
} else {
bidObject = bidfactory.createBid(STATUSCODES.NO_BID, bid);
diff --git a/src/adapters/admixer.js b/src/adapters/admixer.js
index 944aed276ce..24cf81bf9e9 100644
--- a/src/adapters/admixer.js
+++ b/src/adapters/admixer.js
@@ -10,6 +10,7 @@ var utils = require('../utils.js');
*/
var AdmixerAdapter = function AdmixerAdapter() {
var invUrl = '//inv-nets.admixer.net/prebid.aspx';
+ var invVastUrl = '//inv-nets.admixer.net/videoprebid.aspx';
function _callBids(data) {
var bids = data.bids || [];
@@ -21,7 +22,16 @@ var AdmixerAdapter = function AdmixerAdapter() {
'callback_uid': bid.placementCode
};
if (params.zone) {
- _requestBid(invUrl, params);
+ if (bid.mediaType === 'video') {
+ var videoParams = {};
+ if (typeof bid.video === 'object') {
+ Object.assign(videoParams, bid.video);
+ }
+ Object.assign(videoParams, params);
+ _requestBid(invVastUrl, params);
+ } else {
+ _requestBid(invUrl, params);
+ }
} else {
var bidObject = bidfactory.createBid(2);
bidObject.bidderCode = 'admixer';
@@ -48,7 +58,13 @@ var AdmixerAdapter = function AdmixerAdapter() {
bidObject = bidfactory.createBid(1);
bidObject.bidderCode = 'admixer';
bidObject.cpm = bid.cpm;
- bidObject.ad = bid.ad;
+ if (bid.vastUrl) {
+ bidObject.mediaType = 'video';
+ bidObject.vastUrl = bid.vastUrl;
+ bidObject.descriptionUrl = bid.vastUrl;
+ } else {
+ bidObject.ad = bid.ad;
+ }
bidObject.width = bid.width;
bidObject.height = bid.height;
} else {
diff --git a/src/adapters/adyoulike.js b/src/adapters/adyoulike.js
new file mode 100644
index 00000000000..7ee65eebe06
--- /dev/null
+++ b/src/adapters/adyoulike.js
@@ -0,0 +1,201 @@
+import Adapter from 'src/adapters/adapter';
+import bidfactory from 'src/bidfactory';
+import bidmanager from 'src/bidmanager';
+import * as utils from 'src/utils';
+import { format } from 'src/url';
+import { ajax } from 'src/ajax';
+import { STATUS } from 'src/constants';
+
+var AdyoulikeAdapter = function AdyoulikeAdapter() {
+ const _VERSION = '0.1';
+
+ const baseAdapter = Adapter.createNew('adyoulike');
+
+ baseAdapter.callBids = function (bidRequest) {
+ const bidRequests = {};
+ const bids = bidRequest.bids || [];
+
+ const validBids = bids.filter(valid);
+ validBids.forEach(bid => { bidRequests[bid.params.placement] = bid; });
+
+ const placements = validBids.map(bid => bid.params.placement);
+ if (!utils.isEmpty(placements)) {
+ const body = createBody(placements);
+ const endpoint = createEndpoint();
+ ajax(endpoint,
+ (response) => {
+ handleResponse(bidRequests, response);
+ }, body, {
+ contentType: 'text/json',
+ withCredentials: true
+ });
+ }
+ };
+
+ /* Create endpoint url */
+ function createEndpoint() {
+ return format({
+ protocol: (document.location.protocol === 'https:') ? 'https' : 'http',
+ host: 'hb-api.omnitagjs.com',
+ pathname: '/hb-api/prebid',
+ search: createEndpointQS()
+ });
+ }
+
+ /* Create endpoint query string */
+ function createEndpointQS() {
+ const qs = {};
+
+ const ref = getReferrerUrl();
+ if (ref) {
+ qs.RefererUrl = encodeURIComponent(ref);
+ }
+
+ const can = getCanonicalUrl();
+ if (can) {
+ qs.CanonicalUrl = encodeURIComponent(can);
+ }
+
+ return qs;
+ }
+
+ /* Create request body */
+ function createBody(placements) {
+ const body = {
+ Version: _VERSION,
+ Placements: placements,
+ };
+
+ // performance isn't supported by mobile safari iOS7. window.performance works, but
+ // evaluates to true on a unit test which expects false.
+ //
+ // try/catch was added to un-block the Prebid 0.25 release, but the adyoulike adapter
+ // maintainers should revisit this and see if it's really what they want.
+ try {
+ if (performance && performance.navigation) {
+ body.PageRefreshed = performance.navigation.type === performance.navigation.TYPE_RELOAD;
+ }
+ } catch (e) {
+ body.PageRefreshed = false;
+ }
+
+ return JSON.stringify(body);
+ }
+
+ /* Response handler */
+ function handleResponse(bidRequests, response) {
+ let responses = [];
+ try {
+ responses = JSON.parse(response);
+ } catch (error) { utils.logError(error); }
+
+ const bidResponses = {};
+ responses.forEach(response => {
+ bidResponses[response.Placement] = response;
+ });
+
+ Object.keys(bidRequests).forEach(placement => {
+ addResponse(placement, bidRequests[placement], bidResponses[placement]);
+ });
+ }
+
+ /* Check that a bid has required parameters */
+ function valid(bid) {
+ const sizes = getSize(bid.sizes);
+ if (!bid.params.placement || !sizes.width || !sizes.height) {
+ return false;
+ }
+ return true;
+ }
+
+ /* Get current page referrer url */
+ function getReferrerUrl() {
+ let referer = '';
+ if (window.self !== window.top) {
+ try {
+ referer = window.top.document.referrer;
+ } catch (e) { }
+ } else {
+ referer = document.referrer;
+ }
+ return referer;
+ }
+
+ /* Get current page canonical url */
+ function getCanonicalUrl() {
+ let link;
+ if (window.self !== window.top) {
+ try {
+ link = window.top.document.head.querySelector('link[rel="canonical"][href]');
+ } catch (e) { }
+ } else {
+ link = document.head.querySelector('link[rel="canonical"][href]');
+ }
+
+ if (link) {
+ return link.href;
+ }
+ return '';
+ }
+
+ /* Get parsed size from request size */
+ function getSize(requestSizes) {
+ const parsed = {},
+ size = utils.parseSizesInput(requestSizes)[0];
+
+ if (typeof size !== 'string') {
+ return parsed;
+ }
+
+ const parsedSize = size.toUpperCase().split('X');
+ const width = parseInt(parsedSize[0], 10);
+ if (width) {
+ parsed.width = width;
+ }
+
+ const height = parseInt(parsedSize[1], 10);
+ if (height) {
+ parsed.height = height;
+ }
+
+ return parsed;
+ }
+
+ /* Create bid from response */
+ function createBid(placementId, bidRequest, response) {
+ let bid;
+ if (!response || !response.Banner) {
+ bid = bidfactory.createBid(STATUS.NO_BID, bidRequest);
+ } else {
+ bid = bidfactory.createBid(STATUS.GOOD, bidRequest);
+ const size = getSize(bidRequest.sizes);
+ bid.width = size.width;
+ bid.height = size.height;
+ bid.cpm = response.Price;
+ bid.ad = response.Banner;
+ }
+
+ bid.bidderCode = baseAdapter.getBidderCode();
+
+ return bid;
+ }
+
+ /* Add response to bidmanager */
+ function addResponse(placementId, bidRequest, response) {
+ const bid = createBid(placementId, bidRequest, response);
+ const placement = bidRequest.placementCode;
+ bidmanager.addBidResponse(placement, bid);
+ }
+
+ return {
+ createNew: AdyoulikeAdapter.createNew,
+ callBids: baseAdapter.callBids,
+ setBidderCode: baseAdapter.setBidderCode,
+ };
+};
+
+AdyoulikeAdapter.createNew = function () {
+ return new AdyoulikeAdapter();
+};
+
+module.exports = AdyoulikeAdapter;
diff --git a/src/adapters/analytics/aolPartnersIds.json b/src/adapters/analytics/aolPartnersIds.json
index 17ccf9b4fe3..081fc27ebbc 100644
--- a/src/adapters/analytics/aolPartnersIds.json
+++ b/src/adapters/analytics/aolPartnersIds.json
@@ -74,5 +74,9 @@
"quantcast": 73,
"prebidServer": 74,
"onemobile": 75,
- "onedisplay": 76
+ "onedisplay": 76,
+ "adyoulike": 77,
+ "carambola": 78,
+ "cox": 79,
+ "eplanning":80
}
diff --git a/src/adapters/aol.js b/src/adapters/aol.js
index 786321c3338..95b82bbd6eb 100644
--- a/src/adapters/aol.js
+++ b/src/adapters/aol.js
@@ -231,8 +231,8 @@ const AolAdapter = function AolAdapter() {
let formattedPixels = response.ext.pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, '');
ad += '';
+ 'parent.$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped=true;' + formattedPixels +
+ '}';
}
}
@@ -264,8 +264,8 @@ const AolAdapter = function AolAdapter() {
if (_isNexageBidder(bid.bidder) && bid.params.id && bid.params.imp && bid.params.imp[0]) {
let imp = bid.params.imp[0];
return imp.id && imp.tagid &&
- ((imp.banner && imp.banner.w && imp.banner.h) ||
- (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration));
+ ((imp.banner && imp.banner.w && imp.banner.h) ||
+ (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration));
}
}
diff --git a/src/adapters/appnexus.js b/src/adapters/appnexus.js
index 0dea42daf41..fee88da7bf3 100644
--- a/src/adapters/appnexus.js
+++ b/src/adapters/appnexus.js
@@ -43,10 +43,7 @@ AppNexusAdapter = function AppNexusAdapter() {
var query = utils.getBidIdParameter('query', bid.params);
var referrer = utils.getBidIdParameter('referrer', bid.params);
var altReferrer = utils.getBidIdParameter('alt_referrer', bid.params);
-
- // build our base tag, based on if we are http or https
-
- var jptCall = 'http' + (document.location.protocol === 'https:' ? 's://secure.adnxs.com/jpt?' : '://ib.adnxs.com/jpt?');
+ var jptCall = '//ib.adnxs.com/jpt?';
jptCall = utils.tryAppendQueryString(jptCall, 'callback', '$$PREBID_GLOBAL$$.handleAnCB');
jptCall = utils.tryAppendQueryString(jptCall, 'callback_uid', callbackId);
diff --git a/src/adapters/appnexusAst.js b/src/adapters/appnexusAst.js
index a3bd0e72441..4ad2fe4502c 100644
--- a/src/adapters/appnexusAst.js
+++ b/src/adapters/appnexusAst.js
@@ -269,18 +269,17 @@ function AppnexusAstAdapter() {
}
function outstreamRender(bid) {
- window.ANOutstreamVideo.renderAd({
- tagId: bid.adResponse.tag_id,
- sizes: [bid.getSize().split('x')],
- targetId: bid.adUnitCode, // target div id to render video
- uuid: bid.adResponse.uuid,
- adResponse: bid.adResponse,
- rendererOptions: bid.renderer.getConfig()
- }, handleOutstreamRendererEvents.bind(bid));
- }
-
- function onOutstreamRendererLoaded() {
- // setup code for renderer, if any
+ // push to render queue because ANOutstreamVideo may not be loaded yet
+ bid.renderer.push(() => {
+ window.ANOutstreamVideo.renderAd({
+ tagId: bid.adResponse.tag_id,
+ sizes: [bid.getSize().split('x')],
+ targetId: bid.adUnitCode, // target div id to render video
+ uuid: bid.adResponse.uuid,
+ adResponse: bid.adResponse,
+ rendererOptions: bid.renderer.getConfig()
+ }, handleOutstreamRendererEvents.bind(bid));
+ });
}
function handleOutstreamRendererEvents(id, eventName) {
@@ -313,9 +312,8 @@ function AppnexusAstAdapter() {
id: ad.renderer_id,
url: ad.renderer_url,
config: { adText: `AppNexus Outstream Video Ad via Prebid.js` },
- callback: () => onOutstreamRendererLoaded.call(null, bid)
+ loaded: false,
});
-
try {
bid.renderer.setRender(outstreamRender);
} catch (err) {
diff --git a/src/adapters/carambola.js b/src/adapters/carambola.js
new file mode 100644
index 00000000000..18a7c154f6d
--- /dev/null
+++ b/src/adapters/carambola.js
@@ -0,0 +1,193 @@
+/**
+ * Carambola adapter
+ */
+
+var bidfactory = require('../bidfactory.js');
+var bidmanager = require('../bidmanager.js');
+const utils = require('../utils.js');
+const ajax = require('../ajax.js').ajax;
+
+const CarambolaAdapter = function CarambolaAdapter() {
+ const BIDDER_CODE = 'carambola';
+ const REQUEST_PATH = 'hb/inimage/getHbBIdProcessedResponse';
+
+ function _addErrorBidResponse(bid, response = {}, errorMsg = '') {
+ const bidResponse = bidfactory.createBid(2, bid);
+ bidResponse.bidderCode = BIDDER_CODE;
+ bidResponse.reason = errorMsg;
+ bidmanager.addBidResponse(_getCustomAdUnitCode(bid), bidResponse);
+ }
+ // looking at the utils.js at getBidderRequest method. this is what is requested.
+ function _getCustomAdUnitCode(bid) {
+ return bid.placementCode;
+ }
+
+ function _addBidResponse(bid, response) {
+ const bidResponse = bidfactory.createBid(1, bid);
+ bidResponse.bidderCode = BIDDER_CODE;
+ bidResponse.ad = response.ad;
+ bidResponse.cpm = response.cpm;
+ bidResponse.width = response.width;
+ bidResponse.height = response.height;
+ bidResponse.currencyCode = response.cur;
+ bidResponse.token = response.token;
+ bidResponse.pvid = response.pageViewId;
+
+ bidmanager.addBidResponse(_getCustomAdUnitCode(bid), bidResponse);
+ }
+
+ function _getPageViewId() {
+ window.Cbola = window.Cbola || {};
+ window.Cbola.HB = window.Cbola.HB || {};
+ window.Cbola.HB.pvid = window.Cbola.HB.pvid || _createPageViewId();
+ return window.Cbola.HB.pvid;
+ }
+
+ function _createPageViewId() {
+ function _pad(number) {
+ return number > 9 ? number : '0' + number
+ }
+
+ const MIN = 10000;
+ const MAX = 90000;
+ let now = new Date();
+
+ var pvid =
+ _pad(now.getDate()) +
+ _pad(now.getMonth() + 1) +
+ _pad(now.getFullYear() % 100) +
+ _pad(now.getHours()) +
+ _pad(now.getMinutes()) +
+ _pad(now.getSeconds()) +
+ _pad(now.getMilliseconds() % 100) +
+ Math.floor((Math.random() * MAX) + MIN);
+
+ return pvid;
+ }
+
+ // sends a request for each bid
+ function _buildRequest(bids, params) {
+ if (!utils.isArray(bids)) {
+ return;
+ }
+ // iterate on every bid and return the response to the hb manager
+ utils._each(bids, bid => {
+ let tempParams = params || {};
+ tempParams.cbolaMode = bid.params.cbolaMode || 0;
+ tempParams.wid = bid.params.wid || 0;
+ tempParams.pixel = bid.params.pixel || '';
+ tempParams.bidFloor = bid.params.bidFloor || 0;
+ tempParams.pageViewId = _getPageViewId();
+ tempParams.hb_token = utils.generateUUID();
+ tempParams.sizes = utils.parseSizesInput(bid.sizes) + '';
+ tempParams.bidsCount = bids.length;
+
+ for (let customParam in bid.params.customParams) {
+ if (bid.params.customParams.hasOwnProperty(customParam)) {
+ tempParams['c_' + customParam] = bid.params.customParams[customParam];
+ }
+ }
+
+ let server = bid.params.server || 'route.carambo.la';
+ let cbolaHbApiUrl = '//' + server + '/' + REQUEST_PATH;
+
+ // the responses of the bid requests
+ ajax(cbolaHbApiUrl + _jsonToQueryString(tempParams), response => {
+ // no response
+ if (!response || response.cpm <= 0) {
+ utils.logError('Empty bid response', BIDDER_CODE, bid);
+ _addErrorBidResponse(bid, response, 'Empty bid response');
+ return;
+ }
+ try {
+ response = JSON.parse(response);
+ if (response && response.cpm <= 0)
+ {
+ utils.logError('Bid response returned 0', BIDDER_CODE, bid);
+ _addErrorBidResponse(bid, response, 'Bid response returned 0');
+ return;
+ }
+ } catch (e) {
+ utils.logError('Invalid JSON in bid response', BIDDER_CODE, bid);
+ _addErrorBidResponse(bid, response, 'Invalid JSON in bid response');
+ return;
+ }
+ _addBidResponse(bid, response);
+ }, null, {method: 'GET'});
+ });
+ }
+
+ // build the genral request to the server
+ function _callBids(params) {
+ let isIfr,
+ bids = params.bids || [],
+ currentURL = (window.parent !== window) ? document.referrer : window.location.href;
+ currentURL = currentURL && encodeURIComponent(currentURL);
+ try {
+ isIfr = window.self !== window.top;
+ }
+ catch (e) {
+ isIfr = false;
+ }
+ if (bids.length === 0) {
+ return;
+ }
+
+ _buildRequest(bids, {
+ pageUrl: currentURL,
+ did: bids[0].params.did || 0,
+ pid: bids[0].params.pid || '',
+ res: _getScreenSize(screen),
+ ifr: isIfr,
+ viewPortDim: _getViewportDimensions(isIfr)
+ });
+ }
+
+ function _getScreenSize(screen) {
+ return screen ? `${screen.width}x${screen.height}x${screen.colorDepth}` : '0';
+ }
+
+ function _getViewportDimensions(isIfr) {
+ let width,
+ height,
+ tWin = window,
+ tDoc = document,
+ docEl = tDoc.documentElement,
+ body;
+
+ if (isIfr) {
+ try {
+ tWin = window.top;
+ tDoc = window.top.document;
+ }
+ catch (e) {
+ return;
+ }
+ docEl = tDoc.documentElement;
+ body = tDoc.body;
+ width = tWin.innerWidth || docEl.clientWidth || body.clientWidth;
+ height = tWin.innerHeight || docEl.clientHeight || body.clientHeight;
+ } else {
+ docEl = tDoc.documentElement;
+ width = tWin.innerWidth || docEl.clientWidth;
+ height = tWin.innerHeight || docEl.clientHeight;
+ }
+ return `${width}x${height}`;
+ }
+
+ function _jsonToQueryString(json) {
+ return '?' +
+ Object.keys(json).map(function(key) {
+ return encodeURIComponent(key) + '=' +
+ encodeURIComponent(json[key]);
+ }).join('&');
+ }
+
+ // Export the `callBids` function, so that Prebid.js can execute
+ // this function when the page asks to send out bid requests.
+ return {
+ callBids: _callBids
+ };
+};
+
+module.exports = CarambolaAdapter;
diff --git a/src/adapters/conversant.js b/src/adapters/conversant.js
index d8c15c1d7e7..974eb4c8100 100644
--- a/src/adapters/conversant.js
+++ b/src/adapters/conversant.js
@@ -1,21 +1,21 @@
'use strict';
-var VERSION = '2.0.1',
- CONSTANTS = require('../constants.json'),
- utils = require('../utils.js'),
- bidfactory = require('../bidfactory.js'),
- bidmanager = require('../bidmanager.js'),
- adloader = require('../adloader'),
- ajax = require('../ajax').ajax;
+var VERSION = '2.1.0';
+var CONSTANTS = require('../constants.json');
+var utils = require('../utils.js');
+var bidfactory = require('../bidfactory.js');
+var bidmanager = require('../bidmanager.js');
+var adloader = require('../adloader');
+var ajax = require('../ajax').ajax;
/**
* Adapter for requesting bids from Conversant
*/
var ConversantAdapter = function () {
- var w = window,
- n = navigator;
+ var w = window;
+ var n = navigator;
// production endpoint
- var conversantUrl = '//media.msg.dotomi.com/s2s/header?callback=$$PREBID_GLOBAL$$.conversantResponse';
+ var conversantUrl = '//media.msg.dotomi.com/s2s/header/24?callback=$$PREBID_GLOBAL$$.conversantResponse';
// SSAPI returns JSONP with window.pbjs.conversantResponse as the cb
var appendScript = function (code) {
@@ -55,21 +55,26 @@ var ConversantAdapter = function () {
var requestBids = function (bidReqs) {
// build bid request object
- var page = location.pathname + location.search + location.hash,
- siteId = '',
- conversantImps = [],
- conversantBidReqs,
- secure = 0;
+ var page = location.pathname + location.search + location.hash;
+ var siteId = '';
+ var conversantImps = [];
+ var conversantBidReqs;
+ var secure = 0;
// build impression array for conversant
utils._each(bidReqs, function (bid) {
- var bidfloor = utils.getBidIdParameter('bidfloor', bid.params),
- adW = 0,
- adH = 0,
- imp;
+ var bidfloor = utils.getBidIdParameter('bidfloor', bid.params);
+ var adW = 0;
+ var adH = 0;
+ var format;
+ var tagId;
+ var pos;
+ var imp;
secure = utils.getBidIdParameter('secure', bid.params) ? 1 : secure;
siteId = utils.getBidIdParameter('site_id', bid.params) + '';
+ tagId = utils.getBidIdParameter('tag_id', bid.params);
+ pos = utils.getBidIdParameter('position', bid.params);
// Allow sizes to be overridden per placement
var bidSizes = Array.isArray(bid.params.sizes) ? bid.params.sizes : bid.sizes;
@@ -78,22 +83,69 @@ var ConversantAdapter = function () {
adW = bidSizes[0];
adH = bidSizes[1];
} else {
- adW = bidSizes[0][0];
- adH = bidSizes[0][1];
+ format = [];
+ utils._each(bidSizes, function (bidSize) {
+ format.push({
+ w: bidSize[0],
+ h: bidSize[1]
+ });
+ });
}
imp = {
id: bid.bidId,
- banner: {
- w: adW,
- h: adH
- },
secure: secure,
bidfloor: bidfloor || 0,
displaymanager: 'Prebid.js',
displaymanagerver: VERSION
};
+ if (tagId !== '') {
+ imp.tagid = tagId;
+ }
+
+ if (bid.mediaType === 'video') {
+ var mimes = [];
+ var maxduration = 0;
+ var protocols = [];
+ var api = [];
+
+ var video = Array.isArray(format) ? {format: format} : {w: adW, h: adH};
+
+ mimes = utils.getBidIdParameter('mimes', bid.params);
+ if (mimes !== '') {
+ video.mimes = mimes;
+ }
+
+ maxduration = utils.getBidIdParameter('maxduration', bid.params);
+ if (maxduration !== '') {
+ video.maxduration = maxduration;
+ }
+
+ protocols = utils.getBidIdParameter('protocols', bid.params);
+ if (protocols !== '') {
+ video.protocols = protocols;
+ }
+
+ api = utils.getBidIdParameter('api', bid.params);
+ if (api !== '') {
+ video.api = api;
+ }
+
+ if (pos !== '') {
+ video.pos = pos;
+ }
+
+ imp.video = video;
+ } else {
+ var banner = Array.isArray(format) ? {format: format} : {w: adW, h: adH};
+
+ if (pos !== '') {
+ banner.pos = pos;
+ }
+ imp.banner = banner;
+ }
+
conversantImps.push(imp);
});
@@ -135,13 +187,13 @@ var ConversantAdapter = function () {
var parseSeatbid = function (bidResponse) {
var placementsWithBidsBack = [];
utils._each(bidResponse.bid, function (conversantBid) {
- var responseCPM,
- placementCode = '',
- id = conversantBid.impid,
- bid = {},
- responseAd,
- responseNurl,
- sizeArrayLength;
+ var responseCPM;
+ var placementCode = '';
+ var id = conversantBid.impid;
+ var bid = {};
+ var responseAd;
+ var responseNurl;
+ var sizeArrayLength;
// Bid request we sent Conversant
var bidRequested = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant').bids.find(bid => bid.bidId === id);
@@ -162,11 +214,14 @@ var ConversantAdapter = function () {
bid = bidfactory.createBid(1, bidRequested);
bid.creative_id = conversantBid.id || '';
bid.bidderCode = 'conversant';
-
bid.cpm = responseCPM;
- // Track impression image onto returned html
- bid.ad = responseAd + '';
+ if (bidRequested.mediaType === 'video') {
+ bid.vastUrl = responseAd;
+ } else {
+ // Track impression image onto returned html
+ bid.ad = responseAd + '';
+ }
sizeArrayLength = bidRequested.sizes.length;
if (sizeArrayLength === 2 && typeof bidRequested.sizes[0] === 'number' && typeof bidRequested.sizes[1] === 'number') {
diff --git a/src/adapters/cox.js b/src/adapters/cox.js
new file mode 100644
index 00000000000..e0e2a053251
--- /dev/null
+++ b/src/adapters/cox.js
@@ -0,0 +1,255 @@
+var bidfactory = require('../bidfactory.js');
+var bidmanager = require('../bidmanager.js');
+var adLoader = require('../adloader.js');
+
+var CoxAdapter = function CoxAdapter() {
+ var adZoneAttributeKeys = ['id', 'size', 'thirdPartyClickUrl'],
+ otherKeys = ['siteId', 'wrapper', 'referrerUrl'],
+ placementMap = {},
+ W = window;
+
+ var COX_BIDDER_CODE = 'cox';
+
+ function _callBids(params) {
+ var env = '';
+
+ // Create global cdsTag and CMT object (for the latter, only if needed )
+ W.cdsTag = {};
+ if (!W.CMT) W.CMT = _getCoxLite();
+
+ // Populate the tag with the info from prebid
+ var bids = params.bids || [],
+ tag = W.cdsTag,
+ i,
+ j;
+ for (i = 0; i < bids.length; i++) {
+ var bid = bids[i],
+ cfg = bid.params || {};
+
+ if (cfg.id) {
+ tag.zones = tag.zones || {};
+ var zone = {};
+
+ for (j = 0; j < adZoneAttributeKeys.length; j++) {
+ if (cfg[adZoneAttributeKeys[j]]) zone[adZoneAttributeKeys[j]] = cfg[adZoneAttributeKeys[j]];
+ }
+ for (j = 0; j < otherKeys.length; j++) {
+ if (cfg[otherKeys[j]]) tag[otherKeys[j]] = cfg[otherKeys[j]];
+ }
+ var adZoneKey = 'as' + cfg.id;
+ tag.zones[adZoneKey] = zone;
+
+ // Check for an environment setting
+ if (cfg.env) env = cfg.env;
+
+ // Update the placement map
+ var xy = (cfg.size || '0x0').split('x');
+ placementMap[adZoneKey] = {
+ p: bid.placementCode,
+ w: xy[0],
+ h: xy[1]
+ };
+ }
+ }
+ if (tag.zones && Object.keys(tag.zones).length > 0) {
+ tag.__callback__ = function (r) {
+ tag.response = r;
+ _notify();
+ };
+ adLoader.loadScript(W.CMT.Service.buildSrc(tag, env));
+ }
+ }
+
+ function _notify() {
+ // Will execute in the context of a bid
+ // function finalizeAd(price) {
+ // this.ad = W.CMT.Service.setAuctionPrice(this.ad, price);
+ // return this;
+ // }
+
+ for (var adZoneKey in placementMap) {
+ var bid = W.CMT.Service.getBidTrue(adZoneKey),
+ bidObj,
+ data = placementMap[adZoneKey];
+
+ if (bid > 0) {
+ bidObj = bidfactory.createBid(1);
+ bidObj.cpm = bid;
+ bidObj.ad = W.CMT.Service.getAd(adZoneKey);
+ bidObj.width = data.w;
+ bidObj.height = data.h;
+ // bidObj.floor = W.CMT.Service.getSecondPrice(adZoneKey);
+ // bidObj.finalizeAd = finalizeAd;
+ } else {
+ bidObj = bidfactory.createBid(2);
+ }
+ bidObj.bidderCode = COX_BIDDER_CODE;
+ bidmanager.addBidResponse(data.p, bidObj);
+ }
+ }
+
+ function _getCoxLite() {
+ var CMT = {};
+
+ CMT.Util = (function () {
+ return {
+
+ getRand: function getRand() {
+ return Math.round(Math.random() * 100000000);
+ },
+
+ encodeUriObject: function encodeUriObject(obj) {
+ return encodeURIComponent(JSON.stringify(obj));
+ },
+
+ extractUrlInfo: function extractUrlInfo() {
+ function f2(callback) {
+ try {
+ if (!W.location.ancestorOrigins) return;
+ for (var i = 0, len = W.location.ancestorOrigins.length; len > i; i++) {
+ callback.call(null, W.location.ancestorOrigins[i], i);
+ }
+ } catch (ignore) { }
+ return [];
+ }
+
+ function f1(callback) {
+ var oneWindow,
+ infoArray = [];
+ do {
+ try {
+ oneWindow = oneWindow ? oneWindow.parent : W;
+ callback.call(null, oneWindow, infoArray);
+ } catch (t) {
+ infoArray.push({
+ referrer: null,
+ location: null,
+ isTop: !1
+ });
+ return infoArray;
+ }
+ } while (oneWindow !== W.top);
+ return infoArray;
+ }
+ var allInfo = f1(function (oneWindow, infoArray) {
+ try {
+ infoArray.push({ referrer: oneWindow.document.referrer || null, location: oneWindow.location.href || null, isTop: oneWindow === W.top });
+ } catch (e) {
+ infoArray.push({ referrer: null, location: null, isTop: oneWindow === W.top });
+ }
+ });
+ f2(function (n, r) {
+ allInfo[r].ancestor = n;
+ });
+ for (var t = '', e = !1, i = allInfo.length - 1, l = allInfo.length - 1; l >= 0; l--) {
+ t = allInfo[l].location;
+ if (!t && l > 0) {
+ t = allInfo[l - 1].referrer;
+ if (!t) t = allInfo[l - 1].ancestor;
+ if (t) {
+ e = W.location.ancestorOrigins ? !0 : l === allInfo.length - 1 && allInfo[allInfo.length - 1].isTop;
+ break;
+ }
+ }
+ } return { url: t, isTop: e, depth: i };
+ },
+
+ srTestCapabilities: function srTestCapabilities() {
+ var plugins = navigator.plugins,
+ flashVer = -1,
+ sf = 'Shockwave Flash';
+
+ if (plugins && plugins.length > 0) {
+ if (plugins[sf + ' 2.0'] || plugins[sf]) {
+ var swVer2 = plugins[sf + ' 2.0'] ? ' 2.0' : '';
+ var flashDescription = plugins[sf + swVer2].description;
+ flashVer = flashDescription.split(' ')[2].split('.')[0];
+ }
+ }
+ if (flashVer > 4) return 15; else return 7;
+ }
+
+ };
+ }());
+
+ // Ad calling functionality
+ CMT.Service = (function () {
+ // Closure variables shared by the service functions
+ var U = CMT.Util;
+
+ return {
+
+ buildSrc: function buildSrc(tag, env) {
+ var src = (document.location.protocol === 'https:' ? 'https://' : 'http://') + (!env || env === 'PRD' ? '' : env === 'PPE' ? 'ppe-' : env === 'STG' ? 'staging-' : '') + 'ad.afy11.net/ad' + '?mode=11' + '&ct=' + U.srTestCapabilities() + '&nif=0' + '&sf=0' + '&sfd=0' + '&ynw=0' + '&rand=' + U.getRand() + '&hb=1' + '&rk1=' + U.getRand() + '&rk2=' + new Date().valueOf() / 1000;
+
+ // Make sure we don't have a response object...
+ delete tag.response;
+
+ // Extracted url info...
+ var urlInfo = U.extractUrlInfo();
+ tag.pageUrl = urlInfo.url;
+ tag.puTop = urlInfo.isTop;
+
+ // Attach the serialized tag to our string
+ src += '&ab=' + U.encodeUriObject(tag);
+
+ return src;
+ },
+
+ getAd: function (zoneKey) {
+ if (!zoneKey) return;
+
+ return this._getData(zoneKey, 'ad') + (this._getResponse().tpCookieSync || ''); // ...also append cookie sync if present
+ },
+
+ // getSecondPrice: function getSecondPrice(zoneKey) {
+ // if (zoneKey.substring(0, 2) !== 'as') zoneKey = 'as' + zoneKey;
+ // var bid = this.getBidTrue(zoneKey),
+ // floor = this._getData(zoneKey, 'floor');
+
+ // // If no floor, just set it to 80% of the bid
+ // if (!floor) floor = bid * 0.80;
+
+ // // Adjust the floor if it's too high...it needs to always be lower
+ // if (floor >= bid) {
+ // floor = floor * 0.80; // Take off 20% to account for possible non-adjusted 2nd highest bid
+
+ // // If it's still too high, just take 80% to 90% of the bid
+ // if (floor >= bid) floor = bid * ((Math.random() * 10) + 80) / 100;
+ // }
+ // return Math.round(floor * 100) / 100;
+ // },
+
+ // setAuctionPrice: function setAuctionPrice(ad, bid) {
+ // return ad ? ad.replace('${AUCTION_PRICE}', bid) : ad;
+ // },
+
+ getBidTrue: function getBidTrue(zoneKey) {
+ return Math.round(this._getData(zoneKey, 'price') * 100) / 100;
+ },
+
+ _getData: function (zoneKey, field) {
+ var response = this._getResponse(),
+ zoneResponseData = response.zones ? response.zones[zoneKey] : {};
+
+ return (zoneResponseData || {})[field] || null;
+ },
+
+ _getResponse: function () {
+ var tag = W.cdsTag;
+ return (tag && tag.response) ? tag.response : {};
+ },
+ };
+ }());
+
+ return CMT;
+ }
+
+ // Export the callBids function, so that prebid.js can execute this function
+ // when the page asks to send out bid requests.
+ return {
+ callBids: _callBids,
+ };
+};
+
+module.exports = CoxAdapter;
diff --git a/src/adapters/criteo.js b/src/adapters/criteo.js
index aeea56f0851..c0e14fb3d51 100644
--- a/src/adapters/criteo.js
+++ b/src/adapters/criteo.js
@@ -3,7 +3,8 @@ var bidmanager = require('../bidmanager.js');
var adloader = require('../adloader');
var CriteoAdapter = function CriteoAdapter() {
- var _publisherTagUrl = window.location.protocol + '//static.criteo.net/js/ld/publishertag.js';
+ var sProt = (window.location.protocol === 'http:') ? 'http:' : 'https:';
+ var _publisherTagUrl = sProt + '//static.criteo.net/js/ld/publishertag.js';
var _bidderCode = 'criteo';
var _profileId = 125;
diff --git a/src/adapters/eplanning.js b/src/adapters/eplanning.js
new file mode 100644
index 00000000000..fd4acc0e047
--- /dev/null
+++ b/src/adapters/eplanning.js
@@ -0,0 +1,280 @@
+var bidfactory = require('src/bidfactory.js');
+var bidmanager = require('src/bidmanager.js');
+
+var EPlanningAdapter = function EPlanningAdapter() {
+ (function() {
+ var win = window, doc = win.document, pbjs = win.pbjs, _global = {}, _default = { 'sv': 'ads.us.e-planning.net', 't': 0 }, rnd, FILE = 'file', CALLBACK_FUNCTION = 'hbpb.rH', NULL_SIZE = '1x1', _csRequested = [], PROTO = location.protocol === 'https:' ? 'https:' : 'http:', ISV = 'aklc.img.e-planning.net';
+ function Hbpb() {
+ var slots = (function() {
+ var _data = [];
+ function Slot(slotId) {
+ var data = _data[slotId];
+ function hasAds() {
+ return _data[slotId].ads.length;
+ }
+ function getSizes() {
+ return data.sizes;
+ }
+ function getSizesString() {
+ var s = [], i, sizes = getSizes();
+ if (sizes && sizes.length) {
+ if (typeof sizes[0] === 'object') {
+ for (i = 0; i < sizes.length; i++) {
+ s.push(sizes[i][0] + 'x' + sizes[i][1]);
+ }
+ } else {
+ s.push(sizes[0] + 'x' + sizes[1]);
+ }
+ } else {
+ return NULL_SIZE;
+ }
+ return s.join(',');
+ }
+ return {
+ getPlacementCode: function() {
+ return data.placementCode;
+ },
+ getString: function() {
+ return this.getPlacementCode() + ':' + getSizesString();
+ },
+ addAd: function(ad) {
+ _data[slotId].ads.push(ad);
+ },
+ getFormatedResponse: function() {
+ var ad, that = this;
+ if (hasAds()) {
+ ad = data.ads[0];
+ return {
+ 'placementCode': that.getPlacementCode(),
+ 'ad': {
+ 'ad': ad.adm,
+ 'cpm': ad.pr,
+ 'width': ad.size.w,
+ 'height': ad.size.h
+ }
+ };
+ } else {
+ return { 'placementCode': that.getPlacementCode() };
+ }
+ }
+ };
+ }
+ function findAll() {
+ var i = 0, r = [];
+ for (i = 0; i < _data.length; i++) {
+ r.push(new Slot(i));
+ }
+ return r;
+ }
+ return {
+ add: function(slot) {
+ slot.ads = [];
+ _data.push(slot);
+ },
+ get: function(slotId) {
+ return new Slot(slotId);
+ },
+ getString: function() {
+ var _slots = [], i, slot;
+ for (i = 0; i < _data.length; i++) {
+ slot = this.get(i);
+ _slots.push(slot.getString());
+ }
+ return _slots.join('+');
+ },
+ findByPlacementCode: function(placementCode) {
+ var i, _slots = findAll();
+ for (i = 0; i < _slots.length; i++) {
+ if (_slots[i].getPlacementCode() === placementCode) {
+ return _slots[i];
+ }
+ }
+ },
+ getFormatedResponse: function() {
+ var _slots = findAll(), i, r = [];
+ for (i = 0; i < _slots.length; i++) {
+ r.push(_slots[i].getFormatedResponse());
+ }
+ return {
+ 'bids': r
+ };
+ }
+ };
+ })();
+ function call(params) {
+ var i, bids = params.bids;
+ for (i = 0; i < bids.length; i++) {
+ slots.add({
+ _raw: bids[i],
+ placementCode: bids[i].placementCode,
+ sizes: bids[i].sizes
+ });
+ setGlobalParam('sv', bids[i]);
+ setGlobalParam('ci', bids[i]);
+ setGlobalParam('t', bids[i]);
+ }
+ doRequest();
+ }
+ function setGlobalParam(param, bid) {
+ if (!_global[param]) {
+ if (bid && bid.params && bid.params[param]) {
+ _global[param] = bid.params[param];
+ }
+ }
+ }
+ function getGlobalParam(param) {
+ return (_global[param] || _default[param]);
+ }
+ function getRandom() {
+ if (!rnd) {
+ rnd = Math.random();
+ }
+ return rnd;
+ }
+ function getDocURL() {
+ return escape(win.location.href || FILE);
+ }
+ function getReferrerURL() {
+ return doc.referrer;
+ }
+ function getCallbackFunction() {
+ return CALLBACK_FUNCTION;
+ }
+ function doRequest() {
+ var clienteId = getGlobalParam('ci'), url, dfpClienteId = '1', sec = 'ROS', params = [], t = getGlobalParam('t');
+ if (clienteId && !t) {
+ url = PROTO + '//' + getGlobalParam('sv') + '/hb/1/' + clienteId + '/' + dfpClienteId + '/' + (win.location.hostname || FILE) + '/' + sec + '?';
+ params.push('rnd=' + getRandom());
+ params.push('e=' + slots.getString());
+ if (getDocURL()) {
+ params.push('ur=' + getDocURL());
+ }
+ if (getReferrerURL()) {
+ params.push('fr=' + getReferrerURL());
+ }
+ params.push('cb=' + getCallbackFunction());
+ params.push('r=pbjs');
+ url += params.join('&');
+ load(url);
+ } else if (t) {
+ url = PROTO + '//' + ISV + '/layers/t_pbjs_' + t + '.js';
+ load(url);
+ }
+ }
+ function load(url) {
+ var script = doc.createElement('script');
+ script.src = url;
+ doc.body.appendChild(script);
+ }
+ function callback(response) {
+ if (pbjs && pbjs.processEPlanningResponse && typeof pbjs.processEPlanningResponse === 'function') {
+ pbjs.processEPlanningResponse(response);
+ }
+ }
+ function syncUsers(cs) {
+ var i, e, d;
+ for (i = 0; i < cs.length; i++) {
+ if (typeof cs[i] === 'string' && _csRequested.indexOf(cs[i]) === -1) {
+ (new Image()).src = cs[i];
+ _csRequested.push(cs[i]);
+ } else if (typeof cs[i] === 'object' && _csRequested.indexOf(cs[i].u) === -1) {
+ if (cs[i].j) {
+ e = doc.createElement('script');
+ e.src = cs[i].u;
+ } else if (cs[i].ifr) {
+ e = doc.createElement('iframe');
+ e.src = cs[i].u;
+ e.style.width = e.style.height = '1px';
+ e.display = 'none';
+ }
+ if (cs[i].data) {
+ for (d in cs[i].data) {
+ if (cs[i].data.hasOwnProperty(d)) {
+ e.setAttribute('data-' + d, cs[i].data[d]);
+ }
+ }
+ }
+ doc.body.appendChild(e);
+ _csRequested.push(cs[i].u);
+ }
+ }
+ }
+ function rH(response) {
+ var slot, i, o;
+ if (response && response.sp && response.sp.length) {
+ for (i = 0; i < response.sp.length; i++) {
+ if (response.sp[i].a) {
+ slot = slots.findByPlacementCode(response.sp[i].k);
+ if (slot) {
+ for (o = 0; o < response.sp[i].a.length; o++) {
+ slot.addAd({
+ 'adm': response.sp[i].a[o].adm,
+ 'pr': response.sp[i].a[o].pr,
+ 'size': {
+ 'w': response.sp[i].a[o].w,
+ 'h': response.sp[i].a[o].h
+ }
+ });
+ }
+ }
+ }
+ }
+ callback(slots.getFormatedResponse());
+ }
+ if (response && response.cs && response.cs.length) {
+ syncUsers(response.cs);
+ }
+ }
+ return {
+ call: function(params) {
+ return call(params);
+ },
+ rH: function(response) {
+ return rH(response);
+ }
+ };
+ }
+ win.hbpb = win.hbpb || new Hbpb();
+ })();
+
+ window.pbjs = window.pbjs || {};
+ window.pbjs.processEPlanningResponse = function(response) {
+ var bids, bidObject, i;
+ if (response) {
+ bids = response.bids;
+ for (i = 0; i < bids.length; i++) {
+ if (bids[i].ad) {
+ bidObject = getBidObject(bids[i]);
+ bidmanager.addBidResponse(bids[i].placementCode, bidObject);
+ } else {
+ bidObject = bidfactory.createBid(2);
+ bidObject.bidderCode = 'eplanning';
+ bidmanager.addBidResponse(bids[i].placementCode, bidObject);
+ }
+ }
+ }
+ };
+
+ function getBidObject(bid) {
+ var bidObject = bidfactory.createBid(1), i;
+ bidObject.bidderCode = 'eplanning';
+ for (i in bid.ad) {
+ if (bid.ad.hasOwnProperty(i)) {
+ bidObject[i] = bid.ad[i];
+ }
+ }
+ return bidObject;
+ }
+
+ function _callBids(params) {
+ if (window.hbpb) {
+ window.hbpb.call(params);
+ }
+ }
+
+ return {
+ callBids: _callBids
+ };
+};
+
+module.exports = EPlanningAdapter;
diff --git a/src/adapters/gumgum.js b/src/adapters/gumgum.js
index 1d589fa7041..4196287c520 100644
--- a/src/adapters/gumgum.js
+++ b/src/adapters/gumgum.js
@@ -15,6 +15,7 @@ const GumgumAdapter = function GumgumAdapter() {
const requestCache = {};
const throttleTable = {};
const defaultThrottle = 3e4;
+ const dtCredentials = { member: 'YcXr87z2lpbB' };
try {
topWindow = global.top;
@@ -27,6 +28,22 @@ const GumgumAdapter = function GumgumAdapter() {
return new Date().getTime();
}
+ function _getDigiTrustQueryParams() {
+ function getDigiTrustId () {
+ var digiTrustUser = (window.DigiTrust && window.DigiTrust.getUser) ? window.DigiTrust.getUser(dtCredentials) : {};
+ return digiTrustUser && digiTrustUser.success && digiTrustUser.identity || '';
+ };
+
+ let digiTrustId = getDigiTrustId();
+ // Verify there is an ID and this user has not opted out
+ if (!digiTrustId || digiTrustId.privacy && digiTrustId.privacy.optout) {
+ return {};
+ }
+ return {
+ 'dt': digiTrustId.id
+ };
+ }
+
function _callBids({ bids }) {
const browserParams = {
vw: topWindow.innerWidth,
@@ -37,6 +54,7 @@ const GumgumAdapter = function GumgumAdapter() {
ce: navigator.cookieEnabled,
dpr: topWindow.devicePixelRatio || 1
};
+
utils._each(bids, bidRequest => {
const { bidId
, params = {}
@@ -91,7 +109,7 @@ const GumgumAdapter = function GumgumAdapter() {
const callback = { jsonp: `$$PREBID_GLOBAL$$.handleGumGumCB['${bidId}']` };
CALLBACKS[bidId] = _handleGumGumResponse(cachedBid);
- const query = Object.assign(callback, browserParams, bid);
+ const query = Object.assign(callback, browserParams, bid, _getDigiTrustQueryParams());
const bidCall = `${bidEndpoint}?${utils.parseQueryStringParameters(query)}`;
adloader.loadScript(bidCall);
});
diff --git a/src/adapters/openx.js b/src/adapters/openx.js
index 009dde53707..6341b860134 100644
--- a/src/adapters/openx.js
+++ b/src/adapters/openx.js
@@ -218,7 +218,6 @@ const OpenxAdapter = function OpenxAdapter() {
ifr: isIfr,
tz: startTime.getTimezoneOffset(),
tws: getViewportDimensions(isIfr),
- ee: 'api_sync_write',
ef: 'bt%2Cdb',
be: 1,
bc: BIDDER_CONFIG
diff --git a/src/adapters/prebidServer.js b/src/adapters/prebidServer.js
index dc83a04019b..5dc78008060 100644
--- a/src/adapters/prebidServer.js
+++ b/src/adapters/prebidServer.js
@@ -108,34 +108,12 @@ function PrebidServer() {
if (result.status === 'OK') {
if (result.bidder_status) {
result.bidder_status.forEach(bidder => {
- if (bidder.no_bid || bidder.no_cookie) {
- // store a "No Bid" bid response
-
- if (!bidder.ad_unit) {
- utils.getBidderRequestAllAdUnits(bidder.bidder).bids.forEach(bid => {
- let bidObject = bidfactory.createBid(STATUS.NO_BID, bid);
- bidObject.adUnitCode = bid.placementCode;
- bidObject.bidderCode = bidder.bidder;
-
- bidmanager.addBidResponse(bid.placementCode, bidObject);
- });
- } else {
- let bidObject = bidfactory.createBid(STATUS.NO_BID, {
- bidId: bidder.bid_id
- });
-
- bidObject.adUnitCode = bidder.ad_unit;
- bidObject.bidderCode = bidder.bidder;
-
- bidmanager.addBidResponse(bidObject.adUnitCode, bidObject);
- }
- }
if (bidder.no_cookie) {
- // if no cookie is present then no bids were made, we don't store a bid response
queueSync({bidder: bidder.bidder, url: bidder.usersync.url, type: bidder.usersync.type});
}
});
}
+
if (result.bids) {
result.bids.forEach(bidObj => {
let bidRequest = utils.getBidRequest(bidObj.bid_id);
@@ -154,10 +132,30 @@ function PrebidServer() {
bidObject.ad = bidObj.adm;
bidObject.width = bidObj.width;
bidObject.height = bidObj.height;
+ if (bidObj.deal_id) {
+ bidObject.dealId = bidObj.deal_id;
+ }
bidmanager.addBidResponse(bidObj.code, bidObject);
});
}
+
+ const receivedBidIds = result.bids ? result.bids.map(bidObj => bidObj.bid_id) : [];
+
+ // issue a no-bid response for every bid request that can not be matched with received bids
+ config.bidders.forEach(bidder => {
+ utils
+ .getBidderRequestAllAdUnits(bidder)
+ .bids.filter(bidRequest => !receivedBidIds.includes(bidRequest.bidId))
+ .forEach(bidRequest => {
+ let bidObject = bidfactory.createBid(STATUS.NO_BID, bidRequest);
+
+ bidObject.adUnitCode = bidRequest.placementCode;
+ bidObject.bidderCode = bidRequest.bidder;
+
+ bidmanager.addBidResponse(bidObject.adUnitCode, bidObject);
+ });
+ });
}
else if (result.status === 'no_cookie') {
// cookie sync
diff --git a/src/adapters/pubmatic.js b/src/adapters/pubmatic.js
index 44fb9bf97fc..5caa286eed9 100644
--- a/src/adapters/pubmatic.js
+++ b/src/adapters/pubmatic.js
@@ -11,6 +11,9 @@ var bidmanager = require('../bidmanager.js');
var PubmaticAdapter = function PubmaticAdapter() {
var bids;
var _pm_pub_id;
+ var _pm_pub_age;
+ var _pm_pub_gender;
+ var _pm_pub_kvs;
var _pm_optimize_adslots = [];
let iframe;
@@ -21,6 +24,9 @@ var PubmaticAdapter = function PubmaticAdapter() {
var bid = bids[i];
// bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid;
_pm_pub_id = _pm_pub_id || bid.params.publisherId;
+ _pm_pub_age = _pm_pub_age || (bid.params.age || '');
+ _pm_pub_gender = _pm_pub_gender || (bid.params.gender || '');
+ _pm_pub_kvs = _pm_pub_kvs || (bid.params.kvs || '');
_pm_optimize_adslots.push(bid.params.adSlot);
}
@@ -51,11 +57,18 @@ var PubmaticAdapter = function PubmaticAdapter() {
content += '' +
'window.pm_pub_id = "%%PM_PUB_ID%%";' +
'window.pm_optimize_adslots = [%%PM_OPTIMIZE_ADSLOTS%%];' +
+ 'window.kaddctr = "%%PM_ADDCTR%%";' +
+ 'window.kadgender = "%%PM_GENDER%%";' +
+ 'window.kadage = "%%PM_AGE%%";' +
'window.pm_async_callback_fn = "window.parent.$$PREBID_GLOBAL$$.handlePubmaticCallback";';
+
content += '';
var map = {};
map.PM_PUB_ID = _pm_pub_id;
+ map.PM_ADDCTR = _pm_pub_kvs;
+ map.PM_GENDER = _pm_pub_gender;
+ map.PM_AGE = _pm_pub_age;
map.PM_OPTIMIZE_ADSLOTS = _pm_optimize_adslots.map(function (adSlot) {
return "'" + adSlot + "'";
}).join(',');
diff --git a/src/adapters/rubicon.js b/src/adapters/rubicon.js
index 0b30e7ce771..241e30c6fdf 100644
--- a/src/adapters/rubicon.js
+++ b/src/adapters/rubicon.js
@@ -28,6 +28,7 @@ var sizeMap = {
8: '120x600',
9: '160x600',
10: '300x600',
+ 14: '250x250',
15: '300x250',
16: '336x280',
19: '300x100',
@@ -45,6 +46,7 @@ var sizeMap = {
57: '970x250',
58: '1000x90',
59: '320x80',
+ 60: '320x150',
61: '1000x1000',
65: '640x480',
67: '320x480',
diff --git a/src/adapters/smartadserver.js b/src/adapters/smartadserver.js
index ae5ec38844c..21676beebc1 100644
--- a/src/adapters/smartadserver.js
+++ b/src/adapters/smartadserver.js
@@ -37,7 +37,7 @@ var SmartAdServer = function SmartAdServer() {
var adCall = url.parse(bid.params.domain);
adCall.pathname = '/prebid';
adCall.search = {
- 'pbjscbk': 'pbjs.' + generateCallback(bid),
+ 'pbjscbk': '$$PREBID_GLOBAL$$.' + generateCallback(bid),
'siteid': bid.params.siteId,
'pgid': bid.params.pageId,
'fmtid': bid.params.formatId,
diff --git a/src/adapters/trion.js b/src/adapters/trion.js
index e3c5c34c794..c0320f262c3 100644
--- a/src/adapters/trion.js
+++ b/src/adapters/trion.js
@@ -58,16 +58,17 @@ TrionAdapter = function TrionAdapter() {
var pubId = utils.getBidIdParameter('pubId', bid.params);
var sectionId = utils.getBidIdParameter('sectionId', bid.params);
var re = utils.getBidIdParameter('re', bid.params);
- var url = window.location.href;
+ var url = utils.getTopWindowUrl();
var sizes = utils.parseSizesInput(bid.sizes).join(',');
var trionUrl = BID_REQUEST_BASE_URL;
- trionUrl = utils.tryAppendQueryString(trionUrl, 'callback', 'pbjs.handleTrionCB');
+ trionUrl = utils.tryAppendQueryString(trionUrl, 'callback', '$$PREBID_GLOBAL$$.handleTrionCB');
trionUrl = utils.tryAppendQueryString(trionUrl, 'bidId', bidId);
trionUrl = utils.tryAppendQueryString(trionUrl, 'pubId', pubId);
trionUrl = utils.tryAppendQueryString(trionUrl, 'sectionId', sectionId);
trionUrl = utils.tryAppendQueryString(trionUrl, 're', re);
+ trionUrl = utils.tryAppendQueryString(trionUrl, 'slot', bid.placementCode);
if (url) {
trionUrl += 'url=' + url + '&';
}
diff --git a/src/adapters/unruly.js b/src/adapters/unruly.js
new file mode 100644
index 00000000000..c9fbfde1ecc
--- /dev/null
+++ b/src/adapters/unruly.js
@@ -0,0 +1,108 @@
+import { ajax } from 'src/ajax'
+import bidfactory from 'src/bidfactory'
+import bidmanager from 'src/bidmanager'
+import * as utils from 'src/utils'
+import { STATUS } from 'src/constants'
+import { Renderer } from 'src/Renderer'
+
+function createRenderHandler({ bidResponseBid, rendererConfig }) {
+ function createApi() {
+ parent.window.unruly.native.prebid = parent.window.unruly.native.prebid || {}
+ parent.window.unruly.native.prebid.uq = parent.window.unruly.native.prebid.uq || []
+
+ return {
+ render(bidResponseBid) {
+ parent.window.unruly.native.prebid.uq.push(['render', bidResponseBid])
+ },
+ onLoaded(bidResponseBid) {}
+ }
+ }
+
+ parent.window.unruly = parent.window.unruly || {}
+ parent.window.unruly.native = parent.window.unruly.native || {}
+ parent.window.unruly.native.siteId = parent.window.unruly.native.siteId || rendererConfig.siteId
+
+ const api = createApi()
+ return {
+ render() {
+ api.render(bidResponseBid)
+ },
+ onRendererLoad() {
+ api.onLoaded(bidResponseBid)
+ }
+ }
+}
+
+function createBidResponseHandler(bidRequestBids) {
+ return {
+ onBidResponse(responseBody) {
+ try {
+ const exchangeResponse = JSON.parse(responseBody)
+ exchangeResponse.bids.forEach((exchangeBid) => {
+ const bidResponseBid = bidfactory.createBid(exchangeBid.ext.statusCode, exchangeBid)
+
+ Object.assign(
+ bidResponseBid,
+ exchangeBid
+ )
+
+ if (exchangeBid.ext.renderer) {
+ const rendererParams = exchangeBid.ext.renderer
+ const renderHandler = createRenderHandler({
+ bidResponseBid,
+ rendererConfig: rendererParams.config
+ })
+
+ bidResponseBid.renderer = Renderer.install(
+ Object.assign(
+ {},
+ rendererParams,
+ { callback: () => renderHandler.onRendererLoad() }
+ )
+ )
+ bidResponseBid.renderer.setRender(() => renderHandler.render())
+ }
+
+ bidmanager.addBidResponse(exchangeBid.ext.placementCode, bidResponseBid)
+ })
+ } catch (error) {
+ utils.logError(error);
+ bidRequestBids.forEach(bidRequestBid => {
+ const bidResponseBid = bidfactory.createBid(STATUS.NO_BID)
+ bidmanager.addBidResponse(bidRequestBid.placementCode, bidResponseBid)
+ })
+ }
+ }
+ }
+}
+
+function createUnrulyAdapter() {
+ const adapter = {
+ exchangeUrl: 'https://targeting.unrulymedia.com/prebid',
+ callBids({ bids: bidRequestBids }) {
+ if (!bidRequestBids || bidRequestBids.length === 0) {
+ return
+ }
+
+ const payload = {
+ bidRequests: bidRequestBids
+ }
+
+ const bidResponseHandler = createBidResponseHandler(bidRequestBids)
+
+ ajax(
+ adapter.exchangeUrl,
+ bidResponseHandler.onBidResponse,
+ JSON.stringify(payload),
+ {
+ contentType: 'application/json',
+ withCredentials: true
+ }
+ )
+ }
+ }
+
+ return adapter
+}
+
+module.exports = createUnrulyAdapter
diff --git a/src/bidfactory.js b/src/bidfactory.js
index 9b4da791311..74aa5b72a46 100644
--- a/src/bidfactory.js
+++ b/src/bidfactory.js
@@ -18,7 +18,7 @@ function Bid(statusCode, bidRequest) {
var _bidId = bidRequest && bidRequest.bidId || utils.getUniqueIdentifierStr();
var _statusCode = statusCode || 0;
- this.bidderCode = '';
+ this.bidderCode = (bidRequest && bidRequest.bidder) || '';
this.width = 0;
this.height = 0;
this.statusMessage = _getStatus();
diff --git a/src/prebid.js b/src/prebid.js
index 9041027f654..c0adadb3713 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -344,6 +344,7 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) {
iframe.height = height;
iframe.width = width;
iframe.style.display = 'inline';
+ iframe.style.overflow = 'hidden';
iframe.src = url;
utils.insertElement(iframe, doc, 'body');
diff --git a/test/spec/adapters/adform_spec.js b/test/spec/adapters/adform_spec.js
index 80511be92ac..8888dbfe899 100644
--- a/test/spec/adapters/adform_spec.js
+++ b/test/spec/adapters/adform_spec.js
@@ -30,15 +30,16 @@ describe('Adform adapter', () => {
assert.equal(_query.callback.split('.')[1], '_adf_callback');
assert.equal(_query.tid, 145);
assert.equal(_query.rp, 4);
+ assert.equal(_query.fd, 1);
assert.equal(_query.url, encodeURIComponent('some// there'));
});
it('should correctly form bid items', () => {
const _items = parseUrl(adLoader.loadScript.args[0][0]).items;
- assert.deepEqual(_items[0], { mid: '1' });
- assert.deepEqual(_items[1], { mid: '2', someVar: 'someValue' });
- assert.deepEqual(_items[2], { mid: '3', pdom: 'home' });
+ assert.deepEqual(_items[0], { mid: '1', transactionId: 'transactionId' });
+ assert.deepEqual(_items[1], { mid: '2', someVar: 'someValue', transactionId: 'transactionId' });
+ assert.deepEqual(_items[2], { mid: '3', pdom: 'home', transactionId: 'transactionId' });
});
});
@@ -60,6 +61,7 @@ describe('Adform adapter', () => {
assert.equal(_bidObject.width, 90);
assert.equal(_bidObject.height, 90);
assert.equal(_bidObject.dealId, 'deal-1');
+ assert.equal(_bidObject.transactionId, 'transactionId');
});
it('should correctly form empty bid response object', () => {
@@ -111,6 +113,7 @@ describe('Adform adapter', () => {
});
beforeEach(() => {
+ var transactionId = 'transactionId';
_adapter = adapter();
utils.getUniqueIdentifierStr = () => 'callback';
sandbox = sinon.sandbox.create();
@@ -126,7 +129,8 @@ describe('Adform adapter', () => {
url: 'some// there'
},
adxDomain: 'newdomain',
- tid: 45
+ tid: 45,
+ transactionId: transactionId
},
{
bidId: '123',
@@ -136,7 +140,8 @@ describe('Adform adapter', () => {
mid: 2,
tid: 145,
someVar: 'someValue'
- }
+ },
+ transactionId: transactionId
},
{
bidId: 'a1b',
@@ -145,7 +150,8 @@ describe('Adform adapter', () => {
params: {
mid: 3,
pdom: 'home'
- }
+ },
+ transactionId: transactionId
}
]});
});
diff --git a/test/spec/adapters/admixer_spec.js b/test/spec/adapters/admixer_spec.js
index 79174390b62..45f18ce7abc 100644
--- a/test/spec/adapters/admixer_spec.js
+++ b/test/spec/adapters/admixer_spec.js
@@ -39,6 +39,54 @@ describe('Admixer adapter', function () {
}
]
};
+ var validVideoData_1 = {
+ bids: [
+ {
+ mediaType: 'video',
+ bidder: 'admixer',
+ bidId: 'bid_id',
+ params: {zone: 'zone_id'},
+ placementCode: 'ad-unit-1',
+ sizes: [[300, 250], [300, 600]]
+ }
+ ]
+ };
+ var validVideoData_2 = {
+ bids: [
+ {
+ mediaType: 'video',
+ bidder: 'admixer',
+ bidId: 'bid_id',
+ params: {zone: 'zone_id'},
+ placementCode: 'ad-unit-1',
+ sizes: [300, 250]
+ }
+ ]
+ };
+ var validVideoData_3 = {
+ bids: [
+ {
+ mediaType: 'video',
+ bidder: 'admixer',
+ bidId: 'bid_id',
+ params: {zone: 'zone_id', video: {skippable: true}},
+ placementCode: 'ad-unit-1',
+ sizes: [300, 250]
+ }
+ ]
+ };
+ var invalidVideoData = {
+ bids: [
+ {
+ mediaType: 'video',
+ bidder: 'admixer',
+ bidId: 'bid_id',
+ params: {},
+ placementCode: 'ad-unit-1',
+ sizes: [[300, 250], [300, 600]]
+ }
+ ]
+ };
var responseWithAd = JSON.stringify({
'result': {
'cpm': 2.2,
@@ -57,13 +105,38 @@ describe('Admixer adapter', function () {
},
'callback_uid': 'ad-unit-1'
});
+ var responseWithVideoAd = JSON.stringify({
+ 'result': {
+ 'cpm': 2.2,
+ 'vastUrl': 'http://inv-nets.admixer.net/vastxml.aspx?req=9d651544-daf4-48ed-ae0c-38a60a4e1920&vk=e914f026449e49aeb6eea07b9642a2ce',
+ 'width': 300,
+ 'height': 250
+ },
+ 'callback_uid': 'ad-unit-1'
+ });
+ var responseWithoutVideoAd = JSON.stringify({
+ 'result': {
+ 'cpm': 0,
+ 'vastUrl': '',
+ 'width': 0,
+ 'height': 0
+ },
+ 'callback_uid': 'ad-unit-1'
+ });
var responseEmpty = '';
var invUrl = '//inv-nets.admixer.net/prebid.aspx';
+ var invVastUrl = '//inv-nets.admixer.net/videoprebid.aspx';
var validJsonParams = {
zone: 'zone_id',
callback_uid: 'ad-unit-1',
sizes: '300x250-300x600'
};
+ var validJsonVideoParams = {
+ zone: 'zone_id',
+ callback_uid: 'ad-unit-1',
+ sizes: '300x250-300x600',
+ skippable: true
+ };
describe('bid request with valid data', function () {
var stubAjax;
beforeEach(function () {
@@ -73,19 +146,32 @@ describe('Admixer adapter', function () {
afterEach(function () {
stubAjax.restore();
});
- it('bid request should be called. sizes style -> [[],[]]', function () {
+ it('display: bid request should be called. sizes style -> [[],[]]', function () {
Adapter.callBids(validData_1);
sinon.assert.calledOnce(stubAjax);
});
- it('bid request should be called. sizes style -> []', function () {
+ it('video: bid request should be called. sizes style -> [[],[]]', function () {
+ Adapter.callBids(validVideoData_1);
+ sinon.assert.calledOnce(stubAjax);
+ });
+ it('display: bid request should be called. sizes style -> []', function () {
Adapter.callBids(validData_2);
sinon.assert.calledOnce(stubAjax);
});
- it('ajax params should be matched', function () {
+ it('video: bid request should be called. sizes style -> []', function () {
+ Adapter.callBids(validVideoData_2);
+ sinon.assert.calledOnce(stubAjax);
+ });
+ it('display: ajax params should be matched', function () {
Adapter.callBids(validData_1);
sinon.assert.calledWith(stubAjax, sinon.match(invUrl, function () {
}, validJsonParams, {method: 'GET'}));
});
+ it('video: ajax params should be matched', function () {
+ Adapter.callBids(validVideoData_3);
+ sinon.assert.calledWith(stubAjax, sinon.match(invVastUrl, function () {
+ }, validJsonVideoParams, {method: 'GET'}));
+ });
});
describe('bid request with invalid data', function () {
var addBidResponse, stubAjax;
@@ -98,15 +184,24 @@ describe('Admixer adapter', function () {
addBidResponse.restore();
stubAjax.restore();
});
- it('ajax shouldn\'t be called', function () {
+ it('display: ajax shouldn\'t be called', function () {
Adapter.callBids(invalidData);
sinon.assert.notCalled(stubAjax);
});
- it('bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID + '"', function () {
+ it('video: ajax shouldn\'t be called', function () {
+ Adapter.callBids(invalidVideoData);
+ sinon.assert.notCalled(stubAjax);
+ });
+ it('display: bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID + '"', function () {
Adapter.callBids(invalidData);
expect(addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID);
expect(addBidResponse.firstCall.args[1].bidderCode).to.equal('admixer');
});
+ it('video: bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID + '"', function () {
+ Adapter.callBids(invalidVideoData);
+ expect(addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID);
+ expect(addBidResponse.firstCall.args[1].bidderCode).to.equal('admixer');
+ });
});
describe('bid response', function () {
var addBidResponse;
@@ -116,23 +211,35 @@ describe('Admixer adapter', function () {
afterEach(function () {
addBidResponse.restore();
});
- it('response with ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.GOOD + '"', function () {
+ it('display: response with ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.GOOD + '"', function () {
Adapter.responseCallback(responseWithAd);
var arg = addBidResponse.firstCall.args[1];
expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD);
expect(arg.bidderCode).to.equal('admixer');
});
- it('response without ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () {
+ it('video: response with ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.GOOD + '"', function () {
+ Adapter.responseCallback(responseWithVideoAd);
+ var arg = addBidResponse.firstCall.args[1];
+ expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD);
+ expect(arg.bidderCode).to.equal('admixer');
+ });
+ it('display: response without ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () {
Adapter.responseCallback(responseWithoutAd);
var arg = addBidResponse.firstCall.args[1];
expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID);
expect(arg.bidderCode).to.equal('admixer');
});
- it('response empty. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () {
+ it('video: response without ad. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () {
+ Adapter.responseCallback(responseWithoutVideoAd);
+ var arg = addBidResponse.firstCall.args[1];
+ expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID);
+ expect(arg.bidderCode).to.equal('admixer');
+ });
+ it('display/video: response empty. bidmanager.addBidResponse status code must to be equal "' + CONSTANTS.STATUS.NO_BID, function () {
Adapter.responseCallback(responseEmpty);
var arg = addBidResponse.firstCall.args[1];
expect(arg.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID);
expect(arg.bidderCode).to.equal('admixer');
- })
+ });
});
});
diff --git a/test/spec/adapters/adyoulike_spec.js b/test/spec/adapters/adyoulike_spec.js
new file mode 100644
index 00000000000..d702b0ec283
--- /dev/null
+++ b/test/spec/adapters/adyoulike_spec.js
@@ -0,0 +1,308 @@
+import { expect } from 'chai';
+import { parse } from '../../../src/url';
+import AdyoulikAdapter from '../../../src/adapters/adyoulike';
+import bidmanager from 'src/bidmanager';
+import { STATUS } from 'src/constants';
+
+describe('Adyoulike Adapter', () => {
+ const endpoint = 'http://hb-api.omnitagjs.com/hb-api/prebid';
+ const canonicalUrl = 'http://canonical.url/?t=%26';
+ const bidderCode = 'adyoulike';
+ const bidRequestWithEmptyPlacement = {
+ 'bidderCode': 'adyoulike',
+ 'bids': [
+ {
+ 'bidId': 'bid_id_0',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-0',
+ 'params': {},
+ 'sizes': '300x250'
+ }
+ ],
+ };
+ const bidRequestWithEmptySizes = {
+ 'bidderCode': 'adyoulike',
+ 'bids': [
+ {
+ 'bidId': 'bid_id_0',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-0',
+ 'params': {
+ 'placement': 'placement_0'
+ }
+ }
+ ],
+ };
+ const bidRequestWithSinglePlacement = {
+ 'bidderCode': 'adyoulike',
+ 'bids': [
+ {
+ 'bidId': 'bid_id_0',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-0',
+ 'params': {
+ 'placement': 'placement_0'
+ },
+ 'sizes': '300x250'
+ }
+ ],
+ };
+ const bidRequestMultiPlacements = {
+ 'bidderCode': 'adyoulike',
+ 'bids': [
+ {
+ 'bidId': 'bid_id_0',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-0',
+ 'params': {
+ 'placement': 'placement_0'
+ },
+ 'sizes': '300x250'
+ },
+ {
+ 'bidId': 'bid_id_1',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-1',
+ 'params': {
+ 'placement': 'placement_1'
+ },
+ 'sizes': [[300, 600]]
+ },
+ {
+ 'bidId': 'bid_id_2',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-2',
+ 'params': {},
+ 'sizes': '300x400'
+ },
+ {
+ 'bidId': 'bid_id_3',
+ 'bidder': 'adyoulike',
+ 'placementCode': 'adunit/hb-3',
+ 'params': {
+ 'placement': 'placement_3'
+ }
+ }
+ ],
+ };
+
+ const responseWithEmptyPlacement = [
+ {
+ 'Placement': 'placement_0'
+ }
+ ];
+ const responseWithSinglePlacement = [
+ {
+ 'Placement': 'placement_0',
+ 'Banner': 'placement_0',
+ 'Price': 0.5
+ }
+ ];
+ const responseWithMultiplePlacements = [
+ {
+ 'Placement': 'placement_0',
+ 'Banner': 'placement_0',
+ 'Price': 0.5
+ },
+ {
+ 'Placement': 'placement_1',
+ 'Banner': 'placement_1',
+ 'Price': 0.6
+ }
+ ];
+
+ let adapter;
+
+ beforeEach(() => {
+ adapter = new AdyoulikAdapter();
+ });
+
+ describe('adapter public API', () => {
+ const adapter = AdyoulikAdapter.createNew();
+ it('createNew', () => {
+ expect(adapter.createNew).to.be.a('function');
+ });
+
+ it('setBidderCode', () => {
+ expect(adapter.setBidderCode).to.be.a('function');
+ });
+ it('callBids', () => {
+ expect(adapter.setBidderCode).to.be.a('function');
+ });
+ });
+
+ describe('request function', () => {
+ let requests;
+ let xhr;
+ let addBidResponse;
+ let canonicalQuery;
+
+ beforeEach(() => {
+ requests = [];
+
+ xhr = sinon.useFakeXMLHttpRequest();
+ xhr.onCreate = request => requests.push(request);
+
+ addBidResponse = sinon.stub(bidmanager, 'addBidResponse');
+
+ let canonical = document.createElement('link');
+ canonical.rel = 'canonical';
+ canonical.href = canonicalUrl;
+ canonicalQuery = sinon.stub(window.top.document.head, 'querySelector');
+ canonicalQuery.withArgs('link[rel="canonical"][href]').returns(canonical);
+ });
+
+ afterEach(() => {
+ xhr.restore();
+ bidmanager.addBidResponse.restore();
+ canonicalQuery.restore();
+ });
+
+ it('requires placement request', () => {
+ adapter.callBids(bidRequestWithEmptyPlacement);
+ expect(requests).to.be.empty;
+ expect(addBidResponse.calledOnce).to.equal(false);
+ });
+
+ it('requires sizes in request', () => {
+ adapter.callBids(bidRequestWithEmptySizes);
+ expect(requests).to.be.empty;
+ expect(addBidResponse.calledOnce).to.equal(false);
+ });
+
+ it('sends bid request to endpoint with single placement', () => {
+ adapter.callBids(bidRequestWithSinglePlacement);
+ expect(requests[0].url).to.contain(endpoint);
+ expect(requests[0].method).to.equal('POST');
+
+ expect(requests[0].url).to.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl));
+
+ let body = JSON.parse(requests[0].requestBody);
+ expect(body.Version).to.equal('0.1');
+ expect(body.Placements).deep.equal(['placement_0']);
+ expect(body.PageRefreshed).to.equal(false);
+ });
+
+ it('sends bid request to endpoint with single placement without canonical', () => {
+ canonicalQuery.restore();
+
+ adapter.callBids(bidRequestWithSinglePlacement);
+ expect(requests[0].url).to.contain(endpoint);
+ expect(requests[0].method).to.equal('POST');
+
+ expect(requests[0].url).to.not.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl));
+
+ let body = JSON.parse(requests[0].requestBody);
+ expect(body.Version).to.equal('0.1');
+ expect(body.Placements).deep.equal(['placement_0']);
+ expect(body.PageRefreshed).to.equal(false);
+ });
+
+ it('sends bid request to endpoint with multiple placements', () => {
+ adapter.callBids(bidRequestMultiPlacements);
+ expect(requests[0].url).to.contain(endpoint);
+ expect(requests[0].method).to.equal('POST');
+
+ expect(requests[0].url).to.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl));
+
+ let body = JSON.parse(requests[0].requestBody);
+ expect(body.Version).to.equal('0.1');
+ expect(body.Placements).deep.equal(['placement_0', 'placement_1']);
+ expect(body.PageRefreshed).to.equal(false);
+ });
+ });
+
+ describe('response function', () => {
+ let server;
+ let addBidResponse;
+
+ beforeEach(() => {
+ server = sinon.fakeServer.create();
+ addBidResponse = sinon.stub(bidmanager, 'addBidResponse');
+ });
+
+ afterEach(() => {
+ server.restore();
+ bidmanager.addBidResponse.restore();
+ });
+
+ it('invalid json', () => {
+ server.respondWith('{');
+ adapter.callBids(bidRequestWithSinglePlacement);
+ server.respond();
+
+ expect(addBidResponse.calledOnce).to.equal(true);
+ expect(addBidResponse.args[0]).to.have.lengthOf(2);
+ expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.NO_BID);
+ expect(addBidResponse.args[0][1].bidderCode).to.equal(bidderCode);
+ });
+
+ it('receive reponse with empty placement', () => {
+ server.respondWith(JSON.stringify(responseWithEmptyPlacement));
+ adapter.callBids(bidRequestWithSinglePlacement);
+ server.respond();
+
+ expect(addBidResponse.calledOnce).to.equal(true);
+ expect(addBidResponse.args[0]).to.have.lengthOf(2);
+ expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.NO_BID);
+ expect(addBidResponse.args[0][1].bidderCode).to.equal(bidderCode);
+ });
+
+ it('receive reponse with single placement', () => {
+ server.respondWith(JSON.stringify(responseWithSinglePlacement));
+ adapter.callBids(bidRequestWithSinglePlacement);
+ server.respond();
+
+ expect(addBidResponse.calledOnce).to.equal(true);
+ expect(addBidResponse.args[0]).to.have.lengthOf(2);
+ expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.GOOD);
+ expect(addBidResponse.args[0][1].cpm).to.equal(0.5);
+ expect(addBidResponse.args[0][1].ad).to.equal('placement_0');
+ expect(addBidResponse.args[0][1].width).to.equal(300);
+ expect(addBidResponse.args[0][1].height).to.equal(250);
+ });
+
+ it('receive reponse with multiple placement', () => {
+ server.respondWith(JSON.stringify(responseWithMultiplePlacements));
+ adapter.callBids(bidRequestMultiPlacements);
+ server.respond();
+
+ expect(addBidResponse.calledTwice).to.equal(true);
+
+ expect(addBidResponse.args[0]).to.have.lengthOf(2);
+ expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.GOOD);
+ expect(addBidResponse.args[0][1].bidderCode).to.equal(bidderCode);
+ expect(addBidResponse.args[0][1].cpm).to.equal(0.5);
+ expect(addBidResponse.args[0][1].ad).to.equal('placement_0');
+ expect(addBidResponse.args[0][1].width).to.equal(300);
+ expect(addBidResponse.args[0][1].height).to.equal(250);
+
+ expect(addBidResponse.args[1]).to.have.lengthOf(2);
+ expect(addBidResponse.args[1][1].getStatusCode()).to.equal(STATUS.GOOD);
+ expect(addBidResponse.args[1][1].bidderCode).to.equal(bidderCode);
+ expect(addBidResponse.args[1][1].cpm).to.equal(0.6);
+ expect(addBidResponse.args[1][1].ad).to.equal('placement_1');
+ expect(addBidResponse.args[1][1].width).to.equal(300);
+ expect(addBidResponse.args[1][1].height).to.equal(600);
+ });
+
+ it('receive reponse with invalid placement number', () => {
+ server.respondWith(JSON.stringify(responseWithSinglePlacement));
+ adapter.callBids(bidRequestMultiPlacements);
+ server.respond();
+
+ expect(addBidResponse.calledTwice).to.equal(true);
+
+ expect(addBidResponse.args[0]).to.have.lengthOf(2);
+ expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.GOOD);
+ expect(addBidResponse.args[0][1].bidderCode).to.equal(bidderCode);
+ expect(addBidResponse.args[0][1].cpm).to.equal(0.5);
+ expect(addBidResponse.args[0][1].ad).to.equal('placement_0');
+ expect(addBidResponse.args[0][1].width).to.equal(300);
+ expect(addBidResponse.args[0][1].height).to.equal(250);
+
+ expect(addBidResponse.args[1]).to.have.lengthOf(2);
+ expect(addBidResponse.args[1][1].getStatusCode()).to.equal(STATUS.NO_BID);
+ });
+ });
+});
diff --git a/test/spec/adapters/aol_spec.js b/test/spec/adapters/aol_spec.js
index 3a13089295a..6f2a729e7e7 100644
--- a/test/spec/adapters/aol_spec.js
+++ b/test/spec/adapters/aol_spec.js
@@ -76,7 +76,7 @@ describe('AolAdapter', () => {
beforeEach(() => adapter = new AolAdapter());
function createBidderRequest({bids, params} = {}) {
- var bidderRequest = utils.cloneJson(getDefaultBidRequest());
+ var bidderRequest = getDefaultBidRequest();
if (bids && Array.isArray(bids)) {
bidderRequest.bids = bids;
}
@@ -568,8 +568,8 @@ describe('AolAdapter', () => {
adapter.callBids(getDefaultBidRequest());
server.respond();
expect(bidmanager.addBidResponse.calledOnce).to.be.true;
- var addedBidResponse = bidmanager.addBidResponse.firstCall.args[1];
- expect(addedBidResponse.ad).to.equal("");
+ let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ expect(addedBidResponse.ad).to.equal('');
expect(addedBidResponse.cpm).to.equal(0.09);
expect(addedBidResponse.width).to.equal(728);
expect(addedBidResponse.height).to.equal(90);
@@ -580,19 +580,19 @@ describe('AolAdapter', () => {
it('should be added to bidmanager including pixels from pubapi response', () => {
let bidResponse = getDefaultBidResponse();
bidResponse.ext = {
- pixels: ""
+ pixels: ''
};
server.respondWith(JSON.stringify(bidResponse));
adapter.callBids(getDefaultBidRequest());
server.respond();
expect(bidmanager.addBidResponse.calledOnce).to.be.true;
- var addedBidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1];
expect(addedBidResponse.ad).to.equal(
'' +
''
+ 'document.write(\'\');}'
);
});
@@ -604,7 +604,7 @@ describe('AolAdapter', () => {
adapter.callBids(getDefaultBidRequest());
server.respond();
expect(bidmanager.addBidResponse.calledOnce).to.be.true;
- var addedBidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1];
expect(addedBidResponse.dealId).to.equal('12345');
});
@@ -623,7 +623,7 @@ describe('AolAdapter', () => {
it('should not render pixels on pubapi response when no parameter is set', () => {
let bidResponse = getDefaultBidResponse();
bidResponse.ext = {
- pixels: ""
+ pixels: ''
};
server.respondWith(JSON.stringify(bidResponse));
adapter.callBids(getDefaultBidRequest());
@@ -635,8 +635,8 @@ describe('AolAdapter', () => {
it('should render pixels from pubapi response when param userSyncOn is set with \'bidResponse\'', () => {
let bidResponse = getDefaultBidResponse();
bidResponse.ext = {
- pixels: ""
+ pixels: ''
};
server.respondWith(JSON.stringify(bidResponse));
@@ -664,8 +664,8 @@ describe('AolAdapter', () => {
$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true;
let bidResponse = getDefaultBidResponse();
bidResponse.ext = {
- pixels: ""
+ pixels: ''
};
server.respondWith(JSON.stringify(bidResponse));
diff --git a/test/spec/adapters/carambola_spec.js b/test/spec/adapters/carambola_spec.js
new file mode 100644
index 00000000000..fe856f66c21
--- /dev/null
+++ b/test/spec/adapters/carambola_spec.js
@@ -0,0 +1,139 @@
+import {expect} from 'chai';
+import * as utils from 'src/utils';
+import CarambolaAdapter from 'src/adapters/carambola';
+import bidmanager from 'src/bidmanager';
+
+const DEFAULT_BIDDER_REQUEST = {
+ bidderCode: 'carambola',
+ requestId: 'c9ad932a-41d9-4821-b6dc-0c8146029faf',
+ adId: '2e3daacdeed03d',
+ start: new Date().getTime(),
+ bids: [{
+ bidder: 'carambola',
+ adId: '2e3daacdeed03d',
+ requestId: 'c9ad932a-41d9-4821-b6dc-0c8146029faf',
+ adUnitCode: 'cbola_prebid_code_97',
+ token: 'CGYCLyIy',
+ pageViewId: '22478638',
+ params: {
+ pid: 'hbtest',
+ did: 112591,
+ wid: 0
+ }
+ }]
+};
+
+const DEFAULT_HB_RESPONSE = {
+ cpm: 0.1693953107111156,
+ ad: ' ',
+ token: '9cd6bf9c-433d-4663-b67f-da727f4cebff',
+ width: '300',
+ height: '250',
+ currencyCode: 'USD',
+ pageViewId: '22478638',
+ requestStatus: 1
+
+};
+
+describe('carambolaAdapter', function () {
+ let adapter;
+
+ beforeEach(() => adapter = new CarambolaAdapter());
+
+ function createBidderRequest({bids, params} = {}) {
+ var bidderRequest = utils.cloneJson(DEFAULT_BIDDER_REQUEST);
+ if (bids && Array.isArray(bids)) {
+ bidderRequest.bids = bids;
+ }
+ if (params) {
+ bidderRequest.bids.forEach(bid => bid.params = params);
+ }
+ return bidderRequest;
+ }
+
+ describe('callBids()', () => {
+ it('exists and is a function', () => {
+ expect(adapter.callBids).to.exist.and.to.be.a('function');
+ });
+
+ // bid request starts
+ describe('bid request', () => {
+ let xhr;
+ let requests;
+
+ beforeEach(() => {
+ xhr = sinon.useFakeXMLHttpRequest();
+ requests = [];
+ xhr.onCreate = request => requests.push(request);
+ });
+
+ afterEach(() => xhr.restore());
+
+ it('requires parameters to be made', () => {
+ adapter.callBids({});
+ expect(requests[0]).to.be.empty;
+ });
+
+ it('should hit the default route.carambo.la endpoint', () => {
+ adapter.callBids(DEFAULT_BIDDER_REQUEST);
+ expect(requests[0].url).to.contain('route.carambo.la');
+ });
+
+ it('should verifiy that a page_view_id is sent', () => {
+ adapter.callBids(DEFAULT_BIDDER_REQUEST);
+ expect(requests[0].url).to.contain('pageViewId=');
+ });
+
+ it('should should send the correct did', () => {
+ adapter.callBids(createBidderRequest({
+ params: {
+ did: 112591,
+ wid: 0
+ }
+ }));
+ expect(requests[0].url).to.contain('did=112591');
+ });
+ });
+ // bid request ends
+
+ // bid response starts
+ describe('bid response', () => {
+ let server;
+
+ beforeEach(() => {
+ server = sinon.fakeServer.create();
+ sinon.stub(bidmanager, 'addBidResponse');
+ });
+
+ afterEach(() => {
+ server.restore();
+ bidmanager.addBidResponse.restore();
+ });
+
+ it('should be added to bidmanager if response is valid', () => {
+ server.respondWith(JSON.stringify(DEFAULT_HB_RESPONSE));
+ adapter.callBids(DEFAULT_BIDDER_REQUEST);
+ server.respond();
+ expect(bidmanager.addBidResponse.calledOnce).to.be.true;
+ });
+
+ it('should be added to bidmanager with correct bidderCode', () => {
+ server.respondWith(JSON.stringify(DEFAULT_HB_RESPONSE));
+ adapter.callBids(DEFAULT_BIDDER_REQUEST);
+ server.respond();
+ expect(bidmanager.addBidResponse.calledOnce).to.be.true;
+ expect(bidmanager.addBidResponse.firstCall.args[1]).to.have.property('bidderCode', 'carambola');
+ });
+
+ it('should have pageViewId matching the pageViewId from related bid request', () => {
+ server.respondWith(JSON.stringify(DEFAULT_HB_RESPONSE));
+ adapter.callBids(DEFAULT_BIDDER_REQUEST);
+ server.respond();
+ expect(bidmanager.addBidResponse.calledOnce).to.be.true;
+ expect(bidmanager.addBidResponse.firstCall.args[1])
+ .to.have.property('pvid', DEFAULT_BIDDER_REQUEST.bids[0].pageViewId);
+ });
+ });
+ // bid response ends
+ });
+});
diff --git a/test/spec/adapters/conversant_spec.js b/test/spec/adapters/conversant_spec.js
index 3c9d636a700..09d5132683f 100644
--- a/test/spec/adapters/conversant_spec.js
+++ b/test/spec/adapters/conversant_spec.js
@@ -16,10 +16,11 @@ describe('Conversant adapter tests', function () {
sizes: [[300, 600]],
params: {
site_id: '87293',
+ position: 1,
+ tag_id: 'tagid-1',
secure: false
}
- },
- {
+ }, {
bidId: 'bidId2',
bidder: 'conversant',
placementCode: 'div2',
@@ -28,14 +29,27 @@ describe('Conversant adapter tests', function () {
site_id: '87293',
secure: false
}
- },
- {
+ }, {
bidId: 'bidId3',
bidder: 'conversant',
placementCode: 'div3',
sizes: [[300, 600], [160, 600]],
params: {
site_id: '87293',
+ position: 1,
+ tag_id: '',
+ secure: false
+ }
+ }, {
+ bidId: 'bidId4',
+ bidder: 'conversant',
+ placementCode: 'div4',
+ mediaType: 'video',
+ sizes: [[480, 480]],
+ params: {
+ site_id: '89192',
+ pos: 1,
+ tagid: 'tagid-4',
secure: false
}
}
@@ -101,7 +115,7 @@ describe('Conversant adapter tests', function () {
expect(thirdBid.bidderCode).to.equal('conversant');
expect(placementCode3).to.equal('div3');
- expect(addBidResponseSpy.getCalls().length).to.equal(3);
+ expect(addBidResponseSpy.getCalls().length).to.equal(4);
});
it('Should submit bids with statuses of 2 to the bid manager for empty bid responses', function () {
@@ -126,7 +140,7 @@ describe('Conversant adapter tests', function () {
expect(thirdBid.getStatusCode()).to.equal(2);
expect(thirdBid.bidderCode).to.equal('conversant');
- expect(addBidResponseSpy.getCalls().length).to.equal(3);
+ expect(addBidResponseSpy.getCalls().length).to.equal(4);
});
it('Should submit valid bids to the bid manager', function () {
@@ -150,8 +164,7 @@ describe('Conversant adapter tests', function () {
adm: 'adm2',
h: 300,
w: 600
- },
- {
+ }, {
id: 33333,
impid: 'bidId3',
price: 0.33,
@@ -190,8 +203,34 @@ describe('Conversant adapter tests', function () {
expect(thirdBid.ad).to.equal('adm3' + '');
expect(placementCode3).to.equal('div3');
- expect(addBidResponseSpy.getCalls().length).to.equal(3);
+ expect(addBidResponseSpy.getCalls().length).to.equal(4);
});
+
+ it('Should submit video bid responses correctly.', function () {
+ var bidResponse = {
+ id: 123,
+ seatbid: [{
+ bid: [{
+ id: 1111111,
+ impid: 'bidId4',
+ price: 0.11,
+ nurl: 'imp_tracker',
+ adm: 'vasturl'
+ }]
+ }]
+ };
+
+ $$PREBID_GLOBAL$$.conversantResponse(bidResponse);
+
+ var videoBid = addBidResponseSpy.getCall(0).args[1];
+ var placementCode = addBidResponseSpy.getCall(0).args[0];
+
+ expect(videoBid.getStatusCode()).to.equal(1);
+ expect(videoBid.bidderCode).to.equal('conversant');
+ expect(videoBid.cpm).to.equal(0.11);
+ expect(videoBid.vastUrl).to.equal('vasturl');
+ expect(placementCode).to.equal('div4');
+ })
});
describe('Should submit the correct headers in the xhr', function () {
@@ -218,8 +257,7 @@ describe('Conversant adapter tests', function () {
adm: 'adm2',
h: 300,
w: 600
- },
- {
+ }, {
id: 3333,
impid: 'bidId3',
price: 0.33,
@@ -253,4 +291,86 @@ describe('Conversant adapter tests', function () {
expect(request.requestBody).to.not.be.empty;
});
});
+ describe('Should create valid bid requests.', function () {
+ var server,
+ adapter;
+
+ var bidResponse = {
+ id: 123,
+ seatbid: [{
+ bid: [{
+ id: 1111,
+ impid: 'bidId1',
+ price: 0.11,
+ nurl: '',
+ adm: 'adm',
+ h: 250,
+ w: 300,
+ ext: {}
+ }, {
+ id: 2222,
+ impid: 'bidId2',
+ price: 0.22,
+ nurl: '',
+ adm: 'adm2',
+ h: 300,
+ w: 600
+ }, {
+ id: 3333,
+ impid: 'bidId3',
+ price: 0.33,
+ nurl: '',
+ adm: 'adm3',
+ h: 160,
+ w: 600
+ }]
+ }]
+ };
+
+ beforeEach(function () {
+ server = sinon.fakeServer.create();
+ adapter = new Adapter();
+ });
+
+ afterEach(function () {
+ server.restore();
+ });
+
+ beforeEach(function () {
+ var resp = [200, {'Content-type': 'text/javascript'}, '$$PREBID_GLOBAL$$.conversantResponse(\'' + JSON.stringify(bidResponse) + '\')'];
+ server.respondWith('POST', new RegExp('media.msg.dotomi.com/s2s/header'), resp);
+ });
+
+ it('Should create valid bid requests.', function () {
+ adapter.callBids(bidderRequest);
+ server.respond();
+ var request = JSON.parse(server.requests[0].requestBody);
+ expect(request.imp[0].banner.format[0].w).to.equal(300);
+ expect(request.imp[0].banner.format[0].h).to.equal(600);
+ expect(request.imp[0].tagid).to.equal('tagid-1');
+ expect(request.imp[0].banner.pos).to.equal(1);
+ expect(request.imp[0].secure).to.equal(0);
+ expect(request.site.id).to.equal('89192');
+ });
+
+ it('Should not pass empty or missing optional parameters on requests.', function () {
+ adapter.callBids(bidderRequest);
+ server.respond();
+
+ var request = JSON.parse(server.requests[0].requestBody);
+ expect(request.imp[1].tagid).to.equal(undefined);
+ expect(request.imp[2].tagid).to.equal(undefined);
+ expect(request.imp[1].pos).to.equal(undefined);
+ });
+
+ it('Should create the format objects correctly.', function () {
+ adapter.callBids(bidderRequest);
+ server.respond();
+
+ var request = JSON.parse(server.requests[0].requestBody);
+ expect(request.imp[2].banner.format.length).to.equal(2);
+ expect(request.imp[2].banner.format[0].w).to.equal(300);
+ expect(request.imp[2].banner.format[1].w).to.equal(160);
+ });
+ });
});
diff --git a/test/spec/adapters/cox_spec.js b/test/spec/adapters/cox_spec.js
new file mode 100644
index 00000000000..3f1342230db
--- /dev/null
+++ b/test/spec/adapters/cox_spec.js
@@ -0,0 +1,121 @@
+import Adapter from 'src/adapters/cox';
+import bidManager from 'src/bidmanager';
+import adLoader from 'src/adloader';
+import utils from 'src/utils';
+import {expect} from 'chai';
+
+describe('CoxAdapter', () => {
+ let adapter;
+ let loadScriptStub;
+ let addBidResponseSpy;
+
+ let emitScript = (script) => {
+ let node = document.createElement('script');
+ node.type = 'text/javascript';
+ node.appendChild(document.createTextNode(script));
+ document.getElementsByTagName('head')[0].appendChild(node);
+ };
+
+ beforeEach(() => {
+ adapter = new Adapter();
+ addBidResponseSpy = sinon.spy(bidManager, 'addBidResponse');
+ });
+
+ afterEach(() => {
+ loadScriptStub.restore();
+ addBidResponseSpy.restore();
+ });
+
+ describe('response handling', () => {
+ const normalResponse = 'cdsTag.__callback__({"zones":{"as2000005991707":{"ad" : "FOO<\/h1>","uid" : "","price" : 1.51,"floor" : 0,}},"tpCookieSync":"FOOKIE<\/h1>"})';
+ const zeroPriceResponse = 'cdsTag.__callback__({"zones":{"as2000005991707":{"ad" : "DEFAULT FOO<\/h1>","uid" : "","price" : 0,"floor" : 0,}},"tpCookieSync":"FOOKIE<\/h1>"})';
+ const incompleteResponse = 'cdsTag.__callback__({"zones":{},"tpCookieSync":"FOOKIE<\/h1>"})';
+
+ const oneBidConfig = {
+ bidderCode: 'cox',
+ bids: [{
+ bidder: 'cox',
+ placementCode: 'FOO456789',
+ sizes: [300, 250],
+ params: { size: '300x250', id: 2000005991707, siteId: 2000100948180, env: 'PROD' },
+ }]
+ };
+
+ // ===== 1
+ it('should provide a correctly populated Bid given a valid response', () => {
+ loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(normalResponse); })
+
+ adapter.callBids(oneBidConfig);
+
+ let bid = addBidResponseSpy.args[0][1];
+ expect(bid.cpm).to.equal(1.51);
+ expect(bid.ad).to.be.a('string');
+ expect(bid.bidderCode).to.equal('cox');
+ });
+
+ // ===== 2
+ it('should provide an empty Bid given a zero-price response', () => {
+ loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(zeroPriceResponse); })
+
+ adapter.callBids(oneBidConfig);
+
+ let bid = addBidResponseSpy.args[0][1];
+ expect(bid.cpm).to.not.be.ok
+ expect(bid.ad).to.not.be.ok;
+ });
+
+ // ===== 3
+ it('should provide an empty Bid given an incomplete response', () => {
+ loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(incompleteResponse); })
+
+ adapter.callBids(oneBidConfig);
+
+ let bid = addBidResponseSpy.args[0][1];
+ expect(bid.cpm).to.not.be.ok
+ expect(bid.ad).to.not.be.ok;
+ });
+
+ // ===== 4
+ it('should not provide a Bid given no response', () => {
+ loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(''); });
+
+ adapter.callBids(oneBidConfig);
+
+ expect(addBidResponseSpy.callCount).to.equal(0);
+ });
+ });
+
+ describe('request generation', () => {
+ const missingBidsConfig = {
+ bidderCode: 'cox',
+ bids: null,
+ };
+ const missingParamsConfig = {
+ bidderCode: 'cox',
+ bids: [{
+ bidder: 'cox',
+ placementCode: 'FOO456789',
+ sizes: [300, 250],
+ params: null,
+ }]
+ };
+
+ // ===== 5
+ it('should not make an ad call given missing bids in config', () => {
+ loadScriptStub = sinon.stub(adLoader, 'loadScript');
+
+ adapter.callBids(missingBidsConfig);
+
+ expect(loadScriptStub.callCount).to.equal(0);
+ });
+
+ // ===== 6
+ it('should not make an ad call given missing params in config', () => {
+ loadScriptStub = sinon.stub(adLoader, 'loadScript');
+
+ adapter.callBids(missingParamsConfig);
+
+ expect(loadScriptStub.callCount).to.equal(0);
+ });
+ });
+});
diff --git a/test/spec/adapters/eplanning_spec.js b/test/spec/adapters/eplanning_spec.js
new file mode 100644
index 00000000000..668392c169b
--- /dev/null
+++ b/test/spec/adapters/eplanning_spec.js
@@ -0,0 +1,112 @@
+describe('eplanning adapter tests', function () {
+ var urlParse = require('url-parse');
+ var querystringify = require('querystringify');
+ var adapter = require('src/adapters/eplanning');
+ var adLoader = require('src/adloader');
+ var expect = require('chai').expect;
+ var bidmanager = require('src/bidmanager');
+ var CONSTANTS = require('src/constants.json');
+
+ var DEFAULT_PARAMS = {
+ bidderCode: 'eplanning',
+ bids: [{
+ code: 'div-gpt-ad-1460505748561-0',
+ sizes: [[300, 250], [300, 200]],
+ bidder: 'eplanning',
+ params: {
+ ci: '18f66'
+ }
+ }]
+ };
+
+ var PARAMS_SERVER_TEST = {
+ bidderCode: 'eplanning',
+ bids: [{
+ code: 'div-gpt-ad-1460505748561-0',
+ sizes: [[300, 250], [300, 600]],
+ bidder: 'eplanning',
+ params: {
+ ci: '18f66',
+ t: '1'
+ }
+ }]
+ };
+
+ var RESPONSE_AD = {
+ bids: [{
+ placementCode: 'div-gpt-ad-1460505748561-0',
+ ad: {
+ ad: '
test ad
',
+ cpm: 1,
+ width: 300,
+ height: 250
+ }
+ }]
+ };
+
+ var RESPONSE_EMPTY = {
+ bids: [{
+ placementCode: 'div-gpt-ad-1460505748561-0'
+ }]
+ };
+
+ var stubAddBidResponse;
+
+ describe('eplanning tests', function() {
+ beforeEach(function() {
+ stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse');
+ });
+ afterEach(function() {
+ stubAddBidResponse.restore();
+ });
+
+ it('callback function should exist', function() {
+ expect(pbjs.processEPlanningResponse).to.exist.and.to.be.a('function');
+ });
+
+ it('creates a bid response if bid exists', function() {
+ adapter().callBids(DEFAULT_PARAMS);
+ pbjs.processEPlanningResponse(RESPONSE_AD);
+
+ var bidPlacementCode = stubAddBidResponse.getCall(0).args[0];
+ var bidObject = stubAddBidResponse.getCall(0).args[1];
+
+ expect(bidPlacementCode).to.equal('div-gpt-ad-1460505748561-0');
+ expect(bidObject.cpm).to.equal(1);
+ expect(bidObject.ad).to.equal('test ad
');
+ expect(bidObject.width).to.equal(300);
+ expect(bidObject.height).to.equal(250);
+ expect(bidObject.getStatusCode()).to.equal(1);
+ expect(bidObject.bidderCode).to.equal('eplanning');
+ });
+
+ it('creates an empty bid response if there is no bid', function() {
+ adapter().callBids(DEFAULT_PARAMS);
+ pbjs.processEPlanningResponse(RESPONSE_EMPTY);
+
+ var bidPlacementCode = stubAddBidResponse.getCall(0).args[0];
+ var bidObject = stubAddBidResponse.getCall(0).args[1];
+
+ expect(bidPlacementCode).to.equal('div-gpt-ad-1460505748561-0');
+ expect(bidObject.getStatusCode()).to.equal(2);
+ expect(bidObject.bidderCode).to.equal('eplanning');
+ });
+
+ it('creates a bid response and sync users register ad', function() {
+ adapter().callBids(DEFAULT_PARAMS);
+ window.hbpb.rH({
+ 'sI': { 'k': '18f66' },
+ 'sec': { 'k': 'ROS' },
+ 'sp': [ { 'k': 'div-gpt-ad-1460505748561-0', 'a': [{ 'w': 300, 'h': 250, 'adm': 'test ad
', 'pr': 1 }] } ],
+ 'cs': [
+ '//test.gif',
+ { 'j': true, u: '//test.js' },
+ { 'ifr': true, u: '//test.html', data: { 'test': 1 } }
+ ]
+ });
+ var bidPlacementCode = stubAddBidResponse.getCall(0).args[0];
+ var bidObject = stubAddBidResponse.getCall(0).args[1];
+ expect(bidObject.getStatusCode()).to.equal(2);
+ });
+ });
+});
diff --git a/test/spec/adapters/gumgum_spec.js b/test/spec/adapters/gumgum_spec.js
index 96e756d1052..a6d33dc5bd0 100644
--- a/test/spec/adapters/gumgum_spec.js
+++ b/test/spec/adapters/gumgum_spec.js
@@ -92,6 +92,74 @@ describe('gumgum adapter', () => {
sandbox.restore();
});
+ describe('DigiTrust params', () => {
+ beforeEach(() => {
+ sandbox.stub(adLoader, 'loadScript');
+ });
+
+ it('should send digiTrust params', () => {
+ window.DigiTrust = {
+ getUser: function() {}
+ };
+ sandbox.stub(window.DigiTrust, 'getUser', () =>
+ ({
+ success: true,
+ identity: {
+ privacy: {optout: false},
+ id: 'testId'
+ }
+ })
+ );
+
+ adapter.callBids(bidderRequest);
+ expect(adLoader.loadScript.firstCall.args[0]).to.include('&dt=testId');
+ delete window.DigiTrust;
+ });
+
+ it('should not send DigiTrust params when DigiTrust is not loaded', () => {
+ adapter.callBids(bidderRequest);
+ expect(adLoader.loadScript.firstCall.args[0]).to.not.include('&dt');
+ });
+
+ it('should not send DigiTrust params due to opt out', () => {
+ window.DigiTrust = {
+ getUser: function() {}
+ };
+ sandbox.stub(window.DigiTrust, 'getUser', () =>
+ ({
+ success: true,
+ identity: {
+ privacy: {optout: true},
+ id: 'testId'
+ }
+ })
+ );
+
+ adapter.callBids(bidderRequest);
+ expect(adLoader.loadScript.firstCall.args[0]).to.not.include('&dt');
+ delete window.DigiTrust;
+ });
+
+ it('should not send DigiTrust params on failure', () => {
+ window.DigiTrust = {
+ getUser: function() {}
+ };
+ sandbox.stub(window.DigiTrust, 'getUser', () =>
+ ({
+ success: false,
+ identity: {
+ privacy: {optout: false},
+ id: 'testId'
+ }
+ })
+ );
+
+ adapter.callBids(bidderRequest);
+ expect(adLoader.loadScript.firstCall.args[0]).to.not.include('&dt');
+ delete window.DigiTrust;
+ });
+ });
+
describe('callBids', () => {
beforeEach(() => {
sandbox.stub(adLoader, 'loadScript');
diff --git a/test/spec/adapters/prebidServer_spec.js b/test/spec/adapters/prebidServer_spec.js
index 0d047133c97..fe1cb7e9894 100644
--- a/test/spec/adapters/prebidServer_spec.js
+++ b/test/spec/adapters/prebidServer_spec.js
@@ -66,7 +66,8 @@ const RESPONSE = {
'price': 0.5,
'adm': '',
'width': 300,
- 'height': 250
+ 'height': 250,
+ 'deal_id': 'test-dealid'
}
]
};
@@ -143,16 +144,20 @@ describe('S2S Adapter', () => {
sinon.stub(bidmanager, 'addBidResponse');
sinon.stub(utils, 'getBidderRequestAllAdUnits').returns({
bids: [{
- bidId: '32167',
+ bidId: '123',
placementCode: 'div-gpt-ad-1460505748561-0'
}]
});
+ sinon.stub(utils, 'getBidRequest').returns({
+ bidId: '123'
+ });
});
afterEach(() => {
server.restore();
bidmanager.addBidResponse.restore();
utils.getBidderRequestAllAdUnits.restore();
+ utils.getBidRequest.restore();
});
it('registers bids', () => {
@@ -166,9 +171,10 @@ describe('S2S Adapter', () => {
const response = bidmanager.addBidResponse.firstCall.args[1];
expect(response).to.have.property('statusMessage', 'Bid available');
expect(response).to.have.property('cpm', 0.5);
+ expect(response).to.have.property('adId', '123');
});
- it('registers no bid response when ad unit not set', () => {
+ it('registers no-bid response when ad unit not set', () => {
server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT));
adapter.setConfig(CONFIG);
@@ -183,10 +189,10 @@ describe('S2S Adapter', () => {
expect(response).to.have.property('statusMessage', 'Bid returned empty or error response');
const bid_request_passed = bidmanager.addBidResponse.firstCall.args[1];
- expect(bid_request_passed).to.have.property('adId', '32167');
+ expect(bid_request_passed).to.have.property('adId', '123');
});
- it('registers no bid response when server requests cookie sync', () => {
+ it('registers no-bid response when server requests cookie sync', () => {
server.respondWith(JSON.stringify(RESPONSE_NO_COOKIE));
adapter.setConfig(CONFIG);
@@ -201,10 +207,10 @@ describe('S2S Adapter', () => {
expect(response).to.have.property('statusMessage', 'Bid returned empty or error response');
const bid_request_passed = bidmanager.addBidResponse.firstCall.args[1];
- expect(bid_request_passed).to.have.property('adId', '32167');
+ expect(bid_request_passed).to.have.property('adId', '123');
});
- it('registers no bid response when ad unit is set', () => {
+ it('registers no-bid response when ad unit is set', () => {
server.respondWith(JSON.stringify(RESPONSE_NO_BID_UNIT_SET));
adapter.setConfig(CONFIG);
@@ -218,5 +224,47 @@ describe('S2S Adapter', () => {
const response = bidmanager.addBidResponse.firstCall.args[1];
expect(response).to.have.property('statusMessage', 'Bid returned empty or error response');
});
+
+ it('registers no-bid response when there are less bids than requests', () => {
+ utils.getBidderRequestAllAdUnits.restore();
+ sinon.stub(utils, 'getBidderRequestAllAdUnits').returns({
+ bids: [{
+ bidId: '123',
+ placementCode: 'div-gpt-ad-1460505748561-0'
+ }, {
+ bidId: '101111',
+ placementCode: 'div-gpt-ad-1460505748561-1'
+ }]
+ });
+
+ server.respondWith(JSON.stringify(RESPONSE));
+
+ adapter.setConfig(CONFIG);
+ adapter.callBids(REQUEST);
+ server.respond();
+
+ sinon.assert.calledTwice(bidmanager.addBidResponse);
+
+ expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('div-gpt-ad-1460505748561-0');
+ expect(bidmanager.addBidResponse.secondCall.args[0]).to.equal('div-gpt-ad-1460505748561-1');
+
+ expect(bidmanager.addBidResponse.firstCall.args[1]).to.have.property('adId', '123');
+ expect(bidmanager.addBidResponse.secondCall.args[1]).to.have.property('adId', '101111');
+
+ expect(bidmanager.addBidResponse.firstCall.args[1])
+ .to.have.property('statusMessage', 'Bid available');
+ expect(bidmanager.addBidResponse.secondCall.args[1])
+ .to.have.property('statusMessage', 'Bid returned empty or error response');
+ });
+
+ it('should have dealId in bidObject', () => {
+ server.respondWith(JSON.stringify(RESPONSE));
+
+ adapter.setConfig(CONFIG);
+ adapter.callBids(REQUEST);
+ server.respond();
+ const response = bidmanager.addBidResponse.firstCall.args[1];
+ expect(response).to.have.property('dealId', 'test-dealid');
+ });
});
});
diff --git a/test/spec/adapters/trion_spec.js b/test/spec/adapters/trion_spec.js
index b5e79cf7312..dbbcf66ec87 100644
--- a/test/spec/adapters/trion_spec.js
+++ b/test/spec/adapters/trion_spec.js
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import trionAdapter from 'src/adapters/trion';
import bidmanager from 'src/bidmanager';
+import * as utils from 'src/utils';
const CONSTANTS = require('src/constants.json');
const adloader = require('src/adloader');
@@ -106,7 +107,8 @@ describe('Trion adapter tests', () => {
let bidUrl = spyLoadScript.getCall(0).args[0];
expect(bidUrl).to.include('re=1');
- expect(bidUrl).to.include(window.location.href);
+ expect(bidUrl).to.include(utils.getTopWindowUrl());
+ expect(bidUrl).to.include('slot=' + PLACEMENT_CODE);
delete params.re;
});
diff --git a/test/spec/adapters/unruly_spec.js b/test/spec/adapters/unruly_spec.js
new file mode 100644
index 00000000000..de0aa683bc9
--- /dev/null
+++ b/test/spec/adapters/unruly_spec.js
@@ -0,0 +1,278 @@
+/* globals describe, it, beforeEach, afterEach, sinon */
+import { expect } from 'chai'
+import bidfactory from 'src/bidfactory'
+import bidmanager from 'src/bidmanager'
+import * as utils from 'src/utils'
+import { STATUS } from 'src/constants'
+import { Renderer } from 'src/Renderer'
+import createUnrulyAdapter from 'src/adapters/unruly'
+
+describe('UnrulyAdapter', () => {
+ function createBidRequestBid({ placementCode }) {
+ return {
+ 'bidder': 'unruly',
+ 'params': {
+ 'uuid': '74544e00-d43b-4f3a-a799-69d22ce979ce',
+ 'siteId': 794599,
+ 'placementId': '5768085'
+ },
+ 'placementCode': placementCode,
+ 'mediaType': 'video',
+ 'transactionId': '62890707-3770-497c-a3b8-d905a2d0cb98',
+ 'sizes': [
+ 640,
+ 480
+ ],
+ 'bidId': '23b86d8f6335ce',
+ 'bidderRequestId': '1d5b7474eb5416',
+ 'requestId': '406fe12b-fa3b-4bd3-b3c8-043951b4dac1'
+ }
+ }
+
+ function createParams(...bids) {
+ return {
+ 'bidderCode': 'unruly',
+ 'requestId': '406fe12b-fa3b-4bd3-b3c8-043951b4dac1',
+ 'bidderRequestId': '1d5b7474eb5416',
+ 'bids': bids,
+ 'start': 1495794517251,
+ 'auctionStart': 1495794517250,
+ 'timeout': 3000
+ }
+ }
+
+ function createOutStreamExchangeBid({ placementCode, statusCode = 1 }) {
+ return {
+ 'ext': {
+ 'statusCode': statusCode,
+ 'renderer': {
+ 'id': 'unruly_inarticle',
+ 'config': {},
+ 'url': 'https://video.unrulymedia.com/native/prebid-loader.js'
+ },
+ 'placementCode': placementCode
+ },
+ 'cpm': 20,
+ 'bidderCode': 'unruly',
+ 'width': 323,
+ 'vastUrl': 'https://targeting.unrulymedia.com/in_article?uuid=74544e00-d43b-4f3a-a799-69d22ce979ce&supported_mime_type=application/javascript&supported_mime_type=video/mp4&tj=%7B%22site%22%3A%7B%22lang%22%3A%22en-GB%22%2C%22ref%22%3A%22%22%2C%22page%22%3A%22http%3A%2F%2Fdemo.unrulymedia.com%2FinArticle%2Finarticle_nypost_upbeat%2Ftravel_magazines.html%22%2C%22domain%22%3A%22demo.unrulymedia.com%22%7D%2C%22user%22%3A%7B%22profile%22%3A%7B%22quantcast%22%3A%7B%22segments%22%3A%5B%7B%22id%22%3A%22D%22%7D%2C%7B%22id%22%3A%22T%22%7D%5D%7D%7D%7D%7D&video_width=618&video_height=347',
+ 'bidId': 'foo',
+ 'height': 323
+ }
+ }
+
+ function createInStreamExchangeBid({ placementCode, statusCode = 1 }) {
+ return {
+ 'ext': {
+ 'statusCode': statusCode,
+ 'placementCode': placementCode
+ },
+ 'cpm': 20,
+ 'bidderCode': 'unruly',
+ 'width': 323,
+ 'vastUrl': 'https://targeting.unrulymedia.com/in_article?uuid=74544e00-d43b-4f3a-a799-69d22ce979ce&supported_mime_type=application/javascript&supported_mime_type=video/mp4&tj=%7B%22site%22%3A%7B%22lang%22%3A%22en-GB%22%2C%22ref%22%3A%22%22%2C%22page%22%3A%22http%3A%2F%2Fdemo.unrulymedia.com%2FinArticle%2Finarticle_nypost_upbeat%2Ftravel_magazines.html%22%2C%22domain%22%3A%22demo.unrulymedia.com%22%7D%2C%22user%22%3A%7B%22profile%22%3A%7B%22quantcast%22%3A%7B%22segments%22%3A%5B%7B%22id%22%3A%22D%22%7D%2C%7B%22id%22%3A%22T%22%7D%5D%7D%7D%7D%7D&video_width=618&video_height=347',
+ 'bidId': 'foo',
+ 'height': 323
+ }
+ }
+
+ function createExchangeResponse(...bids) {
+ return {
+ 'bids': bids
+ }
+ }
+
+ let adapter
+ let server
+ let sandbox
+ let fakeRenderer
+
+ beforeEach(() => {
+ adapter = createUnrulyAdapter()
+ adapter.exchangeUrl = 'http://localhost:9000/prebid'
+
+ sandbox = sinon.sandbox.create()
+ sandbox.stub(bidmanager, 'addBidResponse')
+ sandbox.stub(bidfactory, 'createBid')
+ sandbox.stub(utils, 'logError')
+
+ fakeRenderer = {
+ setRender: sinon.stub()
+ }
+
+ sandbox.stub(Renderer, 'install')
+ Renderer.install.returns(fakeRenderer)
+
+ server = sinon.fakeServer.create()
+ })
+
+ afterEach(() => {
+ sandbox.restore()
+ server.restore()
+ delete parent.window.unruly
+ })
+
+ describe('callBids', () => {
+ it('exists and is a function', () => {
+ expect(adapter.callBids).to.exist.and.to.be.a('function')
+ })
+
+ it('requires bids to make request', () => {
+ adapter.callBids({})
+ expect(server.requests).to.be.empty
+ })
+
+ it('requires at least one bid to make request', () => {
+ adapter.callBids({ bids: [] })
+ expect(server.requests).to.be.empty
+ })
+
+ it('passes bids through to exchange', () => {
+ const params = createParams(createBidRequestBid({ placementCode: 'placement1' }))
+
+ adapter.callBids(params)
+
+ expect(server.requests).to.have.length(1)
+ expect(server.requests[0].url).to.equal('http://localhost:9000/prebid')
+
+ const requestBody = JSON.parse(server.requests[0].requestBody)
+ expect(requestBody).to.deep.equal({
+ 'bidRequests': params.bids
+ })
+ })
+
+ it('creates a bid response using status code from exchange for each bid and passes in the exchange response', () => {
+ const params = createParams(createBidRequestBid({ placementCode: 'placement1' }))
+
+ const exchangeBid1 = createOutStreamExchangeBid({ placementCode: 'placement1' })
+ const exchangeBid2 = createOutStreamExchangeBid({ placementCode: 'placement2', statusCode: 2 })
+ const exchangeResponse = createExchangeResponse(exchangeBid1, exchangeBid2)
+
+ server.respondWith(JSON.stringify(exchangeResponse))
+ bidfactory.createBid.returns({})
+
+ adapter.callBids(params)
+ server.respond()
+
+ sinon.assert.calledTwice(bidfactory.createBid)
+ sinon.assert.calledWith(bidfactory.createBid, exchangeBid1.ext.statusCode, exchangeResponse.bids[0])
+ sinon.assert.calledWith(bidfactory.createBid, exchangeBid2.ext.statusCode, exchangeResponse.bids[1])
+ })
+
+ it('adds the bid response to the bid manager', () => {
+ const fakeBid = {}
+
+ const params = createParams(createBidRequestBid({ placementCode: 'placement1' }))
+ const exchangeBid = createOutStreamExchangeBid({ placementCode: 'placement1' })
+ const exchangeResponse = createExchangeResponse(exchangeBid)
+
+ server.respondWith(JSON.stringify(exchangeResponse))
+ bidfactory.createBid.withArgs(exchangeBid.ext.statusCode).returns(fakeBid)
+
+ adapter.callBids(params)
+ server.respond()
+
+ sinon.assert.calledOnce(bidmanager.addBidResponse)
+ sinon.assert.calledWith(bidmanager.addBidResponse, exchangeBid.ext.placementCode, fakeBid)
+ })
+
+ describe('on invalid exchange response', () => {
+ it('should create NO_BID response for each bid request bid', () => {
+ const bidRequestBid1 = createBidRequestBid({ placementCode: 'placement1' })
+ const bidRequestBid2 = createBidRequestBid({ placementCode: 'placement2' })
+ const params = createParams(bidRequestBid1, bidRequestBid2)
+ const expectedBid = { 'some': 'props' }
+
+ server.respondWith('this is not json')
+ bidfactory.createBid.withArgs(STATUS.NO_BID).returns(expectedBid)
+
+ adapter.callBids(params)
+ server.respond()
+
+ sinon.assert.calledOnce(utils.logError)
+ sinon.assert.calledTwice(bidmanager.addBidResponse)
+ sinon.assert.calledWith(bidmanager.addBidResponse, bidRequestBid1.placementCode, expectedBid)
+ sinon.assert.calledWith(bidmanager.addBidResponse, bidRequestBid2.placementCode, expectedBid)
+ })
+ })
+
+ describe('InStream', () => {
+ it('merges bid response defaults', () => {
+ const params = createParams(createBidRequestBid({ placementCode: 'placement1' }))
+
+ const fakeBidDefaults = { some: 'default' }
+ const fakeBid = Object.assign({}, fakeBidDefaults)
+
+ const exchangeBid = createInStreamExchangeBid({ placementCode: 'placement1' })
+ const exchangeResponse = createExchangeResponse(exchangeBid)
+ server.respondWith(JSON.stringify(exchangeResponse))
+
+ bidfactory.createBid.withArgs(exchangeBid.ext.statusCode).returns(fakeBid)
+
+ adapter.callBids(params)
+ server.respond()
+
+ sinon.assert.notCalled(Renderer.install)
+ expect(fakeBid).to.deep.equal(Object.assign(
+ {},
+ fakeBidDefaults,
+ exchangeBid
+ ))
+ })
+ })
+
+ describe('OutStream', () => {
+ it('merges bid response defaults with exchange bid and renderer', () => {
+ const params = createParams(createBidRequestBid({ placementCode: 'placement1' }))
+
+ const fakeBidDefaults = { some: 'default' }
+ const fakeBid = Object.assign({}, fakeBidDefaults)
+
+ const exchangeBid = createOutStreamExchangeBid({ placementCode: 'placement1' })
+ const exchangeResponse = createExchangeResponse(exchangeBid)
+ server.respondWith(JSON.stringify(exchangeResponse))
+
+ bidfactory.createBid.withArgs(exchangeBid.ext.statusCode).returns(fakeBid)
+
+ const fakeRenderer = {}
+ Renderer.install.withArgs(Object.assign(
+ {},
+ exchangeBid.ext.renderer,
+ { callback: sinon.match.func }
+ )).returns(fakeRenderer)
+
+ adapter.callBids(params)
+ server.respond()
+
+ expect(fakeBid).to.deep.equal(Object.assign(
+ {},
+ fakeBidDefaults,
+ exchangeBid,
+ { renderer: fakeRenderer }
+ ))
+ })
+
+ it('bid is placed on the bid queue when render is called', () => {
+ const params = createParams(createBidRequestBid({ placementCode: 'placement1' }))
+
+ const fakeBidDefaults = { some: 'default' }
+ const fakeBid = Object.assign({}, fakeBidDefaults)
+
+ const exchangeBid = createOutStreamExchangeBid({ placementCode: 'placement1' })
+ const exchangeResponse = createExchangeResponse(exchangeBid)
+ server.respondWith(JSON.stringify(exchangeResponse))
+
+ bidfactory.createBid.withArgs(exchangeBid.ext.statusCode).returns(fakeBid)
+
+ adapter.callBids(params)
+ server.respond()
+
+ sinon.assert.calledOnce(fakeRenderer.setRender)
+ fakeRenderer.setRender.firstCall.args[0]()
+
+ expect(window.top).to.have.deep.property('unruly.native.prebid.uq');
+ expect(window.top.unruly.native.prebid.uq).to.deep.equal([['render', fakeBid]])
+ })
+ })
+ })
+})
diff --git a/test/spec/renderer_spec.js b/test/spec/renderer_spec.js
index ba60923ed2a..282c4841ac0 100644
--- a/test/spec/renderer_spec.js
+++ b/test/spec/renderer_spec.js
@@ -53,4 +53,39 @@ describe('Renderer: A renderer installed on a bid response', () => {
testRenderer1.handleVideoEvent({ id: 1, eventName: 'testEvent' });
expect(spyEventHandler.called).to.equal(true);
});
+
+ it('pushes commands to queue if renderer is not loaded', () => {
+ testRenderer1.push(spyRenderFn);
+
+ expect(testRenderer1.cmd.length).to.equal(1);
+
+ // clear queue for next tests
+ testRenderer1.cmd = [];
+ });
+
+ it('fires commands immediately if the renderer is loaded', () => {
+ const func = sinon.spy();
+
+ testRenderer1.loaded = true;
+ testRenderer1.push(func);
+
+ expect(testRenderer1.cmd.length).to.equal(0);
+ sinon.assert.calledOnce(func);
+ });
+
+ it('processes queue by calling each function in queue', () => {
+ testRenderer1.loaded = false;
+ const func1 = sinon.spy();
+ const func2 = sinon.spy();
+
+ testRenderer1.push(func1);
+ testRenderer1.push(func2);
+ expect(testRenderer1.cmd.length).to.equal(2);
+
+ testRenderer1.process();
+
+ sinon.assert.calledOnce(func1);
+ sinon.assert.calledOnce(func2);
+ expect(testRenderer1.cmd.length).to.equal(0);
+ });
});