diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dc8d80ec3849..7f4127cf3baa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -55,7 +55,7 @@ When you are adding code to Prebid.js, or modifying code that isn't covered by a
Prebid.js already has many tests. Read them to see how Prebid.js is tested, and for inspiration:
- Look in `test/spec` and its subdirectories
-- Tests for bidder adaptors are located in `test/spec/adapters`
+- Tests for bidder adaptors are located in `test/spec/modules`
A test module might have the following general structure:
diff --git a/RELEASE_SCHEDULE.md b/RELEASE_SCHEDULE.md
index efdc45f7f9fe..4f5c7fc4e9a6 100644
--- a/RELEASE_SCHEDULE.md
+++ b/RELEASE_SCHEDULE.md
@@ -91,6 +91,21 @@ Announcements regarding releases will be made to the #headerbidding-dev channel
git push
```
+## Beta Releases
+
+Prebid.js features may be released as Beta or as Generally Available (GA).
+
+Characteristics of a `Beta` release:
+- May be a partial implementation (e.g. more work needed to flesh out the feature)
+- May not be fully tested with other features
+- Limited documentation, focused on technical aspects
+- Few users
+
+Characteristics of a `GA` release:
+- Complete set of functionality
+- Significant user base with no major issues for at least a month
+- Decent documentation that includes business need, use cases, and examples
+
## FAQs
diff --git a/browsers.json b/browsers.json
index cb523addc7ef..703bf44d41d6 100644
--- a/browsers.json
+++ b/browsers.json
@@ -62,21 +62,5 @@
"browser_version": "8.0",
"device": null,
"os": "OS X"
- },
- "bs_ios_9": {
- "base": "BrowserStack",
- "os": "ios",
- "os_version": "9.1",
- "browser": "iphone",
- "device": "iPhone 6S",
- "browser_version": null
- },
- "bs_ios_8": {
- "base": "BrowserStack",
- "os": "ios",
- "os_version": "8.3",
- "browser": "iphone",
- "device": "iPhone 6",
- "browser_version": null
}
}
\ No newline at end of file
diff --git a/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html
index f1ec912fd265..e54a604e2817 100644
--- a/integrationExamples/gpt/pbjs_example_gpt.html
+++ b/integrationExamples/gpt/pbjs_example_gpt.html
@@ -269,6 +269,13 @@
placement_id: 0
}
},
+ {
+ bidder: 'weborama',
+ params: {
+ placementId: 0,
+ traffic: 'banner'
+ }
+ },
{
bidder: 'pollux',
params: {
diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js
index 6851a7d3bd54..679e11270abf 100644
--- a/modules/admixerBidAdapter.js
+++ b/modules/admixerBidAdapter.js
@@ -25,7 +25,7 @@ export const spec = {
buildRequests: function (bidderRequest) {
const payload = {
imps: [],
- referrer: utils.getTopWindowUrl(),
+ referrer: encodeURIComponent(utils.getTopWindowUrl()),
};
bidderRequest.forEach((bid) => {
if (bid.bidder === BIDDER_CODE) {
diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js
index a61719fe4951..b9f57115e218 100644
--- a/modules/adyoulikeBidAdapter.js
+++ b/modules/adyoulikeBidAdapter.js
@@ -55,7 +55,7 @@ export const spec = {
const data = JSON.stringify(payload);
const options = {
- withCredentials: false
+ withCredentials: true
};
return {
diff --git a/modules/audienceNetworkBidAdapter.js b/modules/audienceNetworkBidAdapter.js
index 612357e0e4ab..7256ce99e73d 100644
--- a/modules/audienceNetworkBidAdapter.js
+++ b/modules/audienceNetworkBidAdapter.js
@@ -15,7 +15,9 @@ const url = 'https://an.facebook.com/v2/placementbid.json';
const supportedMediaTypes = ['banner', 'video'];
const netRevenue = true;
const hb_bidder = 'fan';
-const pbv = '$prebid.version$';
+const platver = '$prebid.version$';
+const platform = '241394079772386';
+const adapterver = '1.0.0';
/**
* Does this bid request contain valid parameters?
@@ -166,7 +168,9 @@ const buildRequests = bids => {
testmode,
pageurl,
sdk,
- pbv
+ adapterver,
+ platform,
+ platver
};
const video = findIndex(adformats, isVideo);
if (video !== -1) {
diff --git a/modules/consentManagement.js b/modules/consentManagement.js
index af668523fa4c..5f040c630516 100644
--- a/modules/consentManagement.js
+++ b/modules/consentManagement.js
@@ -90,6 +90,10 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) {
f = f.parent;
}
+ if (!cmpFrame) {
+ return cmpError('CMP not found.', hookConfig);
+ }
+
callCmpWhileInIframe('getConsentData', cmpFrame, callbackHandler.consentDataCallback);
callCmpWhileInIframe('getVendorConsents', cmpFrame, callbackHandler.vendorConsentsCallback);
}
@@ -124,12 +128,6 @@ function lookupIabConsent(cmpSuccess, cmpError, hookConfig) {
/* Setup up a __cmp function to do the postMessage and stash the callback.
This function behaves (from the caller's perspective identicially to the in-frame __cmp call */
window.__cmp = function(cmd, arg, callback) {
- if (!cmpFrame) {
- removePostMessageListener();
-
- let errmsg = 'CMP not found';
- return cmpError(errmsg, hookConfig);
- }
let callId = Math.random() + '';
let msg = {__cmpCall: {
command: cmd,
@@ -219,11 +217,17 @@ export function requestBidsHook(reqBidsConfigObj, fn) {
* @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
*/
function processCmpData(consentObject, hookConfig) {
+ let gdprApplies = consentObject && consentObject.getConsentData && consentObject.getConsentData.gdprApplies;
if (
- !utils.isPlainObject(consentObject) ||
- (!utils.isPlainObject(consentObject.getVendorConsents) || Object.keys(consentObject.getVendorConsents).length === 0) ||
- (!utils.isPlainObject(consentObject.getConsentData) || Object.keys(consentObject.getConsentData).length === 0)) {
- cmpFailed(`CMP returned unexpected value during lookup process; returned value was (${consentObject}).`, hookConfig);
+ (typeof gdprApplies !== 'boolean') ||
+ (gdprApplies === true &&
+ !(utils.isStr(consentObject.getConsentData.consentData) &&
+ utils.isPlainObject(consentObject.getVendorConsents) &&
+ Object.keys(consentObject.getVendorConsents).length > 1
+ )
+ )
+ ) {
+ cmpFailed(`CMP returned unexpected value during lookup process.`, hookConfig, consentObject);
} else {
clearTimeout(hookConfig.timer);
storeConsentData(consentObject);
@@ -243,15 +247,16 @@ function cmpTimedOut(hookConfig) {
* This function contains the controlled steps to perform when there's a problem with CMP.
* @param {string} errMsg required; should be a short descriptive message for why the failure/issue happened.
* @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
+ * @param {object} extraArgs contains additional data that's passed along in the error/warning messages for easier debugging
*/
-function cmpFailed(errMsg, hookConfig) {
+function cmpFailed(errMsg, hookConfig, extraArgs) {
clearTimeout(hookConfig.timer);
// still set the consentData to undefined when there is a problem as per config options
if (allowAuction) {
storeConsentData(undefined);
}
- exitModule(errMsg, hookConfig);
+ exitModule(errMsg, hookConfig, extraArgs);
}
/**
@@ -282,8 +287,9 @@ function storeConsentData(cmpConsentObject) {
* 3. bad exit with auction canceled (error message is logged).
* @param {string} errMsg optional; only to be used when there was a 'bad' exit. String is a descriptive message for the failure/issue encountered.
* @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
+ * @param {object} extraArgs contains additional data that's passed along in the error/warning messages for easier debugging
*/
-function exitModule(errMsg, hookConfig) {
+function exitModule(errMsg, hookConfig, extraArgs) {
if (hookConfig.haveExited === false) {
hookConfig.haveExited = true;
@@ -293,10 +299,10 @@ function exitModule(errMsg, hookConfig) {
if (errMsg) {
if (allowAuction) {
- utils.logWarn(errMsg + ' Resuming auction without consent data as per consentManagement config.');
+ utils.logWarn(errMsg + ' Resuming auction without consent data as per consentManagement config.', extraArgs);
nextFn.apply(context, args);
} else {
- utils.logError(errMsg + ' Canceling auction as per consentManagement config.');
+ utils.logError(errMsg + ' Canceling auction as per consentManagement config.', extraArgs);
if (typeof hookConfig.bidsBackHandler === 'function') {
hookConfig.bidsBackHandler();
} else {
diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js
index dfc5f514cf35..6ead42d4b2d0 100644
--- a/modules/eplanningBidAdapter.js
+++ b/modules/eplanningBidAdapter.js
@@ -17,6 +17,7 @@ export const spec = {
isBidRequestValid: function(bid) {
return Boolean(bid.params.ci) || Boolean(bid.params.t);
},
+
buildRequests: function(bidRequests) {
const method = 'GET';
const dfpClientId = '1';
@@ -24,6 +25,7 @@ export const spec = {
let url;
let params;
const urlConfig = getUrlConfig(bidRequests);
+ const pcrs = getCharset();
if (urlConfig.t) {
url = urlConfig.isv + '/layers/t_pbjs_2.json';
@@ -40,6 +42,11 @@ export const spec = {
pbv: '$prebid.version$',
ncb: '1'
};
+
+ if (pcrs) {
+ params.crs = pcrs;
+ }
+
if (referrerUrl) {
params.fr = referrerUrl;
}
@@ -147,6 +154,15 @@ function getSpacesString(bids) {
return spacesString;
}
+
+function getCharset() {
+ try {
+ return window.top.document.charset || window.top.document.characterSet;
+ } catch (e) {
+ return document.charset || document.characterSet;
+ }
+}
+
function getBidIdMap(bidRequests) {
let map = {};
bidRequests.forEach(bid => map[cleanName(bid.adUnitCode)] = bid.bidId);
diff --git a/modules/googleAnalyticsAdapter.js b/modules/googleAnalyticsAdapter.js
index 2993697c09da..901159b14a8a 100644
--- a/modules/googleAnalyticsAdapter.js
+++ b/modules/googleAnalyticsAdapter.js
@@ -235,7 +235,8 @@ function sendBidTimeouts(timedOutBidders) {
_analyticsQueue.push(function () {
utils._each(timedOutBidders, function (bidderCode) {
_eventCount++;
- window[_gaGlobal](_trackerSend, 'event', _category, 'Timeouts', bidderCode, _disableInteraction);
+ var bidderName = bidderCode.bidder;
+ window[_gaGlobal](_trackerSend, 'event', _category, 'Timeouts', bidderName, _disableInteraction);
});
});
diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js
index aa194db9ac5a..7a0bec366d5e 100644
--- a/modules/ixBidAdapter.js
+++ b/modules/ixBidAdapter.js
@@ -159,6 +159,7 @@ export const spec = {
return false;
}
+
if (bid.hasOwnProperty('mediaType') && bid.mediaType !== 'banner') {
return false;
}
@@ -167,7 +168,7 @@ export const spec = {
return false;
}
- if (typeof bid.params.siteId !== 'string') {
+ if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') {
return false;
}
diff --git a/modules/prebidServerBidAdapter.js b/modules/prebidServerBidAdapter.js
index 6533d9d00ee6..c217b2934edb 100644
--- a/modules/prebidServerBidAdapter.js
+++ b/modules/prebidServerBidAdapter.js
@@ -589,7 +589,7 @@ const OPEN_RTB_PROTOCOL = {
response.seatbid.forEach(seatbid => {
(seatbid.bid || []).forEach(bid => {
const bidRequest = utils.getBidRequest(
- this.bidMap[`${bid.impid}${seatbid.seat}`],
+ this.bidMap[`${bid.impid}${seatbid.seat}`].bid_id,
bidderRequests
);
@@ -704,11 +704,12 @@ export function PrebidServer() {
/* Notify Prebid of bid responses so bids can get in the auction */
function handleResponse(response, requestedBidders, bidderRequests, addBidResponse, done) {
let result;
+ let bids = [];
try {
result = JSON.parse(response);
- const bids = protocolAdapter().interpretResponse(
+ bids = protocolAdapter().interpretResponse(
result,
bidderRequests,
requestedBidders
@@ -734,7 +735,13 @@ export function PrebidServer() {
utils.logError('error parsing response: ', result.status);
}
- done();
+ const videoBid = bids.some(bidResponse => bidResponse.bid.mediaType === 'video');
+ const cacheEnabled = config.getConfig('cache.url');
+
+ // video bids with cache enabled need to be cached first before they are considered done
+ if (!(videoBid && cacheEnabled)) {
+ done();
+ }
doClientSideSyncs(requestedBidders);
}
diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js
index f10fd48502f5..3639a5a6bb7e 100644
--- a/modules/quantcastBidAdapter.js
+++ b/modules/quantcastBidAdapter.js
@@ -41,9 +41,10 @@ export const spec = {
* `BidRequests`.
*
* @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be send to Quantcast server
+ * @param bidderRequest
* @return ServerRequest information describing the request to the server.
*/
- buildRequests(bidRequests) {
+ buildRequests(bidRequests, bidderRequest) {
const bids = bidRequests || [];
const referrer = utils.getTopWindowUrl();
@@ -75,6 +76,8 @@ export const spec = {
});
});
+ const gdprConsent = bidderRequest ? bidderRequest.gdprConsent : {};
+
// Request Data Format can be found at https://wiki.corp.qc/display/adinf/QCX
const requestData = {
publisherId: bid.params.publisherId,
@@ -94,7 +97,9 @@ export const spec = {
referrer,
domain
},
- bidId: bid.bidId
+ bidId: bid.bidId,
+ gdprSignal: gdprConsent.gdprApplies ? 1 : 0,
+ gdprConsent: gdprConsent.consentString
};
const data = JSON.stringify(requestData);
diff --git a/modules/quantcastBidAdapter.md b/modules/quantcastBidAdapter.md
index 20cf25bffbf8..efc21466c75c 100644
--- a/modules/quantcastBidAdapter.md
+++ b/modules/quantcastBidAdapter.md
@@ -3,7 +3,7 @@
```
Module Name: Quantcast Bidder Adapter
Module Type: Bidder Adapter
-Maintainer: xli@quantcast.com
+Maintainer: igor.soarez@quantcast.com
```
# Description
@@ -28,4 +28,4 @@ const adUnits = [{
}
]
}];
-```
\ No newline at end of file
+```
diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js
index 5f4250352a55..e75c8df083a5 100644
--- a/modules/rubiconBidAdapter.js
+++ b/modules/rubiconBidAdapter.js
@@ -71,7 +71,8 @@ var sizeMap = {
199: '640x200',
213: '1030x590',
214: '980x360',
- 232: '580x400'
+ 232: '580x400',
+ 257: '400x600'
};
utils._each(sizeMap, (item, key) => sizeMap[item] = key);
diff --git a/modules/weboramaBidAdapter.js b/modules/weboramaBidAdapter.js
new file mode 100644
index 000000000000..2fe6f30b361e
--- /dev/null
+++ b/modules/weboramaBidAdapter.js
@@ -0,0 +1,117 @@
+import { registerBidder } from 'src/adapters/bidderFactory';
+import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes';
+import * as utils from 'src/utils';
+
+const BIDDER_CODE = 'weborama';
+const URL = '//supply.nl.weborama.fr/?c=o&m=multi';
+const URL_SYNC = '//supply.nl.weborama.fr/?c=o&m=cookie';
+
+function isBidResponseValid(bid) {
+ if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) {
+ return false;
+ }
+ switch (bid.mediaType) {
+ case BANNER:
+ return Boolean(bid.width && bid.height && bid.ad);
+ case VIDEO:
+ return Boolean(bid.vastUrl);
+ case NATIVE:
+ return Boolean(bid.native);
+ default:
+ return false;
+ }
+}
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER, VIDEO, NATIVE],
+
+ /**
+ * Determines whether or not the given bid request is valid.
+ *
+ * @param {object} bid The bid to validate.
+ * @return boolean True if this is a valid bid, and false otherwise.
+ */
+ isBidRequestValid: (bid) => {
+ return Boolean(bid.bidId &&
+ bid.params &&
+ !isNaN(bid.params.placementId) &&
+ spec.supportedMediaTypes.indexOf(bid.params.traffic) !== -1
+ );
+ },
+
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server.
+ * @return ServerRequest Info describing the request to the server.
+ */
+ buildRequests: (validBidRequests) => {
+ let winTop;
+ try {
+ winTop = utils.getWindowTop();
+ winTop.location.toString();
+ } catch (e) {
+ utils.logMessage(e);
+ winTop = window;
+ };
+
+ const location = utils.getTopWindowLocation();
+ const placements = [];
+ const request = {
+ 'secure': (location.protocol === 'https:') ? 1 : 0,
+ 'deviceWidth': winTop.screen.width,
+ 'deviceHeight': winTop.screen.height,
+ 'host': location.host,
+ 'page': location.pathname,
+ 'placements': placements
+ };
+
+ for (let i = 0; i < validBidRequests.length; i++) {
+ const bid = validBidRequests[i];
+ const params = bid.params;
+ placements.push({
+ placementId: params.placementId,
+ bidId: bid.bidId,
+ sizes: bid.sizes,
+ traffic: params.traffic
+ });
+ }
+ return {
+ method: 'POST',
+ url: URL,
+ data: request
+ };
+ },
+
+ /**
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {*} serverResponse A successful response from the server.
+ * @return {Bid[]} An array of bids which were nested inside the server.
+ */
+ interpretResponse: (serverResponse) => {
+ let response = [];
+ try {
+ serverResponse = serverResponse.body;
+ for (let i = 0; i < serverResponse.length; i++) {
+ let resItem = serverResponse[i];
+ if (isBidResponseValid(resItem)) {
+ response.push(resItem);
+ }
+ }
+ } catch (e) {
+ utils.logMessage(e);
+ };
+ return response;
+ },
+
+ getUserSyncs: () => {
+ return [{
+ type: 'image',
+ url: URL_SYNC
+ }];
+ }
+};
+
+registerBidder(spec);
diff --git a/modules/weboramaBidAdapter.md b/modules/weboramaBidAdapter.md
new file mode 100644
index 000000000000..5bdca0bfcd14
--- /dev/null
+++ b/modules/weboramaBidAdapter.md
@@ -0,0 +1,27 @@
+# Overview
+
+```
+Module Name: Weborama SSP Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: devweborama@gmail.com
+```
+
+# Description
+
+Module that connects to Weborama SSP demand sources
+
+# Test Parameters
+```
+ var adUnits = [{
+ code: 'placementCode',
+ sizes: [[300, 250]],
+ bids: [{
+ bidder: 'weborama',
+ params: {
+ placementId: 0,
+ traffic: 'banner'
+ }
+ }]
+ }
+ ];
+```
diff --git a/package.json b/package.json
index f2628096af77..1223fc5f6ce8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "1.15.0-pre",
+ "version": "1.16.0-pre",
"description": "Header Bidding Management Library",
"main": "src/prebid.js",
"scripts": {
diff --git a/src/adaptermanager.js b/src/adaptermanager.js
index f6c0d5c421e3..4c60b01d8fa8 100644
--- a/src/adaptermanager.js
+++ b/src/adaptermanager.js
@@ -273,7 +273,7 @@ exports.checkBidRequestSizes = (adUnits) => {
return adUnits;
}
-exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb) => {
+exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb, requestCallbacks) => {
if (!bidRequests.length) {
utils.logWarn('callBids executed with no bidRequests. Were they filtered by labels or sizing?');
return;
@@ -285,7 +285,10 @@ exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb) => {
}, [[], []]);
if (serverBidRequests.length) {
- const s2sAjax = ajaxBuilder(serverBidRequests[0].timeout);
+ const s2sAjax = ajaxBuilder(serverBidRequests[0].timeout, requestCallbacks ? {
+ request: requestCallbacks.request.bind(null, 's2s'),
+ done: requestCallbacks.done
+ } : undefined);
let adaptersServerSide = _s2sConfig.bidders;
const s2sAdapter = _bidderRegistry[_s2sConfig.adapter];
let tid = serverBidRequests[0].tid;
@@ -336,7 +339,6 @@ exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb) => {
}
}
- const ajax = (clientBidRequests.length) ? ajaxBuilder(clientBidRequests[0].timeout) : null;
// handle client adapter requests
clientBidRequests.forEach(bidRequest => {
bidRequest.start = timestamp();
@@ -347,6 +349,10 @@ exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb) => {
events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest);
bidRequest.doneCbCallCount = 0;
let done = doneCb(bidRequest.bidderRequestId);
+ let ajax = ajaxBuilder(clientBidRequests[0].timeout, requestCallbacks ? {
+ request: requestCallbacks.request.bind(null, bidRequest.bidderCode),
+ done: requestCallbacks.done
+ } : undefined);
adapter.callBids(bidRequest, addBidResponse, done, ajax);
} else {
utils.logError(`Adapter trying to be called which does not exist: ${bidRequest.bidderCode} adaptermanager.callBids`);
@@ -387,12 +393,20 @@ exports.registerBidAdapter = function (bidAdaptor, bidderCode, {supportedMediaTy
};
exports.aliasBidAdapter = function (bidderCode, alias) {
- var existingAlias = _bidderRegistry[alias];
+ let existingAlias = _bidderRegistry[alias];
if (typeof existingAlias === 'undefined') {
- var bidAdaptor = _bidderRegistry[bidderCode];
+ let bidAdaptor = _bidderRegistry[bidderCode];
if (typeof bidAdaptor === 'undefined') {
- utils.logError('bidderCode "' + bidderCode + '" is not an existing bidder.', 'adaptermanager.aliasBidAdapter');
+ // check if alias is part of s2sConfig and allow them to register if so (as base bidder may be s2s-only)
+ const s2sConfig = config.getConfig('s2sConfig');
+ const s2sBidders = s2sConfig && s2sConfig.bidders;
+
+ if (!(s2sBidders && includes(s2sBidders, alias))) {
+ utils.logError('bidderCode "' + bidderCode + '" is not an existing bidder.', 'adaptermanager.aliasBidAdapter');
+ } else {
+ exports.aliasRegistry[alias] = bidderCode;
+ }
} else {
try {
let newAdapter;
diff --git a/src/ajax.js b/src/ajax.js
index ded2f95f8a52..e17f782ac308 100644
--- a/src/ajax.js
+++ b/src/ajax.js
@@ -15,12 +15,13 @@ const XHR_DONE = 4;
*/
export const ajax = ajaxBuilder();
-export function ajaxBuilder(timeout = 3000) {
+export function ajaxBuilder(timeout = 3000, {request, done} = {}) {
return function(url, callback, data, options = {}) {
try {
let x;
- let useXDomainRequest = false;
let method = options.method || (data ? 'POST' : 'GET');
+ let parser = document.createElement('a');
+ parser.href = url;
let callbacks = typeof callback === 'object' && callback !== null ? callback : {
success: function() {
@@ -35,46 +36,24 @@ export function ajaxBuilder(timeout = 3000) {
callbacks.success = callback;
}
- if (!window.XMLHttpRequest) {
- useXDomainRequest = true;
- } else {
- x = new window.XMLHttpRequest();
- if (x.responseType === undefined) {
- useXDomainRequest = true;
- }
- }
-
- if (useXDomainRequest) {
- x = new window.XDomainRequest();
- x.onload = function () {
- callbacks.success(x.responseText, x);
- };
+ x = new window.XMLHttpRequest();
- // http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9
- x.onerror = function () {
- callbacks.error('error', x);
- };
- x.ontimeout = function () {
- callbacks.error('timeout', x);
- };
- x.onprogress = function() {
- utils.logMessage('xhr onprogress');
- };
- } else {
- x.onreadystatechange = function () {
- if (x.readyState === XHR_DONE) {
- let status = x.status;
- if ((status >= 200 && status < 300) || status === 304) {
- callbacks.success(x.responseText, x);
- } else {
- callbacks.error(x.statusText, x);
- }
+ x.onreadystatechange = function () {
+ if (x.readyState === XHR_DONE) {
+ if (typeof done === 'function') {
+ done(parser.origin);
}
- };
- x.ontimeout = function () {
- utils.logError(' xhr timeout after ', x.timeout, 'ms');
- };
- }
+ let status = x.status;
+ if ((status >= 200 && status < 300) || status === 304) {
+ callbacks.success(x.responseText, x);
+ } else {
+ callbacks.error(x.statusText, x);
+ }
+ }
+ };
+ x.ontimeout = function () {
+ utils.logError(' xhr timeout after ', x.timeout, 'ms');
+ };
if (method === 'GET' && data) {
let urlInfo = parseURL(url, options);
@@ -86,18 +65,21 @@ export function ajaxBuilder(timeout = 3000) {
// IE needs timoeut to be set after open - see #1410
x.timeout = timeout;
- if (!useXDomainRequest) {
- if (options.withCredentials) {
- x.withCredentials = true;
- }
- utils._each(options.customHeaders, (value, header) => {
- x.setRequestHeader(header, value);
- });
- if (options.preflight) {
- x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- }
- x.setRequestHeader('Content-Type', options.contentType || 'text/plain');
+ if (options.withCredentials) {
+ x.withCredentials = true;
}
+ utils._each(options.customHeaders, (value, header) => {
+ x.setRequestHeader(header, value);
+ });
+ if (options.preflight) {
+ x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ }
+ x.setRequestHeader('Content-Type', options.contentType || 'text/plain');
+
+ if (typeof request === 'function') {
+ request(parser.origin);
+ }
+
if (method === 'POST' && data) {
x.send(data);
} else {
diff --git a/src/auction.js b/src/auction.js
index 8992f16218e2..5722bcc6990e 100644
--- a/src/auction.js
+++ b/src/auction.js
@@ -74,6 +74,11 @@ events.on(CONSTANTS.EVENTS.BID_ADJUSTMENT, function (bid) {
adjustBids(bid);
});
+const MAX_REQUESTS_PER_ORIGIN = 4;
+const outstandingRequests = {};
+const sourceInfo = {};
+const queuedCalls = [];
+
/**
* Creates new auction instance
*
@@ -176,26 +181,103 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
}
function callBids() {
- startAuctionTimer();
_auctionStatus = AUCTION_STARTED;
_auctionStart = Date.now();
- const auctionInit = {
- timestamp: _auctionStart,
- auctionId: _auctionId,
- timeout: _timeout
- };
- events.emit(CONSTANTS.EVENTS.AUCTION_INIT, auctionInit);
-
let bidRequests = adaptermanager.makeBidRequests(_adUnits, _auctionStart, _auctionId, _timeout, _labels);
utils.logInfo(`Bids Requested for Auction with id: ${_auctionId}`, bidRequests);
bidRequests.forEach(bidRequest => {
addBidRequests(bidRequest);
});
- _auctionStatus = AUCTION_IN_PROGRESS;
- adaptermanager.callBids(_adUnits, bidRequests, addBidResponse.bind(this), done.bind(this));
- };
+ let requests = {};
+
+ let call = {
+ bidRequests,
+ run: () => {
+ startAuctionTimer();
+
+ _auctionStatus = AUCTION_IN_PROGRESS;
+
+ const auctionInit = {
+ timestamp: _auctionStart,
+ auctionId: _auctionId,
+ timeout: _timeout
+ };
+ events.emit(CONSTANTS.EVENTS.AUCTION_INIT, auctionInit);
+
+ adaptermanager.callBids(_adUnits, bidRequests, addBidResponse.bind(this), done.bind(this), {
+ request(source, origin) {
+ increment(outstandingRequests, origin);
+ increment(requests, source);
+
+ if (!sourceInfo[source]) {
+ sourceInfo[source] = {
+ SRA: true,
+ origin
+ };
+ }
+ if (requests[source] > 1) {
+ sourceInfo[source].SRA = false;
+ }
+ },
+ done(origin) {
+ outstandingRequests[origin]--;
+ if (queuedCalls[0]) {
+ if (runIfOriginHasCapacity(queuedCalls[0])) {
+ queuedCalls.shift();
+ }
+ }
+ }
+ });
+ }
+ };
+
+ if (!runIfOriginHasCapacity(call)) {
+ utils.logWarn('queueing auction due to limited endpoint capacity');
+ queuedCalls.push(call);
+ }
+
+ function runIfOriginHasCapacity(call) {
+ let hasCapacity = true;
+
+ let maxRequests = config.getConfig('maxRequestsPerOrigin') || MAX_REQUESTS_PER_ORIGIN;
+
+ call.bidRequests.some(bidRequest => {
+ let requests = 1;
+ let source = (typeof bidRequest.src !== 'undefined' && bidRequest.src === CONSTANTS.S2S.SRC) ? 's2s'
+ : bidRequest.bidderCode;
+ // if we have no previous info on this source just let them through
+ if (sourceInfo[source]) {
+ if (sourceInfo[source].SRA === false) {
+ // some bidders might use more than the MAX_REQUESTS_PER_ORIGIN in a single auction. In those cases
+ // set their request count to MAX_REQUESTS_PER_ORIGIN so the auction isn't permanently queued waiting
+ // for capacity for that bidder
+ requests = Math.min(bidRequest.bids.length, maxRequests);
+ }
+ if (outstandingRequests[sourceInfo[source].origin] + requests > maxRequests) {
+ hasCapacity = false;
+ }
+ }
+ // return only used for terminating this .some() iteration early if it is determined we don't have capacity
+ return !hasCapacity;
+ });
+
+ if (hasCapacity) {
+ call.run();
+ }
+
+ return hasCapacity;
+ }
+
+ function increment(obj, prop) {
+ if (typeof obj[prop] === 'undefined') {
+ obj[prop] = 1
+ } else {
+ obj[prop]++;
+ }
+ }
+ }
return {
addBidReceived,
diff --git a/src/constants.json b/src/constants.json
index c8a7c3ebefc3..3bbad70585ab 100644
--- a/src/constants.json
+++ b/src/constants.json
@@ -66,6 +66,7 @@
],
"S2S" : {
"SRC" : "s2s",
+ "DEFAULT_ENDPOINT" : "https://prebid.adnxs.com/pbs/v1/openrtb2/auction",
"SYNCED_BIDDERS_KEY": "pbjsSyncs"
}
}
diff --git a/src/debugging.js b/src/debugging.js
new file mode 100644
index 000000000000..fe5c269a18da
--- /dev/null
+++ b/src/debugging.js
@@ -0,0 +1,89 @@
+
+import { config } from 'src/config';
+import { logMessage as utilsLogMessage, logWarn as utilsLogWarn } from 'src/utils';
+import { addBidResponse } from 'src/auction';
+
+const OVERRIDE_KEY = '$$PREBID_GLOBAL$$:debugging';
+
+export let boundHook;
+
+function logMessage(msg) {
+ utilsLogMessage('DEBUG: ' + msg);
+}
+
+function logWarn(msg) {
+ utilsLogWarn('DEBUG: ' + msg);
+}
+
+function enableOverrides(overrides, fromSession = false) {
+ config.setConfig({'debug': true});
+ logMessage(`bidder overrides enabled${fromSession ? ' from session' : ''}`);
+
+ if (boundHook) {
+ addBidResponse.removeHook(boundHook);
+ }
+
+ boundHook = addBidResponseHook.bind(null, overrides);
+ addBidResponse.addHook(boundHook, 5);
+}
+
+export function disableOverrides() {
+ if (boundHook) {
+ addBidResponse.removeHook(boundHook);
+ logMessage('bidder overrides disabled');
+ }
+}
+
+export function addBidResponseHook(overrides, adUnitCode, bid, next) {
+ if (Array.isArray(overrides.bidders) && overrides.bidders.indexOf(bid.bidderCode) === -1) {
+ logWarn(`bidder '${bid.bidderCode}' excluded from auction by bidder overrides`);
+ return;
+ }
+
+ if (Array.isArray(overrides.bids)) {
+ overrides.bids.forEach(overrideBid => {
+ if (overrideBid.bidder && overrideBid.bidder !== bid.bidderCode) {
+ return;
+ }
+ if (overrideBid.adUnitCode && overrideBid.adUnitCode !== adUnitCode) {
+ return;
+ }
+
+ bid = Object.assign({}, bid);
+
+ Object.keys(overrideBid).filter(key => ['bidder', 'adUnitCode'].indexOf(key) === -1).forEach((key) => {
+ let value = overrideBid[key];
+ logMessage(`bidder overrides changed '${adUnitCode}/${bid.bidderCode}' bid.${key} from '${bid[key]}' to '${value}'`);
+ bid[key] = value;
+ });
+ });
+ }
+
+ next(adUnitCode, bid);
+}
+
+export function getConfig(debugging) {
+ if (!debugging.enabled) {
+ disableOverrides();
+ try {
+ window.sessionStorage.removeItem(OVERRIDE_KEY);
+ } catch (e) {}
+ } else {
+ try {
+ window.sessionStorage.setItem(OVERRIDE_KEY, JSON.stringify(debugging));
+ } catch (e) {}
+ enableOverrides(debugging);
+ }
+}
+config.getConfig('debugging', ({debugging}) => getConfig(debugging));
+
+export function sessionLoader(storage = window.sessionStorage) {
+ let overrides;
+ try {
+ overrides = JSON.parse(storage.getItem(OVERRIDE_KEY));
+ } catch (e) {
+ }
+ if (overrides) {
+ enableOverrides(overrides, true);
+ }
+}
diff --git a/src/hook.js b/src/hook.js
index 6c6cefdc56c7..fef62a37c3d7 100644
--- a/src/hook.js
+++ b/src/hook.js
@@ -60,6 +60,9 @@ export function createHook(type, fn, hookName) {
},
removeHook: function(removeFn) {
_hooks = _hooks.filter(hook => hook.fn === fn || hook.fn !== removeFn);
+ },
+ hasHook: function(fn) {
+ return _hooks.some(hook => hook.fn === fn);
}
};
diff --git a/src/prebid.js b/src/prebid.js
index e1691c20f792..767180f32867 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -9,6 +9,7 @@ import { config } from './config';
import { auctionManager } from './auctionManager';
import { targeting, getHighestCpmBidsFromBidPool, RENDERED, BID_TARGETING_SET } from './targeting';
import { createHook } from 'src/hook';
+import { sessionLoader } from 'src/debugging';
import includes from 'core-js/library/fn/array/includes';
const $$PREBID_GLOBAL$$ = getGlobal();
@@ -27,6 +28,9 @@ const eventValidators = {
bidWon: checkDefinedPlacement
};
+// initialize existing debugging sessions if present
+sessionLoader();
+
/* Public vars */
$$PREBID_GLOBAL$$.bidderSettings = $$PREBID_GLOBAL$$.bidderSettings || {};
@@ -336,9 +340,15 @@ $$PREBID_GLOBAL$$.requestBids = createHook('asyncSeries', function ({ bidsBackHa
const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || {'banner': 'banner'});
// get the bidder's mediaTypes
- const bidders = adUnit.bids.map(bid => bid.bidder);
+ const allBidders = adUnit.bids.map(bid => bid.bidder);
const bidderRegistry = adaptermanager.bidderRegistry;
+ const s2sConfig = config.getConfig('s2sConfig');
+ const s2sBidders = s2sConfig && s2sConfig.bidders;
+ const bidders = (s2sBidders) ? allBidders.filter(bidder => {
+ return !includes(s2sBidders, bidder);
+ }) : allBidders;
+
if (!adUnit.transactionId) {
adUnit.transactionId = utils.generateUUID();
}
diff --git a/src/targeting.js b/src/targeting.js
index e33f1a88f10f..d645a8ed20d7 100644
--- a/src/targeting.js
+++ b/src/targeting.js
@@ -16,7 +16,7 @@ const MAX_DFP_KEYLENGTH = 20;
const TTL_BUFFER = 1000;
// return unexpired bids
-export const isBidExpired = (bid) => (bid.responseTimestamp + bid.ttl * 1000 + TTL_BUFFER) > timestamp();
+export const isBidNotExpired = (bid) => (bid.responseTimestamp + bid.ttl * 1000 + TTL_BUFFER) > timestamp();
// return bids whose status is not set. Winning bid can have status `targetingSet` or `rendered`.
const isUnusedBid = (bid) => bid && ((bid.status && !includes([BID_TARGETING_SET, RENDERED], bid.status)) || !bid.status);
@@ -195,7 +195,7 @@ export function newTargeting(auctionManager) {
function getBidsReceived() {
const bidsReceived = auctionManager.getBidsReceived()
.filter(isUnusedBid)
- .filter(exports.isBidExpired)
+ .filter(exports.isBidNotExpired)
;
return getHighestCpmBidsFromBidPool(bidsReceived, getOldestHighestCpmBid);
diff --git a/src/utils.js b/src/utils.js
index 5135a1df21d2..09a526607961 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -14,8 +14,10 @@ var t_Numb = 'Number';
var t_Object = 'Object';
var toString = Object.prototype.toString;
let infoLogger = null;
+let warnLogger = null;
try {
infoLogger = console.info.bind(window.console);
+ warnLogger = console.warn.bind(window.console);
} catch (e) {
}
@@ -257,9 +259,15 @@ exports.getTopWindowReferrer = function() {
}
};
-exports.logWarn = function (msg) {
+exports.logWarn = function (msg, args) {
if (debugTurnedOn() && console.warn) {
- console.warn('WARNING: ' + msg);
+ if (warnLogger) {
+ if (!args || args.length === 0) {
+ args = '';
+ }
+
+ warnLogger('WARNING: ' + msg + ((args === '') ? '' : ' : params : '), args);
+ }
}
};
diff --git a/test/spec/debugging_spec.js b/test/spec/debugging_spec.js
new file mode 100644
index 000000000000..286df26f7ba7
--- /dev/null
+++ b/test/spec/debugging_spec.js
@@ -0,0 +1,142 @@
+
+import { expect } from 'chai';
+import { sessionLoader, addBidResponseHook, getConfig, disableOverrides, boundHook } from 'src/debugging';
+import { addBidResponse } from 'src/auction';
+import { config } from 'src/config';
+
+describe('bid overrides', () => {
+ let sandbox;
+
+ beforeEach(() => {
+ sandbox = sinon.sandbox.create();
+ });
+
+ afterEach(() => {
+ window.sessionStorage.clear();
+ sandbox.restore();
+ });
+
+ describe('initialization', () => {
+ beforeEach(() => {
+ sandbox.stub(config, 'setConfig');
+ });
+
+ afterEach(() => {
+ disableOverrides();
+ });
+
+ it('should happen when enabled with setConfig', () => {
+ getConfig({
+ enabled: true
+ });
+
+ expect(addBidResponse.hasHook(boundHook)).to.equal(true);
+ });
+
+ it('should happen when configuration found in sessionStorage', () => {
+ sessionLoader({
+ getItem: () => ('{"enabled": true}')
+ });
+ expect(addBidResponse.hasHook(boundHook)).to.equal(true);
+ });
+
+ it('should not throw if sessionStorage is inaccessible', () => {
+ expect(() => {
+ sessionLoader({
+ getItem() {
+ throw new Error('test');
+ }
+ });
+ }).not.to.throw();
+ });
+ });
+
+ describe('hook', () => {
+ let mockBids;
+ let bids;
+
+ beforeEach(() => {
+ let baseBid = {
+ 'bidderCode': 'rubicon',
+ 'width': 970,
+ 'height': 250,
+ 'statusMessage': 'Bid available',
+ 'mediaType': 'banner',
+ 'source': 'client',
+ 'currency': 'USD',
+ 'cpm': 0.5,
+ 'ttl': 300,
+ 'netRevenue': false,
+ 'adUnitCode': '/19968336/header-bid-tag-0'
+ };
+ mockBids = [];
+ mockBids.push(baseBid);
+ mockBids.push(Object.assign({}, baseBid, {
+ bidderCode: 'appnexus'
+ }));
+
+ bids = [];
+ });
+
+ function run(overrides) {
+ mockBids.forEach(bid => {
+ addBidResponseHook(overrides, bid.adUnitCode, bid, (adUnitCode, bid) => {
+ bids.push(bid);
+ })
+ });
+ }
+
+ it('should allow us to exclude bidders', () => {
+ run({
+ enabled: true,
+ bidders: ['appnexus']
+ });
+
+ expect(bids.length).to.equal(1);
+ expect(bids[0].bidderCode).to.equal('appnexus');
+ });
+
+ it('should allow us to override all bids', () => {
+ run({
+ enabled: true,
+ bids: [{
+ cpm: 2
+ }]
+ });
+
+ expect(bids.length).to.equal(2);
+ expect(bids[0].cpm).to.equal(2);
+ expect(bids[1].cpm).to.equal(2);
+ });
+
+ it('should allow us to override bids by bidder', () => {
+ run({
+ enabled: true,
+ bids: [{
+ bidder: 'rubicon',
+ cpm: 2
+ }]
+ });
+
+ expect(bids.length).to.equal(2);
+ expect(bids[0].cpm).to.equal(2);
+ expect(bids[1].cpm).to.equal(0.5);
+ });
+
+ it('should allow us to override bids by adUnitCode', () => {
+ mockBids[1].adUnitCode = 'test';
+
+ run({
+ enabled: true,
+ bids: [{
+ adUnitCode: 'test',
+ cpm: 2
+ }]
+ });
+
+ expect(bids.length).to.equal(2);
+ expect(bids[0].cpm).to.equal(0.5);
+ expect(bids[1].cpm).to.equal(2);
+ });
+ });
+});
diff --git a/test/spec/modules/audienceNetworkBidAdapter_spec.js b/test/spec/modules/audienceNetworkBidAdapter_spec.js
index 2b4f87450e8d..c61cd04c422b 100644
--- a/test/spec/modules/audienceNetworkBidAdapter_spec.js
+++ b/test/spec/modules/audienceNetworkBidAdapter_spec.js
@@ -19,7 +19,7 @@ const placementId = 'test-placement-id';
const playerwidth = 320;
const playerheight = 180;
const requestId = 'test-request-id';
-const pbv = '$prebid.version$';
+const debug = 'adapterver=1.0.0&platform=241394079772386&platver=$prebid.version$';
const pageUrl = encodeURIComponent(utils.getTopWindowUrl());
describe('AudienceNetwork adapter', () => {
@@ -140,7 +140,7 @@ describe('AudienceNetwork adapter', () => {
requestIds: [requestId],
sizes: ['300x250'],
url: 'https://an.facebook.com/v2/placementbid.json',
- data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&pbv=${pbv}`
+ data: `placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&${debug}`
}]);
});
@@ -159,7 +159,7 @@ describe('AudienceNetwork adapter', () => {
requestIds: [requestId],
sizes: ['640x480'],
url: 'https://an.facebook.com/v2/placementbid.json',
- data: `placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=${pageUrl}&sdk[]=&pbv=${pbv}&playerwidth=640&playerheight=480`
+ data: `placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=${pageUrl}&sdk[]=&${debug}&playerwidth=640&playerheight=480`
}]);
});
@@ -178,7 +178,7 @@ describe('AudienceNetwork adapter', () => {
requestIds: [requestId],
sizes: ['728x90'],
url: 'https://an.facebook.com/v2/placementbid.json',
- data: `placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&pbv=${pbv}`
+ data: `placementids[]=test-placement-id&adformats[]=native&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&${debug}`
}]);
});
@@ -197,7 +197,7 @@ describe('AudienceNetwork adapter', () => {
requestIds: [requestId],
sizes: ['300x250'],
url: 'https://an.facebook.com/v2/placementbid.json',
- data: `placementids[]=test-placement-id&adformats[]=fullwidth&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&pbv=${pbv}`
+ data: `placementids[]=test-placement-id&adformats[]=fullwidth&testmode=false&pageurl=${pageUrl}&sdk[]=5.5.web&${debug}`
}]);
});
diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js
index e825de2a1841..46ab6c46777c 100644
--- a/test/spec/modules/consentManagement_spec.js
+++ b/test/spec/modules/consentManagement_spec.js
@@ -68,35 +68,47 @@ describe('consentManagement', function () {
});
describe('error checks:', () => {
- describe('unknown CMP framework ID:', () => {
- beforeEach(() => {
- sinon.stub(utils, 'logWarn');
- });
+ beforeEach(() => {
+ didHookReturn = false;
+ sinon.stub(utils, 'logWarn');
+ sinon.stub(utils, 'logError');
+ });
- afterEach(() => {
- utils.logWarn.restore();
- config.resetConfig();
- $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook);
- resetConsentData();
- });
+ afterEach(() => {
+ utils.logWarn.restore();
+ utils.logError.restore();
+ config.resetConfig();
+ $$PREBID_GLOBAL$$.requestBids.removeHook(requestBidsHook);
+ resetConsentData();
+ });
+
+ it('should throw a warning and return to hooked function when an unknown CMP framework ID is used', () => {
+ let badCMPConfig = {
+ cmpApi: 'bad'
+ };
+ setConfig(badCMPConfig);
+ expect(userCMP).to.be.equal(badCMPConfig.cmpApi);
- it('should return Warning message and return to hooked function', () => {
- let badCMPConfig = {
- cmpApi: 'bad'
- };
- setConfig(badCMPConfig);
- expect(userCMP).to.be.equal(badCMPConfig.cmpApi);
+ requestBidsHook({}, () => {
+ didHookReturn = true;
+ });
+ let consent = gdprDataHandler.getConsentData();
+ sinon.assert.calledOnce(utils.logWarn);
+ expect(didHookReturn).to.be.true;
+ expect(consent).to.be.null;
+ });
- didHookReturn = false;
+ it('should throw proper errors when CMP is not found', () => {
+ setConfig(goodConfigWithCancelAuction);
- requestBidsHook({}, () => {
- didHookReturn = true;
- });
- let consent = gdprDataHandler.getConsentData();
- sinon.assert.calledOnce(utils.logWarn);
- expect(didHookReturn).to.be.true;
- expect(consent).to.be.null;
+ requestBidsHook({}, () => {
+ didHookReturn = true;
});
+ let consent = gdprDataHandler.getConsentData();
+ // throw 2 errors; one for no bidsBackHandler and for CMP not being found (this is an error due to gdpr config)
+ sinon.assert.calledTwice(utils.logError);
+ expect(didHookReturn).to.be.false;
+ expect(consent).to.be.null;
});
});
diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js
index 68b9e1b263f6..a56bff42285f 100644
--- a/test/spec/modules/eplanningBidAdapter_spec.js
+++ b/test/spec/modules/eplanningBidAdapter_spec.js
@@ -259,6 +259,19 @@ describe('E-Planning Adapter', () => {
stubGetReferrer.restore()
});
+ it('should return crs parameter with document charset', () => {
+ let expected;
+ try {
+ expected = window.top.document.characterSet;
+ } catch (e) {
+ expected = document.characterSet;
+ }
+
+ const chset = spec.buildRequests(bidRequests).data.crs;
+
+ expect(chset).to.equal(expected);
+ });
+
it('should return the testing url when the request has the t parameter', () => {
const url = spec.buildRequests([testBid]).url;
const expectedUrl = '//' + TEST_ISV + '/layers/t_pbjs_2.json';
diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js
index 0c0096398525..3bf0fb27280f 100644
--- a/test/spec/modules/ixBidAdapter_spec.js
+++ b/test/spec/modules/ixBidAdapter_spec.js
@@ -77,10 +77,10 @@ describe('IndexexchangeAdapter', () => {
expect(spec.isBidRequestValid(bid)).to.equal(true);
});
- it('should return false when siteID is number', () => {
+ it('should return true when siteID is number', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.params.siteId = 123;
- expect(spec.isBidRequestValid(bid)).to.equal(false);
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
});
it('should return false when siteID is missing', () => {
diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js
index b155d61d8e5c..cd2022bcab7d 100644
--- a/test/spec/modules/prebidServerBidAdapter_spec.js
+++ b/test/spec/modules/prebidServerBidAdapter_spec.js
@@ -246,7 +246,7 @@ const RESPONSE_OPENRTB = {
'bid': [
{
'id': '8750901685062148',
- 'impid': '123',
+ 'impid': 'div-gpt-ad-1460505748561-0',
'price': 0.5,
'adm': '',
'adid': '29681110',
@@ -285,7 +285,7 @@ const RESPONSE_OPENRTB_VIDEO = {
bid: [
{
id: '1987250005171537465',
- impid: '/19968336/header-bid-tag-0',
+ impid: 'div-gpt-ad-1460505748561-0',
price: 10,
adm: '