diff --git a/integrationExamples/gpt/gpt_aliasingBidder.html b/integrationExamples/gpt/gpt_aliasingBidder.html
index f9c7120079d..693be76e82e 100644
--- a/integrationExamples/gpt/gpt_aliasingBidder.html
+++ b/integrationExamples/gpt/gpt_aliasingBidder.html
@@ -35,7 +35,7 @@
// Load the Prebid Javascript Library Async. We recommend loading it immediately after
// the initAdserver() and setTimeout functions.
(function () {
- var d = document, pbs = d.createElement("script"), pro = d.location.protocal;
+ var d = document, pbs = d.createElement("script"), pro = d.location.protocol;
pbs.type = "text/javascript";
pbs.src = '/build/dist/prebid.js';
var target = document.getElementsByTagName("head")[0];
diff --git a/integrationExamples/gpt/pbjs_yieldbot_gpt.html b/integrationExamples/gpt/pbjs_yieldbot_gpt.html
new file mode 100644
index 00000000000..986eed8fc5e
--- /dev/null
+++ b/integrationExamples/gpt/pbjs_yieldbot_gpt.html
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+ Prebid.js Yieldbot Adapter Basic Example
+ Use the links below to enable and disable Yieldbot test bids.
+
+ Note:
+
+ The "Enable - Yieldbot Test Bids " link below will set a cookie to force Yieldbot bid requests to return static test creative: the cookie expires in 24 hrs.
+
+
+
+ Enable - Yieldbot Test Bids
+ Disable - Yieldbot Test Bids
+
+ Div-0, 728x90
+ Refresh 728x90 Ad Unit
+
+
+
+ Div-1, 300x250 or 300x600
+ Refresh 300x250, 300x600 Ad Unit
+
+
+
+ Div-2, 300x250 or 300x600
+ The bid for the 300x250 | 300x600
slot is shown under Div-1 above.
+
+ Refresh this slot after initial page view and you should see the Yieldbot test creative.
+
+ Refresh 300x250, 300x600 Ad Unit
+
+
+
+
+
diff --git a/modules/adformBidAdapter.js b/modules/adformBidAdapter.js
index 5d1bb98b4ec..69908e70888 100644
--- a/modules/adformBidAdapter.js
+++ b/modules/adformBidAdapter.js
@@ -1,14 +1,15 @@
-var utils = require('src/utils.js');
-var adloader = require('src/adloader.js');
-var bidmanager = require('src/bidmanager.js');
-var bidfactory = require('src/bidfactory.js');
+var utils = require('src/utils');
+var adloader = require('src/adloader');
+var bidmanager = require('src/bidmanager');
+var bidfactory = require('src/bidfactory');
var STATUSCODES = require('src/constants.json').STATUS;
var adaptermanager = require('src/adaptermanager');
+var Adapter = require('src/adapter').default;
+
+const ADFORM_BIDDER_CODE = 'adform';
function AdformAdapter() {
- return {
- callBids: _callBids
- };
+ let baseAdapter = new Adapter(ADFORM_BIDDER_CODE);
function _callBids(params) {
var bid, _value, _key, i, j, k, l, reqParams;
@@ -47,7 +48,7 @@ function AdformAdapter() {
$$PREBID_GLOBAL$$[callbackName] = handleCallback(bids);
adloader.loadScript(request.join('&'));
- }
+ };
function formRequestUrl(reqData) {
var key;
@@ -63,7 +64,7 @@ function AdformAdapter() {
function handleCallback(bids) {
return function handleResponse(adItems) {
var bidObject;
- var bidder = 'adform';
+ var bidder = baseAdapter.getBidderCode();
var adItem;
var bid;
for (var i = 0, l = adItems.length; i < l; i++) {
@@ -160,8 +161,11 @@ function AdformAdapter() {
return utftext;
}
-}
-adaptermanager.registerBidAdapter(new AdformAdapter(), 'adform');
+ return Object.assign(this, baseAdapter, {
+ callBids: _callBids
+ });
+}
+adaptermanager.registerBidAdapter(new AdformAdapter(), ADFORM_BIDDER_CODE);
module.exports = AdformAdapter;
diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js
new file mode 100644
index 00000000000..ccb0287a866
--- /dev/null
+++ b/modules/adxcgBidAdapter.js
@@ -0,0 +1,139 @@
+import bidfactory from 'src/bidfactory';
+import bidmanager from 'src/bidmanager';
+import * as utils from 'src/utils';
+import { STATUS } from 'src/constants';
+import adaptermanager from 'src/adaptermanager';
+import { ajax } from 'src/ajax';
+import * as url from 'src/url';
+
+/**
+ * Adapter for requesting bids from Adxcg
+ * updated from latest prebid repo on 2017.08.30
+ */
+function AdxcgAdapter() {
+ let bidRequests = {};
+
+ function _callBids(params) {
+ if (params.bids && params.bids.length > 0) {
+ let adZoneIds = [];
+ let prebidBidIds = [];
+ let sizes = [];
+
+ params.bids.forEach(bid => {
+ bidRequests[bid.bidId] = bid;
+ adZoneIds.push(utils.getBidIdParameter('adzoneid', bid.params));
+ prebidBidIds.push(bid.bidId);
+ sizes.push(utils.parseSizesInput(bid.sizes).join('|'));
+ });
+
+ let location = utils.getTopWindowLocation();
+ let secure = location.protocol == 'https:';
+
+ let requestUrl = url.parse(location.href);
+ requestUrl.search = null;
+ requestUrl.hash = null;
+
+ let adxcgRequestUrl = url.format({
+ protocol: secure ? 'https' : 'http',
+ hostname: secure ? 'ad-emea-secure.adxcg.net' : 'ad-emea.adxcg.net',
+ pathname: '/get/adi',
+ search: {
+ renderformat: 'javascript',
+ ver: 'r20141124',
+ adzoneid: adZoneIds.join(','),
+ format: sizes.join(','),
+ prebidBidIds: prebidBidIds.join(','),
+ url: escape(url.format(requestUrl)),
+ secure: secure ? '1' : '0'
+ }
+ });
+
+ utils.logMessage(`submitting request: ${adxcgRequestUrl}`);
+ ajax(adxcgRequestUrl, handleResponse, null, {
+ withCredentials: true
+ });
+ }
+ }
+
+ function handleResponse(response) {
+ let adxcgBidReponseList;
+
+ try {
+ adxcgBidReponseList = JSON.parse(response);
+ utils.logMessage(`adxcgBidReponseList: ${JSON.stringify(adxcgBidReponseList)}`);
+ } catch (error) {
+ adxcgBidReponseList = [];
+ utils.logError(error);
+ }
+
+ adxcgBidReponseList.forEach(adxcgBidReponse => {
+ let bidRequest = bidRequests[adxcgBidReponse.bidId];
+ delete bidRequests[adxcgBidReponse.bidId];
+
+ let bid = bidfactory.createBid(STATUS.GOOD, bidRequest);
+
+ bid.creative_id = adxcgBidReponse.creativeId;
+ bid.code = 'adxcg';
+ bid.bidderCode = 'adxcg';
+ bid.cpm = adxcgBidReponse.cpm;
+
+ if (adxcgBidReponse.ad) {
+ bid.ad = adxcgBidReponse.ad;
+ } else if (adxcgBidReponse.vastUrl) {
+ bid.vastUrl = adxcgBidReponse.vastUrl;
+ bid.descriptionUrl = adxcgBidReponse.vastUrl;
+ bid.mediaType = 'video';
+ } else if (adxcgBidReponse.nativeResponse) {
+ bid.mediaType = 'native';
+
+ let nativeResponse = adxcgBidReponse.nativeResponse;
+
+ bid.native = {
+ clickUrl: escape(nativeResponse.link.url),
+ impressionTrackers: nativeResponse.imptrackers
+ };
+
+ nativeResponse.assets.forEach(asset => {
+ if (asset.title && asset.title.text) {
+ bid.native.title = asset.title.text;
+ }
+
+ if (asset.img && asset.img.url) {
+ bid.native.image = asset.img.url;
+ }
+
+ if (asset.data && asset.data.label == 'DESC' && asset.data.value) {
+ bid.native.body = asset.data.value;
+ }
+
+ if (asset.data && asset.data.label == 'SPONSORED' && asset.data.value) {
+ bid.native.sponsoredBy = asset.data.value;
+ }
+ });
+ }
+
+ bid.width = adxcgBidReponse.width;
+ bid.height = adxcgBidReponse.height;
+
+ utils.logMessage(`submitting bid[${bidRequest.placementCode}]: ${JSON.stringify(bid)}`);
+ bidmanager.addBidResponse(bidRequest.placementCode, bid);
+ });
+
+ Object.keys(bidRequests)
+ .map(bidId => bidRequests[bidId].placementCode)
+ .forEach(placementCode => {
+ utils.logMessage(`creating no_bid bid for: ${placementCode}`);
+ bidmanager.addBidResponse(placementCode, bidfactory.createBid(STATUS.NO_BID));
+ });
+ };
+
+ return {
+ callBids: _callBids
+ };
+};
+
+adaptermanager.registerBidAdapter(new AdxcgAdapter(), 'adxcg', {
+ supportedMediaTypes: ['video', 'native']
+});
+
+module.exports = AdxcgAdapter;
diff --git a/modules/aerservBidAdapter.js b/modules/aerservBidAdapter.js
new file mode 100644
index 00000000000..64a02e77325
--- /dev/null
+++ b/modules/aerservBidAdapter.js
@@ -0,0 +1,117 @@
+import bidfactory from 'src/bidfactory';
+import bidmanager from 'src/bidmanager';
+import * as utils from 'src/utils';
+import {ajax} from 'src/ajax';
+import {STATUS} from 'src/constants';
+import adaptermanager from 'src/adaptermanager';
+
+const BIDDER_CODE = 'aerserv';
+
+const AerServAdapter = function AerServAdapter() {
+ const ENVIRONMENTS = {
+ local: '127.0.0.1:8080',
+ dev: 'dev-ads.aerserv.com',
+ stage: 'staging-ads.aerserv.com',
+ prod: 'ads.aerserv.com'
+ };
+
+ const BANNER_PATH = '/as/json/pbjs/v1?';
+ const VIDEO_PATH = '/as/json/pbjsvast/v1?';
+ const REQUIRED_PARAMS = ['plc'];
+
+ function _isResponseValid(bidRequest, response) {
+ return ((bidRequest.mediaType === 'video' && response.vastUrl) || (bidRequest.mediaType !== 'video' && response.adm)) &&
+ response.cpm && response.cpm > 0;
+ }
+
+ function _createBid(bidRequest, response) {
+ if (_isResponseValid(bidRequest, response)) {
+ let bid = bidfactory.createBid(1, bidRequest);
+ bid.bidderCode = BIDDER_CODE;
+ bid.cpm = response.cpm;
+ bid.width = response.w;
+ bid.height = response.h;
+ if (bidRequest.mediaType === 'video') {
+ bid.vastUrl = response.vastUrl;
+ bid.descriptionUrl = response.vastUrl;
+ bid.mediaType = 'video';
+ } else {
+ bid.ad = response.adm;
+ }
+ bidmanager.addBidResponse(bidRequest.placementCode, bid);
+ } else {
+ bidmanager.addBidResponse(bidRequest.placementCode, bidfactory.createBid(STATUS.NO_BID, bidRequest));
+ }
+ }
+
+ function _getFirstSize(sizes) {
+ let sizeObj = {};
+ if (utils.isArray(sizes) && sizes.length > 0 && utils.isArray(sizes[0]) && sizes[0].length === 2) {
+ sizeObj['vpw'] = sizes[0][0];
+ sizeObj['vph'] = sizes[0][1];
+ }
+ return sizeObj;
+ }
+
+ function _buildQueryParameters(bid, requestParams) {
+ Object.keys(bid.params).filter(param => param !== 'video')
+ .forEach(param => requestParams[param] = bid.params[param]);
+
+ if (bid.mediaType === 'video') {
+ let videoDimensions = _getFirstSize(bid.sizes);
+ Object.keys(videoDimensions).forEach(param => requestParams[param] = videoDimensions[param]);
+ Object.keys(bid.params.video || {}).forEach(param => requestParams[param] = bid.params.video[param]);
+ }
+
+ return utils.parseQueryStringParameters(requestParams);
+ }
+
+ function _handleResponse(bidRequest) {
+ return response => {
+ if (!response && response.length <= 0) {
+ bidmanager.addBidResponse(bidRequest.placementCode, bidfactory.createBid(STATUS.NO_BID, bidRequest));
+ utils.logError('Empty response');
+ return;
+ }
+
+ try {
+ response = JSON.parse(response);
+ } catch (e) {
+ bidmanager.addBidResponse(bidRequest.placementCode, bidfactory.createBid(STATUS.NO_BID, bidRequest));
+ utils.logError('Invalid JSON in response');
+ return;
+ }
+
+ _createBid(bidRequest, response);
+ };
+ }
+
+ function _callBids(bidRequests) {
+ let currentUrl = (window.parent !== window) ? document.referrer : window.location.href;
+ currentUrl = currentUrl && encodeURIComponent(currentUrl);
+
+ let bids = bidRequests.bids || [];
+ bids.forEach(bid => {
+ if (utils.hasValidBidRequest(bid.params, REQUIRED_PARAMS, BIDDER_CODE)) {
+ let env = ENVIRONMENTS[bid.params['env']] || ENVIRONMENTS['prod'];
+ let requestPath = bid.mediaType === 'video' ? VIDEO_PATH : BANNER_PATH;
+ let pageParameters = {url: currentUrl};
+ let parameterStr = _buildQueryParameters(bid, pageParameters);
+
+ let url = `//${env}${requestPath}${parameterStr}`;
+ utils.logMessage('sending request to: ' + url);
+ ajax(url, _handleResponse(bid), null, {withCredentials: true});
+ } else {
+ bidmanager.addBidResponse(bid.placementCode, bidfactory.createBid(STATUS.NO_BID, bid));
+ }
+ });
+ }
+
+ return {
+ callBids: _callBids
+ }
+};
+
+adaptermanager.registerBidAdapter(new AerServAdapter(), BIDDER_CODE, {supportedMediaTypes: ['video']});
+
+module.exports = AerServAdapter;
diff --git a/modules/appnexusAstBidAdapter.js b/modules/appnexusAstBidAdapter.js
index 80e2472e9ef..5ca2178d0a4 100644
--- a/modules/appnexusAstBidAdapter.js
+++ b/modules/appnexusAstBidAdapter.js
@@ -25,6 +25,7 @@ const NATIVE_MAPPING = {
},
sponsoredBy: 'sponsored_by'
};
+const SOURCE = 'pbjs';
/**
* Bidder adapter for /ut endpoint. Given the list of all ad unit tag IDs,
@@ -142,7 +143,14 @@ function AppnexusAstAdapter() {
});
if (!utils.isEmpty(tags)) {
- const payloadJson = {tags: [...tags], user: userObj};
+ const payloadJson = {
+ tags: [...tags],
+ user: userObj,
+ sdk: {
+ source: SOURCE,
+ version: '$prebid.version$'
+ }
+ };
if (member > 0) {
payloadJson.member_id = member;
}
diff --git a/modules/currency.js b/modules/currency.js
new file mode 100644
index 00000000000..1d0286ed569
--- /dev/null
+++ b/modules/currency.js
@@ -0,0 +1,255 @@
+import bidfactory from 'src/bidfactory';
+import { STATUS } from 'src/constants';
+import { ajax } from 'src/ajax';
+import * as utils from 'src/utils';
+import bidmanager from 'src/bidmanager';
+import { config } from 'src/config';
+
+const DEFAULT_CURRENCY_RATE_URL = 'http://currency.prebid.org/latest.json';
+const CURRENCY_RATE_PRECISION = 4;
+
+var bidResponseQueue = [];
+var conversionCache = {};
+var currencyRatesLoaded = false;
+var adServerCurrency = 'USD';
+
+// Used as reference to the original bidmanager.addBidResponse
+var originalBidResponse;
+
+export var currencySupportEnabled = false;
+export var currencyRates = {};
+var bidderCurrencyDefault = {};
+
+/**
+ * Configuration function for currency
+ * @param {string} [config.adServerCurrency = 'USD']
+ * ISO 4217 3-letter currency code that represents the target currency. (e.g. 'EUR'). If this value is present,
+ * the currency conversion feature is activated.
+ * @param {number} [config.granularityMultiplier = 1]
+ * A decimal value representing how mcuh to scale the price granularity calculations.
+ * @param {object} config.bidderCurrencyDefault
+ * An optional argument to specify bid currencies for bid adapters. This option is provided for the transitional phase
+ * before every bid adapter will specify its own bid currency. If the adapter specifies a bid currency, this value is
+ * ignored for that bidder.
+ *
+ * example:
+ * {
+ * rubicon: 'USD'
+ * }
+ * @param {string} [config.conversionRateFile = 'http://currency.prebid.org/latest.json']
+ * Optional path to a file containing currency conversion data. Prebid.org hosts a file that is used as the default,
+ * if not specified.
+ * @param {object} [config.rates]
+ * This optional argument allows you to specify the rates with a JSON object, subverting the need for a external
+ * config.conversionRateFile parameter. If this argument is specified, the conversion rate file will not be loaded.
+ *
+ * example:
+ * {
+ * 'GBP': { 'CNY': 8.8282, 'JPY': 141.7, 'USD': 1.2824 },
+ * 'USD': { 'CNY': 6.8842, 'GBP': 0.7798, 'JPY': 110.49 }
+ * }
+ */
+export function setConfig(config) {
+ let url = DEFAULT_CURRENCY_RATE_URL;
+
+ if (typeof config.rates === 'object') {
+ currencyRates.conversions = config.rates;
+ currencyRatesLoaded = true;
+ }
+
+ if (typeof config.adServerCurrency === 'string') {
+ utils.logInfo('enabling currency support', arguments);
+
+ adServerCurrency = config.adServerCurrency;
+ if (config.conversionRateFile) {
+ utils.logInfo('currency using override conversionRateFile:', config.conversionRateFile);
+ url = config.conversionRateFile;
+ }
+ initCurrency(url);
+ } else {
+ // currency support is disabled, setting defaults
+ utils.logInfo('disabling currency support');
+ resetCurrency();
+ }
+ if (typeof config.bidderCurrencyDefault === 'object') {
+ bidderCurrencyDefault = config.bidderCurrencyDefault;
+ }
+}
+config.getConfig('currency', config => setConfig(config.currency));
+
+function initCurrency(url) {
+ conversionCache = {};
+ currencySupportEnabled = true;
+
+ if (!originalBidResponse) {
+ utils.logInfo('Installing addBidResponse decorator for currency module', arguments);
+
+ originalBidResponse = bidmanager.addBidResponse;
+ bidmanager.addBidResponse = addBidResponseDecorator(bidmanager.addBidResponse);
+ }
+
+ if (!currencyRates.conversions) {
+ ajax(url, function (response) {
+ try {
+ currencyRates = JSON.parse(response);
+ utils.logInfo('currencyRates set to ' + JSON.stringify(currencyRates));
+ currencyRatesLoaded = true;
+ processBidResponseQueue();
+ } catch (e) {
+ utils.logError('failed to parse currencyRates response: ' + response);
+ }
+ });
+ }
+}
+
+function resetCurrency() {
+ if (originalBidResponse) {
+ utils.logInfo('Uninstalling addBidResponse decorator for currency module', arguments);
+
+ bidmanager.addBidResponse = originalBidResponse;
+ originalBidResponse = undefined;
+ }
+
+ adServerCurrency = 'USD';
+ conversionCache = {};
+ currencySupportEnabled = false;
+ currencyRatesLoaded = false;
+ currencyRates = {};
+ bidderCurrencyDefault = {};
+}
+
+export function addBidResponseDecorator(fn) {
+ return function(adUnitCode, bid) {
+ if (!bid) {
+ return fn.apply(this, arguments); // if no bid, call original and let it display warnings
+ }
+
+ let bidder = bid.bidderCode || bid.bidder;
+ if (bidderCurrencyDefault[bidder]) {
+ let currencyDefault = bidderCurrencyDefault[bidder];
+ if (bid.currency && currencyDefault !== bid.currency) {
+ utils.logWarn(`Currency default '${bidder}: ${currencyDefault}' ignored. adapter specified '${bid.currency}'`);
+ } else {
+ bid.currency = currencyDefault;
+ }
+ }
+
+ // default to USD if currency not set
+ if (!bid.currency) {
+ utils.logWarn('Currency not specified on bid. Defaulted to "USD"');
+ bid.currency = 'USD';
+ }
+
+ // execute immediately if the bid is already in the desired currency
+ if (bid.currency === adServerCurrency) {
+ return fn.apply(this, arguments);
+ }
+
+ bidResponseQueue.push(wrapFunction(fn, this, arguments));
+ if (!currencySupportEnabled || currencyRatesLoaded) {
+ processBidResponseQueue();
+ }
+ }
+}
+
+function processBidResponseQueue() {
+ while (bidResponseQueue.length > 0) {
+ (bidResponseQueue.shift())();
+ }
+}
+
+function wrapFunction(fn, context, params) {
+ return function() {
+ var bid = params[1];
+ if (bid !== undefined && 'currency' in bid && 'cpm' in bid) {
+ var fromCurrency = bid.currency;
+ try {
+ var conversion = getCurrencyConversion(fromCurrency);
+ bid.originalCpm = bid.cpm;
+ bid.originalCurrency = bid.currency;
+ if (conversion !== 1) {
+ bid.cpm = (parseFloat(bid.cpm) * conversion).toFixed(4);
+ bid.currency = adServerCurrency;
+ }
+ } catch (e) {
+ utils.logWarn('Returning NO_BID, getCurrencyConversion threw error: ', e);
+ params[1] = bidfactory.createBid(STATUS.NO_BID, {
+ bidder: bid.bidderCode || bid.bidder,
+ bidId: bid.adId
+ });
+ }
+ }
+ return fn.apply(context, params);
+ };
+}
+
+function getCurrencyConversion(fromCurrency) {
+ var conversionRate = null;
+ var rates;
+
+ if (fromCurrency in conversionCache) {
+ conversionRate = conversionCache[fromCurrency];
+ utils.logMessage('Using conversionCache value ' + conversionRate + ' for fromCurrency ' + fromCurrency);
+ } else if (currencySupportEnabled === false) {
+ if (fromCurrency === 'USD') {
+ conversionRate = 1;
+ } else {
+ throw new Error('Prebid currency support has not been enabled and fromCurrency is not USD');
+ }
+ } else if (fromCurrency === adServerCurrency) {
+ conversionRate = 1;
+ } else {
+ var toCurrency = adServerCurrency;
+
+ if (fromCurrency in currencyRates.conversions) {
+ // using direct conversion rate from fromCurrency to toCurrency
+ rates = currencyRates.conversions[fromCurrency];
+ if (!(toCurrency in rates)) {
+ // bid should fail, currency is not supported
+ throw new Error('Specified adServerCurrency in config \'' + toCurrency + '\' not found in the currency rates file');
+ }
+ conversionRate = rates[toCurrency];
+ utils.logInfo('getCurrencyConversion using direct ' + fromCurrency + ' to ' + toCurrency + ' conversionRate ' + conversionRate);
+ } else if (toCurrency in currencyRates.conversions) {
+ // using reciprocal of conversion rate from toCurrency to fromCurrency
+ rates = currencyRates.conversions[toCurrency];
+ if (!(fromCurrency in rates)) {
+ // bid should fail, currency is not supported
+ throw new Error('Specified fromCurrency \'' + fromCurrency + '\' not found in the currency rates file');
+ }
+ conversionRate = roundFloat(1 / rates[fromCurrency], CURRENCY_RATE_PRECISION);
+ utils.logInfo('getCurrencyConversion using reciprocal ' + fromCurrency + ' to ' + toCurrency + ' conversionRate ' + conversionRate);
+ } else {
+ // first defined currency base used as intermediary
+ var anyBaseCurrency = Object.keys(currencyRates.conversions)[0];
+
+ if (!(fromCurrency in currencyRates.conversions[anyBaseCurrency])) {
+ // bid should fail, currency is not supported
+ throw new Error('Specified fromCurrency \'' + fromCurrency + '\' not found in the currency rates file');
+ }
+ var toIntermediateConversionRate = 1 / currencyRates.conversions[anyBaseCurrency][fromCurrency];
+
+ if (!(toCurrency in currencyRates.conversions[anyBaseCurrency])) {
+ // bid should fail, currency is not supported
+ throw new Error('Specified adServerCurrency in config \'' + toCurrency + '\' not found in the currency rates file');
+ }
+ var fromIntermediateConversionRate = currencyRates.conversions[anyBaseCurrency][toCurrency];
+
+ conversionRate = roundFloat(toIntermediateConversionRate * fromIntermediateConversionRate, CURRENCY_RATE_PRECISION);
+ utils.logInfo('getCurrencyConversion using intermediate ' + fromCurrency + ' thru ' + anyBaseCurrency + ' to ' + toCurrency + ' conversionRate ' + conversionRate);
+ }
+ }
+ if (!(fromCurrency in conversionCache)) {
+ utils.logMessage('Adding conversionCache value ' + conversionRate + ' for fromCurrency ' + fromCurrency);
+ conversionCache[fromCurrency] = conversionRate;
+ }
+ return conversionRate;
+}
+
+function roundFloat(num, dec) {
+ var d = 1;
+ for (let i = 0; i < dec; i++) {
+ d += '0';
+ }
+ return Math.round(num * d) / d;
+}
diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js
new file mode 100644
index 00000000000..51fb61b03e2
--- /dev/null
+++ b/modules/improvedigitalBidAdapter.js
@@ -0,0 +1,384 @@
+const LIB_VERSION_GLOBAL = '3.0.5';
+
+const CONSTANTS = require('src/constants');
+const utils = require('src/utils');
+const bidfactory = require('src/bidfactory');
+const bidmanager = require('src/bidmanager');
+const adloader = require('src/adloader');
+const Adapter = require('src/adapter').default;
+const adaptermanager = require('src/adaptermanager');
+
+const IMPROVE_DIGITAL_BIDDER_CODE = 'improvedigital';
+
+const ImproveDigitalAdapter = function () {
+ let baseAdapter = new Adapter(IMPROVE_DIGITAL_BIDDER_CODE);
+ baseAdapter.idClient = new ImproveDigitalAdServerJSClient('hb');
+
+ const LIB_VERSION = LIB_VERSION_GLOBAL;
+
+ // Ad server needs to implement JSONP using this function as the callback
+ const CALLBACK_FUNCTION = '$$PREBID_GLOBAL$$' + '.improveDigitalResponse';
+
+ baseAdapter.getNormalizedBidRequest = function(bid) {
+ let adUnitId = utils.getBidIdParameter('placementCode', bid) || null;
+ let placementId = utils.getBidIdParameter('placementId', bid.params) || null;
+ let publisherId = null;
+ let placementKey = null;
+
+ if (placementId === null) {
+ publisherId = utils.getBidIdParameter('publisherId', bid.params) || null;
+ placementKey = utils.getBidIdParameter('placementKey', bid.params) || null;
+ }
+ let keyValues = utils.getBidIdParameter('keyValues', bid.params) || null;
+ let localSize = utils.getBidIdParameter('size', bid.params) || null;
+ let bidId = utils.getBidIdParameter('bidId', bid);
+
+ let normalizedBidRequest = {};
+ if (placementId) {
+ normalizedBidRequest.placementId = placementId;
+ } else {
+ if (publisherId) {
+ normalizedBidRequest.publisherId = publisherId;
+ }
+ if (placementKey) {
+ normalizedBidRequest.placementKey = placementKey;
+ }
+ }
+
+ if (keyValues) {
+ normalizedBidRequest.keyValues = keyValues;
+ }
+ if (localSize && localSize.w && localSize.h) {
+ normalizedBidRequest.size = {};
+ normalizedBidRequest.size.h = localSize.h;
+ normalizedBidRequest.size.w = localSize.w;
+ }
+ if (bidId) {
+ normalizedBidRequest.id = bidId;
+ }
+ if (adUnitId) {
+ normalizedBidRequest.adUnitId = adUnitId;
+ }
+ return normalizedBidRequest;
+ }
+
+ let submitNoBidResponse = function(bidRequest) {
+ let bid = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bidRequest);
+ bid.bidderCode = IMPROVE_DIGITAL_BIDDER_CODE;
+ bidmanager.addBidResponse(bidRequest.placementCode, bid);
+ };
+
+ $$PREBID_GLOBAL$$.improveDigitalResponse = function(response) {
+ let bidRequests = utils.getBidderRequestAllAdUnits(IMPROVE_DIGITAL_BIDDER_CODE);
+ if (bidRequests && bidRequests.bids && bidRequests.bids.length > 0) {
+ utils._each(bidRequests.bids, function (bidRequest) {
+ let bidObjects = response.bid || [];
+ utils._each(bidObjects, function (bidObject) {
+ if (bidObject.id === bidRequest.bidId) {
+ if (!bidObject.price || bidObject.price === null) {
+ submitNoBidResponse(bidRequest);
+ return;
+ }
+ if (bidObject.errorCode && bidObject.errorCode !== 0) {
+ submitNoBidResponse(bidRequest);
+ return;
+ }
+ if (!bidObject.adm || bidObject.adm === null || typeof bidObject.adm !== 'string') {
+ submitNoBidResponse(bidRequest);
+ return;
+ }
+
+ let bid = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bidRequest);
+
+ let syncString = '';
+ let syncArray = (bidObject.sync && bidObject.sync.length > 0) ? bidObject.sync : [];
+
+ utils._each(syncArray, function (syncElement) {
+ let syncInd = syncElement.replace(/\//g, '\\\/');
+ syncString = `${syncString}${(syncString === '') ? 'document.writeln(\"' : ''} `;
+ });
+ syncString = `${syncString}${(syncString === '') ? '' : '\")'}`;
+
+ let nurl = '';
+ if (bidObject.nurl && bidObject.nurl.length > 0) {
+ nurl = ` `;
+ }
+ bid.ad = `${nurl}`;
+ bid.bidderCode = IMPROVE_DIGITAL_BIDDER_CODE;
+ bid.cpm = parseFloat(bidObject.price);
+ bid.width = bidObject.w;
+ bid.height = bidObject.h;
+
+ bidmanager.addBidResponse(bidRequest.placementCode, bid);
+ }
+ });
+ });
+ }
+ };
+
+ baseAdapter.callBids = function (params) {
+ // params will contain an array
+ let bidRequests = params.bids || [];
+ let loc = utils.getTopWindowLocation();
+ let requestParameters = {
+ singleRequestMode: false,
+ httpRequestType: this.idClient.CONSTANTS.HTTP_REQUEST_TYPE.GET,
+ callback: CALLBACK_FUNCTION,
+ secure: (loc.protocol === 'https:') ? 1 : 0,
+ libVersion: this.LIB_VERSION
+ };
+
+ let normalizedBids = bidRequests.map((bidRequest) => {
+ let normalizedBidRequest = this.getNormalizedBidRequest(bidRequest);
+ if (bidRequest.params && bidRequest.params.singleRequest) {
+ requestParameters.singleRequestMode = true;
+ }
+ return normalizedBidRequest;
+ });
+
+ let request = this.idClient.createRequest(
+ normalizedBids, // requestObject
+ requestParameters
+ );
+
+ if (request.errors && request.errors.length > 0) {
+ utils.logError('ID WARNING 0x01');
+ }
+
+ if (request && request.requests && request.requests[0]) {
+ utils._each(request.requests, function (requestElement) {
+ if (requestElement.url) {
+ adloader.loadScript(requestElement.url, null);
+ }
+ });
+ }
+ }
+
+ // Export the callBids function, so that prebid.js can execute this function
+ // when the page asks to send out bid requests.
+ return Object.assign(this, {
+ LIB_VERSION: LIB_VERSION,
+ idClient: baseAdapter.idClient,
+ getNormalizedBidRequest: baseAdapter.getNormalizedBidRequest,
+ callBids: baseAdapter.callBids
+ });
+};
+
+ImproveDigitalAdapter.createNew = function () {
+ return new ImproveDigitalAdapter();
+};
+
+adaptermanager.registerBidAdapter(new ImproveDigitalAdapter(), IMPROVE_DIGITAL_BIDDER_CODE);
+
+module.exports = ImproveDigitalAdapter;
+
+function ImproveDigitalAdServerJSClient(endPoint) {
+ this.CONSTANTS = {
+ HTTP_REQUEST_TYPE: {
+ GET: 0,
+ POST: 1
+ },
+ HTTP_SECURITY: {
+ STANDARD: 0,
+ SECURE: 1
+ },
+ AD_SERVER_BASE_URL: 'ad.360yield.com',
+ END_POINT: endPoint || 'hb',
+ AD_SERVER_URL_PARAM: '?jsonp=',
+ CLIENT_VERSION: 'JS-4.0.2',
+ MAX_URL_LENGTH: 2083,
+ ERROR_CODES: {
+ BAD_HTTP_REQUEST_TYPE_PARAM: 1,
+ MISSING_PLACEMENT_PARAMS: 2,
+ LIB_VERSION_MISSING: 3
+ }
+ };
+
+ this.getErrorReturn = function(errorCode) {
+ return {
+ idMappings: {},
+ requests: {},
+ 'errorCode': errorCode
+ };
+ };
+
+ this.createRequest = function(requestObject, requestParameters, extraRequestParameters) {
+ if (requestParameters.httpRequestType !== this.CONSTANTS.HTTP_REQUEST_TYPE.GET) {
+ return this.getErrorReturn(this.CONSTANTS.ERROR_CODES.BAD_HTTP_REQUEST_TYPE_PARAM);
+ }
+ if (!requestParameters.libVersion) {
+ return this.getErrorReturn(this.CONSTANTS.ERROR_CODES.LIB_VERSION_MISSING);
+ }
+
+ let impressionObjects = [];
+ let impressionObject;
+ let counter;
+ if (utils.isArray(requestObject)) {
+ for (counter = 0; counter < requestObject.length; counter++) {
+ impressionObject = this.createImpressionObject(requestObject[counter]);
+ impressionObjects.push(impressionObject);
+ }
+ } else {
+ impressionObject = this.createImpressionObject(requestObject);
+ impressionObjects.push(impressionObject);
+ }
+
+ let returnObject = {};
+ returnObject.idMappings = [];
+ returnObject.requests = [];
+ let errors = null;
+
+ let baseUrl = `${(requestParameters.secure === 1 ? 'https' : 'http')}://${this.CONSTANTS.AD_SERVER_BASE_URL}/${this.CONSTANTS.END_POINT}${this.CONSTANTS.AD_SERVER_URL_PARAM}`;
+
+ let bidRequestObject = {
+ bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters)
+ };
+ for (counter = 0; counter < impressionObjects.length; counter++) {
+ impressionObject = impressionObjects[counter];
+
+ if (impressionObject.errorCode) {
+ errors = errors || [];
+ errors.push({
+ errorCode: impressionObject.errorCode,
+ adUnitId: impressionObject.adUnitId
+ });
+ } else {
+ returnObject.idMappings.push({
+ adUnitId: impressionObject.adUnitId,
+ id: impressionObject.impressionObject.id
+ });
+ bidRequestObject.bid_request.imp = bidRequestObject.bid_request.imp || [];
+
+ bidRequestObject.bid_request.imp.push(impressionObject.impressionObject);
+ let outputUri = encodeURIComponent(baseUrl + JSON.stringify(bidRequestObject));
+
+ if (!requestParameters.singleRequestMode) {
+ returnObject.requests.push({
+ url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject))
+ });
+ bidRequestObject = {
+ bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters)
+ };
+ }
+
+ if (outputUri.length > this.CONSTANTS.MAX_URL_LENGTH) {
+ if (bidRequestObject.bid_request.imp.length > 1) {
+ bidRequestObject.bid_request.imp.pop();
+ returnObject.requests.push({
+ url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject))
+ });
+ bidRequestObject = {
+ bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters)
+ };
+ bidRequestObject.bid_request.imp = [];
+ bidRequestObject.bid_request.imp.push(impressionObject.impressionObject);
+ } else {
+ // We have a problem. Single request is too long for a URI
+ }
+ }
+ }
+ }
+ if (bidRequestObject.bid_request &&
+ bidRequestObject.bid_request.imp &&
+ bidRequestObject.bid_request.imp.length > 0) {
+ returnObject.requests = returnObject.requests || [];
+ returnObject.requests.push({
+ url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject))
+ });
+ }
+
+ if (errors) {
+ returnObject.errors = errors;
+ }
+
+ return returnObject;
+ };
+
+ this.createBasicBidRequestObject = function(requestParameters, extraRequestParameters) {
+ let impressionBidRequestObject = {};
+ if (requestParameters.requestId) {
+ impressionBidRequestObject.id = requestParameters.requestId;
+ } else {
+ impressionBidRequestObject.id = utils.getUniqueIdentifierStr();
+ }
+ if (requestParameters.domain) {
+ impressionBidRequestObject.domain = requestParameters.domain;
+ }
+ if (requestParameters.page) {
+ impressionBidRequestObject.page = requestParameters.page;
+ }
+ if (requestParameters.ref) {
+ impressionBidRequestObject.ref = requestParameters.ref;
+ }
+ if (requestParameters.callback) {
+ impressionBidRequestObject.callback = requestParameters.callback;
+ }
+ if ('secure' in requestParameters) {
+ impressionBidRequestObject.secure = requestParameters.secure;
+ }
+ if (requestParameters.libVersion) {
+ impressionBidRequestObject.version = requestParameters.libVersion + '-' + this.CONSTANTS.CLIENT_VERSION;
+ }
+ if (extraRequestParameters) {
+ for (let prop in extraRequestParameters) {
+ impressionBidRequestObject[prop] = extraRequestParameters[prop];
+ }
+ }
+
+ return impressionBidRequestObject;
+ };
+
+ this.createImpressionObject = function(placementObject) {
+ let outputObject = {};
+ let impressionObject = {};
+ outputObject.impressionObject = impressionObject;
+
+ if (placementObject.id) {
+ impressionObject.id = placementObject.id;
+ } else {
+ impressionObject.id = utils.getUniqueIdentifierStr();
+ }
+ if (placementObject.adUnitId) {
+ outputObject.adUnitId = placementObject.adUnitId;
+ }
+ if (placementObject.placementId) {
+ impressionObject.pid = placementObject.placementId;
+ }
+ if (placementObject.publisherId) {
+ impressionObject.pubid = placementObject.publisherId;
+ }
+ if (placementObject.placementKey) {
+ impressionObject.pkey = placementObject.placementKey;
+ }
+ if (placementObject.transactionId) {
+ impressionObject.tid = placementObject.transactionId;
+ }
+ if (placementObject.keyValues) {
+ for (let key in placementObject.keyValues) {
+ for (let valueCounter = 0; valueCounter < placementObject.keyValues[key].length; valueCounter++) {
+ impressionObject.kvw = impressionObject.kvw || {};
+ impressionObject.kvw[key] = impressionObject.kvw[key] || [];
+ impressionObject.kvw[key].push(placementObject.keyValues[key][valueCounter]);
+ }
+ }
+ }
+ if (placementObject.size && placementObject.size.w && placementObject.size.h) {
+ impressionObject.banner = {};
+ impressionObject.banner.w = placementObject.size.w;
+ impressionObject.banner.h = placementObject.size.h;
+ } else {
+ impressionObject.banner = {};
+ }
+
+ if (!impressionObject.pid &&
+ !impressionObject.pubid &&
+ !impressionObject.pkey &&
+ !(impressionObject.banner && impressionObject.banner.w && impressionObject.banner.h)) {
+ outputObject.impressionObject = null;
+ outputObject.errorCode = this.CONSTANTS.ERROR_CODES.MISSING_PLACEMENT_PARAMS;
+ }
+ return outputObject;
+ };
+}
+
+exports.ImproveDigitalAdServerJSClient = ImproveDigitalAdServerJSClient;
diff --git a/modules/indexExchangeBidAdapter.js b/modules/indexExchangeBidAdapter.js
index 3f1984f82df..352613b4887 100644
--- a/modules/indexExchangeBidAdapter.js
+++ b/modules/indexExchangeBidAdapter.js
@@ -269,11 +269,24 @@ var cygnus_index_start = function () {
this.siteID = siteID;
this.impressions = [];
this._parseFnName = undefined;
+
+ // Get page URL
+ this.sitePage = undefined;
+ try {
+ this.sitePage = utils.getTopWindowUrl();
+ } catch (e) {}
+ // Fallback to old logic if utils.getTopWindowUrl() fails to return site.page
+ if (typeof this.sitePage === 'undefined' || this.sitePage === '') {
+ if (top === self) {
+ this.sitePage = location.href;
+ } else {
+ this.sitePage = document.referrer;
+ }
+ }
+
if (top === self) {
- this.sitePage = location.href;
this.topframe = 1;
} else {
- this.sitePage = document.referrer;
this.topframe = 0;
}
diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js
index cd45d6fae40..d59a21c3643 100644
--- a/modules/rubiconBidAdapter.js
+++ b/modules/rubiconBidAdapter.js
@@ -441,7 +441,7 @@ function syncEmily(hasSynced) {
}
const defaultUserSyncConfig = {
- enabled: true,
+ enabled: false,
delay: 5000
};
const iframeUrl = 'https://tap-secure.rubiconproject.com/partner/scripts/rubicon/emily.html?rtb_ext=1';
diff --git a/modules/serverbidBidAdapter.js b/modules/serverbidBidAdapter.js
index 04f2e9964a9..7718e30e55f 100644
--- a/modules/serverbidBidAdapter.js
+++ b/modules/serverbidBidAdapter.js
@@ -8,9 +8,12 @@ import adaptermanager from 'src/adaptermanager';
const ServerBidAdapter = function ServerBidAdapter() {
const baseAdapter = new Adapter('serverbid');
- const BASE_URI = '//e.serverbid.com/api/v2';
+ const SERVERBID_BASE_URI = 'https://e.serverbid.com/api/v2';
+ const SMARTSYNC_BASE_URI = 'https://s.zkcdn.net/ss';
+ const SMARTSYNC_CALLBACK = 'serverbidCallBids';
- const sizeMap = [null,
+ const sizeMap = [
+ null,
'120x90',
'120x90',
'468x60',
@@ -45,44 +48,67 @@ const ServerBidAdapter = function ServerBidAdapter() {
baseAdapter.callBids = function(params) {
if (params && params.bids && utils.isArray(params.bids) && params.bids.length) {
- const data = {
- placements: [],
- time: Date.now(),
- user: {},
- url: utils.getTopWindowUrl(),
- referrer: document.referrer,
- enableBotFiltering: true,
- includePricingData: true
- };
-
- const bids = params.bids || [];
- for (let i = 0; i < bids.length; i++) {
- const bid = bids[i];
-
- bidIds.push(bid.bidId);
-
- const bid_data = {
- networkId: bid.params.networkId,
- siteId: bid.params.siteId,
- zoneIds: bid.params.zoneIds,
- campaignId: bid.params.campaignId,
- flightId: bid.params.flightId,
- adId: bid.params.adId,
- divName: bid.bidId,
- adTypes: bid.adTypes || getSize(bid.sizes)
+ if (!window.SMARTSYNC) {
+ _callBids(params);
+ } else {
+ window[SMARTSYNC_CALLBACK] = function() {
+ window[SMARTSYNC_CALLBACK] = function() {};
+ _callBids(params);
};
- if (bid_data.networkId && bid_data.siteId) {
- data.placements.push(bid_data);
- }
- }
+ const siteId = params.bids[0].params.siteId;
+ _appendScript(SMARTSYNC_BASE_URI + '/' + siteId + '.js');
- if (data.placements.length) {
- ajax(BASE_URI, _responseCallback, JSON.stringify(data), { method: 'POST', withCredentials: true, contentType: 'application/json' });
+ const sstimeout = window.SMARTSYNC_TIMEOUT || ((params.timeout || 500) / 2);
+ setTimeout(function() {
+ var cb = window[SMARTSYNC_CALLBACK];
+ window[SMARTSYNC_CALLBACK] = function() {};
+ cb();
+ }, sstimeout);
}
}
};
+ function _appendScript(src) {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = src;
+ document.getElementsByTagName('head')[0].appendChild(script);
+ }
+
+ function _callBids(params) {
+ const data = {
+ placements: [],
+ time: Date.now(),
+ user: {},
+ url: utils.getTopWindowUrl(),
+ referrer: document.referrer,
+ enableBotFiltering: true,
+ includePricingData: true
+ };
+
+ const bids = params.bids || [];
+
+ for (let i = 0; i < bids.length; i++) {
+ const bid = bids[i];
+
+ bidIds.push(bid.bidId);
+
+ const placement = Object.assign({
+ divName: bid.bidId,
+ adTypes: bid.adTypes || getSize(bid.sizes)
+ }, bid.params);
+
+ if (placement.networkId && placement.siteId) {
+ data.placements.push(placement);
+ }
+ }
+
+ if (data.placements.length) {
+ ajax(SERVERBID_BASE_URI, _responseCallback, JSON.stringify(data), { method: 'POST', withCredentials: true, contentType: 'application/json' });
+ }
+ }
+
function _responseCallback(result) {
let bid;
let bidId;
diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js
index 21d1561192a..d53fb0d92db 100644
--- a/modules/sharethroughBidAdapter.js
+++ b/modules/sharethroughBidAdapter.js
@@ -48,6 +48,20 @@ var SharethroughAdapter = function SharethroughAdapter() {
function _strcallback(bidObj, bidResponse) {
try {
bidResponse = JSON.parse(bidResponse);
+ } catch (e) {
+ _handleInvalidBid(bidObj);
+ return;
+ }
+
+ if (bidResponse.creatives && bidResponse.creatives.length > 0) {
+ _handleBid(bidObj, bidResponse);
+ } else {
+ _handleInvalidBid(bidObj);
+ }
+ }
+
+ function _handleBid(bidObj, bidResponse) {
+ try {
const bidId = bidResponse.bidId;
const bid = bidfactory.createBid(1, bidObj);
bid.bidderCode = STR_BIDDER_CODE;
@@ -91,6 +105,7 @@ var SharethroughAdapter = function SharethroughAdapter() {
function _handleInvalidBid(bidObj) {
const bid = bidfactory.createBid(2, bidObj);
+ bid.bidderCode = STR_BIDDER_CODE;
bidmanager.addBidResponse(bidObj.placementCode, bid);
}
diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js
index 3f8a028a947..e8ed973fe5f 100644
--- a/modules/spotxBidAdapter.js
+++ b/modules/spotxBidAdapter.js
@@ -57,8 +57,9 @@ function Spotx() {
bid.url = adServerKVPs.spotx_ad_key;
bid.cur = 'USD';
bid.bidderCode = 'spotx';
- bid.height = bidReq.sizes[0][1];
- bid.width = bidReq.sizes[0][0];
+ var sizes = utils.isArray(bidReq.sizes[0]) ? bidReq.sizes[0] : bidReq.sizes;
+ bid.height = sizes[1];
+ bid.width = sizes[0];
resp.bids.push(bid);
KVP_Object = adServerKVPs;
handleResponse(resp);
@@ -86,8 +87,9 @@ function Spotx() {
bid.vastUrl = url;
bid.ad = url;
- bid.width = bidReq.sizes[0][0];
- bid.height = bidReq.sizes[0][1];
+ var sizes = utils.isArray(bidReq.sizes[0]) ? bidReq.sizes[0] : bidReq.sizes;
+ bid.height = sizes[1];
+ bid.width = sizes[0];
}
return bid;
diff --git a/modules/springserveBidAdapter.js b/modules/springserveBidAdapter.js
index f84229b8fd6..d54702a230f 100644
--- a/modules/springserveBidAdapter.js
+++ b/modules/springserveBidAdapter.js
@@ -111,6 +111,6 @@ SpringServeAdapter = function SpringServeAdapter() {
};
};
-adaptermanager.registerBidAdapter(new SpringServeAdapter(), 'springserveg');
+adaptermanager.registerBidAdapter(new SpringServeAdapter(), 'springserve');
module.exports = SpringServeAdapter;
diff --git a/modules/yieldbotBidAdapter.js b/modules/yieldbotBidAdapter.js
index 82858827af1..4f874a82502 100644
--- a/modules/yieldbotBidAdapter.js
+++ b/modules/yieldbotBidAdapter.js
@@ -23,7 +23,6 @@ function YieldbotAdapter() {
AVAILABLE: 1,
EMPTY: 2
},
- definedSlots: [],
pageLevelOption: false,
/**
* Builds the Yieldbot creative tag.
@@ -71,6 +70,29 @@ function YieldbotAdapter() {
bid.bidderCode = 'yieldbot';
return bid;
},
+ /**
+ * Unique'ify slot sizes for a Yieldbot bid request
+ * Bids may refer to a slot and dimension multiple times on a page, but should exist once in the request.
+ * @param {Array} sizes An array of sizes to deduplicate
+ * @private
+ */
+ getUniqueSlotSizes: function(sizes) {
+ var newSizes = [];
+ var hasSize = {};
+ if (utils.isArray(sizes)) {
+ for (var idx = 0; idx < sizes.length; idx++) {
+ var bidSize = sizes[idx] || '';
+ if (bidSize && utils.isStr(bidSize) && !hasSize[bidSize]) {
+ var nSize = bidSize.split('x');
+ if (nSize.length > 1) {
+ newSizes.push([nSize[0], nSize[1]]);
+ }
+ hasSize[bidSize] = true;
+ }
+ }
+ }
+ return newSizes;
+ },
/**
* Yieldbot implementation of {@link module:adaptermanger.callBids}
* @param {Object} params - Adapter bid configuration object
@@ -84,8 +106,9 @@ function YieldbotAdapter() {
ybotq.push(function () {
var yieldbot = window.yieldbot;
- // Empty defined slots bidId array
- ybotlib.definedSlots = [];
+ // Empty defined slot bids object
+ ybotlib.bids = {};
+ ybotlib.parsedBidSizes = {};
// Iterate through bids to obtain Yieldbot slot config
// - Slot config can be different between initial and refresh requests
var psn = 'ERROR_PREBID_DEFINE_YB_PSN';
@@ -94,23 +117,33 @@ function YieldbotAdapter() {
var bid = v;
// bidder params config: http://prebid.org/dev-docs/bidders/yieldbot.html
// - last psn wins
- psn = (bid.params && bid.params.psn) || psn;
- var slotName = (bid.params && bid.params.slot) || 'ERROR_PREBID_DEFINE_YB_SLOT';
-
- slots[slotName] = bid.sizes || [];
- ybotlib.definedSlots.push(bid.bidId);
+ psn = bid.params && bid.params.psn ? bid.params.psn : psn;
+ var slotName = bid.params && bid.params.slot ? bid.params.slot : 'ERROR_PREBID_DEFINE_YB_SLOT';
+ var parsedSizes = utils.parseSizesInput(bid.sizes) || [];
+ slots[slotName] = slots[slotName] || [];
+ slots[slotName] = slots[slotName].concat(parsedSizes);
+ ybotlib.bids[bid.bidId] = bid;
+ ybotlib.parsedBidSizes[bid.bidId] = parsedSizes;
});
+ for (var bidSlots in slots) {
+ if (slots.hasOwnProperty(bidSlots)) {
+ // The same slot name and size may be used for multiple bids. Get unique sizes
+ // for the request.
+ slots[bidSlots] = ybotlib.getUniqueSlotSizes(slots[bidSlots]);
+ }
+ }
+
if (yieldbot._initialized !== true) {
yieldbot.pub(psn);
for (var slotName in slots) {
if (slots.hasOwnProperty(slotName)) {
- yieldbot.defineSlot(slotName, { sizes: slots[slotName] || [] });
+ yieldbot.defineSlot(slotName, { sizes: slots[slotName] });
}
}
yieldbot.enableAsync();
yieldbot.go();
- } else {
+ } else if (!utils.isEmpty(slots)) {
yieldbot.nextPageview(slots);
}
});
@@ -128,27 +161,37 @@ function YieldbotAdapter() {
*/
handleUpdateState: function () {
var yieldbot = window.yieldbot;
- utils._each(ybotlib.definedSlots, function (v) {
- var ybRequest;
- var adapterConfig;
-
- ybRequest = $$PREBID_GLOBAL$$._bidsRequested
- .find(bidderRequest => bidderRequest.bidderCode === 'yieldbot');
-
- adapterConfig = ybRequest && ybRequest.bids ? ybRequest.bids.find(bid => bid.bidId === v) : null;
-
- if (adapterConfig && adapterConfig.params && adapterConfig.params.slot) {
- var placementCode = adapterConfig.placementCode || 'ERROR_YB_NO_PLACEMENT';
- var criteria = yieldbot.getSlotCriteria(adapterConfig.params.slot);
- var bid = ybotlib.buildBid(criteria);
-
- bidmanager.addBidResponse(placementCode, bid);
+ var slotUsed = {};
+
+ for (var bidId in ybotlib.bids) {
+ if (ybotlib.bids.hasOwnProperty(bidId)) {
+ var bidRequest = ybotlib.bids[bidId] || null;
+
+ if (bidRequest && bidRequest.params && bidRequest.params.slot) {
+ var placementCode = bidRequest.placementCode || 'ERROR_YB_NO_PLACEMENT';
+ var criteria = yieldbot.getSlotCriteria(bidRequest.params.slot);
+ var requestedSizes = ybotlib.parsedBidSizes[bidId] || [];
+
+ var slotSizeOk = false;
+ for (var idx = 0; idx < requestedSizes.length; idx++) {
+ var requestedSize = requestedSizes[idx];
+
+ if (!slotUsed[criteria.ybot_slot] && requestedSize === criteria.ybot_size) {
+ slotSizeOk = true;
+ slotUsed[criteria.ybot_slot] = true;
+ break;
+ }
+ }
+ var bid = ybotlib.buildBid(slotSizeOk ? criteria : { ybot_ad: 'n' });
+ bidmanager.addBidResponse(placementCode, bid);
+ }
}
- });
+ }
}
};
return {
- callBids: ybotlib.callBids
+ callBids: ybotlib.callBids,
+ getUniqueSlotSizes: ybotlib.getUniqueSlotSizes
};
}
diff --git a/package.json b/package.json
index db8e8be5a47..3d6cfaaff0f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "0.28.0-pre",
+ "version": "0.28.0",
"description": "Header Bidding Management Library",
"main": "src/prebid.js",
"scripts": {
@@ -61,7 +61,7 @@
"gulp-util": "^3.0.0",
"gulp-webdriver": "^1.0.1",
"istanbul": "^0.4.5",
- "istanbul-instrumenter-loader": "^2.0.0",
+ "istanbul-instrumenter-loader": "^3.0.0",
"json-loader": "^0.5.1",
"karma": "^1.7.0",
"karma-babel-preprocessor": "^6.0.1",
diff --git a/src/bidfactory.js b/src/bidfactory.js
index e88eb467413..ff57abb8a39 100644
--- a/src/bidfactory.js
+++ b/src/bidfactory.js
@@ -49,6 +49,6 @@ function Bid(statusCode, bidRequest) {
}
// Bid factory function.
-exports.createBid = function () {
- return new Bid(...arguments);
+exports.createBid = function (statusCode, bidRequest) {
+ return new Bid(statusCode, bidRequest);
};
diff --git a/src/bidmanager.js b/src/bidmanager.js
index 757383386c6..bf8ef86246c 100644
--- a/src/bidmanager.js
+++ b/src/bidmanager.js
@@ -120,8 +120,8 @@ exports.addBidResponse = function (adUnitCode, bid) {
utils.logError(errorMessage('Native bid missing some required properties.'));
return false;
}
- if (bid.mediaType === 'video' && !(bid.vastUrl || bid.vastPayload)) {
- utils.logError(errorMessage(`Video bid has no vastUrl or vastPayload property.`));
+ if (bid.mediaType === 'video' && !(bid.vastUrl || bid.vastXml)) {
+ utils.logError(errorMessage(`Video bid has no vastUrl or vastXml property.`));
return false;
}
if (bid.mediaType === 'banner' && !validBidSize(bid)) {
@@ -191,7 +191,11 @@ exports.addBidResponse = function (adUnitCode, bid) {
bid.renderer.setRender(adUnitRenderer.render);
}
- const priceStringsObj = getPriceBucketString(bid.cpm, config.getConfig('customPriceBucket'));
+ const priceStringsObj = getPriceBucketString(
+ bid.cpm,
+ config.getConfig('customPriceBucket'),
+ config.getConfig('currency.granularityMultiplier')
+ );
bid.pbLg = priceStringsObj.low;
bid.pbMg = priceStringsObj.med;
bid.pbHg = priceStringsObj.high;
diff --git a/src/config.js b/src/config.js
index 42995369ce9..f9c0e24e334 100644
--- a/src/config.js
+++ b/src/config.js
@@ -7,7 +7,7 @@
* Defining and access properties in this way is now deprecated, but these will
* continue to work during a deprecation window.
*/
-import { isValidePriceConfig } from './cpmBucketManager';
+import { isValidPriceConfig } from './cpmBucketManager';
const utils = require('./utils');
const DEFAULT_DEBUG = false;
@@ -86,7 +86,7 @@ export function newConfig() {
this._priceGranularity = (hasGranularity(val)) ? val : GRANULARITY_OPTIONS.MEDIUM;
} else if (typeof val === 'object') {
this._customPriceBucket = val;
- this._priceGranularity = GRANULARITY_OPTIONS.MEDIUM;
+ this._priceGranularity = GRANULARITY_OPTIONS.CUSTOM;
utils.logMessage('Using custom price granularity');
}
}
@@ -134,7 +134,7 @@ export function newConfig() {
utils.logWarn('Prebid Warning: setPriceGranularity was called with invalid setting, using `medium` as default.');
}
} else if (typeof val === 'object') {
- if (!isValidePriceConfig(val)) {
+ if (!isValidPriceConfig(val)) {
utils.logError('Invalid custom price value passed to `setPriceGranularity()`');
return false;
}
@@ -144,8 +144,8 @@ export function newConfig() {
/*
* Returns configuration object if called without parameters,
- * or single configuration property if given a string matching a configuartion
- * property name.
+ * or single configuration property if given a string matching a configuration
+ * property name. Allows deep access e.g. getConfig('currency.adServerCurrency')
*
* If called with callback parameter, or a string and a callback parameter,
* subscribes to configuration updates. See `subscribe` function for usage.
@@ -153,7 +153,7 @@ export function newConfig() {
function getConfig(...args) {
if (args.length <= 1 && typeof args[0] !== 'function') {
const option = args[0];
- return option ? config[option] : config;
+ return option ? utils.deepAccess(config, option) : config;
}
return subscribe(...args);
diff --git a/src/cpmBucketManager.js b/src/cpmBucketManager.js
index 5728601d245..f9515994342 100644
--- a/src/cpmBucketManager.js
+++ b/src/cpmBucketManager.js
@@ -57,26 +57,25 @@ const _autoPriceConfig = {
}]
};
-function getPriceBucketString(cpm, customConfig) {
- let cpmFloat = 0;
- cpmFloat = parseFloat(cpm);
+function getPriceBucketString(cpm, customConfig, granularityMultiplier = 1) {
+ let cpmFloat = parseFloat(cpm);
if (isNaN(cpmFloat)) {
cpmFloat = '';
}
return {
- low: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _lgPriceConfig),
- med: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _mgPriceConfig),
- high: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _hgPriceConfig),
- auto: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _autoPriceConfig),
- dense: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _densePriceConfig),
- custom: (cpmFloat === '') ? '' : getCpmStringValue(cpm, customConfig)
+ low: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _lgPriceConfig, granularityMultiplier),
+ med: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _mgPriceConfig, granularityMultiplier),
+ high: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _hgPriceConfig, granularityMultiplier),
+ auto: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _autoPriceConfig, granularityMultiplier),
+ dense: (cpmFloat === '') ? '' : getCpmStringValue(cpm, _densePriceConfig, granularityMultiplier),
+ custom: (cpmFloat === '') ? '' : getCpmStringValue(cpm, customConfig, granularityMultiplier)
};
}
-function getCpmStringValue(cpm, config) {
+function getCpmStringValue(cpm, config, granularityMultiplier) {
let cpmStr = '';
- if (!isValidePriceConfig(config)) {
+ if (!isValidPriceConfig(config)) {
return cpmStr;
}
const cap = config.buckets.reduce((prev, curr) => {
@@ -88,20 +87,20 @@ function getCpmStringValue(cpm, config) {
'max': 0,
});
let bucket = config.buckets.find(bucket => {
- if (cpm > cap.max) {
+ if (cpm > cap.max * granularityMultiplier) {
const precision = bucket.precision || _defaultPrecision;
- cpmStr = bucket.max.toFixed(precision);
- } else if (cpm <= bucket.max && cpm >= bucket.min) {
+ cpmStr = (bucket.max * granularityMultiplier).toFixed(precision);
+ } else if (cpm <= bucket.max * granularityMultiplier && cpm >= bucket.min * granularityMultiplier) {
return bucket;
}
});
if (bucket) {
- cpmStr = getCpmTarget(cpm, bucket.increment, bucket.precision);
+ cpmStr = getCpmTarget(cpm, bucket.increment, bucket.precision, granularityMultiplier);
}
return cpmStr;
}
-function isValidePriceConfig(config) {
+function isValidPriceConfig(config) {
if (utils.isEmpty(config) || !config.buckets || !Array.isArray(config.buckets)) {
return false;
}
@@ -114,12 +113,12 @@ function isValidePriceConfig(config) {
return isValid;
}
-function getCpmTarget(cpm, increment, precision) {
+function getCpmTarget(cpm, increment, precision, granularityMultiplier) {
if (!precision) {
precision = _defaultPrecision;
}
- let bucketSize = 1 / increment;
+ let bucketSize = 1 / (increment * granularityMultiplier);
return (Math.floor(cpm * bucketSize) / bucketSize).toFixed(precision);
}
-export { getPriceBucketString, isValidePriceConfig };
+export { getPriceBucketString, isValidPriceConfig };
diff --git a/src/prebid.js b/src/prebid.js
index bc61fa1592b..8a8eca318d8 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -116,7 +116,7 @@ function setRenderSize(doc, width, height) {
* This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent.
* @param {string} [adunitCode] adUnitCode to get the bid responses for
* @alias module:$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr
- * @return {array} returnObj return bids array
+ * @return {Array} returnObj return bids array
*/
$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr', arguments);
@@ -133,7 +133,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) {
/**
* This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent.
* @param adUnitCode {string} adUnitCode to get the bid responses for
- * @returns {object} returnObj return bids
+ * @returns {Object} returnObj return bids
*/
$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) {
return $$PREBID_GLOBAL$$.getAdserverTargeting(adUnitCode)[adUnitCode];
@@ -141,7 +141,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) {
/**
* returns all ad server targeting for all ad units
- * @return {object} Map of adUnitCodes and targeting values []
+ * @return {Object} Map of adUnitCodes and targeting values []
* @alias module:$$PREBID_GLOBAL$$.getAdserverTargeting
*/
@@ -168,7 +168,7 @@ $$PREBID_GLOBAL$$.getAdserverTargeting = function (adUnitCode) {
/**
* This function returns the bid responses at the given moment.
* @alias module:$$PREBID_GLOBAL$$.getBidResponses
- * @return {object} map | object that contains the bidResponses
+ * @return {Object} map | object that contains the bidResponses
*/
$$PREBID_GLOBAL$$.getBidResponses = function () {
@@ -193,7 +193,7 @@ $$PREBID_GLOBAL$$.getBidResponses = function () {
/**
* Returns bidResponses for the specified adUnitCode
- * @param {String} adUnitCode adUnitCode
+ * @param {string} adUnitCode adUnitCode
* @alias module:$$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode
* @return {Object} bidResponse object
*/
@@ -318,7 +318,7 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) {
/**
* Remove adUnit from the $$PREBID_GLOBAL$$ configuration
- * @param {String} adUnitCode the adUnitCode to remove
+ * @param {string} adUnitCode the adUnitCode to remove
* @alias module:$$PREBID_GLOBAL$$.removeAdUnit
*/
$$PREBID_GLOBAL$$.removeAdUnit = function (adUnitCode) {
@@ -342,11 +342,11 @@ $$PREBID_GLOBAL$$.clearAuction = function() {
};
/**
- *
- * @param bidsBackHandler
- * @param timeout
- * @param adUnits
- * @param adUnitCodes
+ * @param {Object} requestOptions
+ * @param {function} requestOptions.bidsBackHandler
+ * @param {number} requestOptions.timeout
+ * @param {Array} requestOptions.adUnits
+ * @param {Array} requestOptions.adUnitCodes
*/
$$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, adUnitCodes } = {}) {
events.emit('requestBids');
@@ -443,9 +443,9 @@ $$PREBID_GLOBAL$$.addAdUnits = function (adUnitArr) {
};
/**
- * @param {String} event the name of the event
+ * @param {string} event the name of the event
* @param {Function} handler a callback to set on event
- * @param {String} id an identifier in the context of the event
+ * @param {string} id an identifier in the context of the event
*
* This API call allows you to register a callback to handle a Prebid.js event.
* An optional `id` parameter provides more finely-grained event callback registration.
@@ -473,9 +473,9 @@ $$PREBID_GLOBAL$$.onEvent = function (event, handler, id) {
};
/**
- * @param {String} event the name of the event
+ * @param {string} event the name of the event
* @param {Function} handler a callback to remove from the event
- * @param {String} id an identifier in the context of the event (see `$$PREBID_GLOBAL$$.onEvent`)
+ * @param {string} id an identifier in the context of the event (see `$$PREBID_GLOBAL$$.onEvent`)
*/
$$PREBID_GLOBAL$$.offEvent = function (event, handler, id) {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.offEvent', arguments);
@@ -488,10 +488,10 @@ $$PREBID_GLOBAL$$.offEvent = function (event, handler, id) {
/**
* Add a callback event
- * @param {String} eventStr event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack"
+ * @param {string} eventStr event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack"
* @param {Function} func function to execute. Parameters passed into the function: (bidResObj), [adUnitCode]);
* @alias module:$$PREBID_GLOBAL$$.addCallback
- * @returns {String} id for callback
+ * @returns {string} id for callback
*
* @deprecated This function will be removed in Prebid 1.0
* Please use onEvent instead.
@@ -514,7 +514,7 @@ $$PREBID_GLOBAL$$.addCallback = function (eventStr, func) {
* Remove a callback event
* //@param {string} cbId id of the callback to remove
* @alias module:$$PREBID_GLOBAL$$.removeCallback
- * @returns {String} id for callback
+ * @returns {string} id for callback
*
* @deprecated This function will be removed in Prebid 1.0
* Please use offEvent instead.
@@ -527,9 +527,8 @@ $$PREBID_GLOBAL$$.removeCallback = function (/* cbId */) {
/**
* Wrapper to register bidderAdapter externally (adaptermanager.registerBidAdapter())
- * @param {[type]} bidderAdaptor [description]
- * @param {[type]} bidderCode [description]
- * @return {[type]} [description]
+ * @param {Function} bidderAdaptor [description]
+ * @param {string} bidderCode [description]
*/
$$PREBID_GLOBAL$$.registerBidAdapter = function (bidderAdaptor, bidderCode) {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.registerBidAdapter', arguments);
@@ -542,7 +541,7 @@ $$PREBID_GLOBAL$$.registerBidAdapter = function (bidderAdaptor, bidderCode) {
/**
* Wrapper to register analyticsAdapter externally (adaptermanager.registerAnalyticsAdapter())
- * @param {[type]} options [description]
+ * @param {Object} options [description]
*/
$$PREBID_GLOBAL$$.registerAnalyticsAdapter = function (options) {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.registerAnalyticsAdapter', arguments);
@@ -568,8 +567,8 @@ $$PREBID_GLOBAL$$.bidsAvailableForAdapter = function (bidderCode) {
/**
* Wrapper to bidfactory.createBid()
- * @param {[type]} statusCode [description]
- * @return {[type]} [description]
+ * @param {string} statusCode [description]
+ * @return {Object} bidResponse [description]
*/
$$PREBID_GLOBAL$$.createBid = function (statusCode) {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.createBid', arguments);
@@ -578,8 +577,8 @@ $$PREBID_GLOBAL$$.createBid = function (statusCode) {
/**
* Wrapper to bidmanager.addBidResponse
- * @param {[type]} adUnitCode [description]
- * @param {[type]} bid [description]
+ * @param {string} adUnitCode [description]
+ * @param {Object} bid [description]
*
* @deprecated This function will be removed in Prebid 1.0
* Each bidder will be passed a reference to addBidResponse function in callBids as an argument.
@@ -593,9 +592,8 @@ $$PREBID_GLOBAL$$.addBidResponse = function (adUnitCode, bid) {
/**
* Wrapper to adloader.loadScript
- * @param {[type]} tagSrc [description]
+ * @param {string} tagSrc [description]
* @param {Function} callback [description]
- * @return {[type]} [description]
*/
$$PREBID_GLOBAL$$.loadScript = function (tagSrc, callback, useCache) {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.loadScript', arguments);
@@ -604,7 +602,7 @@ $$PREBID_GLOBAL$$.loadScript = function (tagSrc, callback, useCache) {
/**
* Will enable sending a prebid.js to data provider specified
- * @param {object} config object {provider : 'string', options : {}}
+ * @param {Object} config object {provider : 'string', options : {}}
*/
$$PREBID_GLOBAL$$.enableAnalytics = function (config) {
if (config && !utils.isEmpty(config)) {
@@ -626,7 +624,7 @@ $$PREBID_GLOBAL$$.aliasBidder = function (bidderCode, alias) {
/**
* Sets a default price granularity scheme.
- * @param {String|Object} granularity - the granularity scheme.
+ * @param {string|Object} granularity - the granularity scheme.
* @deprecated - use pbjs.setConfig({ priceGranularity: })
* "low": $0.50 increments, capped at $5 CPM
* "medium": $0.10 increments, capped at $20 CPM (the default)
@@ -656,7 +654,7 @@ $$PREBID_GLOBAL$$.getAllWinningBids = function () {
/**
* Build master video tag from publishers adserver tag
* @param {string} adserverTag default url
- * @param {object} options options for video tag
+ * @param {Object} options options for video tag
*
* @deprecated Include the dfpVideoSupport module in your build, and use the $$PREBID_GLOBAL$$.adservers.dfp.buildVideoAdUrl function instead.
* This function will be removed in Prebid 1.0.
@@ -703,7 +701,7 @@ $$PREBID_GLOBAL$$.setBidderSequence = adaptermanager.setBidderSequence
* Get array of highest cpm bids for all adUnits, or highest cpm bid
* object for the given adUnit
* @param {string} adUnitCode - optional ad unit code
- * @return {array} array containing highest cpm bid object(s)
+ * @return {Array} array containing highest cpm bid object(s)
*/
$$PREBID_GLOBAL$$.getHighestCpmBids = function (adUnitCode) {
return targeting.getWinningBids(adUnitCode);
@@ -748,13 +746,13 @@ $$PREBID_GLOBAL$$.setS2SConfig = function(options) {
/**
* Get Prebid config options
- * @param {object} options
+ * @param {Object} options
*/
$$PREBID_GLOBAL$$.getConfig = config.getConfig;
/**
* Set Prebid config options
- * @param {object} options
+ * @param {Object} options
*/
$$PREBID_GLOBAL$$.setConfig = config.setConfig;
@@ -775,14 +773,15 @@ $$PREBID_GLOBAL$$.que.push(() => listenMessagesFromCreative());
* by prebid once it's done loading. If it runs after prebid loads, then this monkey-patch causes their
* function to execute immediately.
*
- * @param {function} cmd A function which takes no arguments. This is guaranteed to run exactly once, and only after
- * the Prebid script has been fully loaded.
+ * @memberof $$PREBID_GLOBAL$$
+ * @param {function} command A function which takes no arguments. This is guaranteed to run exactly once, and only after
+ * the Prebid script has been fully loaded.
* @alias module:$$PREBID_GLOBAL$$.cmd.push
*/
-$$PREBID_GLOBAL$$.cmd.push = function(cmd) {
- if (typeof cmd === 'function') {
+$$PREBID_GLOBAL$$.cmd.push = function(command) {
+ if (typeof command === 'function') {
try {
- cmd.call();
+ command.call();
} catch (e) {
utils.logError('Error processing command :' + e.message);
}
diff --git a/src/utils.js b/src/utils.js
index 0a81503e1d3..0c1846b18ce 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -157,6 +157,8 @@ export function parseGPTSingleSizeArray(singleSize) {
exports.getTopWindowLocation = function () {
let location;
try {
+ // force an exception in x-domain enviornments. #1509
+ window.top.location.toString();
location = window.top.location;
} catch (e) {
location = window.location;
@@ -677,3 +679,20 @@ export function groupBy(xs, key) {
return rv;
}, {});
}
+
+/**
+ * deepAccess utility function useful for doing safe access (will not throw exceptions) of deep object paths.
+ * @param {object} obj The object containing the values you would like to access.
+ * @param {string|number} path Object path to the value you would like to access. Non-strings are coerced to strings.
+ * @returns {*} The value found at the specified object path, or undefined if path is not found.
+ */
+export function deepAccess(obj, path) {
+ path = String(path).split('.');
+ for (let i = 0; i < path.length; i++) {
+ obj = obj[path[i]];
+ if (typeof obj === 'undefined') {
+ return;
+ }
+ }
+ return obj;
+}
diff --git a/src/videoCache.js b/src/videoCache.js
index 73d249c13f0..fe126fad6e0 100644
--- a/src/videoCache.js
+++ b/src/videoCache.js
@@ -20,7 +20,7 @@ const BASE_URL = 'https://prebid.adnxs.com/pbc/v1/cache'
/**
* @typedef {object} CacheablePayloadBid
- * @property {string} vastPayload Some VAST XML which loads an ad in a video player.
+ * @property {string} vastXml Some VAST XML which loads an ad in a video player.
*/
/**
@@ -58,7 +58,7 @@ function wrapURI(uri) {
* @param {CacheableBid} bid
*/
function toStorageRequest(bid) {
- const vastValue = bid.vastPayload ? bid.vastPayload : wrapURI(bid.vastUrl);
+ const vastValue = bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl);
return {
type: 'xml',
value: vastValue
diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js
index 59b812acc5c..8108da3c555 100644
--- a/test/fixtures/fixtures.js
+++ b/test/fixtures/fixtures.js
@@ -1370,3 +1370,13 @@ export function getBidRequestedPayload() {
'start': 1465426155412
};
}
+
+export function getCurrencyRates() {
+ return {
+ 'dataAsOf': '2017-04-25',
+ 'conversions': {
+ 'GBP': { 'CNY': 8.8282, 'JPY': 141.7, 'USD': 1.2824 },
+ 'USD': { 'CNY': 6.8842, 'GBP': 0.7798, 'JPY': 110.49 }
+ }
+ };
+}
diff --git a/test/fixtures/video/vastPayloadResponse.json b/test/fixtures/video/vastPayloadResponse.json
index 5ffdf99350b..9b621c21d30 100644
--- a/test/fixtures/video/vastPayloadResponse.json
+++ b/test/fixtures/video/vastPayloadResponse.json
@@ -8,6 +8,6 @@
"height": 480,
"mediaType": "video",
"requestId": "6172477f-987f-4523-a967-fa6d7a434ddf",
- "vastPayload": " ",
+ "vastXml": " ",
"width": 640
}
diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js
index 77357df6d9e..041be4591fc 100644
--- a/test/spec/config_spec.js
+++ b/test/spec/config_spec.js
@@ -128,7 +128,7 @@ describe('config API', () => {
}]
};
setConfig({ priceGranularity: goodConfig });
- expect(getConfig('priceGranularity')).to.be.equal('medium');
+ expect(getConfig('priceGranularity')).to.be.equal('custom');
expect(getConfig('customPriceBucket')).to.equal(goodConfig);
});
diff --git a/test/spec/cpmBucketManager_spec.js b/test/spec/cpmBucketManager_spec.js
index 1590a647417..f1f7d697397 100644
--- a/test/spec/cpmBucketManager_spec.js
+++ b/test/spec/cpmBucketManager_spec.js
@@ -1,5 +1,5 @@
import { expect } from 'chai';
-import {getPriceBucketString, isValidePriceConfig} from 'src/cpmBucketManager';
+import {getPriceBucketString, isValidPriceConfig} from 'src/cpmBucketManager';
let cpmFixtures = require('test/fixtures/cpmInputsOutputs.json');
describe('cpmBucketManager', () => {
@@ -35,6 +35,29 @@ describe('cpmBucketManager', () => {
expect(JSON.stringify(output)).to.deep.equal(expected);
});
+ it('gets the correct custom bucket strings in non-USD currency', () => {
+ let cpm = 16.50908 * 110.49;
+ let customConfig = {
+ 'buckets': [{
+ 'precision': 4,
+ 'min': 0,
+ 'max': 3,
+ 'increment': 0.01,
+ },
+ {
+ 'precision': 4,
+ 'min': 3,
+ 'max': 18,
+ 'increment': 0.05,
+ 'cap': true
+ }
+ ]
+ };
+ let expected = '{"low":"552.45","med":"1823.09","high":"1823.09","auto":"1823.09","dense":"1823.09","custom":"1823.0850"}';
+ let output = getPriceBucketString(cpm, customConfig, 110.49);
+ expect(JSON.stringify(output)).to.deep.equal(expected);
+ });
+
it('checks whether custom config is valid', () => {
let badConfig = {
'buckets': [{
@@ -51,6 +74,6 @@ describe('cpmBucketManager', () => {
]
};
- expect(isValidePriceConfig(badConfig)).to.be.false;
+ expect(isValidPriceConfig(badConfig)).to.be.false;
});
});
diff --git a/test/spec/modules/adformBidAdapter_spec.js b/test/spec/modules/adformBidAdapter_spec.js
index 0d2d1e19b68..9d77b4faca5 100644
--- a/test/spec/modules/adformBidAdapter_spec.js
+++ b/test/spec/modules/adformBidAdapter_spec.js
@@ -1,11 +1,11 @@
import { assert } from 'chai';
-import * as utils from '../../../src/utils';
-import adLoader from '../../../src/adloader';
-import bidManager from '../../../src/bidmanager';
-import adapter from '../../../modules/adformBidAdapter';
+import * as utils from 'src/utils';
+import adLoader from 'src/adloader';
+import bidManager from 'src/bidmanager';
+import AdformAdapter from 'modules/adformBidAdapter';
describe('Adform adapter', () => {
- let _adapter, sandbox;
+ let _adformAdapter, sandbox;
describe('request', () => {
it('should create callback method on PREBID_GLOBAL', () => {
@@ -112,11 +112,11 @@ describe('Adform adapter', () => {
beforeEach(() => {
var transactionId = 'transactionId';
- _adapter = adapter();
+ _adformAdapter = new AdformAdapter();
utils.getUniqueIdentifierStr = () => 'callback';
sandbox = sinon.sandbox.create();
sandbox.stub(adLoader, 'loadScript');
- _adapter.callBids({
+ _adformAdapter.callBids({
bids: [
{
bidId: 'abc',
diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js
new file mode 100644
index 00000000000..fa55bf92e2e
--- /dev/null
+++ b/test/spec/modules/adxcgBidAdapter_spec.js
@@ -0,0 +1,212 @@
+import { expect } from 'chai';
+import Adapter from 'modules/adxcgBidAdapter';
+import bidmanager from 'src/bidmanager';
+import * as url from 'src/url';
+
+const REQUEST = {
+ 'bidderCode': 'adxcg',
+ 'bids': [
+ {
+ 'bidder': 'adxcg',
+ 'params': {
+ 'adzoneid': '1',
+ },
+ 'sizes': [
+ [300, 250],
+ [640, 360],
+ [1, 1]
+ ],
+ 'bidId': '84ab500420319d',
+ 'bidderRequestId': '7101db09af0db2'
+ }
+ ]
+};
+
+const RESPONSE = [{
+ 'bidId': '84ab500420319d',
+ 'width': 300,
+ 'height': 250,
+ 'creativeId': '42',
+ 'cpm': 0.45,
+ 'ad': ''
+}]
+
+const VIDEO_RESPONSE = [{
+ 'bidId': '84ab500420319d',
+ 'width': 640,
+ 'height': 360,
+ 'creativeId': '42',
+ 'cpm': 0.45,
+ 'vastUrl': 'vastContentUrl'
+}]
+
+const NATIVE_RESPONSE = [{
+ 'bidId': '84ab500420319d',
+ 'width': 0,
+ 'height': 0,
+ 'creativeId': '42',
+ 'cpm': 0.45,
+ 'nativeResponse': {
+ 'assets': [{
+ 'id': 1,
+ 'required': 0,
+ 'title': {
+ 'text': 'titleContent'
+ }
+ }, {
+ 'id': 2,
+ 'required': 0,
+ 'img': {
+ 'url': 'imageContent',
+ 'w': 600,
+ 'h': 600
+ }
+ }, {
+ 'id': 3,
+ 'required': 0,
+ 'data': {
+ 'label': 'DESC',
+ 'value': 'descriptionContent'
+ }
+ }, {
+ 'id': 0,
+ 'required': 0,
+ 'data': {
+ 'label': 'SPONSORED',
+ 'value': 'sponsoredByContent'
+ }
+ }],
+ 'link': {
+ 'url': 'linkContent'
+ },
+ 'imptrackers': ['impressionTracker1', 'impressionTracker2']
+ }
+}]
+
+describe('AdxcgAdapter', () => {
+ let adapter;
+
+ beforeEach(() => adapter = new Adapter());
+
+ describe('request function', () => {
+ let xhr;
+ let requests;
+
+ beforeEach(() => {
+ xhr = sinon.useFakeXMLHttpRequest();
+ requests = [];
+ xhr.onCreate = request => requests.push(request);
+ });
+
+ afterEach(() => xhr.restore());
+
+ it('creates a valid adxcg request url', () => {
+ adapter.callBids(REQUEST);
+
+ let parsedRequestUrl = url.parse(requests[0].url);
+
+ expect(parsedRequestUrl.hostname).to.equal('ad-emea.adxcg.net');
+ expect(parsedRequestUrl.pathname).to.equal('/get/adi');
+
+ let query = parsedRequestUrl.search;
+ expect(query.renderformat).to.equal('javascript');
+ expect(query.ver).to.equal('r20141124');
+ expect(query.adzoneid).to.equal('1');
+ expect(query.format).to.equal('300x250|640x360|1x1');
+ expect(query.jsonp).to.be.empty;
+ expect(query.prebidBidIds).to.equal('84ab500420319d');
+ });
+ });
+
+ describe('response handler', () => {
+ let server;
+
+ beforeEach(() => {
+ server = sinon.fakeServer.create();
+ sinon.stub(bidmanager, 'addBidResponse');
+ });
+
+ afterEach(() => {
+ server.restore()
+ bidmanager.addBidResponse.restore();
+ });
+
+ it('handles regular responses', () => {
+ server.respondWith(JSON.stringify(RESPONSE));
+
+ adapter.callBids(REQUEST);
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const bidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ expect(bidResponse.bidderCode).to.equal('adxcg');
+ expect(bidResponse.width).to.equal(300);
+ expect(bidResponse.height).to.equal(250);
+ expect(bidResponse.statusMessage).to.equal('Bid available');
+ expect(bidResponse.adId).to.equal('84ab500420319d');
+ expect(bidResponse.mediaType).to.equal('banner');
+ expect(bidResponse.creative_id).to.equal('42');
+ expect(bidResponse.code).to.equal('adxcg');
+ expect(bidResponse.cpm).to.equal(0.45);
+ expect(bidResponse.ad).to.equal('');
+ });
+
+ it('handles video responses', () => {
+ server.respondWith(JSON.stringify(VIDEO_RESPONSE));
+
+ adapter.callBids(REQUEST);
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const bidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ expect(bidResponse.bidderCode).to.equal('adxcg');
+ expect(bidResponse.width).to.equal(640);
+ expect(bidResponse.height).to.equal(360);
+ expect(bidResponse.statusMessage).to.equal('Bid available');
+ expect(bidResponse.adId).to.equal('84ab500420319d');
+ expect(bidResponse.mediaType).to.equal('video');
+ expect(bidResponse.creative_id).to.equal('42');
+ expect(bidResponse.code).to.equal('adxcg');
+ expect(bidResponse.cpm).to.equal(0.45);
+ expect(bidResponse.vastUrl).to.equal('vastContentUrl');
+ expect(bidResponse.descriptionUrl).to.equal('vastContentUrl');
+ });
+
+ it('handles native responses', () => {
+ server.respondWith(JSON.stringify(NATIVE_RESPONSE));
+
+ adapter.callBids(REQUEST);
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const bidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ expect(bidResponse.bidderCode).to.equal('adxcg');
+ expect(bidResponse.width).to.equal(0);
+ expect(bidResponse.height).to.equal(0);
+ expect(bidResponse.statusMessage).to.equal('Bid available');
+ expect(bidResponse.adId).to.equal('84ab500420319d');
+ expect(bidResponse.mediaType).to.equal('native');
+ expect(bidResponse.creative_id).to.equal('42');
+ expect(bidResponse.code).to.equal('adxcg');
+ expect(bidResponse.cpm).to.equal(0.45);
+
+ expect(bidResponse.native.clickUrl).to.equal('linkContent');
+ expect(bidResponse.native.impressionTrackers).to.deep.equal(['impressionTracker1', 'impressionTracker2']);
+ expect(bidResponse.native.title).to.equal('titleContent');
+ expect(bidResponse.native.image).to.equal('imageContent');
+ expect(bidResponse.native.body).to.equal('descriptionContent');
+ expect(bidResponse.native.sponsoredBy).to.equal('sponsoredByContent');
+ });
+
+ it('handles nobid responses', () => {
+ server.respondWith('[]');
+
+ adapter.callBids(REQUEST);
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const bidResponse = bidmanager.addBidResponse.firstCall.args[1];
+ expect(bidResponse.statusMessage).to.equal('Bid returned empty or error response');
+ });
+ });
+});
diff --git a/test/spec/modules/aerservBidAdapter_spec.js b/test/spec/modules/aerservBidAdapter_spec.js
new file mode 100644
index 00000000000..be0f6393063
--- /dev/null
+++ b/test/spec/modules/aerservBidAdapter_spec.js
@@ -0,0 +1,213 @@
+import {expect} from 'chai';
+import AerServAdapter from 'modules/aerservBidAdapter';
+import bidmanager from 'src/bidmanager';
+
+const BASE_REQUEST = JSON.stringify({
+ bidderCode: 'aerserv',
+ requestId: 'a595eff7-d5a3-40f8-971c-5b4ef244ec53',
+ bidderRequestId: '1f8c8c03de01f9',
+ bids: [
+ {
+ bidder: 'aerserv',
+ params: {
+ plc: '480',
+ },
+ placementCode: 'adunit-1',
+ transactionId: 'a0e033af-f50c-4a7e-aeed-c01c5f709848',
+ sizes: [[300, 250], [300, 600]],
+ bidId: '2f4a69463b3bc9',
+ bidderRequestId: '1f8c8c03de01f9',
+ requestId: 'a595eff7-d5a3-40f8-971c-5b4ef244ec53'
+ }
+ ]
+});
+
+describe('AerServ Adapter', () => {
+ let adapter;
+ let bidmanagerStub;
+
+ beforeEach(() => {
+ adapter = new AerServAdapter();
+ bidmanagerStub = sinon.stub(bidmanager, 'addBidResponse');
+ });
+
+ afterEach(() => {
+ bidmanager.addBidResponse.restore();
+ });
+
+ describe('callBids()', () => {
+ let xhr;
+ let requests;
+
+ beforeEach(() => {
+ xhr = sinon.useFakeXMLHttpRequest();
+ requests = [];
+ xhr.onCreate = request => requests.push(request);
+ });
+
+ afterEach(() => {
+ xhr.restore();
+ });
+
+ it('exists and is a function', () => {
+ expect(adapter.callBids).to.exist.and.to.be.a('function');
+ });
+
+ it('should not add bid responses with no bids to call', () => {
+ adapter.callBids({});
+
+ sinon.assert.notCalled(bidmanager.addBidResponse);
+ });
+
+ it('requires plc parameter to make request', () => {
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0].params = {};
+ adapter.callBids(bidRequest);
+ expect(requests).to.be.empty;
+ });
+
+ it('sends requests to normal endpoint for non-video requests', () => {
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ expect(requests.length).to.equal(1);
+ expect(requests[0].url).to.include('/as/json/pbjs/v1');
+ });
+
+ it('sends requests to video endpoint for video requests', () => {
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0]['mediaType'] = 'video';
+ bidRequest.bids[0]['video'] = {};
+ adapter.callBids(bidRequest);
+ expect(requests[0].url).to.include('/as/json/pbjsvast/v1');
+ });
+
+ it('properly adds video parameters to the request', () => {
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0]['mediaType'] = 'video';
+ bidRequest.bids[0].params['video'] = { videoParam: 'videoValue' };
+ adapter.callBids(bidRequest);
+ expect(requests[0].url).to.include('videoParam=videoValue');
+ });
+
+ it('parses the first size for video requests', () => {
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0]['mediaType'] = 'video';
+ adapter.callBids(bidRequest);
+ expect(requests[0].url).to.include('vpw=300');
+ expect(requests[0].url).to.include('vph=250');
+ });
+
+ it('sends requests to production by default', () => {
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ expect(requests[0].url).to.include('//ads.aerserv.com');
+ });
+
+ it('sends requests to the specified endpoint when \'env\' is provided', () => {
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0].params['env'] = 'dev';
+ adapter.callBids(bidRequest);
+ expect(requests[0].url).to.include('//dev-ads.aerserv.com');
+ });
+ });
+
+ describe('response handling', () => {
+ let server;
+
+ beforeEach(() => {
+ server = sinon.fakeServer.create();
+ });
+
+ afterEach(() => {
+ server.restore();
+ });
+
+ it('responds with an empty bid without required parameters', () => {
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0].params = {};
+ adapter.callBids(bidRequest);
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+
+ it('responds with an empty bid on empty response', () => {
+ server.respondWith('');
+
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+
+ it('responds with an empty bid on un-parseable JSON response', () => {
+ server.respondWith('{\"bad\":\"json}');
+
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+
+ it('responds with a valid bid returned ad', () => {
+ server.respondWith(JSON.stringify({cpm: 5, w: 320, h: 50, adm: 'sweet ad markup'}));
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(1);
+ });
+
+ it('responds with a valid bid from returned ad', () => {
+ server.respondWith(JSON.stringify({cpm: 5, w: 320, h: 50, vastUrl: 'sweet URL where VAST is at'}));
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0]['mediaType'] = 'video';
+ bidRequest.bids[0]['video'] = {};
+ adapter.callBids(bidRequest);
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(1);
+ });
+
+ it('responds with empty bid if response has no ad', () => {
+ server.respondWith(JSON.stringify({error: 'no ads'}));
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+
+ // things that should never occur
+ it('responds with empty bid if response has 0 or below cpm', () => {
+ server.respondWith(JSON.stringify({cpm: 0, w: 320, h: 50, adm: 'sweet ad markup'}));
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+
+ it('responds with empty bid if response has no markup', () => {
+ server.respondWith(JSON.stringify({cpm: 5.0, w: 320, h: 50}));
+ adapter.callBids(JSON.parse(BASE_REQUEST));
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+
+ it('responds with an empty bid if response has no video markup', () => {
+ server.respondWith(JSON.stringify({cpm: 5, w: 320, h: 50}));
+ let bidRequest = JSON.parse(BASE_REQUEST);
+ bidRequest.bids[0]['mediaType'] = 'video';
+ bidRequest.bids[0]['video'] = {};
+ adapter.callBids(bidRequest);
+ server.respond();
+ let bid = bidmanagerStub.getCall(0).args[1];
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ expect(bid.getStatusCode()).to.equal(2);
+ });
+ });
+});
diff --git a/test/spec/modules/appnexusAstBidAdapter_spec.js b/test/spec/modules/appnexusAstBidAdapter_spec.js
index 310cd6a1884..3756c2a6651 100644
--- a/test/spec/modules/appnexusAstBidAdapter_spec.js
+++ b/test/spec/modules/appnexusAstBidAdapter_spec.js
@@ -129,6 +129,17 @@ describe('AppNexusAdapter', () => {
delete REQUEST.bids[0].params.user;
});
+ it('should add source and verison to the tag', () => {
+ adapter.callBids(REQUEST);
+
+ const request = JSON.parse(requests[0].requestBody);
+ expect(request.sdk).to.exist;
+ expect(request.sdk).to.deep.equal({
+ source: 'pbjs',
+ version: '$prebid.version$'
+ });
+ });
+
it('attaches native params to the request', () => {
REQUEST.bids[0].mediaType = 'native';
REQUEST.bids[0].nativeParams = {
diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js
new file mode 100644
index 00000000000..f11ef18a35a
--- /dev/null
+++ b/test/spec/modules/currency_spec.js
@@ -0,0 +1,248 @@
+
+import {
+ getCurrencyRates
+} from 'test/fixtures/fixtures';
+
+import {
+ setConfig,
+ addBidResponseDecorator,
+
+ currencySupportEnabled,
+ currencyRates
+} from 'modules/currency';
+
+var assert = require('chai').assert;
+var expect = require('chai').expect;
+
+describe('currency', function () {
+ describe('setConfig', () => {
+ it('results in currencySupportEnabled = false when currency not configured', () => {
+ setConfig({});
+ expect(currencySupportEnabled).to.equal(false);
+ });
+
+ it('results in currencySupportEnabled = true and currencyRates being loaded when configured', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'JPY' });
+ fakeCurrencyFileServer.respond();
+ expect(currencyRates.dataAsOf).to.equal('2017-04-25');
+ expect(currencySupportEnabled).to.equal(true);
+ });
+ });
+
+ describe('bidder override', () => {
+ it('allows setConfig to set bidder currency', () => {
+ setConfig({});
+
+ var bid = { cpm: 1, bidder: 'rubicon' };
+ var innerBid;
+
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+
+ setConfig({
+ adServerCurrency: 'GBP',
+ bidderCurrencyDefault: {
+ rubicon: 'GBP'
+ }
+ });
+
+ wrappedAddBidResponseFn('elementId', bid);
+
+ expect(innerBid.currency).to.equal('GBP')
+ });
+
+ it('uses adapter currency over currency override if specified', () => {
+ setConfig({});
+
+ var bid = { cpm: 1, currency: 'JPY', bidder: 'rubicon' };
+ var innerBid;
+
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+
+ setConfig({
+ adServerCurrency: 'JPY',
+ bidderCurrencyDefault: {
+ rubicon: 'GBP'
+ }
+ });
+
+ wrappedAddBidResponseFn('elementId', bid);
+
+ expect(innerBid.currency).to.equal('JPY')
+ });
+
+ it('uses rates specified in json when provided', () => {
+ setConfig({
+ adServerCurrency: 'USD',
+ rates: {
+ USD: {
+ JPY: 100
+ }
+ }
+ });
+
+ var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' };
+ var innerBid;
+
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+
+ wrappedAddBidResponseFn('elementId', bid);
+
+ expect(innerBid.cpm).to.equal('1.0000');
+ });
+ });
+
+ describe('currency.addBidResponseDecorator bidResponseQueue', () => {
+ it('not run until currency rates file is loaded', () => {
+ setConfig({});
+
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+
+ var marker = false;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ marker = true;
+ });
+ var bid = { 'cpm': 1, 'currency': 'USD' };
+
+ setConfig({ 'adServerCurrency': 'JPY' });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(marker).to.equal(false);
+
+ fakeCurrencyFileServer.respond();
+ expect(marker).to.equal(true);
+ });
+ });
+
+ describe('currency.addBidResponseDecorator', () => {
+ it('should leave bid at 1 when currency support is not enabled and fromCurrency is USD', () => {
+ setConfig({});
+ var bid = { 'cpm': 1, 'currency': 'USD' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.cpm).to.equal(1);
+ });
+
+ it('should result in NO_BID when currency support is not enabled and fromCurrency is not USD', () => {
+ setConfig({});
+ var bid = { 'cpm': 1, 'currency': 'GBP' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.statusMessage).to.equal('Bid returned empty or error response');
+ });
+
+ it('should not buffer bid when currency is already in desired currency', () => {
+ setConfig({
+ 'adServerCurrency': 'USD'
+ });
+ var bid = { 'cpm': 1, 'currency': 'USD' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(bid).to.equal(innerBid);
+ });
+
+ it('should result in NO_BID when fromCurrency is not supported in file', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'JPY' });
+ fakeCurrencyFileServer.respond();
+ var bid = { 'cpm': 1, 'currency': 'ABC' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.statusMessage).to.equal('Bid returned empty or error response');
+ });
+
+ it('should result in NO_BID when adServerCurrency is not supported in file', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'ABC' });
+ fakeCurrencyFileServer.respond();
+ var bid = { 'cpm': 1, 'currency': 'GBP' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.statusMessage).to.equal('Bid returned empty or error response');
+ });
+
+ it('should return 1 when currency support is enabled and same currency code is requested as is set to adServerCurrency', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'JPY' });
+ fakeCurrencyFileServer.respond();
+ var bid = { 'cpm': 1, 'currency': 'JPY' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.cpm).to.equal(1);
+ expect(innerBid.currency).to.equal('JPY');
+ });
+
+ it('should return direct conversion rate when fromCurrency is one of the configured bases', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'GBP' });
+ fakeCurrencyFileServer.respond();
+ var bid = { 'cpm': 1, 'currency': 'USD' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.cpm).to.equal('0.7798');
+ expect(innerBid.currency).to.equal('GBP');
+ });
+
+ it('should return reciprocal conversion rate when adServerCurrency is one of the configured bases, but fromCurrency is not', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'GBP' });
+ fakeCurrencyFileServer.respond();
+ var bid = { 'cpm': 1, 'currency': 'CNY' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.cpm).to.equal('0.1133');
+ expect(innerBid.currency).to.equal('GBP');
+ });
+
+ it('should return intermediate conversion rate when neither fromCurrency nor adServerCurrency is one of the configured bases', () => {
+ var fakeCurrencyFileServer = sinon.fakeServer.create();
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ 'adServerCurrency': 'CNY' });
+ fakeCurrencyFileServer.respond();
+ var bid = { 'cpm': 1, 'currency': 'JPY' };
+ var innerBid;
+ var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) {
+ innerBid = bid;
+ });
+ wrappedAddBidResponseFn('elementId', bid);
+ expect(innerBid.cpm).to.equal('0.0623');
+ expect(innerBid.currency).to.equal('CNY');
+ });
+ });
+});
diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js
new file mode 100644
index 00000000000..c781406da6e
--- /dev/null
+++ b/test/spec/modules/improvedigitalBidAdapter_spec.js
@@ -0,0 +1,599 @@
+describe('improvedigital adapter tests', function () {
+ const expect = require('chai').expect;
+ const Adapter = require('modules/improvedigitalBidAdapter');
+ const bidmanager = require('src/bidmanager');
+ const adloader = require('src/adloader');
+ const constants = require('src/constants.json');
+ var bidfactory = require('src/bidfactory');
+ var utils = require('src/utils.js');
+
+ var improveDigitalAdapter,
+ sandbox,
+ bidsRequestedOriginal;
+
+ const simpleBidRequest = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ placementId: 1012544
+ }
+ }
+ ]
+ };
+
+ const simpleSmartTagBidRequest = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ publisherId: 1032,
+ placementKey: 'data_team_test_hb_smoke_test'
+ }
+ }
+ ]
+ };
+
+ const keyValueBidRequest = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ placementId: 1012546,
+ keyValues: {
+ hbkv: ['01']
+ }
+ }
+ }
+ ]
+ };
+
+ const sizeBidRequest = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ placementId: 1012545,
+ size: {
+ w: 800,
+ h: 600
+ }
+ }
+ }
+ ]
+ };
+
+ const twoAdSlots = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ placementId: 1012544,
+ }
+ },
+ {
+ bidId: '4d5e6f',
+ placementCode: 'placement2',
+ params: {
+ placementId: 1012545,
+ size: {
+ w: 800,
+ h: 600
+ }
+ }
+ }
+ ]
+ };
+
+ const threeAdSlots = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ placementId: 1012544,
+ }
+ },
+ {
+ bidId: '4d5e6f',
+ placementCode: 'placement2',
+ params: {
+ placementId: 1012545,
+ size: {
+ w: 800,
+ h: 600
+ }
+ }
+ },
+ {
+ bidId: '7g8h9i',
+ placementCode: 'placement3',
+ params: {
+ placementId: 1012546,
+ keyValues: {
+ hbkv: ['01']
+ }
+ }
+ }
+ ]
+ };
+
+ const badRequest1 = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ unknownId: 123456
+ }
+ }
+ ]
+ };
+
+ const twoAdSlotsSingleRequest = {
+ bidderCode: 'improvedigital',
+ bids: [
+ {
+ bidId: '1a2b3c',
+ placementCode: 'placement1',
+ params: {
+ singleRequest: true,
+ placementId: 1012544,
+ }
+ },
+ {
+ bidId: '4d5e6f',
+ placementCode: 'placement2',
+ params: {
+ placementId: 1012545,
+ size: {
+ w: 800,
+ h: 600
+ }
+ }
+ }
+ ]
+ };
+
+ const simpleResponse = {
+ id: '701903620',
+ site_id: 191642,
+ bid: [
+ {
+ price: 1.85185185185185,
+ lid: 268514,
+ advid: '5279',
+ id: '1a2b3c',
+ sync: [
+ 'http://link',
+ 'http://link2',
+ 'http://link3'
+ ],
+ nurl: 'http://nurl',
+ h: 300,
+ pid: 1053687,
+ crid: '422030',
+ w: 300,
+ cid: '99005',
+ adm: 'document.writeln(\" {
+ improveDigitalAdapter = new Adapter();
+ sandbox = sinon.sandbox.create();
+ sandbox.stub(
+ utils,
+ 'getUniqueIdentifierStr',
+ function() {
+ var retValue = randomNumber.toString();
+ randomNumber++;
+ return retValue;
+ }
+ );
+ bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested;
+ $$PREBID_GLOBAL$$._bidsRequested = [];
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ $$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal;
+ });
+
+ describe('callBids simpleBidRequest', () => {
+ beforeEach(() => {
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(simpleBidRequest);
+ });
+ it('should call loadScript with correct parameters', () => {
+ sinon.assert.calledOnce(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('callBids simpleSmartTagBidRequest', () => {
+ beforeEach(() => {
+ randomNumber = 9876543210;
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(simpleSmartTagBidRequest);
+ });
+ it('should call loadScript with correct parameters', () => {
+ sinon.assert.calledOnce(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pubid%22%3A1032%2C%22pkey%22%3A%22data_team_test_hb_smoke_test%22%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('callBids keyValueBidRequest', () => {
+ beforeEach(() => {
+ randomNumber = 9876543210;
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(keyValueBidRequest);
+ });
+ it('should call loadScript with correct parameters', () => {
+ sinon.assert.calledOnce(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012546%2C%22kvw%22%3A%7B%22hbkv%22%3A%5B%2201%22%5D%7D%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('callBids sizeBidRequest', () => {
+ beforeEach(() => {
+ randomNumber = 9876543210;
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(sizeBidRequest);
+ });
+ it('should call loadScript with correct parameters', () => {
+ sinon.assert.calledOnce(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('callBids twoAdSlots', () => {
+ beforeEach(() => {
+ randomNumber = 9876543210;
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(twoAdSlots);
+ });
+ it('should call loadScript twice with correct parameters', () => {
+ sinon.assert.calledTwice(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543211%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%224d5e6f%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('callBids threeAdSlots', () => {
+ beforeEach(() => {
+ randomNumber = 9876543210;
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(threeAdSlots);
+ });
+ it('should call loadScript thrice with correct parameters', () => {
+ sinon.assert.calledThrice(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543211%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%224d5e6f%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543212%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%227g8h9i%22%2C%22pid%22%3A1012546%2C%22kvw%22%3A%7B%22hbkv%22%3A%5B%2201%22%5D%7D%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('callBids bad request 1', () => {
+ beforeEach(() => {
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(badRequest1);
+ });
+ it('should not call loadScript', () => {
+ sinon.assert.notCalled(adloader.loadScript);
+ });
+ });
+
+ describe('callBids twoAdSlotsSingleRequest', () => {
+ beforeEach(() => {
+ randomNumber = 9876543210;
+ sandbox.stub(
+ adloader,
+ 'loadScript'
+ );
+ improveDigitalAdapter.callBids(twoAdSlotsSingleRequest);
+ });
+ it('should call loadScript twice with correct parameters', () => {
+ sinon.assert.calledOnce(adloader.loadScript);
+ sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22pbjs.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%2C%7B%22id%22%3A%224d5e6f%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null);
+ });
+ });
+
+ describe('improveDigitalResponse no response', () => {
+ beforeEach(() => {
+ sandbox.stub(
+ bidmanager,
+ 'addBidResponse'
+ );
+ $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest);
+ improveDigitalAdapter.callBids(simpleBidRequest);
+ $$PREBID_GLOBAL$$.improveDigitalResponse([]);
+ });
+ it('should not call bidmanager.addBidResponse', () => {
+ sinon.assert.notCalled(bidmanager.addBidResponse);
+ });
+ });
+
+ describe('improveDigitalResponse simpleResponse', () => {
+ beforeEach(() => {
+ sandbox.stub(
+ bidmanager,
+ 'addBidResponse'
+ );
+ $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest);
+ improveDigitalAdapter.callBids(simpleBidRequest);
+ $$PREBID_GLOBAL$$.improveDigitalResponse(simpleResponse);
+ });
+ it('should call bidmanager.addBidResponse once with correct parameters', () => {
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 300, height: 300, statusMessage: 'Bid available', ad: ' ', cpm: 1.85185185185185, adId: '1a2b3c'}));
+ });
+ });
+
+ describe('improveDigitalResponse zero bid', () => {
+ beforeEach(() => {
+ randomNumber = 1111111111;
+ sandbox.stub(
+ bidmanager,
+ 'addBidResponse'
+ );
+ $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest);
+ improveDigitalAdapter.callBids(simpleBidRequest);
+ $$PREBID_GLOBAL$$.improveDigitalResponse(zeroPriceResponse);
+ });
+ it('should call bidmanager.addBidResponse once with correct parameters', () => {
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, statusMessage: 'Bid returned empty or error response', adId: '1a2b3c'}));
+ });
+ });
+
+ describe('improveDigitalResponse multipleResponseWithOneNoBid', () => {
+ beforeEach(() => {
+ randomNumber = 1111111111;
+ sandbox.stub(
+ bidmanager,
+ 'addBidResponse'
+ );
+ $$PREBID_GLOBAL$$._bidsRequested.push(twoAdSlots);
+ improveDigitalAdapter.callBids(twoAdSlots);
+ $$PREBID_GLOBAL$$.improveDigitalResponse(multipleResponseWithOneNoBid);
+ });
+ it('should call bidmanager.addBidResponse once with correct parameters', () => {
+ sinon.assert.calledTwice(bidmanager.addBidResponse);
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 300, height: 300, adId: '1a2b3c', statusMessage: 'Bid available', ad: ' ', cpm: 1.85185185185185}));
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement2', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, adId: '4d5e6f', statusMessage: 'Bid returned empty or error response'}));
+ });
+ });
+
+ describe('improveDigitalResponse multipleInvalidResponses', () => {
+ beforeEach(() => {
+ randomNumber = 1111111111;
+ sandbox.stub(
+ bidmanager,
+ 'addBidResponse'
+ );
+ $$PREBID_GLOBAL$$._bidsRequested.push(twoAdSlots);
+ improveDigitalAdapter.callBids(twoAdSlots);
+ $$PREBID_GLOBAL$$.improveDigitalResponse(multipleInvalidResponses);
+ });
+ it('should call bidmanager.addBidResponse twice both with invalid', () => {
+ sinon.assert.calledTwice(bidmanager.addBidResponse);
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, adId: '1a2b3c', statusMessage: 'Bid returned empty or error response'}));
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement2', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, adId: '4d5e6f', statusMessage: 'Bid returned empty or error response'}));
+ });
+ });
+
+ describe('improveDigitalResponse simpleResponseNoSync', () => {
+ beforeEach(() => {
+ sandbox.stub(
+ bidmanager,
+ 'addBidResponse'
+ );
+ $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest);
+ improveDigitalAdapter.callBids(simpleBidRequest);
+ $$PREBID_GLOBAL$$.improveDigitalResponse(simpleResponseNoSync);
+ });
+ it('should call bidmanager.addBidResponse once with correct parameters', () => {
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+ sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 300, height: 300, statusMessage: 'Bid available', ad: ' ', cpm: 1.85185185185185, adId: '1a2b3c'}));
+ });
+ });
+});
diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js
index b30fa083a68..02c07ef85af 100644
--- a/test/spec/modules/rubiconBidAdapter_spec.js
+++ b/test/spec/modules/rubiconBidAdapter_spec.js
@@ -1101,7 +1101,7 @@ describe('the rubicon adapter', () => {
window.$$PREBID_GLOBAL$$.getConfig = origGetConfig;
});
- it('should add the Emily iframe by default', () => {
+ it('should not add the Emily iframe by default', () => {
sinon.stub(window.$$PREBID_GLOBAL$$, 'getConfig', (key) => {
var config = { rubicon: {
userSync: {delay: 10}
@@ -1116,10 +1116,10 @@ describe('the rubicon adapter', () => {
clock.tick(9);
let iframes = document.querySelectorAll('[src="' + emilyUrl + '"]');
expect(iframes.length).to.equal(0);
- // move clock to usersync delay, iframe should have been added
+ // move clock to usersync delay, iframe should still not have been added
clock.tick(1);
iframes = document.querySelectorAll('[src="' + emilyUrl + '"]');
- expect(iframes.length).to.equal(1);
+ expect(iframes.length).to.equal(0);
});
it('should add the Emily iframe when enabled', () => {
diff --git a/test/spec/modules/serverbidBidAdapter_spec.js b/test/spec/modules/serverbidBidAdapter_spec.js
index 598695caf16..dcbd644b715 100644
--- a/test/spec/modules/serverbidBidAdapter_spec.js
+++ b/test/spec/modules/serverbidBidAdapter_spec.js
@@ -3,7 +3,8 @@ import Adapter from 'modules/serverbidBidAdapter';
import bidmanager from 'src/bidmanager';
import * as utils from 'src/utils';
-const ENDPOINT = '//e.serverbid.com/api/v2';
+const ENDPOINT = 'https://e.serverbid.com/api/v2';
+const SMARTSYNC_CALLBACK = 'serverbidCallBids';
const REQUEST = {
'bidderCode': 'serverbid',
@@ -37,15 +38,15 @@ const RESPONSE = {
'creativeId': 1950991,
'flightId': 2788300,
'campaignId': 542982,
- 'clickUrl': 'http://e.serverbid.com/r',
- 'impressionUrl': 'http://e.serverbid.com/i.gif',
+ 'clickUrl': 'https://e.serverbid.com/r',
+ 'impressionUrl': 'https://e.serverbid.com/i.gif',
'contents': [{
'type': 'html',
'body': '',
'data': {
'height': 90,
'width': 728,
- 'imageUrl': 'http://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif',
+ 'imageUrl': 'https://static.adzerk.net/Advertisers/b0ab77db8a7848c8b78931aed022a5ef.gif',
'fileName': 'b0ab77db8a7848c8b78931aed022a5ef.gif'
},
'template': 'image'
@@ -136,6 +137,69 @@ describe('serverbidAdapter', () => {
expect(response.cpm).to.be.above(0);
});
+ describe('with SMARTSYNC=true', () => {
+ it('registers bids when callback is called promptly by smartsync', (done) => {
+ window.SMARTSYNC = true;
+ server.respondWith(JSON.stringify(RESPONSE));
+
+ adapter.callBids(REQUEST);
+
+ setTimeout(() => {
+ window[SMARTSYNC_CALLBACK]();
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const response = bidmanager.addBidResponse.firstCall.args[1];
+ expect(response).to.have.property('statusMessage', 'Bid available');
+ expect(response).to.have.property('cpm');
+ expect(response.cpm).to.be.above(0);
+ window.SMARTSYNC = false;
+ done();
+ }, 0);
+ });
+
+ it('registers bids when callback is never called by smartsync', (done) => {
+ window.SMARTSYNC = true;
+ window.SMARTSYNC_TIMEOUT = 100;
+
+ server.respondWith(JSON.stringify(RESPONSE));
+
+ adapter.callBids(REQUEST);
+ setTimeout(() => {
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const response = bidmanager.addBidResponse.firstCall.args[1];
+ expect(response).to.have.property('statusMessage', 'Bid available');
+ expect(response).to.have.property('cpm');
+ expect(response.cpm).to.be.above(0);
+ window.SMARTSYNC = false;
+ done();
+ }, window.SMARTSYNC_TIMEOUT * 2);
+ });
+
+ it('registers bids when callback is called (but late) by smartsync', (done) => {
+ window.SMARTSYNC = true;
+ window.SMARTSYNC_TIMEOUT = 100;
+
+ server.respondWith(JSON.stringify(RESPONSE));
+
+ adapter.callBids(REQUEST);
+ setTimeout(() => {
+ window[SMARTSYNC_CALLBACK]();
+ server.respond();
+ sinon.assert.calledOnce(bidmanager.addBidResponse);
+
+ const response = bidmanager.addBidResponse.firstCall.args[1];
+ expect(response).to.have.property('statusMessage', 'Bid available');
+ expect(response).to.have.property('cpm');
+ expect(response.cpm).to.be.above(0);
+ window.SMARTSYNC = false;
+ done();
+ }, window.SMARTSYNC_TIMEOUT * 2);
+ });
+ });
+
it('handles nobid responses', () => {
server.respondWith(JSON.stringify({
'decisions': []
diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js
index 070f20958bb..5453e594155 100644
--- a/test/spec/modules/sharethroughBidAdapter_spec.js
+++ b/test/spec/modules/sharethroughBidAdapter_spec.js
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import Adapter from '../../../modules/sharethroughBidAdapter';
import bidManager from '../../../src/bidmanager';
+import bidfactory from '../../../src/bidfactory';
describe('sharethrough adapter', () => {
let adapter;
@@ -69,9 +70,11 @@ describe('sharethrough adapter', () => {
let firstBid;
let secondBid;
let server;
+ let stubAddBidResponse;
+ let stubCreateBid;
beforeEach(() => {
- sandbox.stub(bidManager, 'addBidResponse');
+ stubAddBidResponse = sandbox.stub(bidManager, 'addBidResponse');
server = sinon.fakeServer.create();
$$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest);
@@ -117,6 +120,7 @@ describe('sharethrough adapter', () => {
afterEach(() => {
server.restore();
+ stubAddBidResponse.reset();
});
it('should add a bid object for each bid', () => {
@@ -167,5 +171,70 @@ describe('sharethrough adapter', () => {
expect(firstBid).to.have.property('pkey', 'aaaa1111');
expect(secondBid).to.have.property('pkey', 'bbbb2222');
});
+
+ describe('when bidResponse string cannot be JSON parsed', () => {
+ beforeEach(() => {
+ pbjs._bidsRequested.push(bidderRequest);
+ adapter.str.placementCodeSet['foo'] = {};
+
+ server.respondWith(/aaaa1111/, 'non JSON string');
+ adapter.callBids(bidderRequest);
+
+ server.respond();
+ });
+
+ afterEach(() => {
+ server.restore();
+ stubAddBidResponse.reset();
+ });
+
+ it('should add a bid response', () => {
+ sinon.assert.called(bidManager.addBidResponse);
+ });
+
+ it('should set bidder code on invalid bid response', () => {
+ let bidResponse = bidManager.addBidResponse.firstCall.args[1]
+ expect(bidResponse).to.have.property('bidderCode', 'sharethrough')
+ });
+ });
+
+ describe('when no fill', () => {
+ beforeEach(() => {
+ pbjs._bidsRequested.push(bidderRequest);
+ adapter.str.placementCodeSet['foo'] = {};
+
+ let bidderResponse1 = {
+ 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994',
+ 'bidId': 'bidId1',
+ 'creatives': [
+ {
+ 'cpm': 12.34,
+ 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051',
+ 'version': 1
+ }
+ ],
+ 'stxUserId': ''
+ };
+
+ server.respondWith(/aaaa1111/, JSON.stringify(bidderResponse1));
+ adapter.callBids(bidderRequest);
+
+ server.respond();
+ });
+
+ afterEach(() => {
+ server.restore();
+ stubAddBidResponse.reset();
+ });
+
+ it('should add a bid response', () => {
+ sinon.assert.called(bidManager.addBidResponse);
+ });
+
+ it('should set bidder code on invalid bid response', () => {
+ let bidResponse = bidManager.addBidResponse.firstCall.args[1]
+ expect(bidResponse).to.have.property('bidderCode', 'sharethrough')
+ });
+ });
});
});
diff --git a/test/spec/modules/yieldbotBidAdapter_spec.js b/test/spec/modules/yieldbotBidAdapter_spec.js
index bad1368c0f0..a29d441e751 100644
--- a/test/spec/modules/yieldbotBidAdapter_spec.js
+++ b/test/spec/modules/yieldbotBidAdapter_spec.js
@@ -2,6 +2,7 @@ import {expect} from 'chai';
import YieldbotAdapter from 'modules/yieldbotBidAdapter';
import bidManager from 'src/bidmanager';
import adLoader from 'src/adloader';
+import {cloneJson} from 'src/utils';
const bidderRequest = {
bidderCode: 'yieldbot',
@@ -14,7 +15,7 @@ const bidderRequest = {
bidder: 'yieldbot',
bidderRequestId: '187a340cb9ccc0',
params: { psn: '1234', slot: 'medrec' },
- requestId: '5f297a1f-3163-46c2-854f-b55fd2e74ece',
+ requestId: '5f297a1f-3163-46c2-854f-b55fd2e74ec0',
placementCode: '/4294967296/adunit0'
},
{
@@ -23,9 +24,18 @@ const bidderRequest = {
bidder: 'yieldbot',
bidderRequestId: '187a340cb9ccc1',
params: { psn: '1234', slot: 'leaderboard' },
- requestId: '5f297a1f-3163-46c2-854f-b55fd2e74ece',
+ requestId: '5f297a1f-3163-46c2-854f-b55fd2e74ec1',
placementCode: '/4294967296/adunit1'
- }
+ },
+ {
+ bidId: '2640ad280208cd',
+ sizes: [[300, 250]],
+ bidder: 'yieldbot',
+ bidderRequestId: '187a340cb9ccc2',
+ params: { psn: '1234', slot: 'medrec' },
+ requestId: '5f297a1f-3163-46c2-854f-b55fd2e74ec2',
+ placementCode: '/4294967296/adunit2'
+ },
]
};
@@ -38,6 +48,12 @@ const YB_BID_FIXTURE = {
},
leaderboard: {
ybot_ad: 'n'
+ },
+ noop: {
+ ybot_ad: 'y',
+ ybot_slot: 'noop',
+ ybot_cpm: '200',
+ ybot_size: '300x250'
}
};
@@ -47,11 +63,9 @@ function createYieldbotMockLib() {
pub: (psn) => {},
defineSlot: (slotName, optionalDomIdOrConfigObject, optionalTime) => {},
enableAsync: () => {},
- go: () => { window.yieldbot._initialized = true; },
+ go: () => {},
nextPageview: (slots, callback) => {},
- getSlotCriteria: (slotName) => {
- return YB_BID_FIXTURE[slotName] || {ybot_ad: 'n'};
- }
+ getSlotCriteria: (slotName) => {}
};
}
@@ -67,28 +81,63 @@ function mockYieldbotBidRequest() {
window.ybotq = [];
}
+const localSetupTestRegex = /localSetupTest$/;
+const MAKE_BID_REQUEST = true;
let sandbox;
let bidManagerStub;
let yieldbotLibStub;
-beforeEach(function() {
- window.$$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest);
-});
-
-function setupTest() {
+/**
+ * Test initialization hook. Makes initial adapter and mock bid requests
+ * unless the test is a special case with "localSetupTest".
+ * 1. All suite tests are initialized with required mocks and stubs
+ * 2. If the test title does not end in "localSetupTest", adapter and
+ * mock bid requests are executed
+ * 3. Test titles ending in "localSetupTest" are special case tests and are
+ * expected to call setupTest(object, MAKE_BID_REQUEST)
where
+ * applicable
+ * @param {object} testRequest bidder request bids fixture
+ * @param {boolean} force trigger adapter callBids and Yieldbot library request
+ * @private
+ */
+function setupTest(testRequest, force = false) {
sandbox = sinon.sandbox.create();
createYieldbotMockLib();
sandbox.stub(adLoader, 'loadScript');
- yieldbotLibStub = sandbox.stub(window.yieldbot);
- yieldbotLibStub.getSlotCriteria.restore();
+
+ yieldbotLibStub = {};
+ yieldbotLibStub.nextPageview = sandbox.stub(window.yieldbot, 'nextPageview');
+ yieldbotLibStub.defineSlot = sandbox.stub(window.yieldbot, 'defineSlot');
+ yieldbotLibStub.pub = sandbox.stub(window.yieldbot, 'pub');
+ yieldbotLibStub.enableAsync = sandbox.stub(window.yieldbot, 'enableAsync');
+
+ yieldbotLibStub.getSlotCriteria =
+ sandbox.stub(
+ window.yieldbot,
+ 'getSlotCriteria',
+ (slotName) => {
+ return YB_BID_FIXTURE[slotName] || {ybot_ad: 'n'};
+ });
+
+ yieldbotLibStub.go =
+ sandbox.stub(
+ window.yieldbot,
+ 'go',
+ () => {
+ window.yieldbot._initialized = true;
+ });
bidManagerStub = sandbox.stub(bidManager, 'addBidResponse');
- const adapter = new YieldbotAdapter();
- adapter.callBids(bidderRequest);
- mockYieldbotBidRequest();
+ const ybAdapter = new YieldbotAdapter();
+ let request = testRequest || cloneJson(bidderRequest);
+ if ((this && !this.currentTest.parent.title.match(localSetupTestRegex)) || force === MAKE_BID_REQUEST) {
+ ybAdapter.callBids(request);
+ mockYieldbotBidRequest();
+ }
+ return { adapter: ybAdapter, localRequest: request };
}
function restoreTest() {
@@ -97,15 +146,71 @@ function restoreTest() {
}
describe('Yieldbot adapter tests', function() {
- describe('callBids', function() {
- beforeEach(function () {
- setupTest();
+ let adapter;
+ let localRequest;
+ beforeEach(function () {
+ const testSetupCtx = setupTest.call(this);
+ adapter = testSetupCtx.adapter;
+ localRequest = testSetupCtx.localRequest;
+ });
+
+ afterEach(function() {
+ restoreTest();
+ });
+
+ describe('getUniqueSlotSizes', function() {
+ it('should return [] for string sizes', function() {
+ const sizes = adapter.getUniqueSlotSizes('widthxheight');
+ expect(sizes).to.deep.equal([]);
});
- afterEach(function() {
- restoreTest();
+ it('should return [] for Object sizes', function() {
+ const sizes = adapter.getUniqueSlotSizes({width: 300, height: 250});
+ expect(sizes).to.deep.equal([]);
+ });
+
+ it('should return [] for boolean sizes', function() {
+ const sizes = adapter.getUniqueSlotSizes(true);
+ expect(sizes).to.deep.equal([]);
});
+ it('should return [] for undefined sizes', function() {
+ const sizes = adapter.getUniqueSlotSizes(undefined);
+ expect(sizes).to.deep.equal([]);
+ });
+
+ it('should return [] for function sizes', function() {
+ const sizes = adapter.getUniqueSlotSizes(function () {});
+ expect(sizes).to.deep.equal([]);
+ });
+
+ it('should return [] for number sizes', function() {
+ const sizes = adapter.getUniqueSlotSizes(1111);
+ expect(sizes).to.deep.equal([]);
+ });
+
+ it('should return [] for array of numbers', function() {
+ const sizes = adapter.getUniqueSlotSizes([300, 250]);
+ expect(sizes).to.deep.equal([]);
+ });
+
+ it('should return array of unique strings', function() {
+ const sizes = adapter.getUniqueSlotSizes(['300x250', '300x600', '728x90', '300x250']);
+ expect(sizes).to.deep.equal([['300', '250'], ['300', '600'], ['728', '90']]);
+ });
+
+ it('should return array of unique strings for string elements only', function() {
+ const sizes = adapter.getUniqueSlotSizes(['300x250', ['threexfour']]);
+ expect(sizes).to.deep.equal([['300', '250']]);
+ });
+
+ it('should return array of unique strings, including non-numeric', function() {
+ const sizes = adapter.getUniqueSlotSizes(['300x250', 'threexfour', 'fivexsix']);
+ expect(sizes).to.deep.equal([['300', '250'], ['three', 'four'], ['five', 'si']]);
+ });
+ });
+
+ describe('callBids', function() {
it('should request the yieldbot library', function() {
sinon.assert.calledOnce(adLoader.loadScript);
sinon.assert.calledWith(adLoader.loadScript, '//cdn.yldbt.com/js/yieldbot.intent.js');
@@ -116,24 +221,35 @@ describe('Yieldbot adapter tests', function() {
sinon.assert.calledWith(yieldbotLibStub.pub, '1234');
});
- it('should define yieldbot slots', function() {
+ it('should not repeat multiply defined slot sizes', function() {
sinon.assert.calledTwice(yieldbotLibStub.defineSlot);
- sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'medrec', {sizes: [[300, 250], [300, 600]]});
- sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'leaderboard', {sizes: [[728, 90], [970, 90]]});
+ sinon.assert.neverCalledWith(yieldbotLibStub.defineSlot, 'medrec', {sizes: [['300', '250'], ['300', '600'], ['300', '250']]});
});
- it('should not use inherited Object properties', function() {
- restoreTest();
+ it('should define yieldbot slots', function() {
+ sinon.assert.calledTwice(yieldbotLibStub.defineSlot);
+ sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'medrec', {sizes: [['300', '250'], ['300', '600']]});
+ sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'leaderboard', {sizes: [['728', '90'], ['970', '90']]});
+ });
+ it('should not use inherited Object properties, localSetupTest', function() {
let oProto = Object.prototype;
- oProto.superProp = [300, 250];
+ oProto.superProp = ['300', '250'];
expect(Object.prototype.superProp).to.be.an('array');
- setupTest();
+ localRequest.bids.forEach((bid) => {
+ expect(bid.superProp).to.be.an('array');
+ });
+
+ expect(YB_BID_FIXTURE.medrec.superProp).to.deep.equal(['300', '250']);
+ expect(YB_BID_FIXTURE.leaderboard.superProp).to.deep.equal(['300', '250']);
- sinon.assert.neverCalledWith(yieldbotLibStub.defineSlot, 'superProp', {sizes: [300, 250]});
- sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'medrec', {sizes: [[300, 250], [300, 600]]});
- sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'leaderboard', {sizes: [[728, 90], [970, 90]]});
+ restoreTest();
+ setupTest(localRequest, MAKE_BID_REQUEST);
+
+ sinon.assert.neverCalledWith(yieldbotLibStub.defineSlot, 'superProp', {sizes: ['300', '250']});
+ sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'medrec', {sizes: [['300', '250'], ['300', '600']]});
+ sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'leaderboard', {sizes: [['728', '90'], ['970', '90']]});
delete oProto.superProp;
expect(Object.prototype.superProp).to.be.an('undefined');
@@ -145,7 +261,7 @@ describe('Yieldbot adapter tests', function() {
it('should add bid response after yieldbot request callback', function() {
const plc1 = bidManagerStub.firstCall.args[0];
- expect(plc1).to.equal(bidderRequest.bids[0].placementCode);
+ expect(plc1).to.equal(localRequest.bids[0].placementCode);
const pb_bid1 = bidManagerStub.firstCall.args[1];
expect(pb_bid1.bidderCode).to.equal('yieldbot');
@@ -161,7 +277,7 @@ describe('Yieldbot adapter tests', function() {
expect(pb_bid1.ad).to.match(/yieldbot\.renderAd\('medrec:300x250'\)/);
const plc2 = bidManagerStub.secondCall.args[0];
- expect(plc2).to.equal(bidderRequest.bids[1].placementCode);
+ expect(plc2).to.equal(localRequest.bids[1].placementCode);
const pb_bid2 = bidManagerStub.secondCall.args[1];
expect(pb_bid2.bidderCode).to.equal('yieldbot');
@@ -169,104 +285,234 @@ describe('Yieldbot adapter tests', function() {
expect(pb_bid2.height).to.equal(0);
expect(pb_bid2.statusMessage).to.match(/empty.*response/);
});
- });
- describe('callBids, refresh', function() {
- beforeEach(function () {
- if (sandbox) { sandbox.restore(); }
- sandbox = sinon.sandbox.create();
+ it('should validate slot dimensions, localSetupTest', function() {
+ let invalidSizeBid = {
+ bidId: '2640ad280208ce',
+ sizes: [[728, 90], [300, 250], [970, 90]],
+ bidder: 'yieldbot',
+ bidderRequestId: '187a340cb9ccc3',
+ params: { psn: '1234', slot: 'medrec' },
+ requestId: '5f297a1f-3163-46c2-854f-b55fd2e74ec3',
+ placementCode: '/4294967296/adunit3'
+ };
+
+ const bidResponseMedrec = {
+ bidderCode: 'yieldbot',
+ width: '300',
+ height: '250',
+ statusMessage: 'Bid available',
+ cpm: 2,
+ ybot_ad: 'y',
+ ybot_slot: 'medrec',
+ ybot_cpm: '200',
+ ybot_size: '300x250'
+ };
+
+ localRequest.bids = [invalidSizeBid];
+ restoreTest();
+ setupTest(localRequest, MAKE_BID_REQUEST);
- createYieldbotMockLib();
+ let bidManagerFirstCall = bidManagerStub.firstCall;
- sandbox.stub(adLoader, 'loadScript');
- yieldbotLibStub = sandbox.stub(window.yieldbot);
- yieldbotLibStub.getSlotCriteria.restore();
- yieldbotLibStub.go.restore();
- bidManagerStub = sandbox.stub(bidManager, 'addBidResponse');
+ expect(bidManagerFirstCall.args[0]).to.equal('/4294967296/adunit3');
+ expect(bidManagerFirstCall.args[1]).to.include(bidResponseMedrec);
});
- afterEach(function() {
- sandbox.restore();
- restoreYieldbotMockLib();
+ it('should make slot bid available once only', function() {
+ const bidResponseMedrec = {
+ bidderCode: 'yieldbot',
+ width: '300',
+ height: '250',
+ statusMessage: 'Bid available',
+ cpm: 2,
+ ybot_ad: 'y',
+ ybot_slot: 'medrec',
+ ybot_cpm: '200',
+ ybot_size: '300x250'
+ };
+
+ const bidResponseNone = {
+ bidderCode: 'yieldbot',
+ width: 0,
+ height: 0,
+ statusMessage: 'Bid returned empty or error response'
+ };
+
+ let firstCall = bidManagerStub.firstCall;
+ let secondCall = bidManagerStub.secondCall;
+ let thirdCall = bidManagerStub.thirdCall;
+
+ expect(firstCall.args[0]).to.equal('/4294967296/adunit0');
+ expect(firstCall.args[1]).to.include(bidResponseMedrec);
+
+ expect(secondCall.args[0]).to.equal('/4294967296/adunit1');
+ expect(secondCall.args[1]).to.include(bidResponseNone);
+
+ expect(thirdCall.args[0]).to.equal('/4294967296/adunit2');
+ expect(thirdCall.args[1]).to.include(bidResponseNone);
});
+ });
+ describe('callBids, refresh', function() {
it('should use yieldbot.nextPageview after first callBids', function() {
- const adapter = new YieldbotAdapter();
- adapter.callBids(bidderRequest);
- mockYieldbotBidRequest();
-
expect(window.yieldbot._initialized).to.equal(true);
- adapter.callBids(bidderRequest);
+ adapter.callBids(localRequest);
mockYieldbotBidRequest();
sinon.assert.calledOnce(yieldbotLibStub.nextPageview);
});
it('should call yieldbot.nextPageview with slot config of requested bids', function() {
- window.$$PREBID_GLOBAL$$._bidsRequested = window.$$PREBID_GLOBAL$$._bidsRequested.filter(o => {
- return o.bidderCode !== 'yieldbot';
- });
-
- const adapter = new YieldbotAdapter();
- adapter.callBids(bidderRequest);
- mockYieldbotBidRequest();
-
expect(window.yieldbot._initialized).to.equal(true);
sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'medrec');
sinon.assert.calledWith(yieldbotLibStub.defineSlot, 'leaderboard');
- window.$$PREBID_GLOBAL$$._bidsRequested = window.$$PREBID_GLOBAL$$._bidsRequested.filter(o => {
- return o.bidderCode !== 'yieldbot';
- });
-
- const refreshBids = bidderRequest.bids.filter((object) => { return object.placementCode === '/4294967296/adunit1'; });
- let refreshRequest = Object.assign({}, bidderRequest);
+ const refreshBids = localRequest.bids.filter((object) => { return object.placementCode === '/4294967296/adunit1'; });
+ let refreshRequest = cloneJson(localRequest);
refreshRequest.bids = refreshBids;
expect(refreshRequest.bids.length).to.equal(1);
adapter.callBids(refreshRequest);
mockYieldbotBidRequest();
- const bid = refreshBids[0];
- const expectedSlots = { 'leaderboard': [[728, 90], [970, 90]] };
+ const expectedSlots = { 'leaderboard': [['728', '90'], ['970', '90']] };
sinon.assert.calledWithExactly(yieldbotLibStub.nextPageview, expectedSlots);
});
- it('should not throw on callBids without bidsRequested', function() {
- const adapter = new YieldbotAdapter();
- adapter.callBids(bidderRequest);
+ it('should not repeat multiply defined slot sizes', function() {
+ // placementCode: '/4294967296/adunit0'
+ // placementCode: '/4294967296/adunit2'
+ // Both placements declare medrec:300x250
+ adapter.callBids(localRequest);
mockYieldbotBidRequest();
+ sinon.assert.calledOnce(yieldbotLibStub.nextPageview);
+ const expectedSlots = { 'leaderboard': [['728', '90'], ['970', '90']], 'medrec': [['300', '250'], ['300', '600']]};
+ sinon.assert.calledWithExactly(yieldbotLibStub.nextPageview, expectedSlots);
+ });
+
+ it('should not add empty bidResponse on callBids without bidsRequested', function() {
expect(window.yieldbot._initialized).to.equal(true);
+ expect(bidManagerStub.calledThrice).to.equal(true);
+
+ adapter.callBids({});
+ mockYieldbotBidRequest();
+
+ expect(bidManagerStub.calledThrice).to.equal(true); // the initial bids
+ sinon.assert.notCalled(yieldbotLibStub.nextPageview);
+ });
- window.$$PREBID_GLOBAL$$._bidsRequested = window.$$PREBID_GLOBAL$$._bidsRequested.filter(o => {
- return o.bidderCode !== 'yieldbot';
+ it('should validate slot dimensions', function() {
+ localRequest.bids.map(bid => {
+ bid.sizes = [[640, 480], [1024, 768]];
});
- adapter.callBids(bidderRequest);
+ const bidResponseNone = {
+ bidderCode: 'yieldbot',
+ width: 0,
+ height: 0,
+ statusMessage: 'Bid returned empty or error response'
+ };
+
+ adapter.callBids(localRequest);
mockYieldbotBidRequest();
- sinon.assert.calledOnce(yieldbotLibStub.nextPageview);
+
+ expect(bidManagerStub.getCalls().length).to.equal(6);
+
+ let lastNextPageview = yieldbotLibStub.nextPageview.lastCall;
+ let nextPageviewSlots = lastNextPageview.args[0];
+ expect(nextPageviewSlots.medrec).to.deep.equal([['640', '480'], ['1024', '768']]);
+ expect(nextPageviewSlots.leaderboard).to.deep.equal([['640', '480'], ['1024', '768']]);
+
+ let fourthCall = bidManagerStub.getCall(3);
+ let fifthCall = bidManagerStub.getCall(4);
+ let sixthCall = bidManagerStub.getCall(5);
+
+ expect(fourthCall.args[0]).to.equal('/4294967296/adunit0');
+ expect(fourthCall.args[1]).to.include(bidResponseNone);
+
+ expect(fifthCall.args[0]).to.equal('/4294967296/adunit1');
+ expect(fifthCall.args[1]).to.include(bidResponseNone);
+
+ expect(sixthCall.args[0]).to.equal('/4294967296/adunit2');
+ expect(sixthCall.args[1]).to.include(bidResponseNone);
});
- it('should not add empty bidResponse on callBids without bidsRequested', function() {
- window.$$PREBID_GLOBAL$$._bidsRequested = window.$$PREBID_GLOBAL$$._bidsRequested.filter(o => {
- return o.bidderCode !== 'yieldbot';
+ it('should not make requests for previously requested bids', function() {
+ const bidResponseMedrec = {
+ bidderCode: 'yieldbot',
+ width: '300',
+ height: '250',
+ statusMessage: 'Bid available',
+ cpm: 2,
+ ybot_ad: 'y',
+ ybot_slot: 'medrec',
+ ybot_cpm: '200',
+ ybot_size: '300x250'
+ };
+
+ const bidResponseNone = {
+ bidderCode: 'yieldbot',
+ width: 0,
+ height: 0,
+ statusMessage: 'Bid returned empty or error response'
+ };
+
+ // Refresh #1
+ adapter.callBids(localRequest);
+ mockYieldbotBidRequest();
+
+ expect(bidManagerStub.getCalls().length).to.equal(6);
+
+ let lastNextPageview = yieldbotLibStub.nextPageview.lastCall;
+ let nextPageviewSlots = lastNextPageview.args[0];
+ expect(nextPageviewSlots.medrec).to.deep.equal([['300', '250'], ['300', '600']]);
+ expect(nextPageviewSlots.leaderboard).to.deep.equal([['728', '90'], ['970', '90']]);
+
+ let fourthCall = bidManagerStub.getCall(3);
+ let fifthCall = bidManagerStub.getCall(4);
+ let sixthCall = bidManagerStub.getCall(5);
+
+ expect(fourthCall.args[0]).to.equal('/4294967296/adunit0');
+ expect(fourthCall.args[1]).to.include(bidResponseMedrec);
+
+ expect(fifthCall.args[0]).to.equal('/4294967296/adunit1');
+ expect(fifthCall.args[1]).to.include(bidResponseNone);
+
+ expect(sixthCall.args[0]).to.equal('/4294967296/adunit2');
+ expect(sixthCall.args[1]).to.include(bidResponseNone);
+
+ localRequest.bids.map(bid => {
+ bid.sizes = [[640, 480], [1024, 768]];
});
+ let bidForNinethCall = localRequest.bids[localRequest.bids.length - 1];
+ bidForNinethCall.sizes = [[300, 250]];
- const adapter = new YieldbotAdapter();
- adapter.callBids(bidderRequest);
+ // Refresh #2
+ adapter.callBids(localRequest);
mockYieldbotBidRequest();
- let bidResponses = window.$$PREBID_GLOBAL$$._bidsReceived.filter(o => {
- return o.bidderCode === 'yieldbot';
- });
+ expect(bidManagerStub.getCalls().length).to.equal(9);
- expect(bidResponses.length).to.equal(0);
+ lastNextPageview = yieldbotLibStub.nextPageview.lastCall;
+ nextPageviewSlots = lastNextPageview.args[0];
+ expect(nextPageviewSlots.medrec).to.deep.equal([['640', '480'], ['1024', '768'], ['300', '250']]);
+ expect(nextPageviewSlots.leaderboard).to.deep.equal([['640', '480'], ['1024', '768']]);
- adapter.callBids(bidderRequest);
- mockYieldbotBidRequest();
- sinon.assert.calledOnce(yieldbotLibStub.nextPageview);
+ let seventhCall = bidManagerStub.getCall(6);
+ let eighthCall = bidManagerStub.getCall(7);
+ let ninethCall = bidManagerStub.getCall(8);
+
+ expect(seventhCall.args[0]).to.equal('/4294967296/adunit0');
+ expect(seventhCall.args[1]).to.include(bidResponseNone);
+
+ expect(eighthCall.args[0]).to.equal('/4294967296/adunit1');
+ expect(eighthCall.args[1]).to.include(bidResponseNone);
+
+ expect(ninethCall.args[0]).to.equal('/4294967296/adunit2');
+ expect(ninethCall.args[1]).to.include(bidResponseMedrec);
});
});
});
diff --git a/test/spec/unit/bidmanager_spec.js b/test/spec/unit/bidmanager_spec.js
index f70c9906440..d51a0ab8a8f 100644
--- a/test/spec/unit/bidmanager_spec.js
+++ b/test/spec/unit/bidmanager_spec.js
@@ -59,7 +59,7 @@ describe('The Bid Manager', () => {
expect(bid.cpm).to.equal(adjustCpm(0.1));
if (usePayloadResponse) {
- expect(bid.vastPayload).to.equal(' ');
+ expect(bid.vastXml).to.equal(' ');
if (usePrebidCache) {
expect(bid.vastUrl).to.equal(`https://prebid.adnxs.com/pbc/v1/cache?uuid=FAKE_UUID`);
}
@@ -176,7 +176,7 @@ describe('The Bid Manager', () => {
it('should add bids with a vastUrl and then execute the callbacks signaling the end of the auction',
testAddVideoBid(true, true, stubProvider, false));
- it('should add bids with a vastPayload and then execute the callbacks signaling the end of the auction',
+ it('should add bids with a vastXml and then execute the callbacks signaling the end of the auction',
testAddVideoBid(true, true, stubProvider, true));
it('should gracefully do nothing when adUnitCode is undefined', () => {
diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js
index df8c070df97..ba322f23a1d 100644
--- a/test/spec/unit/pbjs_api_spec.js
+++ b/test/spec/unit/pbjs_api_spec.js
@@ -339,6 +339,94 @@ describe('Unit: Prebid Module', function () {
});
});
+ describe('getAdserverTargeting', function() {
+ const customConfigObject = {
+ 'buckets': [
+ { 'precision': 2, 'min': 0, 'max': 5, 'increment': 0.01 },
+ { 'precision': 2, 'min': 5, 'max': 8, 'increment': 0.05},
+ { 'precision': 2, 'min': 8, 'max': 20, 'increment': 0.5 },
+ { 'precision': 2, 'min': 20, 'max': 25, 'increment': 1 }
+ ]
+ };
+ let currentPriceBucket;
+ let bid;
+
+ before(() => {
+ resetAuction();
+ currentPriceBucket = configObj.getConfig('priceGranularity');
+ configObj.setConfig({ priceGranularity: customConfigObject });
+ bid = Object.assign({},
+ bidfactory.createBid(2),
+ getBidResponses()[5]
+ );
+ });
+
+ after(() => {
+ configObj.setConfig({ priceGranularity: currentPriceBucket });
+ resetAuction();
+ })
+
+ beforeEach(() => {
+ $$PREBID_GLOBAL$$._bidsReceived = [];
+ })
+
+ it('should get correct hb_pb when using bid.cpm is between 0 to 5', () => {
+ bid.cpm = 2.1234;
+ bidmanager.addBidResponse(bid.adUnitCode, bid);
+ var expected = {
+ '/19968336/header-bid-tag-0': {
+ hb_adid: '275bd666f5a5a5d',
+ hb_bidder: 'brealtime',
+ hb_pb: '2.12',
+ hb_size: '300x250'
+ }
+ }
+ expect($$PREBID_GLOBAL$$.getAdserverTargeting()).to.deep.equal(expected);
+ });
+
+ it('should get correct hb_pb when using bid.cpm is between 5 to 8', () => {
+ bid.cpm = 6.78;
+ bidmanager.addBidResponse(bid.adUnitCode, bid);
+ var expected = {
+ '/19968336/header-bid-tag-0': {
+ hb_adid: '275bd666f5a5a5d',
+ hb_bidder: 'brealtime',
+ hb_pb: '6.75',
+ hb_size: '300x250'
+ }
+ }
+ expect($$PREBID_GLOBAL$$.getAdserverTargeting()).to.deep.equal(expected);
+ });
+
+ it('should get correct hb_pb when using bid.cpm is between 8 to 20', () => {
+ bid.cpm = 19.5234;
+ bidmanager.addBidResponse(bid.adUnitCode, bid);
+ var expected = {
+ '/19968336/header-bid-tag-0': {
+ hb_adid: '275bd666f5a5a5d',
+ hb_bidder: 'brealtime',
+ hb_pb: '19.50',
+ hb_size: '300x250'
+ }
+ }
+ expect($$PREBID_GLOBAL$$.getAdserverTargeting()).to.deep.equal(expected);
+ });
+
+ it('should get correct hb_pb when using bid.cpm is between 20 to 25', () => {
+ bid.cpm = 21.5234;
+ bidmanager.addBidResponse(bid.adUnitCode, bid);
+ var expected = {
+ '/19968336/header-bid-tag-0': {
+ hb_adid: '275bd666f5a5a5d',
+ hb_bidder: 'brealtime',
+ hb_pb: '21.00',
+ hb_size: '300x250'
+ }
+ }
+ expect($$PREBID_GLOBAL$$.getAdserverTargeting()).to.deep.equal(expected);
+ });
+ });
+
describe('getBidResponses', function () {
it('should return expected bid responses when not passed an adunitCode', function () {
var result = $$PREBID_GLOBAL$$.getBidResponses();
@@ -1300,7 +1388,7 @@ describe('Unit: Prebid Module', function () {
let priceGranularity = configObj.getConfig('priceGranularity');
let newCustomPriceBucket = configObj.getConfig('customPriceBucket');
expect(goodConfig).to.deep.equal(newCustomPriceBucket);
- expect(priceGranularity).to.equal(CONSTANTS.GRANULARITY_OPTIONS.MEDIUM);
+ expect(priceGranularity).to.equal(CONSTANTS.GRANULARITY_OPTIONS.CUSTOM);
});
});
diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js
index 2bbdcbaf6e6..e8cb041ebf2 100755
--- a/test/spec/utils_spec.js
+++ b/test/spec/utils_spec.js
@@ -524,4 +524,34 @@ describe('Utils', function () {
assert.equal(arr.length, count, 'Polyfill test fails')
});
});
+
+ describe('deepAccess', function() {
+ var obj = {
+ 1: 2,
+ test: {
+ first: 11
+ }
+ };
+
+ it('should allow deep access of object properties', function() {
+ var value1 = utils.deepAccess(obj, 'test');
+ assert.deepEqual(value1, obj.test);
+
+ var value2 = utils.deepAccess(obj, 'test.first');
+ assert.equal(value2, 11);
+
+ var value3 = utils.deepAccess(obj, 1);
+ assert.equal(value3, 2);
+ });
+
+ it('should allow safe access (returning undefined for missing properties and not throwing exceptions)', function() {
+ var value;
+
+ assert.doesNotThrow(function() {
+ value = utils.deepAccess(obj, 'test.second.third');
+ });
+
+ assert.equal(value, undefined);
+ });
+ });
});
diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js
index 75118855405..e9af314218e 100644
--- a/test/spec/videoCache_spec.js
+++ b/test/spec/videoCache_spec.js
@@ -92,9 +92,9 @@ describe('The video cache', () => {
assertRequestMade({ vastUrl: 'my-mock-url.com' }, expectedValue)
});
- it('should make the expected request when store() is called on an ad with a vastPayload', () => {
- const vastPaload = ' ';
- assertRequestMade({ vastPayload: vastPaload }, vastPaload);
+ it('should make the expected request when store() is called on an ad with vastXml', () => {
+ const vastXml = ' ';
+ assertRequestMade({ vastXml: vastXml }, vastXml);
});
function assertRequestMade(bid, expectedValue) {
diff --git a/yarn.lock b/yarn.lock
index d14d5c36a6e..949c467070e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -95,7 +95,7 @@ ajv@^4.7.0, ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
-ajv@^5.1.5, ajv@^5.2.0:
+ajv@^5.0.0, ajv@^5.1.5, ajv@^5.2.0:
version "5.2.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39"
dependencies:
@@ -1743,7 +1743,7 @@ content-type@~1.0.1, content-type@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
-convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.3.0, convert-source-map@^1.5.0:
+convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
@@ -4133,14 +4133,14 @@ istanbul-api@^1.1.8:
mkdirp "^0.5.1"
once "^1.4.0"
-istanbul-instrumenter-loader@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz#e5492900ab0bba835efa8024cb00be9b3eea2700"
+istanbul-instrumenter-loader@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz#9f553923b22360bac95e617aaba01add1f7db0b2"
dependencies:
- convert-source-map "^1.3.0"
- istanbul-lib-instrument "^1.1.3"
- loader-utils "^0.2.16"
- object-assign "^4.1.0"
+ convert-source-map "^1.5.0"
+ istanbul-lib-instrument "^1.7.3"
+ loader-utils "^1.1.0"
+ schema-utils "^0.3.0"
istanbul-lib-coverage@^1.1.1:
version "1.1.1"
@@ -4152,7 +4152,19 @@ istanbul-lib-hook@^1.0.7:
dependencies:
append-transform "^0.4.0"
-istanbul-lib-instrument@^1.1.3, istanbul-lib-instrument@^1.7.4:
+istanbul-lib-instrument@^1.7.3:
+ version "1.7.5"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.5.tgz#adb596f8f0cb8b95e739206351a38a586af21b1e"
+ dependencies:
+ babel-generator "^6.18.0"
+ babel-template "^6.16.0"
+ babel-traverse "^6.18.0"
+ babel-types "^6.18.0"
+ babylon "^6.17.4"
+ istanbul-lib-coverage "^1.1.1"
+ semver "^5.3.0"
+
+istanbul-lib-instrument@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz#e9fd920e4767f3d19edc765e2d6b3f5ccbd0eea8"
dependencies:
@@ -4618,7 +4630,7 @@ loader-runner@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
-loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5:
+loader-utils@^0.2.11, loader-utils@^0.2.5, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5:
version "0.2.17"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
dependencies:
@@ -6599,6 +6611,12 @@ saucelabs@^1.4.0:
dependencies:
https-proxy-agent "^1.0.0"
+schema-utils@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
+ dependencies:
+ ajv "^5.0.0"
+
"semver@2 || 3 || 4 || 5", semver@^5.3.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"