diff --git a/modules/.submodules.json b/modules/.submodules.json
index 43dfe8cec21..f9eed96f875 100644
--- a/modules/.submodules.json
+++ b/modules/.submodules.json
@@ -7,6 +7,7 @@
"amxIdSystem",
"britepoolIdSystem",
"connectIdSystem",
+ "cpexIdSystem",
"criteoIdSystem",
"dacIdSystem",
"deepintentDpesIdSystem",
diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js
index 1797585efed..b6c82b90b33 100644
--- a/modules/adfBidAdapter.js
+++ b/modules/adfBidAdapter.js
@@ -206,6 +206,11 @@ export const spec = {
request.is_debug = !!test;
request.test = 1;
}
+
+ if (config.getConfig('coppa')) {
+ deepSetValue(request, 'regs.coppa', 1);
+ }
+
if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) {
deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString);
deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1);
diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js
index 241864c50fc..21bcb6cee26 100644
--- a/modules/admanBidAdapter.js
+++ b/modules/admanBidAdapter.js
@@ -110,6 +110,7 @@ export const spec = {
if (bid.userId) {
getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com');
getUserId(placement.eids, bid.userId.lotamePanoramaId, 'lotame.com');
+ getUserId(placement.eids, bid.userId.idx, 'idx.lat');
}
if (traff === VIDEO) {
placement.playerSize = bid.mediaTypes[VIDEO].playerSize;
diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js
new file mode 100644
index 00000000000..261fd9dee68
--- /dev/null
+++ b/modules/alkimiBidAdapter.js
@@ -0,0 +1,119 @@
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { deepClone, deepAccess } from '../src/utils.js';
+import { ajax } from '../src/ajax.js';
+import { VIDEO } from '../src/mediaTypes.js';
+import { config } from '../src/config.js';
+
+const BIDDER_CODE = 'alkimi';
+export const ENDPOINT = 'https://exchange.alkimi-onboarding.com/bid?prebid=true';
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: ['banner', 'video'],
+
+ isBidRequestValid: function (bid) {
+ return !!(bid.params && bid.params.bidFloor && bid.params.token);
+ },
+
+ buildRequests: function (validBidRequests, bidderRequest) {
+ let bids = [];
+ let bidIds = [];
+ validBidRequests.forEach(bidRequest => {
+ let sizes = prepareSizes(bidRequest.sizes)
+
+ bids.push({
+ token: bidRequest.params.token,
+ pos: bidRequest.params.pos,
+ bidFloor: bidRequest.params.bidFloor,
+ width: sizes[0].width,
+ height: sizes[0].height,
+ impMediaType: getFormatType(bidRequest)
+ })
+ bidIds.push(bidRequest.bidId)
+ })
+
+ const alkimiConfig = config.getConfig('alkimi');
+
+ let payload = {
+ requestId: bidderRequest.auctionId,
+ signRequest: { bids, randomUUID: alkimiConfig && alkimiConfig.randomUUID },
+ bidIds,
+ referer: bidderRequest.refererInfo.referer,
+ signature: alkimiConfig && alkimiConfig.signature
+ }
+
+ const options = {
+ contentType: 'application/json',
+ customHeaders: {
+ 'Rtb-Direct': true
+ }
+ }
+
+ return {
+ method: 'POST',
+ url: ENDPOINT,
+ data: payload,
+ options
+ };
+ },
+
+ interpretResponse: function (serverResponse, request) {
+ const serverBody = serverResponse.body;
+ if (!serverBody || typeof serverBody !== 'object') {
+ return [];
+ }
+
+ const { prebidResponse } = serverBody;
+ if (!prebidResponse || typeof prebidResponse !== 'object') {
+ return [];
+ }
+
+ let bids = [];
+ prebidResponse.forEach(bidResponse => {
+ let bid = deepClone(bidResponse);
+ bid.cpm = parseFloat(bidResponse.cpm);
+
+ // banner or video
+ if (VIDEO === bid.mediaType) {
+ bid.vastXml = bid.ad;
+ }
+
+ bid.meta = {};
+ bid.meta.advertiserDomains = bid.adomain || [];
+
+ bids.push(bid);
+ })
+
+ return bids;
+ },
+
+ onBidWon: function (bid) {
+ let winUrl;
+ if (bid.winUrl || bid.vastUrl) {
+ winUrl = bid.winUrl ? bid.winUrl : bid.vastUrl;
+ winUrl = winUrl.replace(/\$\{AUCTION_PRICE\}/, bid.cpm);
+ } else if (bid.ad) {
+ let trackImg = bid.ad.match(/(?!^)/);
+ bid.ad = bid.ad.replace(trackImg[0], '');
+ winUrl = trackImg[0].split('"')[1];
+ winUrl = winUrl.replace(/\$%7BAUCTION_PRICE%7D/, bid.cpm);
+ } else {
+ return false;
+ }
+
+ ajax(winUrl, null);
+ return true;
+ }
+}
+
+function prepareSizes(sizes) {
+ return sizes && sizes.map(size => ({ width: size[0], height: size[1] }));
+}
+
+const getFormatType = bidRequest => {
+ if (deepAccess(bidRequest, 'mediaTypes.banner')) return 'Banner'
+ if (deepAccess(bidRequest, 'mediaTypes.video')) return 'Video'
+ if (deepAccess(bidRequest, 'mediaTypes.audio')) return 'Audio'
+}
+
+registerBidder(spec);
diff --git a/modules/alkimiBidAdapter.md b/modules/alkimiBidAdapter.md
new file mode 100644
index 00000000000..92a7c2aefe1
--- /dev/null
+++ b/modules/alkimiBidAdapter.md
@@ -0,0 +1,29 @@
+# Overview
+
+```
+Module Name: Alkimi Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: abogdanov@asteriosoft.com
+```
+
+# Description
+
+Connects to Alkimi Bidder for bids.
+Alkimi bid adapter supports Banner and Video ads.
+
+# Test Parameters
+```
+const adUnits = [
+ {
+ bids: [
+ {
+ bidder: 'alkimi',
+ params: {
+ bidFloor: 0.1,
+ token: '?????????????????????', // Publisher Token provided by Alkimi
+ }
+ }
+ ]
+ }
+];
+```
diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js
new file mode 100644
index 00000000000..26a8c370af3
--- /dev/null
+++ b/modules/bidwatchAnalyticsAdapter.js
@@ -0,0 +1,90 @@
+import adapter from '../src/AnalyticsAdapter.js';
+import adapterManager from '../src/adapterManager.js';
+import CONSTANTS from '../src/constants.json';
+import { ajax } from '../src/ajax.js';
+
+const analyticsType = 'endpoint';
+const url = 'URL_TO_SERVER_ENDPOINT';
+
+const {
+ EVENTS: {
+ AUCTION_END,
+ BID_WON,
+ }
+} = CONSTANTS;
+
+let allEvents = {}
+let initOptions = {}
+let endpoint = 'https://default'
+let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids']
+
+function getAdapterNameForAlias(aliasName) {
+ return adapterManager.aliasRegistry[aliasName] || aliasName;
+}
+
+function setOriginalBidder(arg) {
+ Object.keys(arg).forEach(key => {
+ arg[key]['originalBidder'] = getAdapterNameForAlias(arg[key]['bidderCode']);
+ if (typeof arg[key]['creativeId'] == 'number') { arg[key]['creativeId'] = arg[key]['creativeId'].toString(); }
+ });
+ return arg
+}
+
+function checkBidderCode(args) {
+ if (typeof args == 'object') {
+ for (let i = 0; i < objectToSearchForBidderCode.length; i++) {
+ if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]]) }
+ }
+ }
+ if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); }
+ if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); }
+ return args
+}
+
+function addEvent(eventType, args) {
+ if (allEvents[eventType] == undefined) { allEvents[eventType] = [] }
+ if (eventType && args) { args = checkBidderCode(args); }
+ allEvents[eventType].push(args);
+}
+
+function handleBidWon(args) {
+ if (typeof allEvents.bidRequested == 'object' && allEvents.bidRequested.length > 0 && allEvents.bidRequested[0].gdprConsent) { args.gdpr = allEvents.bidRequested[0].gdprConsent; }
+ ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true});
+}
+
+function handleAuctionEnd() {
+ ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true});
+}
+
+let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), {
+ track({
+ eventType,
+ args
+ }) {
+ addEvent(eventType, args);
+ switch (eventType) {
+ case AUCTION_END:
+ handleAuctionEnd();
+ break;
+ case BID_WON:
+ handleBidWon(args);
+ break;
+ }
+ }});
+
+// save the base class function
+bidwatchAnalytics.originEnableAnalytics = bidwatchAnalytics.enableAnalytics;
+
+// override enableAnalytics so we can get access to the config passed in from the page
+bidwatchAnalytics.enableAnalytics = function (config) {
+ bidwatchAnalytics.originEnableAnalytics(config); // call the base class function
+ initOptions = config.options;
+ if (initOptions.domain) { endpoint = 'https://' + initOptions.domain; }
+};
+
+adapterManager.registerAnalyticsAdapter({
+ adapter: bidwatchAnalytics,
+ code: 'bidwatch'
+});
+
+export default bidwatchAnalytics;
diff --git a/modules/bidwatchAnalyticsAdapter.md b/modules/bidwatchAnalyticsAdapter.md
new file mode 100644
index 00000000000..bfa453640b8
--- /dev/null
+++ b/modules/bidwatchAnalyticsAdapter.md
@@ -0,0 +1,21 @@
+# Overview
+Module Name: bidwatch Analytics Adapter
+
+Module Type: Analytics Adapter
+
+Maintainer: tech@bidwatch.io
+
+# Description
+
+Analytics adapter for bidwatch.io.
+
+# Test Parameters
+
+```
+{
+ provider: 'bidwatch',
+ options : {
+ domain: 'test.endpoint'
+ }
+}
+```
diff --git a/modules/big-richmediaBidAdapter.js b/modules/big-richmediaBidAdapter.js
index cd8b2462eb8..2ee31e8cfd6 100644
--- a/modules/big-richmediaBidAdapter.js
+++ b/modules/big-richmediaBidAdapter.js
@@ -8,7 +8,7 @@ const BIDDER_CODE = 'big-richmedia';
const metadataByRequestId = {};
export const spec = {
- version: '1.4.0',
+ version: '1.5.0',
code: BIDDER_CODE,
gvlid: baseAdapter.GVLID, // use base adapter gvlid
supportedMediaTypes: [ BANNER, VIDEO ],
@@ -78,6 +78,14 @@ export const spec = {
customSelector,
isReplayable
};
+
+ // This is a workaround needed for the rendering step (so that the adserver iframe does not get resized to 1800x1000
+ // when there is skin demand
+ if (format === 'skin') {
+ renderParams.width = 1
+ renderParams.height = 1
+ }
+
const encoded = window.btoa(JSON.stringify(renderParams));
bid.ad = `
`;
diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js
index 6223626834d..a798671cbaf 100644
--- a/modules/bizzclickBidAdapter.js
+++ b/modules/bizzclickBidAdapter.js
@@ -95,6 +95,7 @@ export const spec = {
},
regs: {
coppa: config.getConfig('coppa') === true ? 1 : 0,
+ ext: {}
},
user: {
ext: {}
@@ -106,25 +107,15 @@ export const spec = {
imp: [impObject],
};
- if (bidderRequest && bidderRequest.uspConsent) {
- data.regs.ext.us_privacy = bidderRequest.uspConsent;
- }
-
- if (bidderRequest && bidderRequest.gdprConsent) {
- let { gdprApplies, consentString } = bidderRequest.gdprConsent;
- data.regs.ext.gdpr = gdprApplies ? 1 : 0;
- data.user.ext.consent = consentString;
- }
-
- if (bidRequest.schain) {
- deepSetValue(data, 'source.ext.schain', bidRequest.schain);
- }
-
let connection = navigator.connection || navigator.webkitConnection;
if (connection && connection.effectiveType) {
data.device.connectiontype = connection.effectiveType;
}
if (bidRequest) {
+ if (bidRequest.schain) {
+ deepSetValue(data, 'source.ext.schain', bidRequest.schain);
+ }
+
if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) {
deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0);
deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString);
diff --git a/modules/cpexIdSystem.js b/modules/cpexIdSystem.js
new file mode 100644
index 00000000000..4600601cb11
--- /dev/null
+++ b/modules/cpexIdSystem.js
@@ -0,0 +1,49 @@
+/**
+ * This module adds 'caid' to the User ID module
+ * The {@link module:modules/userId} module is required
+ * @module modules/cpexIdSystem
+ * @requires module:modules/userId
+ */
+
+import { submodule } from '../src/hook.js'
+import { getStorageManager } from '../src/storageManager.js'
+
+window.top.cpexIdVersion = '0.0.3'
+
+// Returns StorageManager
+export const storage = getStorageManager({ gvlid: 570, moduleName: 'cpexId' })
+
+// Returns the id string from either cookie or localstorage
+const getId = () => { return storage.getCookie('caid') || storage.getDataFromLocalStorage('caid') }
+
+/** @type {Submodule} */
+export const cpexIdSubmodule = {
+ /**
+ * used to link submodule with config
+ * @type {string}
+ */
+ name: 'cpexId',
+ /**
+ * Vendor ID of Czech Publisher Exchange
+ * @type {Number}
+ */
+ gvlid: 570,
+ /**
+ * decode the stored id value for passing to bid requests
+ * @function decode
+ * @param {(Object|string)} value
+ * @returns {(Object|undefined)}
+ */
+ decode (value) { return { cpexId: getId() } },
+ /**
+ * performs action to obtain id and return a value in the callback's response argument
+ * @function
+ * @param {SubmoduleConfig} [config]
+ * @param {ConsentData} [consentData]
+ * @param {(Object|undefined)} cacheIdObj
+ * @returns {IdResponse|undefined}
+ */
+ getId (config, consentData) { return { cpexId: getId() } }
+}
+
+submodule('userId', cpexIdSubmodule)
diff --git a/modules/cpexIdSystem.md b/modules/cpexIdSystem.md
new file mode 100644
index 00000000000..8aceb7fe4ec
--- /dev/null
+++ b/modules/cpexIdSystem.md
@@ -0,0 +1,27 @@
+## CPEx User ID Submodule
+
+CPExID is provided by [Czech Publisher Exchange](https://www.cpex.cz/), or CPEx. It is a user ID for ad targeting by using first party cookie, or localStorage mechanism. Please contact CPEx before using this ID.
+
+## Building Prebid with CPExID Support
+
+First, make sure to add the cpexId to your Prebid.js package with:
+
+```
+gulp build --modules=cpexIdSystem
+```
+
+The following configuration parameters are available:
+
+```javascript
+pbjs.setConfig({
+ userSync: {
+ userIds: [{
+ name: 'cpexId'
+ }]
+ }
+});
+```
+
+| Param under userSync.userIds[] | Scope | Type | Description | Example |
+| --- | --- | --- | --- | --- |
+| name | Required | String | The name of this module. | `"cpexId"` |
diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js
index f2143c1cced..b57be00d3ac 100644
--- a/modules/id5IdSystem.js
+++ b/modules/id5IdSystem.js
@@ -5,7 +5,16 @@
* @requires module:modules/userId
*/
-import { deepAccess, logInfo, deepSetValue, logError, isEmpty, isEmptyStr, logWarn } from '../src/utils.js';
+import {
+ deepAccess,
+ logInfo,
+ deepSetValue,
+ logError,
+ isEmpty,
+ isEmptyStr,
+ logWarn,
+ safeJSONParse
+} from '../src/utils.js';
import { ajax } from '../src/ajax.js';
import { submodule } from '../src/hook.js';
import { getRefererInfo } from '../src/refererDetection.js';
@@ -24,7 +33,7 @@ const LOG_PREFIX = 'User ID - ID5 submodule: ';
// cookie in the array is the most preferred to use
const LEGACY_COOKIE_NAMES = [ 'pbjs-id5id', 'id5id.1st', 'id5id' ];
-const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME});
+export const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME});
/** @type {Submodule} */
export const id5IdSubmodule = {
@@ -253,7 +262,7 @@ function getLegacyCookieSignature() {
let legacyStoredValue;
LEGACY_COOKIE_NAMES.forEach(function(cookie) {
if (storage.getCookie(cookie)) {
- legacyStoredValue = JSON.parse(storage.getCookie(cookie)) || legacyStoredValue;
+ legacyStoredValue = safeJSONParse(storage.getCookie(cookie)) || legacyStoredValue;
}
});
return (legacyStoredValue && legacyStoredValue.signature) || '';
diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js
index f9957f8612c..4a8201c57b1 100644
--- a/modules/improvedigitalBidAdapter.js
+++ b/modules/improvedigitalBidAdapter.js
@@ -304,7 +304,10 @@ const ID_REQUEST = {
}
if (deepAccess(bidRequest, 'mediaTypes.native')) {
- imp.native = this.buildNativeRequest(bidRequest);
+ const nativeImp = this.buildNativeRequest(bidRequest);
+ if (nativeImp) {
+ imp.native = nativeImp;
+ }
}
return imp;
@@ -358,7 +361,10 @@ const ID_REQUEST = {
},
buildNativeRequest(bidRequest) {
- const nativeParams = bidRequest.mediaTypes.native;
+ const nativeParams = bidRequest.nativeParams;
+ if (!nativeParams) {
+ return null;
+ }
const request = {
assets: [],
}
@@ -392,6 +398,10 @@ const ID_REQUEST = {
request.assets.push(asset);
}
}
+ if (!request.assets.length) {
+ logWarn('No native assets recognized. Ignoring native ad request');
+ return null;
+ }
return { ver: NATIVE_DATA.VERSION, request: JSON.stringify(request) };
},
diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js
index 700d3276e06..90ea17395f7 100644
--- a/modules/jixieBidAdapter.js
+++ b/modules/jixieBidAdapter.js
@@ -1,4 +1,4 @@
-import { logWarn, parseUrl, deepAccess, isArray } from '../src/utils.js';
+import { logWarn, parseUrl, deepAccess, isArray, getDNT } from '../src/utils.js';
import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
@@ -60,9 +60,16 @@ function fetchIds_() {
return ret;
}
+// device in the payload had been a simple string ('desktop', 'mobile')
+// Now changed to an object. yes the backend is able to handle it.
function getDevice_() {
- return ((/(ios|ipod|ipad|iphone|android|blackberry|iemobile|opera mini|webos)/i).test(navigator.userAgent)
- ? 'mobile' : 'desktop');
+ const device = config.getConfig('device') || {};
+ device.w = device.w || window.innerWidth;
+ device.h = device.h || window.innerHeight;
+ device.ua = device.ua || navigator.userAgent;
+ device.dnt = getDNT() ? 1 : 0;
+ device.language = (navigator && navigator.language) ? navigator.language.split('-')[0] : '';
+ return device;
}
function pingTracking_(endpointOverride, qpobj) {
diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js
index 275ab38915d..e58c643f4f0 100644
--- a/modules/lkqdBidAdapter.js
+++ b/modules/lkqdBidAdapter.js
@@ -36,7 +36,6 @@ export const spec = {
const serverRequestObjects = [];
const UTC_OFFSET = new Date().getTimezoneOffset();
const UA = navigator.userAgent;
- const IP = navigator.ip ? navigator.ip : 'prebid.js';
const USP = BIDDER_REQUEST.uspConsent || null;
const REFERER = BIDDER_REQUEST.refererInfo ? new URL(BIDDER_REQUEST.refererInfo.referer).hostname : window.location.hostname;
const BIDDER_GDPR = BIDDER_REQUEST.gdprConsent && BIDDER_REQUEST.gdprConsent.gdprApplies ? 1 : null;
@@ -60,8 +59,7 @@ export const spec = {
ua: UA,
geo: {
utcoffset: UTC_OFFSET
- },
- ip: IP
+ }
},
user: {
ext: {}
diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js
index 1fc42099f04..af2cfa1800d 100644
--- a/modules/prebidServerBidAdapter/index.js
+++ b/modules/prebidServerBidAdapter/index.js
@@ -1233,18 +1233,13 @@ export const processPBSRequest = hook('sync', function (s2sBidRequest, bidReques
let { gdprConsent } = getConsentData(bidRequests);
const adUnits = deepClone(s2sBidRequest.ad_units);
- // at this point ad units should have a size array either directly or mapped so filter for that
- const validAdUnits = adUnits.filter(unit =>
- unit.mediaTypes && (unit.mediaTypes.native || (unit.mediaTypes.banner && unit.mediaTypes.banner.sizes) || (unit.mediaTypes.video && unit.mediaTypes.video.playerSize))
- );
-
// in case config.bidders contains invalid bidders, we only process those we sent requests for
- const requestedBidders = validAdUnits
+ const requestedBidders = adUnits
.map(adUnit => adUnit.bids.map(bid => bid.bidder).filter(uniques))
- .reduce(flatten)
+ .reduce(flatten, [])
.filter(uniques);
- const ortb2 = new ORTB2(s2sBidRequest, bidRequests, validAdUnits, requestedBidders);
+ const ortb2 = new ORTB2(s2sBidRequest, bidRequests, adUnits, requestedBidders);
const request = ortb2.buildRequest();
const requestJson = request && JSON.stringify(request);
logInfo('BidRequest: ' + requestJson);
diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js
index 97e220c9693..1f33250a160 100644
--- a/modules/sovrnBidAdapter.js
+++ b/modules/sovrnBidAdapter.js
@@ -63,8 +63,9 @@ export const spec = {
/**
* Format the bid request object for our endpoint
- * @param {BidRequest[]} bidRequests Array of Sovrn bidders
* @return object of parameters for Prebid AJAX request
+ * @param bidReqs
+ * @param bidderRequest
*/
buildRequests: function(bidReqs, bidderRequest) {
try {
@@ -191,14 +192,12 @@ export const spec = {
* @return {Bid[]} An array of formatted bids.
*/
interpretResponse: function({ body: {id, seatbid} }) {
+ if (!id || !seatbid || !Array.isArray(seatbid)) return []
+
try {
- let sovrnBidResponses = [];
- if (id &&
- seatbid &&
- seatbid.length > 0 &&
- seatbid[0].bid &&
- seatbid[0].bid.length > 0) {
- seatbid[0].bid.map(sovrnBid => {
+ return seatbid
+ .filter(seat => seat)
+ .map(seat => seat.bid.map(sovrnBid => {
const bid = {
requestId: sovrnBid.impid,
cpm: parseFloat(sovrnBid.price),
@@ -208,23 +207,23 @@ export const spec = {
dealId: sovrnBid.dealid || null,
currency: 'USD',
netRevenue: true,
- ttl: sovrnBid.ext ? (sovrnBid.ext.ttl || 90) : 90,
+ mediaType: sovrnBid.nurl ? BANNER : VIDEO,
+ ttl: sovrnBid.ext?.ttl || 90,
meta: { advertiserDomains: sovrnBid && sovrnBid.adomain ? sovrnBid.adomain : [] }
}
- if (!sovrnBid.nurl) {
- bid.mediaType = VIDEO
- bid.vastXml = decodeURIComponent(sovrnBid.adm)
- } else {
- bid.mediaType = BANNER
+ if (sovrnBid.nurl) {
bid.ad = decodeURIComponent(`${sovrnBid.adm}`)
+ } else {
+ bid.vastXml = decodeURIComponent(sovrnBid.adm)
}
- sovrnBidResponses.push(bid);
- });
- }
- return sovrnBidResponses
+
+ return bid
+ }))
+ .flat()
} catch (e) {
- logError('Could not intrepret bidresponse, error deatils:', e);
+ logError('Could not interpret bidresponse, error details:', e)
+ return e
}
},
diff --git a/modules/talkadsBidAdapter.js b/modules/talkadsBidAdapter.js
index 068dce23b43..dae452b9a7d 100644
--- a/modules/talkadsBidAdapter.js
+++ b/modules/talkadsBidAdapter.js
@@ -5,11 +5,12 @@ import {ajax} from '../src/ajax.js';
const CURRENCY = 'EUR';
const BIDDER_CODE = 'talkads';
+const GVLID = 1074;
export const spec = {
code: BIDDER_CODE,
+ gvlid: GVLID,
supportedMediaTypes: [ NATIVE, BANNER ],
- params: null,
/**
* Determines whether or not the given bid request is valid.
@@ -31,7 +32,7 @@ export const spec = {
utils.logError('VALIDATION FAILED : the parameter "bidder_url" must be defined');
return false;
}
- this.params = poBid.params;
+
return !!(poBid.nativeParams || poBid.sizes);
}, // isBidRequestValid
@@ -54,6 +55,7 @@ export const spec = {
}
return loOne;
});
+ let laParams = paValidBidRequests[0].params;
const loServerRequest = {
cur: CURRENCY,
timeout: poBidderRequest.timeout,
@@ -71,7 +73,7 @@ export const spec = {
loServerRequest.gdpr.consent = poBidderRequest.gdprConsent.consentString;
}
}
- const lsUrl = this.params.bidder_url + '/' + this.params.tag_id;
+ const lsUrl = laParams.bidder_url + '/' + laParams.tag_id;
return {
method: 'POST',
url: lsUrl,
@@ -86,7 +88,7 @@ export const spec = {
* @param poPidRequest Request original server request
* @return An array of bids which were nested inside the server.
*/
- interpretResponse: (poServerResponse, poPidRequest) => {
+ interpretResponse: function (poServerResponse, poPidRequest) {
utils.logInfo('interpretResponse : ', poServerResponse);
if (!poServerResponse.body) {
return [];
@@ -120,8 +122,9 @@ export const spec = {
*/
onBidWon: function (poBid) {
utils.logInfo('onBidWon : ', poBid);
+ let laParams = poBid.params[0];
if (poBid.pbid) {
- ajax(this.params.bidder_url + 'won/' + poBid.pbid);
+ ajax(laParams.bidder_url + 'won/' + poBid.pbid);
}
}, // onBidWon
};
diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js
index 006357cd4b9..88e89bcd64b 100644
--- a/modules/tpmnBidAdapter.js
+++ b/modules/tpmnBidAdapter.js
@@ -1,13 +1,16 @@
/* eslint-disable no-tabs */
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { parseUrl, deepAccess } from '../src/utils.js';
+import { getStorageManager } from '../src/storageManager.js';
import { BANNER } from '../src/mediaTypes.js';
+import { config } from '../src/config.js';
export const ADAPTER_VERSION = '1';
const SUPPORTED_AD_TYPES = [BANNER];
-
const BIDDER_CODE = 'tpmn';
const URL = 'https://ad.tpmn.co.kr/prebidhb.tpmn';
+const IFRAMESYNC = 'https://ad.tpmn.co.kr/sync.tpmn?type=iframe';
+export const storage = getStorageManager({bidderCode: BIDDER_CODE});
export const spec = {
code: BIDDER_CODE,
@@ -18,20 +21,20 @@ export const spec = {
* @param {object} bid The bid to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
- isBidRequestValid: function(bid) {
+ isBidRequestValid: function (bid) {
return 'params' in bid &&
- 'inventoryId' in bid.params &&
- 'publisherId' in bid.params &&
- !isNaN(Number(bid.params.inventoryId)) &&
- bid.params.inventoryId > 0 &&
- (typeof bid.mediaTypes.banner.sizes != 'undefined'); // only accepting appropriate sizes
+ 'inventoryId' in bid.params &&
+ 'publisherId' in bid.params &&
+ !isNaN(Number(bid.params.inventoryId)) &&
+ bid.params.inventoryId > 0 &&
+ (typeof bid.mediaTypes.banner.sizes != 'undefined'); // only accepting appropriate sizes
},
/**
- * @param {BidRequest[]} bidRequests
- * @param {*} bidderRequest
- * @return {ServerRequest}
- */
+ * @param {BidRequest[]} bidRequests
+ * @param {*} bidderRequest
+ * @return {ServerRequest}
+ */
buildRequests: (bidRequests, bidderRequest) => {
if (bidRequests.length === 0) {
return [];
@@ -49,11 +52,11 @@ export const spec = {
}];
},
/**
- * Unpack the response from the server into a list of bids.
- *
- * @param {serverResponse} serverResponse A successful response from the server.
- * @return {Bid[]} An array of bids which were nested inside the server.
- */
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {serverResponse} serverResponse A successful response from the server.
+ * @return {Bid[]} An array of bids which were nested inside the server.
+ */
interpretResponse: function (serverResponse, serverRequest) {
if (!Array.isArray(serverResponse.body)) {
return [];
@@ -63,7 +66,48 @@ export const spec = {
// our server directly returns the format needed by prebid.js so no more
// transformation is needed here.
return bidResults;
- }
+ },
+
+ getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) {
+ const syncArr = [];
+ if (syncOptions.iframeEnabled) {
+ let policyParam = '';
+ if (gdprConsent && gdprConsent.consentString) {
+ if (typeof gdprConsent.gdprApplies === 'boolean') {
+ policyParam += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`;
+ } else {
+ policyParam += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`;
+ }
+ }
+ if (uspConsent && uspConsent.consentString) {
+ policyParam += `&ccpa_consent=${uspConsent.consentString}`;
+ }
+ const coppa = config.getConfig('coppa') ? 1 : 0;
+ policyParam += `&coppa=${coppa}`;
+ syncArr.push({
+ type: 'iframe',
+ url: IFRAMESYNC + policyParam
+ })
+ } else {
+ syncArr.push({
+ type: 'image',
+ url: 'https://x.bidswitch.net/sync?ssp=tpmn'
+ });
+ syncArr.push({
+ type: 'image',
+ url: 'https://gocm.c.appier.net/tpmn'
+ });
+ syncArr.push({
+ type: 'image',
+ url: 'https://info.mmnneo.com/getGuidRedirect.info?url=https%3A%2F%2Fad.tpmn.co.kr%2Fcookiesync.tpmn%3Ftpmn_nid%3Dbf91e8b3b9d3f1af3fc1d657f090b4fb%26tpmn_buid%3D'
+ });
+ syncArr.push({
+ type: 'image',
+ url: 'https://sync.aralego.com/idSync?redirect=https%3A%2F%2Fad.tpmn.co.kr%2FpixelCt.tpmn%3Ftpmn_nid%3Dde91e8b3b9d3f1af3fc1d657f090b815%26tpmn_buid%3DSspCookieUserId'
+ });
+ }
+ return syncArr;
+ },
};
registerBidder(spec);
diff --git a/modules/userId/index.js b/modules/userId/index.js
index 045786f59ec..5aed9b808f6 100644
--- a/modules/userId/index.js
+++ b/modules/userId/index.js
@@ -423,7 +423,7 @@ function processSubmoduleCallbacks(submodules, cb) {
}, submodules.length);
}
submodules.forEach(function (submodule) {
- submodule.callback(function callbackCompleted(idObj) {
+ function callbackCompleted(idObj) {
// if valid, id data should be saved to cookie/html storage
if (idObj) {
if (submodule.config.storage) {
@@ -435,8 +435,13 @@ function processSubmoduleCallbacks(submodules, cb) {
logInfo(`${MODULE_NAME}: ${submodule.submodule.name} - request id responded with an empty value`);
}
done();
- });
-
+ }
+ try {
+ submodule.callback(callbackCompleted);
+ } catch (e) {
+ logError(`Error in userID module '${submodule.submodule.name}':`, e);
+ done();
+ }
// clear callback, this prop is used to test if all submodule callbacks are complete below
submodule.callback = undefined;
});
@@ -881,8 +886,12 @@ function initSubmodules(dest, submodules, consentData, forceRefresh = false) {
setStoredConsentData(consentData);
const initialized = userIdModules.reduce((carry, submodule) => {
- populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh);
- carry.push(submodule);
+ try {
+ populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh);
+ carry.push(submodule);
+ } catch (e) {
+ logError(`Error in userID module '${submodule.submodule.name}':`, e);
+ }
return carry;
}, []);
if (initialized.length) {
diff --git a/modules/userId/userId.md b/modules/userId/userId.md
index 166cfb6bfde..4234c9c57d3 100644
--- a/modules/userId/userId.md
+++ b/modules/userId/userId.md
@@ -111,6 +111,8 @@ pbjs.setConfig({
name: '_criteoId',
expires: 1
}
+ }, {
+ name: "cpexId"
}, {
name: 'mwOpenLinkId',
params: {
diff --git a/package-lock.json b/package-lock.json
index c4e86036f99..c5797787eb0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "6.23.0-pre",
+ "version": "6.24.0-pre",
"lockfileVersion": 2,
"requires": true,
"packages": {
diff --git a/package.json b/package.json
index 9e5d68ac5e8..f2d282c77aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "6.23.0-pre",
+ "version": "6.24.0-pre",
"description": "Header Bidding Management Library",
"main": "src/prebid.js",
"scripts": {
diff --git a/src/prebid.js b/src/prebid.js
index 0dc7754bf1a..c12ecc7878a 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -20,7 +20,7 @@ import { executeRenderer, isRendererRequired } from './Renderer.js';
import { createBid } from './bidfactory.js';
import { storageCallbacks } from './storageManager.js';
import { emitAdRenderSucceeded, emitAdRenderFail } from './adRendering.js';
-import { gdprDataHandler, uspDataHandler } from './adapterManager.js'
+import {gdprDataHandler, getS2SBidderSet, uspDataHandler} from './adapterManager.js';
const $$PREBID_GLOBAL$$ = getGlobal();
const CONSTANTS = require('./constants.json');
@@ -584,18 +584,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo
});
export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ortb2Fragments } = {}) {
- let _s2sConfigs = [];
- const s2sBidders = [];
- config.getConfig('s2sConfig', config => {
- if (config && config.s2sConfig) {
- _s2sConfigs = Array.isArray(config.s2sConfig) ? config.s2sConfig : [config.s2sConfig];
- }
- });
-
- _s2sConfigs.forEach(s2sConfig => {
- s2sBidders.push(...s2sConfig.bidders);
- });
-
+ const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []);
adUnits = checkAdUnitSetup(adUnits);
if (adUnitCodes && adUnitCodes.length) {
@@ -620,7 +609,7 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
const allBidders = adUnit.bids.map(bid => bid.bidder);
const bidderRegistry = adapterManager.bidderRegistry;
- const bidders = (s2sBidders) ? allBidders.filter(bidder => !includes(s2sBidders, bidder)) : allBidders;
+ const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder));
adUnit.transactionId = generateUUID();
diff --git a/src/utils.js b/src/utils.js
index 33755a4fb82..3109b52c4df 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1364,3 +1364,14 @@ export function cyrb53Hash(str, seed = 0) {
export function getWindowFromDocument(doc) {
return (doc) ? doc.defaultView : null;
}
+
+/**
+ * returns the result of `JSON.parse(data)`, or undefined if that throws an error.
+ * @param data
+ * @returns {any}
+ */
+export function safeJSONParse(data) {
+ try {
+ return JSON.parse(data);
+ } catch (e) {}
+}
diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js
index 9db41b6934e..94b8100c7fe 100644
--- a/test/spec/modules/adfBidAdapter_spec.js
+++ b/test/spec/modules/adfBidAdapter_spec.js
@@ -178,6 +178,33 @@ describe('Adf adapter', function () {
assert.equal(request.source.fd, 1);
});
+ it('should not set coppa when coppa is not provided or is set to false', function () {
+ config.setConfig({
+ });
+ let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }];
+ let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } };
+ let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data);
+
+ assert.equal(request.regs.coppa, undefined);
+
+ config.setConfig({
+ coppa: false
+ });
+ request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data);
+
+ assert.equal(request.regs.coppa, undefined);
+ });
+
+ it('should set coppa to 1 when coppa is provided with value true', function () {
+ config.setConfig({
+ coppa: true
+ });
+ let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }];
+ let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data);
+
+ assert.equal(request.regs.coppa, 1);
+ });
+
it('should send info about device', function () {
config.setConfig({
device: { w: 100, h: 100 }
diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js
index 89d140a7f25..feee8e39b45 100644
--- a/test/spec/modules/admanBidAdapter_spec.js
+++ b/test/spec/modules/admanBidAdapter_spec.js
@@ -1,8 +1,9 @@
import {expect} from 'chai';
import {spec} from '../../../modules/admanBidAdapter.js';
+import {deepClone} from '../../../src/utils'
describe('AdmanAdapter', function () {
- let bid = {
+ let bidBanner = {
bidId: '2dd581a2b6281d',
bidder: 'adman',
bidderRequestId: '145e1d6a7837c9',
@@ -32,6 +33,20 @@ describe('AdmanAdapter', function () {
]
}
};
+
+ let bidVideo = deepClone({
+ ...bidBanner,
+ params: {
+ placementId: 0,
+ traffic: 'video'
+ },
+ mediaTypes: {
+ video: {
+ playerSize: [300, 250]
+ }
+ }
+ });
+
let bidderRequest = {
bidderCode: 'adman',
auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff',
@@ -40,25 +55,27 @@ describe('AdmanAdapter', function () {
auctionStart: 1472239426000,
timeout: 5000,
uspConsent: '1YN-',
+ gdprConsent: 'gdprConsent',
refererInfo: {
referer: 'http://www.example.com',
reachedTop: true,
},
- bids: [bid]
+ bids: [bidBanner, bidVideo]
}
describe('isBidRequestValid', function () {
it('Should return true when placementId can be cast to a number', function () {
- expect(spec.isBidRequestValid(bid)).to.be.true;
+ expect(spec.isBidRequestValid(bidBanner)).to.be.true;
});
it('Should return false when placementId is not a number', function () {
- bid.params.placementId = 'aaa';
- expect(spec.isBidRequestValid(bid)).to.be.false;
+ bidBanner.params.placementId = 'aaa';
+ expect(spec.isBidRequestValid(bidBanner)).to.be.false;
+ bidBanner.params.placementId = 0;
});
});
describe('buildRequests', function () {
- let serverRequest = spec.buildRequests([bid], bidderRequest);
+ let serverRequest = spec.buildRequests([bidBanner], bidderRequest);
it('Creates a ServerRequest object with method, URL and data', function () {
expect(serverRequest).to.exist;
expect(serverRequest.method).to.exist;
@@ -75,10 +92,11 @@ describe('AdmanAdapter', function () {
expect(serverRequest.data.ccpa).to.be.an('string')
})
- it('Returns valid data if array of bids is valid', function () {
+ it('Returns valid BANNER data if array of bids is valid', function () {
+ serverRequest = spec.buildRequests([bidBanner], bidderRequest);
let data = serverRequest.data;
expect(data).to.be.an('object');
- expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa');
+ expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa', 'gdpr');
expect(data.deviceWidth).to.be.a('number');
expect(data.deviceHeight).to.be.a('number');
expect(data.language).to.be.a('string');
@@ -97,6 +115,33 @@ describe('AdmanAdapter', function () {
expect(placement.bidFloor).to.be.an('number');
}
});
+
+ it('Returns valid VIDEO data if array of bids is valid', function () {
+ serverRequest = spec.buildRequests([bidVideo], bidderRequest);
+ let data = serverRequest.data;
+ expect(data).to.be.an('object');
+ expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'ccpa', 'gdpr');
+ expect(data.deviceWidth).to.be.a('number');
+ expect(data.deviceHeight).to.be.a('number');
+ expect(data.language).to.be.a('string');
+ expect(data.secure).to.be.within(0, 1);
+ expect(data.host).to.be.a('string');
+ expect(data.page).to.be.a('string');
+ let placements = data['placements'];
+ for (let i = 0; i < placements.length; i++) {
+ let placement = placements[i];
+ expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'bidFloor',
+ 'playerSize', 'minduration', 'maxduration', 'mimes', 'protocols', 'startdelay', 'placement', 'skip',
+ 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity');
+ expect(placement.schain).to.be.an('object')
+ expect(placement.placementId).to.be.a('number');
+ expect(placement.bidId).to.be.a('string');
+ expect(placement.traffic).to.be.a('string');
+ expect(placement.sizes).to.be.an('array');
+ expect(placement.bidFloor).to.be.an('number');
+ }
+ });
+
it('Returns empty data if no valid requests are passed', function () {
serverRequest = spec.buildRequests([]);
let data = serverRequest.data;
@@ -105,9 +150,9 @@ describe('AdmanAdapter', function () {
});
describe('buildRequests with user ids', function () {
- bid.userId = {}
- bid.userId.uid2 = { id: 'uid2id123' };
- let serverRequest = spec.buildRequests([bid], bidderRequest);
+ bidBanner.userId = {}
+ bidBanner.userId.uid2 = { id: 'uid2id123' };
+ let serverRequest = spec.buildRequests([bidBanner], bidderRequest);
it('Returns valid data if array of bids is valid', function () {
let data = serverRequest.data;
let placements = data['placements'];
@@ -130,31 +175,34 @@ describe('AdmanAdapter', function () {
});
describe('interpretResponse', function () {
- let resObject = {
- body: [ {
- requestId: '123',
- mediaType: 'banner',
- cpm: 0.3,
- width: 320,
- height: 50,
- ad: '
Hello ad
',
- ttl: 1000,
- creativeId: '123asd',
- netRevenue: true,
- currency: 'USD',
- meta: {
- advertiserDomains: ['google.com'],
- advertiserId: 1234
- }
- } ]
- };
- let serverResponses = spec.interpretResponse(resObject);
- it('Returns an array of valid server responses if response object is valid', function () {
+ it('(BANNER) Returns an array of valid server responses if response object is valid', function () {
+ const resBannerObject = {
+ body: [ {
+ requestId: '123',
+ mediaType: 'banner',
+ cpm: 0.3,
+ width: 320,
+ height: 50,
+ ad: 'Hello ad
',
+ ttl: 1000,
+ creativeId: '123asd',
+ netRevenue: true,
+ currency: 'USD',
+ adomain: ['example.com'],
+ meta: {
+ advertiserDomains: ['google.com'],
+ advertiserId: 1234
+ }
+ } ]
+ };
+
+ const serverResponses = spec.interpretResponse(resBannerObject);
+
expect(serverResponses).to.be.an('array').that.is.not.empty;
for (let i = 0; i < serverResponses.length; i++) {
let dataItem = serverResponses[i];
expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId',
- 'netRevenue', 'currency', 'mediaType', 'meta');
+ 'netRevenue', 'currency', 'mediaType', 'meta', 'adomain');
expect(dataItem.requestId).to.be.a('string');
expect(dataItem.cpm).to.be.a('number');
expect(dataItem.width).to.be.a('number');
@@ -167,21 +215,124 @@ describe('AdmanAdapter', function () {
expect(dataItem.mediaType).to.be.a('string');
expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains');
}
- it('Returns an empty array if invalid response is passed', function () {
- serverResponses = spec.interpretResponse('invalid_response');
- expect(serverResponses).to.be.an('array').that.is.empty;
- });
+ });
+
+ it('(VIDEO) Returns an array of valid server responses if response object is valid', function () {
+ const resVideoObject = {
+ body: [ {
+ requestId: '123',
+ mediaType: 'video',
+ cpm: 0.3,
+ width: 320,
+ height: 50,
+ vastUrl: 'https://',
+ ttl: 1000,
+ creativeId: '123asd',
+ netRevenue: true,
+ currency: 'USD',
+ adomain: ['example.com'],
+ meta: {
+ advertiserDomains: ['google.com'],
+ advertiserId: 1234
+ }
+ } ]
+ };
+
+ const serverResponses = spec.interpretResponse(resVideoObject);
+
+ expect(serverResponses).to.be.an('array').that.is.not.empty;
+ for (let i = 0; i < serverResponses.length; i++) {
+ let dataItem = serverResponses[i];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastUrl', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'mediaType', 'meta', 'adomain');
+ expect(dataItem.requestId).to.be.a('string');
+ expect(dataItem.cpm).to.be.a('number');
+ expect(dataItem.width).to.be.a('number');
+ expect(dataItem.height).to.be.a('number');
+ expect(dataItem.vastUrl).to.be.a('string');
+ expect(dataItem.ttl).to.be.a('number');
+ expect(dataItem.creativeId).to.be.a('string');
+ expect(dataItem.netRevenue).to.be.a('boolean');
+ expect(dataItem.currency).to.be.a('string');
+ expect(dataItem.mediaType).to.be.a('string');
+ expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains');
+ }
+ });
+
+ it('(NATIVE) Returns an array of valid server responses if response object is valid', function () {
+ const resNativeObject = {
+ body: [ {
+ requestId: '123',
+ mediaType: 'native',
+ cpm: 0.3,
+ width: 320,
+ height: 50,
+ native: {
+ title: 'title',
+ image: 'image',
+ impressionTrackers: [ 'https://' ]
+ },
+ ttl: 1000,
+ creativeId: '123asd',
+ netRevenue: true,
+ currency: 'USD',
+ adomain: ['example.com'],
+ meta: {
+ advertiserDomains: ['google.com'],
+ advertiserId: 1234
+ }
+ } ]
+ };
+
+ const serverResponses = spec.interpretResponse(resNativeObject);
+
+ expect(serverResponses).to.be.an('array').that.is.not.empty;
+ for (let i = 0; i < serverResponses.length; i++) {
+ let dataItem = serverResponses[i];
+ expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId',
+ 'netRevenue', 'currency', 'mediaType', 'meta', 'adomain');
+ expect(dataItem.requestId).to.be.a('string');
+ expect(dataItem.cpm).to.be.a('number');
+ expect(dataItem.width).to.be.a('number');
+ expect(dataItem.height).to.be.a('number');
+ expect(dataItem.native).to.be.an('object');
+ expect(dataItem.ttl).to.be.a('number');
+ expect(dataItem.creativeId).to.be.a('string');
+ expect(dataItem.netRevenue).to.be.a('boolean');
+ expect(dataItem.currency).to.be.a('string');
+ expect(dataItem.mediaType).to.be.a('string');
+ expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains');
+ }
+ });
+
+ it('Invalid mediaType in response', function () {
+ const resBadObject = {
+ body: [ {
+ mediaType: 'other',
+ requestId: '123',
+ cpm: 0.3,
+ ttl: 1000,
+ creativeId: '123asd',
+ currency: 'USD'
+ } ]
+ };
+
+ const serverResponses = spec.interpretResponse(resBadObject);
+
+ expect(serverResponses).to.be.an('array').that.is.empty;
});
});
describe('getUserSyncs', function () {
- let userSync = spec.getUserSyncs({});
+ const gdprConsent = { consentString: 'consentString', gdprApplies: 1 };
+ const consentString = { consentString: 'consentString' }
+ let userSync = spec.getUserSyncs({}, {}, gdprConsent, consentString);
it('Returns valid URL and type', function () {
expect(userSync).to.be.an('array').with.lengthOf(1);
expect(userSync[0].type).to.exist;
expect(userSync[0].url).to.exist;
expect(userSync[0].type).to.be.equal('image');
- expect(userSync[0].url).to.be.equal('https://sync.admanmedia.com/image?pbjs=1&coppa=0');
+ expect(userSync[0].url).to.be.equal('https://sync.admanmedia.com/image?pbjs=1&gdpr=0&gdpr_consent=consentString&ccpa_consent=consentString&coppa=0');
});
});
});
diff --git a/test/spec/modules/alkimiBidAdapter_spec.js b/test/spec/modules/alkimiBidAdapter_spec.js
new file mode 100644
index 00000000000..58a5a3b54ab
--- /dev/null
+++ b/test/spec/modules/alkimiBidAdapter_spec.js
@@ -0,0 +1,164 @@
+import { expect } from 'chai'
+import { ENDPOINT, spec } from 'modules/alkimiBidAdapter.js'
+import { newBidder } from 'src/adapters/bidderFactory.js'
+
+const REQUEST = {
+ 'bidId': '456',
+ 'bidder': 'alkimi',
+ 'sizes': [[300, 250]],
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[300, 250]]
+ }
+ },
+ 'params': {
+ bidFloor: 0.1,
+ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f',
+ pos: 7
+ }
+}
+
+const BIDDER_BANNER_RESPONSE = {
+ 'prebidResponse': [{
+ 'ad': 'test
',
+ 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46d',
+ 'cpm': 900.5,
+ 'currency': 'USD',
+ 'width': 640,
+ 'height': 480,
+ 'ttl': 300,
+ 'creativeId': 1,
+ 'netRevenue': true,
+ 'winUrl': 'http://test.com',
+ 'mediaType': 'banner',
+ 'adomain': ['test.com']
+ }]
+}
+
+const BIDDER_VIDEO_RESPONSE = {
+ 'prebidResponse': [{
+ 'ad': 'vast',
+ 'requestId': 'e64782a4-8e68-4c38-965b-80ccf115d46z',
+ 'cpm': 800.4,
+ 'currency': 'USD',
+ 'width': 1024,
+ 'height': 768,
+ 'ttl': 200,
+ 'creativeId': 2,
+ 'netRevenue': true,
+ 'winUrl': 'http://test.com',
+ 'mediaType': 'video',
+ 'adomain': ['test.com']
+ }]
+}
+
+const BIDDER_NO_BID_RESPONSE = ''
+
+describe('alkimiBidAdapter', function () {
+ const adapter = newBidder(spec)
+
+ describe('inherited functions', function () {
+ it('exists and is a function', function () {
+ expect(adapter.callBids).to.exist.and.to.be.a('function')
+ })
+ })
+
+ describe('isBidRequestValid', function () {
+ it('should return true when required params found', function () {
+ expect(spec.isBidRequestValid(REQUEST)).to.equal(true)
+ })
+
+ it('should return false when required params are not passed', function () {
+ let bid = Object.assign({}, REQUEST)
+ delete bid.params.token
+ expect(spec.isBidRequestValid(bid)).to.equal(false)
+
+ bid = Object.assign({}, REQUEST)
+ delete bid.params.bidFloor
+ expect(spec.isBidRequestValid(bid)).to.equal(false)
+
+ bid = Object.assign({}, REQUEST)
+ delete bid.params
+ expect(spec.isBidRequestValid(bid)).to.equal(false)
+ })
+ })
+
+ describe('buildRequests', function () {
+ let bidRequests = [REQUEST]
+ const bidderRequest = spec.buildRequests(bidRequests, {
+ auctionId: '123',
+ refererInfo: {
+ referer: 'http://test.com/path.html'
+ }
+ })
+
+ it('sends bid request to ENDPOINT via POST', function () {
+ expect(bidderRequest.method).to.equal('POST')
+ expect(bidderRequest.data.requestId).to.equal('123')
+ expect(bidderRequest.data.referer).to.equal('http://test.com/path.html')
+ expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, bidFloor: 0.1, width: 300, height: 250, impMediaType: 'Banner' })
+ expect(bidderRequest.data.signRequest.randomUUID).to.equal(undefined)
+ expect(bidderRequest.data.bidIds).to.deep.contains('456')
+ expect(bidderRequest.data.signature).to.equal(undefined)
+ expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true })
+ expect(bidderRequest.options.contentType).to.equal('application/json')
+ expect(bidderRequest.url).to.equal(ENDPOINT)
+ })
+ })
+
+ describe('interpretResponse', function () {
+ it('handles banner request : should get correct bid response', function () {
+ const result = spec.interpretResponse({ body: BIDDER_BANNER_RESPONSE }, {})
+
+ expect(result[0]).to.have.property('ad').equal('test
')
+ expect(result[0]).to.have.property('requestId').equal('e64782a4-8e68-4c38-965b-80ccf115d46d')
+ expect(result[0]).to.have.property('cpm').equal(900.5)
+ expect(result[0]).to.have.property('currency').equal('USD')
+ expect(result[0]).to.have.property('width').equal(640)
+ expect(result[0]).to.have.property('height').equal(480)
+ expect(result[0]).to.have.property('ttl').equal(300)
+ expect(result[0]).to.have.property('creativeId').equal(1)
+ expect(result[0]).to.have.property('netRevenue').equal(true)
+ expect(result[0]).to.have.property('winUrl').equal('http://test.com')
+ expect(result[0]).to.have.property('mediaType').equal('banner')
+ expect(result[0].meta).to.exist.property('advertiserDomains')
+ expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1)
+ })
+
+ it('handles video request : should get correct bid response', function () {
+ const result = spec.interpretResponse({ body: BIDDER_VIDEO_RESPONSE }, {})
+
+ expect(result[0]).to.have.property('ad').equal('vast')
+ expect(result[0]).to.have.property('requestId').equal('e64782a4-8e68-4c38-965b-80ccf115d46z')
+ expect(result[0]).to.have.property('cpm').equal(800.4)
+ expect(result[0]).to.have.property('currency').equal('USD')
+ expect(result[0]).to.have.property('width').equal(1024)
+ expect(result[0]).to.have.property('height').equal(768)
+ expect(result[0]).to.have.property('ttl').equal(200)
+ expect(result[0]).to.have.property('creativeId').equal(2)
+ expect(result[0]).to.have.property('netRevenue').equal(true)
+ expect(result[0]).to.have.property('winUrl').equal('http://test.com')
+ expect(result[0]).to.have.property('mediaType').equal('video')
+ expect(result[0]).to.have.property('vastXml').equal('vast')
+ expect(result[0].meta).to.exist.property('advertiserDomains')
+ expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1)
+ })
+
+ it('handles no bid response : should get empty array', function () {
+ let result = spec.interpretResponse({ body: undefined }, {})
+ expect(result).to.deep.equal([])
+
+ result = spec.interpretResponse({ body: BIDDER_NO_BID_RESPONSE }, {})
+ expect(result).to.deep.equal([])
+ })
+ })
+
+ describe('onBidWon', function () {
+ it('handles banner win: should get true', function () {
+ const win = BIDDER_BANNER_RESPONSE.prebidResponse[0]
+ const bidWonResult = spec.onBidWon(win)
+
+ expect(bidWonResult).to.equal(true)
+ })
+ })
+})
diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js
new file mode 100644
index 00000000000..1a322d131a9
--- /dev/null
+++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js
@@ -0,0 +1,288 @@
+import bidwatchAnalytics from 'modules/bidwatchAnalyticsAdapter.js';
+import { expect } from 'chai';
+import { server } from 'test/mocks/xhr.js';
+let adapterManager = require('src/adapterManager').default;
+let events = require('src/events');
+let constants = require('src/constants.json');
+
+describe('BidWatch Analytics', function () {
+ let timestamp = new Date() - 256;
+ let auctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f';
+ let timeout = 1500;
+
+ let bidTimeout = [
+ {
+ 'bidId': '5fe418f2d70364',
+ 'bidder': 'appnexusAst',
+ 'adUnitCode': 'tag_200124_banner',
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b'
+ }
+ ];
+
+ const auctionEnd = {
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b',
+ 'timestamp': 1647424261187,
+ 'auctionEnd': 1647424261714,
+ 'auctionStatus': 'completed',
+ 'adUnits': [
+ {
+ 'code': 'tag_200124_banner',
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [
+ [
+ 300,
+ 600
+ ]
+ ]
+ }
+ },
+ 'bids': [
+ {
+ 'bidder': 'appnexus',
+ 'params': {
+ 'placementId': 123456
+ }
+ },
+ {
+ 'bidder': 'appnexusAst',
+ 'params': {
+ 'placementId': 234567
+ }
+ }
+ ],
+ 'sizes': [
+ [
+ 300,
+ 600
+ ]
+ ],
+ 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40'
+ }
+ ],
+ 'adUnitCodes': [
+ 'tag_200124_banner'
+ ],
+ 'bidderRequests': [
+ {
+ 'bidderCode': 'appnexus',
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b',
+ 'bidderRequestId': '11dc6ff6378de7',
+ 'bids': [
+ {
+ 'bidder': 'appnexus',
+ 'params': {
+ 'placementId': 123456
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [
+ [
+ 300,
+ 600
+ ]
+ ]
+ }
+ },
+ 'adUnitCode': 'tag_200124_banner',
+ 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40',
+ 'sizes': [
+ [
+ 300,
+ 600
+ ]
+ ],
+ 'bidId': '34a63e5d5378a3',
+ 'bidderRequestId': '11dc6ff6378de7',
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b',
+ 'src': 'client',
+ 'bidRequestsCount': 1,
+ 'bidderRequestsCount': 1,
+ 'bidderWinsCount': 0
+ }
+ ],
+ 'auctionStart': 1647424261187,
+ 'timeout': 1000,
+ 'gdprConsent': {
+ 'consentString': 'CONSENT',
+ 'gdprApplies': true,
+ 'apiVersion': 2
+ },
+ 'start': 1647424261189
+ },
+ ],
+ 'noBids': [
+ {
+ 'bidder': 'appnexusAst',
+ 'params': {
+ 'placementId': 10471298
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [
+ [
+ 300,
+ 600
+ ]
+ ]
+ }
+ },
+ 'adUnitCode': 'tag_200124_banner',
+ 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40',
+ 'sizes': [
+ [
+ 300,
+ 600
+ ]
+ ],
+ 'bidId': '5fe418f2d70364',
+ 'bidderRequestId': '4229a45ab8ea87',
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b',
+ 'src': 'client',
+ 'bidRequestsCount': 1,
+ 'bidderRequestsCount': 1,
+ 'bidderWinsCount': 0
+ }
+ ],
+ 'bidsReceived': [
+ {
+ 'bidderCode': 'appnexus',
+ 'width': 300,
+ 'height': 600,
+ 'statusMessage': 'Bid available',
+ 'adId': '7a4ced80f33d33',
+ 'requestId': '34a63e5d5378a3',
+ 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40',
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b',
+ 'mediaType': 'banner',
+ 'source': 'client',
+ 'cpm': 27.4276,
+ 'creativeId': '158534630',
+ 'currency': 'USD',
+ 'netRevenue': true,
+ 'ttl': 2000,
+ 'ad': 'some html',
+ 'meta': {
+ 'advertiserDomains': [
+ 'example.com'
+ ]
+ },
+ 'originalCpm': 25.02521,
+ 'originalCurrency': 'EUR',
+ 'responseTimestamp': 1647424261559,
+ 'requestTimestamp': 1647424261189,
+ 'bidder': 'appnexus',
+ 'adUnitCode': 'tag_200124_banner',
+ 'timeToRespond': 370,
+ 'pbLg': '5.00',
+ 'pbMg': '20.00',
+ 'pbHg': '20.00',
+ 'pbAg': '20.00',
+ 'pbDg': '20.00',
+ 'pbCg': '20.000000',
+ 'size': '300x600',
+ 'adserverTargeting': {
+ 'hb_bidder': 'appnexus',
+ 'hb_adid': '7a4ced80f33d33',
+ 'hb_pb': '20.000000',
+ 'hb_size': '300x600',
+ 'hb_source': 'client',
+ 'hb_format': 'banner',
+ 'hb_adomain': 'example.com'
+ }
+ }
+ ],
+ 'winningBids': [
+
+ ],
+ 'timeout': 1000
+ };
+
+ let bidWon = {
+ 'bidderCode': 'appnexus',
+ 'width': 970,
+ 'height': 250,
+ 'statusMessage': 'Bid available',
+ 'adId': '65d16ef039a97a',
+ 'requestId': '2bd3e8ff8a113f',
+ 'transactionId': '8b2a8629-d1ea-4bb1-aff0-e335b96dd002',
+ 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b',
+ 'mediaType': 'banner',
+ 'source': 'client',
+ 'cpm': 27.4276,
+ 'creativeId': '158533702',
+ 'currency': 'USD',
+ 'netRevenue': true,
+ 'ttl': 2000,
+ 'ad': 'some html',
+ 'meta': {
+ 'advertiserDomains': [
+ 'example.com'
+ ]
+ },
+ 'originalCpm': 25.02521,
+ 'originalCurrency': 'EUR',
+ 'responseTimestamp': 1647424261558,
+ 'requestTimestamp': 1647424261189,
+ 'bidder': 'appnexus',
+ 'adUnitCode': 'tag_200123_banner',
+ 'timeToRespond': 369,
+ 'originalBidder': 'appnexus',
+ 'pbLg': '5.00',
+ 'pbMg': '20.00',
+ 'pbHg': '20.00',
+ 'pbAg': '20.00',
+ 'pbDg': '20.00',
+ 'pbCg': '20.000000',
+ 'size': '970x250',
+ 'adserverTargeting': {
+ 'hb_bidder': 'appnexus',
+ 'hb_adid': '65d16ef039a97a',
+ 'hb_pb': '20.000000',
+ 'hb_size': '970x250',
+ 'hb_source': 'client',
+ 'hb_format': 'banner',
+ 'hb_adomain': 'example.com'
+ },
+ 'status': 'rendered',
+ 'params': [
+ {
+ 'placementId': 123456
+ }
+ ]
+ };
+
+ after(function () {
+ bidwatchAnalytics.disableAnalytics();
+ });
+
+ describe('main test flow', function () {
+ beforeEach(function () {
+ sinon.stub(events, 'getEvents').returns([]);
+ });
+
+ afterEach(function () {
+ events.getEvents.restore();
+ });
+
+ it('should catch events of interest', function () {
+ sinon.spy(bidwatchAnalytics, 'track');
+
+ adapterManager.registerAnalyticsAdapter({
+ code: 'bidwatch',
+ adapter: bidwatchAnalytics
+ });
+
+ adapterManager.enableAnalytics({
+ provider: 'bidwatch',
+ options: {
+ domain: 'test'
+ }
+ });
+ events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout);
+ events.emit(constants.EVENTS.AUCTION_END, auctionEnd);
+ events.emit(constants.EVENTS.BID_WON, bidWon);
+ sinon.assert.callCount(bidwatchAnalytics.track, 3);
+ });
+ });
+});
diff --git a/test/spec/modules/cpexIdSystem_spec.js b/test/spec/modules/cpexIdSystem_spec.js
new file mode 100644
index 00000000000..ee203e6c83a
--- /dev/null
+++ b/test/spec/modules/cpexIdSystem_spec.js
@@ -0,0 +1,38 @@
+import { cpexIdSubmodule, storage } from 'modules/cpexIdSystem.js';
+
+describe('cpexId module', function () {
+ let getCookieStub;
+
+ beforeEach(function (done) {
+ getCookieStub = sinon.stub(storage, 'getCookie');
+ done();
+ });
+
+ afterEach(function () {
+ getCookieStub.restore();
+ });
+
+ const cookieTestCasesForEmpty = [undefined, null, '']
+
+ describe('getId()', function () {
+ it('should return the uid when it exists in cookie', function () {
+ getCookieStub.withArgs('caid').returns('cpexIdTest');
+ const id = cpexIdSubmodule.getId();
+ expect(id).to.be.deep.equal({ cpexId: 'cpexIdTest' });
+ });
+
+ cookieTestCasesForEmpty.forEach(testCase => it('should not return the uid when it doesnt exist in cookie', function () {
+ getCookieStub.withArgs('caid').returns(testCase);
+ const id = cpexIdSubmodule.getId();
+ expect(id).to.be.deep.equal({ cpexId: null });
+ }));
+ });
+
+ describe('decode()', function () {
+ it('should return the uid when it exists in cookie', function () {
+ getCookieStub.withArgs('caid').returns('cpexIdTest');
+ const decoded = cpexIdSubmodule.decode();
+ expect(decoded).to.be.deep.equal({ cpexId: 'cpexIdTest' });
+ });
+ });
+});
diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js
index 74c2b053ce1..a54542f7278 100644
--- a/test/spec/modules/id5IdSystem_spec.js
+++ b/test/spec/modules/id5IdSystem_spec.js
@@ -5,7 +5,7 @@ import {
ID5_PRIVACY_STORAGE_NAME,
ID5_STORAGE_NAME,
id5IdSubmodule,
- nbCacheName,
+ nbCacheName, storage,
storeInLocalStorage,
storeNbInCache,
} from 'modules/id5IdSystem.js';
@@ -310,6 +310,21 @@ describe('ID5 ID System', function() {
request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE));
expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).to.be.null;
});
+
+ describe('when legacy cookies are set', () => {
+ let sandbox;
+ beforeEach(() => {
+ sandbox = sinon.sandbox.create();
+ sandbox.stub(storage, 'getCookie');
+ });
+ afterEach(() => {
+ sandbox.restore();
+ });
+ it('should not throw if malformed JSON is forced into cookies', () => {
+ storage.getCookie.callsFake(() => ' Not JSON ');
+ id5IdSubmodule.getId(getId5FetchConfig());
+ });
+ })
});
describe('Request Bids Hook', function() {
diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js
index 8338d869fde..be4a99e8f30 100644
--- a/test/spec/modules/improvedigitalBidAdapter_spec.js
+++ b/test/spec/modules/improvedigitalBidAdapter_spec.js
@@ -2,7 +2,7 @@ import { expect } from 'chai';
import { spec } from 'modules/improvedigitalBidAdapter.js';
import { config } from 'src/config.js';
import { deepClone } from 'src/utils.js';
-import {BANNER, VIDEO} from '../../../src/mediaTypes';
+import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes';
describe('Improve Digital Adapter Tests', function () {
const METHOD = 'POST';
@@ -50,17 +50,31 @@ describe('Improve Digital Adapter Tests', function () {
}
};
+ const nativeBidRequest = deepClone(simpleBidRequest);
+ nativeBidRequest.mediaTypes = { native: {} };
+ nativeBidRequest.nativeParams = {
+ title: {required: true},
+ body: {required: true}
+ };
+
const multiFormatBidRequest = deepClone(simpleBidRequest);
multiFormatBidRequest.mediaTypes = {
banner: {
sizes: [[300, 250], [160, 600]]
},
+ native: {},
video: {
context: 'outstream',
playerSize: [640, 480]
}
};
+ multiFormatBidRequest.nativeParams = {
+ body: {
+ required: true
+ }
+ };
+
const simpleSmartTagBidRequest = {
bidder: 'improvedigital',
bidId: '1a2b3c',
@@ -87,6 +101,10 @@ describe('Improve Digital Adapter Tests', function () {
bids: [multiFormatBidRequest]
};
+ const nativeBidderRequest = {
+ bids: [nativeBidRequest]
+ };
+
const bidderRequestGdpr = {
bids: [simpleBidRequest],
gdprConsent: {
@@ -197,6 +215,10 @@ describe('Improve Digital Adapter Tests', function () {
expect(payload.imp).to.deep.equal([
{
id: '33e9500b21129f',
+ native: {
+ request: '{"assets":[{"id":3,"required":1,"data":{"type":2}}]}',
+ ver: '1.2'
+ },
secure: 0,
ext: {
bidder: {
@@ -220,6 +242,28 @@ describe('Improve Digital Adapter Tests', function () {
getConfigStub.restore();
});
+ it('should make a well-formed native request', function () {
+ const payload = JSON.parse(spec.buildRequests([nativeBidRequest], {})[0].data);
+ expect(payload.imp[0].native).to.deep.equal({
+ ver: '1.2',
+ request: '{\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":140}},{\"id\":3,\"required\":1,\"data\":{\"type\":2}}]}'
+ });
+ });
+
+ it('should not make native request when nativeParams is undefined', function () {
+ const request = deepClone(nativeBidRequest);
+ delete request.nativeParams;
+ const payload = JSON.parse(spec.buildRequests([request], {})[0].data);
+ expect(payload.imp[0].native).to.not.exist;
+ });
+
+ it('should not make native request when no assets', function () {
+ const request = deepClone(nativeBidRequest);
+ request.nativeParams = {};
+ const payload = JSON.parse(spec.buildRequests([request], {})[0].data);
+ expect(payload.imp[0].native).to.not.exist;
+ });
+
it('should set placementKey and publisherId for smart tags', function () {
const payload = JSON.parse(spec.buildRequests([simpleSmartTagBidRequest], bidderRequest)[0].data);
expect(payload.imp[0].ext.bidder.publisherId).to.equal(1032);
@@ -238,20 +282,6 @@ describe('Improve Digital Adapter Tests', function () {
expect(payload.imp[0].ext.bidder.keyValues).to.deep.equal(keyValues);
});
- // it('should add single size filter', function () {
- // const bidRequest = Object.assign({}, simpleBidRequest);
- // const size = {
- // w: 800,
- // h: 600
- // };
- // bidRequest.params.size = size;
- // const payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequest).data);
- // expect(payload.imp[0].banner).to.deep.equal(size);
- // // When single size filter is set, format shouldn't be populated. This
- // // is to maintain backward compatibily
- // expect(payload.imp[0].banner.format).to.not.exist;
- // });
-
it('should add currency', function () {
const bidRequest = Object.assign({}, simpleBidRequest);
const getConfigStub = sinon.stub(config, 'getConfig').returns('JPY');
@@ -786,7 +816,7 @@ describe('Improve Digital Adapter Tests', function () {
'id': '52098fad-20c1-476b-a4fa-41e275e5a4a5',
'price': 1.8600000000000003,
'adm': "{\"ver\":\"1.1\",\"imptrackers\":[\"https://secure.adnxs.com/imptr?id=52311&t=2\",\"https://euw-ice.360yield.com/imp_pixel?ic=hcUBlCANx1FabHBf6FR2gC7UO4xEyXahdZAn0-B5qL-bb3A74BJ1smyWIyW7IWcC0SOjSXzVpevTHXxTqJ.sf.Qhahyy6tSo.0j1QWfXlH8sM4-8vKWjMjw-x.IrJJNlwkQ0s1CdwcwTefcLXm5l2E-W19VhACuV7f3mgrZMNjiSw.SjJAfyPC3SIyAMRjYfj53UmjriQ46T7lhmkqxK8wHmksYCdbZc3PZESk8NWl28sxdjNvnYYCFMcJbeav.LOLabyTXfwy-1cEPbQs.IKMRZIKaqccTDPV3wOtzbNv0jQzatd3Nnv-PGFQcjQ-GW3i27W04Fws4kodpFSn-B6VwZAjzLzoyd5gBncyRnAyCplEbgHU5sZ1IyKHWjgCl3ZtRIK5vqrRD5D-xqgSnOi7-phG.CqZWDZ4bMDSfQg2ZnbvUTyGKcEl0WR59dW5izTMV4Fjizcrvr5T-t.zMbGwz.hGnmLIyhTqh.IcwW.GiDLVExlDlix5S1LXIWVsSyrQ==\"],\"assets\":[{\"id\":1,\"data\":{\"value\":\"ImproveDigital\",\"type\":1}},{\"id\":3,\"data\":{\"value\":\"Test content.\",\"type\":2}},{\"id\":0,\"title\":{\"text\":\"Sample Prebid Test Title\"}}],\"link\":{\"url\":\"https://euw-ice.360yield.com/click/hcUBlHOV7YhVse8RyBa0ajjyPa9Vt17e4g-1m3cRj3E67vq-RYux.SiUeAmBfNBcoOqkUc6A15AWmi4yFu5K-BdkaYjildyyk7fNLyR6hWr411kv4vrFwm5jrIBceuHS6K8oN69f.uCo8zGTdR2TbSlldwcpahQPlufZU.6VaMsu4IC53uEiUT5vb7kAw6TTlxuGBNq6zaGryiWEV2.N3YYJDTyYPh8tv-ZFyeFZFm0Gnjv.xWbC.70JcRUVU9UelQaPsTpTWYTXBhJt84YJUw1-GNtaLNVLSjjZbVoA2fsMti5p6OBmF.7u39on2OPgvseIkSmge7Pqg63pRqdP75hp.DAEk6OkcN1jGnwP2DSbvpaSbin5lVqjfO0B-wnQgfQTCUtM5v4JmkNweLhUf9Q-x.nPKLW5SccEk9ZFXzY2-1wpT3PWm8Tix3NRscLPZub9wHzL..pl6ip8cQ9hp16UjwT4H6RMAxL0R7bl-h2pAicGAzYmuO7ntRESKUoIWA==//http%3A%2F%2Fquantum-advertising.com%2Ffr%2F\"},\"jstracker\":\"\"}",
- 'impid': '2d7a7db325c6f',
+ 'impid': '33e9500b21129f',
'cid': '196108'
}
],
@@ -1039,10 +1069,6 @@ describe('Improve Digital Adapter Tests', function () {
//
// Native ads
it('should return a well-formed native ad bid', function () {
- const nativeBidderRequest = JSON.parse(JSON.stringify(bidderRequest));
- nativeBidderRequest.bids[0].bidId = '2d7a7db325c6f';
- delete nativeBidderRequest.bids[0].mediaTypes.banner;
- nativeBidderRequest.bids[0].mediaTypes.native = {};
const bids = spec.interpretResponse(serverResponseNative, {bidderRequest: nativeBidderRequest});
// Verify Native Response
expect(bids[0].native).to.exist;
@@ -1059,6 +1085,11 @@ describe('Improve Digital Adapter Tests', function () {
expect(nativeBid.body).to.exist.and.equal('Test content.');
});
+ it('should return a well-formed native bid for multi-format ad unit', function () {
+ const bids = spec.interpretResponse(serverResponseNative, {bidderRequest: multiFormatBidderRequest});
+ expect(bids[0].mediaType).to.equal(NATIVE);
+ });
+
// Video
it('should return a well-formed instream video bid', function () {
const bids = spec.interpretResponse(serverResponseVideo, {bidderRequest: instreamBidderRequest});
diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js
index 7af0372c22c..0a6ba272970 100644
--- a/test/spec/modules/jixieBidAdapter_spec.js
+++ b/test/spec/modules/jixieBidAdapter_spec.js
@@ -268,6 +268,23 @@ describe('jixie Adapter', function () {
expect(payload.pricegranularity).to.deep.include(content);
});
+ it('it should popular the device info when it is available', function () {
+ let getConfigStub = sinon.stub(config, 'getConfig');
+ let content = {w: 500, h: 400};
+ getConfigStub.callsFake(function fakeFn(prop) {
+ if (prop == 'device') {
+ return content;
+ }
+ return null;
+ });
+ const oneSpecialBidReq = Object.assign({}, bidRequests_[0]);
+ const request = spec.buildRequests([oneSpecialBidReq], bidderRequest_);
+ const payload = JSON.parse(request.data);
+ getConfigStub.restore();
+ expect(payload.device).to.have.property('ua', navigator.userAgent);
+ expect(payload.device).to.deep.include(content);
+ });
+
it('should populate eids when supported userIds are available', function () {
const oneSpecialBidReq = Object.assign({}, bidRequests_[0], {
userId: {
diff --git a/test/spec/modules/lkqdBidAdapter_spec.js b/test/spec/modules/lkqdBidAdapter_spec.js
index 6e2c7fe8e2c..7fee9bf6e41 100644
--- a/test/spec/modules/lkqdBidAdapter_spec.js
+++ b/test/spec/modules/lkqdBidAdapter_spec.js
@@ -226,7 +226,6 @@ describe('lkqdBidAdapter', () => {
'utcoffset': -420,
},
'dnt': 0,
- 'ip': '184.103.177.205',
'ifa': 'f4254ada-4174-6cfd-83c7-2f999c457c1d'
},
'user': {
diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js
index 3091c0fb561..388d10b1453 100644
--- a/test/spec/modules/sovrnBidAdapter_spec.js
+++ b/test/spec/modules/sovrnBidAdapter_spec.js
@@ -391,9 +391,29 @@ describe('sovrnBidAdapter', function() {
'currency': 'USD',
'netRevenue': true,
'mediaType': 'banner',
- 'ad': decodeURIComponent(``),
'ttl': 90,
- 'meta': { advertiserDomains: [] }
+ 'meta': { advertiserDomains: [] },
+ 'ad': decodeURIComponent(``),
+ }
+ const videoBid = {
+ 'id': 'a_403370_332fdb9b064040ddbec05891bd13ab28',
+ 'crid': 'creativelycreatedcreativecreative',
+ 'impid': '263c448586f5a1',
+ 'price': 0.45882675,
+ 'nurl': '',
+ 'adm': 'key%3Dvalue',
+ 'h': 480,
+ 'w': 640
+ }
+ const bannerBid = {
+ 'id': 'a_403370_332fdb9b064040ddbec05891bd13ab28',
+ 'crid': 'creativelycreatedcreativecreative',
+ 'impid': '263c448586f5a1',
+ 'price': 0.45882675,
+ 'nurl': '',
+ 'adm': '',
+ 'h': 90,
+ 'w': 728
}
beforeEach(function () {
response = {
@@ -401,14 +421,7 @@ describe('sovrnBidAdapter', function() {
'id': '37386aade21a71',
'seatbid': [{
'bid': [{
- 'id': 'a_403370_332fdb9b064040ddbec05891bd13ab28',
- 'crid': 'creativelycreatedcreativecreative',
- 'impid': '263c448586f5a1',
- 'price': 0.45882675,
- 'nurl': '',
- 'adm': '',
- 'h': 90,
- 'w': 728
+ ...bannerBid
}]
}]
}
@@ -418,7 +431,6 @@ describe('sovrnBidAdapter', function() {
it('should get the correct bid response', function () {
const expectedResponse = {
...baseResponse,
- 'ad': decodeURIComponent(`>`),
'ttl': 60000,
};
@@ -478,6 +490,36 @@ describe('sovrnBidAdapter', function() {
expect(result.length).to.equal(0);
});
+
+ it('should get the correct bid response with 2 different bids', function () {
+ const expectedVideoResponse = {
+ ...baseResponse,
+ 'vastXml': decodeURIComponent(videoBid.adm)
+ }
+ delete expectedVideoResponse.ad
+
+ const expectedBannerResponse = {
+ ...baseResponse
+ }
+
+ response.body.seatbid = [{ bid: [bannerBid] }, { bid: [videoBid] }]
+ const result = spec.interpretResponse(response)
+
+ expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedBannerResponse))
+ expect(Object.keys(result[1])).to.deep.equal(Object.keys(expectedVideoResponse))
+ })
+
+ it('should get the correct bid response with 2 seatbid items', function () {
+ const expectedResponse = {
+ ...baseResponse
+ }
+ response.body.seatbid = [response.body.seatbid[0], response.body.seatbid[0]]
+
+ const result = spec.interpretResponse(response)
+
+ expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse))
+ expect(Object.keys(result[1])).to.deep.equal(Object.keys(expectedResponse))
+ })
});
describe('interpretResponse video', function () {
diff --git a/test/spec/modules/talkadsBidAdapter_spec.js b/test/spec/modules/talkadsBidAdapter_spec.js
index 00f52ba7b6a..c48808cbc15 100644
--- a/test/spec/modules/talkadsBidAdapter_spec.js
+++ b/test/spec/modules/talkadsBidAdapter_spec.js
@@ -207,6 +207,7 @@ describe('TalkAds adapter', function () {
ttl: 60,
creativeId: 'c123a456',
netRevenue: false,
+ params: [Object.assign({}, commonBidRequest.params)],
}
spec.onBidWon(loBid)
expect(server.requests.length).to.equals(0);
@@ -222,7 +223,8 @@ describe('TalkAds adapter', function () {
ttl: 60,
creativeId: 'c123a456',
netRevenue: false,
- pbid: '6147833a65749742875ace47'
+ pbid: '6147833a65749742875ace47',
+ params: [Object.assign({}, commonBidRequest.params)],
}
spec.onBidWon(loBid)
expect(server.requests[0].url).to.equals('https://test.natexo-programmatic.com/tad/tag/prebidwon/6147833a65749742875ace47');
diff --git a/test/spec/modules/tpmnBidAdapter_spec.js b/test/spec/modules/tpmnBidAdapter_spec.js
index b4f6882dbe1..468769c2573 100644
--- a/test/spec/modules/tpmnBidAdapter_spec.js
+++ b/test/spec/modules/tpmnBidAdapter_spec.js
@@ -1,9 +1,39 @@
/* eslint-disable no-tabs */
-import { expect } from 'chai';
-import { spec } from 'modules/tpmnBidAdapter.js';
+import {expect} from 'chai';
+import {spec, storage} from 'modules/tpmnBidAdapter.js';
+import {generateUUID} from '../../../src/utils.js';
+import {newBidder} from '../../../src/adapters/bidderFactory';
+import * as sinon from 'sinon';
-describe('tpmnAdapterTests', function() {
- describe('isBidRequestValid', function() {
+describe('tpmnAdapterTests', function () {
+ const adapter = newBidder(spec);
+ const BIDDER_CODE = 'tpmn';
+ let sandbox = sinon.sandbox.create();
+ let getCookieStub;
+
+ beforeEach(function () {
+ $$PREBID_GLOBAL$$.bidderSettings = {
+ tpmn: {
+ storageAllowed: true
+ }
+ };
+ sandbox = sinon.sandbox.create();
+ getCookieStub = sinon.stub(storage, 'getCookie');
+ });
+
+ afterEach(function () {
+ sandbox.restore();
+ getCookieStub.restore();
+ $$PREBID_GLOBAL$$.bidderSettings = {};
+ });
+
+ describe('inherited functions', function () {
+ it('exists and is a function', function () {
+ expect(adapter.callBids).to.exist.and.to.be.a('function')
+ })
+ });
+
+ describe('isBidRequestValid', function () {
let bid = {
adUnitCode: 'temp-unitcode',
bidder: 'tpmn',
@@ -20,17 +50,18 @@ describe('tpmnAdapterTests', function() {
}
}
};
- it('should return true if a bid is valid banner bid request', function() {
+
+ it('should return true if a bid is valid banner bid request', function () {
expect(spec.isBidRequestValid(bid)).to.be.equal(true);
});
- it('should return false where requried param is missing', function() {
+ it('should return false where requried param is missing', function () {
let bid = Object.assign({}, bid);
bid.params = {};
expect(spec.isBidRequestValid(bid)).to.be.equal(false);
});
- it('should return false when required param values have invalid type', function() {
+ it('should return false when required param values have invalid type', function () {
let bid = Object.assign({}, bid);
bid.params = {
'inventoryId': null,
@@ -40,14 +71,14 @@ describe('tpmnAdapterTests', function() {
});
});
- describe('buildRequests', function() {
- it('should return an empty list if there are no bid requests', function() {
+ describe('buildRequests', function () {
+ it('should return an empty list if there are no bid requests', function () {
const emptyBidRequests = [];
const bidderRequest = {};
const request = spec.buildRequests(emptyBidRequests, bidderRequest);
expect(request).to.be.an('array').that.is.empty;
});
- it('should generate a POST server request with bidder API url, data', function() {
+ it('should generate a POST server request with bidder API url, data', function () {
const bid = {
adUnitCode: 'temp-unitcode',
bidder: 'tpmn',
@@ -65,13 +96,15 @@ describe('tpmnAdapterTests', function() {
}
};
const tempBidRequests = [bid];
- const tempBidderRequest = {refererInfo: {
- referer: 'http://localhost/test',
- site: {
- domain: 'localhost',
- page: 'http://localhost/test'
+ const tempBidderRequest = {
+ refererInfo: {
+ referer: 'http://localhost/test',
+ site: {
+ domain: 'localhost',
+ page: 'http://localhost/test'
+ }
}
- }};
+ };
const builtRequest = spec.buildRequests(tempBidRequests, tempBidderRequest);
expect(builtRequest).to.have.lengthOf(1);
@@ -96,7 +129,7 @@ describe('tpmnAdapterTests', function() {
});
});
- describe('interpretResponse', function() {
+ describe('interpretResponse', function () {
const bid = {
adUnitCode: 'temp-unitcode',
bidder: 'tpmn',
@@ -115,12 +148,12 @@ describe('tpmnAdapterTests', function() {
};
const tempBidRequests = [bid];
- it('should return an empty aray to indicate no valid bids', function() {
+ it('should return an empty aray to indicate no valid bids', function () {
const emptyServerResponse = {};
const bidResponses = spec.interpretResponse(emptyServerResponse, tempBidRequests);
expect(bidResponses).is.an('array').that.is.empty;
});
- it('should return an empty array to indicate no valid bids', function() {
+ it('should return an empty array to indicate no valid bids', function () {
const mockBidResult = {
requestId: '9cf19229-34f6-4d06-bc1d-0e44e8d616c8',
cpm: 10.0,
@@ -141,4 +174,36 @@ describe('tpmnAdapterTests', function() {
expect(bidResponses).deep.equal([mockBidResult]);
});
});
+
+ describe('getUserSync', function () {
+ const KEY_ID = 'uuid';
+ const TMP_UUID = generateUUID().replace(/-/g, '');
+
+ it('getCookie mock Test', () => {
+ const uuid = storage.getCookie(KEY_ID);
+ expect(uuid).to.equal(undefined);
+ });
+
+ it('getCookie mock Test', () => {
+ expect(TMP_UUID.length).to.equal(32);
+ getCookieStub.withArgs(KEY_ID).returns(TMP_UUID);
+ const uuid = storage.getCookie(KEY_ID);
+ expect(uuid).to.equal(TMP_UUID);
+ });
+
+ it('case 1 -> allow iframe', () => {
+ const syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true});
+ expect(syncs.length).to.equal(1);
+ expect(syncs[0].type).to.equal('iframe');
+ });
+
+ it('case 2 -> allow pixel with static sync', () => {
+ const syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true });
+ expect(syncs.length).to.be.equal(4);
+ expect(syncs[0].type).to.be.equal('image');
+ expect(syncs[1].type).to.be.equal('image');
+ expect(syncs[2].type).to.be.equal('image');
+ expect(syncs[3].type).to.be.equal('image');
+ });
+ });
});
diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js
index 5486f92f2ae..5c3ad3960a0 100644
--- a/test/spec/modules/userId_spec.js
+++ b/test/spec/modules/userId_spec.js
@@ -505,6 +505,57 @@ describe('User ID', function () {
});
});
+ describe('when ID systems throw errors', () => {
+ function mockIdSystem(name) {
+ return {
+ name,
+ decode: function(value) {
+ return {
+ [name]: value
+ };
+ },
+ getId: sinon.stub().callsFake(() => ({id: name}))
+ };
+ }
+ let id1, id2;
+ beforeEach(() => {
+ id1 = mockIdSystem('mock1');
+ id2 = mockIdSystem('mock2');
+ init(config);
+ setSubmoduleRegistry([id1, id2]);
+ config.setConfig({
+ userSync: {
+ auctionDelay: 10,
+ userIds: [{
+ name: 'mock1',
+ storage: {name: 'mock1', type: 'cookie'}
+ }, {
+ name: 'mock2',
+ storage: {name: 'mock2', type: 'cookie'}
+ }]
+ }
+ })
+ });
+ afterEach(() => {
+ config.resetConfig();
+ })
+ Object.entries({
+ 'in init': () => id1.getId.callsFake(() => { throw new Error() }),
+ 'in callback': () => {
+ const mockCallback = sinon.stub().callsFake(() => { throw new Error() });
+ id1.getId.callsFake(() => ({callback: mockCallback}))
+ }
+ }).forEach(([t, setup]) => {
+ describe(`${t}`, () => {
+ beforeEach(setup);
+ it('should still retrieve IDs that do not throw', () => {
+ return getGlobal().getUserIdsAsync().then((uid) => {
+ expect(uid.mock2).to.not.be.undefined;
+ })
+ });
+ })
+ })
+ });
it('pbjs.refreshUserIds updates submodules', function(done) {
let sandbox = sinon.createSandbox();
let mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}});