Enriched ORTB2 device data
diff --git a/integrationExamples/gpt/rewardedInterestIdSystem_example.html b/integrationExamples/gpt/rewardedInterestIdSystem_example.html
new file mode 100644
index 00000000000..c9730f354b3
--- /dev/null
+++ b/integrationExamples/gpt/rewardedInterestIdSystem_example.html
@@ -0,0 +1,114 @@
+
+
+
+
+
Rewarded Interest ID Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Rewarded Interest ID Example
+
+
Generated IDs:
+
+
+
Generated EIDs
+
+
+
+
diff --git a/libraries/advangUtils/index.js b/libraries/advangUtils/index.js
index 08c8e43cea3..f815f389ed6 100644
--- a/libraries/advangUtils/index.js
+++ b/libraries/advangUtils/index.js
@@ -14,7 +14,7 @@ export function isVideoBid(bid) {
export function getBannerBidFloor(bid) {
let floorInfo = isFn(bid.getFloor) ? bid.getFloor({ currency: 'USD', mediaType: 'banner', size: '*' }) : {};
- return floorInfo.floor || getBannerBidParam(bid, 'bidfloor');
+ return floorInfo?.floor || getBannerBidParam(bid, 'bidfloor');
}
export function getVideoBidFloor(bid) {
diff --git a/libraries/appnexusUtils/anKeywords.js b/libraries/appnexusUtils/anKeywords.js
index a6fa8d7a21e..4a0da18024e 100644
--- a/libraries/appnexusUtils/anKeywords.js
+++ b/libraries/appnexusUtils/anKeywords.js
@@ -136,7 +136,7 @@ export function getANMapFromOrtbSegments(ortb2) {
let ortbSegsArrObj = deepAccess(ortb2, path) || [];
ortbSegsArrObj.forEach(segObj => {
// only read segment data from known sources
- const segtax = ORTB_SEGTAX_KEY_MAP[deepAccess(segObj, 'ext.segtax')];
+ const segtax = ORTB_SEGTAX_KEY_MAP[segObj?.ext?.segtax];
if (segtax) {
segObj.segment.forEach(seg => {
// if source was in multiple locations of ortb or had multiple segments in same area, stack them together into an array
diff --git a/libraries/biddoInvamiaUtils/index.js b/libraries/biddoInvamiaUtils/index.js
new file mode 100644
index 00000000000..0d7c4b1b683
--- /dev/null
+++ b/libraries/biddoInvamiaUtils/index.js
@@ -0,0 +1,70 @@
+/**
+ * Helper function to build request payload for banner ads.
+ * @param {Object} bidRequest - The bid request object.
+ * @param {string} endpointUrl - The endpoint URL specific to the bidder.
+ * @returns {Array} An array of server requests.
+ */
+export function buildBannerRequests(bidRequest, endpointUrl) {
+ const serverRequests = [];
+ const sizes = bidRequest.mediaTypes.banner.sizes;
+
+ sizes.forEach(([width, height]) => {
+ bidRequest.params.requestedSizes = [width, height];
+
+ const payload = {
+ ctype: 'div',
+ pzoneid: bidRequest.params.zoneId,
+ width,
+ height,
+ };
+
+ const payloadString = Object.keys(payload)
+ .map((key) => `${key}=${encodeURIComponent(payload[key])}`)
+ .join('&');
+
+ serverRequests.push({
+ method: 'GET',
+ url: endpointUrl,
+ data: payloadString,
+ bidderRequest: bidRequest,
+ });
+ });
+
+ return serverRequests;
+}
+
+/**
+ * Helper function to interpret server response for banner ads.
+ * @param {Object} serverResponse - The server response object.
+ * @param {Object} bidderRequest - The matched bid request for this response.
+ * @returns {Array} An array of bid responses.
+ */
+export function interpretBannerResponse(serverResponse, bidderRequest) {
+ const response = serverResponse.body;
+ const bidResponses = [];
+
+ if (response && response.template && response.template.html) {
+ const { bidId } = bidderRequest;
+ const [width, height] = bidderRequest.params.requestedSizes;
+
+ const bidResponse = {
+ requestId: bidId,
+ cpm: response.hb.cpm,
+ creativeId: response.banner.hash,
+ currency: 'USD',
+ netRevenue: response.hb.netRevenue,
+ ttl: 600,
+ ad: response.template.html,
+ mediaType: 'banner',
+ meta: {
+ advertiserDomains: response.hb.adomains || [],
+ },
+ width,
+ height,
+ };
+
+ bidResponses.push(bidResponse);
+ }
+
+ return bidResponses;
+}
diff --git a/libraries/braveUtils/buildAndInterpret.js b/libraries/braveUtils/buildAndInterpret.js
new file mode 100644
index 00000000000..66cd63896f7
--- /dev/null
+++ b/libraries/braveUtils/buildAndInterpret.js
@@ -0,0 +1,78 @@
+import { isEmpty, parseUrl } from '../../src/utils.js';
+import {config} from '../../src/config.js';
+import { createNativeRequest, createBannerRequest, createVideoRequest } from './index.js';
+import { convertOrtbRequestToProprietaryNative } from '../../src/native.js';
+
+export const buildRequests = (validBidRequests, bidderRequest, endpointURL, defaultCur) => {
+ validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);
+ if (!validBidRequests.length || !bidderRequest) return [];
+
+ const endpoint = endpointURL.replace('hash', validBidRequests[0].params.placementId);
+ const imp = validBidRequests.map((br) => {
+ const impObject = { id: br.bidId, secure: 1 };
+ if (br.mediaTypes.banner) impObject.banner = createBannerRequest(br);
+ else if (br.mediaTypes.video) impObject.video = createVideoRequest(br);
+ else if (br.mediaTypes.native) impObject.native = { id: br.transactionId, ver: '1.2', request: createNativeRequest(br) };
+ return impObject;
+ });
+
+ const page = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation;
+ const data = {
+ id: bidderRequest.bidderRequestId,
+ cur: [defaultCur],
+ device: { w: screen.width, h: screen.height, language: navigator.language?.split('-')[0], ua: navigator.userAgent },
+ site: { domain: parseUrl(page).hostname, page: page },
+ tmax: bidderRequest.timeout,
+ imp,
+ };
+
+ if (bidderRequest.refererInfo.ref) data.site.ref = bidderRequest.refererInfo.ref;
+ if (bidderRequest.gdprConsent) {
+ data.regs = { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0 } };
+ data.user = { ext: { consent: bidderRequest.gdprConsent.consentString || '' } };
+ }
+ if (bidderRequest.uspConsent) data.regs.ext.us_privacy = bidderRequest.uspConsent;
+ if (config.getConfig('coppa')) data.regs.coppa = 1;
+ if (validBidRequests[0].schain) data.source = { ext: { schain: validBidRequests[0].schain } };
+
+ return { method: 'POST', url: endpoint, data };
+};
+
+export const interpretResponse = (serverResponse, defaultCur, parseNative) => {
+ if (!serverResponse || isEmpty(serverResponse.body)) return [];
+
+ let bids = [];
+ serverResponse.body.seatbid.forEach(response => {
+ response.bid.forEach(bid => {
+ const mediaType = bid.ext?.mediaType || 'banner';
+
+ const bidObj = {
+ requestId: bid.impid,
+ cpm: bid.price,
+ width: bid.w,
+ height: bid.h,
+ ttl: 1200,
+ currency: defaultCur,
+ netRevenue: true,
+ creativeId: bid.crid,
+ dealId: bid.dealid || null,
+ mediaType,
+ };
+
+ switch (mediaType) {
+ case 'video':
+ bidObj.vastUrl = bid.adm;
+ break;
+ case 'native':
+ bidObj.native = parseNative(bid.adm);
+ break;
+ default:
+ bidObj.ad = bid.adm;
+ }
+
+ bids.push(bidObj);
+ });
+ });
+
+ return bids;
+};
diff --git a/libraries/braveUtils/index.js b/libraries/braveUtils/index.js
new file mode 100644
index 00000000000..5756e09ae5c
--- /dev/null
+++ b/libraries/braveUtils/index.js
@@ -0,0 +1,95 @@
+import { NATIVE_ASSETS, NATIVE_ASSETS_IDS } from './nativeAssets.js';
+
+/**
+ * Builds a native request object based on the bid request
+ * @param {object} br - The bid request
+ * @returns {object} The native request object
+ */
+export function createNativeRequest(br) {
+ let impObject = {
+ ver: '1.2',
+ assets: []
+ };
+
+ Object.keys(br.mediaTypes.native).forEach((key) => {
+ const props = NATIVE_ASSETS[key];
+ if (props) {
+ const asset = {
+ required: br.mediaTypes.native[key].required ? 1 : 0,
+ id: props.id,
+ [props.name]: {}
+ };
+
+ if (props.type) asset[props.name]['type'] = props.type;
+ if (br.mediaTypes.native[key].len) asset[props.name]['len'] = br.mediaTypes.native[key].len;
+ if (br.mediaTypes.native[key].sizes && br.mediaTypes.native[key].sizes[0]) {
+ asset[props.name]['w'] = br.mediaTypes.native[key].sizes[0];
+ asset[props.name]['h'] = br.mediaTypes.native[key].sizes[1];
+ }
+
+ impObject.assets.push(asset);
+ }
+ });
+
+ return impObject;
+}
+
+/**
+ * Builds a banner request object based on the bid request
+ * @param {object} br - The bid request
+ * @returns {object} The banner request object
+ */
+export function createBannerRequest(br) {
+ let size = br.mediaTypes.banner.sizes?.[0] || [300, 250];
+ return { id: br.transactionId, w: size[0], h: size[1] };
+}
+
+/**
+ * Builds a video request object based on the bid request
+ * @param {object} br - The bid request
+ * @returns {object} The video request object
+ */
+export function createVideoRequest(br) {
+ let videoObj = { id: br.transactionId };
+ const supportedParams = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'skip', 'minbitrate', 'maxbitrate', 'api', 'linearity'];
+
+ supportedParams.forEach((param) => {
+ if (br.mediaTypes.video[param] !== undefined) {
+ videoObj[param] = br.mediaTypes.video[param];
+ }
+ });
+
+ const playerSize = br.mediaTypes.video.playerSize;
+ if (playerSize) {
+ videoObj.w = Array.isArray(playerSize[0]) ? playerSize[0][0] : playerSize[0];
+ videoObj.h = Array.isArray(playerSize[0]) ? playerSize[0][1] : playerSize[1];
+ } else {
+ videoObj.w = 640;
+ videoObj.h = 480;
+ }
+
+ return videoObj;
+}
+
+/**
+ * Parses the native ad response
+ * @param {object} adm - The native ad response
+ * @returns {object} Parsed native ad object
+ */
+export function parseNative(adm) {
+ let bid = {
+ clickUrl: adm.native.link?.url,
+ impressionTrackers: adm.native.imptrackers || [],
+ clickTrackers: adm.native.link?.clicktrackers || [],
+ jstracker: adm.native.jstracker || []
+ };
+ adm.native.assets.forEach((asset) => {
+ const kind = NATIVE_ASSETS_IDS[asset.id];
+ const content = kind && asset[NATIVE_ASSETS[kind].name];
+ if (content) {
+ bid[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
+ }
+ });
+
+ return bid;
+}
diff --git a/libraries/braveUtils/nativeAssets.js b/libraries/braveUtils/nativeAssets.js
new file mode 100644
index 00000000000..07de1264d0c
--- /dev/null
+++ b/libraries/braveUtils/nativeAssets.js
@@ -0,0 +1,23 @@
+/**
+ * IDs and asset types for native ad assets.
+ */
+export const NATIVE_ASSETS_IDS = {
+ 1: 'title',
+ 2: 'icon',
+ 3: 'image',
+ 4: 'body',
+ 5: 'sponsoredBy',
+ 6: 'cta'
+};
+
+/**
+ * Native assets definition for mapping purposes.
+ */
+export const NATIVE_ASSETS = {
+ title: { id: 1, name: 'title' },
+ icon: { id: 2, type: 1, name: 'img' },
+ image: { id: 3, type: 3, name: 'img' },
+ body: { id: 4, type: 2, name: 'data' },
+ sponsoredBy: { id: 5, type: 1, name: 'data' },
+ cta: { id: 6, type: 12, name: 'data' }
+};
diff --git a/libraries/currencyUtils/floor.js b/libraries/currencyUtils/floor.js
index 0bd52172a41..ee41bad01da 100644
--- a/libraries/currencyUtils/floor.js
+++ b/libraries/currencyUtils/floor.js
@@ -16,7 +16,7 @@ export function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (_) {
return 0;
}
diff --git a/libraries/dspxUtils/bidderUtils.js b/libraries/dspxUtils/bidderUtils.js
index f19e2bfc29a..612a20f6865 100644
--- a/libraries/dspxUtils/bidderUtils.js
+++ b/libraries/dspxUtils/bidderUtils.js
@@ -225,7 +225,7 @@ export function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (_) {
return 0
}
diff --git a/libraries/fpdUtils/pageInfo.js b/libraries/fpdUtils/pageInfo.js
index dcf172d96c8..8e02134e070 100644
--- a/libraries/fpdUtils/pageInfo.js
+++ b/libraries/fpdUtils/pageInfo.js
@@ -1,5 +1,3 @@
-import * as utils from '../../src/utils.js';
-
/**
* get page title
* @returns {string}
@@ -67,7 +65,7 @@ export function getReferrer(bidRequest = {}, bidderRequest = {}) {
if (bidRequest.params && bidRequest.params.referrer) {
pageUrl = bidRequest.params.referrer;
} else {
- pageUrl = utils.deepAccess(bidderRequest, 'refererInfo.page');
+ pageUrl = bidderRequest?.refererInfo?.page;
}
return pageUrl;
}
diff --git a/libraries/gptUtils/gptUtils.js b/libraries/gptUtils/gptUtils.js
index 68ce29ef168..923d207c0d9 100644
--- a/libraries/gptUtils/gptUtils.js
+++ b/libraries/gptUtils/gptUtils.js
@@ -11,6 +11,18 @@ export function isSlotMatchingAdUnitCode(adUnitCode) {
return (slot) => compareCodeAndSlot(slot, adUnitCode);
}
+/**
+ * @summary Export a k-v pair to GAM
+ */
+export function setKeyValue(key, value) {
+ if (!key || typeof key !== 'string') return false;
+ window.googletag = window.googletag || {cmd: []};
+ window.googletag.cmd = window.googletag.cmd || [];
+ window.googletag.cmd.push(() => {
+ window.googletag.pubads().setTargeting(key, value);
+ });
+}
+
/**
* @summary Uses the adUnit's code in order to find a matching gpt slot object on the page
*/
diff --git a/libraries/intentIqConstants/intentIqConstants.js b/libraries/intentIqConstants/intentIqConstants.js
index 46c466e936f..ed9856fc213 100644
--- a/libraries/intentIqConstants/intentIqConstants.js
+++ b/libraries/intentIqConstants/intentIqConstants.js
@@ -7,4 +7,4 @@ export const OPT_OUT = 'O';
export const BLACK_LIST = 'L';
export const CLIENT_HINTS_KEY = '_iiq_ch';
export const EMPTY = 'EMPTY'
-export const VERSION = 0.21
+export const VERSION = 0.24
diff --git a/libraries/detectBrowserUtils/detectBrowserUtils.js b/libraries/intentIqUtils/detectBrowserUtils.js
similarity index 91%
rename from libraries/detectBrowserUtils/detectBrowserUtils.js
rename to libraries/intentIqUtils/detectBrowserUtils.js
index 0606388a346..c7004c77ae9 100644
--- a/libraries/detectBrowserUtils/detectBrowserUtils.js
+++ b/libraries/intentIqUtils/detectBrowserUtils.js
@@ -32,6 +32,16 @@ export function detectBrowserFromUserAgent(userAgent) {
ie: /MSIE|Trident/,
};
+ // Check for Edge first
+ if (browserRegexPatterns.edge.test(userAgent)) {
+ return 'edge';
+ }
+
+ // Check for Opera next
+ if (browserRegexPatterns.opera.test(userAgent)) {
+ return 'opera';
+ }
+
// Check for Chrome first to avoid confusion with Safari
if (browserRegexPatterns.chrome.test(userAgent)) {
return 'chrome';
diff --git a/libraries/intentIqUtils/getGppValue.js b/libraries/intentIqUtils/getGppValue.js
new file mode 100644
index 00000000000..9c538b4f753
--- /dev/null
+++ b/libraries/intentIqUtils/getGppValue.js
@@ -0,0 +1,16 @@
+import {gppDataHandler} from '../../src/consentHandler.js';
+
+/**
+ * Retrieves the GPP string value and additional GPP-related information.
+ * This function extracts the GPP data, encodes it, and determines specific GPP flags such as GPI and applicable sections.
+ * @return {Object} An object containing:
+ * - `gppString` (string): The encoded GPP string value.
+ * - `gpi` (number): An indicator representing whether GPP consent is available (0 if available, 1 if not).
+ */
+export function getGppValue() {
+ const gppData = gppDataHandler.getConsentData();
+ const gppString = gppData?.gppString || '';
+ const gpi = gppString ? 0 : 1;
+
+ return { gppString, gpi };
+}
diff --git a/libraries/intentIqUtils/getRefferer.js b/libraries/intentIqUtils/getRefferer.js
new file mode 100644
index 00000000000..39fde70ac24
--- /dev/null
+++ b/libraries/intentIqUtils/getRefferer.js
@@ -0,0 +1,64 @@
+import { getWindowTop, logError, getWindowLocation, getWindowSelf } from '../../src/utils.js';
+
+/**
+ * Determines if the script is running inside an iframe and retrieves the URL.
+ * @return {string} The encoded vrref value representing the relevant URL.
+ */
+export function getReferrer() {
+ try {
+ if (getWindowSelf() === getWindowTop()) {
+ return encodeURIComponent(getWindowLocation().href);
+ } else {
+ return encodeURIComponent(getWindowTop().location.href);
+ }
+ } catch (error) {
+ logError(`Error accessing location: ${error}`);
+ return '';
+ }
+}
+
+/**
+ * Appends `vrref` and `fui` parameters to the provided URL.
+ * If the referrer URL is available, it appends `vrref` with the relevant referrer value based on the domain.
+ * Otherwise, it appends `fui=1`. If a domain name is provided, it may also append `vrref` with the domain.
+ * @param {string} url - The URL to append parameters to.
+ * @param {string} domainName - The domain name used to determine the relevant referrer.
+ * @return {string} The modified URL with appended `vrref` or `fui` parameters.
+ */
+export function appendVrrefAndFui(url, domainName) {
+ const fullUrl = getReferrer();
+ if (fullUrl) {
+ return (url += '&vrref=' + getRelevantRefferer(domainName, fullUrl));
+ }
+ url += '&fui=1'; // Full Url Issue
+ url += '&vrref=' + encodeURIComponent(domainName || '');
+ return url;
+}
+
+/**
+ * Get the relevant referrer based on full URL and domain
+ * @param {string} domainName The domain name to compare
+ * @param {string} fullUrl The full URL to analyze
+ * @return {string} The relevant referrer
+ */
+export function getRelevantRefferer(domainName, fullUrl) {
+ if (domainName && isDomainIncluded(fullUrl, domainName)) {
+ return fullUrl;
+ }
+ return domainName ? encodeURIComponent(domainName) : fullUrl;
+}
+
+/**
+ * Checks if the provided domain name is included in the full URL.
+ * @param {string} fullUrl - The full URL to check.
+ * @param {string} domainName - The domain name to search for within the URL.
+ * @return {boolean} `True` if the domain name is found in the URL, `false` otherwise.
+ */
+export function isDomainIncluded(fullUrl, domainName) {
+ try {
+ return fullUrl.includes(domainName);
+ } catch (error) {
+ logError(`Invalid URL provided: ${error}`);
+ return false;
+ }
+}
diff --git a/libraries/interpretResponseUtils/index.js b/libraries/interpretResponseUtils/index.js
new file mode 100644
index 00000000000..6d081e4c272
--- /dev/null
+++ b/libraries/interpretResponseUtils/index.js
@@ -0,0 +1,22 @@
+import {logError} from '../../src/utils.js';
+
+export function interpretResponseUtil(serverResponse, {bidderRequest}, eachBidCallback) {
+ const bids = [];
+ if (!serverResponse.body || serverResponse.body.error) {
+ let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`;
+ if (serverResponse.body && serverResponse.body.error) { errorMessage += `: ${serverResponse.body.error}`; }
+ logError(errorMessage);
+ return bids;
+ }
+ (serverResponse.body.tags || []).forEach(serverBid => {
+ try {
+ const bid = eachBidCallback(serverBid);
+ if (bid) {
+ bids.push(bid);
+ }
+ } catch (e) {
+ // Do nothing
+ }
+ });
+ return bids;
+}
diff --git a/libraries/liveIntentId/externalIdSystem.js b/libraries/liveIntentId/externalIdSystem.js
index 5db94b90a44..9fcb9e6da1b 100644
--- a/libraries/liveIntentId/externalIdSystem.js
+++ b/libraries/liveIntentId/externalIdSystem.js
@@ -1,7 +1,7 @@
import { logError } from '../../src/utils.js';
import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../../src/adapterManager.js';
import { submodule } from '../../src/hook.js';
-import { DEFAULT_AJAX_TIMEOUT, MODULE_NAME, parseRequestedAttributes, composeIdObject, eids, GVLID, PRIMARY_IDS } from './shared.js'
+import { DEFAULT_AJAX_TIMEOUT, MODULE_NAME, parseRequestedAttributes, composeIdObject, eids, GVLID, PRIMARY_IDS, makeSourceEventToSend } from './shared.js'
// Reference to the client for the liQHub.
let cachedClientRef
@@ -97,8 +97,9 @@ function initializeClient(configParams) {
resolveSettings
})
- if (configParams.emailHash != null) {
- window.liQHub.push({ type: 'collect', clientRef, sourceEvent: { hash: configParams.emailHash } })
+ let sourceEvent = makeSourceEventToSend(configParams)
+ if (sourceEvent != null) {
+ window.liQHub.push({ type: 'collect', clientRef, sourceEvent })
}
cachedClientRef = clientRef
diff --git a/libraries/liveIntentId/idSystem.js b/libraries/liveIntentId/idSystem.js
index 2077df8d8bf..a9b8052c752 100644
--- a/libraries/liveIntentId/idSystem.js
+++ b/libraries/liveIntentId/idSystem.js
@@ -11,7 +11,7 @@ import { submodule } from '../../src/hook.js';
import { LiveConnect } from 'live-connect-js'; // eslint-disable-line prebid/validate-imports
import { getStorageManager } from '../../src/storageManager.js';
import { MODULE_TYPE_UID } from '../../src/activities/modules.js';
-import { DEFAULT_AJAX_TIMEOUT, MODULE_NAME, composeIdObject, eids, GVLID, DEFAULT_DELAY, PRIMARY_IDS, parseRequestedAttributes } from './shared.js'
+import { DEFAULT_AJAX_TIMEOUT, MODULE_NAME, composeIdObject, eids, GVLID, DEFAULT_DELAY, PRIMARY_IDS, parseRequestedAttributes, makeSourceEventToSend } from './shared.js'
/**
* @typedef {import('../modules/userId/index.js').Submodule} Submodule
@@ -23,7 +23,7 @@ const EVENTS_TOPIC = 'pre_lips';
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});
const calls = {
- ajaxGet: (url, onSuccess, onError, timeout) => {
+ ajaxGet: (url, onSuccess, onError, timeout, headers) => {
ajaxBuilder(timeout)(
url,
{
@@ -33,7 +33,8 @@ const calls = {
undefined,
{
method: 'GET',
- withCredentials: true
+ withCredentials: true,
+ customHeaders: headers
}
)
},
@@ -92,7 +93,11 @@ function initializeLiveConnect(configParams) {
const publisherId = configParams.publisherId || 'any';
const identityResolutionConfig = {
publisherId: publisherId,
- requestedAttributes: parseRequestedAttributes(configParams.requestedAttributesOverrides)
+ requestedAttributes: parseRequestedAttributes(configParams.requestedAttributesOverrides),
+ extraAttributes: {
+ ipv4: configParams.ipv4,
+ ipv6: configParams.ipv6
+ }
};
if (configParams.url) {
identityResolutionConfig.url = configParams.url;
@@ -136,8 +141,10 @@ function initializeLiveConnect(configParams) {
// The second param is the storage object, LS & Cookie manipulation uses PBJS.
// The third param is the ajax and pixel object, the AJAX and pixel use PBJS.
liveConnect = liveIntentIdSubmodule.getInitializer()(liveConnectConfig, storage, calls);
- if (configParams.emailHash) {
- liveConnect.push({ hash: configParams.emailHash });
+
+ const sourceEvent = makeSourceEventToSend(configParams)
+ if (sourceEvent != null) {
+ liveConnect.push(sourceEvent);
}
return liveConnect;
}
diff --git a/libraries/liveIntentId/shared.js b/libraries/liveIntentId/shared.js
index f7b9f32760f..509f91e44d9 100644
--- a/libraries/liveIntentId/shared.js
+++ b/libraries/liveIntentId/shared.js
@@ -29,6 +29,31 @@ export function parseRequestedAttributes(overrides) {
}
}
+export function makeSourceEventToSend(configParams) {
+ const sourceEvent = {}
+ let nonEmpty = false
+ if (typeof configParams.emailHash === 'string') {
+ nonEmpty = true
+ sourceEvent.emailHash = configParams.emailHash
+ }
+ if (typeof configParams.ipv4 === 'string') {
+ nonEmpty = true
+ sourceEvent.ipv4 = configParams.ipv4
+ }
+ if (typeof configParams.ipv6 === 'string') {
+ nonEmpty = true
+ sourceEvent.ipv6 = configParams.ipv6
+ }
+ if (typeof configParams.userAgent === 'string') {
+ nonEmpty = true
+ sourceEvent.userAgent = configParams.userAgent
+ }
+
+ if (nonEmpty) {
+ return sourceEvent
+ }
+}
+
export function composeIdObject(value) {
const result = {};
diff --git a/libraries/mediaImpactUtils/index.js b/libraries/mediaImpactUtils/index.js
new file mode 100644
index 00000000000..a56761d8ed4
--- /dev/null
+++ b/libraries/mediaImpactUtils/index.js
@@ -0,0 +1,61 @@
+import { buildUrl } from '../../src/utils.js';
+import { ajax } from '../../src/ajax.js';
+
+/**
+ * Builds the bid requests and beacon parameters.
+ * @param {Array} validBidRequests - The array of valid bid requests.
+ * @param {string} referer - The referer URL.
+ * @returns {Object} - An object containing bidRequests and beaconParams.
+ */
+export function buildBidRequestsAndParams(validBidRequests, referer) {
+ const bidRequests = [];
+ const beaconParams = { tag: [], partner: [], sizes: [], referer: encodeURIComponent(referer) };
+
+ validBidRequests.forEach(function (validBidRequest) {
+ const sizes = validBidRequest.params.sizes || validBidRequest.sizes;
+
+ const bidRequestObject = {
+ adUnitCode: validBidRequest.adUnitCode,
+ sizes: sizes,
+ bidId: validBidRequest.bidId,
+ referer: referer,
+ };
+
+ if (parseInt(validBidRequest.params.unitId)) {
+ bidRequestObject.unitId = parseInt(validBidRequest.params.unitId);
+ beaconParams.tag.push(validBidRequest.params.unitId);
+ }
+
+ if (parseInt(validBidRequest.params.partnerId)) {
+ bidRequestObject.unitId = 0;
+ bidRequestObject.partnerId = parseInt(validBidRequest.params.partnerId);
+ beaconParams.partner.push(validBidRequest.params.partnerId);
+ }
+
+ bidRequests.push(bidRequestObject);
+ beaconParams.sizes.push(joinSizesToString(sizes));
+ });
+
+ // Finalize beaconParams
+ if (beaconParams.partner.length > 0) {
+ beaconParams.partner = beaconParams.partner.join(',');
+ } else {
+ delete beaconParams.partner;
+ }
+ beaconParams.tag = beaconParams.tag.join(',');
+ beaconParams.sizes = beaconParams.sizes.join(',');
+
+ return { bidRequests, beaconParams };
+}
+
+export function joinSizesToString(sizes) {
+ return sizes.map(size => size.join('x')).join('|');
+}
+
+export function postRequest(endpoint, data) {
+ ajax(endpoint, null, data, { method: 'POST' });
+}
+
+export function buildEndpointUrl(protocol, hostname, pathname, searchParams) {
+ return buildUrl({ protocol, hostname, pathname, search: searchParams });
+}
diff --git a/libraries/mspa/activityControls.js b/libraries/mspa/activityControls.js
index eaf515e2385..eb68259d585 100644
--- a/libraries/mspa/activityControls.js
+++ b/libraries/mspa/activityControls.js
@@ -97,6 +97,9 @@ export function mspaRule(sids, getConsent, denies, applicableSids = () => gppDat
if (consent == null) {
return {allow: false, reason: 'consent data not available'};
}
+ if (consent.Version !== 1) {
+ return {allow: false, reason: `unsupported consent specification version "${consent.Version}"`}
+ }
if (denies(consent)) {
return {allow: false};
}
@@ -105,7 +108,7 @@ export function mspaRule(sids, getConsent, denies, applicableSids = () => gppDat
}
function flatSection(subsections) {
- if (subsections == null) return subsections;
+ if (!Array.isArray(subsections)) return subsections;
return subsections.reduceRight((subsection, consent) => {
return Object.assign(consent, subsection);
}, {});
diff --git a/libraries/ortb2Utils/currency.js b/libraries/ortb2Utils/currency.js
new file mode 100644
index 00000000000..5c2c6b7956d
--- /dev/null
+++ b/libraries/ortb2Utils/currency.js
@@ -0,0 +1,3 @@
+export function getCurrencyFromBidderRequest(bidderRequest) {
+ return bidderRequest?.ortb2?.ext?.prebid?.adServerCurrency;
+}
diff --git a/libraries/ortbConverter/processors/banner.js b/libraries/ortbConverter/processors/banner.js
index fca9598022b..877a8b9081b 100644
--- a/libraries/ortbConverter/processors/banner.js
+++ b/libraries/ortbConverter/processors/banner.js
@@ -1,6 +1,5 @@
import {
createTrackPixelHtml,
- deepAccess,
inIframe,
mergeDeep,
sizesToSizeTuples,
@@ -15,7 +14,7 @@ import {BANNER} from '../../../src/mediaTypes.js';
export function fillBannerImp(imp, bidRequest, context) {
if (context.mediaType && context.mediaType !== BANNER) return;
- const bannerParams = deepAccess(bidRequest, 'mediaTypes.banner');
+ const bannerParams = bidRequest?.mediaTypes?.banner;
if (bannerParams) {
const banner = {
topframe: inIframe() === true ? 0 : 1
diff --git a/libraries/ortbConverter/processors/default.js b/libraries/ortbConverter/processors/default.js
index 9d916b87172..f9c7e3baefc 100644
--- a/libraries/ortbConverter/processors/default.js
+++ b/libraries/ortbConverter/processors/default.js
@@ -61,6 +61,12 @@ export const DEFAULT_PROCESSORS = {
delete imp.ext?.data?.pbadslot;
}
}
+ },
+ secure: {
+ // should set imp.secure to 1 unless publisher has set it
+ fn(imp, bidRequest) {
+ imp.secure = imp.secure ?? 1;
+ }
}
},
[BID_RESPONSE]: {
diff --git a/libraries/ortbConverter/processors/video.js b/libraries/ortbConverter/processors/video.js
index 85ab1cd6ecf..3bb4e69e24d 100644
--- a/libraries/ortbConverter/processors/video.js
+++ b/libraries/ortbConverter/processors/video.js
@@ -1,4 +1,4 @@
-import {deepAccess, isEmpty, logWarn, mergeDeep, sizesToSizeTuples, sizeTupleToRtbSize} from '../../../src/utils.js';
+import {isEmpty, logWarn, mergeDeep, sizesToSizeTuples, sizeTupleToRtbSize} from '../../../src/utils.js';
import {VIDEO} from '../../../src/mediaTypes.js';
import {ORTB_VIDEO_PARAMS} from '../../../src/video.js';
@@ -6,7 +6,7 @@ import {ORTB_VIDEO_PARAMS} from '../../../src/video.js';
export function fillVideoImp(imp, bidRequest, context) {
if (context.mediaType && context.mediaType !== VIDEO) return;
- const videoParams = deepAccess(bidRequest, 'mediaTypes.video');
+ const videoParams = bidRequest?.mediaTypes?.video;
if (!isEmpty(videoParams)) {
const video = Object.fromEntries(
// Parameters that share the same name & semantics between pbjs adUnits and imp.video
@@ -27,7 +27,7 @@ export function fillVideoImp(imp, bidRequest, context) {
export function fillVideoResponse(bidResponse, seatbid, context) {
if (bidResponse.mediaType === VIDEO) {
- if (deepAccess(context.imp, 'video.w') && deepAccess(context.imp, 'video.h')) {
+ if (context?.imp?.video?.w && context?.imp?.video?.h) {
[bidResponse.playerWidth, bidResponse.playerHeight] = [context.imp.video.w, context.imp.video.h];
}
diff --git a/libraries/precisoUtils/bidUtilsCommon.js b/libraries/precisoUtils/bidUtilsCommon.js
index 74cf00b8450..a8ea97efcaf 100644
--- a/libraries/precisoUtils/bidUtilsCommon.js
+++ b/libraries/precisoUtils/bidUtilsCommon.js
@@ -37,7 +37,7 @@ export function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (_) {
return 0
}
diff --git a/libraries/riseUtils/index.js b/libraries/riseUtils/index.js
index 25c7183d552..2bc337b9c55 100644
--- a/libraries/riseUtils/index.js
+++ b/libraries/riseUtils/index.js
@@ -5,7 +5,8 @@ import {
isEmpty,
contains,
isInteger,
- getBidIdParameter
+ getBidIdParameter,
+ isPlainObject
} from '../../src/utils.js';
import { BANNER, VIDEO } from '../../src/mediaTypes.js';
import {config} from '../../src/config.js';
@@ -19,7 +20,7 @@ export function getFloor(bid, mediaType) {
mediaType: mediaType,
size: '*'
});
- return floorResult.currency === 'USD' && floorResult.floor ? floorResult.floor : 0;
+ return isPlainObject(floorResult) && floorResult.currency === 'USD' && floorResult.floor ? floorResult.floor : 0;
}
export function getSizesArray(bid, mediaType) {
diff --git a/libraries/targetVideoUtils/bidderUtils.js b/libraries/targetVideoUtils/bidderUtils.js
index f18540818cb..cf106566944 100644
--- a/libraries/targetVideoUtils/bidderUtils.js
+++ b/libraries/targetVideoUtils/bidderUtils.js
@@ -1,6 +1,7 @@
+import {SYNC_URL} from './constants.js';
import {VIDEO} from '../../src/mediaTypes.js';
import {getRefererInfo} from '../../src/refererDetection.js';
-import {createTrackPixelHtml, deepAccess, getBidRequest} from '../../src/utils.js';
+import {createTrackPixelHtml, deepAccess, getBidRequest, formatQS} from '../../src/utils.js';
export function getSizes(request) {
let sizes = request.sizes;
@@ -167,6 +168,40 @@ export function getAd(bid) {
return {ad, adUrl, vastXml, vastUrl};
}
+export function getSyncResponse(syncOptions, gdprConsent, uspConsent, gppConsent, endpoint) {
+ const params = {
+ endpoint
+ };
+
+ // Attaching GDPR Consent Params in UserSync url
+ if (gdprConsent) {
+ params.gdpr = (gdprConsent.gdprApplies ? 1 : 0);
+ params.gdpr_consent = encodeURIComponent(gdprConsent.consentString || '');
+ }
+
+ // CCPA
+ if (uspConsent && typeof uspConsent === 'string') {
+ params.us_privacy = encodeURIComponent(uspConsent);
+ }
+
+ // GPP Consent
+ if (gppConsent?.gppString && gppConsent?.applicableSections?.length) {
+ params.gpp = encodeURIComponent(gppConsent.gppString);
+ params.gpp_sid = encodeURIComponent(gppConsent?.applicableSections?.join(','));
+ }
+
+ const queryParams = Object.keys(params).length > 0 ? formatQS(params) : '';
+ let response = [];
+ if (syncOptions.iframeEnabled) {
+ response = [{
+ type: 'iframe',
+ url: SYNC_URL + 'load-cookie.html?' + queryParams
+ }];
+ }
+
+ return response;
+}
+
export function getSiteObj() {
const refInfo = (getRefererInfo && getRefererInfo()) || {};
diff --git a/libraries/targetVideoUtils/constants.js b/libraries/targetVideoUtils/constants.js
index 8ce94c0eaeb..33076b71e7d 100644
--- a/libraries/targetVideoUtils/constants.js
+++ b/libraries/targetVideoUtils/constants.js
@@ -6,9 +6,10 @@ const BIDDER_CODE = 'targetVideo';
const TIME_TO_LIVE = 300;
const BANNER_ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid';
const VIDEO_ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction';
+const SYNC_URL = 'https://bppb.link/static/';
const VIDEO_PARAMS = [
'api', 'linearity', 'maxduration', 'mimes', 'minduration',
- 'plcmt', 'playbackmethod', 'protocols', 'startdelay'
+ 'plcmt', 'playbackmethod', 'protocols', 'startdelay', 'placement'
];
export {
@@ -16,6 +17,7 @@ export {
GVLID,
MARGIN,
BIDDER_CODE,
+ SYNC_URL,
TIME_TO_LIVE,
BANNER_ENDPOINT_URL,
VIDEO_ENDPOINT_URL,
diff --git a/libraries/teqblazeUtils/bidderUtils.js b/libraries/teqblazeUtils/bidderUtils.js
index 6186e526eb8..f9484ebe5d1 100644
--- a/libraries/teqblazeUtils/bidderUtils.js
+++ b/libraries/teqblazeUtils/bidderUtils.js
@@ -29,7 +29,7 @@ const getBidFloor = (bid) => {
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (err) {
return 0;
}
diff --git a/libraries/timeoutQueue/timeoutQueue.js b/libraries/timeoutQueue/timeoutQueue.js
new file mode 100644
index 00000000000..5046eed150b
--- /dev/null
+++ b/libraries/timeoutQueue/timeoutQueue.js
@@ -0,0 +1,22 @@
+export function timeoutQueue() {
+ const queue = [];
+ return {
+ submit(timeout, onResume, onTimeout) {
+ const item = [
+ onResume,
+ setTimeout(() => {
+ queue.splice(queue.indexOf(item), 1);
+ onTimeout();
+ }, timeout)
+ ];
+ queue.push(item);
+ },
+ resume() {
+ while (queue.length) {
+ const [onResume, timerId] = queue.shift();
+ clearTimeout(timerId);
+ onResume();
+ }
+ }
+ }
+}
diff --git a/libraries/vastTrackers/vastTrackers.js b/libraries/vastTrackers/vastTrackers.js
index f414a65a18c..b8fc829a89a 100644
--- a/libraries/vastTrackers/vastTrackers.js
+++ b/libraries/vastTrackers/vastTrackers.js
@@ -4,27 +4,47 @@ import {logError} from '../../src/utils.js';
import {isActivityAllowed} from '../../src/activities/rules.js';
import {ACTIVITY_REPORT_ANALYTICS} from '../../src/activities/activities.js';
import {activityParams} from '../../src/activities/activityParams.js';
+import {auctionManager} from '../../src/auctionManager.js';
const vastTrackers = [];
+let enabled = false;
export function reset() {
vastTrackers.length = 0;
}
-export function addTrackersToResponse(next, adUnitcode, bidResponse, reject) {
- if (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) {
- const vastTrackers = getVastTrackers(bidResponse);
- if (vastTrackers) {
- bidResponse.vastXml = insertVastTrackers(vastTrackers, bidResponse.vastXml);
- const impTrackers = vastTrackers.get('impressions');
- if (impTrackers) {
- bidResponse.vastImpUrl = [].concat([...impTrackers]).concat(bidResponse.vastImpUrl).filter(t => t);
+export function enable() {
+ if (!enabled) {
+ addBidResponse.before(addTrackersToResponse);
+ enabled = true;
+ }
+}
+
+export function disable() {
+ if (enabled) {
+ addBidResponse.getHooks({hook: addTrackersToResponse}).remove();
+ enabled = false;
+ }
+}
+
+export function responseHook({index = auctionManager.index} = {}) {
+ return function addTrackersToResponse(next, adUnitcode, bidResponse, reject) {
+ if (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) {
+ const vastTrackers = getVastTrackers(bidResponse, {index});
+ if (vastTrackers) {
+ bidResponse.vastXml = insertVastTrackers(vastTrackers, bidResponse.vastXml);
+ const impTrackers = vastTrackers.get('impressions');
+ if (impTrackers) {
+ bidResponse.vastImpUrl = [].concat([...impTrackers]).concat(bidResponse.vastImpUrl).filter(t => t);
+ }
}
}
+ next(adUnitcode, bidResponse, reject);
}
- next(adUnitcode, bidResponse, reject);
}
-addBidResponse.before(addTrackersToResponse);
+
+const addTrackersToResponse = responseHook();
+enable();
export function registerVastTrackers(moduleType, moduleName, trackerFn) {
if (typeof trackerFn === 'function') {
@@ -54,7 +74,7 @@ export function insertVastTrackers(trackers, vastXml) {
return vastXml;
}
-export function getVastTrackers(bid) {
+export function getVastTrackers(bid, {index = auctionManager.index}) {
let trackers = [];
vastTrackers.filter(
({
@@ -63,7 +83,9 @@ export function getVastTrackers(bid) {
trackerFn
}) => isActivityAllowed(ACTIVITY_REPORT_ANALYTICS, activityParams(moduleType, moduleName))
).forEach(({trackerFn}) => {
- let trackersToAdd = trackerFn(bid);
+ const auction = index.getAuction(bid).getProperties();
+ const bidRequest = index.getBidRequest(bid);
+ let trackersToAdd = trackerFn(bid, {auction, bidRequest});
trackersToAdd.forEach(trackerToAdd => {
if (isValidVastTracker(trackers, trackerToAdd)) {
trackers.push(trackerToAdd);
diff --git a/libraries/vidazooUtils/bidderUtils.js b/libraries/vidazooUtils/bidderUtils.js
index 5c3409f4780..df947142a4c 100644
--- a/libraries/vidazooUtils/bidderUtils.js
+++ b/libraries/vidazooUtils/bidderUtils.js
@@ -255,7 +255,7 @@ export function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidder
size: '*'
});
- if (floorInfo.currency === 'USD') {
+ if (floorInfo?.currency === 'USD') {
bidFloor = floorInfo.floor;
}
}
diff --git a/libraries/viewport/viewport.js b/libraries/viewport/viewport.js
new file mode 100644
index 00000000000..b385e9a27ec
--- /dev/null
+++ b/libraries/viewport/viewport.js
@@ -0,0 +1,13 @@
+import {getWindowTop} from '../../src/utils.js';
+
+export function getViewportCoordinates() {
+ try {
+ const win = getWindowTop();
+ let { scrollY: top, scrollX: left, innerHeight, innerWidth } = win;
+ innerHeight = innerHeight || win.document.documentElement.clientWidth || win.document.body.clientWidth;
+ innerWidth = innerWidth || win.document.documentElement.clientHeight || win.document.body.clientHeight
+ return { top, right: left + innerWidth, bottom: top + innerHeight, left };
+ } catch (e) {
+ return {};
+ }
+}
diff --git a/libraries/xeUtils/bidderUtils.js b/libraries/xeUtils/bidderUtils.js
index abbd14db6a9..f03c6323d76 100644
--- a/libraries/xeUtils/bidderUtils.js
+++ b/libraries/xeUtils/bidderUtils.js
@@ -1,4 +1,4 @@
-import {deepAccess, getBidIdParameter, isFn, logError, isArray, parseSizesInput} from '../../src/utils.js';
+import {deepAccess, getBidIdParameter, isFn, logError, isArray, parseSizesInput, isPlainObject} from '../../src/utils.js';
import {getAdUnitSizes} from '../sizeUtils/sizeUtils.js';
import {findIndex} from '../../src/polyfill.js';
@@ -13,7 +13,7 @@ export function getBidFloor(bid, currency = 'USD') {
size: '*'
});
- if (typeof floor === 'object' && !isNaN(floor.floor) && floor.currency === currency) {
+ if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === currency) {
return floor.floor;
}
diff --git a/modules/.submodules.json b/modules/.submodules.json
index d998a62500a..6dac11bf0ed 100644
--- a/modules/.submodules.json
+++ b/modules/.submodules.json
@@ -40,6 +40,7 @@
"pubProvidedIdSystem",
"publinkIdSystem",
"quantcastIdSystem",
+ "rewardedInterestIdSystem",
"sharedIdSystem",
"tapadIdSystem",
"teadsIdSystem",
diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js
index ae002d79d58..555d9c8a291 100644
--- a/modules/33acrossBidAdapter.js
+++ b/modules/33acrossBidAdapter.js
@@ -535,7 +535,7 @@ function _getBidFloors(bidRequest, size, mediaType) {
size: [ size.w, size.h ]
});
- if (!isNaN(bidFloors.floor) && (bidFloors.currency === CURRENCY)) {
+ if (!isNaN(bidFloors?.floor) && (bidFloors?.currency === CURRENCY)) {
return bidFloors.floor;
}
}
diff --git a/modules/51DegreesRtdProvider.js b/modules/51DegreesRtdProvider.js
index 4f8024aa84f..8251fd76e28 100644
--- a/modules/51DegreesRtdProvider.js
+++ b/modules/51DegreesRtdProvider.js
@@ -4,6 +4,7 @@ import {submodule} from '../src/hook.js';
import {
deepAccess,
deepSetValue,
+ formatQS,
mergeDeep,
prefixLog,
} from '../src/utils.js';
@@ -107,15 +108,42 @@ export const extractConfig = (moduleConfig, reqBidsConfigObj) => {
* @param {Object} pathData API path data
* @param {string} [pathData.resourceKey] Resource key
* @param {string} [pathData.onPremiseJSUrl] On-premise JS URL
+ * @param {Object
} [pathData.hev] High entropy values
+ * @param {Window} [win] Window object (mainly for testing)
* @returns {string} 51Degrees JS URL
*/
-export const get51DegreesJSURL = (pathData) => {
- if (pathData.onPremiseJSUrl) {
- return pathData.onPremiseJSUrl;
- }
- return `https://cloud.51degrees.com/api/v4/${pathData.resourceKey}.js`;
+export const get51DegreesJSURL = (pathData, win) => {
+ const _window = win || window;
+ const baseURL = pathData.onPremiseJSUrl || `https://cloud.51degrees.com/api/v4/${pathData.resourceKey}.js`;
+
+ const queryPrefix = baseURL.includes('?') ? '&' : '?';
+ const qs = {};
+
+ deepSetNotEmptyValue(
+ qs,
+ '51D_GetHighEntropyValues',
+ pathData.hev && Object.keys(pathData.hev).length ? btoa(JSON.stringify(pathData.hev)) : null,
+ );
+ deepSetNotEmptyValue(qs, '51D_ScreenPixelsHeight', _window?.screen?.height);
+ deepSetNotEmptyValue(qs, '51D_ScreenPixelsWidth', _window?.screen?.width);
+ deepSetNotEmptyValue(qs, '51D_PixelRatio', _window?.devicePixelRatio);
+
+ const _qs = formatQS(qs);
+ const _qsString = _qs ? `${queryPrefix}${_qs}` : '';
+
+ return `${baseURL}${_qsString}`;
}
+/**
+ * Retrieves high entropy values from `navigator.userAgentData` if available
+ *
+ * @param {Array} hints - An array of hints indicating which high entropy values to retrieve
+ * @returns {Promise>} A promise that resolves to an object containing high entropy values if supported, or `undefined` if not
+ */
+export const getHighEntropyValues = async (hints) => {
+ return navigator?.userAgentData?.getHighEntropyValues?.(hints);
+};
+
/**
* Check if meta[http-equiv="Delegate-CH"] tag is present in the document head and points to 51Degrees cloud
*
@@ -251,10 +279,6 @@ export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, user
logMessage('Resource key: ', resourceKey);
logMessage('On-premise JS URL: ', onPremiseJSUrl);
- // Get 51Degrees JS URL, which is either cloud or on-premise
- const scriptURL = get51DegreesJSURL(resourceKey ? {resourceKey} : {onPremiseJSUrl});
- logMessage('URL of the script to be injected: ', scriptURL);
-
// Check if 51Degrees meta is present (cloud only)
if (resourceKey) {
logMessage('Checking if 51Degrees meta is present in the document head');
@@ -263,21 +287,27 @@ export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, user
}
}
- // Inject 51Degrees script, get device data and merge it into the ORTB2 object
- loadExternalScript(scriptURL, MODULE_TYPE_RTD, MODULE_NAME, () => {
- logMessage('Successfully injected 51Degrees script');
- const fod = /** @type {Object} */ (window.fod);
- // Convert and merge device data in the callback
- fod.complete((data) => {
- logMessage('51Degrees raw data: ', data);
- mergeDeep(
- reqBidsConfigObj.ortb2Fragments.global,
- convert51DegreesDataToOrtb2(data),
- );
- logMessage('reqBidsConfigObj: ', reqBidsConfigObj);
- callback();
- });
- }, document, {crossOrigin: 'anonymous'});
+ getHighEntropyValues(['model', 'platform', 'platformVersion', 'fullVersionList']).then((hev) => {
+ // Get 51Degrees JS URL, which is either cloud or on-premise
+ const scriptURL = get51DegreesJSURL({resourceKey, onPremiseJSUrl, hev});
+ logMessage('URL of the script to be injected: ', scriptURL);
+
+ // Inject 51Degrees script, get device data and merge it into the ORTB2 object
+ loadExternalScript(scriptURL, MODULE_TYPE_RTD, MODULE_NAME, () => {
+ logMessage('Successfully injected 51Degrees script');
+ const fod = /** @type {Object} */ (window.fod);
+ // Convert and merge device data in the callback
+ fod.complete((data) => {
+ logMessage('51Degrees raw data: ', data);
+ mergeDeep(
+ reqBidsConfigObj.ortb2Fragments.global,
+ convert51DegreesDataToOrtb2(data),
+ );
+ logMessage('reqBidsConfigObj: ', reqBidsConfigObj);
+ callback();
+ });
+ }, document, {crossOrigin: 'anonymous'});
+ });
} catch (error) {
// In case of an error, log it and continue
logError(error);
diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js
index 452e521c680..410accc946a 100644
--- a/modules/adagioAnalyticsAdapter.js
+++ b/modules/adagioAnalyticsAdapter.js
@@ -68,6 +68,7 @@ const cache = {
return { auctionId: null, adUnitCode: null }
}
};
+
const enc = window.encodeURIComponent;
/**
@@ -195,7 +196,14 @@ function handlerAuctionInit(event) {
const w = getBestWindowForAdagio();
const prebidAuctionId = event.auctionId;
- const adUnitCodes = removeDuplicates(event.adUnitCodes, adUnitCode => adUnitCode);
+
+ // adUnitCodes come from `event.bidderRequests` to be sure to keep the ad-units that are valid and will be effectively used during the auction.
+ // This array can be different than `event.adUnitCodes` because of the usage of conditionnal ad-units (see: https://docs.prebid.org/dev-docs/conditional-ad-units.html)
+ const adUnitCodes = new Set(
+ event.bidderRequests
+ .map(br => br.bids.map(bid => bid.adUnitCode))
+ .flat()
+ );
// Check if Adagio is on the bid requests.
const adagioBidRequest = event.bidderRequests.find(bidRequest => isAdagio(bidRequest.bidderCode));
@@ -207,6 +215,7 @@ function handlerAuctionInit(event) {
adUnitCodes.forEach(adUnitCode => {
// event.adUnits are splitted by mediatypes
+ // having twin ad-unit codes is ok: https://docs.prebid.org/dev-docs/adunit-reference.html#twin-adunit-codes
const adUnits = event.adUnits.filter(adUnit => adUnit.code === adUnitCode);
// Get all bidders configured for the ad unit.
@@ -222,7 +231,7 @@ function handlerAuctionInit(event) {
mediaTypeKey => mediaTypeKey
).map(mediaType => getMediaTypeAlias(mediaType)).sort();
const bannerSizes = removeDuplicates(
- mediaTypes.filter(mediaType => mediaType.hasOwnProperty(BANNER))
+ mediaTypes.filter(mediaType => mediaType.hasOwnProperty(BANNER) && mediaType[BANNER].hasOwnProperty('sizes'))
.map(mediaType => mediaType[BANNER].sizes.map(size => size.join('x')))
.flat(),
bannerSize => bannerSize
@@ -236,6 +245,7 @@ function handlerAuctionInit(event) {
const request = event.bidderRequests.find(br => br.bidderCode === bidder)
return request ? request.bids[0].src : null
}
+
const biddersSrc = sortedBidderNames.map(bidSrcMapper).join(',');
const biddersCode = sortedBidderNames.map(bidder => adapterManager.resolveAlias(bidder)).join(',');
diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js
index cb125d4446e..d44c7e249d6 100644
--- a/modules/adagioBidAdapter.js
+++ b/modules/adagioBidAdapter.js
@@ -313,7 +313,7 @@ function _getFloors(bidRequest) {
floors.push(cleanObj({
mt: mediaType,
s: isArray(size) ? `${size[0]}x${size[1]}` : undefined,
- f: (!isNaN(info.floor) && info.currency === CURRENCY) ? info.floor : undefined
+ f: (!isNaN(info?.floor) && info?.currency === CURRENCY) ? info?.floor : undefined
}));
}
diff --git a/modules/adagioRtdProvider.js b/modules/adagioRtdProvider.js
index 7098406be0d..2260ae8d31a 100644
--- a/modules/adagioRtdProvider.js
+++ b/modules/adagioRtdProvider.js
@@ -73,13 +73,14 @@ const _SESSION = (function() {
storage.getDataFromLocalStorage('adagio', (storageValue) => {
// session can be an empty object
- const { rnd, new: isNew = false, vwSmplg, vwSmplgNxt, lastActivityTime, id, testName, testVersion, initiator } = _internal.getSessionFromLocalStorage(storageValue);
+ const { rnd, new: isNew = false, vwSmplg, vwSmplgNxt, lastActivityTime, id, testName, testVersion, initiator, pages } = _internal.getSessionFromLocalStorage(storageValue);
// isNew can be `true` if the session has been initialized by the A/B test snippet (external)
const isNewSess = (initiator === 'snippet') ? isNew : isNewSession(lastActivityTime);
data.session = {
rnd,
+ pages: pages || 1,
new: isNewSess, // legacy: `new` was used but the choosen name is not good.
// Don't use values if they are not defined.
...(vwSmplg !== undefined && { vwSmplg }),
@@ -685,6 +686,7 @@ function registerEventsForAdServers(config) {
* @property {number} vwSmplg - View sampling rate.
* @property {number} vwSmplgNxt - Next view sampling rate.
* @property {number} lastActivityTime - Last activity time.
+ * @property {number} pages - current number of pages seen.
*/
/**
diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js
index 925c0b3500e..dc2cab498ea 100644
--- a/modules/adfBidAdapter.js
+++ b/modules/adfBidAdapter.js
@@ -6,6 +6,7 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {deepAccess, deepClone, deepSetValue, mergeDeep, parseSizesInput, setOnAny} from '../src/utils.js';
import {config} from '../src/config.js';
import {Renderer} from '../src/Renderer.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const { getConfig } = config;
@@ -60,7 +61,7 @@ export const spec = {
const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net';
const tid = bidderRequest.ortb2?.source?.tid;
const test = setOnAny(validBidRequests, 'params.test');
- const currency = getConfig('currency.adServerCurrency');
+ const currency = getCurrencyFromBidderRequest(bidderRequest);
const cur = currency && [ currency ];
const eids = setOnAny(validBidRequests, 'userIdAsEids');
const schain = setOnAny(validBidRequests, 'schain');
@@ -75,8 +76,8 @@ export const spec = {
mediaType: '*'
}) : {};
- const bidfloor = floorInfo.floor;
- const bidfloorcur = floorInfo.currency;
+ const bidfloor = floorInfo?.floor;
+ const bidfloorcur = floorInfo?.currency;
const { mid, inv, mname } = bid.params;
const impExtData = bid.ortb2Imp?.ext?.data;
diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js
index 16375d92194..3ef9e495ea2 100644
--- a/modules/adgenerationBidAdapter.js
+++ b/modules/adgenerationBidAdapter.js
@@ -1,10 +1,10 @@
-import {deepAccess, getBidIdParameter} from '../src/utils.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {BANNER, NATIVE} from '../src/mediaTypes.js';
-import {config} from '../src/config.js';
-import {convertOrtbRequestToProprietaryNative} from '../src/native.js';
-import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js';
-import {escapeUnsafeChars} from '../libraries/htmlEscape/htmlEscape.js';
+import { escapeUnsafeChars } from '../libraries/htmlEscape/htmlEscape.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER, NATIVE } from '../src/mediaTypes.js';
+import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
+import { deepAccess, getBidIdParameter } from '../src/utils.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -61,7 +61,7 @@ export const spec = {
data = tryAppendQueryString(data, 't', 'json3');
data = tryAppendQueryString(data, 'transactionid', validReq.ortb2Imp?.ext?.tid);
data = tryAppendQueryString(data, 'sizes', getSizes(validReq));
- data = tryAppendQueryString(data, 'currency', getCurrencyType());
+ data = tryAppendQueryString(data, 'currency', getCurrencyType(bidderRequest));
data = tryAppendQueryString(data, 'pbver', '$prebid.version$');
data = tryAppendQueryString(data, 'sdkname', 'prebidjs');
data = tryAppendQueryString(data, 'adapterver', ADGENE_PREBID_VERSION);
@@ -94,7 +94,8 @@ export const spec = {
method: 'GET',
url: url,
data: data,
- bidRequest: validBidRequests[i]
+ bidRequest: validBidRequests[i],
+ bidderRequest
});
}
return serverRequests;
@@ -119,7 +120,7 @@ export const spec = {
height: body.h ? body.h : 1,
creativeId: body.creativeid || '',
dealId: body.dealid || '',
- currency: getCurrencyType(),
+ currency: getCurrencyFromBidderRequest(bidRequests.bidderRequest),
netRevenue: true,
ttl: body.ttl || 10,
};
@@ -304,9 +305,9 @@ function getSizes(validReq) {
/**
* @return {?string} USD or JPY
*/
-function getCurrencyType() {
- if (config.getConfig('currency.adServerCurrency') && config.getConfig('currency.adServerCurrency').toUpperCase() === 'USD') return 'USD';
- return 'JPY';
+function getCurrencyType(bidderRequest) {
+ const adServerCurrency = getCurrencyFromBidderRequest(bidderRequest) || ''
+ return adServerCurrency.toUpperCase() === 'USD' ? 'USD' : 'JPY'
}
/**
diff --git a/modules/adgridBidAdapter.js b/modules/adgridBidAdapter.js
index 17156280c0d..71c55a54395 100644
--- a/modules/adgridBidAdapter.js
+++ b/modules/adgridBidAdapter.js
@@ -1,14 +1,14 @@
import { _each, isEmpty, deepAccess } from '../src/utils.js';
import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
-import { BANNER } from '../src/mediaTypes.js';
+import { BANNER, VIDEO } from '../src/mediaTypes.js';
const BIDDER = Object.freeze({
CODE: 'adgrid',
HOST: 'https://api-prebid.adgrid.io',
REQUEST_METHOD: 'POST',
REQUEST_ENDPOINT: '/api/v1/auction',
- SUPPORTED_MEDIA_TYPES: [BANNER],
+ SUPPORTED_MEDIA_TYPES: [BANNER, VIDEO],
});
const CURRENCY = Object.freeze({
@@ -132,10 +132,19 @@ function interpretResponse(response, bidRequest) {
creativeId: adUnit.creativeId,
netRevenue: true,
currency: adUnit.currency || bidRequest.currency,
- mediaType: adUnit.mediaType,
- ad: adUnit.ad,
+ mediaType: adUnit.mediaType
};
+ if (adUnit.mediaType == 'video') {
+ if (adUnit.admUrl) {
+ bidResponse.vastUrl = adUnit.admUrl;
+ } else {
+ bidResponse.vastXml = adUnit.adm;
+ }
+ } else {
+ bidResponse.ad = adUnit.ad;
+ }
+
bidResponses.push(bidResponse);
});
@@ -158,6 +167,11 @@ function getBidData(bid) {
if (bid.mediaTypes.banner != null) {
bidData.mediaType = 'banner';
bidData.sizes = bid.mediaTypes.banner.sizes;
+ } else if (bid.mediaTypes.video != null) {
+ bidData.mediaType = 'video';
+ bidData.sizes = bid.mediaTypes.video.playerSize;
+ bidData.videoData = bid.mediaTypes.video;
+ bidData.videoParams = bid.params.video;
}
}
diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js
index 4997c92dffd..414d1ca28e9 100644
--- a/modules/adkernelBidAdapter.js
+++ b/modules/adkernelBidAdapter.js
@@ -84,7 +84,6 @@ export const spec = {
{code: 'unibots'},
{code: 'ergadx'},
{code: 'turktelekom'},
- {code: 'felixads'},
{code: 'motionspots'},
{code: 'sonic_twist'},
{code: 'displayioads'},
@@ -99,7 +98,11 @@ export const spec = {
{code: 'hyperbrainz'},
{code: 'voisetech'},
{code: 'global_sun'},
- {code: 'rxnetwork'}
+ {code: 'rxnetwork'},
+ {code: 'revbid'},
+ {code: 'spinx', gvlid: 1308},
+ {code: 'oppamedia'},
+ {code: 'pixelpluses', gvlid: 1209}
],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
@@ -263,7 +266,7 @@ function buildImps(bidRequest, secure) {
'tagid': bidRequest.adUnitCode
};
if (secure) {
- imp.secure = 1;
+ imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
}
var sizes = [];
let mediaTypes = bidRequest.mediaTypes;
diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js
index 78e76651177..2a3ec5499e6 100644
--- a/modules/admaticBidAdapter.js
+++ b/modules/admaticBidAdapter.js
@@ -1,8 +1,8 @@
-import {getValue, formatQS, logError, deepAccess, isArray, getBidIdParameter} from '../src/utils.js';
-import { registerBidder } from '../src/adapters/bidderFactory.js';
-import { config } from '../src/config.js';
-import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
import { Renderer } from '../src/Renderer.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
+import { deepAccess, formatQS, getBidIdParameter, getValue, isArray, logError } from '../src/utils.js';
import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js';
import { interpretNativeAd } from '../libraries/precisoUtils/bidNativeUtils.js';
@@ -23,7 +23,8 @@ export const spec = {
{code: 'admaticde', gvlid: 1281},
{code: 'pixad', gvlid: 1281},
{code: 'monetixads', gvlid: 1281},
- {code: 'netaddiction', gvlid: 1281}
+ {code: 'netaddiction', gvlid: 1281},
+ {code: 'adt', gvlid: 779}
],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
/**
@@ -54,7 +55,8 @@ export const spec = {
const bids = validBidRequests.map(buildRequestObject);
const ortb = bidderRequest.ortb2;
const networkId = getValue(validBidRequests[0].params, 'networkId');
- const host = getValue(validBidRequests[0].params, 'host');
+ let host = getValue(validBidRequests[0].params, 'host');
+ const currency = getCurrencyFromBidderRequest(bidderRequest) || 'TRY';
const bidderName = validBidRequests[0].bidder;
const payload = {
@@ -83,10 +85,7 @@ export const spec = {
tmax: parseInt(tmax)
};
- if (config.getConfig('currency.adServerCurrency')) {
- payload.ext.cur = config.getConfig('currency.adServerCurrency');
- }
-
+ payload.ext.cur = currency;
if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) {
const consentStr = (bidderRequest.gdprConsent.consentString)
? bidderRequest.gdprConsent.consentString.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') : '';
@@ -137,11 +136,15 @@ export const spec = {
case 'admaticde':
SYNC_URL = 'https://static.cdn.admatic.de/admaticde/sync.html';
break;
+ case 'adt':
+ SYNC_URL = 'https://static.cdn.adtarget.org/adt/sync.html';
+ break;
default:
SYNC_URL = 'https://static.cdn.admatic.com.tr/sync.html';
break;
}
+ host = host?.replace('https://', '')?.replace('http://', '')?.replace('/', '');
return { method: 'POST', url: `https://${host}/pb`, data: payload, options: { contentType: 'application/json' } };
}
},
@@ -150,7 +153,7 @@ export const spec = {
if (!hasSynced && syncOptions.iframeEnabled) {
// data is only assigned if params are available to pass to syncEndpoint
let params = getUserSyncParams(gdprConsent, uspConsent, gppConsent);
- params = Object.keys(params).length ? `?${formatQS(params)}` : '';
+ params = Object.keys(params).length ? `&${formatQS(params)}` : '';
hasSynced = true;
return {
diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js
index 4deeaee5206..1570a36c5f0 100644
--- a/modules/admixerBidAdapter.js
+++ b/modules/admixerBidAdapter.js
@@ -133,7 +133,7 @@ function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (_) {
return 0;
}
diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js
index fd24ccdeecb..3924537061c 100644
--- a/modules/adotBidAdapter.js
+++ b/modules/adotBidAdapter.js
@@ -1,11 +1,13 @@
-import {Renderer} from '../src/Renderer.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
-import {isArray, isBoolean, isFn, isPlainObject, isStr, logError, replaceAuctionPrice} from '../src/utils.js';
-import {find} from '../src/polyfill.js';
-import {config} from '../src/config.js';
-import {OUTSTREAM} from '../src/video.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { Renderer } from '../src/Renderer.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { config } from '../src/config.js';
+import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
+import { find } from '../src/polyfill.js';
+import { isArray, isBoolean, isFn, isPlainObject, isStr, logError, replaceAuctionPrice } from '../src/utils.js';
+import { OUTSTREAM } from '../src/video.js';
+import { NATIVE_ASSETS_IDS as NATIVE_ID_MAPPING, NATIVE_ASSETS as NATIVE_PLACEMENTS } from '../libraries/braveUtils/nativeAssets.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -24,6 +26,37 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
* @typedef {import('../src/adapters/bidderFactory.js').Imp} Imp
*/
+/**
+ * @typedef {Object} OpenRtbRequest
+ * @property {string} id - Unique request ID
+ * @property {Array} imp - List of impression objects
+ * @property {Site} site - Site information
+ * @property {Device} device - Device information
+ * @property {User} user - User information
+ * @property {object} regs - Regulatory data, including GDPR and COPPA
+ * @property {object} ext - Additional extensions, such as custom data for the bid request
+ * @property {number} at - Auction type, typically first-price or second-price
+ */
+
+/**
+ * @typedef {Object} OpenRtbBid
+ * @property {string} impid - ID of the impression this bid relates to
+ * @property {number} price - Bid price for the impression
+ * @property {string} adid - Ad ID for the bid
+ * @property {number} [crid] - Creative ID, if available
+ * @property {string} [dealid] - Deal ID if the bid is part of a private marketplace deal
+ * @property {object} [ext] - Additional bid-specific extensions, such as media type
+ * @property {string} [adm] - Ad markup if it’s directly included in the bid response
+ * @property {string} [nurl] - Notification URL to be called when the bid wins
+ */
+
+/**
+ * @typedef {Object} OpenRtbBidResponse
+ * @property {string} id - ID of the bid response
+ * @property {Array<{bid: Array}>} seatbid - Array of seat bids, each containing a list of bids
+ * @property {string} cur - Currency in which bid amounts are expressed
+ */
+
const BIDDER_CODE = 'adot';
const ADAPTER_VERSION = 'v2.0.0';
const GVLID = 272;
@@ -32,15 +65,6 @@ const BIDDER_URL = 'https://dsp.adotmob.com/headerbidding{PUBLISHER_PATH}/bidreq
const REQUIRED_VIDEO_PARAMS = ['mimes', 'protocols'];
const FIRST_PRICE = 1;
const IMP_BUILDER = { banner: buildBanner, video: buildVideo, native: buildNative };
-const NATIVE_PLACEMENTS = {
- title: { id: 1, name: 'title' },
- icon: { id: 2, type: 1, name: 'img' },
- image: { id: 3, type: 3, name: 'img' },
- sponsoredBy: { id: 4, name: 'data', type: 1 },
- body: { id: 5, name: 'data', type: 2 },
- cta: { id: 6, type: 12, name: 'data' }
-};
-const NATIVE_ID_MAPPING = { 1: 'title', 2: 'icon', 3: 'image', 4: 'sponsoredBy', 5: 'body', 6: 'cta' };
const OUTSTREAM_VIDEO_PLAYER_URL = 'https://adserver.adotmob.com/video/player.min.js';
const BID_RESPONSE_NET_REVENUE = true;
const BID_RESPONSE_TTL = 10;
@@ -287,7 +311,7 @@ function buildImpFromAdUnit(adUnit, bidderRequest) {
if (!mediaType) return null;
const media = IMP_BUILDER[mediaType](mediaTypes[mediaType], bidderRequest, adUnit)
- const currency = config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY;
+ const currency = getCurrencyFromBidderRequest(bidderRequest) || DEFAULT_CURRENCY;
const bidfloor = getMainFloor(adUnit, media.format, mediaType, currency);
return {
@@ -618,7 +642,7 @@ function getFloor(adUnit, size, mediaType, currency) {
const floorResult = adUnit.getFloor({ currency, mediaType, size });
- return floorResult.currency === currency ? floorResult.floor : 0;
+ return floorResult?.currency === currency ? floorResult?.floor : 0;
}
/**
diff --git a/modules/adpartnerBidAdapter.js b/modules/adpartnerBidAdapter.js
index 471a0bba64a..504809afa87 100644
--- a/modules/adpartnerBidAdapter.js
+++ b/modules/adpartnerBidAdapter.js
@@ -1,6 +1,5 @@
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import { buildUrl } from '../src/utils.js'
-import {ajax} from '../src/ajax.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { buildBidRequestsAndParams, postRequest, buildEndpointUrl } from '../libraries/mediaImpactUtils/index.js';
const BIDDER_CODE = 'adpartner';
export const ENDPOINT_PROTOCOL = 'https';
@@ -15,74 +14,25 @@ export const spec = {
},
buildRequests: function (validBidRequests, bidderRequest) {
- // TODO does it make sense to fall back to window.location.href?
const referer = bidderRequest?.refererInfo?.page || window.location.href;
- let bidRequests = [];
- let beaconParams = {
- tag: [],
- partner: [],
- sizes: [],
- referer: ''
- };
-
- validBidRequests.forEach(function(validBidRequest) {
- let bidRequestObject = {
- adUnitCode: validBidRequest.adUnitCode,
- sizes: validBidRequest.sizes,
- bidId: validBidRequest.bidId,
- referer: referer
- };
-
- if (parseInt(validBidRequest.params.unitId)) {
- bidRequestObject.unitId = parseInt(validBidRequest.params.unitId);
- beaconParams.tag.push(validBidRequest.params.unitId);
- }
-
- if (parseInt(validBidRequest.params.partnerId)) {
- bidRequestObject.unitId = 0;
- bidRequestObject.partnerId = parseInt(validBidRequest.params.partnerId);
- beaconParams.partner.push(validBidRequest.params.partnerId);
- }
-
- bidRequests.push(bidRequestObject);
+ // Use the common function to build bidRequests and beaconParams
+ const { bidRequests, beaconParams } = buildBidRequestsAndParams(validBidRequests, referer);
- beaconParams.sizes.push(spec.joinSizesToString(validBidRequest.sizes));
- beaconParams.referer = encodeURIComponent(referer);
- });
-
- if (beaconParams.partner.length > 0) {
- beaconParams.partner = beaconParams.partner.join(',');
- } else {
- delete beaconParams.partner;
- }
-
- beaconParams.tag = beaconParams.tag.join(',');
- beaconParams.sizes = beaconParams.sizes.join(',');
-
- let adPartnerRequestUrl = buildUrl({
- protocol: ENDPOINT_PROTOCOL,
- hostname: ENDPOINT_DOMAIN,
- pathname: ENDPOINT_PATH,
- search: beaconParams
- });
+ const adPartnerRequestUrl = buildEndpointUrl(
+ ENDPOINT_PROTOCOL,
+ ENDPOINT_DOMAIN,
+ ENDPOINT_PATH,
+ beaconParams
+ );
return {
method: 'POST',
url: adPartnerRequestUrl,
- data: JSON.stringify(bidRequests)
+ data: JSON.stringify(bidRequests),
};
},
- joinSizesToString: function(sizes) {
- let res = [];
- sizes.forEach(function(size) {
- res.push(size.join('x'));
- });
-
- return res.join('|');
- },
-
interpretResponse: function (serverResponse, bidRequest) {
const validBids = JSON.parse(bidRequest.data);
@@ -91,15 +41,12 @@ export const spec = {
}
return validBids
- .map(bid => ({
- bid: bid,
- ad: serverResponse.body[bid.adUnitCode]
- }))
+ .map(bid => ({ bid: bid, ad: serverResponse.body[bid.adUnitCode] }))
.filter(item => item.ad)
.map(item => spec.adResponse(item.bid, item.ad));
},
- adResponse: function(bid, ad) {
+ adResponse: function (bid, ad) {
const bidObject = {
requestId: bid.bidId,
ad: ad.ad,
@@ -110,38 +57,30 @@ export const spec = {
creativeId: ad.creativeId,
netRevenue: ad.netRevenue,
currency: ad.currency,
- winNotification: ad.winNotification
- }
-
- bidObject.meta = {};
- if (ad.adomain && ad.adomain.length > 0) {
- bidObject.meta.advertiserDomains = ad.adomain;
- }
+ winNotification: ad.winNotification,
+ meta: ad.adomain && ad.adomain.length > 0 ? { advertiserDomains: ad.adomain } : {},
+ };
return bidObject;
},
- onBidWon: function(data) {
- data.winNotification.forEach(function(unitWon) {
- let adPartnerBidWonUrl = buildUrl({
- protocol: ENDPOINT_PROTOCOL,
- hostname: ENDPOINT_DOMAIN,
- pathname: unitWon.path
- });
+ onBidWon: function (data) {
+ data.winNotification.forEach(function (unitWon) {
+ const adPartnerBidWonUrl = buildEndpointUrl(
+ ENDPOINT_PROTOCOL,
+ ENDPOINT_DOMAIN,
+ unitWon.path
+ );
if (unitWon.method === 'POST') {
- spec.postRequest(adPartnerBidWonUrl, JSON.stringify(unitWon.data));
+ postRequest(adPartnerBidWonUrl, JSON.stringify(unitWon.data));
}
});
return true;
},
- postRequest(endpoint, data) {
- ajax(endpoint, null, data, {method: 'POST'});
- },
-
- getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {
+ getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) {
const syncs = [];
if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) {
@@ -205,7 +144,6 @@ export const spec = {
return syncs;
},
-
-}
+};
registerBidder(spec);
diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js
index 5bce315f572..7ac8e4fb728 100644
--- a/modules/adriverBidAdapter.js
+++ b/modules/adriverBidAdapter.js
@@ -1,5 +1,5 @@
// ADRIVER BID ADAPTER for Prebid 1.13
-import {logInfo, getWindowLocation, _each, getBidIdParameter} from '../src/utils.js';
+import {logInfo, getWindowLocation, _each, getBidIdParameter, isPlainObject} from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
@@ -188,12 +188,12 @@ function _getFloor(bid, currencyPar, sizes) {
size: isSize ? sizes : '*'
});
- if (typeof floorInfo === 'object' &&
- !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) &&
+ !isNaN(parseFloat(floorInfo?.floor))) {
floor = floorInfo.floor;
}
- if (typeof floorInfo === 'object' && floorInfo.currency) {
+ if (isPlainObject(floorInfo) && floorInfo.currency) {
currencyResult = floorInfo.currency;
}
}
diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js
index a1dec5a420f..0faf740ac54 100644
--- a/modules/adtargetBidAdapter.js
+++ b/modules/adtargetBidAdapter.js
@@ -12,6 +12,7 @@ const syncsCache = {};
export const spec = {
code: BIDDER_CODE,
+ gvlid: 779,
supportedMediaTypes: [VIDEO, BANNER],
isBidRequestValid: function (bid) {
return !!deepAccess(bid, 'params.aid');
diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js
index 955e908f000..18cbd1e6ae7 100644
--- a/modules/adtrgtmeBidAdapter.js
+++ b/modules/adtrgtmeBidAdapter.js
@@ -55,7 +55,7 @@ function extractUserSyncUrls(syncOptions, pixels) {
}
function isSecure(bid) {
- return deepAccess(bid, 'params.bidOverride.imp.secure') || (document.location.protocol === 'https:') ? 1 : 0;
+ return deepAccess(bid, 'params.bidOverride.imp.secure') ?? deepAccess(bid, 'ortb2Imp.secure') ?? 1;
};
function getMediaType(bid) {
@@ -90,7 +90,7 @@ function getFloorModuleData(bid) {
mediaType: BANNER,
size: '*'
};
- return (isFn(bid.getFloor)) ? bid.getFloor(getFloorRequestObject) : false;
+ return (isFn(bid.getFloor)) ? (bid.getFloor(getFloorRequestObject) || {}) : false;
};
function generateOpenRtbObject(bidderRequest, bid) {
diff --git a/modules/adverxoBidAdapter.js b/modules/adverxoBidAdapter.js
new file mode 100644
index 00000000000..f293d7e01a6
--- /dev/null
+++ b/modules/adverxoBidAdapter.js
@@ -0,0 +1,356 @@
+import * as utils from '../src/utils.js';
+import {registerBidder} from '../src/adapters/bidderFactory.js';
+import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js';
+import {ortbConverter as OrtbConverter} from '../libraries/ortbConverter/converter.js';
+import {Renderer} from '../src/Renderer.js';
+import {deepAccess, deepSetValue} from '../src/utils.js';
+import {config} from '../src/config.js';
+
+/**
+ * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
+ * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
+ * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest
+ * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse
+ * @typedef {import('../src/auction.js').BidderRequest} BidderRequest
+ * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions
+ * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync
+ */
+
+const BIDDER_CODE = 'adverxo';
+
+const ALIASES = [
+ {code: 'adport', skipPbsAliasing: true},
+ {code: 'bidsmind', skipPbsAliasing: true},
+ {code: 'mobupps', skipPbsAliasing: true}
+];
+
+const AUCTION_URLS = {
+ adverxo: 'js.pbsadverxo.com',
+ adport: 'diclotrans.com',
+ bidsmind: 'egrevirda.com',
+ mobupps: 'traffhb.com'
+};
+
+const ENDPOINT_URL_AD_UNIT_PLACEHOLDER = '{AD_UNIT}';
+const ENDPOINT_URL_AUTH_PLACEHOLDER = '{AUTH}';
+const ENDPOINT_URL_HOST_PLACEHOLDER = '{HOST}';
+
+const ENDPOINT_URL = `https://${ENDPOINT_URL_HOST_PLACEHOLDER}/pickpbs?id=${ENDPOINT_URL_AD_UNIT_PLACEHOLDER}&auth=${ENDPOINT_URL_AUTH_PLACEHOLDER}`;
+
+const ORTB_MTYPES = {
+ 1: BANNER,
+ 2: VIDEO,
+ 4: NATIVE
+};
+
+const USYNC_TYPES = {
+ IFRAME: 'iframe',
+ REDIRECT: 'image'
+};
+
+const DEFAULT_CURRENCY = 'USD';
+
+const ortbConverter = OrtbConverter({
+ context: {
+ netRevenue: true,
+ ttl: 60,
+ },
+ request: function request(buildRequest, imps, bidderRequest, context) {
+ const request = buildRequest(imps, bidderRequest, context);
+
+ utils.deepSetValue(request, 'device.ip', 'caller');
+ utils.deepSetValue(request, 'ext.avx_add_vast_url', 1);
+
+ const eids = deepAccess(bidderRequest, 'bids.0.userIdAsEids');
+
+ if (eids && eids.length) {
+ deepSetValue(request, 'user.ext.eids', eids);
+ }
+
+ return request;
+ },
+ imp(buildImp, bidRequest, context) {
+ const imp = buildImp(bidRequest, context);
+ const floor = adverxoUtils.getBidFloor(bidRequest);
+
+ if (floor) {
+ imp.bidfloor = floor;
+ imp.bidfloorcur = DEFAULT_CURRENCY;
+ }
+
+ return imp;
+ },
+ bidResponse: function (buildBidResponse, bid, context) {
+ bid.adm = bid.adm.replaceAll(`\${AUCTION_PRICE}`, bid.price);
+
+ if (FEATURES.NATIVE && ORTB_MTYPES[bid.mtype] === NATIVE) {
+ if (typeof bid?.adm === 'string') {
+ bid.adm = JSON.parse(bid.adm);
+ }
+
+ if (bid?.adm?.native) {
+ bid.adm = bid.adm.native;
+ }
+ }
+
+ const result = buildBidResponse(bid, context);
+
+ if (FEATURES.VIDEO) {
+ if (bid?.ext?.avx_vast_url) {
+ result.vastUrl = bid.ext.avx_vast_url;
+ }
+
+ if (bid?.ext?.avx_video_renderer_url) {
+ result.avxVideoRendererUrl = bid.ext.avx_video_renderer_url;
+ }
+ }
+
+ return result;
+ }
+});
+
+const userSyncUtils = {
+ buildUsyncParams: function (gdprConsent, uspConsent, gppConsent) {
+ const params = [];
+
+ if (gdprConsent) {
+ params.push('gdpr=' + (gdprConsent.gdprApplies ? 1 : 0));
+ params.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''));
+ }
+
+ if (config.getConfig('coppa') === true) {
+ params.push('coppa=1');
+ }
+
+ if (uspConsent) {
+ params.push('us_privacy=' + encodeURIComponent(uspConsent));
+ }
+
+ if (gppConsent?.gppString && gppConsent?.applicableSections?.length) {
+ params.push('gpp=' + encodeURIComponent(gppConsent.gppString));
+ params.push('gpp_sid=' + encodeURIComponent(gppConsent?.applicableSections?.join(',')));
+ }
+
+ return params.length ? params.join('&') : '';
+ }
+};
+
+const videoUtils = {
+ createOutstreamVideoRenderer: function (bid) {
+ const renderer = Renderer.install({
+ id: bid.bidId,
+ url: bid.avxVideoRendererUrl,
+ loaded: false,
+ adUnitCode: bid.adUnitCode
+ });
+
+ try {
+ renderer.setRender(this.outstreamRender.bind(this));
+ } catch (err) {
+ utils.logWarn('Prebid Error calling setRender on renderer', err);
+ }
+
+ return renderer;
+ },
+
+ outstreamRender: function (bid, doc) {
+ bid.renderer.push(() => {
+ const win = (doc) ? doc.defaultView : window;
+
+ win.adxVideoRenderer.renderAd({
+ targetId: bid.adUnitCode,
+ adResponse: {content: bid.vastXml}
+ });
+ });
+ }
+};
+
+const adverxoUtils = {
+ buildAuctionUrl: function (bidderCode, host, adUnitId, adUnitAuth) {
+ const auctionUrl = host || AUCTION_URLS[bidderCode];
+
+ return ENDPOINT_URL
+ .replace(ENDPOINT_URL_HOST_PLACEHOLDER, auctionUrl)
+ .replace(ENDPOINT_URL_AD_UNIT_PLACEHOLDER, adUnitId)
+ .replace(ENDPOINT_URL_AUTH_PLACEHOLDER, adUnitAuth);
+ },
+
+ groupBidRequestsByAdUnit: function (bidRequests) {
+ const groupedBidRequests = new Map();
+
+ bidRequests.forEach(bidRequest => {
+ const adUnit = {
+ host: bidRequest.params.host,
+ id: bidRequest.params.adUnitId,
+ auth: bidRequest.params.auth,
+ };
+
+ if (!groupedBidRequests.get(adUnit)) {
+ groupedBidRequests.set(adUnit, []);
+ }
+
+ groupedBidRequests.get(adUnit).push(bidRequest);
+ });
+
+ return groupedBidRequests;
+ },
+
+ getBidFloor: function (bid) {
+ if (utils.isFn(bid.getFloor)) {
+ const floor = bid.getFloor({
+ currency: DEFAULT_CURRENCY,
+ mediaType: '*',
+ size: '*',
+ });
+
+ if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === DEFAULT_CURRENCY) {
+ return floor.floor;
+ }
+ }
+
+ return null;
+ }
+};
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [BANNER, NATIVE, VIDEO],
+ aliases: ALIASES,
+
+ /**
+ * Determines whether the given bid request is valid.
+ *
+ * @param {BidRequest} bid The bid params to validate.
+ * @return {boolean} True if this is a valid bid, and false otherwise.
+ */
+ isBidRequestValid: function (bid) {
+ if (!utils.isPlainObject(bid.params) || !Object.keys(bid.params).length) {
+ utils.logWarn('Adverxo Bid Adapter: bid params must be provided.');
+ return false;
+ }
+
+ if (!bid.params.adUnitId || typeof bid.params.adUnitId !== 'number') {
+ utils.logWarn('Adverxo Bid Adapter: adUnitId bid param is required and must be a number');
+ return false;
+ }
+
+ if (!bid.params.auth || typeof bid.params.auth !== 'string') {
+ utils.logWarn('Adverxo Bid Adapter: auth bid param is required and must be a string');
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {BidRequest[]} validBidRequests an array of bids
+ * @param {BidderRequest} bidderRequest an array of bids
+ * @return {ServerRequest} Info describing the request to the server.
+ */
+ buildRequests: function (validBidRequests, bidderRequest) {
+ const result = [];
+
+ const bidRequestsByAdUnit = adverxoUtils.groupBidRequestsByAdUnit(validBidRequests);
+
+ bidRequestsByAdUnit.forEach((adUnitBidRequests, adUnit) => {
+ const ortbRequest = ortbConverter.toORTB({
+ bidRequests: adUnitBidRequests,
+ bidderRequest
+ });
+
+ result.push({
+ method: 'POST',
+ url: adverxoUtils.buildAuctionUrl(bidderRequest.bidderCode, adUnit.host, adUnit.id, adUnit.auth),
+ data: ortbRequest,
+ bids: adUnitBidRequests
+ });
+ });
+
+ return result;
+ },
+
+ /**
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {ServerResponse} serverResponse A successful response from the server.
+ * @param {BidRequest} bidRequest Adverxo bidRequest
+ * @return {Bid[]} An array of bids which were nested inside the server.
+ */
+ interpretResponse: function (serverResponse, bidRequest) {
+ if (!serverResponse || !bidRequest) {
+ return [];
+ }
+
+ const bids = ortbConverter.fromORTB({
+ response: serverResponse.body,
+ request: bidRequest.data,
+ }).bids;
+
+ return bids.map((bid) => {
+ const thisRequest = utils.getBidRequest(bid.requestId, [bidRequest]);
+ const context = utils.deepAccess(thisRequest, 'mediaTypes.video.context');
+
+ if (FEATURES.VIDEO && bid.mediaType === 'video' && context === 'outstream') {
+ bid.renderer = videoUtils.createOutstreamVideoRenderer(bid);
+ }
+
+ return bid;
+ });
+ },
+
+ /**
+ * Register the user sync pixels which should be dropped after the auction.
+ *
+ * @param {SyncOptions} syncOptions Which user syncs are allowed?
+ * @param {ServerResponse[]} responses List of server's responses.
+ * @param {*} gdprConsent
+ * @param {*} uspConsent
+ * @param {*} gppConsent
+ * @return {UserSync[]} The user syncs which should be dropped.
+ */
+ getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent, gppConsent) => {
+ if (!responses || responses.length === 0 || (!syncOptions.pixelEnabled && !syncOptions.iframeEnabled)) {
+ return [];
+ }
+
+ const privacyParams = userSyncUtils.buildUsyncParams(gdprConsent, uspConsent, gppConsent);
+ const syncType = syncOptions.iframeEnabled ? USYNC_TYPES.IFRAME : USYNC_TYPES.REDIRECT;
+
+ const result = [];
+
+ for (const response of responses) {
+ const syncUrls = response.body?.ext?.avx_usync;
+
+ if (!syncUrls || syncUrls.length === 0) {
+ continue;
+ }
+
+ for (const url of syncUrls) {
+ let finalUrl = url;
+
+ if (!finalUrl.includes('?')) {
+ finalUrl += '?';
+ } else {
+ finalUrl += '&';
+ }
+
+ finalUrl += 'type=' + syncType;
+
+ if (privacyParams.length !== 0) {
+ finalUrl += `&${privacyParams}`;
+ }
+
+ result.push({
+ type: syncType,
+ url: finalUrl
+ });
+ }
+ }
+
+ return result;
+ }
+}
+
+registerBidder(spec);
diff --git a/modules/adverxoBidAdapter.md b/modules/adverxoBidAdapter.md
new file mode 100644
index 00000000000..ae6072d2738
--- /dev/null
+++ b/modules/adverxoBidAdapter.md
@@ -0,0 +1,101 @@
+# Overview
+
+```
+Module Name: Adverxo Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: developer@adverxo.com
+```
+
+# Description
+
+Module that connects to Adverxo to fetch bids.
+Banner, native and video formats are supported.
+
+# Bid Parameters
+
+| Name | Required? | Description | Example | Type |
+|------------|-----------|-------------------------------------------------------------------|----------------------------------------------|-----------|
+| `host` | No | Ad network host. | `prebidTest.adverxo.com` | `String` |
+| `adUnitId` | Yes | Unique identifier for the ad unit in Adverxo platform. | `413` | `Integer` |
+| `auth` | Yes | Authentication token provided by Adverxo platform for the AdUnit. | `'61336d75e414c77c367ce5c47c2599ce80a8x32b'` | `String` |
+
+# Test Parameters
+
+```javascript
+var adUnits = [
+ {
+ code: 'banner-ad-div',
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [400, 300],
+ [320, 50]
+ ]
+ }
+ },
+ bids: [{
+ bidder: 'adverxo',
+ params: {
+ host: 'example.com',
+ adUnitId: 1,
+ auth: '61336e753414c77c367deac47c2595ce80a8032b'
+ }
+ }]
+ },
+ {
+ code: 'native-ad-div',
+ mediaTypes: {
+ native: {
+ image: {
+ required: true,
+ sizes: [400, 300]
+ },
+ title: {
+ required: true,
+ len: 75
+ },
+ body: {
+ required: false,
+ len: 200
+ },
+ cta: {
+ required: false,
+ len: 75
+ },
+ sponsoredBy: {
+ required: false
+ }
+ }
+ },
+ bids: [{
+ bidder: 'adverxo',
+ params: {
+ host: 'example.com',
+ adUnitId: 2,
+ auth: '9a640dfccc3381e71fxc29ffd4a72wabd29g9d86'
+ }
+ }]
+ },
+ {
+ code: 'video-div',
+ mediaTypes: {
+ video: {
+ playerSize: [640, 480],
+ context: 'outstream',
+ mimes: ['video/mp4'],
+ maxduration: 30,
+ skip: 1
+ }
+ },
+ bids: [{
+ bidder: 'adverxo',
+ params: {
+ host: 'example.com',
+ adUnitId: 3,
+ auth: '1ax23d9621f21da28a2eab6f79bd5fbcf4d037c1'
+ }
+ }]
+ }
+];
+
+```
diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js
index c6dde0d3f6c..a0e99572809 100644
--- a/modules/adxcgBidAdapter.js
+++ b/modules/adxcgBidAdapter.js
@@ -122,7 +122,7 @@ const converter = ortbConverter({
};
}
- imp.secure = Number(window.location.protocol === 'https:');
+ imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
if (!imp.bidfloor && bidRequest.params.bidFloor) {
imp.bidfloor = bidRequest.params.bidFloor;
diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js
index 146e1d3b24a..fce5d1ae000 100644
--- a/modules/adyoulikeBidAdapter.js
+++ b/modules/adyoulikeBidAdapter.js
@@ -255,7 +255,7 @@ function getFloor(bidRequest, size, mediaType) {
size: [ size.width, size.height ]
});
- if (!isNaN(bidFloors.floor) && (bidFloors.currency === CURRENCY)) {
+ if (!isNaN(bidFloors?.floor) && (bidFloors?.currency === CURRENCY)) {
return bidFloors.floor;
}
}
diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js
index df260958104..508c4d6a0c7 100644
--- a/modules/amxBidAdapter.js
+++ b/modules/amxBidAdapter.js
@@ -142,7 +142,7 @@ function getFloor(bid) {
size: '*',
bidRequest: bid,
});
- return floor.floor;
+ return floor?.floor;
} catch (e) {
logError('call to getFloor failed: ', e);
return DEFAULT_MIN_FLOOR;
diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js
index 9a58308278b..e5baac1add0 100644
--- a/modules/aniviewBidAdapter.js
+++ b/modules/aniviewBidAdapter.js
@@ -31,7 +31,6 @@ const converter = ortbConverter({
imp(buildImp, bidRequest, context) {
const { mediaType } = context;
const imp = buildImp(bidRequest, context);
- const isVideo = mediaType === VIDEO;
const isBanner = mediaType === BANNER;
const { width, height } = getSize(context, bidRequest);
const floor = getFloor(bidRequest, { width, height }, mediaType);
@@ -43,14 +42,7 @@ const converter = ortbConverter({
imp.bidfloorcur = DEFAULT_CURRENCY;
}
- if (isVideo) {
- deepSetValue(imp, `ext.${BIDDER_CODE}`, {
- AV_WIDTH: width,
- AV_HEIGHT: height,
- bidWidth: width,
- bidHeight: height,
- });
- } else if (isBanner) {
+ if (isBanner) {
// TODO: remove once serving will be fixed
deepSetValue(imp, 'banner', { w: width, h: height });
}
diff --git a/modules/appushBidAdapter.js b/modules/appushBidAdapter.js
index 67557aed10c..ec742120582 100644
--- a/modules/appushBidAdapter.js
+++ b/modules/appushBidAdapter.js
@@ -85,7 +85,7 @@ function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (err) {
logError(err);
return 0;
diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js
index a4a6c78566e..08612757de1 100644
--- a/modules/asoBidAdapter.js
+++ b/modules/asoBidAdapter.js
@@ -17,7 +17,8 @@ export const spec = {
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
aliases: [
{code: 'bcmint'},
- {code: 'bidgency'}
+ {code: 'bidgency'},
+ {code: 'kuantyx'}
],
isBidRequestValid: bid => {
@@ -106,7 +107,7 @@ const converter = ortbConverter({
const imp = buildImp(bidRequest, context);
imp.tagid = bidRequest.adUnitCode;
- imp.secure = Number(window.location.protocol === 'https:');
+ imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
return imp;
},
diff --git a/modules/audiencerunBidAdapter.js b/modules/audiencerunBidAdapter.js
index 92a4343b3ed..df3bbda6a53 100644
--- a/modules/audiencerunBidAdapter.js
+++ b/modules/audiencerunBidAdapter.js
@@ -1,3 +1,7 @@
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { config } from '../src/config.js';
+import { BANNER } from '../src/mediaTypes.js';
import {
_each,
deepAccess,
@@ -8,9 +12,6 @@ import {
logError,
triggerPixel,
} from '../src/utils.js';
-import {config} from '../src/config.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {BANNER} from '../src/mediaTypes.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -47,7 +48,7 @@ function getBidFloor(bid) {
mediaType: BANNER,
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (_) {
return 0;
}
@@ -136,7 +137,7 @@ export const spec = {
referer: deepAccess(bidderRequest, 'refererInfo.topmostLocation'),
// TODO: please do not send internal data structures over the network
refererInfo: deepAccess(bidderRequest, 'refererInfo.legacy'),
- currencyCode: config.getConfig('currency.adServerCurrency'),
+ currencyCode: getCurrencyFromBidderRequest(bidderRequest),
timeout: config.getConfig('bidderTimeout'),
bids,
};
diff --git a/modules/axonixBidAdapter.js b/modules/axonixBidAdapter.js
index b1ccef155de..2eefb617636 100644
--- a/modules/axonixBidAdapter.js
+++ b/modules/axonixBidAdapter.js
@@ -21,7 +21,7 @@ function getBidFloor(bidRequest) {
});
}
- return floorInfo.floor || 0;
+ return floorInfo?.floor || 0;
}
function getPageUrl(bidRequest, bidderRequest) {
diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js
index 5237f3d7573..a24579af9a9 100644
--- a/modules/beopBidAdapter.js
+++ b/modules/beopBidAdapter.js
@@ -1,16 +1,18 @@
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { getRefererInfo } from '../src/refererDetection.js';
import {
buildUrl,
- deepAccess, getBidIdParameter,
+ deepAccess, generateUUID, getBidIdParameter,
getValue,
isArray,
+ isPlainObject,
logInfo,
logWarn,
triggerPixel
} from '../src/utils.js';
-import {getRefererInfo} from '../src/refererDetection.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {config} from '../src/config.js';
-import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js';
+import { getStorageManager } from '../src/storageManager.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
@@ -20,9 +22,11 @@ import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js';
const BIDDER_CODE = 'beop';
const ENDPOINT_URL = 'https://hb.beop.io/bid';
+const COOKIE_NAME = 'beopid';
const TCF_VENDOR_ID = 666;
const validIdRegExp = /^[0-9a-fA-F]{24}$/
+const storage = getStorageManager({bidderCode: BIDDER_CODE});
export const spec = {
code: BIDDER_CODE,
@@ -52,7 +56,7 @@ export const spec = {
* @return ServerRequest Info describing the request to the BeOp's server
*/
buildRequests: function(validBidRequests, bidderRequest) {
- const slots = validBidRequests.map(beOpRequestSlotsMaker);
+ const slots = validBidRequests.map((bid) => beOpRequestSlotsMaker(bid, bidderRequest));
const firstPartyData = bidderRequest.ortb2 || {};
const psegs = firstPartyData.user?.ext?.permutive || firstPartyData.user?.ext?.data?.permutive || [];
const userBpSegs = firstPartyData.user?.ext?.bpsegs || firstPartyData.user?.ext?.data?.bpsegs || [];
@@ -63,6 +67,19 @@ export const spec = {
const kwdsFromRequest = firstSlot.kwds;
let keywords = getAllOrtbKeywords(bidderRequest.ortb2, kwdsFromRequest);
+ let beopid = '';
+ if (storage.cookiesAreEnabled) {
+ beopid = storage.getCookie(COOKIE_NAME, undefined);
+ if (!beopid) {
+ beopid = generateUUID();
+ let expirationDate = new Date();
+ expirationDate.setTime(expirationDate.getTime() + 86400 * 183 * 1000);
+ storage.setCookie(COOKIE_NAME, beopid, expirationDate.toUTCString());
+ }
+ } else {
+ storage.setCookie(COOKIE_NAME, '', 0);
+ }
+
const payloadObject = {
at: new Date().toString(),
nid: firstSlot.nid,
@@ -73,6 +90,7 @@ export const spec = {
lang: (window.navigator.language || window.navigator.languages[0]),
kwds: keywords,
dbg: false,
+ fg: beopid,
slts: slots,
is_amp: deepAccess(bidderRequest, 'referrerInfo.isAmp'),
gdpr_applies: gdpr ? gdpr.gdprApplies : false,
@@ -141,13 +159,13 @@ function buildTrackingParams(data, info, value) {
};
}
-function beOpRequestSlotsMaker(bid) {
+function beOpRequestSlotsMaker(bid, bidderRequest) {
const bannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes');
- const publisherCurrency = config.getConfig('currency.adServerCurrency') || getValue(bid.params, 'currency') || 'EUR';
+ const publisherCurrency = getCurrencyFromBidderRequest(bidderRequest) || getValue(bid.params, 'currency') || 'EUR';
let floor;
if (typeof bid.getFloor === 'function') {
const floorInfo = bid.getFloor({currency: publisherCurrency, mediaType: 'banner', size: [1, 1]});
- if (typeof floorInfo === 'object' && floorInfo.currency === publisherCurrency && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === publisherCurrency && !isNaN(parseFloat(floorInfo.floor))) {
floor = parseFloat(floorInfo.floor);
}
}
diff --git a/modules/biddoBidAdapter.js b/modules/biddoBidAdapter.js
index cf39c572629..6bfa0ac6ef8 100644
--- a/modules/biddoBidAdapter.js
+++ b/modules/biddoBidAdapter.js
@@ -1,5 +1,6 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
+import { buildBannerRequests, interpretBannerResponse } from '../libraries/biddoInvamiaUtils/index.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -12,86 +13,15 @@ const ENDPOINT_URL = 'https://ad.adopx.net/delivery/impress';
export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER],
- /**
- * Determines whether or not the given bid request is valid.
- *
- * @param {BidRequest} bidRequest The bid request params to validate.
- * @return boolean True if this is a valid bid request, and false otherwise.
- */
- isBidRequestValid: function(bidRequest) {
+ isBidRequestValid: function (bidRequest) {
return !!bidRequest.params.zoneId;
},
- /**
- * Make a server request from the list of BidRequests.
- *
- * @param {Array} validBidRequests an array of bid requests
- * @return ServerRequest Info describing the request to the server.
- */
- buildRequests: function(validBidRequests) {
- let serverRequests = [];
-
- validBidRequests.forEach(bidRequest => {
- const sizes = bidRequest.mediaTypes.banner.sizes;
-
- sizes.forEach(([width, height]) => {
- bidRequest.params.requestedSizes = [width, height];
-
- const payload = {
- ctype: 'div',
- pzoneid: bidRequest.params.zoneId,
- width,
- height,
- };
-
- const payloadString = Object.keys(payload).map(k => k + '=' + encodeURIComponent(payload[k])).join('&');
-
- serverRequests.push({
- method: 'GET',
- url: ENDPOINT_URL,
- data: payloadString,
- bidderRequest: bidRequest,
- });
- });
- });
-
- return serverRequests;
+ buildRequests: function (validBidRequests) {
+ return validBidRequests.flatMap((bidRequest) => buildBannerRequests(bidRequest, ENDPOINT_URL));
},
- /**
- * Unpack the response from the server into a list of bids.
- *
- * @param {ServerResponse} serverResponse A successful response from the server.
- * @param {BidRequest} bidderRequest A matched bid request for this response.
- * @return Array An array of bids which were nested inside the server.
- */
- interpretResponse: function(serverResponse, {bidderRequest}) {
- const response = serverResponse.body;
- const bidResponses = [];
-
- if (response && response.template && response.template.html) {
- const {bidId} = bidderRequest;
- const [width, height] = bidderRequest.params.requestedSizes;
-
- const bidResponse = {
- requestId: bidId,
- cpm: response.hb.cpm,
- creativeId: response.banner.hash,
- currency: 'USD',
- netRevenue: response.hb.netRevenue,
- ttl: 600,
- ad: response.template.html,
- mediaType: 'banner',
- meta: {
- advertiserDomains: response.hb.adomains || [],
- },
- width,
- height,
- };
-
- bidResponses.push(bidResponse);
- }
-
- return bidResponses;
+ interpretResponse: function (serverResponse, { bidderRequest }) {
+ return interpretBannerResponse(serverResponse, bidderRequest);
},
-}
+};
registerBidder(spec);
diff --git a/modules/bidmaticBidAdapter.js b/modules/bidmaticBidAdapter.js
index 8c22d70c632..6c88a3f1932 100644
--- a/modules/bidmaticBidAdapter.js
+++ b/modules/bidmaticBidAdapter.js
@@ -3,9 +3,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { replaceAuctionPrice, isNumber, deepAccess, isFn } from '../src/utils.js';
-export const END_POINT = 'https://adapter.bidmatic.io/ortb-client';
+const HOST = 'https://adapter.bidmatic.io';
const BIDDER_CODE = 'bidmatic';
const DEFAULT_CURRENCY = 'USD';
+export const SYNC_URL = `${HOST}/sync.html`;
+export const END_POINT = `${HOST}/ortb-client`;
export const converter = ortbConverter({
context: {
@@ -59,6 +61,33 @@ export const converter = ortbConverter({
}
});
+const PROCESSED_SOURCES = {};
+
+export function createUserSyncs(processedSources, syncOptions, gdprConsent, uspConsent, gppConsent) {
+ if (syncOptions?.iframeEnabled) {
+ return Object.entries(processedSources)
+ .filter(([_, syncMade]) => syncMade === 0)
+ .map(([sourceId]) => {
+ processedSources[sourceId] = 1
+
+ let url = `${SYNC_URL}?aid=${sourceId}`
+ if (gdprConsent && gdprConsent.gdprApplies) {
+ url += `&gdpr=${+(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`
+ }
+ if (uspConsent) {
+ url += `&usp=${uspConsent}`;
+ }
+ if (gppConsent) {
+ url += `&gpp=${gppConsent.gppString}&gpp_sid=${gppConsent.applicableSections?.toString()}`
+ }
+ return {
+ type: 'iframe',
+ url
+ };
+ })
+ }
+}
+
export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER, VIDEO],
@@ -66,7 +95,9 @@ export const spec = {
isBidRequestValid: function (bid) {
return isNumber(deepAccess(bid, 'params.source'))
},
-
+ getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) {
+ return createUserSyncs(PROCESSED_SOURCES, syncOptions, gdprConsent, uspConsent, gppConsent);
+ },
buildRequests: function (validBidRequests, bidderRequest) {
const requestsBySource = validBidRequests.reduce((acc, bidRequest) => {
acc[bidRequest.params.source] = acc[bidRequest.params.source] || [];
@@ -75,6 +106,9 @@ export const spec = {
}, {});
return Object.entries(requestsBySource).map(([source, bidRequests]) => {
+ if (!PROCESSED_SOURCES[source]) {
+ PROCESSED_SOURCES[source] = 0;
+ }
const data = converter.toORTB({ bidRequests, bidderRequest });
const url = new URL(END_POINT);
url.searchParams.append('source', source);
diff --git a/modules/bidmaticBidAdapter.md b/modules/bidmaticBidAdapter.md
index d248b386ea3..242d7d9e77b 100644
--- a/modules/bidmaticBidAdapter.md
+++ b/modules/bidmaticBidAdapter.md
@@ -24,3 +24,9 @@ var adUnits = [{
}]
}]
```
+
+
+# Testing
+```gulp test-only --file=./test/spec/modules/bidmaticBidAdapter_spec.js```
+```gulp test-coverage --file=./test/spec/modules/bidmaticBidAdapter_spec.js```
+```gulp view-coverage```
diff --git a/modules/bidtheatreBidAdapter.js b/modules/bidtheatreBidAdapter.js
new file mode 100644
index 00000000000..8fb3dc2fd3b
--- /dev/null
+++ b/modules/bidtheatreBidAdapter.js
@@ -0,0 +1,117 @@
+import { ortbConverter } from '../libraries/ortbConverter/converter.js'
+import { BANNER, VIDEO } from '../src/mediaTypes.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { deepSetValue, logError, replaceAuctionPrice } from '../src/utils.js';
+import { getStorageManager } from '../src/storageManager.js';
+
+const GVLID = 30;
+export const BIDDER_CODE = 'bidtheatre';
+export const ENDPOINT_URL = 'https://prebidjs-bids.bidtheatre.net/prebidjsbid';
+const METHOD = 'POST';
+const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO];
+export const DEFAULT_CURRENCY = 'USD';
+const BIDTHEATRE_COOKIE_NAME = '__kuid';
+const storage = getStorageManager({bidderCode: BIDDER_CODE});
+
+const converter = ortbConverter({
+ context: {
+ netRevenue: true,
+ ttl: 120,
+ currency: DEFAULT_CURRENCY
+ }
+});
+
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: SUPPORTED_MEDIA_TYPES,
+ gvlid: GVLID,
+ isBidRequestValid: function (bidRequest) {
+ const isValid = bidRequest &&
+ bidRequest.params &&
+ typeof bidRequest.params.publisherId === 'string' &&
+ bidRequest.params.publisherId.trim().length === 36
+
+ if (!isValid) {
+ logError('Bidtheatre Header Bidding Publisher ID not provided or in incorrect format');
+ }
+
+ return isValid;
+ },
+ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {
+ const seenUrls = new Set();
+ const syncs = [];
+
+ if (syncOptions.pixelEnabled && serverResponses) {
+ serverResponses.forEach(response => {
+ if (response.body && response.body.seatbid) {
+ response.body.seatbid.forEach(seatbid => {
+ if (seatbid.bid) {
+ seatbid.bid.forEach(bid => {
+ const urls = bid.ext && bid.ext.usersync_urls;
+ if (Array.isArray(urls)) {
+ urls.forEach(url => {
+ if (!seenUrls.has(url)) {
+ seenUrls.add(url);
+ syncs.push({
+ type: 'image',
+ url: url
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ return syncs;
+ },
+ buildRequests(bidRequests, bidderRequest) {
+ const data = converter.toORTB({bidRequests, bidderRequest});
+
+ const cookieValue = storage.getCookie(BIDTHEATRE_COOKIE_NAME);
+ if (cookieValue) {
+ deepSetValue(data, 'user.buyeruid', cookieValue);
+ }
+
+ data.imp.forEach((impObj, index) => {
+ let publisherId = bidRequests[index].params.publisherId;
+
+ if (publisherId) {
+ deepSetValue(impObj, 'ext.bidder.publisherId', publisherId);
+ }
+ });
+
+ return [{
+ method: METHOD,
+ url: ENDPOINT_URL,
+ data
+ }]
+ },
+ interpretResponse(response, request) {
+ if (!response || !response.body || !response.body.seatbid) {
+ return [];
+ }
+
+ const macroReplacedSeatbid = response.body.seatbid.map(seatbidItem => {
+ const macroReplacedBid = seatbidItem.bid.map((bidItem) => ({
+ ...bidItem,
+ adm: replaceAuctionPrice(bidItem.adm, bidItem.price),
+ nurl: replaceAuctionPrice(bidItem.nurl, bidItem.price)
+ }));
+ return { ...seatbidItem, bid: macroReplacedBid };
+ });
+
+ const macroReplacedResponseBody = { ...response.body, seatbid: macroReplacedSeatbid };
+ const bids = converter.fromORTB({response: macroReplacedResponseBody, request: request.data}).bids;
+ return bids;
+ },
+ onTimeout: function(timeoutData) {},
+ onBidWon: function(bid) {},
+ onSetTargeting: function(bid) {},
+ // onBidderError: function({ error, bidderRequest }) {},
+ onAdRenderSucceeded: function(bid) {}
+}
+
+registerBidder(spec);
diff --git a/modules/bidtheatreBidAdapter.md b/modules/bidtheatreBidAdapter.md
new file mode 100644
index 00000000000..7f9301596aa
--- /dev/null
+++ b/modules/bidtheatreBidAdapter.md
@@ -0,0 +1,111 @@
+# Overview
+
+```
+Module Name : Bidtheatre Bidder Adapter
+Module Type : Bidder Adapter
+Maintainer : operations@bidtheatre.com
+```
+
+# Description
+
+Module that connects to Bidtheatre's demand sources
+
+About us: https://www.bidtheatre.com
+
+The Bidtheatre Bidding adapter requires manual set up before use. Please contact us at [operations@bidtheatre.com](mailto:operations@bidtheatre.com) if you would like to access Bidtheatre demand.
+
+# Bid params
+| Name | Scope | Description | Example |
+|:--------------| :------- |:---------------------------------------|:-------------------------------------|
+| `publisherId` | required | Manually set up publisher ID | `73b20b3a-12a0-4869-b54e-8d42b55786ee`|
+
+In addition to the required bid param above, Bidtheatre will also enforce the following requirements
+- All ad slots on a page must belong to the same publisher ID
+- The publisher must provide either a client IP and/or explicit geo data in the request
+
+# Test Parameters
+
+## Banner
+
+```javascript
+var displayAdUnits = [
+ {
+ code: 'test-banner',
+ mediaTypes: {
+ banner: {
+ sizes: [[980,240]]
+ }
+ },
+ bids: [
+ {
+ bidder: 'bidtheatre',
+ params: {
+ publisherId: '73b20b3a-12a0-4869-b54e-8d42b55786ee'
+ }
+ }
+ ]
+ }
+];
+```
+
+## Video
+
+```javascript
+var videoAdUnits = [
+ {
+ code: 'test-video',
+ mediaTypes: {
+ video: {
+ playerSize: [[1280, 720]]
+ }
+ },
+ bids: [
+ {
+ bidder: 'bidtheatre',
+ params: {
+ publisherId: '73b20b3a-12a0-4869-b54e-8d42b55786ee'
+ }
+ }
+ ]
+ }
+];
+```
+
+## Multiformat
+
+```javascript
+var adUnits = [
+ {
+ code: 'test-banner',
+ mediaTypes: {
+ banner: {
+ sizes: [[980,240]]
+ }
+ },
+ bids: [
+ {
+ bidder: 'bidtheatre',
+ params: {
+ publisherId: '73b20b3a-12a0-4869-b54e-8d42b55786ee'
+ }
+ }
+ ]
+ },
+ {
+ code: 'test-video',
+ mediaTypes: {
+ video: {
+ playerSize: [[1280, 720]]
+ }
+ },
+ bids: [
+ {
+ bidder: 'bidtheatre',
+ params: {
+ publisherId: '73b20b3a-12a0-4869-b54e-8d42b55786ee'
+ }
+ }
+ ]
+ }
+];
+```
diff --git a/modules/braveBidAdapter.js b/modules/braveBidAdapter.js
index 4c5448482db..7689aade114 100644
--- a/modules/braveBidAdapter.js
+++ b/modules/braveBidAdapter.js
@@ -1,8 +1,8 @@
-import {isEmpty, isStr, parseUrl, triggerPixel} from '../src/utils.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
-import {config} from '../src/config.js';
-import {convertOrtbRequestToProprietaryNative} from '../src/native.js';
+import { isStr, triggerPixel } from '../src/utils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
+import { parseNative } from '../libraries/braveUtils/index.js';
+import { buildRequests, interpretResponse } from '../libraries/braveUtils/buildAndInterpret.js'
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -13,160 +13,15 @@ const BIDDER_CODE = 'brave';
const DEFAULT_CUR = 'USD';
const ENDPOINT_URL = `https://point.bravegroup.tv/?t=2&partner=hash`;
-const NATIVE_ASSETS_IDS = { 1: 'title', 2: 'icon', 3: 'image', 4: 'body', 5: 'sponsoredBy', 6: 'cta' };
-const NATIVE_ASSETS = {
- title: { id: 1, name: 'title' },
- icon: { id: 2, type: 1, name: 'img' },
- image: { id: 3, type: 3, name: 'img' },
- body: { id: 4, type: 2, name: 'data' },
- sponsoredBy: { id: 5, type: 1, name: 'data' },
- cta: { id: 6, type: 12, name: 'data' }
-};
-
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 !!(bid.params.placementId && bid.params.placementId.toString().length === 32);
- },
-
- /**
- * 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, bidderRequest) => {
- // convert Native ORTB definition to old-style prebid native definition
- validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);
-
- if (validBidRequests.length === 0 || !bidderRequest) return [];
-
- const endpointURL = ENDPOINT_URL.replace('hash', validBidRequests[0].params.placementId);
-
- let imp = validBidRequests.map(br => {
- let impObject = {
- id: br.bidId,
- secure: 1
- };
-
- if (br.mediaTypes.banner) {
- impObject.banner = createBannerRequest(br);
- } else if (br.mediaTypes.video) {
- impObject.video = createVideoRequest(br);
- } else if (br.mediaTypes.native) {
- impObject.native = {
- // TODO: `id` is not part of the ORTB native spec, is this intentional?
- id: br.bidId,
- ver: '1.2',
- request: createNativeRequest(br)
- };
- }
- return impObject;
- });
-
- // TODO: do these values make sense?
- let page = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation;
- let r = bidderRequest.refererInfo.ref;
-
- let data = {
- id: bidderRequest.bidderRequestId,
- cur: [ DEFAULT_CUR ],
- device: {
- w: screen.width,
- h: screen.height,
- language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '',
- ua: navigator.userAgent,
- },
- site: {
- domain: parseUrl(page).hostname,
- page: page,
- },
- tmax: bidderRequest.timeout,
- imp
- };
-
- if (r) {
- data.site.ref = r;
- }
-
- if (bidderRequest.gdprConsent) {
- data['regs'] = {'ext': {'gdpr': bidderRequest.gdprConsent.gdprApplies ? 1 : 0}};
- data['user'] = {'ext': {'consent': bidderRequest.gdprConsent.consentString ? bidderRequest.gdprConsent.consentString : ''}};
- }
-
- if (bidderRequest.uspConsent !== undefined) {
- if (!data['regs'])data['regs'] = {'ext': {}};
- data['regs']['ext']['us_privacy'] = bidderRequest.uspConsent;
- }
-
- if (config.getConfig('coppa') === true) {
- if (!data['regs'])data['regs'] = {'coppa': 1};
- else data['regs']['coppa'] = 1;
- }
-
- if (validBidRequests[0].schain) {
- data['source'] = {'ext': {'schain': validBidRequests[0].schain}};
- }
-
- return {
- method: 'POST',
- url: endpointURL,
- data: data
- };
- },
-
- /**
- * 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) => {
- if (!serverResponse || isEmpty(serverResponse.body)) return [];
-
- let bids = [];
- serverResponse.body.seatbid.forEach(response => {
- response.bid.forEach(bid => {
- let mediaType = bid.ext && bid.ext.mediaType ? bid.ext.mediaType : 'banner';
-
- let bidObj = {
- requestId: bid.impid,
- cpm: bid.price,
- width: bid.w,
- height: bid.h,
- ttl: 1200,
- currency: DEFAULT_CUR,
- netRevenue: true,
- creativeId: bid.crid,
- dealId: bid.dealid || null,
- mediaType: mediaType
- };
-
- switch (mediaType) {
- case 'video':
- bidObj.vastUrl = bid.adm;
- break;
- case 'native':
- bidObj.native = parseNative(bid.adm);
- break;
- default:
- bidObj.ad = bid.adm;
- }
+ isBidRequestValid: (bid) => !!(bid.params.placementId && bid.params.placementId.toString().length === 32),
- bids.push(bidObj);
- });
- });
+ buildRequests: (validBidRequests, bidderRequest) => buildRequests(validBidRequests, bidderRequest, ENDPOINT_URL, DEFAULT_CUR),
- return bids;
- },
+ interpretResponse: (serverResponse) => interpretResponse(serverResponse, DEFAULT_CUR, parseNative),
onBidWon: (bid) => {
if (isStr(bid.nurl) && bid.nurl !== '') {
@@ -175,90 +30,4 @@ export const spec = {
}
};
-const parseNative = adm => {
- let bid = {
- clickUrl: adm.native.link && adm.native.link.url,
- impressionTrackers: adm.native.imptrackers || [],
- clickTrackers: (adm.native.link && adm.native.link.clicktrackers) || [],
- jstracker: adm.native.jstracker || []
- };
- adm.native.assets.forEach(asset => {
- let kind = NATIVE_ASSETS_IDS[asset.id];
- let content = kind && asset[NATIVE_ASSETS[kind].name];
- if (content) {
- bid[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
- }
- });
-
- return bid;
-}
-
-const createNativeRequest = br => {
- let impObject = {
- ver: '1.2',
- assets: []
- };
-
- let keys = Object.keys(br.mediaTypes.native);
-
- for (let key of keys) {
- const props = NATIVE_ASSETS[key];
- if (props) {
- const asset = {
- required: br.mediaTypes.native[key].required ? 1 : 0,
- id: props.id,
- [props.name]: {}
- };
-
- if (props.type) asset[props.name]['type'] = props.type;
- if (br.mediaTypes.native[key].len) asset[props.name]['len'] = br.mediaTypes.native[key].len;
- if (br.mediaTypes.native[key].sizes && br.mediaTypes.native[key].sizes[0]) {
- asset[props.name]['w'] = br.mediaTypes.native[key].sizes[0];
- asset[props.name]['h'] = br.mediaTypes.native[key].sizes[1];
- }
-
- impObject.assets.push(asset);
- }
- }
-
- return impObject;
-}
-
-const createBannerRequest = br => {
- let size = [];
-
- if (br.mediaTypes.banner.sizes && Array.isArray(br.mediaTypes.banner.sizes)) {
- if (Array.isArray(br.mediaTypes.banner.sizes[0])) { size = br.mediaTypes.banner.sizes[0]; } else { size = br.mediaTypes.banner.sizes; }
- } else size = [300, 250];
-
- return { id: br.transactionId, w: size[0], h: size[1] };
-};
-
-const createVideoRequest = br => {
- // TODO: `id` is not part of imp.video in ORTB; is this intentional?
- let videoObj = {id: br.bidId};
- let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'skip', 'minbitrate', 'maxbitrate', 'api', 'linearity'];
-
- for (let param of supportParamsList) {
- if (br.mediaTypes.video[param] !== undefined) {
- videoObj[param] = br.mediaTypes.video[param];
- }
- }
-
- if (br.mediaTypes.video.playerSize && Array.isArray(br.mediaTypes.video.playerSize)) {
- if (Array.isArray(br.mediaTypes.video.playerSize[0])) {
- videoObj.w = br.mediaTypes.video.playerSize[0][0];
- videoObj.h = br.mediaTypes.video.playerSize[0][1];
- } else {
- videoObj.w = br.mediaTypes.video.playerSize[0];
- videoObj.h = br.mediaTypes.video.playerSize[1];
- }
- } else {
- videoObj.w = 640;
- videoObj.h = 480;
- }
-
- return videoObj;
-}
-
registerBidder(spec);
diff --git a/modules/bridBidAdapter.js b/modules/bridBidAdapter.js
index 527cb9d5d5d..c822f4d5c80 100644
--- a/modules/bridBidAdapter.js
+++ b/modules/bridBidAdapter.js
@@ -1,27 +1,17 @@
import {_each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js';
import {VIDEO} from '../src/mediaTypes.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {getAd, getSiteObj} from '../libraries/targetVideoUtils/bidderUtils.js'
+import {getAd, getSiteObj, getSyncResponse} from '../libraries/targetVideoUtils/bidderUtils.js'
+import {GVLID, SOURCE, TIME_TO_LIVE, VIDEO_ENDPOINT_URL, VIDEO_PARAMS} from '../libraries/targetVideoUtils/constants.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
* @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
* @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest
*/
-
-const SOURCE = 'pbjs';
-const BIDDER_CODE = 'brid';
-const ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction';
-const GVLID = 934;
-const TIME_TO_LIVE = 300;
-const VIDEO_PARAMS = [
- 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'plcmt',
- 'playbackmethod', 'protocols', 'startdelay'
-];
-
export const spec = {
- code: BIDDER_CODE,
+ code: 'brid',
gvlid: GVLID,
supportedMediaTypes: [VIDEO],
@@ -117,7 +107,7 @@ export const spec = {
requests.push({
method: 'POST',
- url: ENDPOINT_URL,
+ url: VIDEO_ENDPOINT_URL,
data: JSON.stringify(postBody),
options: {
withCredentials: true
@@ -138,92 +128,59 @@ export const spec = {
*/
interpretResponse: function(serverResponse, bidRequest) {
const response = serverResponse.body;
- const bidResponses = [];
-
- _each(response.seatbid, (resp) => {
- _each(resp.bid, (bid) => {
- const requestId = bidRequest.bidId;
- const params = bidRequest.params;
-
- const {ad, adUrl, vastUrl, vastXml} = getAd(bid);
-
- const bidResponse = {
- requestId,
- params,
- cpm: bid.price,
- width: bid.w,
- height: bid.h,
- creativeId: bid.adid,
- currency: response.cur,
- netRevenue: false,
- ttl: TIME_TO_LIVE,
- meta: {
- advertiserDomains: bid.adomain || []
- }
- };
+ let highestBid = null;
+
+ if (response && response.seatbid && response.seatbid.length && response.seatbid[0].bid && response.seatbid[0].bid.length) {
+ _each(response.seatbid, (resp) => {
+ _each(resp.bid, (bid) => {
+ const requestId = bidRequest.bidId;
+ const params = bidRequest.params;
+
+ const {ad, adUrl, vastUrl, vastXml} = getAd(bid);
+
+ const bidResponse = {
+ requestId,
+ params,
+ cpm: bid.price,
+ width: bid.w,
+ height: bid.h,
+ creativeId: bid.crid || bid.adid,
+ currency: response.cur,
+ netRevenue: false,
+ ttl: TIME_TO_LIVE,
+ meta: {
+ advertiserDomains: bid.adomain || []
+ }
+ };
- if (vastUrl || vastXml) {
- bidResponse.mediaType = VIDEO;
- if (vastUrl) bidResponse.vastUrl = vastUrl;
- if (vastXml) bidResponse.vastXml = vastXml;
- } else {
- bidResponse.ad = ad;
- bidResponse.adUrl = adUrl;
- };
+ if (vastUrl || vastXml) {
+ bidResponse.mediaType = VIDEO;
+ if (vastUrl) bidResponse.vastUrl = vastUrl;
+ if (vastXml) bidResponse.vastXml = vastXml;
+ } else {
+ bidResponse.ad = ad;
+ bidResponse.adUrl = adUrl;
+ };
- bidResponses.push(bidResponse);
+ if (!highestBid || highestBid.cpm < bidResponse.cpm) {
+ highestBid = bidResponse;
+ }
+ });
});
- });
+ }
- return bidResponses;
+ return highestBid ? [highestBid] : [];
},
-}
+ /**
+ * Determine the user sync type (either 'iframe' or 'image') based on syncOptions.
+ * Construct the sync URL by appending required query parameters such as gdpr, ccpa, and coppa consents.
+ * Return an array containing an object with the sync type and the constructed URL.
+ */
+ getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) => {
+ return getSyncResponse(syncOptions, gdprConsent, uspConsent, gppConsent, 'brid');
+ }
-// /**
-// * Helper function to get ad
-// *
-// * @param {object} bid The bid.
-// * @return {object} ad object.
-// */
-// function getAd(bid) {
-// let ad, adUrl, vastXml, vastUrl;
-
-// switch (deepAccess(bid, 'ext.prebid.type')) {
-// case VIDEO:
-// if (bid.adm.substr(0, 4) === 'http') {
-// vastUrl = bid.adm;
-// } else {
-// vastXml = bid.adm;
-// };
-// break;
-// default:
-// if (bid.adm && bid.nurl) {
-// ad = bid.adm;
-// ad += createTrackPixelHtml(decodeURIComponent(bid.nurl));
-// } else if (bid.adm) {
-// ad = bid.adm;
-// } else if (bid.nurl) {
-// adUrl = bid.nurl;
-// };
-// }
-
-// return {ad, adUrl, vastXml, vastUrl};
-// }
-
-// /**
-// * Helper function to get site object
-// *
-// * @return {object} siteObj.
-// */
-// function getSiteObj() {
-// const refInfo = (getRefererInfo && getRefererInfo()) || {};
-
-// return {
-// page: refInfo.page,
-// ref: refInfo.ref,
-// domain: refInfo.domain
-// };
-// }
+}
registerBidder(spec);
diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js
index 98b97286631..5e5b062889d 100644
--- a/modules/brightMountainMediaBidAdapter.js
+++ b/modules/brightMountainMediaBidAdapter.js
@@ -1,4 +1,4 @@
-import { generateUUID, deepAccess, logWarn, deepSetValue } from '../src/utils.js';
+import { generateUUID, deepAccess, logWarn, deepSetValue, isPlainObject } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';
@@ -224,7 +224,7 @@ function getFloor(bid, size) {
size: size,
});
- if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') {
+ if (isPlainObject(floorInfo) && floorInfo.currency === 'USD') {
return parseFloat(floorInfo.floor);
}
}
diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js
index 8f5fea80ffa..6ac19b39c79 100644
--- a/modules/browsiRtdProvider.js
+++ b/modules/browsiRtdProvider.js
@@ -26,6 +26,7 @@ import {getGlobal} from '../src/prebidGlobal.js';
import * as events from '../src/events.js';
import {EVENTS} from '../src/constants.js';
import {MODULE_TYPE_RTD} from '../src/activities/modules.js';
+import {setKeyValue as setGptKeyValue} from '../libraries/gptUtils/gptUtils.js';
/**
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
@@ -67,14 +68,7 @@ export function addBrowsiTag(data) {
return script;
}
-export function setKeyValue(key) {
- if (!key || typeof key !== 'string') return false;
- window.googletag = window.googletag || {cmd: []};
- window.googletag.cmd = window.googletag.cmd || [];
- window.googletag.cmd.push(() => {
- window.googletag.pubads().setTargeting(key, RANDOM.toString());
- });
-}
+export const setKeyValue = (key) => setGptKeyValue(key, RANDOM.toString());
export function sendPageviewEvent(eventType) {
if (eventType === 'PAGEVIEW') {
diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js
index 79ba8cf499d..d1b51dcb27d 100644
--- a/modules/c1xBidAdapter.js
+++ b/modules/c1xBidAdapter.js
@@ -185,7 +185,7 @@ function getBidFloor(bidRequest) {
}
let floor =
- floorInfo.floor ||
+ floorInfo?.floor ||
bidRequest.params.bidfloor ||
bidRequest.params.floorPriceMap ||
0;
diff --git a/modules/cadentApertureMXBidAdapter.js b/modules/cadentApertureMXBidAdapter.js
index 97283952888..fa441a4f4fa 100644
--- a/modules/cadentApertureMXBidAdapter.js
+++ b/modules/cadentApertureMXBidAdapter.js
@@ -230,11 +230,6 @@ export const spec = {
return false;
}
- if (bid.bidder !== BIDDER_CODE) {
- logWarn(BIDDER_CODE + ': Must use "cadent_aperture_mx" as bidder code.');
- return false;
- }
-
if (!bid.params.tagid || !isStr(bid.params.tagid)) {
logWarn(BIDDER_CODE + ': Missing tagid param or tagid present and not type String.');
return false;
diff --git a/modules/carodaBidAdapter.js b/modules/carodaBidAdapter.js
index 8ec260213b6..ebe4686630f 100644
--- a/modules/carodaBidAdapter.js
+++ b/modules/carodaBidAdapter.js
@@ -1,7 +1,9 @@
// jshint esversion: 6, es3: false, node: true
'use strict'
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { config } from '../src/config.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import {
deepAccess,
@@ -11,7 +13,6 @@ import {
sizeTupleToRtbSize,
sizesToSizeTuples
} from '../src/utils.js';
-import { config } from '../src/config.js';
const { getConfig } = config;
@@ -45,7 +46,7 @@ export const spec = {
getFirstWithKey(validBidRequests, 'params.priceType') ||
'net';
const test = getFirstWithKey(validBidRequests, 'params.test');
- const currency = getConfig('currency.adServerCurrency');
+ const currency = getCurrencyFromBidderRequest(bidderRequest);
const eids = getFirstWithKey(validBidRequests, 'userIdAsEids');
const schain = getFirstWithKey(validBidRequests, 'schain');
const request = {
@@ -184,8 +185,8 @@ function getImps (validBidRequests, common) {
const floorInfo = bid.getFloor
? bid.getFloor({ currency: common.currency || 'EUR' })
: {};
- const bidfloor = floorInfo.floor;
- const bidfloorcur = floorInfo.currency;
+ const bidfloor = floorInfo?.floor;
+ const bidfloorcur = floorInfo?.currency;
const { ctok, placementId } = bid.params;
const imp = {
bid_id: bid.bidId,
diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js
index 3b90529b6cc..c626d1f56aa 100644
--- a/modules/cointrafficBidAdapter.js
+++ b/modules/cointrafficBidAdapter.js
@@ -2,6 +2,7 @@ import { parseSizesInput, logError, isEmpty } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER } from '../src/mediaTypes.js'
import { config } from '../src/config.js'
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -45,7 +46,7 @@ export const spec = {
const sizes = parseSizesInput(bidRequest.params.size || bidRequest.sizes);
const currency =
config.getConfig(`currency.bidderCurrencyDefault.${BIDDER_CODE}`) ||
- config.getConfig('currency.adServerCurrency') ||
+ getCurrencyFromBidderRequest(bidderRequest) ||
DEFAULT_CURRENCY;
if (ALLOWED_CURRENCIES.indexOf(currency) === -1) {
diff --git a/modules/connatixBidAdapter.js b/modules/connatixBidAdapter.js
index aeadd2d1cd9..ea02b49d8ed 100644
--- a/modules/connatixBidAdapter.js
+++ b/modules/connatixBidAdapter.js
@@ -57,7 +57,7 @@ export function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (err) {
logError(err);
return 0;
diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js
index 6789c937afb..982bff22585 100644
--- a/modules/connectadBidAdapter.js
+++ b/modules/connectadBidAdapter.js
@@ -243,7 +243,7 @@ function getBidFloor(bidRequest) {
});
}
- let floor = floorInfo.floor || bidRequest.params.bidfloor || bidRequest.params.floorprice || 0;
+ let floor = floorInfo?.floor || bidRequest.params.bidfloor || bidRequest.params.floorprice || 0;
return floor;
}
diff --git a/modules/contxtfulBidAdapter.js b/modules/contxtfulBidAdapter.js
index 7f1a8702a3b..acd9811b871 100644
--- a/modules/contxtfulBidAdapter.js
+++ b/modules/contxtfulBidAdapter.js
@@ -151,11 +151,7 @@ const getSamplingRate = (bidderConfig, eventType) => {
};
// Handles the logging of events
-const logEvent = (eventType, data, options = {}) => {
- const {
- samplingEnabled = false,
- } = options;
-
+const logEvent = (eventType, data) => {
try {
// Log event
logInfo(BIDDER_CODE, `[${eventType}] ${JSON.stringify(data)}`);
@@ -165,12 +161,15 @@ const logEvent = (eventType, data, options = {}) => {
const {version, customer} = extractParameters(bidderConfig);
// Sampled monitoring
- if (samplingEnabled) {
- const shouldSampleDice = Math.random();
+ if (['onBidBillable', 'onAdRenderSucceeded'].includes(eventType)) {
+ const randomNumber = Math.random();
const samplingRate = getSamplingRate(bidderConfig, eventType);
- if (shouldSampleDice >= samplingRate) {
+ if (randomNumber >= samplingRate) {
return; // Don't sample
}
+ } else if (!['onTimeout', 'onBidderError', 'onBidWon'].includes(eventType)) {
+ // Unsupported event type.
+ return;
}
const payload = { type: eventType, data };
@@ -206,12 +205,12 @@ export const spec = {
buildRequests,
interpretResponse,
getUserSyncs,
- onBidWon: function(bid, options) { logEvent('onBidWon', bid, { samplingEnabled: false, ...options }); },
- onBidBillable: function(bid, options) { logEvent('onBidBillable', bid, { samplingEnabled: false, ...options }); },
- onAdRenderSucceeded: function(bid, options) { logEvent('onAdRenderSucceeded', bid, { samplingEnabled: false, ...options }); },
- onSetTargeting: function(bid, options) { },
- onTimeout: function(timeoutData, options) { logEvent('onTimeout', timeoutData, { samplingEnabled: true, ...options }); },
- onBidderError: function(args, options) { logEvent('onBidderError', args, { samplingEnabled: true, ...options }); },
+ onBidWon: function(bid) { logEvent('onBidWon', bid); },
+ onBidBillable: function(bid) { logEvent('onBidBillable', bid); },
+ onAdRenderSucceeded: function(bid) { logEvent('onAdRenderSucceeded', bid); },
+ onSetTargeting: function(bid) { },
+ onTimeout: function(timeoutData) { logEvent('onTimeout', timeoutData); },
+ onBidderError: function({ error, bidderRequest }) { logEvent('onBidderError', { error, bidderRequest }); },
};
registerBidder(spec);
diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js
index a0d11328427..55623c00591 100644
--- a/modules/contxtfulRtdProvider.js
+++ b/modules/contxtfulRtdProvider.js
@@ -23,11 +23,12 @@ import { MODULE_TYPE_RTD } from '../src/activities/modules.js';
const MODULE_NAME = 'contxtful';
const MODULE = `${MODULE_NAME}RtdProvider`;
-const CONTXTFUL_RECEPTIVITY_DOMAIN = 'api.receptivity.io';
+const CONTXTFUL_HOSTNAME_DEFAULT = 'api.receptivity.io';
+const CONTXTFUL_DEFER_DEFAULT = 0;
const storageManager = getStorageManager({
moduleType: MODULE_TYPE_RTD,
- moduleName: MODULE_NAME
+ moduleName: MODULE_NAME,
});
let rxApi = null;
@@ -43,14 +44,11 @@ function getRxEngineReceptivity(requester) {
}
function getItemFromSessionStorage(key) {
- let value = null;
try {
- // Use the Storage Manager
- value = storageManager.getDataFromSessionStorage(key, null);
+ return storageManager.getDataFromSessionStorage(key);
} catch (error) {
+ logError(MODULE, error);
}
-
- return value;
}
function loadSessionReceptivity(requester) {
@@ -102,6 +100,9 @@ function init(config) {
try {
initCustomer(config);
+
+ observeLastCursorPosition();
+
return true;
} catch (error) {
logError(MODULE, error);
@@ -128,9 +129,10 @@ export function extractParameters(config) {
throw Error(`${MODULE}: params.customer should be a non-empty string`);
}
- const hostname = config?.params?.hostname || CONTXTFUL_RECEPTIVITY_DOMAIN;
+ const hostname = config?.params?.hostname || CONTXTFUL_HOSTNAME_DEFAULT;
+ const defer = config?.params?.defer || CONTXTFUL_DEFER_DEFAULT;
- return { version, customer, hostname };
+ return { version, customer, hostname, defer };
}
/**
@@ -139,7 +141,7 @@ export function extractParameters(config) {
* @param { String } config
*/
function initCustomer(config) {
- const { version, customer, hostname } = extractParameters(config);
+ const { version, customer, hostname, defer } = extractParameters(config);
const CONNECTOR_URL = buildUrl({
protocol: 'https',
host: hostname,
@@ -147,7 +149,14 @@ function initCustomer(config) {
});
addConnectorEventListener(customer, config);
- loadExternalScript(CONNECTOR_URL, MODULE_TYPE_RTD, MODULE_NAME);
+
+ const loadScript = () => loadExternalScript(CONNECTOR_URL, MODULE_TYPE_RTD, MODULE_NAME);
+ // Optionally defer the loading of the script
+ if (Number.isFinite(defer) && defer > 0) {
+ setTimeout(loadScript, defer);
+ } else {
+ loadScript();
+ }
}
/**
@@ -170,6 +179,9 @@ function addConnectorEventListener(tagId, prebidConfig) {
}
config['prebid'] = prebidConfig || {};
rxApi = await rxApiBuilder(config);
+
+ // Remove listener now that we can use rxApi.
+ removeListeners();
}
);
}
@@ -189,8 +201,11 @@ function getTargetingData(adUnits, config, _userConsent) {
logInfo(MODULE, 'getTargetingData');
const requester = config?.params?.customer;
- const rx = getRxEngineReceptivity(requester) ||
- loadSessionReceptivity(requester) || {};
+ const rx =
+ getRxEngineReceptivity(requester) ||
+ loadSessionReceptivity(requester) ||
+ {};
+
if (isEmpty(rx)) {
return {};
}
@@ -215,9 +230,10 @@ function getBidRequestData(reqBidsConfigObj, onDone, config, userConsent) {
function onReturn() {
if (isFirstBidRequestCall) {
isFirstBidRequestCall = false;
- };
+ }
onDone();
}
+
logInfo(MODULE, 'getBidRequestData');
const bidders = config?.params?.bidders || [];
if (isEmpty(bidders) || !isArray(bidders)) {
@@ -225,46 +241,31 @@ function getBidRequestData(reqBidsConfigObj, onDone, config, userConsent) {
return;
}
- let fromApiBatched = () => rxApi?.receptivityBatched?.(bidders);
- let fromApiSingle = () => prepareBatch(bidders, getRxEngineReceptivity);
- let fromStorage = () => prepareBatch(bidders, loadSessionReceptivity);
-
- function tryMethods(methods) {
- for (let method of methods) {
- try {
- let batch = method();
- if (!isEmpty(batch)) {
- return batch;
- }
- } catch (error) { }
- }
- return {};
+ let fromApi = rxApi?.receptivityBatched?.(bidders) || {};
+ let fromStorage = prepareBatch(bidders, (bidder) => loadSessionReceptivity(`${config?.params?.customer}_${bidder}`));
+
+ let sources = [fromStorage, fromApi];
+ if (isFirstBidRequestCall) {
+ sources.reverse();
}
- let rxBatch = {};
- try {
- if (isFirstBidRequestCall) {
- rxBatch = tryMethods([fromStorage, fromApiBatched, fromApiSingle]);
- } else {
- rxBatch = tryMethods([fromApiBatched, fromApiSingle, fromStorage])
- }
- } catch (error) { }
+ let rxBatch = Object.assign(...sources);
+
+ let singlePointEvents;
if (isEmpty(rxBatch)) {
- onReturn();
- return;
+ singlePointEvents = btoa(JSON.stringify({ ui: getUiEvents() }));
}
bidders
- .map((bidderCode) => ({ bidderCode, rx: rxBatch[bidderCode] }))
- .filter(({ rx }) => !isEmpty(rx))
- .forEach(({ bidderCode, rx }) => {
+ .forEach(bidderCode => {
const ortb2 = {
user: {
data: [
{
name: MODULE_NAME,
ext: {
- rx,
+ rx: rxBatch[bidderCode],
+ events: singlePointEvents,
params: {
ev: config.params?.version,
ci: config.params?.customer,
@@ -274,13 +275,96 @@ function getBidRequestData(reqBidsConfigObj, onDone, config, userConsent) {
],
},
};
+
mergeDeep(reqBidsConfigObj.ortb2Fragments?.bidder, {
[bidderCode]: ortb2,
});
});
onReturn();
-};
+}
+
+function getUiEvents() {
+ return {
+ position: lastCursorPosition,
+ screen: getScreen(),
+ };
+}
+
+function getScreen() {
+ function getInnerSize() {
+ let w = window?.innerWidth;
+ let h = window?.innerHeight;
+
+ if (w && h) {
+ return [w, h];
+ }
+ }
+
+ function getDocumentSize() {
+ let body = window?.document?.body;
+ let w = body.clientWidth;
+ let h = body.clientHeight;
+
+ if (w && h) {
+ return [w, h];
+ }
+ }
+
+ // If we cannot access or cast the window dimensions, we get None.
+ // If we cannot collect the size from the window we try to use the root document dimensions
+ let [width, height] = getInnerSize() || getDocumentSize() || [0, 0];
+ let topLeft = { x: window.scrollX, y: window.scrollY };
+
+ return {
+ topLeft,
+ width,
+ height,
+ timestampMs: performance.now(),
+ };
+}
+
+let lastCursorPosition;
+
+function observeLastCursorPosition() {
+ function pointerEventToPosition(event) {
+ lastCursorPosition = {
+ x: event.clientX,
+ y: event.clientY,
+ timestampMs: performance.now()
+ };
+ }
+
+ function touchEventToPosition(event) {
+ let touch = event.touches.item(0);
+ if (!touch) {
+ return;
+ }
+
+ lastCursorPosition = {
+ x: touch.clientX,
+ y: touch.clientY,
+ timestampMs: performance.now()
+ };
+ }
+
+ addListener('pointermove', pointerEventToPosition);
+ addListener('touchmove', touchEventToPosition);
+}
+
+let listeners = {};
+function addListener(name, listener) {
+ listeners[name] = listener;
+
+ window.addEventListener(name, listener);
+}
+
+function removeListeners() {
+ for (const name in listeners) {
+ window.removeEventListener(name, listeners[name]);
+ delete listeners[name];
+ }
+}
export const contxtfulSubmodule = {
name: MODULE_NAME,
diff --git a/modules/contxtfulRtdProvider.md b/modules/contxtfulRtdProvider.md
index 622b353c27a..de2376e782d 100644
--- a/modules/contxtfulRtdProvider.md
+++ b/modules/contxtfulRtdProvider.md
@@ -8,7 +8,7 @@
The Contxtful RTD module offers a unique feature—Receptivity. Receptivity is an efficiency metric, enabling the qualification of any instant in a session in real time based on attention. The core idea is straightforward: the likelihood of an ad’s success increases when it grabs attention and is presented in the right context at the right time.
-To utilize this module, you need to register for an account with [Contxtful](https://contxtful.com). For inquiries, please contact [contact@contxtful.com](mailto:contact@contxtful.com).
+To utilize this module, you need to register for an account with [Contxtful](https://contxtful.com). For inquiries, please reach out to [contact@contxtful.com](mailto:contact@contxtful.com).
## Build Instructions
@@ -72,7 +72,7 @@ pbjs.setConfig({
| `customer` | `String` | Required | Your unique customer identifier. |
| `hostname` | `String` | Optional | Target URL for CONTXTFUL external JavaScript file. Default is "api.receptivity.io". Changing default behaviour is not recommended. Please reach out to contact@contxtful.com if you experience issues. |
| `adServerTargeting` | `Boolean`| Optional | Enables the `getTargetingData` to inject targeting value in ad units. Setting to true enables the feature, false disables the feature. Default is true |
-| `bidders` | `Array` | Optional | Setting this array enables Receptivity in the `ortb2` object through `getBidRequestData` for all the listed `bidders`. Default is `[]` (an empty array). RECOMMENDED : Add all the bidders active like this `["bidderCode1", "bidderCode", "..."]` |
+| `bidders` | `Array` | Optional | Setting this array enables Receptivity in the `ortb2` object through `getBidRequestData` for all the listed `bidders`. Default is `[]` (an empty array). RECOMMENDED : Add all the active bidders like this `["bidderCode1", "bidderCode", "..."]` |
## Usage: Injection in Ad Servers
diff --git a/modules/copper6sspBidAdapter.js b/modules/copper6sspBidAdapter.js
index 335b3b3d144..e05ed241cc6 100644
--- a/modules/copper6sspBidAdapter.js
+++ b/modules/copper6sspBidAdapter.js
@@ -5,9 +5,11 @@ import { isBidRequestValid, buildRequests, interpretResponse, getUserSyncs } fro
const BIDDER_CODE = 'copper6ssp';
const AD_URL = 'https://endpoint.copper6.com/pbjs';
const SYNC_URL = 'https://сsync.copper6.com';
+const GVLID = 1356;
export const spec = {
code: BIDDER_CODE,
+ gvlid: GVLID,
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
isBidRequestValid: isBidRequestValid(),
diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js
index f8d216f0838..3161534a441 100644
--- a/modules/craftBidAdapter.js
+++ b/modules/craftBidAdapter.js
@@ -1,4 +1,4 @@
-import {getBidRequest, logError} from '../src/utils.js';
+import {getBidRequest} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {find, includes} from '../src/polyfill.js';
@@ -7,6 +7,7 @@ import {ajax} from '../src/ajax.js';
import {hasPurpose1Consent} from '../src/utils/gdpr.js';
import {convertOrtbRequestToProprietaryNative} from '../src/native.js';
import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js';
+import {interpretResponseUtil} from '../libraries/interpretResponseUtils/index.js';
const BIDDER_CODE = 'craft';
const URL_BASE = 'https://gacraft.jp/prebid-v3';
@@ -68,31 +69,14 @@ export const spec = {
interpretResponse: function(serverResponse, {bidderRequest}) {
try {
- serverResponse = serverResponse.body;
- const bids = [];
- if (!serverResponse) {
- return [];
- }
- if (serverResponse.error) {
- let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`;
- if (serverResponse.error) {
- errorMessage += `: ${serverResponse.error}`;
+ const bids = interpretResponseUtil(serverResponse, {bidderRequest}, serverBid => {
+ const rtbBid = getRtbBid(serverBid);
+ if (rtbBid && rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) {
+ const bid = newBid(serverBid, rtbBid, bidderRequest);
+ bid.mediaType = parseMediaType(rtbBid);
+ return bid;
}
- logError(errorMessage);
- return bids;
- }
- if (serverResponse.tags) {
- serverResponse.tags.forEach(serverBid => {
- const rtbBid = getRtbBid(serverBid);
- if (rtbBid) {
- if (rtbBid.cpm !== 0 && includes(this.supportedMediaTypes, rtbBid.ad_type)) {
- const bid = newBid(serverBid, rtbBid, bidderRequest);
- bid.mediaType = parseMediaType(rtbBid);
- bids.push(bid);
- }
- }
- });
- }
+ });
return bids;
} catch (e) {
return [];
diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js
index d44f947dd3e..b01e7361e3f 100644
--- a/modules/criteoBidAdapter.js
+++ b/modules/criteoBidAdapter.js
@@ -1,4 +1,4 @@
-import {deepAccess, deepSetValue, isArray, logError, logWarn, parseUrl, triggerPixel} from '../src/utils.js';
+import {deepSetValue, isArray, logError, logWarn, parseUrl, triggerPixel} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {getStorageManager} from '../src/storageManager.js';
@@ -92,7 +92,7 @@ function imp(buildImp, bidRequest, context) {
}
deepSetValue(imp, 'video.ext', {
context: bidRequest.mediaTypes.video.context,
- playersizes: parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'), parseSize),
+ playersizes: parseSizes(bidRequest?.mediaTypes?.video?.playerSize, parseSize),
plcmt: bidRequest.mediaTypes.video.plcmt,
poddur: bidRequest.mediaTypes.video.adPodDurationSec,
rqddurs: bidRequest.mediaTypes.video.durationRangeSec,
@@ -156,7 +156,7 @@ function request(buildRequest, imps, bidderRequest, context) {
* @returns {*}
*/
function bidResponse(buildBidResponse, bid, context) {
- context.mediaType = deepAccess(bid, 'ext.mediatype');
+ context.mediaType = bid?.ext?.mediatype;
if (context.mediaType === NATIVE && typeof bid.adm_native !== 'undefined') {
bid.adm = bid.adm_native;
delete bid.adm_native;
@@ -165,22 +165,22 @@ function bidResponse(buildBidResponse, bid, context) {
let bidResponse = buildBidResponse(bid, context);
const {bidRequest} = context;
- bidResponse.currency = deepAccess(bid, 'ext.cur')
+ bidResponse.currency = bid?.ext?.cur;
- if (typeof deepAccess(bid, 'ext.meta') !== 'undefined') {
+ if (typeof bid?.ext?.meta !== 'undefined') {
deepSetValue(bidResponse, 'meta', {
...bidResponse.meta,
...bid.ext.meta
});
}
- if (typeof deepAccess(bid, 'ext.paf.content_id') !== 'undefined') {
+ if (typeof bid?.ext?.paf?.content_id !== 'undefined') {
deepSetValue(bidResponse, 'meta.paf.content_id', bid.ext.paf.content_id)
}
if (bidResponse.mediaType === VIDEO) {
bidResponse.vastUrl = bid.ext?.displayurl;
// if outstream video, add a default render for it.
- if (deepAccess(bidRequest, 'mediaTypes.video.context') === OUTSTREAM) {
+ if (bidRequest?.mediaTypes?.video?.context === OUTSTREAM) {
bidResponse.renderer = createOutstreamVideoRenderer(bid);
}
}
@@ -200,9 +200,9 @@ function bidResponse(buildBidResponse, bid, context) {
function response(buildResponse, bidResponses, ortbResponse, context) {
let response = buildResponse(bidResponses, ortbResponse, context);
- const pafTransmission = deepAccess(ortbResponse, 'ext.paf.transmission');
+ const pafTransmission = ortbResponse?.ext?.paf?.transmission;
response.bids.forEach(bid => {
- if (typeof pafTransmission !== 'undefined' && typeof deepAccess(bid, 'meta.paf.content_id') !== 'undefined') {
+ if (typeof pafTransmission !== 'undefined' && typeof bid?.meta?.paf?.content_id !== 'undefined') {
deepSetValue(bid, 'meta.paf.transmission', pafTransmission);
} else {
delete bid.meta.paf;
@@ -362,7 +362,7 @@ export const spec = {
// We support native request without assets requirements because we can fill them later on.
// This is a trick to fool oRTB converter isOpenRTBBidRequestValid(ortb) fn because it needs
// nativeOrtbRequest.assets to be non-empty.
- if (deepAccess(bidRequest, 'nativeOrtbRequest.assets') == null) {
+ if (bidRequest?.nativeOrtbRequest?.assets == null) {
logWarn(LOG_PREFIX + 'native asset requirements are missing');
deepSetValue(bidRequest, 'nativeOrtbRequest.assets', [{}]);
}
@@ -391,7 +391,7 @@ export const spec = {
const interpretedResponse = CONVERTER.fromORTB({response: response.body, request: request.data});
const bids = interpretedResponse.bids || [];
- const fledgeAuctionConfigs = deepAccess(response.body, 'ext.igi')?.filter(igi => isArray(igi?.igs))
+ const fledgeAuctionConfigs = response.body?.ext?.igi?.filter(igi => isArray(igi?.igs))
.flatMap(igi => igi.igs);
if (fledgeAuctionConfigs?.length) {
return {
@@ -548,11 +548,11 @@ function parseSize(size) {
}
function hasVideoMediaType(bidRequest) {
- return deepAccess(bidRequest, 'mediaTypes.video') !== undefined;
+ return bidRequest?.mediaTypes?.video !== undefined;
}
function hasNativeMediaType(bidRequest) {
- return deepAccess(bidRequest, 'mediaTypes.native') !== undefined;
+ return bidRequest?.mediaTypes?.native !== undefined;
}
function hasValidVideoMediaType(bidRequest) {
@@ -562,12 +562,12 @@ function hasValidVideoMediaType(bidRequest) {
requiredMediaTypesParams.forEach(function (param) {
if (param === 'placement') {
- if (deepAccess(bidRequest, 'mediaTypes.video.' + param) === undefined && deepAccess(bidRequest, 'params.video.' + param) === undefined && deepAccess(bidRequest, 'mediaTypes.video.plcmt') === undefined && deepAccess(bidRequest, 'params.video.plcmt') === undefined) {
+ if (bidRequest?.mediaTypes?.video?.[param] === undefined && bidRequest?.params?.video?.[param] === undefined && bidRequest?.mediaTypes?.video?.plcmt === undefined && bidRequest?.params?.video?.plcmt === undefined) {
isValid = false;
logError('Criteo Bid Adapter: mediaTypes.video.' + param + ' or mediaTypes.video.plcmt is required');
}
} else {
- if (deepAccess(bidRequest, 'mediaTypes.video.' + param) === undefined && deepAccess(bidRequest, 'params.video.' + param) === undefined) {
+ if (bidRequest?.mediaTypes?.video?.[param] === undefined && bidRequest?.params?.video?.[param] === undefined) {
isValid = false;
logError('Criteo Bid Adapter: mediaTypes.video.' + param + ' is required');
}
@@ -604,13 +604,13 @@ function getFloors(bidRequest) {
if (getFloor) {
if (bidRequest.mediaTypes?.banner) {
floors.banner = {};
- const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'))
+ const bannerSizes = parseSizes(bidRequest?.mediaTypes?.banner?.sizes)
bannerSizes.forEach(bannerSize => floors.banner[parseSize(bannerSize).toString()] = getFloor.call(bidRequest, { size: bannerSize, mediaType: BANNER }));
}
if (bidRequest.mediaTypes?.video) {
floors.video = {};
- const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'))
+ const videoSizes = parseSizes(bidRequest?.mediaTypes?.video?.playerSize)
videoSizes.forEach(videoSize => floors.video[parseSize(videoSize).toString()] = getFloor.call(bidRequest, { size: videoSize, mediaType: VIDEO }));
}
diff --git a/modules/currency.js b/modules/currency.js
index 8ac2b8cbead..b149a1934c3 100644
--- a/modules/currency.js
+++ b/modules/currency.js
@@ -1,4 +1,4 @@
-import {logError, logInfo, logMessage, logWarn} from '../src/utils.js';
+import {deepSetValue, logError, logInfo, logMessage, logWarn} from '../src/utils.js';
import {getGlobal} from '../src/prebidGlobal.js';
import { EVENTS, REJECTION_REASON } from '../src/constants.js';
import {ajax} from '../src/ajax.js';
@@ -6,11 +6,14 @@ import {config} from '../src/config.js';
import {getHook} from '../src/hook.js';
import {defer} from '../src/utils/promise.js';
import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js';
-import {timedBidResponseHook} from '../src/utils/perfMetrics.js';
+import {timedAuctionHook, timedBidResponseHook} from '../src/utils/perfMetrics.js';
import {on as onEvent, off as offEvent} from '../src/events.js';
+import { enrichFPD } from '../src/fpd/enrichment.js';
+import { timeoutQueue } from '../libraries/timeoutQueue/timeoutQueue.js';
const DEFAULT_CURRENCY_RATE_URL = 'https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=$$TODAY$$';
const CURRENCY_RATE_PRECISION = 4;
+const MODULE_NAME = 'currency';
let ratesURL;
let bidResponseQueue = [];
@@ -26,6 +29,9 @@ let defaultRates;
export let responseReady = defer();
+const delayedAuctions = timeoutQueue();
+let auctionDelay = 0;
+
/**
* Configuration function for currency
* @param {object} config
@@ -77,6 +83,7 @@ export function setConfig(config) {
}
if (typeof config.adServerCurrency === 'string') {
+ auctionDelay = config.auctionDelay;
logInfo('enabling currency support', arguments);
adServerCurrency = config.adServerCurrency;
@@ -106,6 +113,7 @@ export function setConfig(config) {
initCurrency();
} else {
// currency support is disabled, setting defaults
+ auctionDelay = 0;
logInfo('disabling currency support');
resetCurrency();
}
@@ -137,6 +145,7 @@ function loadRates() {
conversionCache = {};
currencyRatesLoaded = true;
processBidResponseQueue();
+ delayedAuctions.resume();
} catch (e) {
errorSettingsRates('Failed to parse currencyRates response: ' + response);
}
@@ -145,6 +154,7 @@ function loadRates() {
errorSettingsRates(...args);
currencyRatesLoaded = true;
processBidResponseQueue();
+ delayedAuctions.resume();
needToCallForCurrencyFile = true;
}
}
@@ -162,16 +172,20 @@ function initCurrency() {
getGlobal().convertCurrency = (cpm, fromCurrency, toCurrency) => parseFloat(cpm) * getCurrencyConversion(fromCurrency, toCurrency);
getHook('addBidResponse').before(addBidResponseHook, 100);
getHook('responsesReady').before(responsesReadyHook);
+ enrichFPD.before(enrichFPDHook);
+ getHook('requestBids').before(requestBidsHook, 50);
onEvent(EVENTS.AUCTION_TIMEOUT, rejectOnAuctionTimeout);
onEvent(EVENTS.AUCTION_INIT, loadRates);
loadRates();
}
}
-function resetCurrency() {
+export function resetCurrency() {
if (currencySupportEnabled) {
getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove();
getHook('responsesReady').getHooks({hook: responsesReadyHook}).remove();
+ enrichFPD.getHooks({hook: enrichFPDHook}).remove();
+ getHook('requestBids').getHooks({hook: requestBidsHook}).remove();
offEvent(EVENTS.AUCTION_TIMEOUT, rejectOnAuctionTimeout);
offEvent(EVENTS.AUCTION_INIT, loadRates);
delete getGlobal().convertCurrency;
@@ -335,3 +349,23 @@ export function setOrtbCurrency(ortbRequest, bidderRequest, context) {
}
registerOrtbProcessor({type: REQUEST, name: 'currency', fn: setOrtbCurrency});
+
+function enrichFPDHook(next, fpd) {
+ return next(fpd.then(ortb2 => {
+ deepSetValue(ortb2, 'ext.prebid.adServerCurrency', adServerCurrency);
+ return ortb2;
+ }))
+}
+
+export const requestBidsHook = timedAuctionHook('currency', function requestBidsHook(fn, reqBidsConfigObj) {
+ const continueAuction = ((that) => () => fn.call(that, reqBidsConfigObj))(this);
+
+ if (!currencyRatesLoaded && auctionDelay > 0) {
+ delayedAuctions.submit(auctionDelay, continueAuction, () => {
+ logWarn(`${MODULE_NAME}: Fetch attempt did not return in time for auction ${reqBidsConfigObj.auctionId}`)
+ continueAuction();
+ });
+ } else {
+ continueAuction();
+ }
+});
diff --git a/modules/dailyhuntBidAdapter.js b/modules/dailyhuntBidAdapter.js
index f96e07b71bf..fd7a5c137a7 100644
--- a/modules/dailyhuntBidAdapter.js
+++ b/modules/dailyhuntBidAdapter.js
@@ -129,7 +129,7 @@ const createOrtbPublisherObj = (validBidRequests) => ({ ...extractKeyInfo(validB
// get bidFloor Function for different creatives
function getBidFloor(bid, creative) {
let floorInfo = typeof (bid.getFloor) == 'function' ? bid.getFloor({ currency: 'USD', mediaType: creative, size: '*' }) : {};
- return Math.floor(floorInfo.floor || (bid.params.bidfloor ? bid.params.bidfloor : 0.0));
+ return Math.floor(floorInfo?.floor || (bid.params.bidfloor ? bid.params.bidfloor : 0.0));
}
const createOrtbImpObj = (bid) => {
diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js
index 15e94a5bc36..2111643b344 100644
--- a/modules/deltaprojectsBidAdapter.js
+++ b/modules/deltaprojectsBidAdapter.js
@@ -1,5 +1,6 @@
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {BANNER} from '../src/mediaTypes.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER } from '../src/mediaTypes.js';
import {
_each,
_map,
@@ -11,7 +12,6 @@ import {
logWarn,
setOnAny
} from '../src/utils.js';
-import {config} from '../src/config.js';
export const BIDDER_CODE = 'deltaprojects';
export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid';
@@ -74,7 +74,7 @@ function buildRequests(validBidRequests, bidderRequest) {
// build bid specific
return validBidRequests.map(validBidRequest => {
- const openRTBRequest = buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs);
+ const openRTBRequest = buildOpenRTBRequest(validBidRequest, bidderRequest, id, site, device, user, tmax, regs);
return {
method: 'POST',
url: BIDDER_ENDPOINT_URL,
@@ -85,9 +85,9 @@ function buildRequests(validBidRequests, bidderRequest) {
});
}
-function buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs) {
+function buildOpenRTBRequest(validBidRequest, bidderRequest, id, site, device, user, tmax, regs) {
// build cur
- const currency = config.getConfig('currency.adServerCurrency') || deepAccess(validBidRequest, 'params.currency');
+ const currency = getCurrencyFromBidderRequest(bidderRequest) || deepAccess(validBidRequest, 'params.currency');
const cur = currency && [currency];
// build impression
@@ -229,7 +229,7 @@ export function getBidFloor(bid, mediaType, size, currency) {
if (isFn(bid.getFloor)) {
const bidFloorCurrency = currency || 'USD';
const bidFloor = bid.getFloor({currency: bidFloorCurrency, mediaType: mediaType, size: size});
- if (isNumber(bidFloor.floor)) {
+ if (isNumber(bidFloor?.floor)) {
return bidFloor;
}
}
diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js
index 3e1a716d8e7..87ead9fe980 100644
--- a/modules/dfpAdServerVideo.js
+++ b/modules/dfpAdServerVideo.js
@@ -14,7 +14,6 @@ import { getRefererInfo } from '../src/refererDetection.js';
import { targeting } from '../src/targeting.js';
import {
buildUrl,
- deepAccess,
formatQS,
isEmpty,
isNumber,
@@ -90,7 +89,7 @@ export function buildDfpVideoUrl(options) {
const derivedParams = {
correlator: Date.now(),
- sz: parseSizesInput(deepAccess(adUnit, 'mediaTypes.video.playerSize')).join('|'),
+ sz: parseSizesInput(adUnit?.mediaTypes?.video?.playerSize).join('|'),
url: encodeURIComponent(location.href),
};
@@ -208,7 +207,7 @@ function buildUrlFromAdserverUrlComponents(components, bid, options) {
* @return {string | undefined} The encoded vast url if it exists, or undefined
*/
function getDescriptionUrl(bid, components, prop) {
- return deepAccess(components, `${prop}.description_url`) || encodeURIComponent(dep.ri().page);
+ return components?.[prop]?.description_url || encodeURIComponent(dep.ri().page);
}
/**
@@ -240,7 +239,7 @@ function getCustParams(bid, options, urlCustParams) {
events.emit(EVENTS.SET_TARGETING, {[adUnit.code]: prebidTargetingSet});
// merge the prebid + publisher targeting sets
- const publisherTargetingSet = deepAccess(options, 'params.cust_params');
+ const publisherTargetingSet = options?.params?.cust_params;
const targetingSet = Object.assign({}, prebidTargetingSet, publisherTargetingSet);
let encodedParams = encodeURIComponent(formatQS(targetingSet));
if (urlCustParams) {
diff --git a/modules/dianomiBidAdapter.js b/modules/dianomiBidAdapter.js
index 3ae8b4d6b80..3e90a76cf9e 100644
--- a/modules/dianomiBidAdapter.js
+++ b/modules/dianomiBidAdapter.js
@@ -15,6 +15,7 @@ import {
import { config } from '../src/config.js';
import { Renderer } from '../src/Renderer.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js';
const { getConfig } = config;
@@ -115,7 +116,7 @@ export const spec = {
setOnAny(validBidRequests, 'params.priceType') ||
'net';
const tid = bidderRequest.ortb2?.source?.tid;
- const currency = getConfig('currency.adServerCurrency');
+ const currency = getCurrencyFromBidderRequest(bidderRequest);
const cur = currency && [currency];
const eids = setOnAny(validBidRequests, 'userIdAsEids');
const schain = setOnAny(validBidRequests, 'schain');
@@ -128,8 +129,8 @@ export const spec = {
currency: currency || 'USD',
})
: {};
- const bidfloor = floorInfo.floor;
- const bidfloorcur = floorInfo.currency;
+ const bidfloor = floorInfo?.floor;
+ const bidfloorcur = floorInfo?.currency;
const { smartadId } = bid.params;
const imp = {
diff --git a/modules/dsp_genieeBidAdapter.js b/modules/dsp_genieeBidAdapter.js
index 57aafd47fc8..f97c13379f3 100644
--- a/modules/dsp_genieeBidAdapter.js
+++ b/modules/dsp_genieeBidAdapter.js
@@ -3,6 +3,7 @@ import { BANNER } from '../src/mediaTypes.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
import { deepAccess, deepSetValue } from '../src/utils.js';
import { config } from '../src/config.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -71,7 +72,7 @@ export const spec = {
if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || // gdpr
USPConsent(bidderRequest.uspConsent) || // usp
config.getConfig('coppa') || // coppa
- invalidCurrency(config.getConfig('currency.adServerCurrency')) // currency validation
+ invalidCurrency(getCurrencyFromBidderRequest(bidderRequest)) // currency validation
) {
return {
method: 'GET',
diff --git a/modules/ehealthcaresolutionsBidAdapter.js b/modules/ehealthcaresolutionsBidAdapter.js
new file mode 100644
index 00000000000..9df4c38e4f2
--- /dev/null
+++ b/modules/ehealthcaresolutionsBidAdapter.js
@@ -0,0 +1,41 @@
+import {
+ BANNER,
+ NATIVE
+} from '../src/mediaTypes.js';
+import {
+ registerBidder
+} from '../src/adapters/bidderFactory.js';
+import {
+ getBannerRequest,
+ getBannerResponse,
+ getNativeResponse,
+} from '../libraries/audUtils/bidderUtils.js';
+
+const ENDPOINT_URL = 'https://rtb.ehealthcaresolutions.com/hb';
+// Export const spec
+export const spec = {
+ code: 'ehealthcaresolutions',
+ supportedMediaTypes: [BANNER, NATIVE],
+ // Determines whether or not the given bid request is valid
+ isBidRequestValid: (bParam) => {
+ return !!(bParam.params.placement_id);
+ },
+ // Make a server request from the list of BidRequests
+ buildRequests: (bidRequests, serverRequest) => {
+ // Get Requests based on media types
+ return getBannerRequest(bidRequests, serverRequest, ENDPOINT_URL);
+ },
+ // Unpack the response from the server into a list of bids.
+ interpretResponse: (bResponse, bRequest) => {
+ let Response = {};
+ const mediaType = JSON.parse(bRequest.data)[0].MediaType;
+ if (mediaType == BANNER) {
+ Response = getBannerResponse(bResponse, BANNER);
+ } else if (mediaType == NATIVE) {
+ Response = getNativeResponse(bResponse, bRequest, NATIVE);
+ }
+ return Response;
+ }
+}
+
+registerBidder(spec);
diff --git a/modules/ehealthcaresolutionsBidAdapter.md b/modules/ehealthcaresolutionsBidAdapter.md
new file mode 100644
index 00000000000..fdf859404d2
--- /dev/null
+++ b/modules/ehealthcaresolutionsBidAdapter.md
@@ -0,0 +1,61 @@
+# Overview
+
+```
+Module Name: eHealthcareSolutions Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: info@ehsmail.com
+```
+
+# Description
+
+eHealthcareSolutions currently supports the BANNER and NATIVE type ads through prebid js
+
+Module that connects to eHealthcareSolutions's demand sources.
+
+# Test Request
+```
+ var adUnits = [
+ {
+ code: 'display-ad',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]],
+ }
+ }
+ bids: [
+ {
+ bidder: 'ehealthcaresolutions',
+ params: {
+ placement_id: 111519, // Required parameter
+ width: 300, // Optional parameter
+ height: 250, // Optional parameter
+ bid_floor: 0.5 // Optional parameter
+ }
+ }
+ ]
+ },
+ {
+ code: 'native-ad-container',
+ mediaTypes: {
+ native: {
+ title: { required: true, len: 100 },
+ image: { required: true, sizes: [300, 250] },
+ sponsored: { required: false },
+ clickUrl: { required: true },
+ desc: { required: true },
+ icon: { required: false, sizes: [50, 50] },
+ cta: { required: false }
+ }
+ },
+ bids: [
+ {
+ bidder: 'eHealthcareSolutions',
+ params: {
+ placement_id: 111519, // Required parameter
+ bid_floor: 1 // Optional parameter
+ }
+ }
+ ]
+ }
+ ];
+```
diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js
index d57804c04e6..1fe5cb09c10 100644
--- a/modules/eplanningBidAdapter.js
+++ b/modules/eplanningBidAdapter.js
@@ -274,7 +274,7 @@ function getFloorStr(bid) {
currency: DOLLAR_CODE,
mediaType: '*',
size: '*'
- });
+ }) || {};
if (bidFloor.floor) {
return '|' + encodeURIComponent(bidFloor.floor);
diff --git a/modules/equativBidAdapter.js b/modules/equativBidAdapter.js
index c7cb304d22b..a53597a9074 100644
--- a/modules/equativBidAdapter.js
+++ b/modules/equativBidAdapter.js
@@ -1,9 +1,10 @@
-import { BANNER } from '../src/mediaTypes.js';
+import { config } from '../src/config.js';
+import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { getBidFloor } from '../libraries/equativUtils/equativUtils.js'
+import { getStorageManager } from '../src/storageManager.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
-import { getStorageManager } from '../src/storageManager.js';
-import { deepAccess, deepSetValue, mergeDeep } from '../src/utils.js';
+import { deepAccess, deepSetValue, logError, logWarn, mergeDeep } from '../src/utils.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
@@ -13,14 +14,26 @@ import { deepAccess, deepSetValue, mergeDeep } from '../src/utils.js';
const BIDDER_CODE = 'equativ';
const COOKIE_SYNC_ORIGIN = 'https://apps.smartadserver.com';
const COOKIE_SYNC_URL = `${COOKIE_SYNC_ORIGIN}/diff/templates/asset/csync.html`;
+const LOG_PREFIX = 'Equativ:';
const PID_COOKIE_NAME = 'eqt_pid';
+/**
+ * Evaluates a bid request for validity. Returns false if the
+ * request contains a video media type with no properties, true
+ * otherwise.
+ * @param {*} bidReq A bid request object to evaluate
+ * @returns boolean
+ */
+function isValid(bidReq) {
+ return !(bidReq.mediaTypes.video && JSON.stringify(bidReq.mediaTypes.video) === '{}');
+}
+
export const storage = getStorageManager({ bidderCode: BIDDER_CODE });
export const spec = {
code: BIDDER_CODE,
gvlid: 45,
- supportedMediaTypes: [BANNER],
+ supportedMediaTypes: [BANNER, VIDEO],
/**
* @param bidRequests
@@ -28,11 +41,23 @@ export const spec = {
* @returns {ServerRequest[]}
*/
buildRequests: (bidRequests, bidderRequest) => {
- return {
- data: converter.toORTB({ bidderRequest, bidRequests }),
- method: 'POST',
- url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169'
- };
+ if (bidRequests.filter(isValid).length === 0) {
+ logError(`${LOG_PREFIX} No useful bid requests to process. No request will be sent.`, bidRequests);
+ return undefined;
+ }
+
+ const requests = [];
+
+ bidRequests.forEach(bid => {
+ const data = converter.toORTB({bidRequests: [bid], bidderRequest});
+ requests.push({
+ data,
+ method: 'POST',
+ url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169',
+ })
+ });
+
+ return requests;
},
/**
@@ -89,32 +114,23 @@ export const converter = ortbConverter({
imp(buildImp, bidRequest, context) {
const imp = buildImp(bidRequest, context);
+ const mediaType = deepAccess(bidRequest, 'mediaTypes.video') ? VIDEO : BANNER;
const { siteId, pageId, formatId } = bidRequest.params;
delete imp.dt;
- imp.bidfloor = imp.bidfloor || getBidFloor(bidRequest);
+ imp.bidfloor = imp.bidfloor || getBidFloor(bidRequest, config.getConfig('currency.adServerCurrency'), mediaType);
imp.secure = 1;
- imp.tagid = bidRequest.adUnitCode;
- if (siteId || pageId || formatId) {
- const bidder = {};
-
- if (siteId) {
- bidder.siteId = siteId;
- }
-
- if (pageId) {
- bidder.pageId = pageId;
- }
+ imp.tagid = bidRequest.adUnitCode;
- if (formatId) {
- bidder.formatId = formatId;
- }
+ if (!deepAccess(bidRequest, 'ortb2Imp.rwdd') && deepAccess(bidRequest, 'mediaTypes.video.ext.rewarded')) {
+ mergeDeep(imp, { rwdd: bidRequest.mediaTypes.video.ext.rewarded });
+ }
- mergeDeep(imp, {
- ext: { bidder },
- });
+ const bidder = { ...(siteId && { siteId }), ...(pageId && { pageId }), ...(formatId && { formatId }) };
+ if (Object.keys(bidder).length) {
+ mergeDeep(imp.ext, { bidder });
}
return imp;
@@ -124,14 +140,15 @@ export const converter = ortbConverter({
const bid = context.bidRequests[0];
const req = buildRequest(imps, bidderRequest, context);
- if (deepAccess(bid, 'ortb2.site.publisher')) {
- deepSetValue(req, 'site.publisher.id', bid.ortb2.site.publisher.id || bid.params.networkId);
- } else if (deepAccess(bid, 'ortb2.app.publisher')) {
- deepSetValue(req, 'app.publisher.id', bid.ortb2.app.publisher.id || bid.params.networkId);
- } else if (deepAccess(bid, 'ortb2.dooh.publisher')) {
- deepSetValue(req, 'dooh.publisher.id', bid.ortb2.dooh.publisher.id || bid.params.networkId);
- } else {
- deepSetValue(req, 'site.publisher.id', bid.params.networkId);
+ let env = ['ortb2.site.publisher', 'ortb2.app.publisher', 'ortb2.dooh.publisher'].find(propPath => deepAccess(bid, propPath)) || 'ortb2.site.publisher';
+ deepSetValue(req, env.replace('ortb2.', '') + '.id', deepAccess(bid, env + '.id') || bid.params.networkId);
+
+ if (deepAccess(bid, 'mediaTypes.video')) {
+ ['mimes', 'placement'].forEach(prop => {
+ if (!bid.mediaTypes.video[prop]) {
+ logWarn(`${LOG_PREFIX} Property "${prop}" is missing from request`, bid);
+ }
+ });
}
const pid = storage.getCookie(PID_COOKIE_NAME);
@@ -140,7 +157,7 @@ export const converter = ortbConverter({
}
return req;
- },
+ }
});
registerBidder(spec);
diff --git a/modules/eskimiBidAdapter.js b/modules/eskimiBidAdapter.js
index 36feda03ec1..8b1beba09c3 100644
--- a/modules/eskimiBidAdapter.js
+++ b/modules/eskimiBidAdapter.js
@@ -80,7 +80,7 @@ const CONVERTER = ortbConverter({
},
imp(buildImp, bidRequest, context) {
let imp = buildImp(bidRequest, context);
- imp.secure = Number(window.location.protocol === 'https:');
+ imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
if (!imp.bidfloor && bidRequest.params.bidFloor) {
imp.bidfloor = bidRequest.params.bidFloor;
imp.bidfloorcur = getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || 'USD'
diff --git a/modules/finativeBidAdapter.js b/modules/finativeBidAdapter.js
index e7613bf9cce..0cdcae15e61 100644
--- a/modules/finativeBidAdapter.js
+++ b/modules/finativeBidAdapter.js
@@ -4,8 +4,8 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {NATIVE} from '../src/mediaTypes.js';
import {_map, deepSetValue, isEmpty, setOnAny} from '../src/utils.js';
-import {config} from '../src/config.js';
import {convertOrtbRequestToProprietaryNative} from '../src/native.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const BIDDER_CODE = 'finative';
const DEFAULT_CUR = 'EUR';
@@ -64,7 +64,7 @@ export const spec = {
validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);
const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net';
const tid = bidderRequest.ortb2?.source?.tid;
- const cur = [config.getConfig('currency.adServerCurrency') || DEFAULT_CUR];
+ const cur = [getCurrencyFromBidderRequest(bidderRequest) || DEFAULT_CUR];
let url = bidderRequest.refererInfo.referer;
const imp = validBidRequests.map((bid, id) => {
diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js
index 0ac848bf62a..fc85edc483b 100644
--- a/modules/freewheel-sspBidAdapter.js
+++ b/modules/freewheel-sspBidAdapter.js
@@ -231,7 +231,7 @@ function getBidFloor(bid, config) {
mediaType: typeof bid.mediaTypes['banner'] == 'object' ? 'banner' : 'video',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
} catch (e) {
return -1;
}
diff --git a/modules/gameraRtdProvider.js b/modules/gameraRtdProvider.js
new file mode 100644
index 00000000000..96c4bac5f87
--- /dev/null
+++ b/modules/gameraRtdProvider.js
@@ -0,0 +1,107 @@
+import { submodule } from '../src/hook.js';
+import { getGlobal } from '../src/prebidGlobal.js';
+import {
+ isPlainObject,
+ logError,
+ mergeDeep,
+ deepClone,
+} from '../src/utils.js';
+
+const MODULE_NAME = 'gamera';
+const MODULE = `${MODULE_NAME}RtdProvider`;
+
+/**
+ * Initialize the Gamera RTD Module.
+ * @param {Object} config
+ * @param {Object} userConsent
+ * @returns {boolean}
+ */
+function init(config, userConsent) {
+ return true;
+}
+
+/**
+ * Modify bid request data before auction
+ * @param {Object} reqBidsConfigObj - The bid request config object
+ * @param {function} callback - Callback function to execute after data handling
+ * @param {Object} config - Module configuration
+ * @param {Object} userConsent - User consent data
+ */
+function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) {
+ // Check if window.gamera.getPrebidSegments is available
+ if (typeof window.gamera?.getPrebidSegments !== 'function') {
+ window.gamera = window.gamera || {};
+ window.gamera.cmd = window.gamera.cmd || [];
+ window.gamera.cmd.push(function () {
+ enrichAuction(reqBidsConfigObj, callback, config, userConsent);
+ });
+ return;
+ }
+
+ enrichAuction(reqBidsConfigObj, callback, config, userConsent);
+}
+
+/**
+ * Enriches the auction with user and content segments from Gamera's on-page script
+ * @param {Object} reqBidsConfigObj - The bid request config object
+ * @param {Function} callback - Callback function to execute after data handling
+ * @param {Object} config - Module configuration
+ * @param {Object} userConsent - User consent data
+ */
+function enrichAuction(reqBidsConfigObj, callback, config, userConsent) {
+ try {
+ /**
+ * @function external:"window.gamera".getPrebidSegments
+ * @description Retrieves user and content segments from Gamera's on-page script
+ * @param {Function|null} onSegmentsUpdateCallback - Callback for segment updates (not used here)
+ * @param {Object} config - Module configuration
+ * @param {Object} userConsent - User consent data
+ * @returns {Object|undefined} segments - The targeting segments object containing:
+ * @property {Object} [user] - User-level attributes to merge into ortb2.user
+ * @property {Object} [site] - Site-level attributes to merge into ortb2.site
+ * @property {Object.} [adUnits] - Ad unit specific attributes, keyed by adUnitCode,
+ * to merge into each ad unit's ortb2Imp
+ */
+ const segments = window.gamera.getPrebidSegments(null, deepClone(config || {}), deepClone(userConsent || {})) || {};
+
+ // Initialize ortb2Fragments and its nested objects
+ reqBidsConfigObj.ortb2Fragments = reqBidsConfigObj.ortb2Fragments || {};
+ reqBidsConfigObj.ortb2Fragments.global = reqBidsConfigObj.ortb2Fragments.global || {};
+
+ // Add user-level data
+ if (segments.user && isPlainObject(segments.user)) {
+ reqBidsConfigObj.ortb2Fragments.global.user = reqBidsConfigObj.ortb2Fragments.global.user || {};
+ mergeDeep(reqBidsConfigObj.ortb2Fragments.global.user, segments.user);
+ }
+
+ // Add site-level data
+ if (segments.site && isPlainObject(segments.site)) {
+ reqBidsConfigObj.ortb2Fragments.global.site = reqBidsConfigObj.ortb2Fragments.global.site || {};
+ mergeDeep(reqBidsConfigObj.ortb2Fragments.global.site, segments.site);
+ }
+
+ // Add adUnit-level data
+ const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits || [];
+ adUnits.forEach(adUnit => {
+ const gameraData = segments.adUnits && segments.adUnits[adUnit.code];
+ if (!gameraData || !isPlainObject(gameraData)) {
+ return;
+ }
+
+ adUnit.ortb2Imp = adUnit.ortb2Imp || {};
+ mergeDeep(adUnit.ortb2Imp, gameraData);
+ });
+ } catch (error) {
+ logError(MODULE, 'Error getting segments:', error);
+ }
+
+ callback();
+}
+
+export const subModuleObj = {
+ name: MODULE_NAME,
+ init: init,
+ getBidRequestData: getBidRequestData,
+};
+
+submodule('realTimeData', subModuleObj);
diff --git a/modules/gameraRtdProvider.md b/modules/gameraRtdProvider.md
new file mode 100644
index 00000000000..44260b88d1f
--- /dev/null
+++ b/modules/gameraRtdProvider.md
@@ -0,0 +1,51 @@
+# Overview
+
+Module Name: Gamera Rtd Provider
+Module Type: Rtd Provider
+Maintainer: aleksa@gamera.ai
+
+# Description
+
+RTD provider for Gamera.ai that enriches bid requests with real-time data, by populating the [First Party Data](https://docs.prebid.org/features/firstPartyData.html) attributes.
+The module integrates with Gamera's AI-powered audience segmentation system to provide enhanced bidding capabilities.
+The Gamera RTD Provider works in conjunction with the Gamera script, which must be available on the page for the module to enrich bid requests. To learn more about the Gamera script, please visit the [Gamera website](https://gamera.ai/).
+
+ORTB2 enrichments that gameraRtdProvider can provide:
+ * `ortb2.site`
+ * `ortb2.user`
+ * `AdUnit.ortb2Imp`
+
+# Integration
+
+## Build
+
+Include the Gamera RTD module in your Prebid.js build:
+
+```bash
+gulp build --modules=rtdModule,gameraRtdProvider
+```
+
+## Configuration
+
+Configure the module in your Prebid.js configuration:
+
+```javascript
+pbjs.setConfig({
+ realTimeData: {
+ dataProviders: [{
+ name: 'gamera',
+ params: {
+ // Optional configuration parameters
+ }
+ }]
+ }
+});
+```
+
+### Configuration Parameters
+
+The module currently supports basic initialization without required parameters. Future versions may include additional configuration options.
+
+## Support
+
+For more information or support, please contact gareth@gamera.ai.
diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js
index a8888893333..67a0e1e91be 100644
--- a/modules/getintentBidAdapter.js
+++ b/modules/getintentBidAdapter.js
@@ -152,7 +152,7 @@ function getBidFloor(bidRequest, currency) {
currency: currency || DEFAULT_CURRENCY,
mediaType: bidRequest.mediaType,
size: bidRequest.sizes || '*'
- });
+ }) || {};
}
return {
diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js
index d7af51f7312..e0a5861f40c 100644
--- a/modules/gmosspBidAdapter.js
+++ b/modules/gmosspBidAdapter.js
@@ -1,3 +1,7 @@
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER } from '../src/mediaTypes.js';
import {
createTrackPixelHtml,
deepAccess,
@@ -7,10 +11,6 @@ import {
isEmpty,
logError
} from '../src/utils.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {config} from '../src/config.js';
-import {BANNER} from '../src/mediaTypes.js';
-import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -50,7 +50,7 @@ export const spec = {
const bidRequests = [];
const urlInfo = getUrlInfo(bidderRequest.refererInfo);
- const cur = getCurrencyType();
+ const cur = getCurrencyType(bidderRequest);
const dnt = getDNT() ? '1' : '0';
for (let i = 0; i < validBidRequests.length; i++) {
@@ -156,11 +156,8 @@ export const spec = {
};
-function getCurrencyType() {
- if (config.getConfig('currency.adServerCurrency')) {
- return config.getConfig('currency.adServerCurrency');
- }
- return 'JPY';
+function getCurrencyType(bidderRequest) {
+ return getCurrencyFromBidderRequest(bidderRequest) || 'JPY';
}
function getUrlInfo(refererInfo) {
diff --git a/modules/goldfishAdsRtdProvider.md b/modules/goldfishAdsRtdProvider.md
index 4625c9a7988..9a6fd939bd1 100755
--- a/modules/goldfishAdsRtdProvider.md
+++ b/modules/goldfishAdsRtdProvider.md
@@ -8,7 +8,7 @@
## Description
-This RTD module provides access to the Goldfish Ads Geograph, which leverages geographic and temporal data on a privcay-first platform. This module works without using cookies, PII, emails, or device IDs across all website traffic, including unauthenticated users, and adds audience data into bid requests to increase scale and yields.
+This RTD module provides access to the Goldfish Ads Geograph, which leverages geographic and temporal data on a privacy-first platform. This module works without using cookies, PII, emails, or device IDs across all website traffic, including unauthenticated users, and adds audience data into bid requests to increase scale and yields.
## Usage
diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js
index 8d10a0f18d2..4f3dfb94747 100644
--- a/modules/gridBidAdapter.js
+++ b/modules/gridBidAdapter.js
@@ -7,7 +7,8 @@ import {
mergeDeep,
logWarn,
isNumber,
- isStr
+ isStr,
+ isPlainObject
} from '../src/utils.js';
import { ajax } from '../src/ajax.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
@@ -508,7 +509,7 @@ function _getFloor (mediaTypes, bid) {
size: bid.sizes.map(([w, h]) => ({w, h}))
});
- if (typeof floorInfo === 'object' &&
+ if (isPlainObject(floorInfo) &&
floorInfo.currency === 'USD' &&
!isNaN(parseFloat(floorInfo.floor))) {
floor = Math.max(floor, parseFloat(floorInfo.floor));
diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js
index dbfdbef2e91..2911522c89b 100644
--- a/modules/gumgumBidAdapter.js
+++ b/modules/gumgumBidAdapter.js
@@ -233,7 +233,7 @@ function _getFloor(mediaTypes, staticBidFloor, bid) {
const { currency, floor } = bid.getFloor({
mediaType: curMediaType,
size: '*'
- });
+ }) || {};
floor && (bidFloor.floor = floor);
currency && (bidFloor.currency = currency);
diff --git a/modules/hadronIdSystem.js b/modules/hadronIdSystem.js
index bdb8e634de6..ccd63bc0184 100644
--- a/modules/hadronIdSystem.js
+++ b/modules/hadronIdSystem.js
@@ -19,9 +19,9 @@ import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterM
* @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
*/
-const LOG_PREFIX = '[hadronIdSystem]';
-const HADRONID_LOCAL_NAME = 'auHadronId';
-const MODULE_NAME = 'hadronId';
+export const MODULE_NAME = 'hadronId';
+const LOG_PREFIX = `[${MODULE_NAME}System]`;
+export const LS_TAM_KEY = 'auHadronId';
const AU_GVLID = 561;
const DEFAULT_HADRON_URL_ENDPOINT = 'https://id.hadron.ad.gt/api/v1/pbhid';
@@ -68,11 +68,9 @@ export const hadronIdSubmodule = {
* @returns {Object}
*/
decode(value) {
- const hadronId = storage.getDataFromLocalStorage(HADRONID_LOCAL_NAME);
- if (isStr(hadronId)) {
- return {hadronId: hadronId};
+ return {
+ hadronId: isStr(value) ? value : value.hasOwnProperty('id') ? value.id[MODULE_NAME] : value[MODULE_NAME]
}
- return (value && typeof value['hadronId'] === 'string') ? {'hadronId': value['hadronId']} : undefined;
},
/**
* performs action to obtain id and return a value in the callback's response argument
@@ -81,14 +79,19 @@ export const hadronIdSubmodule = {
* @returns {IdResponse|undefined}
*/
getId(config) {
+ logInfo(LOG_PREFIX, `getId is called`, config);
if (!isPlainObject(config.params)) {
config.params = {};
}
- const partnerId = config.params.partnerId | 0;
- let hadronId = storage.getDataFromLocalStorage(HADRONID_LOCAL_NAME);
- if (isStr(hadronId)) {
- return {id: {hadronId}};
+ let hadronId = '';
+ // at this point hadronId was not found by prebid, let check if it is in the webpage by other ways
+ hadronId = storage.getDataFromLocalStorage(LS_TAM_KEY);
+ if (isStr(hadronId) && hadronId.length > 0) {
+ logInfo(LOG_PREFIX, `${LS_TAM_KEY} found in localStorage = ${hadronId}`)
+ // return {callback: function(cb) { cb(hadronId) }};
+ return {id: hadronId}
}
+ const partnerId = config.params.partnerId | 0;
const resp = function (callback) {
let responseObj = {};
const callbacks = {
@@ -98,11 +101,13 @@ export const hadronIdSubmodule = {
responseObj = JSON.parse(response);
} catch (error) {
logError(error);
+ callback();
}
logInfo(LOG_PREFIX, `Response from backend is ${response}`, responseObj);
- hadronId = responseObj['hadronId'];
- storage.setDataInLocalStorage(HADRONID_LOCAL_NAME, hadronId);
- responseObj = {id: {hadronId}};
+ if (isPlainObject(responseObj) && responseObj.hasOwnProperty(MODULE_NAME)) {
+ hadronId = responseObj[MODULE_NAME];
+ }
+ responseObj = hadronId; // {id: {hadronId: hadronId}};
}
callback(responseObj);
},
@@ -137,7 +142,7 @@ export const hadronIdSubmodule = {
url += `${gppConsent.applicableSections ? '&gpp_sid=' + encodeURIComponent(gppConsent.applicableSections) : ''}`;
}
- logInfo(LOG_PREFIX, `hadronId not found in storage, calling home (${url})`);
+ logInfo(LOG_PREFIX, `${MODULE_NAME} not found, calling home (${url})`);
ajax(url, callbacks, undefined, {method: 'GET'});
};
diff --git a/modules/hadronIdSystem.md b/modules/hadronIdSystem.md
index 212030cbcd9..f58cd46ef61 100644
--- a/modules/hadronIdSystem.md
+++ b/modules/hadronIdSystem.md
@@ -12,7 +12,7 @@ pbjs.setConfig({
userIds: [{
name: 'hadronId',
params: {
- partnerId: 1234 // change it to the Partner ID you'll get from Audigent
+ partnerId: 1234 // change it to the Partner ID you got from Audigent
},
storage: {
name: 'hadronId',
@@ -25,14 +25,13 @@ pbjs.setConfig({
## Parameter Descriptions for the `usersync` Configuration Section
The below parameters apply only to the HadronID User ID Module integration.
-| Param under usersync.userIds[] | Scope | Type | Description | Example |
-|--------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
-| name | Required | String | ID value for the HadronID module - `"hadronId"` | `"hadronId"` |
-| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | |
-| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` |
-| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"hadronid"` |
-| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. | `365` |
-| value | Optional | Object | Used only if the page has a separate mechanism for storing the Hadron ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"hadronId": "eb33b0cb-8d35-4722-b9c0-1a31d4064888"}` |
+| Param under usersync.userIds[] | Scope | Type | Description | Example |
+|--------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
+| name | Required | String | ID value for the HadronID module - `"hadronId"` | `"hadronId"` |
+| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | |
+| storage.type | Required | String | This is where the the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` |
+| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. The recommended value is `hadronId`. | `"auHadronId"` |
+| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. The recommended value is 14 days. | `14` |
+| value | Optional | Object | Used only if the page has a separate mechanism for storing the Hadron ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"hadronId": "0aRSTUAackg79ijgd8e8j6kah9ed9j6hdfgb6cl00volopxo00npzjmmb"}` |
| params | Optional | Object | Used to store params for the id system |
-| params.partnerId | Required | Number | This is the Audigent Partner ID obtained from Audigent. | `1234` |
- |
+| params.partnerId | Required | Number | This is the Audigent Partner ID obtained from Audigent. | `1234` |
diff --git a/modules/hadronRtdProvider.js b/modules/hadronRtdProvider.js
index f9a2eaed9c9..56f4861b41b 100644
--- a/modules/hadronRtdProvider.js
+++ b/modules/hadronRtdProvider.js
@@ -5,12 +5,11 @@
* @module modules/hadronRtdProvider
* @requires module:modules/realTimeData
*/
-import {ajax} from '../src/ajax.js';
import {config} from '../src/config.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {getStorageManager} from '../src/storageManager.js';
import {submodule} from '../src/hook.js';
-import {isFn, isStr, isArray, deepEqual, isPlainObject, logError, logInfo} from '../src/utils.js';
+import {isFn, isStr, isArray, deepEqual, isPlainObject, logInfo} from '../src/utils.js';
import {loadExternalScript} from '../src/adloader.js';
import {MODULE_TYPE_RTD} from '../src/activities/modules.js';
@@ -18,14 +17,13 @@ import {MODULE_TYPE_RTD} from '../src/activities/modules.js';
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
*/
-const LOG_PREFIX = 'User ID - HadronRtdProvider submodule: ';
+const LOG_PREFIX = '[HadronRtdProvider] ';
const MODULE_NAME = 'realTimeData';
const SUBMODULE_NAME = 'hadron';
const AU_GVLID = 561;
-const HADRON_ID_DEFAULT_URL = 'https://id.hadron.ad.gt/api/v1/hadronid?_it=prebid';
-const HADRON_SEGMENT_URL = 'https://id.hadron.ad.gt/api/v1/rtd';
-export const HADRONID_LOCAL_NAME = 'auHadronId';
-export const RTD_LOCAL_NAME = 'auHadronRtd';
+const HADRON_JS_URL = 'https://cdn.hadronid.net/hadron.js';
+const LS_TAM_KEY = 'auHadronId';
+const RTD_LOCAL_NAME = 'auHadronRtd';
export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME});
/**
@@ -37,19 +35,6 @@ const urlAddParams = (url, params) => {
return url + (url.indexOf('?') > -1 ? '&' : '?') + params
};
-/**
- * Deep set an object unless value present.
- * @param {Object} obj
- * @param {String} path
- * @param {Object} val
- */
-function set(obj, path, val) {
- const keys = path.split('.');
- const lastKey = keys.pop();
- const lastObj = keys.reduce((obj, key) => obj[key] = obj[key] || {}, obj);
- lastObj[lastKey] = lastObj[lastKey] || val;
-}
-
/**
* Deep object merging with array deduplication.
* @param {Object} target
@@ -62,11 +47,11 @@ function mergeDeep(target, ...sources) {
if (isPlainObject(target) && isPlainObject(source)) {
for (const key in source) {
if (isPlainObject(source[key])) {
- if (!target[key]) Object.assign(target, { [key]: {} });
+ if (!target[key]) Object.assign(target, {[key]: {}});
mergeDeep(target[key], source[key]);
} else if (isArray(source[key])) {
if (!target[key]) {
- Object.assign(target, { [key]: source[key] });
+ Object.assign(target, {[key]: source[key]});
} else if (isArray(target[key])) {
source[key].forEach(obj => {
let e = 1;
@@ -82,7 +67,7 @@ function mergeDeep(target, ...sources) {
});
}
} else {
- Object.assign(target, { [key]: source[key] });
+ Object.assign(target, {[key]: source[key]});
}
}
}
@@ -150,6 +135,17 @@ export function addRealTimeData(bidConfig, rtd, rtdConfig) {
* @param {Object} userConsent
*/
export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {
+ if (!storage.getDataFromLocalStorage(LS_TAM_KEY)) {
+ const partnerId = rtdConfig.params.partnerId | 0;
+ const hadronIdUrl = rtdConfig.params && rtdConfig.params.hadronIdUrl;
+ const scriptUrl = urlAddParams(
+ paramOrDefault(hadronIdUrl, HADRON_JS_URL, {}),
+ `partner_id=${partnerId}&_it=prebid`
+ );
+ loadExternalScript(scriptUrl, SUBMODULE_NAME, () => {
+ logInfo(LOG_PREFIX, 'hadronId JS snippet loaded', scriptUrl);
+ })
+ }
if (rtdConfig && isPlainObject(rtdConfig.params) && rtdConfig.params.segmentCache) {
let jsonData = storage.getDataFromLocalStorage(RTD_LOCAL_NAME);
@@ -166,81 +162,19 @@ export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {
const userIds = {};
- let hadronId = storage.getDataFromLocalStorage(HADRONID_LOCAL_NAME);
- if (isStr(hadronId)) {
- if (typeof getGlobal().refreshUserIds === 'function') {
- (getGlobal()).refreshUserIds({submoduleNames: 'hadronId'});
- }
- userIds.hadronId = hadronId;
- getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds);
+ const allUserIds = getGlobal().getUserIds();
+ if (allUserIds.hasOwnProperty('hadronId')) {
+ userIds['hadronId'] = allUserIds.hadronId;
+ logInfo(LOG_PREFIX, 'hadronId user module found', allUserIds.hadronId);
} else {
- window.pubHadronCb = (hadronId) => {
- userIds.hadronId = hadronId;
- getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds);
+ let hadronId = storage.getDataFromLocalStorage(LS_TAM_KEY);
+ if (isStr(hadronId) && hadronId.length > 0) {
+ userIds['hadronId'] = hadronId;
+ logInfo(LOG_PREFIX, 'hadronId TAM found', hadronId);
}
- const partnerId = rtdConfig.params.partnerId | 0;
- const hadronIdUrl = rtdConfig.params && rtdConfig.params.hadronIdUrl;
- const scriptUrl = urlAddParams(
- paramOrDefault(hadronIdUrl, HADRON_ID_DEFAULT_URL, userIds),
- `partner_id=${partnerId}&_it=prebid`
- );
- loadExternalScript(scriptUrl, MODULE_TYPE_RTD, 'hadron', () => {
- logInfo(LOG_PREFIX, 'hadronIdTag loaded', scriptUrl);
- })
}
}
-/**
- * Async rtd retrieval from Audigent
- * @param {Object} bidConfig
- * @param {function} onDone
- * @param {Object} rtdConfig
- * @param {Object} userConsent
- * @param {Object} userIds
- */
-export function getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds) {
- let reqParams = {};
-
- if (isPlainObject(rtdConfig)) {
- set(rtdConfig, 'params.requestParams.ortb2', bidConfig.ortb2Fragments.global);
- reqParams = rtdConfig.params.requestParams;
- }
-
- if (isPlainObject(window.pubHadronPm)) {
- reqParams.pubHadronPm = window.pubHadronPm;
- }
-
- ajax(HADRON_SEGMENT_URL, {
- success: function (response, req) {
- if (req.status === 200) {
- try {
- const data = JSON.parse(response);
- if (data && data.rtd) {
- addRealTimeData(bidConfig, data.rtd, rtdConfig);
- onDone();
- storage.setDataInLocalStorage(RTD_LOCAL_NAME, JSON.stringify(data));
- } else {
- onDone();
- }
- } catch (err) {
- logError('unable to parse audigent segment data');
- onDone();
- }
- } else if (req.status === 204) {
- // unrecognized partner config
- onDone();
- }
- },
- error: function () {
- onDone();
- logError('unable to get audigent segment data');
- }
- },
- JSON.stringify({'userIds': userIds, 'config': reqParams}),
- {contentType: 'application/json'}
- );
-}
-
/**
* Module init
* @param {Object} provider
diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js
index c72d21d08b4..90bc0c78212 100644
--- a/modules/holidBidAdapter.js
+++ b/modules/holidBidAdapter.js
@@ -93,13 +93,13 @@ export const spec = {
}
const bidders = getBidders(serverResponse)
-
- if (optionsType.iframeEnabled && bidders) {
+ // note this only does the iframe sync when gdpr consent object exists to match previous behavior (generate error on gdprconsent not existing)
+ if (optionsType.iframeEnabled && bidders && gdprConsent) {
const queryParams = []
queryParams.push('bidders=' + bidders)
- queryParams.push('gdpr=' + +gdprConsent.gdprApplies)
- queryParams.push('gdpr_consent=' + gdprConsent.consentString)
+ queryParams.push('gdpr=' + +gdprConsent?.gdprApplies)
+ queryParams.push('gdpr_consent=' + gdprConsent?.consentString)
queryParams.push('usp_consent=' + (uspConsent || ''))
let strQueryParams = queryParams.join('&')
diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js
index 79522f4a089..fcd9360d973 100644
--- a/modules/impactifyBidAdapter.js
+++ b/modules/impactifyBidAdapter.js
@@ -1,6 +1,6 @@
'use strict';
-import { deepAccess, deepSetValue, generateUUID } from '../src/utils.js';
+import { deepAccess, deepSetValue, generateUUID, isPlainObject } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { ajax } from '../src/ajax.js';
@@ -98,7 +98,7 @@ const helpers = {
mediaType: '*',
size: '*'
});
- if (typeof floorInfo === 'object' && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
return parseFloat(floorInfo.floor);
}
return null;
diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js
index 158c2bd6c75..33ba08e8fd2 100644
--- a/modules/improvedigitalBidAdapter.js
+++ b/modules/improvedigitalBidAdapter.js
@@ -153,7 +153,7 @@ export const CONVERTER = ortbConverter({
},
imp(buildImp, bidRequest, context) {
const imp = buildImp(bidRequest, context);
- imp.secure = Number(window.location.protocol === 'https:');
+ imp.secure = bidRequest.ortb2Imp?.secure ?? 1;
if (!imp.bidfloor && bidRequest.params.bidFloor) {
imp.bidfloor = bidRequest.params.bidFloor;
imp.bidfloorcur = getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || DEFAULT_CURRENCY;
diff --git a/modules/inmobiBidAdapter.js b/modules/inmobiBidAdapter.js
new file mode 100644
index 00000000000..910c53bf838
--- /dev/null
+++ b/modules/inmobiBidAdapter.js
@@ -0,0 +1,347 @@
+import { deepAccess, deepSetValue, isFn } from '../src/utils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
+import { ortbConverter } from '../libraries/ortbConverter/converter.js';
+import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js';
+import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js';
+
+/**
+ * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
+ * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
+ * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest
+ * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec
+ * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid
+ */
+
+const GVLID = 333;
+export const ADAPTER_VERSION = 1.0;
+const BIDDER_CODE = 'inmobi';
+const BID_ENDPOINT = 'https://api.w.inmobi.com/openrtb/bidder/prebidjs';
+export const EVENT_ENDPOINT = 'https://sync.inmobi.com';
+export const SYNC_ENDPOINT = 'https://sync.inmobi.com/prebidjs?';
+const TRANSLATOR = ortb25Translator();
+const CURRENCY = 'USD';
+const POST_METHOD = 'POST';
+const PLAIN_CONTENT_TYPE = 'text/plain';
+
+/**
+ * Defines the core oRTB converter inherited from converter library and all customization functions.
+ */
+const CONVERTER = ortbConverter({
+ context: {
+ netRevenue: true,
+ ttl: 3600,
+ currency: CURRENCY
+ },
+ imp,
+ request,
+ bidResponse,
+ response
+});
+
+/**
+ * Builds an impression object for oRTB 2.5 requests based on the bid request.
+ *
+ * @param {function} buildImp - Function to build the imp object.
+ * @param {Object} bidRequest - The request containing bid details.
+ * @param {Object} context - Context for the impression.
+ * @returns {Object} The constructed impression object.
+ */
+function imp(buildImp, bidRequest, context) {
+ let imp = buildImp(bidRequest, context);
+ const params = bidRequest.params;
+
+ imp.tagid = bidRequest.adUnitCode;
+ let floorInfo = {};
+
+ if (isFn(bidRequest.getFloor)) {
+ floorInfo = bidRequest.getFloor({
+ currency: CURRENCY,
+ size: '*',
+ mediaType: '*'
+ });
+ }
+
+ // if floor price module is not set reading from bidRequest.params
+ if (!imp.bidfloor && bidRequest.params.bidfloor) {
+ imp.bidfloor = bidRequest.params.bidfloor;
+ imp.bidfloorcur = CURRENCY;
+ }
+
+ deepSetValue(imp, 'ext', {
+ ...imp.ext,
+ params: bidRequest.params,
+ bidder: {
+ plc: params?.plc,
+ },
+ moduleFloors: floorInfo
+ });
+ imp.secure = Number(window.location.protocol === 'https:');
+
+ return imp;
+}
+
+/**
+ * Constructs the oRTB 2.5 request object.
+ *
+ * @param {function} buildRequest - Function to build the request.
+ * @param {Array} imps - Array of impression objects.
+ * @param {Object} bidderRequest - Object containing bidder request information.
+ * @param {Object} context - Additional context.
+ * @returns {Object} The complete oRTB request object.
+ */
+function request(buildRequest, imps, bidderRequest, context) {
+ let request = buildRequest(imps, bidderRequest, context);
+
+ deepSetValue(request, 'ext.prebid.channel.name', 'pbjs_InMobi');
+ deepSetValue(request, 'ext.prebid.channel.pbjsversion', '$prebid.version$');
+ deepSetValue(request, 'ext.prebid.channel.adapterversion', ADAPTER_VERSION);
+
+ request = TRANSLATOR(request);
+ return request;
+}
+
+/**
+ * Transforms an oRTB 2.5 bid into a bid response format for Prebid.js.
+ *
+ * @param {function} buildBidResponse - Function to build a bid response.
+ * @param {Object} bid - The bid to be transformed.
+ * @param {Object} context - Context for the bid.
+ * @returns {Object} Formatted bid response.
+ */
+function bidResponse(buildBidResponse, bid, context) {
+ context.mtype = deepAccess(bid, 'mtype');
+ if (context.mtype === 4) {
+ const admJson = JSON.parse(bid.adm);
+ bid.adm = JSON.stringify(admJson.native);
+ }
+ let bidResponse = buildBidResponse(bid, context);
+
+ if (typeof deepAccess(bid, 'ext') !== 'undefined') {
+ deepSetValue(bidResponse, 'meta', {
+ ...bidResponse.meta,
+ ...bid.ext,
+ });
+ }
+
+ return bidResponse;
+}
+
+/**
+ * Converts the oRTB 2.5 bid response into the format required by Prebid.js.
+ *
+ * @param {function} buildResponse - Function to build the response.
+ * @param {Array} bidResponses - List of bid responses.
+ * @param {Object} ortbResponse - Original oRTB response data.
+ * @param {Object} context - Additional context.
+ * @returns {Object} Prebid.js compatible bid response.
+ */
+function response(buildResponse, bidResponses, ortbResponse, context) {
+ let response = buildResponse(bidResponses, ortbResponse, context);
+
+ return response;
+}
+
+/** @type {BidderSpec} */
+export const spec = {
+ code: BIDDER_CODE,
+ gvlid: GVLID,
+ supportedMediaTypes: [BANNER, VIDEO, NATIVE],
+
+ /**
+ * Determines user sync options based on consent and supported sync types.
+ *
+ * @param {Object} syncOptions - Options for user syncing (iframe, pixel).
+ * @param {Array} responses - List of bid responses.
+ * @param {Object} gdprConsent - GDPR consent details.
+ * @param {Object} uspConsent - CCPA consent details.
+ * @param {Object} gppConsent - GPP consent details.
+ * @returns {Array} List of user sync URLs.
+ */
+ getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent, gppConsent) => {
+ const urls = [];
+ if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) {
+ return urls;
+ }
+ const pixelType = syncOptions.iframeEnabled ? 'iframe' : 'image';
+
+ let query = '';
+ if (gdprConsent) {
+ query = tryAppendQueryString(query, 'gdpr', (gdprConsent.gdprApplies ? 1 : 0));
+ }
+ if (gdprConsent && typeof gdprConsent.consentString === 'string') {
+ query = tryAppendQueryString(query, 'gdpr_consent', gdprConsent.consentString);
+ }
+ if (uspConsent) {
+ query = tryAppendQueryString(query, 'us_privacy', uspConsent);
+ }
+ if (gppConsent?.gppString && gppConsent?.applicableSections?.length) {
+ query = tryAppendQueryString(query, 'gpp', gppConsent.gppString);
+ query = tryAppendQueryString(query, 'gpp_sid', gppConsent?.applicableSections?.join(','));
+ }
+ if (query.slice(-1) === '&') {
+ query = query.slice(0, -1);
+ }
+
+ if (pixelType === 'iframe' || (!responses || responses.length === 0)) {
+ return [{
+ type: pixelType,
+ url: SYNC_ENDPOINT + query
+ }];
+ } else {
+ responses.forEach(resp => {
+ const userSyncs = deepAccess(resp, 'body.ext.prebidjs.urls');
+ if (!userSyncs) {
+ return;
+ }
+
+ userSyncs.forEach(us => {
+ let url = us.url;
+ if (query) {
+ url = url + (url.indexOf('?') === -1 ? '?' : '&') + query;
+ }
+
+ urls.push({
+ type: pixelType,
+ url: url
+ });
+ });
+ });
+ return urls;
+ }
+ },
+
+ /**
+ * Validates if a bid request contains the required parameters for InMobi.
+ *
+ * @param {Object} bid - Bid request to validate.
+ * @returns {boolean} True if the bid request is valid, otherwise false.
+ */
+ isBidRequestValid: (bid) => {
+ if (!(bid && bid.params && bid.params.plc)) {
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Builds the server request from bid requests for InMobi.
+ *
+ * @param {BidRequest[]} bidRequests - Array of bid requests.
+ * @param {Object} bidderRequest - Additional request details.
+ * @returns {ServerRequest} The server request for bidding.
+ */
+ buildRequests: (bidRequests, bidderRequest) => {
+ const data = CONVERTER.toORTB({ bidderRequest, bidRequests });
+
+ if (data) {
+ const requestPayload = {
+ method: POST_METHOD,
+ url: BID_ENDPOINT,
+ data: data,
+ options: {
+ contentType: PLAIN_CONTENT_TYPE,
+ crossOrigin: true,
+ withCredentials: true
+ }
+ };
+ return requestPayload;
+ }
+ },
+
+ /**
+ * Interprets the server response and formats it into bids.
+ *
+ * @param {Object} response - Response from the server.
+ * @param {ServerRequest} request - Original bid request.
+ * @returns {Bid[]} Parsed bids or configurations.
+ */
+ interpretResponse: (response, request) => {
+ if (typeof response?.body == 'undefined') {
+ return [];
+ }
+
+ const interpretedResponse = CONVERTER.fromORTB({ response: response.body, request: request.data });
+ const bids = interpretedResponse.bids || [];
+
+ return bids;
+ },
+
+ /**
+ * Callback to report timeout event.
+ *
+ * @param {TimedOutBid[]} timeoutData - Array of timeout details.
+ */
+ onTimeout: (timeoutData) => {
+ report('onTimeout', timeoutData);
+ },
+
+ /**
+ * Callback to report targeting event.
+ *
+ * @param {Bid} bid - The bid object
+ */
+ onSetTargeting: (bid) => {
+ report('onSetTargeting', bid?.meta);
+ },
+
+ /**
+ * Callback to report successful ad render event.
+ *
+ * @param {Bid} bid - The bid that successfully rendered.
+ */
+ onAdRenderSucceeded: (bid) => {
+ report('onAdRenderSucceeded', bid?.meta);
+ },
+
+ /**
+ * Callback to report bidder error event.
+ *
+ * @param {Object} errorData - Details about the error.
+ */
+ onBidderError: (errorData) => {
+ report('onBidderError', errorData);
+ },
+
+ /**
+ * Callback to report bid won event.
+ *
+ * @param {Bid} bid - The bid that won the auction.
+ */
+ onBidWon: (bid) => {
+ report('onBidWon', bid?.meta);
+ }
+
+};
+
+function isReportingAllowed(loggingPercentage) {
+ return loggingPercentage != 0;
+}
+
+function report(type, data) {
+ if (!data) {
+ return;
+ }
+ if (['onBidWon', 'onAdRenderSucceeded', 'onSetTargeting'].includes(type) && !isReportingAllowed(data.loggingPercentage)) {
+ return;
+ }
+ const payload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: data
+ });
+
+ fetch(`${EVENT_ENDPOINT}/report/${type}`, {
+ body: payload,
+ keepalive: true,
+ credentials: 'include',
+ method: POST_METHOD,
+ headers: {
+ 'Content-Type': PLAIN_CONTENT_TYPE
+ }
+ }).catch((_e) => {
+ // do nothing; ignore errors
+ });
+}
+
+registerBidder(spec);
diff --git a/modules/inmobiBidAdapter.md b/modules/inmobiBidAdapter.md
new file mode 100644
index 00000000000..bf309fcb01d
--- /dev/null
+++ b/modules/inmobiBidAdapter.md
@@ -0,0 +1,87 @@
+# Overview
+
+Module Name: InMobi Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: prebid-support@inmobi.com
+
+# Description
+
+Module that connects to InMobi's demand sources.
+
+# Bid Params
+
+| Name | Scope | Description | Example | Type |
+|------------|----------|--------------|-----------|-------- |
+| `plc` | required | Placement ID | `'1234'` | `string` |
+| `bidfloor` | optional | Bid Floor | `1.2` | `float` |
+
+
+# Test Parameters
+
+## Banner
+```
+ var adUnits = [{
+ code: 'div-gpt-ad-1460505748561-0',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]],
+ }
+ },
+
+ bids: [{
+ bidder: 'inmobi',
+ params: {
+ plc: '1719108420057' // Mandatory
+ }
+ }]
+
+ }];
+```
+
+## Video
+```
+ var adUnits = [{
+ code: 'div-gpt-ad-1460505748561-0',
+ mediaTypes: {
+ video: {
+ playerSize : [300,250],
+ mimes : ["video/x-ms-wmv", "video/mp4"],
+ minduration : 0,
+ maxduration: 30,
+ protocols : [1,2],
+ api: [1, 2, 4, 6],
+ protocols: [3, 4, 7, 8, 10],
+ placement: 1,
+ plcmt: 1
+ }
+ },
+
+ // Replace this object to test a new Adapter!
+ bids: [{
+ bidder: 'inmobi',
+ params: {
+ plc: '1443164204446401' //Mandatory
+ }
+ }]
+ }];
+```
+
+## Native
+```
+ var adUnits = [{
+ code: 'div-gpt-ad-1460505748561-0',
+ mediaTypes: {
+ native: {
+ type: 'image'
+ }
+ },
+
+ bids: [{
+ bidder: 'inmobi',
+ params: {
+ plc: '10000033152',
+ bidfloor: 0.9
+ }
+ }]
+ }];
+```
\ No newline at end of file
diff --git a/modules/intentIqAnalyticsAdapter.js b/modules/intentIqAnalyticsAdapter.js
index 7962080a138..1cf270117b7 100644
--- a/modules/intentIqAnalyticsAdapter.js
+++ b/modules/intentIqAnalyticsAdapter.js
@@ -1,4 +1,4 @@
-import {getWindowLocation, getWindowSelf, getWindowTop, logError, logInfo} from '../src/utils.js';
+import {logError, logInfo} from '../src/utils.js';
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
import {ajax} from '../src/ajax.js';
@@ -6,7 +6,9 @@ import {getStorageManager} from '../src/storageManager.js';
import {config} from '../src/config.js';
import {EVENTS} from '../src/constants.js';
import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js';
-import {detectBrowser} from '../libraries/detectBrowserUtils/detectBrowserUtils.js';
+import {detectBrowser} from '../libraries/intentIqUtils/detectBrowserUtils.js';
+import {appendVrrefAndFui, getReferrer} from '../libraries/intentIqUtils/getRefferer.js';
+import {getGppValue} from '../libraries/intentIqUtils/getGppValue.js';
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY, VERSION} from '../libraries/intentIqConstants/intentIqConstants.js';
const MODULE_NAME = 'iiqAnalytics'
@@ -49,7 +51,8 @@ const PARAMS_NAMES = {
isInBrowserBlacklist: 'inbbl',
prebidVersion: 'pbjsver',
partnerId: 'partnerId',
- firstPartyId: 'pcid'
+ firstPartyId: 'pcid',
+ placementId: 'placementId'
};
let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({defaultUrl, analyticsType}), {
@@ -61,7 +64,8 @@ let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({defaultUrl, analyticsT
dataInLs: null,
eidl: null,
lsIdsInitialized: false,
- manualWinReportEnabled: false
+ manualWinReportEnabled: false,
+ domainName: null
},
track({eventType, args}) {
switch (eventType) {
@@ -114,7 +118,8 @@ function initLsValues() {
iiqAnalyticsAnalyticsAdapter.initOptions.partner = iiqArr[0].params.partner;
}
iiqAnalyticsAnalyticsAdapter.initOptions.browserBlackList = typeof iiqArr[0].params.browserBlackList === 'string' ? iiqArr[0].params.browserBlackList.toLowerCase() : '';
- iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = iiqArr[0].params.manualWinReportEnabled || false
+ iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = iiqArr[0].params.manualWinReportEnabled || false;
+ iiqAnalyticsAnalyticsAdapter.initOptions.domainName = iiqArr[0].params.domainName || '';
}
}
@@ -134,6 +139,10 @@ function initReadLsIds() {
iiqAnalyticsAnalyticsAdapter.initOptions.terminationCause = pData.terminationCause
iiqAnalyticsAnalyticsAdapter.initOptions.dataInLs = pData.data;
iiqAnalyticsAnalyticsAdapter.initOptions.eidl = pData.eidl || -1;
+ iiqAnalyticsAnalyticsAdapter.initOptions.ct = pData.ct || null;
+ iiqAnalyticsAnalyticsAdapter.initOptions.siteId = pData.siteId || null;
+ iiqAnalyticsAnalyticsAdapter.initOptions.wsrvcll = pData.wsrvcll || false;
+ iiqAnalyticsAnalyticsAdapter.initOptions.rrtt = pData.rrtt || null;
}
iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints = clientsHints
@@ -194,13 +203,18 @@ export function preparePayload(data) {
result[PARAMS_NAMES.referrer] = getReferrer();
result[PARAMS_NAMES.terminationCause] = iiqAnalyticsAnalyticsAdapter.initOptions.terminationCause;
result[PARAMS_NAMES.abTestGroup] = iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup;
+ result[PARAMS_NAMES.clientType] = iiqAnalyticsAnalyticsAdapter.initOptions.ct;
+ result[PARAMS_NAMES.siteId] = iiqAnalyticsAnalyticsAdapter.initOptions.siteId;
+ result[PARAMS_NAMES.wasServerCalled] = iiqAnalyticsAnalyticsAdapter.initOptions.wsrvcll;
+ result[PARAMS_NAMES.requestRtt] = iiqAnalyticsAnalyticsAdapter.initOptions.rrtt;
result[PARAMS_NAMES.isInTestGroup] = iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup == 'A';
result[PARAMS_NAMES.agentId] = REPORTER_ID;
- if (iiqAnalyticsAnalyticsAdapter.initOptions.fpid?.pcid) result[PARAMS_NAMES.firstPartyId] = encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pcid)
+ if (iiqAnalyticsAnalyticsAdapter.initOptions.fpid?.pcid) result[PARAMS_NAMES.firstPartyId] = encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pcid);
+ if (iiqAnalyticsAnalyticsAdapter.initOptions.fpid?.pid) result[PARAMS_NAMES.profile] = encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pid)
- fillPrebidEventData(data, result);
+ prepareData(data, result);
fillEidsData(result);
@@ -214,27 +228,46 @@ function fillEidsData(result) {
}
}
-function fillPrebidEventData(eventData, result) {
- if (eventData.bidderCode) {
- result.bidderCode = eventData.bidderCode;
+function prepareData (data, result) {
+ if (data.bidderCode) {
+ result.bidderCode = data.bidderCode;
}
- if (eventData.cpm) {
- result.cpm = eventData.cpm;
+ if (data.cpm) {
+ result.cpm = data.cpm;
}
- if (eventData.currency) {
- result.currency = eventData.currency;
+ if (data.currency) {
+ result.currency = data.currency;
}
- if (eventData.originalCpm) {
- result.originalCpm = eventData.originalCpm;
+ if (data.originalCpm) {
+ result.originalCpm = data.originalCpm;
}
- if (eventData.originalCurrency) {
- result.originalCurrency = eventData.originalCurrency;
+ if (data.originalCurrency) {
+ result.originalCurrency = data.originalCurrency;
}
- if (eventData.status) {
- result.status = eventData.status;
+ if (data.status) {
+ result.status = data.status;
}
- if (eventData.auctionId) {
- result.prebidAuctionId = eventData.auctionId;
+ if (data.auctionId) {
+ result.prebidAuctionId = data.auctionId;
+ }
+ if (data.placementId) {
+ result.placementId = data.placementId;
+ } else {
+ // Simplified placementId determination
+ let placeIdFound = false;
+ if (data.params && Array.isArray(data.params)) {
+ for (let i = 0; i < data.params.length; i++) {
+ const param = data.params[i];
+ if (param.placementId) {
+ result.placementId = param.placementId;
+ placeIdFound = true;
+ break;
+ }
+ }
+ }
+ if (!placeIdFound && data.adUnitCode) {
+ result.placementId = data.adUnitCode;
+ }
}
result.biddingPlatformId = 1;
@@ -258,32 +291,24 @@ function getDefaultDataObject() {
}
function constructFullUrl(data) {
- let report = []
- data = btoa(JSON.stringify(data))
- report.push(data)
- return defaultUrl + '?pid=' + iiqAnalyticsAnalyticsAdapter.initOptions.partner +
+ let report = [];
+ data = btoa(JSON.stringify(data));
+ report.push(data);
+ const gppData = getGppValue();
+
+ let url = defaultUrl + '?pid=' + iiqAnalyticsAnalyticsAdapter.initOptions.partner +
'&mct=1' +
- ((iiqAnalyticsAnalyticsAdapter.initOptions && iiqAnalyticsAnalyticsAdapter.initOptions.fpid)
+ ((iiqAnalyticsAnalyticsAdapter.initOptions?.fpid)
? '&iiqid=' + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pcid) : '') +
'&agid=' + REPORTER_ID +
'&jsver=' + VERSION +
- '&vrref=' + getReferrer() +
'&source=pbjs' +
'&payload=' + JSON.stringify(report) +
- '&uh=' + iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints
-}
+ '&uh=' + iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints +
+ (gppData.gppString ? '&gpp=' + encodeURIComponent(gppData.gppString) : '');
-export function getReferrer() {
- try {
- if (getWindowSelf() === getWindowTop()) {
- return encodeURIComponent(getWindowLocation().href);
- } else {
- return encodeURIComponent(getWindowTop().location.href);
- }
- } catch (error) {
- logError(`Error accessing location: ${error}`);
- return '';
- }
+ url = appendVrrefAndFui(url, iiqAnalyticsAnalyticsAdapter.initOptions.domainName);
+ return url;
}
iiqAnalyticsAnalyticsAdapter.originEnableAnalytics = iiqAnalyticsAnalyticsAdapter.enableAnalytics;
diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js
index 069a955477b..4a7d0f47b18 100644
--- a/modules/intentIqIdSystem.js
+++ b/modules/intentIqIdSystem.js
@@ -10,10 +10,12 @@ import {ajax} from '../src/ajax.js';
import {submodule} from '../src/hook.js'
import {getStorageManager} from '../src/storageManager.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
-import {gppDataHandler, uspDataHandler} from '../src/consentHandler.js';
+import {uspDataHandler} from '../src/consentHandler.js';
import AES from 'crypto-js/aes.js';
import Utf8 from 'crypto-js/enc-utf8.js';
-import {detectBrowser} from '../libraries/detectBrowserUtils/detectBrowserUtils.js';
+import {detectBrowser} from '../libraries/intentIqUtils/detectBrowserUtils.js';
+import {appendVrrefAndFui} from '../libraries/intentIqUtils/getRefferer.js';
+import {getGppValue} from '../libraries/intentIqUtils/getGppValue.js';
import {
FIRST_PARTY_KEY,
WITH_IIQ, WITHOUT_IIQ,
@@ -157,22 +159,6 @@ function tryParse(data) {
}
}
-/**
- * Convert GPP data to an object
- * @param {Object} data
- * @return {string} The JSON string representation of the input data.
- */
-export function handleGPPData(data = {}) {
- if (Array.isArray(data)) {
- let obj = {};
- for (const element of data) {
- obj = Object.assign(obj, element);
- }
- return JSON.stringify(obj);
- }
- return JSON.stringify(data);
-}
-
/**
* Processes raw client hints data into a structured format.
* @param {object} clientHints - Raw client hints data
@@ -230,17 +216,19 @@ export const intentIqIdSubmodule = {
getId(config) {
const configParams = (config?.params) || {};
let decryptedData, callbackTimeoutID;
- let callbackFired = false
- let runtimeEids = {}
+ let callbackFired = false;
+ let runtimeEids = { eids: [] };
const allowedStorage = defineStorageType(config.enabledStorageTypes);
let firstPartyData = tryParse(readData(FIRST_PARTY_KEY, allowedStorage));
+ const isGroupB = firstPartyData?.group === WITHOUT_IIQ;
const firePartnerCallback = () => {
if (configParams.callback && !callbackFired) {
callbackFired = true;
if (callbackTimeoutID) clearTimeout(callbackTimeoutID);
+ if (isGroupB) runtimeEids = { eids: [] };
configParams.callback(runtimeEids, firstPartyData?.group || NOT_YET_DEFINED);
}
}
@@ -275,22 +263,14 @@ export const intentIqIdSubmodule = {
// Get consent information
const cmpData = {};
const uspData = uspDataHandler.getConsentData();
- const gppData = gppDataHandler.getConsentData();
+ const gppData = getGppValue();
if (uspData) {
cmpData.us_privacy = uspData;
}
- if (gppData) {
- cmpData.gpp = '';
- cmpData.gpi = 1;
-
- if (gppData.parsedSections && 'usnat' in gppData.parsedSections) {
- cmpData.gpp = handleGPPData(gppData.parsedSections['usnat']);
- cmpData.gpi = 0
- }
- cmpData.gpp_sid = gppData.applicableSections;
- }
+ cmpData.gpp = gppData.gppString;
+ cmpData.gpi = gppData.gpi;
// Read client hints from storage
let clientHints = readData(CLIENT_HINTS_KEY, allowedStorage);
@@ -335,6 +315,11 @@ export const intentIqIdSubmodule = {
const savedData = tryParse(readData(FIRST_PARTY_DATA_KEY, allowedStorage))
if (savedData) {
partnerData = savedData;
+
+ if (partnerData.wsrvcll) {
+ partnerData.wsrvcll = false;
+ storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData), allowedStorage);
+ }
}
if (partnerData.data) {
@@ -344,9 +329,9 @@ export const intentIqIdSubmodule = {
}
}
- if (!firstPartyData.cttl || Date.now() - firstPartyData.date > firstPartyData.cttl || firstPartyData.uspapi_value !== cmpData.us_privacy || firstPartyData.gpp_value !== cmpData.gpp) {
+ if (!firstPartyData.cttl || Date.now() - firstPartyData.date > firstPartyData.cttl || firstPartyData.uspapi_value !== cmpData.us_privacy || firstPartyData.gpp_string_value !== cmpData.gpp) {
firstPartyData.uspapi_value = cmpData.us_privacy;
- firstPartyData.gpp_value = cmpData.gpp;
+ firstPartyData.gpp_string_value = cmpData.gpp;
firstPartyData.isOptedOut = false
firstPartyData.cttl = 0
shouldCallServer = true;
@@ -363,8 +348,9 @@ export const intentIqIdSubmodule = {
}
if (!shouldCallServer) {
+ if (isGroupB) runtimeEids = { eids: [] };
firePartnerCallback();
- return {id: runtimeEids?.eids || []};
+ return { id: runtimeEids.eids };
}
// use protocol relative urls for http or https
@@ -377,12 +363,15 @@ export const intentIqIdSubmodule = {
url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : '';
url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : '';
url += cmpData.us_privacy ? '&pa=' + encodeURIComponent(cmpData.us_privacy) : '';
- url += cmpData.gpp ? '&gpv=' + encodeURIComponent(cmpData.gpp) : '';
+ url += cmpData.gpp ? '&gpp=' + encodeURIComponent(cmpData.gpp) : '';
url += cmpData.gpi ? '&gpi=' + cmpData.gpi : '';
url += clientHints ? '&uh=' + encodeURIComponent(clientHints) : '';
url += VERSION ? '&jsver=' + VERSION : '';
url += firstPartyData?.group ? '&testGroup=' + encodeURIComponent(firstPartyData.group) : '';
+ // Add vrref and fui to the URL
+ url = appendVrrefAndFui(url, configParams.domainName);
+
const storeFirstPartyData = () => {
partnerData.eidl = runtimeEids?.eids?.length || -1
storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), allowedStorage);
@@ -398,8 +387,7 @@ export const intentIqIdSubmodule = {
partnerData.date = Date.now();
firstPartyData.date = Date.now();
const defineEmptyDataAndFireCallback = () => {
- respJson.data = partnerData.data = runtimeEids = {};
- partnerData.data = ''
+ respJson.data = partnerData.data = runtimeEids = { eids: [] };
storeFirstPartyData()
firePartnerCallback()
callback(runtimeEids)
@@ -451,6 +439,14 @@ export const intentIqIdSubmodule = {
partnerData.data = respJson.data;
}
+ if ('ct' in respJson) {
+ partnerData.ct = respJson.ct;
+ }
+
+ if ('sid' in respJson) {
+ partnerData.siteId = respJson.sid;
+ }
+
if (rrttStrtTime && rrttStrtTime > 0) {
partnerData.rrtt = Date.now() - rrttStrtTime;
}
@@ -476,7 +472,10 @@ export const intentIqIdSubmodule = {
callback(runtimeEids);
}
};
+ rrttStrtTime = Date.now();
+ partnerData.wsrvcll = true;
+ storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData), allowedStorage);
ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true});
};
const respObj = {callback: resp};
diff --git a/modules/intentIqIdSystem.md b/modules/intentIqIdSystem.md
index 089fbeef509..34fd495d625 100644
--- a/modules/intentIqIdSystem.md
+++ b/modules/intentIqIdSystem.md
@@ -42,7 +42,7 @@ Please find below list of paramters that could be used in configuring Intent IQ
| params.timeoutInMillis | Optional | Number | This is the timeout in milliseconds, which defines the maximum duration before the callback is triggered. The default value is 500. | `450` |
| params.browserBlackList | Optional | String | This is the name of a browser that can be added to a blacklist. | `"chrome"` |
| params.manualWinReportEnabled | Optional | Boolean | This variable determines whether the bidWon event is triggered automatically. If set to false, the event will occur automatically, and manual reporting with reportExternalWin will be disabled. If set to true, the event will not occur automatically, allowing manual reporting through reportExternalWin. The default value is false. | `true`|
-
+| params.domainName | Optional | String | Specifies the domain of the page in which the IntentIQ object is currently running and serving the impression. This domain will be used later in the revenue reporting breakdown by domain. For example, cnn.com. It identifies the primary source of requests to the IntentIQ servers, even within nested web pages. | `"currentDomain.com"` |
### Configuration example
@@ -56,7 +56,8 @@ pbjs.setConfig({
timeoutInMillis: 500,
browserBlackList: "chrome",
callback: (data, group) => window.pbjs.requestBids(),
- manualWinReportEnabled: true
+ manualWinReportEnabled: true,
+ domainName: "currentDomain.com"
},
storage: {
type: "html5",
diff --git a/modules/interactiveOffersBidAdapter.js b/modules/interactiveOffersBidAdapter.js
index 8d2f0a7734d..bb27239fef4 100644
--- a/modules/interactiveOffersBidAdapter.js
+++ b/modules/interactiveOffersBidAdapter.js
@@ -79,7 +79,6 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest, bidderRequest) {
// TODO: these should probably look at refererInfo
let pageURL = window.location.href;
let domain = window.location.hostname;
- let secure = (window.location.protocol == 'https:' ? 1 : 0);
let openRTBRequest = deepClone(DEFAULT['OpenRTBBidRequest']);
openRTBRequest.id = bidderRequest.bidderRequestId;
openRTBRequest.ext = {
@@ -120,7 +119,7 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest, bidderRequest) {
}
let imp = deepClone(DEFAULT['OpenRTBBidRequestImp']);
imp.id = bid.bidId;
- imp.secure = secure;
+ imp.secure = bid.ortb2Imp?.secure ?? 1;
imp.tagid = bid.adUnitCode;
imp.ext = {
rawdata: bid
diff --git a/modules/invamiaBidAdapter.js b/modules/invamiaBidAdapter.js
index 96af163ca4f..7f9a40e1473 100644
--- a/modules/invamiaBidAdapter.js
+++ b/modules/invamiaBidAdapter.js
@@ -1,5 +1,6 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
+import { buildBannerRequests, interpretBannerResponse } from '../libraries/biddoInvamiaUtils/index.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -12,86 +13,15 @@ const ENDPOINT_URL = 'https://ad.invamia.com/delivery/impress';
export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER],
- /**
- * Determines whether or not the given bid request is valid.
- *
- * @param {BidRequest} bidRequest The bid request params to validate.
- * @return boolean True if this is a valid bid request, and false otherwise.
- */
- isBidRequestValid: function(bidRequest) {
+ isBidRequestValid: function (bidRequest) {
return !!bidRequest.params.zoneId;
},
- /**
- * Make a server request from the list of BidRequests.
- *
- * @param {Array} validBidRequests an array of bid requests
- * @return ServerRequest Info describing the request to the server.
- */
- buildRequests: function(validBidRequests) {
- let serverRequests = [];
-
- validBidRequests.forEach(bidRequest => {
- const sizes = bidRequest.mediaTypes.banner.sizes;
-
- sizes.forEach(([width, height]) => {
- bidRequest.params.requestedSizes = [width, height];
-
- const payload = {
- ctype: 'div',
- pzoneid: bidRequest.params.zoneId,
- width,
- height,
- };
-
- const payloadString = Object.keys(payload).map(k => k + '=' + encodeURIComponent(payload[k])).join('&');
-
- serverRequests.push({
- method: 'GET',
- url: ENDPOINT_URL,
- data: payloadString,
- bidderRequest: bidRequest,
- });
- });
- });
-
- return serverRequests;
+ buildRequests: function (validBidRequests) {
+ return validBidRequests.flatMap((bidRequest) => buildBannerRequests(bidRequest, ENDPOINT_URL));
},
- /**
- * Unpack the response from the server into a list of bids.
- *
- * @param {ServerResponse} serverResponse A successful response from the server.
- * @param {BidRequest} bidderRequest A matched bid request for this response.
- * @return Array An array of bids which were nested inside the server.
- */
- interpretResponse: function(serverResponse, {bidderRequest}) {
- const response = serverResponse.body;
- const bidResponses = [];
-
- if (response && response.template && response.template.html) {
- const {bidId} = bidderRequest;
- const [width, height] = bidderRequest.params.requestedSizes;
-
- const bidResponse = {
- requestId: bidId,
- cpm: response.hb.cpm,
- creativeId: response.banner.hash,
- currency: 'USD',
- netRevenue: response.hb.netRevenue,
- ttl: 600,
- ad: response.template.html,
- mediaType: 'banner',
- meta: {
- advertiserDomains: response.hb.adomains || [],
- },
- width,
- height,
- };
-
- bidResponses.push(bidResponse);
- }
-
- return bidResponses;
+ interpretResponse: function (serverResponse, { bidderRequest }) {
+ return interpretBannerResponse(serverResponse, bidderRequest);
},
-}
+};
registerBidder(spec);
diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js
index 46ad7dfec71..9416e6a0411 100644
--- a/modules/kargoBidAdapter.js
+++ b/modules/kargoBidAdapter.js
@@ -1,4 +1,4 @@
-import { _each, isEmpty, buildUrl, deepAccess, pick, logError } from '../src/utils.js';
+import { _each, isEmpty, buildUrl, deepAccess, pick, logError, isPlainObject } from '../src/utils.js';
import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
@@ -526,7 +526,7 @@ function getImpression(bid) {
} catch (e) {
logError('Kargo: getFloor threw an error: ', e);
}
- imp.floor = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined;
+ imp.floor = isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined;
}
}
diff --git a/modules/koblerBidAdapter.js b/modules/koblerBidAdapter.js
index 3ef40c8a921..9cde2e96d4e 100644
--- a/modules/koblerBidAdapter.js
+++ b/modules/koblerBidAdapter.js
@@ -7,10 +7,22 @@ import {
replaceAuctionPrice,
triggerPixel
} from '../src/utils.js';
-import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {getRefererInfo} from '../src/refererDetection.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+
+const additionalData = new WeakMap();
+
+export function setAdditionalData(obj, key, value) {
+ const prevValue = additionalData.get(obj) || {};
+ additionalData.set(obj, { ...prevValue, [key]: value });
+}
+
+export function getAdditionalData(obj, key) {
+ const data = additionalData.get(obj) || {};
+ return data[key];
+}
const BIDDER_CODE = 'kobler';
const BIDDER_ENDPOINT = 'https://bid.essrtb.com/bid/prebid_rtb_call';
@@ -36,17 +48,18 @@ export const buildRequests = function (validBidRequests, bidderRequest) {
data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest),
options: {
contentType: 'application/json'
- }
+ },
+ bidderRequest
};
};
-export const interpretResponse = function (serverResponse) {
+export const interpretResponse = function (serverResponse, request) {
const res = serverResponse.body;
const bids = []
if (res) {
res.seatbid.forEach(sb => {
sb.bid.forEach(b => {
- bids.push({
+ const bid = {
requestId: b.impid,
cpm: b.price,
currency: res.cur,
@@ -61,20 +74,24 @@ export const interpretResponse = function (serverResponse) {
meta: {
advertiserDomains: b.adomain
}
- })
+ }
+ setAdditionalData(bid, 'adServerCurrency', getCurrencyFromBidderRequest(request.bidderRequest));
+ bids.push(bid);
})
});
}
+
return bids;
};
export const onBidWon = function (bid) {
+ const adServerCurrency = getAdditionalData(bid, 'adServerCurrency');
// We intentionally use the price set by the publisher to replace the ${AUCTION_PRICE} macro
// instead of the `originalCpm` here. This notification is not used for billing, only for extra logging.
const publisherPrice = bid.cpm || 0;
- const publisherCurrency = bid.currency || config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY;
+ const publisherCurrency = bid.currency || adServerCurrency || SUPPORTED_CURRENCY;
const adServerPrice = deepAccess(bid, 'adserverTargeting.hb_pb', 0);
- const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY;
+ const adServerPriceCurrency = adServerCurrency || SUPPORTED_CURRENCY;
if (isStr(bid.nurl) && bid.nurl !== '') {
const winNotificationUrl = replaceAuctionPrice(bid.nurl, publisherPrice)
.replace(/\${AUCTION_PRICE_CURRENCY}/g, publisherCurrency)
@@ -163,7 +180,7 @@ function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) {
function buildOpenRtbImpObject(validBidRequest) {
const sizes = getSizes(validBidRequest);
const mainSize = sizes[0];
- const floorInfo = getFloorInfo(validBidRequest, mainSize);
+ const floorInfo = getFloorInfo(validBidRequest, mainSize) || {};
return {
id: validBidRequest.bidId,
diff --git a/modules/kubientBidAdapter.js b/modules/kubientBidAdapter.js
index 57cbe6acd07..8ccfa4ab059 100644
--- a/modules/kubientBidAdapter.js
+++ b/modules/kubientBidAdapter.js
@@ -1,4 +1,4 @@
-import { isArray, deepAccess } from '../src/utils.js';
+import { isArray, deepAccess, isPlainObject } from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import { config } from '../src/config.js';
@@ -33,7 +33,7 @@ export const spec = {
const mediaType = (Object.keys(bid.mediaTypes).length == 1) ? Object.keys(bid.mediaTypes)[0] : '*';
const sizes = bid.sizes || '*';
const floorInfo = bid.getFloor({currency: 'USD', mediaType: mediaType, size: sizes});
- if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') {
+ if (isPlainObject(floorInfo) && floorInfo.currency === 'USD') {
let floor = parseFloat(floorInfo.floor)
if (!isNaN(floor) && floor > 0) {
adSlot.floor = parseFloat(floorInfo.floor);
diff --git a/modules/kueezBidAdapter.js b/modules/kueezBidAdapter.js
index 63a01bfea02..f11d71f3318 100644
--- a/modules/kueezBidAdapter.js
+++ b/modules/kueezBidAdapter.js
@@ -155,7 +155,7 @@ function getFloorPrice(bid, mediaType) {
currency: MAIN_CURRENCY,
mediaType: mediaType,
size: '*'
- });
+ }) || {};
floor = floorResult.currency === MAIN_CURRENCY && floorResult.floor ? floorResult.floor : 0;
}
diff --git a/modules/lemmaDigitalBidAdapter.js b/modules/lemmaDigitalBidAdapter.js
index dde7c25d9b9..f2d677f14db 100644
--- a/modules/lemmaDigitalBidAdapter.js
+++ b/modules/lemmaDigitalBidAdapter.js
@@ -295,7 +295,7 @@ export var spec = {
[BANNER, VIDEO].forEach(mediaType => {
if (impObj.hasOwnProperty(mediaType)) {
let floorInfo = bid.getFloor({ currency: impObj.bidfloorcur, mediaType: mediaType, size: '*' });
- if (typeof floorInfo === 'object' && floorInfo.currency === impObj.bidfloorcur && !isNaN(parseInt(floorInfo.floor))) {
+ if (utils.isPlainObject(floorInfo) && floorInfo.currency === impObj.bidfloorcur && !isNaN(parseInt(floorInfo.floor))) {
let mediaTypeFloor = parseFloat(floorInfo.floor);
bidFloor = (bidFloor == -1 ? mediaTypeFloor : Math.min(mediaTypeFloor, bidFloor));
}
diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js
index cfbd2b5b3b5..203996c9fb5 100644
--- a/modules/livewrappedBidAdapter.js
+++ b/modules/livewrappedBidAdapter.js
@@ -4,6 +4,7 @@ import {config} from '../src/config.js';
import {find} from '../src/polyfill.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {getStorageManager} from '../src/storageManager.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -69,7 +70,7 @@ export const spec = {
bidUrl = bidUrl ? bidUrl.params.bidUrl : URL;
url = url ? url.params.url : (getAppDomain() || getTopWindowLocation(bidderRequest));
test = test ? test.params.test : undefined;
- const currency = config.getConfig('currency.adServerCurrency') || 'USD';
+ const currency = getCurrencyFromBidderRequest(bidderRequest) || 'USD';
var adRequests = bidRequests.map(b => bidToAdRequest(b, currency));
const adRequestsContainFloors = adRequests.some(r => r.flr !== undefined);
diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js
index 64d631c2469..3be7b261723 100644
--- a/modules/lotamePanoramaIdSystem.js
+++ b/modules/lotamePanoramaIdSystem.js
@@ -11,13 +11,11 @@ import {
isBoolean,
buildUrl,
isEmpty,
- isArray,
- isEmptyStr
+ isArray
} from '../src/utils.js';
import { ajax } from '../src/ajax.js';
import { submodule } from '../src/hook.js';
import {getStorageManager} from '../src/storageManager.js';
-import { uspDataHandler } from '../src/adapterManager.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
/**
@@ -38,16 +36,24 @@ const MISSING_CORE_CONSENT = 111;
const GVLID = 95;
const ID_HOST = 'id.crwdcntrl.net';
const ID_HOST_COOKIELESS = 'c.ltmsphrcl.net';
+const DO_NOT_HONOR_CONFIG = false;
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});
let cookieDomain;
+let appliedConfig = {
+ name: 'lotamePanoramaId',
+ storage: {
+ type: 'cookie&html5',
+ name: 'panoramaId'
+ }
+};
/**
* Set the Lotame First Party Profile ID in the first party namespace
* @param {String} profileId
*/
function setProfileId(profileId) {
- if (storage.cookiesAreEnabled()) {
+ if (cookiesAreEnabled()) {
let expirationDate = new Date(timestamp() + NINE_MONTHS_MS).toUTCString();
storage.setCookie(
KEY_PROFILE,
@@ -58,7 +64,7 @@ function setProfileId(profileId) {
undefined
);
}
- if (storage.hasLocalStorage()) {
+ if (localStorageIsEnabled()) {
storage.setDataInLocalStorage(KEY_PROFILE, profileId, undefined);
}
}
@@ -68,10 +74,10 @@ function setProfileId(profileId) {
*/
function getProfileId() {
let profileId;
- if (storage.cookiesAreEnabled()) {
+ if (cookiesAreEnabled(DO_NOT_HONOR_CONFIG)) {
profileId = storage.getCookie(KEY_PROFILE, undefined);
}
- if (!profileId && storage.hasLocalStorage()) {
+ if (!profileId && localStorageIsEnabled(DO_NOT_HONOR_CONFIG)) {
profileId = storage.getDataFromLocalStorage(KEY_PROFILE, undefined);
}
return profileId;
@@ -83,21 +89,11 @@ function getProfileId() {
*/
function getFromStorage(key) {
let value = null;
- if (storage.cookiesAreEnabled()) {
+ if (cookiesAreEnabled(DO_NOT_HONOR_CONFIG)) {
value = storage.getCookie(key, undefined);
}
- if (storage.hasLocalStorage() && value === null) {
- const storedValueExp = storage.getDataFromLocalStorage(
- `${key}_exp`, undefined
- );
-
- if (storedValueExp === '' || storedValueExp === null) {
- value = storage.getDataFromLocalStorage(key, undefined);
- } else if (storedValueExp) {
- if ((new Date(parseInt(storedValueExp, 10))).getTime() - Date.now() > 0) {
- value = storage.getDataFromLocalStorage(key, undefined);
- }
- }
+ if (value === null && localStorageIsEnabled(DO_NOT_HONOR_CONFIG)) {
+ value = storage.getDataFromLocalStorage(key, undefined);
}
return value;
}
@@ -115,7 +111,7 @@ function saveLotameCache(
) {
if (key && value) {
let expirationDate = new Date(expirationTimestamp).toUTCString();
- if (storage.cookiesAreEnabled()) {
+ if (cookiesAreEnabled()) {
storage.setCookie(
key,
value,
@@ -125,12 +121,7 @@ function saveLotameCache(
undefined
);
}
- if (storage.hasLocalStorage()) {
- storage.setDataInLocalStorage(
- `${key}_exp`,
- String(expirationTimestamp),
- undefined
- );
+ if (localStorageIsEnabled()) {
storage.setDataInLocalStorage(key, value, undefined);
}
}
@@ -172,7 +163,7 @@ function getLotameLocalCache(clientId = undefined) {
*/
function clearLotameCache(key) {
if (key) {
- if (storage.cookiesAreEnabled()) {
+ if (cookiesAreEnabled(DO_NOT_HONOR_CONFIG)) {
let expirationDate = new Date(0).toUTCString();
storage.setCookie(
key,
@@ -183,11 +174,50 @@ function clearLotameCache(key) {
undefined
);
}
- if (storage.hasLocalStorage()) {
+ if (localStorageIsEnabled(DO_NOT_HONOR_CONFIG)) {
storage.removeDataFromLocalStorage(key, undefined);
}
}
}
+/**
+ * @param {boolean} honorConfig - false to override for reading or deleting old cookies
+ * @returns {boolean} for whether we can write the cookie
+ */
+function cookiesAreEnabled(honorConfig = true) {
+ if (honorConfig) {
+ return storage.cookiesAreEnabled() && appliedConfig.storage.type.includes('cookie');
+ }
+ return storage.cookiesAreEnabled();
+}
+/**
+ * @param {boolean} honorConfig - false to override for reading or deleting old stored items
+ * @returns {boolean} for whether we can write the cookie
+ */
+function localStorageIsEnabled(honorConfig = true) {
+ if (honorConfig) {
+ return storage.hasLocalStorage() && appliedConfig.storage.type.includes('html5');
+ }
+ return storage.hasLocalStorage();
+}
+/**
+ * @param {SubmoduleConfig} config
+ * @returns {null|string} - string error if it finds one, null otherwise.
+ */
+function checkConfigHasErrorsAndReport(config) {
+ let error = null;
+ if (typeof config.storage !== 'undefined') {
+ Object.assign(appliedConfig.storage, appliedConfig.storage, config.storage);
+ const READABLE_MODULE_NAME = 'Lotame ID module';
+ const PERMITTED_STORAGE_TYPES = ['cookie', 'html5', 'cookie&html5'];
+ if (typeof config.storage.name !== 'undefined' && config.storage.name !== KEY_ID) {
+ logError(`Misconfigured ${READABLE_MODULE_NAME}, "storage.name" is expected to be "${KEY_ID}", actual is "${config.storage.name}"`);
+ error = true;
+ } else if (config.storage.type !== 'undefined' && !PERMITTED_STORAGE_TYPES.includes(config.storage.type)) {
+ logError(`Misconfigured ${READABLE_MODULE_NAME}, "storage.type" is expected to be one of "${PERMITTED_STORAGE_TYPES.join(', ')}", actual is "${config.storage.type}"`);
+ }
+ }
+ return error;
+}
/** @type {Submodule} */
export const lotamePanoramaIdSubmodule = {
/**
@@ -222,6 +252,9 @@ export const lotamePanoramaIdSubmodule = {
* @returns {IdResponse|undefined}
*/
getId(config, consentData, cacheIdObj) {
+ if (checkConfigHasErrorsAndReport(config)) {
+ return;
+ }
cookieDomain = lotamePanoramaIdSubmodule.findRootDomain();
const configParams = (config && config.params) || {};
const clientId = configParams.clientId;
@@ -249,18 +282,6 @@ export const lotamePanoramaIdSubmodule = {
const storedUserId = getProfileId();
- // Add CCPA Consent data handling
- const usp = uspDataHandler.getConsentData();
-
- let usPrivacy;
- if (typeof usp !== 'undefined' && !isEmpty(usp) && !isEmptyStr(usp)) {
- usPrivacy = usp;
- }
- if (!usPrivacy) {
- // fallback to 1st party cookie
- usPrivacy = getFromStorage('us_privacy');
- }
-
const getRequestHost = function() {
if (navigator.userAgent && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
return ID_HOST_COOKIELESS;
@@ -281,22 +302,10 @@ export const lotamePanoramaIdSubmodule = {
}
consentString = consentData.consentString;
}
- // If no consent string, try to read it from 1st party cookies
- if (!consentString) {
- consentString = getFromStorage('eupubconsent-v2');
- }
- if (!consentString) {
- consentString = getFromStorage('euconsent-v2');
- }
if (consentString) {
queryParams.gdpr_consent = consentString;
}
- // Add usPrivacy to the url
- if (usPrivacy) {
- queryParams.us_privacy = usPrivacy;
- }
-
// Add clientId to the url
if (hasCustomClientId) {
queryParams.c = clientId;
diff --git a/modules/lotamePanoramaIdSystem.md b/modules/lotamePanoramaIdSystem.md
index e960f4b5695..1fbc3f561c7 100644
--- a/modules/lotamePanoramaIdSystem.md
+++ b/modules/lotamePanoramaIdSystem.md
@@ -17,9 +17,32 @@ Retrieve the Lotame Panorama Id
pbjs.setConfig({
usersync: {
userIds: [
- {
- name: 'lotamePanoramaId' // The only parameter that is needed
- }],
+ {
+ name: 'lotamePanoramaId',
+ storage: {
+ name: 'panoramaId',
+ type: 'cookie&html5',
+ expires: 7
+ }
+ }
+ ],
}
});
-```
\ No newline at end of file
+```
+
+| Parameters under `userSync.userIds[]` | Scope | Type | Description | Example |
+| ---| --- | --- | --- | --- |
+| name | Required | String | Name for the Lotame ID submodule | `"lotamePanoramaId"` |
+| storage | Optional | Object | Configures how to cache User IDs locally in the browser | See [storage settings](#storage-settings) |
+
+
+### Storage Settings
+
+The following settings are available for the `storage` property in the `userSync.userIds[]` object. Please note that inclusion of the `storage` property is optional, but if provided, all three attributes listed below *must* be specified:
+
+| Param name | Scope | Type | Description | Example |
+| --- | --- | --- | --- | --- |
+| name | Required | String| Name of the cookie or localStorage where the user ID will be stored; *must* be `"panoramaId"` | `"panoramaId"` |
+| type | Required | String | `"cookie&html5"` (preferred) or `"cookie"` or `"html5"` | `"cookie&html5"` |
+| expires | Required | Number | How long (in days) the user ID information will be stored. Lotame recommends `7`. | `7` |
+
diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js
index 447257f97da..63435437967 100755
--- a/modules/luponmediaBidAdapter.js
+++ b/modules/luponmediaBidAdapter.js
@@ -6,6 +6,7 @@ import {
isArray,
isEmpty,
isFn,
+ isPlainObject,
logError,
logMessage,
logWarn,
@@ -320,7 +321,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) {
} catch (e) {
logError('LuponMedia: getFloor threw an error: ', e);
}
- bidFloor = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? parseFloat(floorInfo.floor) : undefined;
+ bidFloor = isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? parseFloat(floorInfo.floor) : undefined;
} else {
bidFloor = parseFloat(deepAccess(bidRequest, 'params.floor'));
}
diff --git a/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js
index 82a25af60d1..1d322a23a77 100644
--- a/modules/marsmediaBidAdapter.js
+++ b/modules/marsmediaBidAdapter.js
@@ -1,6 +1,6 @@
'use strict';
-import { deepAccess, getDNT, parseSizesInput, isArray, getWindowTop, deepSetValue, triggerPixel, getWindowSelf } from '../src/utils.js';
+import { deepAccess, getDNT, parseSizesInput, isArray, getWindowTop, deepSetValue, triggerPixel, getWindowSelf, isPlainObject } from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import {config} from '../src/config.js';
@@ -341,7 +341,7 @@ function MarsmediaAdapter() {
size: '*'
});
- if (typeof floorInfo === 'object' &&
+ if (isPlainObject(floorInfo) &&
floorInfo.currency === 'USD' &&
!isNaN(parseFloat(floorInfo.floor))) {
floor = floorInfo.floor;
diff --git a/modules/mediaimpactBidAdapter.js b/modules/mediaimpactBidAdapter.js
index d62cb789ea4..a1f04a44142 100644
--- a/modules/mediaimpactBidAdapter.js
+++ b/modules/mediaimpactBidAdapter.js
@@ -1,6 +1,5 @@
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import { buildUrl } from '../src/utils.js'
-import {ajax} from '../src/ajax.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { buildBidRequestsAndParams, postRequest, buildEndpointUrl } from '../libraries/mediaImpactUtils/index.js';
const BIDDER_CODE = 'mediaimpact';
export const ENDPOINT_PROTOCOL = 'https';
@@ -15,79 +14,25 @@ export const spec = {
},
buildRequests: function (validBidRequests, bidderRequest) {
- // TODO does it make sense to fall back to window.location.href?
const referer = bidderRequest?.refererInfo?.page || window.location.href;
- let bidRequests = [];
- let beaconParams = {
- tag: [],
- partner: [],
- sizes: [],
- referer: ''
- };
-
- validBidRequests.forEach(function(validBidRequest) {
- let sizes = validBidRequest.sizes;
- if (typeof validBidRequest.params.sizes !== 'undefined') {
- sizes = validBidRequest.params.sizes;
- }
-
- let bidRequestObject = {
- adUnitCode: validBidRequest.adUnitCode,
- sizes: sizes,
- bidId: validBidRequest.bidId,
- referer: referer
- };
-
- if (parseInt(validBidRequest.params.unitId)) {
- bidRequestObject.unitId = parseInt(validBidRequest.params.unitId);
- beaconParams.tag.push(validBidRequest.params.unitId);
- }
-
- if (parseInt(validBidRequest.params.partnerId)) {
- bidRequestObject.unitId = 0;
- bidRequestObject.partnerId = parseInt(validBidRequest.params.partnerId);
- beaconParams.partner.push(validBidRequest.params.partnerId);
- }
-
- bidRequests.push(bidRequestObject);
-
- beaconParams.sizes.push(spec.joinSizesToString(sizes));
- beaconParams.referer = encodeURIComponent(referer);
- });
-
- if (beaconParams.partner.length > 0) {
- beaconParams.partner = beaconParams.partner.join(',');
- } else {
- delete beaconParams.partner;
- }
+ // Use the common function to build bidRequests and beaconParams
+ const { bidRequests, beaconParams } = buildBidRequestsAndParams(validBidRequests, referer);
- beaconParams.tag = beaconParams.tag.join(',');
- beaconParams.sizes = beaconParams.sizes.join(',');
-
- let adRequestUrl = buildUrl({
- protocol: ENDPOINT_PROTOCOL,
- hostname: ENDPOINT_DOMAIN,
- pathname: ENDPOINT_PATH,
- search: beaconParams
- });
+ const adRequestUrl = buildEndpointUrl(
+ ENDPOINT_PROTOCOL,
+ ENDPOINT_DOMAIN,
+ ENDPOINT_PATH,
+ beaconParams
+ );
return {
method: 'POST',
url: adRequestUrl,
- data: JSON.stringify(bidRequests)
+ data: JSON.stringify(bidRequests),
};
},
- joinSizesToString: function(sizes) {
- let res = [];
- sizes.forEach(function(size) {
- res.push(size.join('x'));
- });
-
- return res.join('|');
- },
-
interpretResponse: function (serverResponse, bidRequest) {
const validBids = JSON.parse(bidRequest.data);
@@ -96,16 +41,13 @@ export const spec = {
}
return validBids
- .map(bid => ({
- bid: bid,
- ad: serverResponse.body[bid.adUnitCode]
- }))
+ .map(bid => ({ bid: bid, ad: serverResponse.body[bid.adUnitCode] }))
.filter(item => item.ad)
.map(item => spec.adResponse(item.bid, item.ad));
},
- adResponse: function(bid, ad) {
- const bidObject = {
+ adResponse: function (bid, ad) {
+ return {
requestId: bid.bidId,
ad: ad.ad,
cpm: ad.cpm,
@@ -116,36 +58,26 @@ export const spec = {
netRevenue: ad.netRevenue,
currency: ad.currency,
winNotification: ad.winNotification,
- meta: {}
+ meta: ad.meta || {},
};
-
- if (ad.meta) {
- bidObject.meta = ad.meta;
- }
-
- return bidObject;
},
- onBidWon: function(data) {
- data.winNotification.forEach(function(unitWon) {
- let adBidWonUrl = buildUrl({
- protocol: ENDPOINT_PROTOCOL,
- hostname: ENDPOINT_DOMAIN,
- pathname: unitWon.path
- });
+ onBidWon: function (data) {
+ data.winNotification.forEach(function (unitWon) {
+ const adBidWonUrl = buildEndpointUrl(
+ ENDPOINT_PROTOCOL,
+ ENDPOINT_DOMAIN,
+ unitWon.path
+ );
if (unitWon.method === 'POST') {
- spec.postRequest(adBidWonUrl, JSON.stringify(unitWon.data));
+ postRequest(adBidWonUrl, JSON.stringify(unitWon.data));
}
});
return true;
},
- postRequest(endpoint, data) {
- ajax(endpoint, null, data, {method: 'POST'});
- },
-
getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {
const syncs = [];
@@ -210,7 +142,6 @@ export const spec = {
return syncs;
},
-
-}
+};
registerBidder(spec);
diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js
index 987b2689f6b..efe9eb90256 100644
--- a/modules/mediakeysBidAdapter.js
+++ b/modules/mediakeysBidAdapter.js
@@ -11,6 +11,7 @@ import {
isFn,
isInteger,
isNumber,
+ isPlainObject,
isStr,
logError,
logWarn,
@@ -139,7 +140,7 @@ function getFloor(bid, mediaType, size = '*') {
size
})
- return (!isNaN(floor.floor) && floor.currency === DEFAULT_CURRENCY) ? floor.floor : false
+ return (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === DEFAULT_CURRENCY) ? floor.floor : false
}
/**
diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js
index e8d73e77d5f..1b4ccc45c88 100644
--- a/modules/medianetAnalyticsAdapter.js
+++ b/modules/medianetAnalyticsAdapter.js
@@ -8,7 +8,8 @@ import {
logError,
logInfo,
triggerPixel,
- uniques
+ uniques,
+ deepSetValue
} from '../src/utils.js';
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
@@ -18,6 +19,7 @@ import {getRefererInfo} from '../src/refererDetection.js';
import {AUCTION_COMPLETED, AUCTION_IN_PROGRESS, getPriceGranularity} from '../src/auction.js';
import {includes} from '../src/polyfill.js';
import {getGlobal} from '../src/prebidGlobal.js';
+import {convertCurrency} from '../libraries/currencyUtils/currency.js';
const analyticsType = 'endpoint';
const ENDPOINT = 'https://pb-logs.media.net/log?logid=kfk&evtid=prebid_analytics_events_client';
@@ -112,6 +114,7 @@ class Configure {
pbv: PREBID_VERSION,
pbav: ANALYTICS_VERSION,
flt: 1,
+ enableDbf: 1
}
}
@@ -303,21 +306,17 @@ class BidWrapper {
this.bidObjs.push(bidObj);
}
- getAdSlotBids(adSlot) {
- const bidResponses = this.getAdSlotBidObjs(adSlot);
- return bidResponses.map((bid) => bid.getLoggingData());
+ getAdSlotBidRequests(adSlot) {
+ return this.bidReqs.filter((bid) => bid.adUnitCode === adSlot);
}
- getAdSlotBidObjs(adSlot) {
- const bidResponses = this.bidObjs
- .filter((bid) => bid.adUnitCode === adSlot);
- const remResponses = this.bidReqs.filter(bid => !bid.used && bid.adUnitCode === adSlot);
- return [...bidResponses, ...remResponses];
+ getAdSlotBidResponses(adSlot) {
+ return this.bidObjs.filter((bid) => bid.adUnitCode === adSlot);
}
}
class Bid {
- constructor(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes) {
+ constructor(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes, resSizes) {
this.bidId = bidId;
this.bidder = bidder;
this.src = src;
@@ -348,6 +347,14 @@ class Bid {
this.used = false;
this.originalRequestId = bidId;
this.requestId = undefined;
+ this.advUrl = undefined;
+ this.latestAcid = undefined;
+ this.originalCurrency = undefined;
+ this.currMul = undefined;
+ this.inCurrMul = undefined;
+ this.res_mtype = undefined;
+ this.res_sizes = resSizes;
+ this.req_mtype = mediaType;
}
get size() {
@@ -374,7 +381,7 @@ class Bid {
cbdp: this.dfpbd,
dfpbd: this.dfpbd,
szs: this.allMediaTypeSizes.join('|'),
- size: this.size,
+ size: (this.res_sizes || [this.size]).join('|'),
mtype: this.mediaType,
dId: this.dealId,
winner: this.winner,
@@ -389,6 +396,13 @@ class Bid {
flrrule: this.floorRule,
ext: JSON.stringify(this.ext),
rtime: this.serverLatencyMillis,
+ advurl: this.advUrl,
+ lacid: this.latestAcid,
+ icurr: this.originalCurrency,
+ imul: this.inCurrMul,
+ omul: this.currMul,
+ res_mtype: this.res_mtype,
+ req_mtype: this.req_mtype
}
}
}
@@ -454,11 +468,12 @@ class Auction {
return this.bidWrapper.findBidObj(key, value)
}
- getAdSlotBids(adSlot) {
- return this.bidWrapper.getAdSlotBids(adSlot);
+ getAdSlotBidRequests(adSlot) {
+ return this.bidWrapper.getAdSlotBidRequests(adSlot);
}
- getAdSlotBidObjs(adSlot) {
- return this.bidWrapper.getAdSlotBidObjs(adSlot);
+
+ getAdSlotBidResponses(adSlot) {
+ return this.bidWrapper.getAdSlotBidResponses(adSlot);
}
_mergeFieldsToLog(objParams) {
@@ -558,8 +573,8 @@ function _getSizes(mediaTypes, sizes) {
}
function bidResponseHandler(bid) {
- const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId, originalRequestId, bidder } = bid;
- const {originalCpm, creativeId, adId, currency} = bid;
+ const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId, originalRequestId, bidder, meta } = bid;
+ let {originalCpm, creativeId, adId, currency, originalCurrency} = bid;
if (!(auctions[auctionId] instanceof Auction)) {
return;
@@ -575,13 +590,26 @@ function bidResponseHandler(bid) {
bidObj = {};
isBidOverridden = false;
}
+ currency = currency ? currency.toUpperCase() : '';
+ originalCurrency = originalCurrency ? originalCurrency.toUpperCase() : currency;
Object.assign(bidObj, bidReq,
{ cpm, width, height, mediaType, timeToRespond, dealId, creativeId, originalRequestId, requestId },
- { adId, currency }
+ { adId, currency, originalCurrency }
);
bidObj.floorPrice = deepAccess(bid, 'floorData.floorValue');
bidObj.floorRule = deepAccess(bid, 'floorData.floorRule');
bidObj.originalCpm = originalCpm || cpm;
+ bidObj.advUrl = meta && meta.advertiserDomains && meta.advertiserDomains.join(',');
+ bidObj.currMul = 1;
+ bidObj.inCurrMul = 1;
+ if (bidObj.originalCurrency !== 'USD') {
+ bidObj.originalCpm = exchangeCurrency(bidObj.originalCpm, bidObj.originalCurrency, 'USD');
+ bidObj.inCurrMul = exchangeCurrency(1, 'USD', bidObj.originalCurrency)
+ }
+ if (bidObj.currency !== 'USD') {
+ bidObj.cpm = exchangeCurrency(bidObj.cpm, bidObj.currency, 'USD');
+ bidObj.currMul = exchangeCurrency(1, 'USD', bidObj.currency)
+ }
let dfpbd = deepAccess(bid, 'adserverTargeting.hb_pb');
if (!dfpbd) {
let priceGranularity = getPriceGranularity(bid);
@@ -606,9 +634,19 @@ function bidResponseHandler(bid) {
if (typeof bid.serverResponseTimeMs !== 'undefined') {
bidObj.serverLatencyMillis = bid.serverResponseTimeMs;
}
+ bidObj.res_mtype = mediaType;
!isBidOverridden && auctions[auctionId].addBidObj(bidObj);
}
+function exchangeCurrency(price, fromCurrency, toCurrency) {
+ try {
+ return convertCurrency(price, fromCurrency, toCurrency, false).toFixed(4)
+ } catch (e) {
+ logError(`Media.net Analytics Adapter: Could not convert ${fromCurrency} to ${toCurrency} for price ${price}`);
+ }
+ return price;
+}
+
function noBidResponseHandler({ auctionId, bidId }) {
if (!(auctions[auctionId] instanceof Auction)) {
return;
@@ -630,7 +668,7 @@ function bidTimeoutHandler(timedOutBids) {
if (!(auctions[auctionId] instanceof Auction)) {
return;
}
- const bidReq = auctions[auctionId].findReqBid('bidId', bidId);
+ const bidReq = auctions[auctionId].findReqBid(bidId);
if (!(bidReq instanceof Bid) || bidReq.used) {
return;
}
@@ -710,6 +748,7 @@ function bidWonHandler(bid) {
}).send();
return;
}
+ bidObj.latestAcid = bid.latestTargetedAuctionId;
auctions[auctionId].bidWonTime = Date.now();
bidObj.winner = 1;
sendEvent(auctionId, adUnitCode, LOG_TYPE.RA, bidObj.adId);
@@ -749,6 +788,43 @@ function getCommonLoggingData(acid, adtag) {
return Object.assign(commonParams, adunitParams, auctionParams);
}
+function getResponseSizeMap(acid, adtag) {
+ const responses = auctions[acid].getAdSlotBidResponses(adtag);
+ const receivedResponse = {};
+ // Set true in map for success bids
+ responses.filter((bid) => bid.size !== '')
+ .forEach((bid) => deepSetValue(receivedResponse, `${bid.bidId}.${bid.size}`, true));
+
+ // For non-success bids:
+ // 1) set bid.res_sizes = (sizes for which no successful bid received)
+ // 2) set true in map
+ responses.filter((bid) => bid.size === '').forEach((bid) => {
+ bid.res_sizes = bid.allMediaTypeSizes.filter((size) => !deepAccess(receivedResponse, `${bid.bidId}.${size}`));
+ bid.allMediaTypeSizes.forEach((size) => deepSetValue(receivedResponse, `${bid.bidId}.${size}`, true));
+ });
+ return receivedResponse;
+}
+
+function getDummyBids(acid, adtag, receivedResponse) {
+ const emptyBids = [];
+ auctions[acid].getAdSlotBidRequests(adtag).forEach(({ bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes, status }) => {
+ const emptySizes = allMediaTypeSizes.filter((size) => !deepAccess(receivedResponse, `${bidId}.${size}`));
+ if (bidder !== DUMMY_BIDDER && emptySizes.length > 0) {
+ const bid = new Bid(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes, emptySizes);
+ bid.status = status === BID_SUCCESS ? BID_NOBID : status;
+ emptyBids.push(bid);
+ }
+ });
+ return emptyBids;
+}
+
+function getLoggingBids(acid, adtag) {
+ const receivedResponse = getResponseSizeMap(acid, adtag);
+ const dummyBids = getDummyBids(acid, adtag, receivedResponse);
+
+ return [...auctions[acid].getAdSlotBidResponses(adtag), ...dummyBids];
+}
+
function fireAuctionLog(acid, adtag, logType, adId) {
let commonParams = getCommonLoggingData(acid, adtag);
commonParams.lgtp = logType;
@@ -766,7 +842,7 @@ function fireAuctionLog(acid, adtag, logType, adId) {
bidParams = [winLogData];
commonParams.lper = 1;
} else {
- bidParams = auctions[acid].getAdSlotBids(adtag).map(({winner, ...restParams}) => restParams);
+ bidParams = getLoggingBids(acid, adtag).map((bid) => bid.getLoggingData())
delete commonParams.wts;
}
let mnetPresent = bidParams.filter(b => b.pvnm === MEDIANET_BIDDER_CODE).length > 0;
diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js
index c90292a74ac..5adf3f743a7 100644
--- a/modules/medianetBidAdapter.js
+++ b/modules/medianetBidAdapter.js
@@ -21,6 +21,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js';
import {ajax} from '../src/ajax.js';
+import {getViewportCoordinates} from '../libraries/viewport/viewport.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -180,6 +181,7 @@ function extParams(bidRequest, bidderRequests) {
const gdprApplies = !!(gdpr && gdpr.gdprApplies);
const uspApplies = !!(uspConsent);
const coppaApplies = !!(config.getConfig('coppa'));
+ const {top = -1, right = -1, bottom = -1, left = -1} = getViewportCoordinates();
return Object.assign({},
{ customer_id: params.cid },
{ prebid_version: 'v' + '$prebid.version$' },
@@ -191,7 +193,13 @@ function extParams(bidRequest, bidderRequests) {
windowSize.w !== -1 && windowSize.h !== -1 && { screen: windowSize },
userId && { user_id: userId },
getGlobal().medianetGlobals.analyticsEnabled && { analytics: true },
- !isEmpty(sChain) && {schain: sChain}
+ !isEmpty(sChain) && {schain: sChain},
+ {
+ vcoords: {
+ top_left: { x: left, y: top },
+ bottom_right: { x: right, y: bottom }
+ }
+ }
);
}
@@ -283,7 +291,7 @@ function getBidFloorByType(bidRequest) {
return floorInfo;
}
function setFloorInfo(bidRequest, mediaType, size, floorInfo) {
- let floor = bidRequest.getFloor({currency: 'USD', mediaType: mediaType, size: size});
+ let floor = bidRequest.getFloor({currency: 'USD', mediaType: mediaType, size: size}) || {};
if (size.length > 1) floor.size = size;
floor.mediaType = mediaType;
floorInfo.push(floor);
@@ -317,14 +325,15 @@ function getOverlapArea(topLeft1, bottomRight1, topLeft2, bottomRight2) {
}
function normalizeCoordinates(coordinates) {
+ const {scrollX, scrollY} = window;
return {
top_left: {
- x: coordinates.top_left.x + window.pageXOffset,
- y: coordinates.top_left.y + window.pageYOffset,
+ x: coordinates.top_left.x + scrollX,
+ y: coordinates.top_left.y + scrollY,
},
bottom_right: {
- x: coordinates.bottom_right.x + window.pageXOffset,
- y: coordinates.bottom_right.y + window.pageYOffset,
+ x: coordinates.bottom_right.x + scrollX,
+ y: coordinates.bottom_right.y + scrollY,
}
}
}
diff --git a/modules/mediasniperBidAdapter.js b/modules/mediasniperBidAdapter.js
index 5cf0ceaba18..796a15e1778 100644
--- a/modules/mediasniperBidAdapter.js
+++ b/modules/mediasniperBidAdapter.js
@@ -7,6 +7,7 @@ import {
isEmpty,
isFn,
isNumber,
+ isPlainObject,
isStr,
logError,
logMessage,
@@ -262,7 +263,7 @@ function getFloor(bid, mediaType, size = '*') {
size,
});
- return !isNaN(floor.floor) && floor.currency === DEFAULT_CURRENCY
+ return isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === DEFAULT_CURRENCY
? floor.floor
: false;
}
diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js
index a384b9d676c..9e2ea06df69 100644
--- a/modules/mgidBidAdapter.js
+++ b/modules/mgidBidAdapter.js
@@ -22,6 +22,7 @@ import {config} from '../src/config.js';
import { getStorageManager } from '../src/storageManager.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
import { getUserSyncs } from '../libraries/mgidUtils/mgidUtils.js'
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -158,7 +159,7 @@ export const spec = {
if (isStr(muid) && muid.length > 0) {
url += '?muid=' + muid;
}
- const cur = setOnAny(validBidRequests, 'params.currency') || setOnAny(validBidRequests, 'params.cur') || config.getConfig('currency.adServerCurrency') || DEFAULT_CUR;
+ const cur = setOnAny(validBidRequests, 'params.currency') || setOnAny(validBidRequests, 'params.cur') || getCurrencyFromBidderRequest(bidderRequest) || DEFAULT_CUR;
const secure = window.location.protocol === 'https:' ? 1 : 0;
let imp = [];
validBidRequests.forEach(bid => {
diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js
index bba8f988be6..cd5650e73c2 100644
--- a/modules/missenaBidAdapter.js
+++ b/modules/missenaBidAdapter.js
@@ -7,10 +7,11 @@ import {
safeJSONParse,
triggerPixel,
} from '../src/utils.js';
-import { config } from '../src/config.js';
import { BANNER } from '../src/mediaTypes.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
+import { isAutoplayEnabled } from '../libraries/autoplayDetection/autoplay.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -40,7 +41,7 @@ function getFloor(bidRequest) {
mediaType: BANNER,
});
- if (!isNaN(bidFloors.floor)) {
+ if (!isNaN(bidFloors?.floor)) {
return bidFloors;
}
}
@@ -70,18 +71,8 @@ function toPayload(bidRequest, bidderRequest) {
}
const baseUrl = bidRequest.params.baseUrl || ENDPOINT_URL;
- if (bidRequest.params.test) {
- payload.test = bidRequest.params.test;
- }
- if (bidRequest.params.placement) {
- payload.placement = bidRequest.params.placement;
- }
- if (bidRequest.params.formats) {
- payload.formats = bidRequest.params.formats;
- }
- if (bidRequest.params.isInternal) {
- payload.is_internal = bidRequest.params.isInternal;
- }
+ payload.params = bidRequest.params;
+
if (bidRequest.ortb2?.device?.ext?.cdep) {
payload.cdep = bidRequest.ortb2?.device?.ext?.cdep;
}
@@ -91,8 +82,11 @@ function toPayload(bidRequest, bidderRequest) {
const bidFloor = getFloor(bidRequest);
payload.floor = bidFloor?.floor;
payload.floor_currency = bidFloor?.currency;
- payload.currency = config.getConfig('currency.adServerCurrency') || 'EUR';
+ payload.currency = getCurrencyFromBidderRequest(bidderRequest);
payload.schain = bidRequest.schain;
+ payload.coppa = bidderRequest?.ortb2?.regs?.coppa ? 1 : 0;
+ payload.autoplay = isAutoplayEnabled() === true ? 1 : 0;
+ payload.screen = { height: screen.height, width: screen.width };
return {
method: 'POST',
@@ -137,6 +131,8 @@ export const spec = {
return [];
}
+ this.msnaApiKey = validBidRequests[0]?.params.apiKey;
+
return validBidRequests.map((bidRequest) =>
toPayload(bidRequest, bidderRequest),
);
@@ -161,26 +157,25 @@ export const spec = {
getUserSyncs: function (
syncOptions,
serverResponses,
- gdprConsent,
+ gdprConsent = {},
uspConsent,
) {
- if (!syncOptions.iframeEnabled) {
+ if (!syncOptions.iframeEnabled || !this.msnaApiKey) {
return [];
}
- let gdprParams = '';
- if (
- gdprConsent &&
- 'gdprApplies' in gdprConsent &&
- typeof gdprConsent.gdprApplies === 'boolean'
- ) {
- gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${
- gdprConsent.consentString
- }`;
+ const url = new URL('https://sync.missena.io/iframe');
+ url.searchParams.append('t', this.msnaApiKey);
+
+ if (typeof gdprConsent.gdprApplies === 'boolean') {
+ url.searchParams.append('gdpr', Number(gdprConsent.gdprApplies));
+ url.searchParams.append('gdpr_consent', gdprConsent.consentString);
}
- return [
- { type: 'iframe', url: 'https://sync.missena.io/iframe' + gdprParams },
- ];
+ if (uspConsent) {
+ url.searchParams.append('us_privacy', uspConsent);
+ }
+
+ return [{ type: 'iframe', url: url.href }];
},
/**
* Register bidder specific code, which will execute if bidder timed out after an auction
diff --git a/modules/mobianRtdProvider.js b/modules/mobianRtdProvider.js
index 3b9d2632246..02f2d1b83cf 100644
--- a/modules/mobianRtdProvider.js
+++ b/modules/mobianRtdProvider.js
@@ -4,85 +4,200 @@
*/
import { submodule } from '../src/hook.js';
import { ajaxBuilder } from '../src/ajax.js';
-import { deepSetValue, safeJSONParse } from '../src/utils.js';
+import { safeJSONParse, logMessage as _logMessage } from '../src/utils.js';
+import { setKeyValue } from '../libraries/gptUtils/gptUtils.js';
/**
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
*/
+/**
+ * @typedef {Object} MobianConfig
+ * @property {MobianConfigParams} params
+ */
+
+/**
+ * @typedef {Object} MobianConfigParams
+ * @property {string} [prefix] - Optional prefix for targeting keys (default: 'mobian')
+ * @property {boolean} [publisherTargeting] - Optional boolean to enable targeting for publishers (default: false)
+ * @property {boolean} [advertiserTargeting] - Optional boolean to enable targeting for advertisers (default: false)
+ */
+
+/**
+ * @typedef {Object} MobianContextData
+ * @property {Object} apValues
+ * @property {string[]} categories
+ * @property {string[]} emotions
+ * @property {string[]} genres
+ * @property {string} risk
+ * @property {string} sentiment
+ * @property {string[]} themes
+ * @property {string[]} tones
+ */
+
export const MOBIAN_URL = 'https://prebid.outcomes.net/api/prebid/v1/assessment/async';
-/** @type {RtdSubmodule} */
-export const mobianBrandSafetySubmodule = {
- name: 'mobianBrandSafety',
- init: init,
- getBidRequestData: getBidRequestData
+export const CONTEXT_KEYS = [
+ 'apValues',
+ 'categories',
+ 'emotions',
+ 'genres',
+ 'risk',
+ 'sentiment',
+ 'themes',
+ 'tones'
+];
+
+const AP_KEYS = ['a0', 'a1', 'p0', 'p1'];
+
+const logMessage = (...args) => {
+ _logMessage('Mobian', ...args);
};
-function init() {
- return true;
+function makeMemoizedFetch() {
+ let cachedResponse = null;
+ return async function () {
+ if (cachedResponse) {
+ return Promise.resolve(cachedResponse);
+ }
+ try {
+ const response = await fetchContextData();
+ cachedResponse = makeDataFromResponse(response);
+ return cachedResponse;
+ } catch (error) {
+ logMessage('error', error);
+ return Promise.resolve({});
+ }
+ }
}
-function getBidRequestData(bidReqConfig, callback, config) {
- const { site: ortb2Site } = bidReqConfig.ortb2Fragments.global;
- const pageUrl = encodeURIComponent(getPageUrl());
+export const getContextData = makeMemoizedFetch();
+
+export async function fetchContextData() {
+ const pageUrl = encodeURIComponent(window.location.href);
const requestUrl = `${MOBIAN_URL}?url=${pageUrl}`;
+ const request = ajaxBuilder();
+
+ return new Promise((resolve, reject) => {
+ request(requestUrl, { success: resolve, error: reject });
+ });
+}
+
+export function getConfig(config) {
+ const [advertiserTargeting, publisherTargeting] = ['advertiserTargeting', 'publisherTargeting'].map((key) => {
+ const value = config?.params?.[key];
+ if (!value) {
+ return [];
+ } else if (value === true) {
+ return CONTEXT_KEYS;
+ } else if (Array.isArray(value) && value.length) {
+ return value.filter((key) => CONTEXT_KEYS.includes(key));
+ }
+ return [];
+ });
+
+ const prefix = config?.params?.prefix || 'mobian';
+ return { advertiserTargeting, prefix, publisherTargeting };
+}
+
+/**
+ * @param {MobianConfigParams} parsedConfig
+ * @param {MobianContextData} contextData
+ * @returns {function}
+ */
+export function setTargeting(parsedConfig, contextData) {
+ const { publisherTargeting, prefix } = parsedConfig;
+ logMessage('context', contextData);
+
+ CONTEXT_KEYS.forEach((key) => {
+ if (!publisherTargeting.includes(key)) return;
- const ajax = ajaxBuilder();
-
- return new Promise((resolve) => {
- ajax(requestUrl, {
- success: function(responseData) {
- let response = safeJSONParse(responseData);
- if (!response || !response.meta.has_results) {
- resolve({});
- callback();
- return;
- }
-
- const results = response.results;
- const mobianRisk = results.mobianRisk || 'unknown';
- const contentCategories = results.mobianContentCategories || [];
- const sentiment = results.mobianSentiment || 'unknown';
- const emotions = results.mobianEmotions || [];
- const themes = results.mobianThemes || [];
- const tones = results.mobianTones || [];
- const genres = results.mobianGenres || [];
- const apValues = results.ap || {};
-
- const risk = {
- risk: mobianRisk,
- contentCategories: contentCategories,
- sentiment: sentiment,
- emotions: emotions,
- themes: themes,
- tones: tones,
- genres: genres,
- apValues: apValues,
- };
-
- deepSetValue(ortb2Site, 'ext.data.mobianRisk', mobianRisk);
- deepSetValue(ortb2Site, 'ext.data.mobianContentCategories', contentCategories);
- deepSetValue(ortb2Site, 'ext.data.mobianSentiment', sentiment);
- deepSetValue(ortb2Site, 'ext.data.mobianEmotions', emotions);
- deepSetValue(ortb2Site, 'ext.data.mobianThemes', themes);
- deepSetValue(ortb2Site, 'ext.data.mobianTones', tones);
- deepSetValue(ortb2Site, 'ext.data.mobianGenres', genres);
- deepSetValue(ortb2Site, 'ext.data.apValues', apValues);
-
- resolve(risk);
- callback();
- },
- error: function () {
- resolve({});
- callback();
- }
- });
+ if (key === 'apValues') {
+ AP_KEYS.forEach((apKey) => {
+ if (!contextData[key]?.[apKey]?.length) return;
+ logMessage(`${prefix}_ap_${apKey}`, contextData[key][apKey]);
+ setKeyValue(`${prefix}_ap_${apKey}`, contextData[key][apKey]);
+ });
+ return;
+ }
+
+ if (contextData[key]?.length) {
+ logMessage(`${prefix}_${key}`, contextData[key]);
+ setKeyValue(`${prefix}_${key}`, contextData[key]);
+ }
});
}
-function getPageUrl() {
- return window.location.href;
+export function makeDataFromResponse(contextData) {
+ const data = typeof contextData === 'string' ? safeJSONParse(contextData) : contextData;
+ const results = data.results;
+ if (!results) {
+ return {};
+ }
+ return {
+ apValues: results.ap || {},
+ categories: results.mobianContentCategories,
+ emotions: results.mobianEmotions,
+ genres: results.mobianGenres,
+ risk: results.mobianRisk || 'unknown',
+ sentiment: results.mobianSentiment || 'unknown',
+ themes: results.mobianThemes,
+ tones: results.mobianTones,
+ };
+}
+
+export function extendBidRequestConfig(bidReqConfig, contextData) {
+ logMessage('extendBidRequestConfig', bidReqConfig, contextData);
+ const { site: ortb2Site } = bidReqConfig.ortb2Fragments.global;
+
+ ortb2Site.ext = ortb2Site.ext || {};
+ ortb2Site.ext.data = {
+ ...(ortb2Site.ext.data || {}),
+ ...contextData
+ };
+
+ return bidReqConfig;
}
+/**
+ * @param {MobianConfig} config
+ * @returns {boolean}
+ */
+function init(config) {
+ logMessage('init', config);
+
+ const parsedConfig = getConfig(config);
+
+ if (parsedConfig.publisherTargeting.length) {
+ getContextData().then((contextData) => setTargeting(parsedConfig, contextData));
+ }
+
+ return true;
+}
+
+function getBidRequestData(bidReqConfig, callback, config) {
+ logMessage('getBidRequestData', bidReqConfig);
+
+ const { advertiserTargeting } = getConfig(config);
+
+ if (!advertiserTargeting.length) {
+ callback();
+ return;
+ }
+
+ getContextData()
+ .then((contextData) => {
+ extendBidRequestConfig(bidReqConfig, contextData);
+ })
+ .catch(() => {})
+ .finally(() => callback());
+}
+
+/** @type {RtdSubmodule} */
+export const mobianBrandSafetySubmodule = {
+ name: 'mobianBrandSafety',
+ init: init,
+ getBidRequestData: getBidRequestData
+};
+
submodule('realTimeData', mobianBrandSafetySubmodule);
diff --git a/modules/mobianRtdProvider.md b/modules/mobianRtdProvider.md
index e7475eb40fb..e974e56dcdd 100644
--- a/modules/mobianRtdProvider.md
+++ b/modules/mobianRtdProvider.md
@@ -1,11 +1,41 @@
-# Overview
+# Mobian Rtd Provider
+
+## Overview
Module Name: Mobian Rtd Provider
Module Type: Rtd Provider
Maintainer: rich.rodriguez@themobian.com
-# Description
+## Description
RTD provider for themobian Brand Safety determinations. Publishers
should use this to get Mobian's GARM Risk evaluations for
a URL.
+
+## Configuration
+
+```js
+pbjs.setConfig({
+ realTimeData: {
+ dataProviders: [{
+ name: 'mobianBrandSafety',
+ params: {
+ // Prefix for the targeting keys (default: 'mobian')
+ prefix: 'mobian',
+
+ // Enable targeting keys for advertiser data
+ advertiserTargeting: true,
+ // Or set it as an array to pick specific targeting keys:
+ // advertiserTargeting: ['genres', 'emotions', 'themes'],
+ // Available values: 'apValues', 'categories', 'emotions', 'genres', 'risk', 'sentiment', 'themes', 'tones'
+
+ // Enable targeting keys for publisher data
+ publisherTargeting: true,
+ // Or set it as an array to pick specific targeting keys:
+ // publisherTargeting: ['tones', 'risk'],
+ // Available values: 'apValues', 'categories', 'emotions', 'genres', 'risk', 'sentiment', 'themes', 'tones'
+ }
+ }]
+ }
+});
+```
diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js
index c9da876b292..bdd0fa7e054 100644
--- a/modules/nativoBidAdapter.js
+++ b/modules/nativoBidAdapter.js
@@ -1,6 +1,6 @@
import { deepAccess, isEmpty } from '../src/utils.js'
import { registerBidder } from '../src/adapters/bidderFactory.js'
-import { BANNER } from '../src/mediaTypes.js'
+import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'
import { getGlobal } from '../src/prebidGlobal.js'
import { ortbConverter } from '../libraries/ortbConverter/converter.js'
@@ -8,14 +8,16 @@ const converter = ortbConverter({
context: {
// `netRevenue` and `ttl` are required properties of bid responses - provide a default for them
netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false
- ttl: 30 // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp)
+ ttl: 30, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp)
},
imp(buildImp, bidRequest, context) {
- const imp = buildImp(bidRequest, context);
+ const imp = buildImp(bidRequest, context)
imp.tagid = bidRequest.adUnitCode
- return imp;
- }
-});
+ if (imp.ext) imp.ext.placementId = bidRequest.params.placementId
+
+ return imp
+ },
+})
const BIDDER_CODE = 'nativo'
const BIDDER_ENDPOINT = 'https://exchange.postrelease.com/prebid'
@@ -24,12 +26,22 @@ const GVLID = 263
const TIME_TO_LIVE = 360
-const SUPPORTED_AD_TYPES = [BANNER]
+const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE]
const FLOOR_PRICE_CURRENCY = 'USD'
const PRICE_FLOOR_WILDCARD = '*'
const localPbjsRef = getGlobal()
+function getMediaType(accessObj) {
+ if (deepAccess(accessObj, 'mediaTypes.video')) {
+ return VIDEO
+ } else if (deepAccess(accessObj, 'mediaTypes.native')) {
+ return NATIVE
+ } else {
+ return BANNER
+ }
+}
+
/**
* Keep track of bid data by keys
* @returns {Object} - Map of bid data that can be referenced by multiple keys
@@ -122,8 +134,7 @@ export const spec = {
*/
isBidRequestValid: function (bid) {
// We don't need any specific parameters to make a bid request
- // If not parameters are supplied just verify it's the correct bidder code
- if (!bid.params) return bid.bidder === BIDDER_CODE
+ if (!bid.params) return true
// Check if any supplied parameters are invalid
const hasInvalidParameters = Object.keys(bid.params).some((key) => {
@@ -150,7 +161,10 @@ export const spec = {
*/
buildRequests: function (validBidRequests, bidderRequest) {
// Get OpenRTB Data
- const openRTBData = converter.toORTB({bidRequests: validBidRequests, bidderRequest})
+ const openRTBData = converter.toORTB({
+ bidRequests: validBidRequests,
+ bidderRequest,
+ })
const openRTBDataString = JSON.stringify(openRTBData)
const requestData = new RequestData()
@@ -201,7 +215,8 @@ export const spec = {
let params = [
// Prebid version
{
- key: 'ntv_pbv', value: localPbjsRef.version
+ key: 'ntv_pbv',
+ value: localPbjsRef.version,
},
// Prebid request id
{ key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId },
@@ -278,19 +293,31 @@ export const spec = {
})
}
+ // Add GPP params
+ if (bidderRequest.gppConsent) {
+ params.unshift({
+ key: 'ntv_gpp_consent',
+ value: bidderRequest.gppConsent.gppString,
+ })
+ }
+
// Add USP params
if (bidderRequest.uspConsent) {
// Put on the beginning of the qs param array
params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent })
}
- const qsParamStrings = [requestData.getRequestDataQueryString(), arrayToQS(params)]
+ const qsParamStrings = [
+ requestData.getRequestDataQueryString(),
+ arrayToQS(params),
+ ]
const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings)
let serverRequest = {
method: 'POST',
url: requestUrl,
data: openRTBDataString,
+ bidderRequest: bidderRequest,
}
return serverRequest
@@ -320,9 +347,10 @@ export const spec = {
// Step through and grab pertinent data
let bidResponse, adUnit
- seatbids.forEach((seatbid) => {
+ seatbids.forEach((seatbid, i) => {
seatbid.bid.forEach((bid) => {
adUnit = this.getAdUnitData(body.id, bid)
+
bidResponse = {
requestId: adUnit.bidId,
cpm: bid.price,
@@ -337,10 +365,18 @@ export const spec = {
meta: {
advertiserDomains: bid.adomain,
},
+ mediaType: getMediaType(request.bidderRequest.bids[i]),
}
if (bid.ext) extData[bid.id] = bid.ext
-
+ if (bidResponse.mediaType === VIDEO) {
+ bidResponse.vastUrl = bid.adm
+ }
+ if (bidResponse.mediaType === NATIVE) {
+ bidResponse.native = {
+ ortb: JSON.parse(bidResponse.ad),
+ }
+ }
bidResponses.push(bidResponse)
})
})
@@ -414,23 +450,27 @@ export const spec = {
typeof response.body === 'string'
? JSON.parse(response.body)
: response.body
- } catch (err) { return }
+ } catch (err) {
+ return
+ }
// Make sure we have valid content
if (!body || !body.seatbid || body.seatbid.length === 0) return
body.seatbid.forEach((seatbid) => {
// Grab the syncs for each seatbid
- seatbid.syncUrls.forEach((sync) => {
- if (types[sync.type]) {
- if (sync.url.trim() !== '') {
- syncs.push({
- type: sync.type,
- url: sync.url.replace('{GDPR_params}', params),
- })
+ if (seatbid.syncUrls) {
+ seatbid.syncUrls.forEach((sync) => {
+ if (types[sync.type]) {
+ if (sync.url.trim() !== '') {
+ syncs.push({
+ type: sync.type,
+ url: sync.url.replace('{GDPR_params}', params),
+ })
+ }
}
- }
- })
+ })
+ }
})
})
@@ -491,7 +531,9 @@ export class RequestData {
getRequestDataQueryString() {
if (this.bidRequestDataSources.length == 0) return
- const queryParams = this.bidRequestDataSources.map(dataSource => dataSource.getRequestQueryString()).filter(queryString => queryString !== '')
+ const queryParams = this.bidRequestDataSources
+ .map((dataSource) => dataSource.getRequestQueryString())
+ .filter((queryString) => queryString !== '')
return queryParams.join('&')
}
}
@@ -500,8 +542,10 @@ export class BidRequestDataSource {
constructor() {
this.type = 'BidRequestDataSource'
}
- processBidRequestData(bidRequest, bidderRequest) { }
- getRequestQueryString() { return '' }
+ processBidRequestData(bidRequest, bidderRequest) {}
+ getRequestQueryString() {
+ return ''
+ }
}
export class UserEIDs extends BidRequestDataSource {
@@ -540,7 +584,7 @@ QueryStringParam.prototype.toString = function () {
export function encodeToBase64(value) {
try {
return btoa(JSON.stringify(value))
- } catch (err) { }
+ } catch (err) {}
}
export function parseFloorPriceData(bidRequest) {
@@ -565,7 +609,7 @@ export function parseFloorPriceData(bidRequest) {
size,
})
// Save the data and track the sizes
- mediaTypeFloorPriceData[sizeToString(size)] = priceFloorData.floor
+ mediaTypeFloorPriceData[sizeToString(size)] = priceFloorData?.floor
sizeOptions.add(size)
})
bidRequestFloorPriceData[mediaType] = mediaTypeFloorPriceData
@@ -573,7 +617,7 @@ export function parseFloorPriceData(bidRequest) {
// Get floor price of current media type with a wildcard size
const sizeWildcardFloor = getSizeWildcardPrice(bidRequest, mediaType)
// Save the wildcard floor price if it was retrieved successfully
- if (sizeWildcardFloor.floor > 0) {
+ if (sizeWildcardFloor?.floor > 0) {
mediaTypeFloorPriceData['*'] = sizeWildcardFloor.floor
}
})
@@ -708,9 +752,13 @@ function getLargestSize(sizes, method = area) {
* Build the final request url
*/
export function buildRequestUrl(baseUrl, qsParamStringArray = []) {
- if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) return baseUrl
+ if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) {
+ return baseUrl
+ }
- const nonEmptyQSParamStrings = qsParamStringArray.filter(qsParamString => qsParamString.trim() !== '')
+ const nonEmptyQSParamStrings = qsParamStringArray.filter(
+ (qsParamString) => qsParamString.trim() !== ''
+ )
if (nonEmptyQSParamStrings.length === 0) return baseUrl
@@ -752,7 +800,7 @@ export function getPageUrlFromBidRequest(bidRequest) {
try {
const url = new URL(paramPageUrl)
return url.href
- } catch (err) { }
+ } catch (err) {}
}
export function hasProtocol(url) {
diff --git a/modules/nativoBidAdapter.md b/modules/nativoBidAdapter.md
index f83fb45b52e..515d87af28e 100644
--- a/modules/nativoBidAdapter.md
+++ b/modules/nativoBidAdapter.md
@@ -16,24 +16,91 @@ gulp serve --modules=nativoBidAdapter
# Test Parameters
+## Banner
+
+```js
+var adUnits = [
+ {
+ code: 'div-gpt-ad-1460505748561-0',
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [300, 250],
+ [300, 600],
+ ],
+ },
+ },
+ // Replace this object to test a new Adapter!
+ bids: [
+ {
+ bidder: 'nativo',
+ params: {
+ url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html',
+ },
+ },
+ ],
+ },
+]
```
+
+## Video
+
+```js
var adUnits = [
- {
- code: 'div-gpt-ad-1460505748561-0',
- mediaTypes: {
- banner: {
- sizes: [[300, 250], [300,600]],
- }
- },
- // Replace this object to test a new Adapter!
- bids: [{
- bidder: 'nativo',
- params: {
- url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html'
- }
- }]
-
- }
- ];
+ {
+ code: 'ntvPlaceholder-1',
+ mediaTypes: {
+ video: {
+ mimes: ['video/mp4'],
+ protocols: [2, 3, 5, 6],
+ playbackmethod: [1, 2],
+ skip: 1,
+ skipafter: 5,
+ },
+ },
+ video: {
+ divId: 'player',
+ },
+ bids: [
+ {
+ bidder: 'nativo',
+ params: {
+ url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html',
+ },
+ },
+ ],
+ },
+]
+```
+
+## Native
+```js
+var adUnits = [
+ {
+ code: '/416881364/prebid-native-test-unit',
+ sizes: [[300, 250]],
+ mediaTypes: {
+ native: {
+ title: {
+ required: true,
+ },
+ image: {
+ required: true,
+ },
+ sponsoredBy: {
+ required: true,
+ },
+ },
+ },
+ bids: [
+ {
+ bidder: 'nativo',
+ params: {
+ url: 'https://test-sites.internal.nativo.net/testing/prebid_adpater.html',
+ },
+ },
+ ],
+ },
+]
```
diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js
index 540659a7034..112f0d68504 100644
--- a/modules/nextMillenniumBidAdapter.js
+++ b/modules/nextMillenniumBidAdapter.js
@@ -11,6 +11,7 @@ import {
parseUrl,
triggerPixel,
} from '../src/utils.js';
+
import {getAd} from '../libraries/targetVideoUtils/bidderUtils.js';
import { EVENTS } from '../src/constants.js';
@@ -20,7 +21,7 @@ import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {getRefererInfo} from '../src/refererDetection.js';
-const NM_VERSION = '4.1.0';
+const NM_VERSION = '4.2.1';
const PBJS_VERSION = 'v$prebid.version$';
const GVLID = 1060;
const BIDDER_CODE = 'nextMillennium';
@@ -86,10 +87,10 @@ export const spec = {
buildRequests: function(validBidRequests, bidderRequest) {
const requests = [];
- const bidIds = {};
window.nmmRefreshCounts = window.nmmRefreshCounts || {};
const site = getSiteObj();
const device = getDeviceObj();
+ const source = getSourceObj(validBidRequests, bidderRequest);
const tmax = deepAccess(bidderRequest, 'timeout') || DEFAULT_TMAX;
const postBody = {
@@ -101,6 +102,7 @@ export const spec = {
device,
site,
+ source,
imp: [],
};
@@ -118,8 +120,6 @@ export const spec = {
if (i === 0) postBody.cur = cur;
postBody.imp.push(getImp(bid, id, mediaTypes));
postBody.ext.next_mil_imps.push(getExtNextMilImp(bid));
-
- bidIds[bid.adUnitCode] = bid.bidId;
});
this.getUrlPixelMetric(EVENTS.BID_REQUESTED, validBidRequests);
@@ -132,21 +132,19 @@ export const spec = {
contentType: 'text/plain',
withCredentials: true,
},
-
- bidIds,
});
return requests;
},
- interpretResponse: function(serverResponse, bidRequest) {
+ interpretResponse: function(serverResponse) {
const response = serverResponse.body;
const bidResponses = [];
const bids = [];
_each(response.seatbid, (resp) => {
_each(resp.bid, (bid) => {
- const requestId = bidRequest.bidIds[bid.impid];
+ const requestId = bid.impid;
const {ad, adUrl, vastUrl, vastXml} = getAd(bid);
@@ -268,7 +266,7 @@ export const spec = {
function getExtNextMilImp(bid) {
if (typeof window?.nmmRefreshCounts[bid.adUnitCode] === 'number') ++window.nmmRefreshCounts[bid.adUnitCode];
const nextMilImp = {
- impId: bid.adUnitCode,
+ impId: bid.bidId,
nextMillennium: {
nm_version: NM_VERSION,
pbjs_version: PBJS_VERSION,
@@ -283,7 +281,7 @@ function getExtNextMilImp(bid) {
export function getImp(bid, id, mediaTypes) {
const {banner, video} = mediaTypes;
const imp = {
- id: bid.adUnitCode,
+ id: bid.bidId,
ext: {
prebid: {
storedrequest: {
@@ -421,8 +419,8 @@ function getCurrency(bid = {}) {
if (typeof bid.getFloor === 'function') {
let floorInfo = bid.getFloor({currency, mediaType, size: '*'});
- mediaTypes[mediaType].bidfloorcur = floorInfo.currency;
- mediaTypes[mediaType].bidfloor = floorInfo.floor;
+ mediaTypes[mediaType].bidfloorcur = floorInfo?.currency;
+ mediaTypes[mediaType].bidfloor = floorInfo?.floor;
} else {
mediaTypes[mediaType].bidfloorcur = currency;
};
@@ -495,6 +493,19 @@ function getDeviceObj() {
};
}
+export function getSourceObj(validBidRequests, bidderRequest) {
+ const schain = validBidRequests?.[0]?.schain ||
+ (bidderRequest?.ortb2?.source && (bidderRequest?.ortb2?.source?.schain || bidderRequest?.ortb2?.source?.ext?.schain));
+
+ if (!schain) return;
+
+ const source = {
+ schain,
+ };
+
+ return source;
+}
+
function getSua() {
let {brands, mobile, platform} = (window?.navigator?.userAgentData || {});
if (!(brands && platform)) return undefined;
diff --git a/modules/nexx360BidAdapter.js b/modules/nexx360BidAdapter.js
index 69032fb151b..933c0378f03 100644
--- a/modules/nexx360BidAdapter.js
+++ b/modules/nexx360BidAdapter.js
@@ -1,4 +1,3 @@
-import {config} from '../src/config.js';
import { deepAccess, deepSetValue, generateUUID, logError, logInfo } from '../src/utils.js';
import {Renderer} from '../src/Renderer.js';
import {getStorageManager} from '../src/storageManager.js';
@@ -7,6 +6,7 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js'
import { INSTREAM, OUTSTREAM } from '../src/video.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -34,6 +34,7 @@ const ALIASES = [
{ code: 'prjads' },
{ code: 'pubtech' },
{ code: '1accord', gvlid: 965 },
+ { code: 'easybid', gvlid: 1068 },
];
export const storage = getStorageManager({
@@ -131,7 +132,7 @@ const converter = ortbConverter({
deepSetValue(request, 'ext.source', 'prebid.js');
deepSetValue(request, 'ext.pageViewId', PAGE_VIEW_ID);
deepSetValue(request, 'ext.bidderVersion', BIDDER_VERSION);
- deepSetValue(request, 'cur', [config.getConfig('currency.adServerCurrency') || 'USD']);
+ deepSetValue(request, 'cur', [getCurrencyFromBidderRequest(bidderRequest) || 'USD']);
if (!request.user) request.user = {};
if (getAmxId()) {
if (!request.user.ext) request.user.ext = {};
diff --git a/modules/nexx360BidAdapter.md b/modules/nexx360BidAdapter.md
index 532d48418b6..5935b568a13 100644
--- a/modules/nexx360BidAdapter.md
+++ b/modules/nexx360BidAdapter.md
@@ -30,8 +30,7 @@ var adUnits = [
bids: [{
bidder: 'nexx360',
params: {
- account: '1067',
- tagId: 'luvxjvgn'
+ tagId: 'testnexx'
}
}]
},
@@ -51,8 +50,7 @@ var adUnits = [
bids: [{
bidder: 'nexx360',
params: {
- account: '1067',
- tagId: 'luvxjvgn'
+ tagId: 'testnexx'
}
}]
};
diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js
index 4865ea3bc9c..7cacac819c1 100644
--- a/modules/nobidBidAdapter.js
+++ b/modules/nobidBidAdapter.js
@@ -231,7 +231,7 @@ function nobidBuildRequests(bids, bidderRequest) {
return adunits;
}
function getFloor (bid) {
- if (bid && typeof bid.getFloor === 'function' && bid.getFloor().floor) {
+ if (bid && typeof bid.getFloor === 'function' && bid.getFloor()?.floor) {
return bid.getFloor().floor;
}
return null;
diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js
index c33fccdbac9..ecf512f11d3 100644
--- a/modules/oguryBidAdapter.js
+++ b/modules/oguryBidAdapter.js
@@ -1,7 +1,7 @@
'use strict';
import {BANNER} from '../src/mediaTypes.js';
-import {getWindowSelf, getWindowTop, isFn, logWarn, deepAccess} from '../src/utils.js';
+import {getWindowSelf, getWindowTop, isFn, logWarn, deepAccess, isPlainObject} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {ajax} from '../src/ajax.js';
import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
@@ -211,7 +211,7 @@ function getFloor(bid) {
mediaType: 'banner',
size: '*'
});
- return floorResult.currency === 'USD' ? floorResult.floor : 0;
+ return (isPlainObject(floorResult) && floorResult.currency === 'USD') ? floorResult.floor : 0;
}
function getWindowContext() {
diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js
index 8ddcb2c3980..8bf326b4848 100644
--- a/modules/onetagBidAdapter.js
+++ b/modules/onetagBidAdapter.js
@@ -411,7 +411,7 @@ function getBidFloor(bidRequest, mediaType, sizes) {
currency: 'EUR',
mediaType: mediaType || '*',
size: [size.width, size.height]
- });
+ }) || {};
floor.size = deepClone(size);
if (!floor.floor) { floor.floor = null; }
priceFloors.push(floor);
diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js
index 396131fd8aa..f8dea0f5a92 100755
--- a/modules/optidigitalBidAdapter.js
+++ b/modules/optidigitalBidAdapter.js
@@ -1,6 +1,6 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
-import {deepAccess, parseSizesInput} from '../src/utils.js';
+import {deepAccess, isPlainObject, parseSizesInput} from '../src/utils.js';
import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
/**
@@ -247,7 +247,7 @@ function _getFloor (bid, sizes, currency) {
mediaType: 'banner',
size: size
});
- if (typeof floorInfo === 'object' && floorInfo.currency === CUR && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === CUR && !isNaN(parseFloat(floorInfo.floor))) {
floor = parseFloat(floorInfo.floor);
}
} catch (err) {}
diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js
index 5ce514a46d7..f626b2485d1 100644
--- a/modules/outbrainBidAdapter.js
+++ b/modules/outbrainBidAdapter.js
@@ -5,7 +5,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import { getStorageManager } from '../src/storageManager.js';
import {OUTSTREAM} from '../src/video.js';
-import {_map, deepAccess, deepSetValue, logWarn, replaceAuctionPrice, setOnAny, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js';
+import {_map, deepAccess, deepSetValue, logWarn, replaceAuctionPrice, setOnAny, parseGPTSingleSizeArrayToRtbSize, isPlainObject} from '../src/utils.js';
import {ajax} from '../src/ajax.js';
import {config} from '../src/config.js';
import {convertOrtbRequestToProprietaryNative} from '../src/native.js';
@@ -353,7 +353,7 @@ function _getFloor(bid, type) {
mediaType: type,
size: '*'
});
- if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
return parseFloat(floorInfo.floor);
}
return null;
diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js
index d3ba7fc0792..d554c0afeda 100644
--- a/modules/ozoneBidAdapter.js
+++ b/modules/ozoneBidAdapter.js
@@ -494,13 +494,13 @@ export const spec = {
logInfo('getFloorObjectForAuction mediaTypesSizes : ', mediaTypesSizes);
let ret = {};
if (mediaTypesSizes.banner) {
- ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner});
+ ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner}) || {};
}
if (mediaTypesSizes.video) {
- ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video});
+ ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video}) || {};
}
if (mediaTypesSizes.native) {
- ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native});
+ ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native}) || {};
}
logInfo('getFloorObjectForAuction returning : ', deepClone(ret));
return ret;
diff --git a/modules/pgamsspBidAdapter.js b/modules/pgamsspBidAdapter.js
index 859bfc9de7e..36dbd1159cc 100644
--- a/modules/pgamsspBidAdapter.js
+++ b/modules/pgamsspBidAdapter.js
@@ -3,11 +3,13 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { isBidRequestValid, buildRequests, interpretResponse, getUserSyncs } from '../libraries/teqblazeUtils/bidderUtils.js';
const BIDDER_CODE = 'pgamssp';
+const GVLID = 1353;
const AD_URL = 'https://us-east.pgammedia.com/pbjs';
const SYNC_URL = 'https://cs.pgammedia.com';
export const spec = {
code: BIDDER_CODE,
+ gvlid: GVLID,
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
isBidRequestValid: isBidRequestValid(),
diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js
index 33ddb9847fa..673e92f709b 100644
--- a/modules/prebidServerBidAdapter/index.js
+++ b/modules/prebidServerBidAdapter/index.js
@@ -1,6 +1,5 @@
import Adapter from '../../src/adapter.js';
import {
- deepAccess,
deepClone,
flatten,
generateUUID,
@@ -450,7 +449,7 @@ export function PrebidServer() {
/* Prebid executes this function when the page asks to send out bid requests */
baseAdapter.callBids = function(s2sBidRequest, bidRequests, addBidResponse, done, ajax) {
- const adapterMetrics = s2sBidRequest.metrics = useMetrics(deepAccess(bidRequests, '0.metrics'))
+ const adapterMetrics = s2sBidRequest.metrics = useMetrics(bidRequests?.[0]?.metrics)
.newMetrics()
.renameWith((n) => [`adapter.s2s.${n}`, `adapters.s2s.${s2sBidRequest.s2sConfig.defaultVendor}.${n}`])
done = adapterMetrics.startTiming('total').stopBefore(done);
@@ -568,7 +567,7 @@ export const processPBSRequest = hook('sync', function (s2sBidRequest, bidReques
const requestJson = request && JSON.stringify(request);
logInfo('BidRequest: ' + requestJson);
const endpointUrl = getMatchingConsentUrl(s2sBidRequest.s2sConfig.endpoint, gdprConsent);
- const customHeaders = deepAccess(s2sBidRequest, 's2sConfig.customHeaders', {});
+ const customHeaders = s2sBidRequest?.s2sConfig?.customHeaders ?? {};
if (request && requestJson && endpointUrl) {
const networkDone = s2sBidRequest.metrics.startTiming('net');
ajax(
diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js
index 31ea363c9df..9de7d3f05c9 100644
--- a/modules/prebidServerBidAdapter/ortbConverter.js
+++ b/modules/prebidServerBidAdapter/ortbConverter.js
@@ -1,5 +1,5 @@
import {ortbConverter} from '../../libraries/ortbConverter/converter.js';
-import {deepAccess, deepSetValue, getBidRequest, logError, logWarn, mergeDeep, timestamp} from '../../src/utils.js';
+import {deepSetValue, getBidRequest, logError, logWarn, mergeDeep, timestamp} from '../../src/utils.js';
import {config} from '../../src/config.js';
import {S2S, STATUS} from '../../src/constants.js';
import {createBid} from '../../src/bidfactory.js';
@@ -37,7 +37,7 @@ const PBS_CONVERTER = ortbConverter({
}
});
if (Object.values(SUPPORTED_MEDIA_TYPES).some(mtype => imp[mtype])) {
- imp.secure = context.s2sBidRequest.s2sConfig.secure;
+ imp.secure = proxyBidRequest.ortb2Imp?.secure ?? 1;
return imp;
}
},
@@ -183,7 +183,7 @@ const PBS_CONVERTER = ortbConverter({
},
sourceExtSchain(orig, ortbRequest, proxyBidderRequest, context) {
// pass schains in ext.prebid.schains
- let chains = (deepAccess(ortbRequest, 'ext.prebid.schains') || []);
+ let chains = ortbRequest?.ext?.prebid?.schains || [];
const chainBidders = new Set(chains.flatMap((item) => item.bidders));
chains = Object.values(
@@ -192,7 +192,7 @@ const PBS_CONVERTER = ortbConverter({
.filter((req) => !chainBidders.has(req.bidderCode)) // schain defined in s2sConfig.extPrebid takes precedence
.map((req) => ({
bidders: [req.bidderCode],
- schain: deepAccess(req, 'bids.0.schain')
+ schain: req?.bids?.[0]?.schain
})))
.filter(({bidders, schain}) => bidders?.length > 0 && schain)
.reduce((chains, {bidders, schain}) => {
diff --git a/modules/priceFloors.js b/modules/priceFloors.js
index 5df8f938c3d..d14a82af360 100644
--- a/modules/priceFloors.js
+++ b/modules/priceFloors.js
@@ -3,7 +3,6 @@ import {
deepAccess,
deepClone,
deepSetValue,
- generateUUID,
getParameterByName,
isNumber,
logError,
@@ -13,7 +12,8 @@ import {
parseGPTSingleSizeArray,
parseUrl,
pick,
- deepEqual
+ deepEqual,
+ generateUUID
} from '../src/utils.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {config} from '../src/config.js';
@@ -30,6 +30,7 @@ import {timedAuctionHook, timedBidResponseHook} from '../src/utils/perfMetrics.j
import {adjustCpm} from '../src/utils/cpm.js';
import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js';
import {convertCurrency} from '../libraries/currencyUtils/currency.js';
+import { timeoutQueue } from '../libraries/timeoutQueue/timeoutQueue.js';
export const FLOOR_SKIPPED_REASON = {
NOT_FOUND: 'not_found',
@@ -72,7 +73,7 @@ let _floorsConfig = {};
/**
* @summary If a auction is to be delayed by an ongoing fetch we hold it here until it can be resumed
*/
-let _delayedAuctions = [];
+const _delayedAuctions = timeoutQueue();
/**
* @summary Each auction can have differing floors data depending on execution time or per adunit setup
@@ -270,6 +271,10 @@ export function getFloor(requestParams = {currency: 'USD', mediaType: '*', size:
}
}
+ if (floorInfo.floorRuleValue === null) {
+ return null;
+ }
+
if (floorInfo.matchingFloor) {
return {
floor: roundUp(floorInfo.matchingFloor, 4),
@@ -436,17 +441,11 @@ export function createFloorsDataForAuction(adUnits, auctionId) {
* @summary This is the function which will be called to exit our module and continue the auction.
*/
export function continueAuction(hookConfig) {
- // only run if hasExited
if (!hookConfig.hasExited) {
- // if this current auction is still fetching, remove it from the _delayedAuctions
- _delayedAuctions = _delayedAuctions.filter(auctionConfig => auctionConfig.timer !== hookConfig.timer);
-
// We need to know the auctionId at this time. So we will use the passed in one or generate and set it ourselves
hookConfig.reqBidsConfigObj.auctionId = hookConfig.reqBidsConfigObj.auctionId || generateUUID();
-
// now we do what we need to with adUnits and save the data object to be used for getFloor and enforcement calls
_floorDataForAuction[hookConfig.reqBidsConfigObj.auctionId] = createFloorsDataForAuction(hookConfig.reqBidsConfigObj.adUnits || getGlobal().adUnits, hookConfig.reqBidsConfigObj.auctionId);
-
hookConfig.nextFn.apply(hookConfig.context, [hookConfig.reqBidsConfigObj]);
hookConfig.hasExited = true;
}
@@ -467,7 +466,7 @@ function isValidRule(key, floor, numFields, delimiter) {
if (typeof key !== 'string' || key.split(delimiter).length !== numFields) {
return false;
}
- return typeof floor === 'number';
+ return typeof floor === 'number' || floor === null;
}
function validateRules(floorsData, numFields, delimiter) {
@@ -577,36 +576,22 @@ export const requestBidsHook = timedAuctionHook('priceFloors', function requestB
reqBidsConfigObj,
context: this,
nextFn: fn,
- haveExited: false,
+ hasExited: false,
timer: null
};
// If auction delay > 0 AND we are fetching -> Then wait until it finishes
if (_floorsConfig.auctionDelay > 0 && fetching) {
- hookConfig.timer = setTimeout(() => {
+ _delayedAuctions.submit(_floorsConfig.auctionDelay, () => continueAuction(hookConfig), () => {
logWarn(`${MODULE_NAME}: Fetch attempt did not return in time for auction`);
_floorsConfig.fetchStatus = 'timeout';
continueAuction(hookConfig);
- }, _floorsConfig.auctionDelay);
- _delayedAuctions.push(hookConfig);
+ });
} else {
continueAuction(hookConfig);
}
});
-/**
- * @summary If an auction was queued to be delayed (waiting for a fetch) then this function will resume
- * those delayed auctions when delay is hit or success return or fail return
- */
-function resumeDelayedAuctions() {
- _delayedAuctions.forEach(auctionConfig => {
- // clear the timeout
- clearTimeout(auctionConfig.timer);
- continueAuction(auctionConfig);
- });
- _delayedAuctions = [];
-}
-
/**
* This function handles the ajax response which comes from the user set URL to fetch floors data from
* @param {object} fetchResponse The floors data response which came back from the url configured in config.floors
@@ -631,7 +616,7 @@ export function handleFetchResponse(fetchResponse) {
}
// if any auctions are waiting for fetch to finish, we need to continue them!
- resumeDelayedAuctions();
+ _delayedAuctions.resume();
}
function handleFetchError(status) {
@@ -640,7 +625,7 @@ function handleFetchError(status) {
logError(`${MODULE_NAME}: Fetch errored with: `, status);
// if any auctions are waiting for fetch to finish, we need to continue them!
- resumeDelayedAuctions();
+ _delayedAuctions.resume();
}
/**
@@ -827,7 +812,7 @@ export function setOrtbImpBidFloor(imp, bidRequest, context) {
currency: context.currency || config.getConfig('currency.adServerCurrency') || 'USD',
mediaType: context.mediaType || '*',
size: '*'
- }));
+ }) || {});
} catch (e) {
logWarn('Cannot compute floor for bid', bidRequest);
return;
diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js
index 617123746e5..19260e65e60 100644
--- a/modules/pubgeniusBidAdapter.js
+++ b/modules/pubgeniusBidAdapter.js
@@ -194,7 +194,7 @@ function buildImp(bid) {
mediaType: bid.mediaTypes.banner ? 'banner' : 'video',
size: '*',
currency: 'USD',
- });
+ }) || {};
if (floor) {
imp.bidfloor = floor;
diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js
index a03091f1f4a..ab83cfdf88a 100644
--- a/modules/pubmaticBidAdapter.js
+++ b/modules/pubmaticBidAdapter.js
@@ -22,6 +22,7 @@ const AUCTION_TYPE = 1;
const UNDEFINED = undefined;
const DEFAULT_WIDTH = 0;
const DEFAULT_HEIGHT = 0;
+const DEFAULT_TTL = 360;
const PREBID_NATIVE_HELP_LINK = 'http://prebid.org/dev-docs/show-native-ads.html';
const PUBLICATION = 'pubmatic'; // Your publication on Blue Billywig, potentially with environment (e.g. publication.bbvms.com or publication.test.bbvms.com)
const RENDERER_URL = 'https://pubmatic.bbvms.com/r/'.concat('$RENDERER', '.js'); // URL of the renderer application
@@ -140,6 +141,12 @@ const MEDIATYPE = [
NATIVE
]
+const MEDIATYPE_TTL = {
+ 'banner': 360,
+ 'video': 1800,
+ 'native': 1800
+};
+
let publisherId = 0;
let isInvalidNativeRequest = false;
let biddersList = ['pubmatic'];
@@ -826,7 +833,7 @@ function _addFloorFromFloorModule(impObj, bid) {
sizesArray.forEach(size => {
let floorInfo = bid.getFloor({ currency: impObj.bidfloorcur, mediaType: mediaType, size: size });
logInfo(LOG_WARN_PREFIX, 'floor from floor module returned for mediatype:', mediaType, ' and size:', size, ' is: currency', floorInfo.currency, 'floor', floorInfo.floor);
- if (typeof floorInfo === 'object' && floorInfo.currency === impObj.bidfloorcur && !isNaN(parseInt(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === impObj.bidfloorcur && !isNaN(parseInt(floorInfo.floor))) {
let mediaTypeFloor = parseFloat(floorInfo.floor);
logInfo(LOG_WARN_PREFIX, 'floor from floor module:', mediaTypeFloor, 'previous floor value', bidFloor, 'Min:', Math.min(mediaTypeFloor, bidFloor));
if (bidFloor === -1) {
@@ -859,6 +866,23 @@ function _handleEids(payload, validBidRequests) {
}
}
+export function setTTL(bid, newBid) {
+ let ttl = MEDIATYPE_TTL[newBid?.mediaType] || DEFAULT_TTL;
+ newBid.ttl = bid.exp || ttl;
+}
+
+// Setting IBV & meta.mediaType field into the bid response
+export function setIBVField(bid, newBid) {
+ if (bid?.ext?.ibv) {
+ newBid.ext = newBid.ext || {};
+ newBid.ext['ibv'] = bid.ext.ibv;
+
+ // Overriding the mediaType field in meta with the `video` value if bid.ext.ibv is present
+ newBid.meta = newBid.meta || {};
+ newBid.meta.mediaType = VIDEO;
+ }
+}
+
function _checkMediaType(bid, newBid) {
// Create a regex here to check the strings
if (bid.ext && bid.ext['bidtype'] != undefined) {
@@ -1008,7 +1032,7 @@ function isNonEmptyArray(test) {
* @param {*} bid : bids
*/
export function prepareMetaObject(br, bid, seat) {
- br.meta = {};
+ br.meta = br.meta || {};
if (bid.ext && bid.ext.dspid) {
br.meta.networkId = bid.ext.dspid;
@@ -1047,6 +1071,11 @@ export function prepareMetaObject(br, bid, seat) {
if (bid.ext && bid.ext.dsa && Object.keys(bid.ext.dsa).length) {
br.meta.dsa = bid.ext.dsa;
}
+
+ // Initializing meta.mediaType field to the actual bidType returned by the bidder
+ if (br.mediaType) {
+ br.meta.mediaType = br.mediaType;
+ }
}
export const spec = {
@@ -1373,7 +1402,7 @@ export const spec = {
dealId: bid.dealid,
currency: respCur,
netRevenue: NET_REVENUE,
- ttl: 300,
+ ttl: DEFAULT_TTL,
referrer: parsedReferrer,
ad: bid.adm,
pm_seat: seatbidder.seat || null,
@@ -1384,6 +1413,7 @@ export const spec = {
parsedRequest.imp.forEach(req => {
if (bid.impid === req.id) {
_checkMediaType(bid, newBid);
+ setTTL(bid, newBid);
switch (newBid.mediaType) {
case BANNER:
break;
@@ -1401,12 +1431,12 @@ export const spec = {
}
});
}
+ prepareMetaObject(newBid, bid, seatbidder.seat);
+ setIBVField(bid, newBid);
if (bid.ext && bid.ext.deal_channel) {
newBid['dealChannel'] = dealChannelValues[bid.ext.deal_channel] || null;
}
- prepareMetaObject(newBid, bid, seatbidder.seat);
-
// adserverTargeting
if (seatbidder.ext && seatbidder.ext.buyid) {
newBid.adserverTargeting = {
diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js
index 639a39d4636..df31dde127b 100644
--- a/modules/pubwiseBidAdapter.js
+++ b/modules/pubwiseBidAdapter.js
@@ -1,5 +1,5 @@
-import { _each, isBoolean, isEmptyStr, isNumber, isStr, deepClone, isArray, deepSetValue, inIframe, mergeDeep, deepAccess, logMessage, logInfo, logWarn, logError } from '../src/utils.js';
+import { _each, isBoolean, isEmptyStr, isNumber, isStr, deepClone, isArray, deepSetValue, inIframe, mergeDeep, deepAccess, logMessage, logInfo, logWarn, logError, isPlainObject } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';
@@ -667,7 +667,7 @@ function _addFloorFromFloorModule(impObj, bid) {
[BANNER, VIDEO, NATIVE].forEach(mediaType => {
if (impObj.hasOwnProperty(mediaType)) {
let floorInfo = bid.getFloor({ currency: impObj.bidFloorCur, mediaType: mediaType, size: '*' });
- if (typeof floorInfo === 'object' && floorInfo.currency === impObj.bidFloorCur && !isNaN(parseInt(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === impObj.bidFloorCur && !isNaN(parseInt(floorInfo.floor))) {
let mediaTypeFloor = parseFloat(floorInfo.floor);
bidFloor = (bidFloor == -1 ? mediaTypeFloor : Math.min(mediaTypeFloor, bidFloor))
}
diff --git a/modules/qortexRtdProvider.js b/modules/qortexRtdProvider.js
index 3505f64789a..1ee8f8cf5d0 100644
--- a/modules/qortexRtdProvider.js
+++ b/modules/qortexRtdProvider.js
@@ -1,14 +1,21 @@
import { submodule } from '../src/hook.js';
-import { ajax } from '../src/ajax.js';
import { logWarn, mergeDeep, logMessage, generateUUID } from '../src/utils.js';
import { loadExternalScript } from '../src/adloader.js';
import * as events from '../src/events.js';
import { EVENTS } from '../src/constants.js';
import { MODULE_TYPE_RTD } from '../src/activities/modules.js';
-const DEFAULT_API_URL = 'https://demand.qortex.ai';
-
-const qortexSessionInfo = {}
+const qortexSessionInfo = {};
+const QX_IN_MESSAGE = {
+ BID_ENRICH_INITIALIZED: 'CX-BID-ENRICH-INITIALIZED',
+ DISPATCH_CONTEXT: 'DISPATCH-CONTEXT'
+}
+const QX_OUT_MESSAGE = {
+ AUCTION_END: 'AUCTION-END',
+ NO_CONTEXT: 'NO-CONTEXT',
+ RTD_INITIALIZED: 'RTD-INITIALIZED',
+ REQUEST_CONTEXT: 'REQUEST-CONTEXT'
+}
/**
* Init if module configuration is valid
@@ -22,25 +29,7 @@ function init (config) {
} else {
initializeModuleData(config);
if (config?.params?.enableBidEnrichment) {
- logMessage('Requesting Qortex group configuration')
- getGroupConfig()
- .then(groupConfig => {
- logMessage(['Received response for qortex group config', groupConfig])
- if (groupConfig?.active === true && groupConfig?.prebidBidEnrichment === true) {
- setGroupConfigData(groupConfig);
- initializeBidEnrichment();
- } else {
- logWarn('Group config is not configured for qortex bid enrichment')
- setGroupConfigData(groupConfig);
- }
- })
- .catch((e) => {
- const errorStatus = e.message;
- logWarn('Returned error status code: ' + errorStatus);
- if (errorStatus == 404) {
- logWarn('No Group Config found');
- }
- });
+ initializeBidEnrichment();
} else {
logWarn('Bid Enrichment Function has been disabled in module configuration')
}
@@ -58,20 +47,11 @@ function init (config) {
*/
function getBidRequestData (reqBidsConfig, callback) {
if (reqBidsConfig?.adUnits?.length > 0 && shouldAllowBidEnrichment()) {
- getContext()
- .then(contextData => {
- setContextData(contextData)
- addContextToRequests(reqBidsConfig)
- callback();
- })
- .catch(e => {
- logWarn('Returned error status code: ' + e.message);
- callback();
- });
+ addContextToRequests(reqBidsConfig);
} else {
- logWarn('Module function is paused due to configuration \n Module Config: ' + JSON.stringify(reqBidsConfig) + `\n Group Config: ${JSON.stringify(qortexSessionInfo.groupConfig) ?? 'NO GROUP CONFIG'}`)
- callback();
+ logWarn('Module function is paused due to configuration \n Module Config: ' + JSON.stringify(reqBidsConfig));
}
+ callback();
}
/**
@@ -79,121 +59,13 @@ function getBidRequestData (reqBidsConfig, callback) {
* @param {Object} data Auction end object
*/
function onAuctionEndEvent (data, config, t) {
+ logMessage('Auction ended: ', JSON.stringify(data));
if (shouldAllowBidEnrichment()) {
- sendAnalyticsEvent('AUCTION', 'AUCTION_END', attachContextAnalytics(data))
- .then(result => {
- logMessage('Qortex analytics event sent')
- })
- .catch(e => logWarn(e.message))
- }
-}
-
-/**
- * determines whether to send a request to context api and does so if necessary
- * @returns {Promise} ortb Content object
- */
-export function getContext () {
- if (!qortexSessionInfo.currentSiteContext) {
- const pageUrlObject = { pageUrl: qortexSessionInfo.indexData?.pageUrl ?? '' }
- logMessage('Requesting new context data');
- return new Promise((resolve, reject) => {
- const callbacks = {
- success(text, data) {
- const responseStatus = data.status;
- let result = null;
- if (responseStatus === 200) {
- qortexSessionInfo.pageAnalysisData.contextRetrieved = true
- result = JSON.parse(data.response)?.content;
- }
- resolve(result);
- },
- error(e, x) {
- const responseStatus = x.status;
- reject(new Error(responseStatus));
- }
- }
- ajax(qortexSessionInfo.contextUrl, callbacks, JSON.stringify(pageUrlObject), {contentType: 'application/json'})
- })
- } else {
- logMessage('Adding Content object from existing context data');
- return new Promise((resolve, reject) => resolve(qortexSessionInfo.currentSiteContext));
- }
-}
-
-/**
- * Requests Qortex group configuration using group id
- * @returns {Promise} Qortex group configuration
- */
-export function getGroupConfig () {
- return new Promise((resolve, reject) => {
- const callbacks = {
- success(text, data) {
- const result = data.status === 200 ? JSON.parse(data.response) : null;
- resolve(result);
- },
- error(e, x) {
- reject(new Error(x.status));
- }
+ if (!qortexSessionInfo.auctionsEnded) {
+ qortexSessionInfo.auctionsEnded = [];
}
- ajax(qortexSessionInfo.groupConfigUrl, callbacks)
- })
-}
-
-/**
- * Sends analytics events to Qortex
- * @returns {Promise}
- */
-export function sendAnalyticsEvent(eventType, subType, data) {
- if (qortexSessionInfo.analyticsUrl !== null) {
- if (shouldSendAnalytics(data)) {
- const analtyicsEventObject = generateAnalyticsEventObject(eventType, subType, data)
- logMessage('Sending qortex analytics event');
- return new Promise((resolve, reject) => {
- const callbacks = {
- success() {
- resolve();
- },
- error(e, x) {
- reject(new Error('Returned error status code: ' + x.status));
- }
- }
- ajax(qortexSessionInfo.analyticsUrl, callbacks, JSON.stringify(analtyicsEventObject), {contentType: 'application/json'})
- })
- } else {
- return new Promise((resolve, reject) => reject(new Error('Current request did not meet analytics percentage threshold, cancelling sending event')));
- }
- } else {
- return new Promise((resolve, reject) => reject(new Error('Analytics host not initialized')));
- }
-}
-
-/**
- * Creates analytics object for Qortex
- * @returns {Object} analytics object
- */
-export function generateAnalyticsEventObject(eventType, subType, data) {
- return {
- sessionId: qortexSessionInfo.sessionId,
- groupId: qortexSessionInfo.groupId,
- eventType: eventType,
- subType: subType,
- eventOriginSource: 'RTD',
- data: data
- }
-}
-
-/**
- * Determines API host for Qortex
- * @param qortexUrlBase api url from config or default
- * @returns {string} Qortex analytics host url
- */
-export function generateAnalyticsHostUrl(qortexUrlBase) {
- if (qortexUrlBase === DEFAULT_API_URL) {
- return 'https://events.qortex.ai/api/v1/player-event';
- } else if (qortexUrlBase.includes('stg-demand')) {
- return 'https://stg-events.qortex.ai/api/v1/player-event';
- } else {
- return 'https://dev-events.qortex.ai/api/v1/player-event';
+ qortexSessionInfo.auctionsEnded.push(JSON.stringify(data));
+ postBidEnrichmentMessage(QX_OUT_MESSAGE.AUCTION_END, JSON.stringify(data));
}
}
@@ -204,20 +76,17 @@ export function generateAnalyticsHostUrl(qortexUrlBase) {
export function addContextToRequests (reqBidsConfig) {
if (qortexSessionInfo.currentSiteContext === null) {
logWarn('No context data received at this time');
+ requestContextData();
} else {
if (checkPercentageOutcome(qortexSessionInfo.groupConfig?.prebidBidEnrichmentPercentage)) {
const fragment = { site: {content: qortexSessionInfo.currentSiteContext} }
if (qortexSessionInfo.bidderArray?.length > 0) {
- qortexSessionInfo.bidderArray.forEach(bidder => mergeDeep(reqBidsConfig.ortb2Fragments.bidder, {[bidder]: fragment}))
- saveContextAdded(reqBidsConfig);
+ qortexSessionInfo.bidderArray.forEach(bidder => mergeDeep(reqBidsConfig.ortb2Fragments.bidder, {[bidder]: fragment}));
} else if (!qortexSessionInfo.bidderArray) {
mergeDeep(reqBidsConfig.ortb2Fragments.global, fragment);
- saveContextAdded(reqBidsConfig);
} else {
logWarn('Config contains an empty bidders array, unable to determine which bids to enrich');
}
- } else {
- saveContextAdded(reqBidsConfig, true);
}
}
}
@@ -265,111 +134,127 @@ export function loadScriptTag(config) {
loadExternalScript(src, MODULE_TYPE_RTD, code, undefined, undefined, attr);
}
+/**
+ * Request contextual data about page (after checking for allow) and begin listening for postMessages from publisher
+ */
export function initializeBidEnrichment() {
if (shouldAllowBidEnrichment()) {
- getContext()
- .then(contextData => {
- if (qortexSessionInfo.pageAnalysisData.contextRetrieved) {
- logMessage('Contextual record Received from Qortex API')
- setContextData(contextData)
- } else {
- logWarn('Contexual record is not yet complete at this time')
- }
- })
- .catch((e) => {
- logWarn('Returned error status code: ' + e.message)
- })
+ requestContextData()
}
+ addEventListener('message', windowPostMessageReceived);
}
+
+/**
+ * Call Qortex code on page for available contextual information about current environment
+ */
+export function requestContextData() {
+ if (qortexSessionInfo.currentSiteContext) {
+ logMessage('Context data already retrieved.');
+ } else {
+ postBidEnrichmentMessage(QX_OUT_MESSAGE.REQUEST_CONTEXT);
+ }
+}
+
/**
* Helper function to set initial values when they are obtained by init
* @param {Object} config module config obtained during init
*/
export function initializeModuleData(config) {
- const {apiUrl, groupId, bidders, enableBidEnrichment} = config.params;
- const qortexUrlBase = apiUrl || DEFAULT_API_URL;
- const windowUrl = window.top.location.host;
+ const {groupId, bidders, enableBidEnrichment} = config.params;
qortexSessionInfo.bidEnrichmentDisabled = enableBidEnrichment !== null ? !enableBidEnrichment : true;
qortexSessionInfo.bidderArray = bidders;
qortexSessionInfo.impressionIds = new Set();
qortexSessionInfo.currentSiteContext = null;
- qortexSessionInfo.pageAnalysisData = {
- contextRetrieved: false,
- contextAdded: {}
- };
qortexSessionInfo.sessionId = generateSessionId();
qortexSessionInfo.groupId = groupId;
- qortexSessionInfo.groupConfigUrl = `${qortexUrlBase}/api/v1/prebid/group/configs/${groupId}/${windowUrl}`;
- qortexSessionInfo.contextUrl = `${qortexUrlBase}/api/v1/prebid/${groupId}/page/lookup`;
- qortexSessionInfo.analyticsUrl = generateAnalyticsHostUrl(qortexUrlBase);
return qortexSessionInfo;
}
-export function saveContextAdded(reqBids, skipped = false) {
- const id = reqBids.auctionId;
- const contextBidders = qortexSessionInfo.bidderArray ?? Array.from(new Set(reqBids.adUnits.flatMap(adunit => adunit.bids.map(bid => bid.bidder))))
- qortexSessionInfo.pageAnalysisData.contextAdded[id] = {
- bidders: contextBidders,
- contextSkipped: skipped
- };
-}
-
+/**
+ * Allows setting of contextual data
+ */
export function setContextData(value) {
qortexSessionInfo.currentSiteContext = value
}
+/**
+ * Allows setting of group configuration data
+ */
export function setGroupConfigData(value) {
qortexSessionInfo.groupConfig = value
}
-export function getContextAddedEntry (id) {
- return qortexSessionInfo?.pageAnalysisData?.contextAdded[id]
-}
-
+/**
+ * Unique id generator creating an identifier through datetime and random number
+ * @returns {string}
+ */
function generateSessionId() {
const randomInt = window.crypto.getRandomValues(new Uint32Array(1));
const currentDateTime = Math.floor(Date.now() / 1000);
return 'QX' + randomInt.toString() + 'X' + currentDateTime.toString()
}
-function attachContextAnalytics (data) {
- const contextAddedEntry = getContextAddedEntry(data.auctionId);
- if (contextAddedEntry) {
- data.qortexContext = qortexSessionInfo.currentSiteContext ?? {};
- data.qortexContextBidders = contextAddedEntry?.bidders;
- data.qortexContextSkipped = contextAddedEntry?.contextSkipped;
- return data;
- } else {
- logMessage(`Auction ${data.auctionId} did not interact with qortex bid enrichment`)
- return null;
- }
-}
-
+/**
+ * Check for a random value to be above given percentage threshold
+ * @param {number} percentageValue 0-100 number for percentage check.
+ * @returns {Boolean}
+ */
function checkPercentageOutcome(percentageValue) {
- const analyticsPercentage = percentageValue ?? 0;
- const randomInt = Math.random().toFixed(5) * 100;
- return analyticsPercentage > randomInt;
-}
-
-function shouldSendAnalytics(data) {
- if (data) {
- return checkPercentageOutcome(qortexSessionInfo.groupConfig?.prebidReportingPercentage)
- } else {
- return false;
- }
+ return (percentageValue ?? 0) > (Math.random() * 100);
}
+/**
+ * Check for allowing functionality of bid enrichment capabilities.
+ * @returns {Boolean}
+ */
function shouldAllowBidEnrichment() {
if (qortexSessionInfo.bidEnrichmentDisabled) {
logWarn('Bid enrichment disabled at prebid config')
return false;
- } else if (!qortexSessionInfo.groupConfig?.prebidBidEnrichment) {
- logWarn('Bid enrichment disabled at group config')
- return false;
}
return true
}
+/**
+ * Passes message out to external page through postMessage method
+ * @param {string} msg message string to be passed to CX-BID-ENRICH target on current page
+ * @param {Object} data optional parameter object with additional data to send with post
+ */
+function postBidEnrichmentMessage(msg, data = null) {
+ window.postMessage({
+ target: 'CX-BID-ENRICH',
+ message: msg,
+ params: data
+ }, window.location.protocol + '//' + window.location.host);
+ logMessage('Dispatching window postMessage: ' + msg);
+}
+
+/**
+ * Receives messages passed through postMessage method to QORTEX-PREBIDJS-RTD-MODULE on current page
+ * @param {Object} evt data object holding Event information
+ */
+export function windowPostMessageReceived(evt) {
+ const data = evt.data;
+ if (typeof data.target !== 'undefined' && data.target === 'QORTEX-PREBIDJS-RTD-MODULE') {
+ if (shouldAllowBidEnrichment()) {
+ if (data.message === QX_IN_MESSAGE.BID_ENRICH_INITIALIZED) {
+ if (Boolean(data.params) && Boolean(data.params?.groupConfig)) {
+ setGroupConfigData(data.params.groupConfig);
+ }
+ postBidEnrichmentMessage(QX_OUT_MESSAGE.RTD_INITIALIZED);
+ if (qortexSessionInfo?.auctionsEnded?.length > 0) {
+ qortexSessionInfo.auctionsEnded.forEach(data => postBidEnrichmentMessage(QX_OUT_MESSAGE.AUCTION_END, data));
+ }
+ requestContextData();
+ } else if (data.message === QX_IN_MESSAGE.DISPATCH_CONTEXT) {
+ if (data.params?.context) {
+ setContextData(data.params.context);
+ }
+ }
+ }
+ }
+}
+
export const qortexSubmodule = {
name: 'qortex',
init,
diff --git a/modules/readpeakBidAdapter.js b/modules/readpeakBidAdapter.js
index 4ff51aeb43e..da3153c0b68 100644
--- a/modules/readpeakBidAdapter.js
+++ b/modules/readpeakBidAdapter.js
@@ -136,7 +136,7 @@ function impression(slot) {
mediaType: 'native',
size: '\*'
});
- bidFloorFromModule = floorInfo.currency === 'USD' ? floorInfo.floor : undefined;
+ bidFloorFromModule = floorInfo?.currency === 'USD' ? floorInfo?.floor : undefined;
}
const imp = {
id: slot.bidId,
diff --git a/modules/resetdigitalBidAdapter.js b/modules/resetdigitalBidAdapter.js
index 1fe4b4d750c..4f1ebafdf9c 100644
--- a/modules/resetdigitalBidAdapter.js
+++ b/modules/resetdigitalBidAdapter.js
@@ -1,4 +1,4 @@
-import { timestamp, deepAccess, isStr, deepClone } from '../src/utils.js';
+import { timestamp, deepAccess, isStr, deepClone, isPlainObject } from '../src/utils.js';
import { getOrigin } from '../libraries/getOrigin/index.js';
import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
@@ -92,7 +92,7 @@ export const spec = {
size: '*',
});
if (
- typeof floorInfo === 'object' &&
+ isPlainObject(floorInfo) &&
floorInfo.currency === CURRENCY &&
!isNaN(parseFloat(floorInfo.floor))
) {
diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js
index f1d5521f780..ce04e3aa822 100644
--- a/modules/revcontentBidAdapter.js
+++ b/modules/revcontentBidAdapter.js
@@ -207,7 +207,7 @@ function buildImp(bid, id) {
currency: 'USD',
mediaType: '*',
size: '*'
- }).floor;
+ })?.floor;
} else {
bidfloor = deepAccess(bid, `params.bidfloor`) || 0.1;
}
diff --git a/modules/rewardedInterestIdSystem.js b/modules/rewardedInterestIdSystem.js
new file mode 100644
index 00000000000..8cf514f372b
--- /dev/null
+++ b/modules/rewardedInterestIdSystem.js
@@ -0,0 +1,142 @@
+/**
+ * This module adds rewarded interest ID to the User ID module
+ * The {@link module:modules/userId} module is required
+ * @module modules/rewardedInterestIdSystem
+ * @requires module:modules/userId
+ */
+
+/**
+ * @typedef {import('../modules/userId/index.js').Submodule} Submodule
+ * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
+ */
+
+/**
+ * @typedef RewardedInterestApi
+ * @property {getApiVersion} getApiVersion
+ * @property {getIdentityToken} getIdentityToken
+ */
+
+/**
+ * Retrieves the Rewarded Interest API version.
+ * @callback getApiVersion
+ * @return {string}
+ */
+
+/**
+ * Retrieves the current identity token.
+ * @callback getIdentityToken
+ * @return {Promise}
+ */
+
+import {submodule} from '../src/hook.js';
+import {logError} from '../src/utils.js';
+
+export const MODULE_NAME = 'rewardedInterestId';
+export const SOURCE = 'rewardedinterest.com';
+
+/**
+ * Get rewarded interest API
+ * @function
+ * @returns {RewardedInterestApi|undefined}
+ */
+export function getRewardedInterestApi() {
+ if (window.__riApi && window.__riApi.getIdentityToken) {
+ return window.__riApi;
+ }
+}
+
+/**
+ * Wait while rewarded interest API to be set and execute the callback function
+ * @param {function} callback
+ */
+export function watchRewardedInterestApi(callback) {
+ Object.defineProperties(window, {
+ __rewardedInterestApi: {
+ value: undefined,
+ writable: true
+ },
+ __riApi: {
+ get: () => {
+ return window.__rewardedInterestApi;
+ },
+ set: value => {
+ window.__rewardedInterestApi = value;
+ callback(value);
+ },
+ configurable: true,
+ }
+ });
+}
+
+/**
+ * Get rewarded interest ID from API and pass it to the callback function
+ * @param {RewardedInterestApi} rewardedInterestApi
+ * @param {function} callback User ID callbackCompleted
+ */
+export function getRewardedInterestId(rewardedInterestApi, callback) {
+ rewardedInterestApi.getIdentityToken().then(callback).catch(error => {
+ callback();
+ logError(`${MODULE_NAME} module: ID fetch encountered an error`, error);
+ });
+}
+
+/**
+ * @param {function} callback User ID callbackCompleted
+ */
+export function apiNotAvailable(callback) {
+ callback();
+ logError(`${MODULE_NAME} module: Rewarded Interest API not found`);
+}
+
+/** @type {Submodule} */
+export const rewardedInterestIdSubmodule = {
+ /**
+ * Used to link submodule with config
+ * @type {string}
+ */
+ name: MODULE_NAME,
+
+ /**
+ * Decode the stored id value for passing to bid requests
+ * @function
+ * @param {string} value
+ * @returns {{rewardedInterestId: string}|undefined}
+ */
+ decode(value) {
+ return value ? {[MODULE_NAME]: value} : undefined;
+ },
+
+ /**
+ * Performs action to obtain id and return a value in the callback's response argument
+ * @function
+ * @returns {IdResponse|undefined}
+ */
+ getId() {
+ return {
+ callback: cb => {
+ const api = getRewardedInterestApi();
+ if (api) {
+ getRewardedInterestId(api, cb);
+ } else if (document.readyState === 'complete') {
+ apiNotAvailable(cb);
+ } else {
+ watchRewardedInterestApi(api => getRewardedInterestId(api, cb));
+ // Ensure that cb is called when API is not available
+ window.addEventListener('load', () => {
+ if (!getRewardedInterestApi()) {
+ apiNotAvailable(cb);
+ }
+ })
+ }
+ },
+ };
+ },
+ eids: {
+ [MODULE_NAME]: {
+ source: SOURCE,
+ atype: 3,
+ },
+ },
+};
+
+submodule('userId', rewardedInterestIdSubmodule);
diff --git a/modules/rewardedInterestIdSystem.md b/modules/rewardedInterestIdSystem.md
new file mode 100644
index 00000000000..8d12aa86e61
--- /dev/null
+++ b/modules/rewardedInterestIdSystem.md
@@ -0,0 +1,23 @@
+## Rewarded Interest User ID Submodule
+
+This module adds rewarded interest advertising token to the user ID module
+
+*Note: The storage config should be omitted
+
+### Prebid Params
+
+```javascript
+pbjs.setConfig({
+ userSync: {
+ userIds: [{
+ name: 'rewardedInterestId',
+ }]
+ }
+});
+```
+
+## Parameter Descriptions for the `usersync` Configuration Section
+
+| Param under usersync.userIds[] | Scope | Type | Description | Example |
+|--------------------------------|----------|--------|--------------------------|------------------------|
+| name | Required | String | The name of this module. | `"rewardedInterestId"` |
diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js
index edf5c03cf15..135d2e94b65 100644
--- a/modules/richaudienceBidAdapter.js
+++ b/modules/richaudienceBidAdapter.js
@@ -3,7 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js';
import {config} from '../src/config.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {Renderer} from '../src/Renderer.js';
-import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const BIDDER_CODE = 'richaudience';
let REFERER = '';
@@ -36,7 +36,7 @@ export const spec = {
ifa: bid.params.ifa,
pid: bid.params.pid,
supplyType: bid.params.supplyType,
- currencyCode: config.getConfig('currency.adServerCurrency'),
+ currencyCode: getCurrencyFromBidderRequest(bidderRequest),
auctionId: bid.auctionId,
bidId: bid.bidId,
BidRequestsCount: bid.bidRequestsCount,
@@ -53,7 +53,7 @@ export const spec = {
videoData: raiGetVideoInfo(bid),
scr_rsl: raiGetResolution(),
cpuc: (typeof window.navigator != 'undefined' ? window.navigator.hardwareConcurrency : null),
- kws: getAllOrtbKeywords(bidderRequest.ortb2, bid.params.keywords).join(','),
+ kws: bid.params.keywords,
schain: bid.schain,
gpid: raiSetPbAdSlot(bid),
dsa: setDSA(bid),
@@ -118,7 +118,9 @@ export const spec = {
netRevenue: response.netRevenue,
currency: response.currency,
ttl: response.ttl,
- meta: response.adomain,
+ meta: {
+ advertiserDomains: [response.adomain[0]]
+ },
dealId: response.dealId
};
diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js
index bcc076f1781..74a4df14f6f 100644
--- a/modules/rtbhouseBidAdapter.js
+++ b/modules/rtbhouseBidAdapter.js
@@ -215,7 +215,7 @@ function applyFloor(slot) {
if (typeof slot.getFloor === 'function') {
Object.keys(slot.mediaTypes).forEach(type => {
if (includes(SUPPORTED_MEDIA_TYPES, type)) {
- floors.push(slot.getFloor({ currency: DEFAULT_CURRENCY_ARR[0], mediaType: type, size: slot.sizes || '*' }).floor);
+ floors.push(slot.getFloor({ currency: DEFAULT_CURRENCY_ARR[0], mediaType: type, size: slot.sizes || '*' })?.floor);
}
});
}
diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js
index c0e356b86e2..411e194b1ee 100644
--- a/modules/rubiconBidAdapter.js
+++ b/modules/rubiconBidAdapter.js
@@ -3,7 +3,6 @@ import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
-import { find } from '../src/polyfill.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { Renderer } from '../src/Renderer.js';
import {
@@ -19,7 +18,8 @@ import {
mergeDeep,
parseSizesInput,
pick,
- _each
+ _each,
+ isPlainObject
} from '../src/utils.js';
import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js';
import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js';
@@ -499,6 +499,8 @@ export const spec = {
'x_imp.ext.tid': bidRequest.ortb2Imp?.ext?.tid,
'l_pb_bid_id': bidRequest.bidId,
'o_cdep': bidRequest.ortb2?.device?.ext?.cdep,
+ 'ip': bidRequest.ortb2?.device?.ip,
+ 'ipv6': bidRequest.ortb2?.device?.ipv6,
'p_screen_res': _getScreenResolution(),
'tk_user_key': params.userId,
'p_geo.latitude': isNaN(parseFloat(latitude)) ? undefined : parseFloat(latitude).toFixed(4),
@@ -519,7 +521,7 @@ export const spec = {
} catch (e) {
logError('Rubicon: getFloor threw an error: ', e);
}
- data['rp_hard_floor'] = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined;
+ data['rp_hard_floor'] = isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined;
}
// Send multiformat data if requested
@@ -542,42 +544,47 @@ export const spec = {
if (bidRequest?.ortb2Imp?.ext?.ae) {
data['o_ae'] = 1;
}
+ // If the bid request contains a 'mobile' property under 'ortb2.site', add it to 'data' as 'p_site.mobile'.
+ if (typeof bidRequest?.ortb2?.site?.mobile === 'number') {
+ data['p_site.mobile'] = bidRequest.ortb2.site.mobile
+ }
addDesiredSegtaxes(bidderRequest, data);
// loop through userIds and add to request
- if (bidRequest.userIdAsEids) {
- bidRequest.userIdAsEids.forEach(eid => {
+ if (bidRequest?.ortb2?.user?.ext?.eids) {
+ bidRequest.ortb2.user.ext.eids.forEach(({ source, uids = [], inserter, matcher, mm, ext = {} }) => {
try {
- // special cases
- if (eid.source === 'adserver.org') {
- data['tpid_tdid'] = eid.uids[0].id;
- data['eid_adserver.org'] = eid.uids[0].id;
- } else if (eid.source === 'liveintent.com') {
- data['tpid_liveintent.com'] = eid.uids[0].id;
- data['eid_liveintent.com'] = eid.uids[0].id;
- if (eid.ext && Array.isArray(eid.ext.segments) && eid.ext.segments.length) {
- data['tg_v.LIseg'] = eid.ext.segments.join(',');
- }
- } else if (eid.source === 'liveramp.com') {
- data['x_liverampidl'] = eid.uids[0].id;
- } else if (eid.source === 'id5-sync.com') {
- data['eid_id5-sync.com'] = `${eid.uids[0].id}^${eid.uids[0].atype}^${(eid.uids[0].ext && eid.uids[0].ext.linkType) || ''}`;
- } else {
- // add anything else with this generic format
- // if rubicon drop ^
- const id = eid.source === 'rubiconproject.com' ? eid.uids[0].id : `${eid.uids[0].id}^${eid.uids[0].atype || ''}`
- data[`eid_${eid.source}`] = id;
- }
- // send AE "ppuid" signal if exists, and hasn't already been sent
+ // Ensure there is at least one valid UID in the 'uids' array
+ const uidData = uids[0];
+ if (!uidData) return; // Skip processing if no valid UID exists
+
+ // Function to build the EID value in the required format
+ const buildEidValue = (uidData) => [
+ uidData.id, // uid: The user ID
+ uidData.atype || '',
+ '', // third: Always empty, as specified in the requirement
+ inserter || '',
+ matcher || '',
+ mm || '',
+ uidData?.ext?.rtipartner || ''
+ ].join('^'); // Return a single string formatted with '^' delimiter
+
+ const eidValue = buildEidValue(uidData); // Build the EID value string
+
+ // Store the constructed EID value for the given source
+ data[`eid_${source}`] = eidValue;
+
+ // Handle the "ppuid" signal, ensuring it is set only once
if (!data['ppuid']) {
- // get the first eid.uids[*].ext.stype === 'ppuid', if one exists
- const ppId = find(eid.uids, uid => uid.ext && uid.ext.stype === 'ppuid');
- if (ppId && ppId.id) {
- data['ppuid'] = ppId.id;
+ // Search for a UID with the 'stype' field equal to 'ppuid' in its extension
+ const ppId = uids.find(uid => uid.ext?.stype === 'ppuid');
+ if (ppId?.id) {
+ data['ppuid'] = ppId.id; // Store the ppuid if found
}
}
} catch (e) {
- logWarn('Rubicon: error reading eid:', eid, e);
+ // Log any errors encountered during processing
+ logWarn('Rubicon: error reading eid:', { source, uids }, e);
}
});
}
@@ -717,6 +724,10 @@ export const spec = {
bid.meta.advertiserDomains = Array.isArray(ad.adomain) ? ad.adomain : [ad.adomain];
}
+ if (ad.emulated_format) {
+ bid.meta.mediaType = ad.emulated_format;
+ }
+
if (ad.creative_type === VIDEO) {
bid.width = associatedBidRequest.params.video.playerWidth;
bid.height = associatedBidRequest.params.video.playerHeight;
@@ -836,7 +847,7 @@ function renderBid(bid) {
height: bid.height,
vastUrl: bid.vastUrl,
placement: {
- attachTo: adUnitElement,
+ attachTo: `#${bid.adUnitCode}`,
align: config.align,
position: config.position
},
diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js
index 7a87f3a45bc..ad579f1cdbf 100644
--- a/modules/seedtagBidAdapter.js
+++ b/modules/seedtagBidAdapter.js
@@ -18,12 +18,6 @@ const BIDDER_CODE = 'seedtag';
const SEEDTAG_ALIAS = 'st';
const SEEDTAG_SSP_ENDPOINT = 'https://s.seedtag.com/c/hb/bid';
const SEEDTAG_SSP_ONTIMEOUT_ENDPOINT = 'https://s.seedtag.com/se/hb/timeout';
-const ALLOWED_DISPLAY_PLACEMENTS = [
- 'inScreen',
- 'inImage',
- 'inArticle',
- 'inBanner',
-];
// Global Vendor List Id
// https://iabeurope.eu/vendor-list-tcf-v2-0/
@@ -53,7 +47,7 @@ function getBidFloor(bidRequest) {
});
}
- return floorInfo.floor;
+ return floorInfo?.floor;
}
const getConnectionType = () => {
@@ -95,8 +89,7 @@ function hasMandatoryDisplayParams(bid) {
const p = bid.params;
return (
!!p.publisherId &&
- !!p.adUnitId &&
- ALLOWED_DISPLAY_PLACEMENTS.indexOf(p.placement) > -1
+ !!p.adUnitId
);
}
@@ -111,19 +104,7 @@ function hasMandatoryVideoParams(bid) {
isArray(videoParams.playerSize) &&
videoParams.playerSize.length > 0;
- switch (bid.params.placement) {
- // instream accept only video format
- case 'inStream':
- return isValid && videoParams.context === 'instream';
- // outstream accept banner/native/video format
- default:
- return (
- isValid &&
- videoParams.context === 'outstream' &&
- hasBannerMediaType(bid) &&
- hasMandatoryDisplayParams(bid)
- );
- }
+ return isValid
}
function buildBidRequest(validBidRequest) {
@@ -172,6 +153,10 @@ function getVideoParams(validBidRequest) {
return videoParams;
}
+function isVideoOutstream(validBidRequest) {
+ return getVideoParams(validBidRequest).context === 'outstream';
+}
+
function buildBidResponse(seedtagBid) {
const mediaType = mapMediaType(seedtagBid.mediaType);
const bid = {
@@ -286,9 +271,19 @@ export const spec = {
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid(bid) {
- return hasVideoMediaType(bid)
- ? hasMandatoryVideoParams(bid)
- : hasMandatoryDisplayParams(bid);
+ const hasVideo = hasVideoMediaType(bid);
+ const hasBanner = hasBannerMediaType(bid);
+
+ // when accept both mediatype but it must be outstream
+ if (hasVideo && hasBanner) {
+ return hasMandatoryVideoParams(bid) && isVideoOutstream(bid) && hasMandatoryDisplayParams(bid);
+ } else if (hasVideo) {
+ return hasMandatoryVideoParams(bid);
+ } else if (hasBanner) {
+ return hasMandatoryDisplayParams(bid);
+ } else {
+ return false;
+ }
},
/**
diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js
index 4096d0320e9..7144370dc9c 100644
--- a/modules/sharethroughBidAdapter.js
+++ b/modules/sharethroughBidAdapter.js
@@ -1,7 +1,7 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
-import { deepAccess, generateUUID, inIframe, logWarn, mergeDeep } from '../src/utils.js';
+import { deepAccess, generateUUID, inIframe, isPlainObject, logWarn, mergeDeep } from '../src/utils.js';
const VERSION = '4.3.0';
const BIDDER_CODE = 'sharethrough';
@@ -285,7 +285,7 @@ function getBidRequestFloor(bid) {
mediaType: bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner',
size: bid.sizes.map((size) => ({ w: size[0], h: size[1] })),
});
- if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) {
floor = parseFloat(floorInfo.floor);
}
}
diff --git a/modules/silverpushBidAdapter.js b/modules/silverpushBidAdapter.js
index 1d5662f88eb..70c0e475cc4 100644
--- a/modules/silverpushBidAdapter.js
+++ b/modules/silverpushBidAdapter.js
@@ -1,4 +1,3 @@
-import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import * as utils from '../src/utils.js';
import { mergeDeep } from '../src/utils.js';
@@ -6,6 +5,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
import { Renderer } from '../src/Renderer.js';
import { ajax } from '../src/ajax.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const BIDDER_CODE = 'silverpush';
const bidderConfig = 'sp_pb_ortb';
@@ -65,7 +65,7 @@ export const CONVERTER = ortbConverter({
imp = buildBannerImp(bidRequest, imp);
}
- const bidFloor = getBidFloor(bidRequest);
+ const bidFloor = getBidFloor(bidRequest, bidRequest.bidderRequest);
utils.deepSetValue(imp, 'bidfloor', bidFloor);
@@ -216,7 +216,8 @@ function createRequest(bidRequests, bidderRequest, mediaType) {
return {
method: 'POST',
url: REQUEST_URL,
- data: CONVERTER.toORTB({ bidRequests, bidderRequest, context: { mediaType } })
+ data: CONVERTER.toORTB({ bidRequests, bidderRequest, context: { mediaType } }),
+ bidderRequest
}
}
@@ -247,8 +248,8 @@ function buildVideoOutstreamResponse(bidResponse, context) {
return {...bidResponse};
}
-function getBidFloor(bid) {
- const currency = config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY;
+function getBidFloor(bid, bidderRequest) {
+ const currency = getCurrencyFromBidderRequest(bidderRequest) || DEFAULT_CURRENCY;
if (typeof bid.getFloor !== 'function') {
return utils.deepAccess(bid, 'params.bidFloor', 0.05);
@@ -259,7 +260,7 @@ function getBidFloor(bid) {
mediaType: '*',
size: '*',
});
- return bidFloor.floor;
+ return bidFloor?.floor;
}
function _renderer(bid) {
diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js
index e0ddf0a66e6..e3cdb3dc5bf 100644
--- a/modules/smartadserverBidAdapter.js
+++ b/modules/smartadserverBidAdapter.js
@@ -11,6 +11,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';
import { getBidFloor } from '../libraries/equativUtils/equativUtils.js'
import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -180,7 +181,7 @@ export const spec = {
*/
buildRequests: function (validBidRequests, bidderRequest) {
// use bidderRequest.bids[] to get bidder-dependent request info
- const adServerCurrency = config.getConfig('currency.adServerCurrency');
+ const adServerCurrency = getCurrencyFromBidderRequest(bidderRequest);
const sellerDefinedAudience = deepAccess(bidderRequest, 'ortb2.user.data', config.getAnyConfig('ortb2.user.data'));
const sellerDefinedContext = deepAccess(bidderRequest, 'ortb2.site.content.data', config.getAnyConfig('ortb2.site.content.data'));
diff --git a/modules/smarthubBidAdapter.js b/modules/smarthubBidAdapter.js
index 367011d68d5..c9cc737fac7 100644
--- a/modules/smarthubBidAdapter.js
+++ b/modules/smarthubBidAdapter.js
@@ -9,6 +9,7 @@ import {
const BIDDER_CODE = 'smarthub';
const ALIASES = [
+ {code: 'attekmi', skipPbsAliasing: true},
{code: 'markapp', skipPbsAliasing: true},
{code: 'jdpmedia', skipPbsAliasing: true},
{code: 'tredio', skipPbsAliasing: true},
@@ -16,12 +17,13 @@ const ALIASES = [
{code: 'vimayx', skipPbsAliasing: true},
];
const BASE_URLS = {
- smarthub: 'https://prebid.smart-hub.io/pbjs',
- markapp: 'https://markapp-prebid.smart-hub.io/pbjs',
- jdpmedia: 'https://jdpmedia-prebid.smart-hub.io/pbjs',
- tredio: 'https://tredio-prebid.smart-hub.io/pbjs',
- felixads: 'https://felixads-prebid.smart-hub.io/pbjs',
- vimayx: 'https://vimayx-prebid.smart-hub.io/pbjs',
+ attekmi: 'https://prebid.attekmi.com/pbjs',
+ smarthub: 'https://prebid.attekmi.com/pbjs',
+ markapp: 'https://markapp-prebid.attekmi.com/pbjs',
+ jdpmedia: 'https://jdpmedia-prebid.attekmi.com/pbjs',
+ tredio: 'https://tredio-prebid.attekmi.com/pbjs',
+ felixads: 'https://felixads-prebid.attekmi.com/pbjs',
+ vimayx: 'https://vimayx-prebid.attekmi.com/pbjs',
};
const _getUrl = (partnerName) => {
diff --git a/modules/smarthubBidAdapter.md b/modules/smarthubBidAdapter.md
index c09855303e2..dbdae927097 100644
--- a/modules/smarthubBidAdapter.md
+++ b/modules/smarthubBidAdapter.md
@@ -1,16 +1,16 @@
# Overview
```
-Module Name: SmartHub Bidder Adapter
-Module Type: SmartHub Bidder Adapter
-Maintainer: support@smart-hub.io
+Module Name: Attekmi Bidder Adapter
+Module Type: Attekmi Bidder Adapter
+Maintainer: prebid@attekmi.com
```
# Description
-Connects to SmartHub exchange for bids.
+Connects to Attekmi exchange for bids.
-SmartHub bid adapter supports Banner, Video (instream and outstream) and Native.
+Attekmi bid adapter supports Banner, Video (instream and outstream) and Native.
# Test Parameters
```
diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js
index 483a7a86d73..1442199cd6d 100644
--- a/modules/smartxBidAdapter.js
+++ b/modules/smartxBidAdapter.js
@@ -422,6 +422,12 @@ function createOutstreamConfig(bid) {
var playerListener = function callback(event) {
switch (event) {
+ case 'AdError':
+ try {
+ window.sc_smartIntxtError();
+ } catch (f) {}
+ break;
+
case 'AdSlotStarted':
try {
window.sc_smartIntxtStart();
diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js
index 7f83dc025cb..a78c60f8308 100644
--- a/modules/smilewantedBidAdapter.js
+++ b/modules/smilewantedBidAdapter.js
@@ -1,11 +1,11 @@
import {deepAccess, deepClone, isArray, isFn, isPlainObject, logError, logWarn} from '../src/utils.js';
import {Renderer} from '../src/Renderer.js';
-import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {INSTREAM, OUTSTREAM} from '../src/video.js';
import {serializeSupplyChain} from '../libraries/schainSerializer/schainSerializer.js'
import {convertOrtbRequestToProprietaryNative, toOrtbNativeRequest, toLegacyResponse} from '../src/native.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const BIDDER_CODE = 'smilewanted';
@@ -66,7 +66,7 @@ export const spec = {
return validBidRequests.map(bid => {
const payload = {
zoneId: bid.params.zoneId,
- currencyCode: config.getConfig('currency.adServerCurrency') || 'EUR',
+ currencyCode: getCurrencyFromBidderRequest(bidderRequest) || 'EUR',
tagId: bid.adUnitCode,
sizes: bid.sizes.map(size => ({
w: size[0],
diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js
index 4e0de53ca0d..13731e2b7e1 100644
--- a/modules/snigelBidAdapter.js
+++ b/modules/snigelBidAdapter.js
@@ -16,6 +16,7 @@ const SESSION_ID_KEY = '_sn_session_pba';
const getConfig = config.getConfig;
const storageManager = getStorageManager({bidderCode: BIDDER_CODE});
const refreshes = {};
+const placementCounters = {};
const pageViewId = generateUUID();
const pageViewStart = new Date().getTime();
let auctionCounter = 0;
@@ -46,6 +47,7 @@ export const spec = {
cur: getCurrencies(),
test: getTestFlag(),
version: 'v' + '$prebid.version$',
+ adapterVersion: '2.0',
gpp: deepAccess(bidderRequest, 'gppConsent.gppString') || deepAccess(bidderRequest, 'ortb2.regs.gpp'),
gpp_sid:
deepAccess(bidderRequest, 'gppConsent.applicableSections') || deepAccess(bidderRequest, 'ortb2.regs.gpp_sid'),
@@ -71,6 +73,7 @@ export const spec = {
gpid: deepAccess(r, 'ortb2Imp.ext.gpid'),
pbadslot: deepAccess(r, 'ortb2Imp.ext.data.pbadslot') || deepAccess(r, 'ortb2Imp.ext.gpid'),
name: r.params.placement,
+ counter: getPlacementCounter(r.params.placement),
sizes: r.sizes,
floor: getPriceFloor(r, BANNER, FLOOR_MATCH_ALL_SIZES),
refresh: getRefreshInformation(r.adUnitCode),
@@ -182,6 +185,17 @@ function getRefreshInformation(adUnitCode) {
};
}
+function getPlacementCounter(placement) {
+ const counter = placementCounters[placement];
+ if (counter === undefined) {
+ placementCounters[placement] = 0;
+ return 0;
+ }
+
+ placementCounters[placement]++;
+ return placementCounters[placement];
+}
+
function mapIdToRequestId(id, bidRequest) {
return bidRequest.bidderRequest.bids.filter((bid) => bid.adUnitCode === id)[0].bidId;
}
diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js
index 5478f9aa6e5..d4e53c00472 100644
--- a/modules/sovrnBidAdapter.js
+++ b/modules/sovrnBidAdapter.js
@@ -373,7 +373,7 @@ function _getBidFloors(bid) {
mediaType: bid.mediaTypes && bid.mediaTypes.banner ? 'banner' : 'video',
size: '*'
}) : {}
- const floorModuleValue = parseFloat(floorInfo.floor)
+ const floorModuleValue = parseFloat(floorInfo?.floor)
if (!isNaN(floorModuleValue)) {
return floorModuleValue
}
diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js
index 5649afd3946..2bb08707c85 100644
--- a/modules/sparteoBidAdapter.js
+++ b/modules/sparteoBidAdapter.js
@@ -24,12 +24,14 @@ const converter = ortbConverter({
request(buildRequest, imps, bidderRequest, context) {
const request = buildRequest(imps, bidderRequest, context);
+ deepSetValue(request, 'site.publisher.ext.params.pbjsVersion', '$prebid.version$');
+
if (bidderRequest.bids[0].params.networkId) {
- deepSetValue(request, 'site.publisher.ext.params.networkId', bidderRequest.bids[0].params.networkId);
+ request.site.publisher.ext.params.networkId = bidderRequest.bids[0].params.networkId;
}
if (bidderRequest.bids[0].params.publisherId) {
- deepSetValue(request, 'site.publisher.ext.params.publisherId', bidderRequest.bids[0].params.publisherId);
+ request.site.publisher.ext.params.publisherId = bidderRequest.bids[0].params.publisherId;
}
return request;
diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js
index e05c9db6303..03a9f5a1da9 100644
--- a/modules/sspBCBidAdapter.js
+++ b/modules/sspBCBidAdapter.js
@@ -1,18 +1,19 @@
import { deepAccess, getWindowTop, isArray, logInfo, logWarn } from '../src/utils.js';
import { ajax } from '../src/ajax.js';
-import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { includes as strIncludes } from '../src/polyfill.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const BIDDER_CODE = 'sspBC';
const BIDDER_URL = 'https://ssp.wp.pl/bidder/';
-const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync';
+const SYNC_URL_IFRAME = 'https://ssp.wp.pl/bidder/usersync';
+const SYNC_URL_IMAGE = 'https://ssp.wp.pl/v1/sync/pixel';
const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify';
const GVLID = 676;
const TMAX = 450;
-const BIDDER_VERSION = '6.00';
+const BIDDER_VERSION = '6.10';
const DEFAULT_CURRENCY = 'PLN';
const W = window;
const { navigator } = W;
@@ -70,7 +71,20 @@ const getContentLanguage = () => {
const topWindow = getWindowTop();
return topWindow.document.body.parentNode.lang;
} catch (err) {
- logWarn('Could not read language form top-level html', err);
+ logWarn('Could not read language from top-level html', err);
+ }
+};
+
+/**
+ * Get host name of the top level html object
+ * @returns {string} host name
+ */
+const getTopHost = () => {
+ try {
+ const topWindow = getWindowTop();
+ return topWindow.location.host;
+ } catch (err) {
+ logWarn('Could not read host from top-level window', err);
}
};
@@ -261,18 +275,18 @@ const getHighestFloor = (slot) => {
mediaType: 'banner',
size: next,
currency
- });
+ }) || {};
return prev > currentFloor ? prev : currentFloor;
}, 0);
}
const { floor: nativeFloor = 0 } = slot.getFloor({
mediaType: 'native', currency
- });
+ }) || {};
const { floor: videoFloor = 0 } = slot.getFloor({
mediaType: 'video', currency
- });
+ }) || {};
result.floor = Math.max(bannerFloor, nativeFloor, videoFloor);
}
@@ -284,7 +298,7 @@ const getHighestFloor = (slot) => {
* Get currency (either default or adserver)
* @returns {string} currency name
*/
-const getCurrency = () => config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY;
+const getCurrency = (bidderRequest) => getCurrencyFromBidderRequest(bidderRequest) || DEFAULT_CURRENCY;
/**
* Get value for first occurence of key within the collection
@@ -604,7 +618,9 @@ const spec = {
const pbver = '$prebid.version$';
const testMode = setOnAny(validBidRequests, 'params.test') ? 1 : undefined;
const ref = bidderRequest.refererInfo.ref;
- const { regs = {} } = ortb2 || {};
+ const { source = {}, regs = {} } = ortb2 || {};
+
+ source.schain = setOnAny(validBidRequests, 'schain');
const payload = {
id: bidderRequest.bidderRequestId,
@@ -617,10 +633,11 @@ const spec = {
content: { language: getContentLanguage() },
},
imp: validBidRequests.map(slot => mapImpression(slot)),
- cur: [getCurrency()],
+ cur: [getCurrency(bidderRequest)],
tmax,
user: {},
regs,
+ source,
device: {
language: getBrowserLanguage(),
w: screen.width,
@@ -764,14 +781,22 @@ const spec = {
return fledgeAuctionConfigs.length ? { bids, fledgeAuctionConfigs } : bids;
},
- getUserSyncs(syncOptions) {
+
+ getUserSyncs(syncOptions, _, gdprConsent = {}) {
+ const {iframeEnabled, pixelEnabled} = syncOptions;
+ const {gdprApplies, consentString = ''} = gdprConsent;
let mySyncs = [];
- if (syncOptions.iframeEnabled) {
+ if (iframeEnabled) {
mySyncs.push({
type: 'iframe',
- url: `${SYNC_URL}?tcf=2&pvid=${pageView.id}&sn=${pageView.sn}`,
+ url: `${SYNC_URL_IFRAME}?tcf=2&pvid=${pageView.id}&sn=${pageView.sn}`,
});
- };
+ } else if (pixelEnabled) {
+ mySyncs.push({
+ type: 'image',
+ url: `${SYNC_URL_IMAGE}?inver=0&platform=wpartner&host=${getTopHost() || ''}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}`,
+ });
+ }
return mySyncs;
},
diff --git a/modules/ssp_genieeBidAdapter.js b/modules/ssp_genieeBidAdapter.js
index dc4cd13d4a1..ed15842f96b 100644
--- a/modules/ssp_genieeBidAdapter.js
+++ b/modules/ssp_genieeBidAdapter.js
@@ -213,7 +213,7 @@ function makeCommonRequestData(bid, geparameter, refererInfo) {
}
// imuid
- const imuidQuery = getImuidAsQueryParameter();
+ const imuidQuery = getImuidAsQueryParameter(bid);
if (imuidQuery) data.extuid = imuidQuery;
// makeUAQuery
@@ -308,21 +308,11 @@ function makeBidResponseAd(innerHTML) {
return '' + innerHTML + '';
}
-/**
- * add imuid script tag
- */
-function appendImuidScript() {
- const scriptEl = document.createElement('script');
- scriptEl.src = '//dmp.im-apps.net/scripts/im-uid-hook.js?cid=3929';
- scriptEl.async = true;
- document.body.appendChild(scriptEl);
-}
-
/**
* return imuid strings as query parameters
*/
-function getImuidAsQueryParameter() {
- const imuid = storage.getCookie('_im_uid.3929');
+function getImuidAsQueryParameter(bid) {
+ const imuid = utils.deepAccess(bid, 'userId.imuid');
return imuid ? 'im:' + imuid : ''; // To avoid double encoding, not using encodeURIComponent here
}
@@ -404,8 +394,6 @@ export const spec = {
return bidResponses;
}
- appendImuidScript();
-
const zoneId = bidderRequest.bid.params.zoneId;
let successBid;
successBid = serverResponse.body || {};
diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js
index 072b72a6724..5a492a8e5e0 100644
--- a/modules/stroeerCoreBidAdapter.js
+++ b/modules/stroeerCoreBidAdapter.js
@@ -75,7 +75,7 @@ export const spec = {
};
}
- const ORTB2_KEYS = ['regs.ext.dsa', 'device.ext.cdep'];
+ const ORTB2_KEYS = ['regs.ext.dsa', 'device.ext.cdep', 'site.ext'];
ORTB2_KEYS.forEach(key => {
const value = deepAccess(bidderRequest.ortb2, key);
if (value !== undefined) {
@@ -116,7 +116,8 @@ export const spec = {
creativeId: '',
meta: {
advertiserDomains: bidResponse.adomain,
- dsa: bidResponse.dsa
+ dsa: bidResponse.dsa,
+ campaignType: bidResponse.campaignType,
},
mediaType,
};
@@ -252,14 +253,14 @@ const createFloorPriceObject = (mediaType, sizes, bidRequest) => {
currency: 'EUR',
mediaType: mediaType,
size: '*'
- });
+ }) || {};
const sizeFloors = sizes.map(size => {
const floor = bidRequest.getFloor({
currency: 'EUR',
mediaType: mediaType,
size: [size[0], size[1]]
- });
+ }) || {};
return {...floor, size};
});
diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js
index a29265ce9cd..eaae877ba0e 100644
--- a/modules/sublimeBidAdapter.js
+++ b/modules/sublimeBidAdapter.js
@@ -1,6 +1,7 @@
import { logInfo, generateUUID, formatQS, triggerPixel, deepAccess } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -152,7 +153,7 @@ function buildRequests(validBidRequests, bidderRequest) {
pbav: SUBLIME_VERSION,
// Current Prebid params
prebidVersion: '$prebid.version$',
- currencyCode: config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY,
+ currencyCode: getCurrencyFromBidderRequest(bidderRequest) || DEFAULT_CURRENCY,
timeout: (typeof bidderRequest === 'object' && !!bidderRequest) ? bidderRequest.timeout : config.getConfig('bidderTimeout'),
};
diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js
index 5fa7f2c8b8e..7d1c58bcdcf 100644
--- a/modules/taboolaBidAdapter.js
+++ b/modules/taboolaBidAdapter.js
@@ -3,7 +3,7 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {config} from '../src/config.js';
-import {deepAccess, deepSetValue, getWindowSelf, replaceAuctionPrice, isArray, safeJSONParse} from '../src/utils.js';
+import {deepSetValue, getWindowSelf, replaceAuctionPrice, isArray, safeJSONParse, isPlainObject} from '../src/utils.js';
import {getStorageManager} from '../src/storageManager.js';
import {ajax} from '../src/ajax.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js';
@@ -338,7 +338,7 @@ function fillTaboolaImpData(bid, imp) {
currency: CURRENCY,
size: '*'
});
- if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
imp.bidfloor = parseFloat(floorInfo.floor);
imp.bidfloorcur = CURRENCY;
}
@@ -347,7 +347,7 @@ function fillTaboolaImpData(bid, imp) {
imp.bidfloor = bidfloor;
imp.bidfloorcur = bidfloorcur;
}
- deepSetValue(imp, 'ext.gpid', deepAccess(bid, 'ortb2Imp.ext.gpid'));
+ deepSetValue(imp, 'ext.gpid', bid?.ortb2Imp?.ext?.gpid);
}
function getBanners(bid, pos) {
diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js
index 28965cf8ad0..cab3d4f35c2 100644
--- a/modules/tappxBidAdapter.js
+++ b/modules/tappxBidAdapter.js
@@ -369,7 +369,7 @@ function buildOneRequest(validBidRequests, bidderRequest) {
imp.id = validBidRequests.bidId;
imp.tagid = tagid;
- imp.secure = 1;
+ imp.secure = validBidRequests.ortb2Imp?.secure ?? 1;
imp.bidfloor = deepAccess(validBidRequests, 'params.bidfloor');
if (isFn(validBidRequests.getFloor)) {
diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js
index fd5d79d08b7..b01e3dddab3 100644
--- a/modules/targetVideoBidAdapter.js
+++ b/modules/targetVideoBidAdapter.js
@@ -1,7 +1,7 @@
import {_each, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {formatRequest, getRtbBid, getSiteObj, videoBid, bannerBid, createVideoTag} from '../libraries/targetVideoUtils/bidderUtils.js';
+import {formatRequest, getRtbBid, getSiteObj, getSyncResponse, videoBid, bannerBid, createVideoTag} from '../libraries/targetVideoUtils/bidderUtils.js';
import {SOURCE, GVLID, BIDDER_CODE, VIDEO_PARAMS, BANNER_ENDPOINT_URL, VIDEO_ENDPOINT_URL, MARGIN, TIME_TO_LIVE} from '../libraries/targetVideoUtils/constants.js';
/**
@@ -167,14 +167,26 @@ export const spec = {
_each(resp.bid, (bid) => {
const requestId = bidRequest.bidId;
const params = bidRequest.params;
-
- bids.push(videoBid(bid, requestId, currency, params, TIME_TO_LIVE));
+ const vBid = videoBid(bid, requestId, currency, params, TIME_TO_LIVE);
+ if (bids.length == 0 || bids[0].cpm < vBid.cpm) {
+ bids[0] = vBid;
+ }
});
});
}
return bids;
+ },
+
+ /**
+ * Determine the user sync type (either 'iframe' or 'image') based on syncOptions.
+ * Construct the sync URL by appending required query parameters such as gdpr, ccpa, and coppa consents.
+ * Return an array containing an object with the sync type and the constructed URL.
+ */
+ getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) => {
+ return getSyncResponse(syncOptions, gdprConsent, uspConsent, gppConsent, 'targetvideo');
}
+
}
registerBidder(spec);
diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js
index d7b9ef9be2b..cbf7a989e91 100644
--- a/modules/teadsBidAdapter.js
+++ b/modules/teadsBidAdapter.js
@@ -1,4 +1,4 @@
-import {getValue, logError, deepAccess, parseSizesInput, isArray, getBidIdParameter} from '../src/utils.js';
+import {logError, deepAccess, parseSizesInput, isArray, getBidIdParameter} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {getStorageManager} from '../src/storageManager.js';
import {isAutoplayEnabled} from '../libraries/autoplayDetection/autoplay.js';
@@ -34,8 +34,8 @@ export const spec = {
isBidRequestValid: function(bid) {
let isValid = false;
if (typeof bid.params !== 'undefined') {
- let isValidPlacementId = _validateId(getValue(bid.params, 'placementId'));
- let isValidPageId = _validateId(getValue(bid.params, 'pageId'));
+ let isValidPlacementId = _validateId(bid.params.placementId);
+ let isValidPageId = _validateId(bid.params.pageId);
isValid = isValidPlacementId && isValidPageId;
}
@@ -113,12 +113,12 @@ export const spec = {
payload.us_privacy = bidderRequest.uspConsent;
}
- const userAgentClientHints = deepAccess(firstBidRequest, 'ortb2.device.sua');
+ const userAgentClientHints = firstBidRequest?.ortb2?.device?.sua;
if (userAgentClientHints) {
payload.userAgentClientHints = userAgentClientHints;
}
- const dsa = deepAccess(bidderRequest, 'ortb2.regs.ext.dsa');
+ const dsa = bidderRequest?.ortb2?.regs?.ext?.dsa;
if (dsa) {
payload.dsa = dsa;
}
@@ -287,10 +287,10 @@ function findGdprStatus(gdprApplies, gdprData) {
function buildRequestObject(bid) {
const reqObj = {};
- let placementId = getValue(bid.params, 'placementId');
- let pageId = getValue(bid.params, 'pageId');
- const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid');
- const videoPlcmt = deepAccess(bid, 'mediaTypes.video.plcmt');
+ let placementId = bid.params.placementId;
+ let pageId = bid.params.pageId;
+ const gpid = bid?.ortb2Imp?.ext?.gpid;
+ const videoPlcmt = bid?.mediaTypes?.video?.plcmt;
reqObj.sizes = getSizes(bid);
reqObj.bidId = getBidIdParameter('bidId', bid);
@@ -309,9 +309,9 @@ function getSizes(bid) {
}
function concatSizes(bid) {
- let playerSize = deepAccess(bid, 'mediaTypes.video.playerSize');
- let videoSizes = deepAccess(bid, 'mediaTypes.video.sizes');
- let bannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes');
+ let playerSize = bid?.mediaTypes?.video?.playerSize;
+ let videoSizes = bid?.mediaTypes?.video?.sizes;
+ let bannerSizes = bid?.mediaTypes?.banner?.sizes;
if (isArray(bannerSizes) || isArray(playerSize) || isArray(videoSizes)) {
let mediaTypesSizes = [bannerSizes, videoSizes, playerSize];
@@ -343,7 +343,7 @@ function _validateId(id) {
* @returns `{} | {firstPartyCookieTeadsId: string}`
*/
function getFirstPartyTeadsIdParameter(validBidRequests) {
- const firstPartyTeadsIdFromUserIdModule = deepAccess(validBidRequests, '0.userId.teadsId');
+ const firstPartyTeadsIdFromUserIdModule = validBidRequests?.[0]?.userId?.teadsId;
if (firstPartyTeadsIdFromUserIdModule) {
return {firstPartyCookieTeadsId: firstPartyTeadsIdFromUserIdModule};
diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js
index a665de6140f..0c8bd330e11 100644
--- a/modules/tripleliftBidAdapter.js
+++ b/modules/tripleliftBidAdapter.js
@@ -255,7 +255,7 @@ function _getFloor (bid) {
mediaType: _isVideoBidRequest(bid) ? 'video' : 'banner',
size: '*'
});
- if (typeof floorInfo === 'object' &&
+ if (utils.isPlainObject(floorInfo) &&
floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) {
floor = parseFloat(floorInfo.floor);
}
diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js
index 19b933a8666..d5017db0705 100644
--- a/modules/ucfunnelBidAdapter.js
+++ b/modules/ucfunnelBidAdapter.js
@@ -235,7 +235,7 @@ function getFloor(bid, size, mediaTypes) {
mediaType: getMediaType(mediaTypes),
size: (size) ? [ size[0], size[1] ] : '*',
});
- if (bidFloor.currency === CURRENCY) {
+ if (bidFloor?.currency === CURRENCY) {
return bidFloor.floor;
}
}
diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js
index 39d77c81b57..53e6629d8cc 100644
--- a/modules/unrulyBidAdapter.js
+++ b/modules/unrulyBidAdapter.js
@@ -31,7 +31,7 @@ const addBidFloorInfo = (validBid) => {
currency: 'USD',
mediaType: key,
size: '*'
- }).floor || 0;
+ })?.floor || 0;
} else {
floor = validBid.params.floor || 0;
}
diff --git a/modules/userId/index.js b/modules/userId/index.js
index d0299427603..69988e00f3b 100644
--- a/modules/userId/index.js
+++ b/modules/userId/index.js
@@ -131,7 +131,6 @@ import {
STORAGE_TYPE_LOCALSTORAGE
} from '../../src/storageManager.js';
import {
- deepAccess,
deepSetValue,
delayExecution,
isArray,
@@ -165,9 +164,6 @@ export const dep = {
isAllowed: isActivityAllowed
}
-/** @type {boolean} */
-let addedUserIdHook = false;
-
/** @type {SubmoduleContainer[]} */
let submodules = [];
@@ -662,7 +658,7 @@ let initIdSystem;
function getPPID(eids = getUserIdsAsEids() || []) {
// userSync.ppid should be one of the 'source' values in getUserIdsAsEids() eg pubcid.org or id5-sync.com
const matchingUserId = ppidSource && eids.find(userID => userID.source === ppidSource);
- if (matchingUserId && typeof deepAccess(matchingUserId, 'uids.0.id') === 'string') {
+ if (matchingUserId && typeof matchingUserId?.uids?.[0]?.id === 'string') {
const ppidValue = matchingUserId.uids[0].id.replace(/[\W_]/g, '');
if (ppidValue.length >= 32 && ppidValue.length <= 150) {
return ppidValue;
@@ -693,6 +689,25 @@ export const startAuctionHook = timedAuctionHook('userId', function requestBidsH
});
});
+/**
+ * Append user id data from config to bids to be accessed in adapters when there are no submodules.
+ * @param {function} fn required; The next function in the chain, used by hook.js
+ * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
+ */
+export const addUserIdsHook = timedAuctionHook('userId', function requestBidsHook(fn, reqBidsConfigObj) {
+ addIdData(reqBidsConfigObj);
+ // calling fn allows prebid to continue processing
+ fn.call(this, reqBidsConfigObj);
+});
+
+/**
+ * Is startAuctionHook added
+ * @returns {boolean}
+ */
+function addedStartAuctionHook() {
+ return !!startAuction.getHooks({hook: startAuctionHook}).length;
+}
+
/**
* This function will be exposed in global-name-space so that userIds stored by Prebid UserId module can be used by external codes as well.
* Simple use case will be passing these UserIds to A9 wrapper solution
@@ -1110,11 +1125,11 @@ function updateSubmodules() {
.forEach((sm) => submodules.push(sm));
if (submodules.length) {
- if (!addedUserIdHook) {
+ if (!addedStartAuctionHook()) {
+ startAuction.getHooks({hook: addUserIdsHook}).remove();
startAuction.before(startAuctionHook, 100) // use higher priority than dataController / rtd
adapterManager.callDataDeletionRequest.before(requestDataDeletion);
coreGetPPID.after((next) => next(getPPID()));
- addedUserIdHook = true;
}
logInfo(`${MODULE_NAME} - usersync config updated for ${submodules.length} submodules: `, submodules.map(a => a.submodule.name));
}
@@ -1221,6 +1236,10 @@ export function init(config, {delay = GreedyPromise.timeout} = {}) {
(getGlobal()).refreshUserIds = normalizePromise(refreshUserIds);
(getGlobal()).getUserIdsAsync = normalizePromise(getUserIdsAsync);
(getGlobal()).getUserIdsAsEidBySource = getUserIdsAsEidBySource;
+ if (!addedStartAuctionHook()) {
+ // Add ortb2.user.ext.eids even if 0 submodules are added
+ startAuction.before(addUserIdsHook, 100); // use higher priority than dataController / rtd
+ }
}
// init config update listener to start the application
diff --git a/modules/utiqIdSystem.js b/modules/utiqIdSystem.js
index d6b8aae44a3..b5da87627a5 100644
--- a/modules/utiqIdSystem.js
+++ b/modules/utiqIdSystem.js
@@ -28,6 +28,17 @@ function getUtiqFromStorage() {
storage.getDataFromLocalStorage('utiqPass')
);
+ const netIdAdtechpass = storage.getDataFromLocalStorage('netid_utiq_adtechpass');
+
+ if (netIdAdtechpass) {
+ logInfo(
+ `${LOG_PREFIX}: Local storage netid_utiq_adtechpass: ${netIdAdtechpass}`
+ );
+ return {
+ utiq: netIdAdtechpass,
+ }
+ }
+
if (
utiqPassStorage &&
utiqPassStorage.connectId &&
diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js
index f375e161f88..8fda9cba593 100644
--- a/modules/vdoaiBidAdapter.js
+++ b/modules/vdoaiBidAdapter.js
@@ -1,6 +1,8 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
+import { deepClone, logError, deepAccess } from '../src/utils.js';
+import { config } from '../src/config.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -9,7 +11,86 @@ import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
*/
const BIDDER_CODE = 'vdoai';
-const ENDPOINT_URL = 'https://prebid.vdo.ai/auction';
+const ENDPOINT_URL = 'https://prebid-v2.vdo.ai/auction';
+
+function getFrameNesting() {
+ let topmostFrame = window;
+ let parent = window.parent;
+ try {
+ while (topmostFrame !== topmostFrame.parent) {
+ parent = topmostFrame.parent;
+ // eslint-disable-next-line no-unused-expressions
+ parent.location.href;
+ topmostFrame = topmostFrame.parent;
+ }
+ } catch (e) { }
+ return topmostFrame;
+}
+
+/**
+ * Returns information about the page needed by the server in an object to be converted in JSON
+ *
+ * @returns {{location: *, referrer: (*|string), stack: (*|Array.), numIframes: (*|Number), wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}}
+ */
+function getPageInfo(bidderRequest) {
+ const topmostFrame = getFrameNesting();
+ return {
+ referrer: deepAccess(bidderRequest, 'refererInfo.ref', null),
+ stack: deepAccess(bidderRequest, 'refererInfo.stack', []),
+ numIframes: deepAccess(bidderRequest, 'refererInfo.numIframes', 0),
+ wWidth: topmostFrame.innerWidth,
+ location: deepAccess(bidderRequest, 'refererInfo.page', null),
+ wHeight: topmostFrame.innerHeight,
+ aWidth: topmostFrame.screen.availWidth,
+ aHeight: topmostFrame.screen.availHeight,
+ oWidth: topmostFrame.outerWidth,
+ oHeight: topmostFrame.outerHeight,
+ sWidth: topmostFrame.screen.width,
+ sHeight: topmostFrame.screen.height,
+ sLeft: 'screenLeft' in topmostFrame ? topmostFrame.screenLeft : topmostFrame.screenX,
+ sTop: 'screenTop' in topmostFrame ? topmostFrame.screenTop : topmostFrame.screenY,
+ xOffset: topmostFrame.pageXOffset,
+ docHeight: topmostFrame.document.body ? topmostFrame.document.body.scrollHeight : null,
+ hLength: history.length,
+ yOffset: topmostFrame.pageYOffset,
+ version: {
+ prebid_version: '$prebid.version$',
+ adapter_version: '1.0.0',
+ vendor: '$$PREBID_GLOBAL$$',
+ }
+ };
+}
+
+export function isSchainValid(schain) {
+ let isValid = false;
+ const requiredFields = ['asi', 'sid', 'hp'];
+ if (!schain || !schain.nodes) return isValid;
+ isValid = schain.nodes.reduce((status, node) => {
+ if (!status) return status;
+ return requiredFields.every(field => node.hasOwnProperty(field));
+ }, true);
+ if (!isValid) {
+ logError('VDO.AI: required schain params missing');
+ }
+ return isValid;
+}
+
+function parseVideoSize(bid) {
+ const playerSize = bid.mediaTypes.video.playerSize;
+ if (typeof playerSize !== 'undefined' && Array.isArray(playerSize) && playerSize.length > 0) {
+ return getSizes(playerSize)
+ }
+ return [];
+}
+
+function getSizes(sizes) {
+ const ret = [];
+ for (let i = 0; i < sizes.length; i++) {
+ const size = sizes[i];
+ ret.push({ width: size[0], height: size[1] })
+ }
+ return ret;
+}
export const spec = {
code: BIDDER_CODE,
@@ -21,7 +102,7 @@ export const spec = {
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function (bid) {
- return !!(bid.params.placementId);
+ return !!(bid.params.placementId) && typeof bid.params.placementId === 'string';
},
/**
@@ -31,23 +112,82 @@ export const spec = {
* @param validBidRequests
* @param bidderRequest
*/
+
buildRequests: function (validBidRequests, bidderRequest) {
if (validBidRequests.length === 0) {
return [];
}
-
return validBidRequests.map(bidRequest => {
const sizes = getAdUnitSizes(bidRequest);
- const payload = {
+ let payload = {
placementId: bidRequest.params.placementId,
sizes: sizes,
bidId: bidRequest.bidId,
- // TODO: is 'page' the right value here?
- referer: bidderRequest.refererInfo.page,
- // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781
- id: bidRequest.auctionId,
- mediaType: bidRequest.mediaTypes.video ? 'video' : 'banner'
+ mediaType: bidRequest.mediaTypes.video ? 'video' : 'banner',
+ domain: bidderRequest.ortb2.site.domain,
+ publisherDomain: bidderRequest.ortb2.site.publisher.domain,
+ adUnitCode: bidRequest.adUnitCode,
+ bidder: bidRequest.bidder,
+ tmax: bidderRequest.timeout
};
+
+ payload.bidderRequestId = bidRequest.bidderRequestId;
+ payload.auctionId = deepAccess(bidRequest, 'ortb2.source.tid');
+ payload.transactionId = deepAccess(bidRequest, 'ortb2Imp.ext.tid');
+ payload.gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot');
+ payload.ortb2Imp = deepAccess(bidRequest, 'ortb2Imp');
+
+ if (payload.mediaType === 'video') {
+ payload.context = bidRequest.mediaTypes.video.context;
+ payload.playerSize = parseVideoSize(bidRequest);
+ payload.mediaTypeInfo = deepClone(bidRequest.mediaTypes.video);
+ }
+
+ if (typeof bidRequest.getFloor === 'function') {
+ let floor = bidRequest.getFloor({
+ currency: 'USD',
+ mediaType: '*',
+ size: '*'
+ });
+ if (floor && floor.floor && floor.currency === 'USD') {
+ payload.bidFloor = floor.floor;
+ }
+ } else if (bidRequest.params.bidFloor) {
+ payload.bidFloor = bidRequest.params.bidFloor;
+ }
+
+ payload.pageInfo = getPageInfo(bidderRequest);
+
+ if (bidderRequest && bidderRequest.gdprConsent) {
+ payload.gdprConsent = {
+ consentRequired: bidderRequest.gdprConsent.gdprApplies,
+ consentString: bidderRequest.gdprConsent.consentString,
+ addtlConsent: bidderRequest.gdprConsent.addtlConsent
+ };
+ }
+ if (bidderRequest && bidderRequest.gppConsent) {
+ payload.gppConsent = {
+ applicableSections: bidderRequest.gppConsent.applicableSections,
+ consentString: bidderRequest.gppConsent.gppString,
+ }
+ }
+ if (bidderRequest && bidderRequest.ortb2) {
+ payload.ortb2 = bidderRequest.ortb2;
+ }
+ if (bidderRequest && bidderRequest.uspConsent) {
+ payload.usPrivacy = bidderRequest.uspConsent;
+ }
+ if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].schain && isSchainValid(validBidRequests[0].schain)) {
+ payload.schain = validBidRequests[0].schain;
+ }
+ if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userIdAsEids) {
+ payload.userId = validBidRequests[0].userIdAsEids;
+ }
+ let coppaOrtb2 = !!deepAccess(bidderRequest, 'ortb2.regs.coppa');
+ let coppaConfig = config.getConfig('coppa');
+ if (coppaOrtb2 === true || coppaConfig === true) {
+ payload.coppa = true;
+ }
return {
method: 'POST',
url: ENDPOINT_URL,
@@ -67,35 +207,24 @@ export const spec = {
const bidResponses = [];
const response = serverResponse.body;
const creativeId = response.adid || 0;
- // const width = response.w || 0;
- const width = response.width;
- // const height = response.h || 0;
- const height = response.height;
+ const width = response.w;
+ const height = response.h;
const cpm = response.price || 0;
- response.rWidth = width;
- response.rHeight = height;
-
const adCreative = response.vdoCreative;
if (width !== 0 && height !== 0 && cpm !== 0 && creativeId !== 0) {
- // const dealId = response.dealid || '';
const currency = response.cur || 'USD';
const netRevenue = true;
- // const referrer = bidRequest.data.referer;
const bidResponse = {
requestId: response.bidId,
cpm: cpm,
width: width,
height: height,
creativeId: creativeId,
- // dealId: dealId,
currency: currency,
netRevenue: netRevenue,
ttl: 60,
- // referrer: referrer,
- // ad: response.adm
- // ad: adCreative,
mediaType: response.mediaType
};
@@ -104,9 +233,9 @@ export const spec = {
} else {
bidResponse.ad = adCreative;
}
- if (response.adDomain) {
+ if (response.adomain) {
bidResponse.meta = {
- advertiserDomains: response.adDomain
+ advertiserDomains: response.adomain
};
}
bidResponses.push(bidResponse);
@@ -130,7 +259,7 @@ export const spec = {
return [];
},
- onTImeout: function(data) {},
+ onTimeout: function(data) {},
onBidWon: function(bid) {},
onSetTargeting: function(bid) {}
};
diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js
index b62474d0c25..c34d3ecb097 100644
--- a/modules/videobyteBidAdapter.js
+++ b/modules/videobyteBidAdapter.js
@@ -214,8 +214,8 @@ function buildRequestData(bidRequest, bidderRequest) {
id: '1',
video: video,
secure: isSecure() ? 1 : 0,
- bidfloor: floorData.floor,
- bidfloorcur: floorData.currency
+ bidfloor: floorData?.floor,
+ bidfloorcur: floorData?.currency
}
],
site: {
diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js
index ee2c2deef8b..4adabacc33b 100644
--- a/modules/videoheroesBidAdapter.js
+++ b/modules/videoheroesBidAdapter.js
@@ -1,8 +1,8 @@
-import { isEmpty, parseUrl, isStr, triggerPixel } from '../src/utils.js';
+import { triggerPixel, isStr } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
-import { config } from '../src/config.js';
-import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
+import { parseNative } from '../libraries/braveUtils/index.js';
+import { buildRequests, interpretResponse } from '../libraries/braveUtils/buildAndInterpret.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -13,158 +13,15 @@ const BIDDER_CODE = 'videoheroes';
const DEFAULT_CUR = 'USD';
const ENDPOINT_URL = `https://point.contextualadv.com/?t=2&partner=hash`;
-const NATIVE_ASSETS_IDS = { 1: 'title', 2: 'icon', 3: 'image', 4: 'body', 5: 'sponsoredBy', 6: 'cta' };
-const NATIVE_ASSETS = {
- title: { id: 1, name: 'title' },
- icon: { id: 2, type: 1, name: 'img' },
- image: { id: 3, type: 3, name: 'img' },
- body: { id: 4, type: 2, name: 'data' },
- sponsoredBy: { id: 5, type: 1, name: 'data' },
- cta: { id: 6, type: 12, name: 'data' }
-};
-
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 !!(bid.params.placementId && bid.params.placementId.toString().length === 32);
- },
-
- /**
- * 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, bidderRequest) => {
- // convert Native ORTB definition to old-style prebid native definition
- validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);
- if (validBidRequests.length === 0 || !bidderRequest) return [];
-
- const endpointURL = ENDPOINT_URL.replace('hash', validBidRequests[0].params.placementId);
-
- let imp = validBidRequests.map(br => {
- let impObject = {
- id: br.bidId,
- secure: 1
- };
-
- if (br.mediaTypes.banner) {
- impObject.banner = createBannerRequest(br);
- } else if (br.mediaTypes.video) {
- impObject.video = createVideoRequest(br);
- } else if (br.mediaTypes.native) {
- impObject.native = {
- // TODO: fix transactionId leak: https://github.com/prebid/Prebid.js/issues/9781
- // Also, `id` is not in the ORTB native spec
- id: br.transactionId,
- ver: '1.2',
- request: createNativeRequest(br)
- };
- }
- return impObject;
- });
-
- let page = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation;
-
- let data = {
- id: bidderRequest.bidderRequestId,
- cur: [ DEFAULT_CUR ],
- device: {
- w: screen.width,
- h: screen.height,
- language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '',
- ua: navigator.userAgent,
- },
- site: {
- domain: parseUrl(page).hostname,
- page: page,
- },
- tmax: bidderRequest.timeout,
- imp
- };
-
- if (bidderRequest.refererInfo.ref) {
- data.site.ref = bidderRequest.refererInfo.ref;
- }
-
- if (bidderRequest.gdprConsent) {
- data['regs'] = {'ext': {'gdpr': bidderRequest.gdprConsent.gdprApplies ? 1 : 0}};
- data['user'] = {'ext': {'consent': bidderRequest.gdprConsent.consentString ? bidderRequest.gdprConsent.consentString : ''}};
- }
-
- if (bidderRequest.uspConsent !== undefined) {
- if (!data['regs'])data['regs'] = {'ext': {}};
- data['regs']['ext']['us_privacy'] = bidderRequest.uspConsent;
- }
-
- if (config.getConfig('coppa') === true) {
- if (!data['regs'])data['regs'] = {'coppa': 1};
- else data['regs']['coppa'] = 1;
- }
-
- if (validBidRequests[0].schain) {
- data['source'] = {'ext': {'schain': validBidRequests[0].schain}};
- }
-
- return {
- method: 'POST',
- url: endpointURL,
- data: data
- };
- },
-
- /**
- * 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) => {
- if (!serverResponse || isEmpty(serverResponse.body)) return [];
-
- let bids = [];
- serverResponse.body.seatbid.forEach(response => {
- response.bid.forEach(bid => {
- let mediaType = bid.ext && bid.ext.mediaType ? bid.ext.mediaType : 'banner';
-
- let bidObj = {
- requestId: bid.impid,
- cpm: bid.price,
- width: bid.w,
- height: bid.h,
- ttl: 1200,
- currency: DEFAULT_CUR,
- netRevenue: true,
- creativeId: bid.crid,
- dealId: bid.dealid || null,
- mediaType: mediaType
- };
-
- switch (mediaType) {
- case 'video':
- bidObj.vastUrl = bid.adm;
- break;
- case 'native':
- bidObj.native = parseNative(bid.adm);
- break;
- default:
- bidObj.ad = bid.adm;
- }
+ isBidRequestValid: (bid) => !!(bid.params.placementId && bid.params.placementId.toString().length === 32),
- bids.push(bidObj);
- });
- });
+ buildRequests: (validBidRequests, bidderRequest) => buildRequests(validBidRequests, bidderRequest, ENDPOINT_URL, DEFAULT_CUR),
- return bids;
- },
+ interpretResponse: (serverResponse) => interpretResponse(serverResponse, DEFAULT_CUR, parseNative),
onBidWon: (bid) => {
if (isStr(bid.nurl) && bid.nurl !== '') {
@@ -173,89 +30,4 @@ export const spec = {
}
};
-const parseNative = adm => {
- let bid = {
- clickUrl: adm.native.link && adm.native.link.url,
- impressionTrackers: adm.native.imptrackers || [],
- clickTrackers: (adm.native.link && adm.native.link.clicktrackers) || [],
- jstracker: adm.native.jstracker || []
- };
- adm.native.assets.forEach(asset => {
- let kind = NATIVE_ASSETS_IDS[asset.id];
- let content = kind && asset[NATIVE_ASSETS[kind].name];
- if (content) {
- bid[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
- }
- });
-
- return bid;
-}
-
-const createNativeRequest = br => {
- let impObject = {
- ver: '1.2',
- assets: []
- };
-
- let keys = Object.keys(br.mediaTypes.native);
-
- for (let key of keys) {
- const props = NATIVE_ASSETS[key];
- if (props) {
- const asset = {
- required: br.mediaTypes.native[key].required ? 1 : 0,
- id: props.id,
- [props.name]: {}
- };
-
- if (props.type) asset[props.name]['type'] = props.type;
- if (br.mediaTypes.native[key].len) asset[props.name]['len'] = br.mediaTypes.native[key].len;
- if (br.mediaTypes.native[key].sizes && br.mediaTypes.native[key].sizes[0]) {
- asset[props.name]['w'] = br.mediaTypes.native[key].sizes[0];
- asset[props.name]['h'] = br.mediaTypes.native[key].sizes[1];
- }
-
- impObject.assets.push(asset);
- }
- }
-
- return impObject;
-}
-
-const createBannerRequest = br => {
- let size = [];
-
- if (br.mediaTypes.banner.sizes && Array.isArray(br.mediaTypes.banner.sizes)) {
- if (Array.isArray(br.mediaTypes.banner.sizes[0])) { size = br.mediaTypes.banner.sizes[0]; } else { size = br.mediaTypes.banner.sizes; }
- } else size = [300, 250];
-
- return { id: br.transactionId, w: size[0], h: size[1] };
-};
-
-const createVideoRequest = br => {
- let videoObj = {id: br.transactionId};
- let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'skip', 'minbitrate', 'maxbitrate', 'api', 'linearity'];
-
- for (let param of supportParamsList) {
- if (br.mediaTypes.video[param] !== undefined) {
- videoObj[param] = br.mediaTypes.video[param];
- }
- }
-
- if (br.mediaTypes.video.playerSize && Array.isArray(br.mediaTypes.video.playerSize)) {
- if (Array.isArray(br.mediaTypes.video.playerSize[0])) {
- videoObj.w = br.mediaTypes.video.playerSize[0][0];
- videoObj.h = br.mediaTypes.video.playerSize[0][1];
- } else {
- videoObj.w = br.mediaTypes.video.playerSize[0];
- videoObj.h = br.mediaTypes.video.playerSize[1];
- }
- } else {
- videoObj.w = 640;
- videoObj.h = 480;
- }
-
- return videoObj;
-}
-
registerBidder(spec);
diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js
index c9ac9fae0f4..9f341b42ff5 100644
--- a/modules/vidoomyBidAdapter.js
+++ b/modules/vidoomyBidAdapter.js
@@ -1,4 +1,4 @@
-import {deepAccess, logError, parseSizesInput} from '../src/utils.js';
+import {deepAccess, isPlainObject, logError, parseSizesInput} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {config} from '../src/config.js';
@@ -88,7 +88,7 @@ function getBidFloor(bid, mediaType, sizes, bidfloor) {
var size = sizes && sizes.length > 0 ? sizes[0] : '*';
if (typeof bid.getFloor === 'function') {
const floorInfo = bid.getFloor({currency: 'USD', mediaType, size});
- if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) {
+ if (isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) {
floor = Math.max(bidfloor, parseFloat(floorInfo.floor));
}
}
diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js
index 217cab48cee..510106845db 100644
--- a/modules/visxBidAdapter.js
+++ b/modules/visxBidAdapter.js
@@ -6,6 +6,7 @@ import {INSTREAM as VIDEO_INSTREAM} from '../src/video.js';
import {getStorageManager} from '../src/storageManager.js';
import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js';
import { getBidFromResponse } from '../libraries/processResponse/index.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
const BIDDER_CODE = 'visx';
const GVLID = 154;
@@ -57,9 +58,8 @@ export const spec = {
const bids = validBidRequests || [];
const currency =
config.getConfig(`currency.bidderCurrencyDefault.${BIDDER_CODE}`) ||
- config.getConfig('currency.adServerCurrency') ||
+ getCurrencyFromBidderRequest(bidderRequest) ||
DEFAULT_CUR;
-
let request;
let reqId;
let payloadSchain;
diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js
index da72b975717..b0cfdabee9f 100644
--- a/modules/voxBidAdapter.js
+++ b/modules/voxBidAdapter.js
@@ -3,7 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {find} from '../src/polyfill.js';
import {Renderer} from '../src/Renderer.js';
-import {config} from '../src/config.js';
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -12,17 +12,15 @@ import {config} from '../src/config.js';
* @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests
*/
-const { getConfig } = config;
-
const BIDDER_CODE = 'vox';
const SSP_ENDPOINT = 'https://ssp.hybrid.ai/auction/prebid';
const VIDEO_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js';
const TTL = 60;
const GVLID = 206;
-function buildBidRequests(validBidRequests) {
+function buildBidRequests(validBidRequests, bidderRequest) {
return _map(validBidRequests, function(bid) {
- const currency = getConfig('currency.adServerCurrency');
+ const currency = getCurrencyFromBidderRequest(bidderRequest);
const floorInfo = bid.getFloor ? bid.getFloor({
currency: currency || 'USD'
}) : {};
@@ -218,7 +216,7 @@ export const spec = {
// TODO: is 'page' the right value here?
url: bidderRequest.refererInfo.page,
cmp: !!bidderRequest.gdprConsent,
- bidRequests: buildBidRequests(validBidRequests)
+ bidRequests: buildBidRequests(validBidRequests, bidderRequest)
};
if (payload.cmp) {
diff --git a/modules/wurflRtdProvider.js b/modules/wurflRtdProvider.js
index f019d2dbe52..dbf50744d25 100644
--- a/modules/wurflRtdProvider.js
+++ b/modules/wurflRtdProvider.js
@@ -101,13 +101,12 @@ function enrichBidderRequests(reqBidsConfigObj, bidders, wjsResponse) {
// inject WURFL data
enrichedBidders.add(bidderCode);
const data = bidderData(wjsResponse.WURFL, caps, authBidders[bidderCode]);
- logger.logMessage(`injecting data for ${bidderCode}: `, data);
+ data['enrich_device'] = true;
enrichBidderRequest(reqBidsConfigObj, bidderCode, data);
return;
}
// inject WURFL low entropy data
const data = lowEntropyData(wjsResponse.WURFL, wjsResponse.wurfl_pbjs?.low_entropy_caps);
- logger.logMessage(`injecting low entropy data for ${bidderCode}: `, data);
enrichBidderRequest(reqBidsConfigObj, bidderCode, data);
});
}
@@ -147,9 +146,14 @@ export const lowEntropyData = (wurflData, lowEntropyCaps) => {
}
data[cap] = value;
});
+ if ('model_name' in wurflData) {
+ data['model_name'] = wurflData.model_name.replace(/(iP(hone|ad|od)).*/, 'iP$2');
+ }
+ if ('brand_name' in wurflData) {
+ data['brand_name'] = wurflData.brand_name;
+ }
return data;
}
-
/**
* enrichBidderRequest enriches the bidder request with WURFL data
* @param {Object} reqBidsConfigObj Bid request configuration object
@@ -159,14 +163,82 @@ export const lowEntropyData = (wurflData, lowEntropyCaps) => {
export const enrichBidderRequest = (reqBidsConfigObj, bidderCode, wurflData) => {
const ortb2data = {
'device': {
- 'ext': {
- 'wurfl': wurflData,
- }
+ 'ext': {},
},
};
+
+ const device = reqBidsConfigObj.ortb2Fragments.global.device;
+ enrichOrtb2DeviceData('make', wurflData.brand_name, device, ortb2data);
+ enrichOrtb2DeviceData('model', wurflData.model_name, device, ortb2data);
+ if (wurflData.enrich_device) {
+ delete wurflData.enrich_device;
+ enrichOrtb2DeviceData('devicetype', makeOrtb2DeviceType(wurflData), device, ortb2data);
+ enrichOrtb2DeviceData('os', wurflData.advertised_device_os, device, ortb2data);
+ enrichOrtb2DeviceData('osv', wurflData.advertised_device_os_version, device, ortb2data);
+ enrichOrtb2DeviceData('hwv', wurflData.model_name, device, ortb2data);
+ enrichOrtb2DeviceData('h', wurflData.resolution_height, device, ortb2data);
+ enrichOrtb2DeviceData('w', wurflData.resolution_width, device, ortb2data);
+ enrichOrtb2DeviceData('ppi', wurflData.pixel_density, device, ortb2data);
+ enrichOrtb2DeviceData('pxratio', wurflData.density_class, device, ortb2data);
+ enrichOrtb2DeviceData('js', wurflData.ajax_support_javascript, device, ortb2data);
+ }
+ ortb2data.device.ext['wurfl'] = wurflData
mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, { [bidderCode]: ortb2data });
}
+/**
+ * makeOrtb2DeviceType returns the ortb2 device type based on WURFL data
+ * @param {Object} wurflData WURFL data
+ * @returns {Number} ortb2 device type
+ * @see https://www.scientiamobile.com/how-to-populate-iab-openrtb-device-object/
+ */
+export function makeOrtb2DeviceType(wurflData) {
+ if (wurflData.is_mobile) {
+ if (!('is_phone' in wurflData) || !('is_tablet' in wurflData)) {
+ return undefined;
+ }
+ if (wurflData.is_phone || wurflData.is_tablet) {
+ return 1;
+ }
+ return 6;
+ }
+ if (wurflData.is_full_desktop) {
+ return 2;
+ }
+ if (wurflData.is_connected_tv) {
+ return 3;
+ }
+ if (wurflData.is_phone) {
+ return 4;
+ }
+ if (wurflData.is_tablet) {
+ return 5;
+ }
+ if (wurflData.is_ott) {
+ return 7;
+ }
+ return undefined;
+}
+
+/**
+ * enrichOrtb2DeviceData enriches the ortb2data device data with WURFL data.
+ * Note: it does not overrides properties set by Prebid.js
+ * @param {String} key the device property key
+ * @param {any} value the value of the device property
+ * @param {Object} device the ortb2 device object from Prebid.js
+ * @param {Object} ortb2data the ortb2 device data enrchiced with WURFL data
+ */
+function enrichOrtb2DeviceData(key, value, device, ortb2data) {
+ if (device?.[key] !== undefined) {
+ // value already defined by Prebid.js, do not overrides
+ return;
+ }
+ if (value === undefined) {
+ return;
+ }
+ ortb2data.device[key] = value;
+}
+
/**
* onAuctionEndEvent is called when the auction ends
* @param {Object} auctionDetails Auction details
diff --git a/modules/yahooAdsBidAdapter.js b/modules/yahooAdsBidAdapter.js
index eaffa1f6fae..7622a5b7587 100644
--- a/modules/yahooAdsBidAdapter.js
+++ b/modules/yahooAdsBidAdapter.js
@@ -150,7 +150,7 @@ function getSupportedEids(bid) {
}
function isSecure(bid) {
- return deepAccess(bid, 'params.bidOverride.imp.secure') || (document.location.protocol === 'https:') ? 1 : 0;
+ return deepAccess(bid, 'params.bidOverride.imp.secure') ?? bid.ortb2Imp?.secure ?? 1;
};
function getPubIdMode(bid) {
@@ -346,7 +346,7 @@ function appendImpObject(bid, openRtbObject) {
const impObject = {
id: bid.bidId,
secure: isSecure(bid),
- bidfloor: getFloorModuleData(bid).floor || deepAccess(bid, 'params.bidOverride.imp.bidfloor')
+ bidfloor: getFloorModuleData(bid)?.floor || deepAccess(bid, 'params.bidOverride.imp.bidfloor')
};
if (bid.mediaTypes.banner && (typeof mediaTypeMode === 'undefined' || mediaTypeMode === BANNER || mediaTypeMode === '*')) {
diff --git a/modules/yandexAnalyticsAdapter.js b/modules/yandexAnalyticsAdapter.js
index 8afe7298c13..6c44bea7cd2 100644
--- a/modules/yandexAnalyticsAdapter.js
+++ b/modules/yandexAnalyticsAdapter.js
@@ -3,6 +3,7 @@ import adapterManager from '../src/adapterManager.js';
import { logError, logInfo } from '../src/utils.js';
import { EVENTS } from '../src/constants.js';
import * as events from '../src/events.js';
+import { getGlobal } from '../src/prebidGlobal.js';
const timeoutIds = {};
const tryUntil = (operationId, conditionCb, cb) => {
@@ -23,6 +24,7 @@ const clearTryUntilTimeouts = (timeouts) => {
});
};
+export const PBJS_INIT_EVENT_NAME = 'pbjsInit';
const SEND_EVENTS_BUNDLE_TIMEOUT = 1500;
const {
BID_REQUESTED,
@@ -122,6 +124,9 @@ const yandexAnalytics = Object.assign(buildAdapter({ analyticsType: 'endpoint' }
logError('Aborting yandex analytics provider initialization.');
}, 25000);
+ yandexAnalytics.onEvent(PBJS_INIT_EVENT_NAME, {
+ 'version': getGlobal().version,
+ });
events.getEvents().forEach((event) => {
if (event && EVENTS_TO_TRACK.indexOf(event.eventType) >= 0) {
yandexAnalytics.onEvent(event.eventType, event);
diff --git a/modules/yandexAnalyticsAdapter.md b/modules/yandexAnalyticsAdapter.md
index 43460550471..0863cc1dfb6 100644
--- a/modules/yandexAnalyticsAdapter.md
+++ b/modules/yandexAnalyticsAdapter.md
@@ -20,7 +20,7 @@ Disclosure: The adapter utilizes the Metrica Tag build based on [github.com/yand
2. **Insert Counter Initialization Code:**
- Retrieve the counter initialization code from the Yandex Metrica settings page at `https://metrica.yandex.com/settings?id={counterId}`, where `{counterId}` is your counter ID, and embed it into your website's HTML.
+ Retrieve the counter initialization code from the Yandex Metrica settings page at [metrica.yandex.com/r/settings](https://metrica.yandex.com/r/settings), and embed it into your website's HTML.
3. **Initialize the Adapter in Prebid.js:**
@@ -43,4 +43,4 @@ Disclosure: The adapter utilizes the Metrica Tag build based on [github.com/yand
## Accessing Analytics Data
-You can view the collected analytics data in the Yandex Metrica dashboard. Navigate to [metrika.yandex.com/dashboard](https://metrika.yandex.com/dashboard) and look for the Prebid Analytics section to analyze your data.
+You can view the collected analytics data in the Yandex Metrica dashboard. Navigate to [metrika.yandex.com/r/stat/prebid_events](https://metrika.yandex.com/r/stat/prebid_events) to analyze your data.
diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js
index 97bd793d633..eed0902e9dc 100644
--- a/modules/yandexBidAdapter.js
+++ b/modules/yandexBidAdapter.js
@@ -1,5 +1,5 @@
+import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
-import { config } from '../src/config.js';
import { BANNER, NATIVE } from '../src/mediaTypes.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
import { _each, _map, deepAccess, deepSetValue, formatQS, triggerPixel, logInfo } from '../src/utils.js';
@@ -128,7 +128,7 @@ export const spec = {
timeout = bidderRequest.timeout;
}
- const adServerCurrency = config.getConfig('currency.adServerCurrency');
+ const adServerCurrency = getCurrencyFromBidderRequest(bidderRequest);
return validBidRequests.map((bidRequest) => {
const { params } = bidRequest;
diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js
index 67a34ae4870..9be01096084 100644
--- a/modules/yieldlabBidAdapter.js
+++ b/modules/yieldlabBidAdapter.js
@@ -555,7 +555,7 @@ function getBidFloor(bid, sizes) {
mediaType: mediaType !== undefined && spec.supportedMediaTypes.includes(mediaType) ? mediaType : '*',
size: sizes.length !== 1 ? '*' : sizes[0].split(DIMENSION_SIGN),
});
- if (floor.currency === CURRENCY_CODE) {
+ if (floor?.currency === CURRENCY_CODE) {
return (floor.floor * 100).toFixed(0);
}
return undefined;
diff --git a/package-lock.json b/package-lock.json
index 9a47e65df49..c3012d1d9a4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "prebid.js",
- "version": "9.19.0-pre",
+ "version": "9.23.0-pre",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "prebid.js",
- "version": "9.19.0-pre",
+ "version": "9.23.0-pre",
"license": "Apache-2.0",
"dependencies": {
"@babel/core": "^7.25.2",
@@ -22,7 +22,7 @@
"fun-hooks": "^0.9.9",
"gulp-wrap": "^0.15.0",
"klona": "^2.0.6",
- "live-connect-js": "^6.7.3"
+ "live-connect-js": "^7.1.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.16.5",
@@ -11337,9 +11337,9 @@
}
},
"node_modules/express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -11360,7 +11360,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -11375,6 +11375,10 @@
},
"engines": {
"node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/debug": {
@@ -18197,23 +18201,23 @@
"dev": true
},
"node_modules/live-connect-common": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.1.4.tgz",
- "integrity": "sha512-NK5HH0b/6bQX6hZQttlDfqrpDiP+iYtYYGO47LfM9YVwT1OZITgYZUJ0oG4IVynwdpas/VGvXv5hN0UcVK97oQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-4.1.0.tgz",
+ "integrity": "sha512-sRklgbe13377aR+G0qCBiZPayQw5oZZozkuxKEoyipxscLbVzwe9gtA7CPpbmo6UcOdQxdCE6A7J1tI0wTSmqw==",
"engines": {
- "node": ">=18"
+ "node": ">=20"
}
},
"node_modules/live-connect-js": {
- "version": "6.7.3",
- "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.7.3.tgz",
- "integrity": "sha512-K2/GGhyhJ7/bFJfjiNw41W5xLRER9Smc49a8A6PImCcgit/sp2UsYz/F+sQwoj8IkJ3PufHvBnIGBbeQ31VsBg==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-7.1.0.tgz",
+ "integrity": "sha512-fFxvQjOsHkCjulWsbirjxb6Y8xuAoWdgYqZvBLoSVKry48IyvVnLfvWgJg66qENjxig+8RH9bvlE16I6hJ7J7Q==",
"dependencies": {
- "live-connect-common": "^v3.1.4",
+ "live-connect-common": "^v4.1.0",
"tiny-hashes": "1.0.1"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
}
},
"node_modules/livereload-js": {
@@ -21597,9 +21601,9 @@
"dev": true
},
"node_modules/path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"node_modules/path-type": {
"version": "1.1.0",
@@ -36414,9 +36418,9 @@
}
},
"express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -36437,7 +36441,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -41721,16 +41725,16 @@
"dev": true
},
"live-connect-common": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.1.4.tgz",
- "integrity": "sha512-NK5HH0b/6bQX6hZQttlDfqrpDiP+iYtYYGO47LfM9YVwT1OZITgYZUJ0oG4IVynwdpas/VGvXv5hN0UcVK97oQ=="
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-4.1.0.tgz",
+ "integrity": "sha512-sRklgbe13377aR+G0qCBiZPayQw5oZZozkuxKEoyipxscLbVzwe9gtA7CPpbmo6UcOdQxdCE6A7J1tI0wTSmqw=="
},
"live-connect-js": {
- "version": "6.7.3",
- "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.7.3.tgz",
- "integrity": "sha512-K2/GGhyhJ7/bFJfjiNw41W5xLRER9Smc49a8A6PImCcgit/sp2UsYz/F+sQwoj8IkJ3PufHvBnIGBbeQ31VsBg==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-7.1.0.tgz",
+ "integrity": "sha512-fFxvQjOsHkCjulWsbirjxb6Y8xuAoWdgYqZvBLoSVKry48IyvVnLfvWgJg66qENjxig+8RH9bvlE16I6hJ7J7Q==",
"requires": {
- "live-connect-common": "^v3.1.4",
+ "live-connect-common": "^v4.1.0",
"tiny-hashes": "1.0.1"
}
},
@@ -44256,9 +44260,9 @@
}
},
"path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"path-type": {
"version": "1.1.0",
diff --git a/package.json b/package.json
index ba453eb6da0..2b6d3cae7aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "9.19.0-pre",
+ "version": "9.23.0-pre",
"description": "Header Bidding Management Library",
"main": "src/prebid.public.js",
"exports": {
@@ -143,7 +143,7 @@
"fun-hooks": "^0.9.9",
"gulp-wrap": "^0.15.0",
"klona": "^2.0.6",
- "live-connect-js": "^6.7.3"
+ "live-connect-js": "^7.1.0"
},
"optionalDependencies": {
"fsevents": "^2.3.2"
diff --git a/src/Renderer.js b/src/Renderer.js
index 912259206c4..772d8d93655 100644
--- a/src/Renderer.js
+++ b/src/Renderer.js
@@ -1,6 +1,6 @@
import { loadExternalScript } from './adloader.js';
import {
- logError, logWarn, logMessage, deepAccess
+ logError, logWarn, logMessage
} from './utils.js';
import {find} from './polyfill.js';
import {getGlobal} from './prebidGlobal.js';
@@ -144,11 +144,11 @@ function isRendererPreferredFromAdUnit(adUnitCode) {
}
// renderer defined at adUnit level
- const adUnitRenderer = deepAccess(adUnit, 'renderer');
+ const adUnitRenderer = adUnit?.renderer;
const hasValidAdUnitRenderer = !!(adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render);
// renderer defined at adUnit.mediaTypes level
- const mediaTypeRenderer = deepAccess(adUnit, 'mediaTypes.video.renderer');
+ const mediaTypeRenderer = adUnit?.mediaTypes?.video?.renderer;
const hasValidMediaTypeRenderer = !!(mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render)
return !!(
diff --git a/src/adRendering.js b/src/adRendering.js
index 4b1f2960428..502721dea38 100644
--- a/src/adRendering.js
+++ b/src/adRendering.js
@@ -1,7 +1,6 @@
import {
createIframe,
createInvisibleIframe,
- deepAccess,
inIframe,
insertElement,
logError,
@@ -20,8 +19,9 @@ import {fireNativeTrackers} from './native.js';
import {GreedyPromise} from './utils/promise.js';
import adapterManager from './adapterManager.js';
import {useMetrics} from './utils/perfMetrics.js';
+import {filters} from './targeting.js';
-const { AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON } = EVENTS;
+const { AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON, EXPIRED_RENDER } = EVENTS;
const { EXCEPTION } = AD_RENDER_FAILED_REASON;
export const getBidToRender = hook('sync', function (adId, forRender = true, override = GreedyPromise.resolve()) {
@@ -181,10 +181,18 @@ export function handleRender({renderFn, resizeFn, adId, options, bidResponse, do
if (bidResponse.status === BID_STATUS.RENDERED) {
logWarn(`Ad id ${adId} has been rendered before`);
events.emit(STALE_RENDER, bidResponse);
- if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) {
+ if (config.getConfig('auctionOptions')?.suppressStaleRender) {
return;
}
}
+ if (!filters.isBidNotExpired(bidResponse)) {
+ logWarn(`Ad id ${adId} has been expired`);
+ events.emit(EXPIRED_RENDER, bidResponse);
+ if (config.getConfig('auctionOptions')?.suppressExpiredRender) {
+ return;
+ }
+ }
+
try {
doRender({renderFn, resizeFn, bidResponse, options, doc});
} catch (e) {
@@ -255,7 +263,7 @@ export function renderAdDirect(doc, adId, options) {
if (adData.ad) {
doc.write(adData.ad);
doc.close();
- emitAdRenderSucceeded({doc, bid, adId: bid.adId});
+ emitAdRenderSucceeded({doc, bid, id: bid.adId});
} else {
getCreativeRenderer(bid)
.then(render => render(adData, {
@@ -263,7 +271,7 @@ export function renderAdDirect(doc, adId, options) {
mkFrame: createIframe,
}, doc.defaultView))
.then(
- () => emitAdRenderSucceeded({doc, bid, adId: bid.adId}),
+ () => emitAdRenderSucceeded({doc, bid, id: bid.adId}),
(e) => {
fail(e?.reason || AD_RENDER_FAILED_REASON.EXCEPTION, e?.message)
e?.stack && logError(e);
diff --git a/src/adapterManager.js b/src/adapterManager.js
index c39ef039af3..fb396d8d794 100644
--- a/src/adapterManager.js
+++ b/src/adapterManager.js
@@ -1,7 +1,6 @@
/** @module adaptermanger */
import {
- deepAccess,
deepClone,
flatten,
generateUUID,
@@ -128,7 +127,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src, metrics}
adUnitCode: adUnit.code,
transactionId: adUnit.transactionId,
adUnitId: adUnit.adUnitId,
- sizes: deepAccess(mediaTypes, 'banner.sizes') || deepAccess(mediaTypes, 'video.playerSize') || [],
+ sizes: mediaTypes?.banner?.sizes || mediaTypes?.video?.playerSize || [],
bidId: bid.bid_id || getUniqueIdentifierStr(),
bidderRequestId,
auctionId,
diff --git a/src/ajax.js b/src/ajax.js
index 7f9857ad18d..0178f95eadf 100644
--- a/src/ajax.js
+++ b/src/ajax.js
@@ -47,10 +47,14 @@ export function toFetchRequest(url, data, options = {}) {
if (options.withCredentials) {
rqOpts.credentials = 'include';
}
- if (options.browsingTopics && isSecureContext) {
- // the Request constructor will throw an exception if the browser supports topics
- // but we're not in a secure context
- rqOpts.browsingTopics = true;
+ if (isSecureContext) {
+ ['browsingTopics', 'adAuctionHeaders'].forEach(opt => {
+ // the Request constructor will throw an exception if the browser supports topics/fledge
+ // but we're not in a secure context
+ if (options[opt]) {
+ rqOpts[opt] = true;
+ }
+ })
}
if (options.keepalive) {
rqOpts.keepalive = true;
diff --git a/src/auction.js b/src/auction.js
index f122840affc..3945e0bfe53 100644
--- a/src/auction.js
+++ b/src/auction.js
@@ -66,9 +66,7 @@
*/
import {
- deepAccess,
generateUUID,
- getValue,
isEmpty,
isEmptyStr,
isFn,
@@ -98,6 +96,8 @@ import {defer, GreedyPromise} from './utils/promise.js';
import {useMetrics} from './utils/perfMetrics.js';
import {adjustCpm} from './utils/cpm.js';
import {getGlobal} from './prebidGlobal.js';
+import {ttlCollection} from './utils/ttlCollection.js';
+import {getMinBidCacheTTL, onMinBidCacheTTLChange} from './bidTTL.js';
const { syncUsers } = userSync;
@@ -153,7 +153,10 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
let _bidsRejected = [];
let _callback = callback;
let _bidderRequests = [];
- let _bidsReceived = [];
+ let _bidsReceived = ttlCollection({
+ startTime: (bid) => bid.responseTimestamp,
+ ttl: (bid) => getMinBidCacheTTL() == null ? null : Math.max(getMinBidCacheTTL(), bid.ttl) * 1000
+ });
let _noBids = [];
let _winningBids = [];
let _auctionStart;
@@ -162,8 +165,10 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
let _auctionStatus;
let _nonBids = [];
+ onMinBidCacheTTLChange(() => _bidsReceived.refresh());
+
function addBidRequests(bidderRequests) { _bidderRequests = _bidderRequests.concat(bidderRequests); }
- function addBidReceived(bidsReceived) { _bidsReceived = _bidsReceived.concat(bidsReceived); }
+ function addBidReceived(bid) { _bidsReceived.add(bid); }
function addBidRejected(bidsRejected) { _bidsRejected = _bidsRejected.concat(bidsRejected); }
function addNoBid(noBid) { _noBids = _noBids.concat(noBid); }
function addNonBids(seatnonbids) { _nonBids = _nonBids.concat(seatnonbids); }
@@ -179,7 +184,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
labels: _labels,
bidderRequests: _bidderRequests,
noBids: _noBids,
- bidsReceived: _bidsReceived,
+ bidsReceived: _bidsReceived.toArray(),
bidsRejected: _bidsRejected,
winningBids: _winningBids,
timeout: _timeout,
@@ -219,7 +224,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
bidsBackCallback(_adUnits, function () {
try {
if (_callback != null) {
- const bids = _bidsReceived
+ const bids = _bidsReceived.toArray()
.filter(bid => _adUnitCodes.includes(bid.adUnitCode))
.reduce(groupByPlacement, {});
_callback.apply(pbjsInstance, [bids, timedOut, _auctionId]);
@@ -246,7 +251,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
function auctionDone() {
config.resetBidder();
// when all bidders have called done callback atleast once it means auction is complete
- logInfo(`Bids Received for Auction with id: ${_auctionId}`, _bidsReceived);
+ logInfo(`Bids Received for Auction with id: ${_auctionId}`, _bidsReceived.toArray());
_auctionStatus = AUCTION_COMPLETED;
executeCallback(false);
}
@@ -404,7 +409,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
getAdUnits: () => _adUnits,
getAdUnitCodes: () => _adUnitCodes,
getBidRequests: () => _bidderRequests,
- getBidsReceived: () => _bidsReceived,
+ getBidsReceived: () => _bidsReceived.toArray(),
getNoBids: () => _noBids,
getNonBids: () => _nonBids,
getFPD: () => ortb2Fragments,
@@ -559,13 +564,12 @@ export function addBidToAuction(auctionInstance, bidResponse) {
function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = auctionManager.index} = {}) {
let addBid = true;
- const videoMediaType = deepAccess(
- index.getMediaTypes({
- requestId: bidResponse.originalRequestId || bidResponse.requestId,
- adUnitId: bidResponse.adUnitId
- }), 'video');
- const context = videoMediaType && deepAccess(videoMediaType, 'context');
- const useCacheKey = videoMediaType && deepAccess(videoMediaType, 'useCacheKey');
+ const videoMediaType = index.getMediaTypes({
+ requestId: bidResponse.originalRequestId || bidResponse.requestId,
+ adUnitId: bidResponse.adUnitId
+ })?.video;
+ const context = videoMediaType && videoMediaType?.context;
+ const useCacheKey = videoMediaType && videoMediaType?.useCacheKey;
if (config.getConfig('cache.url') && (useCacheKey || context !== OUTSTREAM)) {
if (!bidResponse.videoCacheKey || config.getConfig('cache.ignoreBidderCacheKey')) {
@@ -635,15 +639,15 @@ function getPreparedBidForAuction(bid, {index = auctionManager.index} = {}) {
var renderer = null;
// the renderer for the mediaType takes precendence
- if (mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render && !(mediaTypeRenderer.backupOnly === true && bid.renderer)) {
+ if (mediaTypeRenderer && mediaTypeRenderer.render && !(mediaTypeRenderer.backupOnly === true && bid.renderer)) {
renderer = mediaTypeRenderer;
- } else if (bidRenderer && bidRenderer.url && bidRenderer.render && !(bidRenderer.backupOnly === true && bid.renderer)) {
+ } else if (bidRenderer && bidRenderer.render && !(bidRenderer.backupOnly === true && bid.renderer)) {
renderer = bidRenderer;
}
if (renderer) {
// be aware, an adapter could already have installed the bidder, in which case this overwrite's the existing adapter
- bid.renderer = Renderer.install({ url: renderer.url, config: renderer.options });// rename options to config, to make it consistent?
+ bid.renderer = Renderer.install({ url: renderer.url, config: renderer.options, renderNow: renderer.url == null });// rename options to config, to make it consistent?
bid.renderer.setRender(renderer.render);
}
@@ -684,7 +688,7 @@ function setupBidTargeting(bidObject) {
export function getMediaTypeGranularity(mediaType, mediaTypes, mediaTypePriceGranularity) {
if (mediaType && mediaTypePriceGranularity) {
if (FEATURES.VIDEO && mediaType === VIDEO) {
- const context = deepAccess(mediaTypes, `${VIDEO}.context`, 'instream');
+ const context = mediaTypes?.[VIDEO]?.context ?? 'instream';
if (mediaTypePriceGranularity[`${VIDEO}-${context}`]) {
return mediaTypePriceGranularity[`${VIDEO}-${context}`];
}
@@ -757,7 +761,7 @@ export const getAdvertiserDomain = () => {
*/
export const getDSP = () => {
return (bid) => {
- return (bid.meta && (bid.meta.networkId || bid.meta.networkName)) ? deepAccess(bid, 'meta.networkName') || deepAccess(bid, 'meta.networkId') : '';
+ return (bid.meta && (bid.meta.networkId || bid.meta.networkName)) ? bid?.meta?.networkName || bid?.meta?.networkId : '';
}
}
@@ -780,7 +784,7 @@ function createKeyVal(key, value) {
return value(bidResponse, bidReq);
}
: function (bidResponse) {
- return getValue(bidResponse, value);
+ return bidResponse[value];
}
};
}
@@ -829,8 +833,7 @@ export function getStandardBidderSettings(mediaType, bidderCode) {
if (typeof find(adserverTargeting, targetingKeyVal => targetingKeyVal.key === TARGETING_KEYS.CACHE_HOST) === 'undefined') {
adserverTargeting.push(createKeyVal(TARGETING_KEYS.CACHE_HOST, function(bidResponse) {
- return deepAccess(bidResponse, `adserverTargeting.${TARGETING_KEYS.CACHE_HOST}`)
- ? bidResponse.adserverTargeting[TARGETING_KEYS.CACHE_HOST] : urlInfo.hostname;
+ return bidResponse?.adserverTargeting?.[TARGETING_KEYS.CACHE_HOST] || urlInfo.hostname;
}));
}
}
diff --git a/src/auctionManager.js b/src/auctionManager.js
index 27a2c2beaf2..ab2947b96b4 100644
--- a/src/auctionManager.js
+++ b/src/auctionManager.js
@@ -26,10 +26,7 @@ import {AuctionIndex} from './auctionIndex.js';
import { BID_STATUS, JSON_MAPPING } from './constants.js';
import {useMetrics} from './utils/perfMetrics.js';
import {ttlCollection} from './utils/ttlCollection.js';
-import {getTTL, onTTLBufferChange} from './bidTTL.js';
-import {config} from './config.js';
-
-const CACHE_TTL_SETTING = 'minBidCacheTTL';
+import {getMinBidCacheTTL, onMinBidCacheTTLChange} from './bidTTL.js';
/**
* Creates new instance of auctionManager. There will only be one instance of auctionManager but
@@ -38,27 +35,14 @@ const CACHE_TTL_SETTING = 'minBidCacheTTL';
* @returns {AuctionManager} auctionManagerInstance
*/
export function newAuctionManager() {
- let minCacheTTL = null;
-
const _auctions = ttlCollection({
startTime: (au) => au.end.then(() => au.getAuctionEnd()),
- ttl: (au) => minCacheTTL == null ? null : au.end.then(() => {
- return Math.max(minCacheTTL, ...au.getBidsReceived().map(getTTL)) * 1000
+ ttl: (au) => getMinBidCacheTTL() == null ? null : au.end.then(() => {
+ return Math.max(getMinBidCacheTTL(), ...au.getBidsReceived().map(bid => bid.ttl)) * 1000
}),
});
- onTTLBufferChange(() => {
- if (minCacheTTL != null) _auctions.refresh();
- })
-
- config.getConfig(CACHE_TTL_SETTING, (cfg) => {
- const prev = minCacheTTL;
- minCacheTTL = cfg?.[CACHE_TTL_SETTING];
- minCacheTTL = typeof minCacheTTL === 'number' ? minCacheTTL : null;
- if (prev !== minCacheTTL) {
- _auctions.refresh();
- }
- })
+ onMinBidCacheTTLChange(() => _auctions.refresh());
const auctionManager = {
onExpiry: _auctions.onExpiry
diff --git a/src/bidTTL.js b/src/bidTTL.js
index 55ba0c026b0..d685c4aa4f0 100644
--- a/src/bidTTL.js
+++ b/src/bidTTL.js
@@ -1,25 +1,35 @@
import {config} from './config.js';
import {logError} from './utils.js';
+const CACHE_TTL_SETTING = 'minBidCacheTTL';
let TTL_BUFFER = 1;
-
+let minCacheTTL = null;
const listeners = [];
config.getConfig('ttlBuffer', (cfg) => {
if (typeof cfg.ttlBuffer === 'number') {
- const prev = TTL_BUFFER;
TTL_BUFFER = cfg.ttlBuffer;
- if (prev !== TTL_BUFFER) {
- listeners.forEach(l => l(TTL_BUFFER))
- }
} else {
logError('Invalid value for ttlBuffer', cfg.ttlBuffer);
}
})
-export function getTTL(bid) {
+export function getBufferedTTL(bid) {
return bid.ttl - (bid.hasOwnProperty('ttlBuffer') ? bid.ttlBuffer : TTL_BUFFER);
}
-export function onTTLBufferChange(listener) {
+export function getMinBidCacheTTL() {
+ return minCacheTTL;
+}
+
+config.getConfig(CACHE_TTL_SETTING, (cfg) => {
+ const prev = minCacheTTL;
+ minCacheTTL = cfg?.[CACHE_TTL_SETTING];
+ minCacheTTL = typeof minCacheTTL === 'number' ? minCacheTTL : null;
+ if (prev !== minCacheTTL) {
+ listeners.forEach(l => l(minCacheTTL))
+ }
+})
+
+export function onMinBidCacheTTLChange(listener) {
listeners.push(listener);
}
diff --git a/src/config.js b/src/config.js
index 53f53f99529..88affc40faf 100644
--- a/src/config.js
+++ b/src/config.js
@@ -179,7 +179,7 @@ function attachProperties(config, useDefaultValues = true) {
}
for (let k of Object.keys(val)) {
- if (k !== 'secondaryBidders' && k !== 'suppressStaleRender') {
+ if (k !== 'secondaryBidders' && k !== 'suppressStaleRender' && k !== 'suppressExpiredRender') {
logWarn(`Auction Options given an incorrect param: ${k}`)
return false
}
@@ -191,7 +191,7 @@ function attachProperties(config, useDefaultValues = true) {
logWarn(`Auction Options ${k} must be only string`);
return false
}
- } else if (k === 'suppressStaleRender') {
+ } else if (k === 'suppressStaleRender' || k === 'suppressExpiredRender') {
if (!isBoolean(val[k])) {
logWarn(`Auction Options ${k} must be of type boolean`);
return false;
diff --git a/src/constants.js b/src/constants.js
index 095ee648b7e..1a17cf3eec0 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -40,6 +40,7 @@ export const EVENTS = {
AUCTION_DEBUG: 'auctionDebug',
BID_VIEWABLE: 'bidViewable',
STALE_RENDER: 'staleRender',
+ EXPIRED_RENDER: 'expiredRender',
BILLABLE_EVENT: 'billableEvent',
BID_ACCEPTED: 'bidAccepted',
RUN_PAAPI_AUCTION: 'paapiRunAuction',
diff --git a/src/events.js b/src/events.js
index 279acc27b9a..38e7f633d16 100644
--- a/src/events.js
+++ b/src/events.js
@@ -161,7 +161,6 @@ const _public = (function () {
return eventsFired.toArray().map(val => Object.assign({}, val))
};
- window.prebidEvents = _public
return _public;
}());
diff --git a/src/fpd/enrichment.js b/src/fpd/enrichment.js
index 4c3fd4b6c07..ec5047238a2 100644
--- a/src/fpd/enrichment.js
+++ b/src/fpd/enrichment.js
@@ -129,7 +129,7 @@ const ENRICHMENTS = {
regs() {
const regs = {};
if (winFallback((win) => win.navigator.globalPrivacyControl)) {
- deepSetValue(regs, 'ext.gpc', 1);
+ deepSetValue(regs, 'ext.gpc', '1');
}
const coppa = config.getConfig('coppa');
if (typeof coppa === 'boolean') {
diff --git a/src/native.js b/src/native.js
index a641beb71d5..19833406451 100644
--- a/src/native.js
+++ b/src/native.js
@@ -1,5 +1,4 @@
import {
- deepAccess,
deepClone, getDefinedParams,
insertHtmlIntoIframe,
isArray,
@@ -129,7 +128,7 @@ export function processNativeAdUnitParams(params) {
export function decorateAdUnitsWithNativeParams(adUnits) {
adUnits.forEach(adUnit => {
const nativeParams =
- adUnit.nativeParams || deepAccess(adUnit, 'mediaTypes.native');
+ adUnit.nativeParams || adUnit?.mediaTypes?.native;
if (nativeParams) {
adUnit.nativeParams = processNativeAdUnitParams(nativeParams);
}
@@ -213,7 +212,7 @@ function typeIsSupported(type) {
*/
export const nativeAdUnit = adUnit => {
const mediaType = adUnit.mediaType === 'native';
- const mediaTypes = deepAccess(adUnit, 'mediaTypes.native');
+ const mediaTypes = adUnit?.mediaTypes?.native;
return mediaType || mediaTypes;
}
export const nativeBidder = bid => includes(nativeAdapters, bid.bidder);
@@ -236,7 +235,7 @@ export function nativeBidIsValid(bid, {index = auctionManager.index} = {}) {
}
export function isNativeOpenRTBBidValid(bidORTB, bidRequestORTB) {
- if (!deepAccess(bidORTB, 'link.url')) {
+ if (!bidORTB?.link?.url) {
logError(`native response doesn't have 'link' property. Ortb response: `, bidORTB);
return false;
}
@@ -364,10 +363,7 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) {
let keyValues = {};
const adUnit = index.getAdUnit(bid);
- const globalSendTargetingKeys = adUnit?.nativeParams?.ortb == null && deepAccess(
- adUnit,
- `nativeParams.sendTargetingKeys`
- ) !== false;
+ const globalSendTargetingKeys = adUnit?.nativeParams?.ortb == null && adUnit?.nativeParams?.sendTargetingKeys !== false;
const nativeKeys = getNativeKeys(adUnit);
@@ -376,15 +372,15 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) {
Object.keys(flatBidNativeKeys).forEach(asset => {
const key = nativeKeys[asset];
- let value = getAssetValue(bid.native[asset]) || getAssetValue(deepAccess(bid, `native.ext.${asset}`));
+ let value = getAssetValue(bid.native[asset]) || getAssetValue(bid?.native?.ext?.[asset]);
if (asset === 'adTemplate' || !key || !value) {
return;
}
- let sendPlaceholder = deepAccess(adUnit, `nativeParams.${asset}.sendId`);
+ let sendPlaceholder = adUnit?.nativeParams?.[asset]?.sendId;
if (typeof sendPlaceholder !== 'boolean') {
- sendPlaceholder = deepAccess(adUnit, `nativeParams.ext.${asset}.sendId`);
+ sendPlaceholder = adUnit?.nativeParams?.ext?.[asset]?.sendId;
}
if (sendPlaceholder) {
@@ -392,9 +388,9 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) {
value = placeholder;
}
- let assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.${asset}.sendTargetingKeys`);
+ let assetSendTargetingKeys = adUnit?.nativeParams?.[asset]?.sendTargetingKeys;
if (typeof assetSendTargetingKeys !== 'boolean') {
- assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.ext.${asset}.sendTargetingKeys`);
+ assetSendTargetingKeys = adUnit?.nativeParams?.ext?.[asset]?.sendTargetingKeys;
}
const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys;
@@ -482,7 +478,7 @@ function getAssetValue(value) {
function getNativeKeys(adUnit) {
const extraNativeKeys = {}
- if (deepAccess(adUnit, 'nativeParams.ext')) {
+ if (adUnit?.nativeParams?.ext) {
Object.keys(adUnit.nativeParams.ext).forEach(extKey => {
extraNativeKeys[extKey] = `hb_native_${extKey}`;
})
diff --git a/src/prebid.js b/src/prebid.js
index 975e4b4517b..37d37acfee7 100644
--- a/src/prebid.js
+++ b/src/prebid.js
@@ -2,7 +2,6 @@
import {getGlobal} from './prebidGlobal.js';
import {
- deepAccess,
deepClone,
deepSetValue,
flatten,
@@ -39,7 +38,13 @@ import {newMetrics, useMetrics} from './utils/perfMetrics.js';
import {defer, GreedyPromise} from './utils/promise.js';
import {enrichFPD} from './fpd/enrichment.js';
import {allConsent} from './consentHandler.js';
-import {insertLocatorFrame, markBidAsRendered, renderAdDirect, renderIfDeferred} from './adRendering.js';
+import {
+ insertLocatorFrame,
+ markBidAsRendered,
+ markWinningBid,
+ renderAdDirect,
+ renderIfDeferred
+} from './adRendering.js';
import {getHighestCpm} from './utils/reducers.js';
import {fillVideoDefaults, validateOrtbVideoFields} from './video.js';
@@ -207,7 +212,7 @@ function validateNativeMediaType(adUnit) {
}
function validateAdUnitPos(adUnit, mediaType) {
- let pos = deepAccess(adUnit, `mediaTypes.${mediaType}.pos`);
+ let pos = adUnit?.mediaTypes?.[mediaType]?.pos;
if (!isNumber(pos) || isNaN(pos) || !isFinite(pos)) {
let warning = `Value of property 'pos' on ad unit ${adUnit.code} should be of type: Number`;
@@ -905,7 +910,7 @@ if (FEATURES.VIDEO) {
*
* @alias module:pbjs.markWinningBidAsUsed
*/
- pbjsInstance.markWinningBidAsUsed = function ({adId, adUnitCode}) {
+ pbjsInstance.markWinningBidAsUsed = function ({adId, adUnitCode, analytics = false}) {
let bids;
if (adUnitCode && adId == null) {
bids = targeting.getWinningBids(adUnitCode);
@@ -915,7 +920,11 @@ if (FEATURES.VIDEO) {
logWarn('Improper use of markWinningBidAsUsed. It needs an adUnitCode or an adId to function.');
}
if (bids.length > 0) {
- auctionManager.addWinningBid(bids[0]);
+ if (analytics) {
+ markWinningBid(bids[0]);
+ } else {
+ auctionManager.addWinningBid(bids[0]);
+ }
markBidAsRendered(bids[0])
}
}
@@ -957,12 +966,12 @@ pbjsInstance.que.push(() => listenMessagesFromCreative());
* by prebid once it's done loading. If it runs after prebid loads, then this monkey-patch causes their
* function to execute immediately.
*
- * @memberof pbjs
* @param {function} command A function which takes no arguments. This is guaranteed to run exactly once, and only after
* the Prebid script has been fully loaded.
* @alias module:pbjs.cmd.push
+ * @alias module:pbjs.que.push
*/
-pbjsInstance.cmd.push = function (command) {
+function quePush(command) {
if (typeof command === 'function') {
try {
command.call();
@@ -972,9 +981,7 @@ pbjsInstance.cmd.push = function (command) {
} else {
logError('Commands written into $$PREBID_GLOBAL$$.cmd.push must be wrapped in a function');
}
-};
-
-pbjsInstance.que.push = pbjsInstance.cmd.push;
+}
function processQueue(queue) {
queue.forEach(function (cmd) {
@@ -993,6 +1000,7 @@ function processQueue(queue) {
* @alias module:pbjs.processQueue
*/
pbjsInstance.processQueue = function () {
+ pbjsInstance.que.push = pbjsInstance.cmd.push = quePush;
insertLocatorFrame();
hook.ready();
processQueue(pbjsInstance.que);
diff --git a/src/targeting.js b/src/targeting.js
index aecc29787c7..1903524984b 100644
--- a/src/targeting.js
+++ b/src/targeting.js
@@ -1,5 +1,5 @@
import { auctionManager } from './auctionManager.js';
-import { getTTL } from './bidTTL.js';
+import { getBufferedTTL } from './bidTTL.js';
import { bidderSettings } from './bidderSettings.js';
import { config } from './config.js';
import {
@@ -48,7 +48,7 @@ export const TARGETING_KEYS_ARR = Object.keys(TARGETING_KEYS).map(
);
// return unexpired bids
-const isBidNotExpired = (bid) => (bid.responseTimestamp + getTTL(bid) * 1000) > timestamp();
+const isBidNotExpired = (bid) => (bid.responseTimestamp + getBufferedTTL(bid) * 1000) > timestamp();
// return bids whose status is not set. Winning bids can only have a status of `rendered`.
const isUnusedBid = (bid) => bid && ((bid.status && !includes([BID_STATUS.RENDERED], bid.status)) || !bid.status);
@@ -207,9 +207,7 @@ export function newTargeting(auctionManager) {
});
};
- function addBidToTargeting(bids, bidderLevelTargetingEnabled = false, deals = false) {
- if (!bidderLevelTargetingEnabled) return [];
-
+ function addBidToTargeting(bids, enableSendAllBids = false, deals = false) {
const standardKeys = FEATURES.NATIVE ? TARGETING_KEYS_ARR.concat(NATIVE_TARGETING_KEYS) : TARGETING_KEYS_ARR.slice();
const allowSendAllBidsTargetingKeys = config.getConfig('targetingControls.allowSendAllBidsTargetingKeys');
@@ -218,7 +216,7 @@ export function newTargeting(auctionManager) {
: standardKeys;
return bids.reduce((result, bid) => {
- if ((!deals || bid.dealId)) {
+ if (enableSendAllBids || (deals && bid.dealId)) {
const targetingValue = getTargetingMap(bid, standardKeys.filter(
key => typeof bid.adserverTargeting[key] !== 'undefined' &&
(deals || allowedSendAllBidTargeting.indexOf(key) !== -1)));
@@ -233,8 +231,8 @@ export function newTargeting(auctionManager) {
function getBidderTargeting(bids) {
const alwaysIncludeDeals = config.getConfig('targetingControls.alwaysIncludeDeals');
- const bidderLevelTargetingEnabled = config.getConfig('enableSendAllBids') || alwaysIncludeDeals;
- return addBidToTargeting(bids, bidderLevelTargetingEnabled, alwaysIncludeDeals);
+ const enableSendAllBids = config.getConfig('enableSendAllBids');
+ return addBidToTargeting(bids, enableSendAllBids, alwaysIncludeDeals);
}
/**
diff --git a/src/utils/gdpr.js b/src/utils/gdpr.js
index 19c7126b7d7..6151861a67b 100644
--- a/src/utils/gdpr.js
+++ b/src/utils/gdpr.js
@@ -1,5 +1,3 @@
-import {deepAccess} from '../utils.js';
-
/**
* Check if GDPR purpose 1 consent was given.
*
@@ -8,7 +6,7 @@ import {deepAccess} from '../utils.js';
*/
export function hasPurpose1Consent(gdprConsent) {
if (gdprConsent?.gdprApplies) {
- return deepAccess(gdprConsent, 'vendorData.purpose.consents.1') === true;
+ return gdprConsent?.vendorData?.purpose?.consents?.[1] === true;
}
return true;
}
diff --git a/src/video.js b/src/video.js
index 9be9adec4c5..a859fcb17bd 100644
--- a/src/video.js
+++ b/src/video.js
@@ -1,4 +1,4 @@
-import {deepAccess, isArrayOfNums, isInteger, isNumber, isPlainObject, isStr, logError, logWarn} from './utils.js';
+import {isArrayOfNums, isInteger, isNumber, isPlainObject, isStr, logError, logWarn} from './utils.js';
import {config} from '../src/config.js';
import {hook} from './hook.js';
import {auctionManager} from './auctionManager.js';
@@ -107,9 +107,9 @@ export function validateOrtbVideoFields(adUnit, onInvalidParam) {
* @return {Boolean} If object is valid
*/
export function isValidVideoBid(bid, {index = auctionManager.index} = {}) {
- const videoMediaType = deepAccess(index.getMediaTypes(bid), 'video');
- const context = videoMediaType && deepAccess(videoMediaType, 'context');
- const useCacheKey = videoMediaType && deepAccess(videoMediaType, 'useCacheKey');
+ const videoMediaType = index.getMediaTypes(bid)?.video;
+ const context = videoMediaType && videoMediaType?.context;
+ const useCacheKey = videoMediaType && videoMediaType?.useCacheKey;
const adUnit = index.getAdUnit(bid);
// if context not defined assume default 'instream' for video bids
diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js
index 6a8a826bec5..72a8940e4d8 100644
--- a/test/spec/auctionmanager_spec.js
+++ b/test/spec/auctionmanager_spec.js
@@ -29,6 +29,7 @@ import { setConfig as setCurrencyConfig } from '../../modules/currency.js';
import { REJECTION_REASON } from '../../src/constants.js';
import { setDocumentHidden } from './unit/utils/focusTimeout_spec.js';
import {sandbox} from 'sinon';
+import {getMinBidCacheTTL, onMinBidCacheTTLChange} from '../../src/bidTTL.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -849,7 +850,23 @@ describe('auctionmanager.js', function () {
await auction.requestsDone;
})
- describe('stale auctions', () => {
+ describe('setConfig(minBidCacheTTL)', () => {
+ it('should update getMinBidCacheTTL', () => {
+ expect(getMinBidCacheTTL()).to.eql(null);
+ config.setConfig({minBidCacheTTL: 123});
+ expect(getMinBidCacheTTL()).to.eql(123);
+ });
+
+ it('should run listeners registered with onMinBidCacheTTLChange', () => {
+ config.setConfig({minBidCacheTTL: 1});
+ let newTTL = null;
+ onMinBidCacheTTLChange((ttl) => { newTTL = ttl; });
+ config.setConfig({minBidCacheTTL: 2});
+ expect(newTTL).to.eql(2);
+ })
+ })
+
+ describe('minBidCacheTTL', () => {
let clock, auction;
beforeEach(() => {
clock = sinon.useFakeTimers();
@@ -861,79 +878,121 @@ describe('auctionmanager.js', function () {
config.resetConfig();
});
- it('are dropped after their last bid becomes stale (if minBidCacheTTL is set)', () => {
- config.setConfig({
- minBidCacheTTL: 0
- });
- bids = [
- {
- adUnitCode: ADUNIT_CODE,
- adUnitId: ADUNIT_CODE,
- ttl: 10
- }, {
- adUnitCode: ADUNIT_CODE,
- adUnitId: ADUNIT_CODE,
- ttl: 100
- }
- ];
- auction.callBids();
- return auction.end.then(() => {
- clock.tick(50 * 1000);
- expect(auctionManager.getBidsReceived().length).to.equal(2);
- clock.tick(56 * 1000);
- expect(auctionManager.getBidsReceived()).to.eql([]);
+ describe('individual bids', () => {
+ beforeEach(() => {
+ bids = [
+ {
+ adUnitCode: ADUNIT_CODE,
+ adUnitId: ADUNIT_CODE,
+ ttl: 10
+ }, {
+ adUnitCode: ADUNIT_CODE,
+ adUnitId: ADUNIT_CODE,
+ ttl: 100
+ }
+ ];
+ })
+ it('are dropped when stale (if minBidCacheTTL is set)', () => {
+ config.setConfig({
+ minBidCacheTTL: 30
+ });
+ auction.callBids();
+ return auction.end.then(() => {
+ clock.tick(20 * 1000);
+ expect(auctionManager.getBidsReceived().length).to.equal(2);
+ clock.tick(50 * 1000);
+ expect(auctionManager.getBidsReceived().length).to.equal(1);
+ });
});
- });
- it('are dropped after `minBidCacheTTL` seconds if they had no bid', () => {
- auction.callBids();
- config.setConfig({
- minBidCacheTTL: 2
- });
- return auction.end.then(() => {
- expect(auctionManager.getNoBids().length).to.eql(1);
- clock.tick(10 * 10000);
- expect(auctionManager.getNoBids().length).to.eql(0);
+ it('pick up updates to minBidCacheTTL that happen during bid lifetime', () => {
+ auction.callBids();
+ return auction.end.then(() => {
+ clock.tick(10 * 1000);
+ config.setConfig({
+ minBidCacheTTL: 20
+ })
+ clock.tick(20 * 1000);
+ expect(auctionManager.getBidsReceived().length).to.equal(1);
+ });
})
- });
+ })
- it('are not dropped after `minBidCacheTTL` seconds if the page was hidden', () => {
- auction.callBids();
- config.setConfig({
- minBidCacheTTL: 10
+ describe('stale auctions', () => {
+ it('are dropped after their last bid becomes stale (if minBidCacheTTL is set)', () => {
+ config.setConfig({
+ minBidCacheTTL: 90
+ });
+ bids = [
+ {
+ adUnitCode: ADUNIT_CODE,
+ adUnitId: ADUNIT_CODE,
+ ttl: 10
+ }, {
+ adUnitCode: ADUNIT_CODE,
+ adUnitId: ADUNIT_CODE,
+ ttl: 100
+ }
+ ];
+ auction.callBids();
+ return auction.end.then(() => {
+ clock.tick(50 * 1000);
+ expect(auctionManager.getBidsReceived().length).to.equal(2);
+ clock.tick(56 * 1000);
+ expect(auctionManager.getBidsReceived()).to.eql([]);
+ });
});
- return auction.end.then(() => {
- expect(auctionManager.getNoBids().length).to.eql(1);
- setDocumentHidden(true);
- clock.tick(10 * 10000);
- setDocumentHidden(false);
- expect(auctionManager.getNoBids().length).to.eql(1);
- })
- });
- Object.entries({
- 'bids': {
- bd: [{
- adUnitCode: ADUNIT_CODE,
- adUnitId: ADUNIT_CODE,
- ttl: 10
- }],
- entries: () => auctionManager.getBidsReceived()
- },
- 'no bids': {
- bd: [],
- entries: () => auctionManager.getNoBids()
- }
- }).forEach(([t, {bd, entries}]) => {
- it(`with ${t} are never dropped if minBidCacheTTL is not set`, () => {
- bids = bd;
+ it('are dropped after `minBidCacheTTL` seconds if they had no bid', () => {
auction.callBids();
+ config.setConfig({
+ minBidCacheTTL: 2
+ });
return auction.end.then(() => {
- clock.tick(100 * 1000);
- expect(entries().length > 0).to.be.true;
+ expect(auctionManager.getNoBids().length).to.eql(1);
+ clock.tick(10 * 10000);
+ expect(auctionManager.getNoBids().length).to.eql(0);
})
- })
- });
+ });
+
+ it('are not dropped after `minBidCacheTTL` seconds if the page was hidden', () => {
+ auction.callBids();
+ config.setConfig({
+ minBidCacheTTL: 10
+ });
+ return auction.end.then(() => {
+ expect(auctionManager.getNoBids().length).to.eql(1);
+ setDocumentHidden(true);
+ clock.tick(10 * 10000);
+ setDocumentHidden(false);
+ expect(auctionManager.getNoBids().length).to.eql(1);
+ })
+ });
+
+ Object.entries({
+ 'bids': {
+ bd: [{
+ adUnitCode: ADUNIT_CODE,
+ adUnitId: ADUNIT_CODE,
+ ttl: 10
+ }],
+ entries: () => auctionManager.getBidsReceived()
+ },
+ 'no bids': {
+ bd: [],
+ entries: () => auctionManager.getNoBids()
+ }
+ }).forEach(([t, {bd, entries}]) => {
+ it(`with ${t} are never dropped if minBidCacheTTL is not set`, () => {
+ bids = bd;
+ auction.callBids();
+ return auction.end.then(() => {
+ clock.tick(100 * 1000);
+ expect(entries().length > 0).to.be.true;
+ })
+ })
+ });
+ })
})
});
@@ -1044,26 +1103,38 @@ describe('auctionmanager.js', function () {
Object.entries({
'on adUnit': () => adUnits[0],
'on bid': () => bidderRequests[0].bids[0],
+ 'on mediatype': () => bidderRequests[0].bids[0].mediaTypes.banner,
}).forEach(([t, getObj]) => {
- it(t, () => {
- let renderer = {
+ let renderer, bid;
+ beforeEach(() => {
+ renderer = {
url: 'renderer.js',
render: (bid) => bid
};
+ })
- let bids1 = Object.assign({},
+ function getBid() {
+ let bid = Object.assign({},
bids[0],
{
bidderCode: BIDDER_CODE,
- mediaType: 'video-outstream',
+ mediaType: 'banner',
}
);
Object.assign(getObj(), {renderer});
- spec.interpretResponse.returns(bids1);
+ spec.interpretResponse.returns(bid);
auction.callBids();
- const addedBid = auction.getBidsReceived().pop();
- assert.equal(addedBid.renderer.url, 'renderer.js');
- })
+ return auction.getBidsReceived().pop();
+ }
+
+ it(t, () => {
+ expect(getBid().renderer.url).to.eql('renderer.js');
+ });
+
+ it('allows renderers without URL', () => {
+ delete renderer.url;
+ expect(getBid().renderer.renderNow).to.be.true;
+ });
})
})
@@ -1431,10 +1502,10 @@ describe('auctionmanager.js', function () {
it('should not alter bid requestID', function () {
auction.callBids();
-
- const addedBid2 = auction.getBidsReceived().pop();
+ const bidsReceived = auction.getBidsReceived();
+ const addedBid2 = bidsReceived.pop();
assert.equal(addedBid2.requestId, bids1[0].requestId);
- const addedBid1 = auction.getBidsReceived().pop();
+ const addedBid1 = bidsReceived.pop();
assert.equal(addedBid1.requestId, bids[0].requestId);
});
diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js
index b54f207e6a9..9aa4f42da48 100644
--- a/test/spec/config_spec.js
+++ b/test/spec/config_spec.js
@@ -345,6 +345,14 @@ describe('config API', function () {
expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig);
});
+ it('sets auctionOptions suppressExpiredRender', function () {
+ const auctionOptionsConfig = {
+ 'suppressExpiredRender': true
+ }
+ setConfig({ auctionOptions: auctionOptionsConfig });
+ expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig);
+ });
+
it('should log warning for the wrong value passed to auctionOptions', function () {
setConfig({ auctionOptions: '' });
expect(logWarnSpy.calledOnce).to.equal(true);
@@ -370,6 +378,15 @@ describe('config API', function () {
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
});
+ it('should log warning for invalid auctionOptions suppress expired render', function () {
+ setConfig({ auctionOptions: {
+ 'suppressExpiredRender': 'test',
+ }});
+ expect(logWarnSpy.calledOnce).to.equal(true);
+ const warning = 'Auction Options suppressExpiredRender must be of type boolean';
+ assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
+ });
+
it('should log warning for invalid properties to auctionOptions', function () {
setConfig({ auctionOptions: {
'testing': true
diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js
index cec6597f2d4..5bf1dbc22a4 100644
--- a/test/spec/fpd/enrichment_spec.js
+++ b/test/spec/fpd/enrichment_spec.js
@@ -222,7 +222,7 @@ describe('FPD enrichment', () => {
it('is set if globalPrivacyControl is set', () => {
win.navigator.globalPrivacyControl = true;
return fpd().then(ortb2 => {
- expect(ortb2.regs.ext.gpc).to.eql(1);
+ expect(ortb2.regs.ext.gpc).to.eql('1');
});
});
diff --git a/test/spec/libraries/mspa/activityControls_spec.js b/test/spec/libraries/mspa/activityControls_spec.js
index f232dc2563f..80d9fc500b1 100644
--- a/test/spec/libraries/mspa/activityControls_spec.js
+++ b/test/spec/libraries/mspa/activityControls_spec.js
@@ -181,13 +181,18 @@ describe('mspaRule', () => {
expect(mkRule()().allow).to.equal(false);
});
+ it('should deny when consent is using version != 1', () => {
+ consent = {Version: 2};
+ expect(mkRule()().allow).to.equal(false);
+ })
+
Object.entries({
'denies': true,
'allows': false
}).forEach(([t, denied]) => {
it(`should check if deny fn ${t}`, () => {
denies.returns(denied);
- consent = {mock: 'value'};
+ consent = {mock: 'value', Version: 1};
const result = mkRule()();
sinon.assert.calledWith(denies, consent);
if (denied) {
@@ -212,6 +217,7 @@ describe('setupRules', () => {
parsedSections: {
mockApi: [
{
+ Version: 1,
mock: 'consent'
}
]
@@ -226,9 +232,16 @@ describe('setupRules', () => {
it('should use flatten section data for the given api', () => {
runSetup('mockApi', [1]);
expect(isAllowed('mockActivity', {})).to.equal(false);
- sinon.assert.calledWith(rules.mockActivity, {mock: 'consent'})
+ sinon.assert.calledWith(rules.mockActivity, consent.parsedSections.mockApi[0])
});
+ it('should accept already flattened section data', () => {
+ consent.parsedSections.mockApi = {flat: 'consent', Version: 1};
+ runSetup('mockApi', [1]);
+ isAllowed('mockActivity', {});
+ sinon.assert.calledWith(rules.mockActivity, consent.parsedSections.mockApi)
+ })
+
it('should not choke when no consent data is available', () => {
consent = null;
runSetup('mockApi', [1]);
@@ -241,11 +254,11 @@ describe('setupRules', () => {
});
it('should pass flattened consent through normalizeConsent', () => {
- const normalize = sinon.stub().returns({normalized: 'consent'})
+ const normalize = sinon.stub().returns({normalized: 'consent', Version: 1})
runSetup('mockApi', [1], normalize);
expect(isAllowed('mockActivity', {})).to.equal(false);
- sinon.assert.calledWith(normalize, {mock: 'consent'});
- sinon.assert.calledWith(rules.mockActivity, {normalized: 'consent'});
+ sinon.assert.calledWith(normalize, {mock: 'consent', Version: 1});
+ sinon.assert.calledWith(rules.mockActivity, {normalized: 'consent', Version: 1});
});
it('should return a function that unregisters activity controls', () => {
diff --git a/test/spec/libraries/vastTrackers_spec.js b/test/spec/libraries/vastTrackers_spec.js
index c336eec0321..3e8a8456e9c 100644
--- a/test/spec/libraries/vastTrackers_spec.js
+++ b/test/spec/libraries/vastTrackers_spec.js
@@ -4,38 +4,74 @@ import {
getVastTrackers,
insertVastTrackers,
registerVastTrackers,
- reset
+ reset, responseHook,
+ disable
} from 'libraries/vastTrackers/vastTrackers.js';
import {MODULE_TYPE_ANALYTICS} from '../../../src/activities/modules.js';
+import {AuctionIndex} from '../../../src/auctionIndex.js';
describe('vast trackers', () => {
+ let sandbox, tracker, auction, bid, bidRequest, index;
beforeEach(() => {
- registerVastTrackers(MODULE_TYPE_ANALYTICS, 'test', function(bidResponse) {
+ bid = {
+ requestId: 'bid',
+ cpm: 1.0,
+ auctionId: 'aid',
+ mediaType: 'video',
+ }
+ bidRequest = {
+ auctionId: 'aid',
+ bidId: 'bid',
+ }
+ auction = {
+ getAuctionId() {
+ return 'aid';
+ },
+ getProperties() {
+ return {auction: 'props'};
+ },
+ getBidRequests() {
+ return [{bids: [bidRequest]}]
+ }
+ };
+ sandbox = sinon.sandbox.create();
+ index = new AuctionIndex(() => [auction]);
+ tracker = sinon.stub().callsFake(function (bidResponse) {
return [
{'event': 'impressions', 'url': `https://vasttracking.mydomain.com/vast?cpm=${bidResponse.cpm}`}
];
});
+ registerVastTrackers(MODULE_TYPE_ANALYTICS, 'test', tracker);
})
afterEach(() => {
reset();
+ sandbox.restore();
});
- it('insert into tracker list', function() {
- const trackers = getVastTrackers({'cpm': 1.0});
+ after(disable);
+
+ it('insert into tracker list', function () {
+ const trackers = getVastTrackers(bid, {index});
expect(trackers).to.be.a('map');
expect(trackers.get('impressions')).to.exists;
expect(trackers.get('impressions').has('https://vasttracking.mydomain.com/vast?cpm=1')).to.be.true;
});
- it('insert trackers in vastXml', function() {
- const trackers = getVastTrackers({'cpm': 1.0});
+ it('insert trackers in vastXml', function () {
+ const trackers = getVastTrackers(bid, {index});
let vastXml = '';
vastXml = insertVastTrackers(trackers, vastXml);
expect(vastXml).to.equal('');
});
- it('test addImpUrlToTrackers', function() {
- const trackers = addImpUrlToTrackers({'vastImpUrl': 'imptracker.com'}, getVastTrackers({'cpm': 1.0}));
+ it('should pass request and auction properties to trackerFn', () => {
+ const bid = {requestId: 'bid', auctionId: 'aid'};
+ getVastTrackers(bid, {index});
+ sinon.assert.calledWith(tracker, bid, sinon.match({auction: auction.getProperties(), bidRequest}))
+ })
+
+ it('test addImpUrlToTrackers', function () {
+ const trackers = addImpUrlToTrackers({'vastImpUrl': 'imptracker.com'}, getVastTrackers(bid, {index}));
expect(trackers).to.be.a('map');
expect(trackers.get('impressions')).to.exists;
expect(trackers.get('impressions').has('imptracker.com')).to.be.true;
@@ -43,12 +79,8 @@ describe('vast trackers', () => {
if (FEATURES.VIDEO) {
it('should add trackers to bid response', () => {
- const bidResponse = {
- mediaType: 'video',
- cpm: 1
- }
- addTrackersToResponse(sinon.stub(), 'au', bidResponse);
- expect(bidResponse.vastImpUrl).to.eql([
+ responseHook({index})(sinon.stub(), 'au', bid);
+ expect(bid.vastImpUrl).to.eql([
'https://vasttracking.mydomain.com/vast?cpm=1'
])
});
diff --git a/test/spec/modules/51DegreesRtdProvider_spec.js b/test/spec/modules/51DegreesRtdProvider_spec.js
index 4847587c7d3..7b60a08b906 100644
--- a/test/spec/modules/51DegreesRtdProvider_spec.js
+++ b/test/spec/modules/51DegreesRtdProvider_spec.js
@@ -160,16 +160,114 @@ describe('51DegreesRtdProvider', function() {
});
describe('get51DegreesJSURL', function() {
+ const hev = {
+ 'brands': [
+ {
+ 'brand': 'Chromium',
+ 'version': '130'
+ },
+ {
+ 'brand': 'Google Chrome',
+ 'version': '130'
+ },
+ {
+ 'brand': 'Not?A_Brand',
+ 'version': '99'
+ }
+ ],
+ 'fullVersionList': [
+ {
+ 'brand': 'Chromium',
+ 'version': '130.0.6723.92'
+ },
+ {
+ 'brand': 'Google Chrome',
+ 'version': '130.0.6723.92'
+ },
+ {
+ 'brand': 'Not?A_Brand',
+ 'version': '99.0.0.0'
+ }
+ ],
+ 'mobile': false,
+ 'model': '',
+ 'platform': 'macOS',
+ 'platformVersion': '14.6.1'
+ };
+ const mockWindow = {
+ ...window,
+ screen: {
+ height: 1117,
+ width: 1728,
+ },
+ devicePixelRatio: 2,
+ };
+
it('returns the cloud URL if the resourceKey is provided', function() {
const config = {resourceKey: 'TEST_RESOURCE_KEY'};
- expect(get51DegreesJSURL(config)).to.equal(
- 'https://cloud.51degrees.com/api/v4/TEST_RESOURCE_KEY.js'
+ expect(get51DegreesJSURL(config, mockWindow)).to.equal(
+ 'https://cloud.51degrees.com/api/v4/TEST_RESOURCE_KEY.js?' +
+ `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` +
+ `51D_ScreenPixelsWidth=${mockWindow.screen.width}&` +
+ `51D_PixelRatio=${mockWindow.devicePixelRatio}`
+ );
+ });
+
+ it('returns the on-premise URL if the onPremiseJSUrl is provided', function () {
+ const config = {onPremiseJSUrl: 'https://example.com/51Degrees.core.js'};
+ expect(get51DegreesJSURL(config, mockWindow)).to.equal(
+ `https://example.com/51Degrees.core.js?` +
+ `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` +
+ `51D_ScreenPixelsWidth=${mockWindow.screen.width}&` +
+ `51D_PixelRatio=${mockWindow.devicePixelRatio}`
+ );
+ });
+
+ it('doesn\'t override static query string parameters', function () {
+ const config = {onPremiseJSUrl: 'https://example.com/51Degrees.core.js?test=1'};
+ expect(get51DegreesJSURL(config, mockWindow)).to.equal(
+ `https://example.com/51Degrees.core.js?test=1&` +
+ `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` +
+ `51D_ScreenPixelsWidth=${mockWindow.screen.width}&` +
+ `51D_PixelRatio=${mockWindow.devicePixelRatio}`
+ );
+ });
+
+ it('adds high entropy values to the query string, if available', async function () {
+ const config = {
+ onPremiseJSUrl: 'https://example.com/51Degrees.core.js',
+ hev,
+ };
+ expect(get51DegreesJSURL(config, mockWindow)).to.equal(
+ `https://example.com/51Degrees.core.js?` +
+ `51D_GetHighEntropyValues=${btoa(JSON.stringify(hev))}&` +
+ `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` +
+ `51D_ScreenPixelsWidth=${mockWindow.screen.width}&` +
+ `51D_PixelRatio=${mockWindow.devicePixelRatio}`
);
});
- it('returns the on-premise URL if the onPremiseJSUrl is provided', function() {
+ it('doesn\'t add high entropy values to the query string if object is empty', function () {
+ const config = {
+ onPremiseJSUrl: 'https://example.com/51Degrees.core.js',
+ hev: {},
+ };
+ expect(get51DegreesJSURL(config, mockWindow)).to.equal(
+ `https://example.com/51Degrees.core.js?` +
+ `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` +
+ `51D_ScreenPixelsWidth=${mockWindow.screen.width}&` +
+ `51D_PixelRatio=${mockWindow.devicePixelRatio}`
+ );
+ });
+
+ it('keeps the original URL if none of the additional parameters are available', function () {
+ // delete screen and devicePixelRatio properties to test the case when they are not available
+ delete mockWindow.screen;
+ delete mockWindow.devicePixelRatio;
+
const config = {onPremiseJSUrl: 'https://example.com/51Degrees.core.js'};
- expect(get51DegreesJSURL(config)).to.equal('https://example.com/51Degrees.core.js');
+ expect(get51DegreesJSURL(config, mockWindow)).to.equal('https://example.com/51Degrees.core.js');
+ expect(get51DegreesJSURL(config, window)).to.not.equal('https://example.com/51Degrees.core.js');
});
});
diff --git a/test/spec/modules/a1MediaBidAdapter_spec.js b/test/spec/modules/a1MediaBidAdapter_spec.js
index e1db2b9ad8d..8031b584d65 100644
--- a/test/spec/modules/a1MediaBidAdapter_spec.js
+++ b/test/spec/modules/a1MediaBidAdapter_spec.js
@@ -75,7 +75,8 @@ const getConvertedBidReq = () => {
},
bidfloor: 0,
bidfloorcur: 'JPY',
- id: '2e9f38ea93bb9e'
+ id: '2e9f38ea93bb9e',
+ secure: 1
}
],
test: 0,
diff --git a/test/spec/modules/adagioAnalyticsAdapter_spec.js b/test/spec/modules/adagioAnalyticsAdapter_spec.js
index 329aa980caf..d1058170f44 100644
--- a/test/spec/modules/adagioAnalyticsAdapter_spec.js
+++ b/test/spec/modules/adagioAnalyticsAdapter_spec.js
@@ -287,141 +287,192 @@ const AUCTION_INIT_ANOTHER = {
},
} ],
'adUnitCodes': ['/19968336/header-bid-tag-1', '/19968336/footer-bid-tag-1'],
- 'bidderRequests': [ {
- 'bidderCode': 'another',
- 'auctionId': AUCTION_ID,
- 'bidderRequestId': '1be65d7958826a',
- 'bids': [ {
- 'bidder': 'another',
- 'params': {
- 'publisherId': '1001',
- },
- 'mediaTypes': {
- 'banner': {
- 'sizes': [[640, 480]]
- }
- },
- 'adUnitCode': '/19968336/header-bid-tag-1',
- 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
- 'sizes': [[640, 480]],
- 'bidId': '2ecff0db240757',
- 'bidderRequestId': '1be65d7958826a',
+ 'bidderRequests': [
+ {
+ 'bidderCode': 'another',
'auctionId': AUCTION_ID,
- 'src': 'client',
- 'bidRequestsCount': 1
- }, {
- 'bidder': 'another',
- 'params': {
- 'publisherId': '1001'
+ 'bidderRequestId': '1be65d7958826a',
+ 'bids': [
+ {
+ 'bidder': 'another',
+ 'params': {
+ 'publisherId': '1001',
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[640, 480]]
+ }
+ },
+ 'adUnitCode': '/19968336/header-bid-tag-1',
+ 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
+ 'sizes': [[640, 480]],
+ 'bidId': '2ecff0db240757',
+ 'bidderRequestId': '1be65d7958826a',
+ 'auctionId': AUCTION_ID,
+ 'src': 'client',
+ 'bidRequestsCount': 1
+ },
+ {
+ 'bidder': 'another',
+ 'params': {
+ 'publisherId': '1001'
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[640, 480]]
+ }
+ },
+ 'adUnitCode': '/19968336/footer-bid-tag-1',
+ 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
+ 'sizes': [[640, 480]],
+ 'bidId': '2ecff0db240757',
+ 'bidderRequestId': '1be65d7958826a',
+ 'auctionId': AUCTION_ID,
+ 'src': 'client',
+ 'bidRequestsCount': 1
+ },
+ ],
+ 'timeout': 3000,
+ 'refererInfo': {
+ 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html']
},
- 'mediaTypes': {
- 'banner': {
- 'sizes': [[640, 480]]
+ 'ortb2': {
+ 'site': {
+ 'ext': {
+ 'data': {
+ 'adg_rtd': {
+ ...ADG_RTD
+ },
+ ...ORTB_DATA
+ }
+ }
}
- },
- 'adUnitCode': '/19968336/footer-bid-tag-1',
- 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
- 'sizes': [[640, 480]],
- 'bidId': '2ecff0db240757',
- 'bidderRequestId': '1be65d7958826a',
+ }
+ },
+ {
+ 'bidderCode': 'nobid',
'auctionId': AUCTION_ID,
- 'src': 'client',
- 'bidRequestsCount': 1
- }, {
- 'bidder': 'nobid',
- 'params': {
- 'publisherId': '1001'
+ 'bidderRequestId': '1be65d7958826a',
+ 'bids': [{
+ 'bidder': 'nobid',
+ 'params': {
+ 'publisherId': '1001'
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[640, 480]]
+ }
+ },
+ 'adUnitCode': '/19968336/header-bid-tag-1',
+ 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
+ 'sizes': [[640, 480]],
+ 'bidId': '2ecff0db240757',
+ 'bidderRequestId': '1be65d7958826a',
+ 'auctionId': AUCTION_ID,
+ 'src': 'client',
+ 'bidRequestsCount': 1
+ }
+ ],
+ 'timeout': 3000,
+ 'refererInfo': {
+ 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html']
},
- 'mediaTypes': {
- 'banner': {
- 'sizes': [[640, 480]]
+ 'ortb2': {
+ 'site': {
+ 'ext': {
+ 'data': {
+ 'adg_rtd': {
+ ...ADG_RTD
+ },
+ ...ORTB_DATA
+ }
+ }
}
- },
- 'adUnitCode': '/19968336/footer-bid-tag-1',
- 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
- 'sizes': [[640, 480]],
- 'bidId': '2ecff0db240757',
- 'bidderRequestId': '1be65d7958826a',
+ }
+ },
+ {
+ bidderCode: 'anotherWithAlias',
'auctionId': AUCTION_ID,
- 'src': 'client',
- 'bidRequestsCount': 1
- }, {
- 'bidder': 'anotherWithAlias',
- 'params': {
- 'publisherId': '1001',
+ 'bidderRequestId': '1be65d7958826a',
+ 'bids': [
+ {
+ 'bidder': 'anotherWithAlias',
+ 'params': {
+ 'publisherId': '1001',
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[640, 480]]
+ }
+ },
+ 'adUnitCode': '/19968336/header-bid-tag-1',
+ 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
+ 'sizes': [[640, 480]],
+ 'bidId': '2ecff0db240757',
+ 'bidderRequestId': '1be65d7958826a',
+ 'auctionId': AUCTION_ID,
+ 'src': 'client',
+ 'bidRequestsCount': 1
+ },
+ ],
+ 'timeout': 3000,
+ 'refererInfo': {
+ 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html']
},
- 'mediaTypes': {
- 'banner': {
- 'sizes': [[640, 480]]
+ 'ortb2': {
+ 'site': {
+ 'ext': {
+ 'data': {
+ 'adg_rtd': {
+ ...ADG_RTD
+ },
+ ...ORTB_DATA
+ }
+ }
}
- },
- 'adUnitCode': '/19968336/header-bid-tag-1',
- 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
- 'sizes': [[640, 480]],
- 'bidId': '2ecff0db240757',
- 'bidderRequestId': '1be65d7958826a',
- 'auctionId': AUCTION_ID,
- 'src': 'client',
- 'bidRequestsCount': 1
- },
- ],
- 'timeout': 3000,
- 'refererInfo': {
- 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html']
+ }
},
- 'ortb2': {
- 'site': {
- 'ext': {
- 'data': {
- 'adg_rtd': {
- ...ADG_RTD
- },
- ...ORTB_DATA
+ {
+ 'bidderCode': 'adagio',
+ 'auctionId': AUCTION_ID,
+ 'bidderRequestId': '1be65d7958826a',
+ 'bids': [ {
+ 'bidder': 'adagio',
+ 'params': {
+ ...PARAMS_ADG
+ },
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[640, 480]]
}
- }
+ },
+ 'adUnitCode': '/19968336/header-bid-tag-1',
+ 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
+ 'sizes': [[640, 480]],
+ 'bidId': '2ecff0db240757',
+ 'bidderRequestId': '1be65d7958826a',
+ 'auctionId': AUCTION_ID,
+ 'src': 'client',
+ 'bidRequestsCount': 1
}
- }
- }, {
- 'bidderCode': 'adagio',
- 'auctionId': AUCTION_ID,
- 'bidderRequestId': '1be65d7958826a',
- 'bids': [ {
- 'bidder': 'adagio',
- 'params': {
- ...PARAMS_ADG
+ ],
+ 'timeout': 3000,
+ 'refererInfo': {
+ 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html']
},
- 'mediaTypes': {
- 'banner': {
- 'sizes': [[640, 480]]
- }
- },
- 'adUnitCode': '/19968336/header-bid-tag-1',
- 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014',
- 'sizes': [[640, 480]],
- 'bidId': '2ecff0db240757',
- 'bidderRequestId': '1be65d7958826a',
- 'auctionId': AUCTION_ID,
- 'src': 'client',
- 'bidRequestsCount': 1
- }
- ],
- 'timeout': 3000,
- 'refererInfo': {
- 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html']
- },
- 'ortb2': {
- 'site': {
- 'ext': {
- 'data': {
- 'adg_rtd': {
- ...ADG_RTD
- },
- ...ORTB_DATA
+ 'ortb2': {
+ 'site': {
+ 'ext': {
+ 'data': {
+ 'adg_rtd': {
+ ...ADG_RTD
+ },
+ ...ORTB_DATA
+ }
}
}
}
}
- }
],
'bidsReceived': [],
'winningBids': [],
diff --git a/test/spec/modules/adagioRtdProvider_spec.js b/test/spec/modules/adagioRtdProvider_spec.js
index 6dbe4b34985..64c006fbc3f 100644
--- a/test/spec/modules/adagioRtdProvider_spec.js
+++ b/test/spec/modules/adagioRtdProvider_spec.js
@@ -121,7 +121,8 @@ describe('Adagio Rtd Provider', function () {
id: 'uid-1234',
rnd: 0.5697,
vwSmplg: 0.1,
- vwSmplgNxt: 0.1
+ vwSmplgNxt: 0.1,
+ pages: 1
};
it('store new session data for further usage', function () {
@@ -139,7 +140,8 @@ describe('Adagio Rtd Provider', function () {
session: {
new: true,
id: utils.generateUUID(),
- rnd: Math.random()
+ rnd: Math.random(),
+ pages: 1,
}
}
@@ -211,7 +213,8 @@ describe('Adagio Rtd Provider', function () {
vwSmplgNxt: 0.1,
testName: 'adg-test',
testVersion: 'srv',
- initiator: 'snippet'
+ initiator: 'snippet',
+ pages: 1
};
it('store new session data instancied by the AB Test snippet for further usage', function () {
@@ -591,7 +594,8 @@ describe('Adagio Rtd Provider', function () {
'new': true,
'rnd': 0.020644826280300954,
'vwSmplg': 0.1,
- 'vwSmplgNxt': 0.1
+ 'vwSmplgNxt': 0.1,
+ 'pages': 1
}
}
}
@@ -617,7 +621,8 @@ describe('Adagio Rtd Provider', function () {
'new': true,
'rnd': 0.020644826280300954,
'vwSmplg': 0.1,
- 'vwSmplgNxt': 0.1
+ 'vwSmplgNxt': 0.1,
+ 'pages': 1
}
}
}
diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js
index 6f4395d548f..14abb33ba68 100644
--- a/test/spec/modules/adfBidAdapter_spec.js
+++ b/test/spec/modules/adfBidAdapter_spec.js
@@ -3,7 +3,8 @@
import { assert } from 'chai';
import { spec } from 'modules/adfBidAdapter.js';
import { config } from 'src/config.js';
-import { createEidsArray } from 'modules/userId/eids.js';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
describe('Adf adapter', function () {
let bids = [];
@@ -332,12 +333,15 @@ describe('Adf adapter', function () {
});
it('should send currency if defined', function () {
- config.setConfig({ currency: { adServerCurrency: 'EUR' } });
let validBidRequests = [{ params: {} }];
let refererInfo = { page: 'page' };
- let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data);
-
- assert.deepEqual(request.cur, [ 'EUR' ]);
+ const bidderRequest = { refererInfo };
+ setCurrencyConfig({ adServerCurrency: 'EUR' })
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ let request = JSON.parse(spec.buildRequests(validBidRequests, res).data);
+ assert.deepEqual(request.cur, [ 'EUR' ]);
+ setCurrencyConfig({});
+ });
});
it('should pass supply chain object', function () {
@@ -480,12 +484,14 @@ describe('Adf adapter', function () {
});
it('should request floor price in adserver currency', function () {
- config.setConfig({ currency: { adServerCurrency: 'DKK' } });
+ setCurrencyConfig({ adServerCurrency: 'DKK' })
const validBidRequests = [ getBidWithFloor() ];
- let imp = getRequestImps(validBidRequests)[0];
-
- assert.equal(imp.bidfloor, undefined);
- assert.equal(imp.bidfloorcur, 'DKK');
+ return addFPDToBidderRequest(validBidRequests[0]).then(res => {
+ const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ...res }).data).imp[0];
+ assert.equal(imp.bidfloor, undefined);
+ assert.equal(imp.bidfloorcur, 'DKK');
+ setCurrencyConfig({});
+ });
});
it('should add correct floor values', function () {
@@ -505,30 +511,29 @@ describe('Adf adapter', function () {
playerSize: [ 100, 200 ]
} };
const expectedFloors = [ 1, 1.3, 0.5 ];
- config.setConfig({ currency: { adServerCurrency: 'DKK' } });
+ setCurrencyConfig({ adServerCurrency: 'DKK' });
let validBidRequests = expectedFloors.map(getBidWithFloorTest);
- getRequestImps(validBidRequests);
- assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
+ return addFPDToBidderRequest(validBidRequests[0]).then(res => {
+ getRequestImps(validBidRequests, res);
+ assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' })
+ mediaTypes = { banner: {
+ sizes: [ [100, 200], [300, 400] ]
+ }};
+ getRequestImps(validBidRequests, res);
- mediaTypes = { banner: {
- sizes: [ [100, 200], [300, 400] ]
- }};
- validBidRequests = expectedFloors.map(getBidWithFloorTest);
- getRequestImps(validBidRequests);
+ assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
- assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
+ mediaTypes = { native: {} };
+ getRequestImps(validBidRequests, res);
- mediaTypes = { native: {} };
- validBidRequests = expectedFloors.map(getBidWithFloorTest);
- getRequestImps(validBidRequests);
+ assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
- assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
+ mediaTypes = {};
+ getRequestImps(validBidRequests, res);
- mediaTypes = {};
- validBidRequests = expectedFloors.map(getBidWithFloorTest);
- getRequestImps(validBidRequests);
-
- assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
+ assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' });
+ setCurrencyConfig({});
+ });
function getBidWithFloorTest(floor) {
return {
@@ -913,8 +918,8 @@ describe('Adf adapter', function () {
});
});
- function getRequestImps(validBidRequests) {
- return JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp;
+ function getRequestImps(validBidRequests, enriched = {}) {
+ return JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ...enriched }).data).imp;
}
});
diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js
index 7a95d4272fb..c0bb40a1bf2 100644
--- a/test/spec/modules/adgenerationBidAdapter_spec.js
+++ b/test/spec/modules/adgenerationBidAdapter_spec.js
@@ -4,6 +4,8 @@ import {newBidder} from 'src/adapters/bidderFactory.js';
import {NATIVE} from 'src/mediaTypes.js';
import {config} from 'src/config.js';
import prebid from '../../../package.json';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
describe('AdgenerationAdapter', function () {
const adapter = newBidder(spec);
@@ -248,19 +250,20 @@ describe('AdgenerationAdapter', function () {
config.resetConfig();
});
it('allows setConfig to set bidder currency for USD', function () {
- config.setConfig({
- currency: {
- adServerCurrency: 'USD'
- }
+ setCurrencyConfig({ adServerCurrency: 'USD' });
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ const bidRequest = spec.buildRequests(bidRequests, res)[0];
+ expect(bidRequest.data).to.equal(data.bannerUSD);
+ setCurrencyConfig({});
});
- const request = spec.buildRequests(bidRequests, bidderRequest)[0];
- expect(request.data).to.equal(data.bannerUSD);
- config.resetConfig();
});
});
describe('interpretResponse', function () {
const bidRequests = {
banner: {
+ bidderRequest: {
+ ortb2: {ext: {prebid: {adServerCurrency: 'JPY'}}}
+ },
bidRequest: {
bidder: 'adg',
params: {
@@ -275,6 +278,9 @@ describe('AdgenerationAdapter', function () {
},
},
native: {
+ bidderRequest: {
+ ortb2: {ext: {prebid: {adServerCurrency: 'JPY'}}}
+ },
bidRequest: {
bidder: 'adg',
params: {
@@ -312,6 +318,9 @@ describe('AdgenerationAdapter', function () {
},
},
upperBillboard: {
+ bidderRequest: {
+ ortb2: {ext: {prebid: {adServerCurrency: 'JPY'}}}
+ },
bidRequest: {
bidder: 'adg',
params: {
@@ -916,21 +925,26 @@ describe('AdgenerationAdapter', function () {
});
it('handles ADGBrowserM responses', function () {
- config.setConfig({
- currency: {
- adServerCurrency: 'JPY'
+ setCurrencyConfig({ adServerCurrency: 'JPY' });
+ const bidderRequest = {
+ refererInfo: {
+ page: 'https://example.com'
}
+ };
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ spec.buildRequests(bidRequests, res)[0];
+ const result = spec.interpretResponse({body: serverResponse.normal.upperBillboard}, { ...bidRequests.upperBillboard, bidderRequest: res })[0];
+ expect(result.requestId).to.equal(bidResponses.normal.upperBillboard.requestId);
+ expect(result.width).to.equal(bidResponses.normal.upperBillboard.width);
+ expect(result.height).to.equal(bidResponses.normal.upperBillboard.height);
+ expect(result.creativeId).to.equal(bidResponses.normal.upperBillboard.creativeId);
+ expect(result.dealId).to.equal(bidResponses.normal.upperBillboard.dealId);
+ expect(result.currency).to.equal(bidResponses.normal.upperBillboard.currency);
+ expect(result.netRevenue).to.equal(bidResponses.normal.upperBillboard.netRevenue);
+ expect(result.ttl).to.equal(bidResponses.normal.upperBillboard.ttl);
+ expect(result.ad).to.equal(bidResponses.normal.upperBillboard.ad);
+ setCurrencyConfig({});
});
- const result = spec.interpretResponse({body: serverResponse.normal.upperBillboard}, bidRequests.upperBillboard)[0];
- expect(result.requestId).to.equal(bidResponses.normal.upperBillboard.requestId);
- expect(result.width).to.equal(bidResponses.normal.upperBillboard.width);
- expect(result.height).to.equal(bidResponses.normal.upperBillboard.height);
- expect(result.creativeId).to.equal(bidResponses.normal.upperBillboard.creativeId);
- expect(result.dealId).to.equal(bidResponses.normal.upperBillboard.dealId);
- expect(result.currency).to.equal(bidResponses.normal.upperBillboard.currency);
- expect(result.netRevenue).to.equal(bidResponses.normal.upperBillboard.netRevenue);
- expect(result.ttl).to.equal(bidResponses.normal.upperBillboard.ttl);
- expect(result.ad).to.equal(bidResponses.normal.upperBillboard.ad);
});
it('handles banner responses for empty adomain', function () {
diff --git a/test/spec/modules/adgridBidAdapter_spec.js b/test/spec/modules/adgridBidAdapter_spec.js
index f7da18403fd..0d7ad9c245d 100644
--- a/test/spec/modules/adgridBidAdapter_spec.js
+++ b/test/spec/modules/adgridBidAdapter_spec.js
@@ -20,6 +20,22 @@ describe('AdGrid Bid Adapter', function () {
}
}];
+ const videoRequest = [{
+ bidId: 123456,
+ auctionId: 98765,
+ mediaTypes: {
+ video: {
+ playerSize: [
+ [640, 480]
+ ],
+ context: 'instream'
+ }
+ },
+ params: {
+ domainId: 12345
+ }
+ }];
+
describe('isBidRequestValid', function () {
it('Should return true when domainId exist inside params object', function () {
const isBidValid = spec.isBidRequestValid(bannerRequest[0]);
@@ -34,6 +50,7 @@ describe('AdGrid Bid Adapter', function () {
describe('buildRequests', function () {
const request = spec.buildRequests(bannerRequest, bannerRequest[0]);
+ const requestVideo = spec.buildRequests(videoRequest, videoRequest[0]);
const payload = request.data;
const apiURL = request.url;
const method = request.method;
@@ -50,9 +67,23 @@ describe('AdGrid Bid Adapter', function () {
expect(apiURL).to.equal(globalConfig.endPoint);
});
- it('Test the API Method', function () {
+ it('should send the correct method', function () {
expect(method).to.equal(globalConfig.method);
});
+
+ it('should send the correct requestId', function () {
+ expect(request.data.bids[0].requestId).to.equal(bannerRequest[0].bidId);
+ expect(requestVideo.data.bids[0].requestId).to.equal(videoRequest[0].bidId);
+ });
+
+ it('should send the correct sizes array', function () {
+ expect(request.data.bids[0].sizes).to.be.an('array');
+ });
+
+ it('should send the correct media type', function () {
+ expect(request.data.bids[0].mediaType).to.equal('banner')
+ expect(requestVideo.data.bids[0].mediaType).to.equal('video')
+ });
});
describe('interpretResponse', function () {
diff --git a/test/spec/modules/adpartnerBidAdapter_spec.js b/test/spec/modules/adpartnerBidAdapter_spec.js
index d9f9b0d0074..597974acce8 100644
--- a/test/spec/modules/adpartnerBidAdapter_spec.js
+++ b/test/spec/modules/adpartnerBidAdapter_spec.js
@@ -1,6 +1,7 @@
import {expect} from 'chai';
import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/adpartnerBidAdapter.js';
import {newBidder} from 'src/adapters/bidderFactory.js';
+import * as miUtils from 'libraries/mediaImpactUtils/index.js';
const BIDDER_CODE = 'adpartner';
@@ -117,7 +118,7 @@ describe('AdpartnerAdapter', function () {
describe('joinSizesToString', function () {
it('success convert sizes list to string', function () {
- const sizesStr = spec.joinSizesToString([[300, 250], [300, 600]]);
+ const sizesStr = miUtils.joinSizesToString([[300, 250], [300, 600]]);
expect(sizesStr).to.equal('300x250|300x600');
});
});
@@ -238,7 +239,7 @@ describe('AdpartnerAdapter', function () {
let ajaxStub;
beforeEach(() => {
- ajaxStub = sinon.stub(spec, 'postRequest')
+ ajaxStub = sinon.stub(miUtils, 'postRequest')
})
afterEach(() => {
diff --git a/test/spec/modules/adverxoBidAdapter_spec.js b/test/spec/modules/adverxoBidAdapter_spec.js
new file mode 100644
index 00000000000..e3b98d49f07
--- /dev/null
+++ b/test/spec/modules/adverxoBidAdapter_spec.js
@@ -0,0 +1,725 @@
+import {expect} from 'chai';
+import {spec} from 'modules/adverxoBidAdapter.js';
+import {config} from 'src/config';
+
+describe('Adverxo Bid Adapter', () => {
+ function makeBidRequestWithParams(params) {
+ return {
+ bidId: '2e9f38ea93bb9e',
+ bidder: 'adverxo',
+ adUnitCode: 'adunit-code',
+ mediaTypes: {banner: {sizes: [[300, 250]]}},
+ params: params,
+ bidderRequestId: 'test-bidder-request-id'
+ };
+ }
+
+ const bannerBidRequests = [
+ {
+ bidId: 'bid-banner',
+ bidder: 'adverxo',
+ adUnitCode: 'adunit-code',
+ userIdAsEids: [{
+ 'source': 'pubcid.org',
+ 'uids': [{
+ 'atype': 1,
+ 'id': '01EAJWWNEPN3CYMM5N8M5VXY22'
+ }]
+ }],
+ mediaTypes: {banner: {sizes: [[300, 250]]}},
+ params: {
+ host: 'bid.example.com',
+ adUnitId: 1,
+ auth: 'authExample',
+ },
+ bidderRequestId: 'test-bidder-request-id',
+ },
+ ];
+
+ const bannerBidderRequest = {
+ bidderCode: 'adverxo',
+ bidderRequestId: 'test-bidder-request-id',
+ bids: bannerBidRequests,
+ auctionId: 'new-auction-id'
+ };
+
+ const nativeOrtbRequest = {
+ assets: [
+ {
+ id: 1,
+ required: 1,
+ img: {type: 3, w: 150, h: 50}
+ },
+ {
+ id: 2,
+ required: 1,
+ title: {len: 80}
+ },
+ {
+ id: 3,
+ required: 0,
+ data: {type: 1}
+ }
+ ]
+ };
+
+ const nativeBidRequests = [
+ {
+ bidId: 'bid-native',
+ mediaTypes: {
+ native: {
+ ortb: nativeOrtbRequest
+ }
+ },
+ nativeOrtbRequest,
+ params: {
+ host: 'bid.example.com',
+ adUnitId: 1,
+ auth: 'authExample'
+ }
+ },
+ ];
+
+ const nativeBidderRequest = {
+ bidderCode: 'adverxo',
+ bidderRequestId: 'test-bidder-request-id',
+ bids: nativeBidRequests,
+ auctionId: 'new-auction-id'
+ };
+
+ const videoInstreamBidRequests = [
+ {
+ bidId: 'bid-video',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ playerSize: [400, 300],
+ w: 400,
+ h: 300,
+ minduration: 5,
+ maxduration: 10,
+ startdelay: 0,
+ skip: 1,
+ minbitrate: 200,
+ protocols: [1, 2, 4]
+ }
+ },
+ params: {
+ host: 'bid.example.com',
+ adUnitId: 1,
+ auth: 'authExample'
+ }
+ }
+ ];
+
+ const videoInstreamBidderRequest = {
+ bidderCode: 'adverxo',
+ bidderRequestId: 'test-bidder-request-id',
+ bids: videoInstreamBidRequests,
+ auctionId: 'new-auction-id'
+ };
+
+ const videoOutstreamBidRequests = [
+ {
+ bidId: 'bid-video',
+ mediaTypes: {
+ video: {
+ context: 'outstream',
+ playerSize: [400, 300],
+ w: 400,
+ h: 300,
+ minduration: 5,
+ maxduration: 10,
+ startdelay: 0,
+ skip: 1,
+ minbitrate: 200,
+ protocols: [1, 2, 4]
+ }
+ },
+ params: {
+ host: 'bid.example.com',
+ adUnitId: 1,
+ auth: 'authExample'
+ }
+ }
+ ];
+
+ const videoOutstreamBidderRequest = {
+ bidderCode: 'adverxo',
+ bidderRequestId: 'test-bidder-request-id',
+ bids: videoOutstreamBidRequests,
+ auctionId: 'new-auction-id'
+ };
+
+ afterEach(function () {
+ config.resetConfig();
+ });
+
+ describe('isBidRequestValid', function () {
+ it('should validate bid request with valid params', () => {
+ const validBid = makeBidRequestWithParams({
+ adUnitId: 1,
+ auth: 'authExample',
+ host: 'www.bidExample.com'
+ });
+
+ const isValid = spec.isBidRequestValid(validBid);
+
+ expect(isValid).to.be.true;
+ });
+
+ it('should not validate bid request with empty params', () => {
+ const invalidBid = makeBidRequestWithParams({});
+
+ const isValid = spec.isBidRequestValid(invalidBid);
+
+ expect(isValid).to.be.false;
+ });
+
+ it('should not validate bid request with missing param(adUnitId)', () => {
+ const invalidBid = makeBidRequestWithParams({
+ auth: 'authExample',
+ host: 'www.bidExample.com'
+ });
+
+ const isValid = spec.isBidRequestValid(invalidBid);
+
+ expect(isValid).to.be.false;
+ });
+
+ it('should not validate bid request with missing param(auth)', () => {
+ const invalidBid = makeBidRequestWithParams({
+ adUnitId: 1,
+ host: 'www.bidExample.com'
+ });
+
+ const isValid = spec.isBidRequestValid(invalidBid);
+
+ expect(isValid).to.be.false;
+ });
+
+ it('should validate bid request with missing param(host)', () => {
+ const invalidBid = makeBidRequestWithParams({
+ adUnitId: 1,
+ auth: 'authExample',
+ });
+
+ const isValid = spec.isBidRequestValid(invalidBid);
+
+ expect(isValid).to.be.true;
+ });
+ });
+
+ describe('buildRequests', () => {
+ it('should add eids information to the request', function () {
+ const request = spec.buildRequests(bannerBidRequests, bannerBidderRequest)[0];
+
+ expect(request.data.user.ext.eids).to.exist;
+ expect(request.data.user.ext.eids).to.deep.equal(bannerBidRequests[0].userIdAsEids);
+ });
+
+ it('should use correct bidUrl for an alias', () => {
+ const bidRequests = [
+ {
+ bidder: 'bidsmind',
+ mediaTypes: {banner: {sizes: [[300, 250]]}},
+ params: {
+ adUnitId: 1,
+ auth: 'authExample',
+ }
+ },
+ ];
+
+ const bidderRequest = {
+ bidderCode: 'bidsmind',
+ bids: bidRequests,
+ };
+
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
+
+ expect(request.method).to.equal('POST');
+ expect(request.url).to.equal('https://egrevirda.com/pickpbs?id=1&auth=authExample');
+ });
+
+ it('should use correct default bidUrl', () => {
+ const bidRequests = [
+ {
+ bidder: 'adverxo',
+ mediaTypes: {banner: {sizes: [[300, 250]]}},
+ params: {
+ adUnitId: 1,
+ auth: 'authExample',
+ }
+ },
+ ];
+
+ const bidderRequest = {
+ bidderCode: 'adverxo',
+ bids: bidRequests
+ };
+
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
+
+ expect(request.method).to.equal('POST');
+ expect(request.url).to.equal('https://js.pbsadverxo.com/pickpbs?id=1&auth=authExample');
+ });
+
+ it('should build post request for banner', () => {
+ const request = spec.buildRequests(bannerBidRequests, bannerBidderRequest)[0];
+
+ expect(request.method).to.equal('POST');
+ expect(request.url).to.equal('https://bid.example.com/pickpbs?id=1&auth=authExample');
+ expect(request.data.device.ip).to.equal('caller');
+ expect(request.data.ext.avx_add_vast_url).to.equal(1);
+ });
+
+ if (FEATURES.NATIVE) {
+ it('should build post request for native', () => {
+ const request = spec.buildRequests(nativeBidRequests, nativeBidderRequest)[0];
+
+ expect(request.method).to.equal('POST');
+ expect(request.url).to.equal('https://bid.example.com/pickpbs?id=1&auth=authExample');
+
+ const nativeRequest = JSON.parse(request.data.imp[0]['native'].request);
+
+ expect(nativeRequest.assets).to.have.lengthOf(3);
+
+ expect(nativeRequest.assets[0]).to.deep.equal({
+ id: 1,
+ required: 1,
+ img: {w: 150, h: 50, type: 3}
+ });
+
+ expect(nativeRequest.assets[1]).to.deep.equal({
+ id: 2,
+ required: 1,
+ title: {len: 80}
+ });
+
+ expect(nativeRequest.assets[2]).to.deep.equal({
+ id: 3,
+ required: 0,
+ data: {type: 1}
+ });
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('should build post request for video', function () {
+ const request = spec.buildRequests(videoInstreamBidRequests, videoInstreamBidderRequest)[0];
+
+ expect(request.method).to.equal('POST');
+ expect(request.url).to.equal('https://bid.example.com/pickpbs?id=1&auth=authExample');
+
+ const ortbRequest = request.data;
+
+ expect(ortbRequest.imp).to.have.lengthOf(1);
+
+ expect(ortbRequest.imp[0]).to.deep.equal({
+ id: 'bid-video',
+ secure: 1,
+ video: {
+ w: 400,
+ h: 300,
+ minduration: 5,
+ maxduration: 10,
+ startdelay: 0,
+ skip: 1,
+ minbitrate: 200,
+ protocols: [1, 2, 4]
+ }
+ });
+ });
+ }
+
+ it('should add bid floor to request', function () {
+ const bannerBidRequestWithFloor = {
+ ...bannerBidRequests[0],
+ getFloor: () => ({currency: 'USD', floor: 3})
+ };
+
+ const request = spec.buildRequests([bannerBidRequestWithFloor], {})[0].data;
+
+ expect(request.imp[0].bidfloor).to.equal(3);
+ expect(request.imp[0].bidfloorcur).to.equal('USD');
+ });
+ });
+
+ describe('interpretResponse', () => {
+ it('should return empty array if serverResponse is not defined', () => {
+ const bidRequest = spec.buildRequests(bannerBidRequests, bannerBidderRequest);
+ const bids = spec.interpretResponse(undefined, bidRequest);
+
+ expect(bids.length).to.equal(0);
+ });
+
+ it('should interpret banner response', () => {
+ const bidResponse = {
+ body: {
+ id: 'bid-response',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ impid: 'bid-banner',
+ crid: 'creative-id',
+ cur: 'USD',
+ price: 2,
+ w: 300,
+ h: 250,
+ mtype: 1,
+ adomain: ['test.com'],
+ adm: ''
+ },
+ ],
+ seat: 'test-seat',
+ },
+ ],
+ },
+ };
+
+ const expectedBids = [
+ {
+ cpm: 2,
+ creativeId: 'creative-id',
+ creative_id: 'creative-id',
+ currency: 'USD',
+ height: 250,
+ mediaType: 'banner',
+ meta: {
+ advertiserDomains: ['test.com'],
+ },
+ netRevenue: true,
+ requestId: 'bid-banner',
+ ttl: 60,
+ width: 300,
+ ad: ''
+ },
+ ];
+
+ const request = spec.buildRequests(bannerBidRequests, bannerBidderRequest)[0];
+ const bids = spec.interpretResponse(bidResponse, request);
+
+ expect(bids).to.deep.equal(expectedBids);
+ });
+
+ it('should replace openrtb auction price macro', () => {
+ const bidResponse = {
+ body: {
+ id: 'bid-response',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ impid: 'bid-banner',
+ crid: 'creative-id',
+ cur: 'USD',
+ price: 2,
+ w: 300,
+ h: 250,
+ mtype: 1,
+ adomain: ['test.com'],
+ adm: ''
+ },
+ ],
+ seat: 'test-seat',
+ },
+ ],
+ },
+ };
+
+ const request = spec.buildRequests(bannerBidRequests, bannerBidderRequest)[0];
+ const bids = spec.interpretResponse(bidResponse, request);
+
+ expect(bids[0].ad).to.equal('');
+ });
+
+ if (FEATURES.NATIVE) {
+ it('should interpret native response', () => {
+ const bidResponse = {
+ body: {
+ id: 'native-response',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ impid: 'bid-native',
+ crid: 'creative-id',
+ cur: 'USD',
+ price: 2,
+ w: 300,
+ h: 250,
+ mtype: 4,
+ adomain: ['test.com'],
+ adm: '{"native":{"assets":[{"id":2,"title":{"text":"Title"}},{"id":3,"data":{"value":"Description"}},{"id":1,"img":{"url":"http://example.com?img","w":150,"h":50}}],"link":{"url":"http://example.com?link"}}}'
+ },
+ ],
+ seat: 'test-seat',
+ },
+ ],
+ },
+ };
+
+ const expectedBids = [
+ {
+ cpm: 2,
+ creativeId: 'creative-id',
+ creative_id: 'creative-id',
+ currency: 'USD',
+ height: 250,
+ mediaType: 'native',
+ meta: {
+ advertiserDomains: ['test.com'],
+ },
+ netRevenue: true,
+ requestId: 'bid-native',
+ ttl: 60,
+ width: 300,
+ native: {
+ ortb: {
+ assets: [
+ {id: 2, title: {text: 'Title'}},
+ {id: 3, data: {value: 'Description'}},
+ {id: 1, img: {url: 'http://example.com?img', w: 150, h: 50}}
+ ],
+ link: {url: 'http://example.com?link'}
+ }
+ }
+ }
+ ];
+
+ const request = spec.buildRequests(nativeBidRequests, nativeBidderRequest)[0];
+ const bids = spec.interpretResponse(bidResponse, request);
+
+ expect(bids).to.deep.equal(expectedBids);
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('should interpret video instream response', () => {
+ const bidResponse = {
+ body: {
+ id: 'video-response',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ impid: 'bid-video',
+ crid: 'creative-id',
+ cur: 'USD',
+ price: 2,
+ w: 300,
+ h: 250,
+ mtype: 2,
+ adomain: ['test.com'],
+ adm: 'vastXml',
+ ext: {
+ avx_vast_url: 'vastUrl'
+ }
+ },
+ ],
+ seat: 'test-seat',
+ },
+ ],
+ },
+ };
+
+ const expectedBids = [
+ {
+ cpm: 2,
+ creativeId: 'creative-id',
+ creative_id: 'creative-id',
+ currency: 'USD',
+ height: 250,
+ mediaType: 'video',
+ meta: {
+ advertiserDomains: ['test.com'],
+ },
+ netRevenue: true,
+ playerHeight: 300,
+ playerWidth: 400,
+ requestId: 'bid-video',
+ ttl: 60,
+ vastUrl: 'vastUrl',
+ vastXml: 'vastXml',
+ width: 300
+ }
+ ];
+
+ const request = spec.buildRequests(videoInstreamBidRequests, videoInstreamBidderRequest)[0];
+ const bids = spec.interpretResponse(bidResponse, request);
+
+ expect(bids).to.deep.equal(expectedBids);
+ });
+
+ it('should interpret video outstream response', () => {
+ const bidResponse = {
+ body: {
+ id: 'video-response',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ impid: 'bid-video',
+ crid: 'creative-id',
+ cur: 'USD',
+ price: 2,
+ w: 300,
+ h: 250,
+ mtype: 2,
+ adomain: ['test.com'],
+ adm: 'vastXml',
+ ext: {
+ avx_vast_url: 'vastUrl',
+ avx_video_renderer_url: 'videoRendererUrl',
+ }
+ },
+ ],
+ seat: 'test-seat',
+ },
+ ],
+ },
+ };
+
+ const expectedBids = [
+ {
+ avxVideoRendererUrl: 'videoRendererUrl',
+ cpm: 2,
+ creativeId: 'creative-id',
+ creative_id: 'creative-id',
+ currency: 'USD',
+ height: 250,
+ mediaType: 'video',
+ meta: {
+ advertiserDomains: ['test.com'],
+ },
+ netRevenue: true,
+ playerHeight: 300,
+ playerWidth: 400,
+ requestId: 'bid-video',
+ ttl: 60,
+ vastUrl: 'vastUrl',
+ vastXml: 'vastXml',
+ width: 300
+ }
+ ];
+
+ const request = spec.buildRequests(videoOutstreamBidRequests, videoOutstreamBidderRequest)[0];
+ const bids = spec.interpretResponse(bidResponse, request);
+
+ expect(bids[0].renderer.url).to.equal('videoRendererUrl');
+
+ delete (bids[0].renderer);
+ expect(bids).to.deep.equal(expectedBids);
+ });
+ }
+ });
+
+ describe('getUserSyncs', () => {
+ const exampleUrl = 'https://example.com/usync?id=5';
+ const iframeConfig = {iframeEnabled: true};
+
+ const responses = [{
+ body: {ext: {avx_usync: [exampleUrl]}}
+ }];
+
+ const responseWithoutQueryString = [{
+ body: {ext: {avx_usync: ['https://example.com/usync/sf/5']}}
+ }];
+
+ it('should not return empty list if not allowed', function () {
+ expect(spec.getUserSyncs({
+ iframeEnabled: false,
+ pixelEnabled: false
+ }, responses, undefined, undefined, undefined)).to.be.empty;
+ });
+
+ it('should not return iframe if not allowed', function () {
+ expect(spec.getUserSyncs({
+ iframeEnabled: false,
+ pixelEnabled: true
+ }, responses, undefined, undefined, undefined)).to.deep.equal([{
+ type: 'image', url: `${exampleUrl}&type=image`
+ }]);
+ });
+
+ it('should add query string to url when missing', function () {
+ expect(spec.getUserSyncs(iframeConfig, responseWithoutQueryString, undefined, undefined, undefined)).to.deep.equal([{
+ type: 'iframe', url: `https://example.com/usync/sf/5?type=iframe`
+ }]);
+ });
+
+ it('should not add parameters if not provided', function () {
+ expect(spec.getUserSyncs(iframeConfig, responses, undefined, undefined, undefined)).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe`
+ }]);
+ });
+
+ it('should add GDPR parameters if provided', function () {
+ expect(spec.getUserSyncs(iframeConfig, responses, {gdprApplies: true}, undefined, undefined)).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe&gdpr=1&gdpr_consent=`
+ }]);
+
+ expect(spec.getUserSyncs(iframeConfig, responses,
+ {gdprApplies: true, consentString: 'foo?'}, undefined, undefined)).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe&gdpr=1&gdpr_consent=foo%3F`
+ }]);
+
+ expect(spec.getUserSyncs(iframeConfig, responses,
+ {gdprApplies: false, consentString: 'bar'}, undefined, undefined)).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe&gdpr=0&gdpr_consent=bar`
+ }]);
+ });
+
+ it('should add CCPA parameters if provided', function () {
+ expect(spec.getUserSyncs(iframeConfig, responses, undefined, 'foo?', undefined)).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe&us_privacy=foo%3F`
+ }]);
+ });
+
+ it('should not apply if not gppConsent.gppString', function () {
+ const gppConsent = {gppString: '', applicableSections: [123]};
+ const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent);
+ expect(result).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe`
+ }]);
+ });
+
+ it('should not apply if not gppConsent.applicableSections', function () {
+ const gppConsent = {gppString: '', applicableSections: undefined};
+ const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent);
+ expect(result).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe`
+ }]);
+ });
+
+ it('should not apply if empty gppConsent.applicableSections', function () {
+ const gppConsent = {gppString: '', applicableSections: []};
+ const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent);
+ expect(result).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe`
+ }]);
+ });
+
+ it('should apply if all above are available', function () {
+ const gppConsent = {gppString: 'foo?', applicableSections: [123]};
+ const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent);
+ expect(result).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe&gpp=foo%3F&gpp_sid=123`
+ }]);
+ });
+
+ it('should support multiple sections', function () {
+ const gppConsent = {gppString: 'foo', applicableSections: [123, 456]};
+ const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent);
+ expect(result).to.deep.equal([{
+ type: 'iframe', url: `${exampleUrl}&type=iframe&gpp=foo&gpp_sid=123%2C456`
+ }]);
+ });
+ });
+});
diff --git a/test/spec/modules/aidemBidAdapter_spec.js b/test/spec/modules/aidemBidAdapter_spec.js
index c9d29ff09dd..c07b999afe6 100644
--- a/test/spec/modules/aidemBidAdapter_spec.js
+++ b/test/spec/modules/aidemBidAdapter_spec.js
@@ -455,7 +455,7 @@ describe('Aidem adapter', () => {
expect(data.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_BANNER_REQUESTS.length)
expect(data.imp[0]).to.be.a('object').that.has.all.keys(
- 'banner', 'id', 'tagId'
+ 'banner', 'id', 'tagId', 'secure'
)
expect(data.imp[0].banner).to.be.a('object').that.has.all.keys(
'format', 'topframe'
@@ -471,7 +471,7 @@ describe('Aidem adapter', () => {
expect(data.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_VIDEO_REQUESTS.length)
expect(data.imp[0]).to.be.a('object').that.has.all.keys(
- 'video', 'id', 'tagId'
+ 'video', 'id', 'tagId', 'secure'
)
expect(data.imp[0].video).to.be.a('object').that.has.all.keys(
'mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h'
diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js
index d87c84a05c5..726bccaa027 100644
--- a/test/spec/modules/aniviewBidAdapter_spec.js
+++ b/test/spec/modules/aniviewBidAdapter_spec.js
@@ -188,7 +188,6 @@ describe('Aniview Bid Adapter', function () {
expect(url).equal('https://rtb.aniview.com/sspRTB2');
expect(method).equal('POST');
expect(imp[0].tagid).equal(CHANNEL_ID_1);
- expect(imp[0].ext.aniview.AV_HEIGHT).equal(VIDEO_SIZE.height);
expect(imp[0].id).equal(videoBidRequest.bids[0].bidId);
expect(ext.aniview.pbjs).equal(1);
});
diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js
index d044e71ceb7..3f06cd04910 100644
--- a/test/spec/modules/beopBidAdapter_spec.js
+++ b/test/spec/modules/beopBidAdapter_spec.js
@@ -2,6 +2,8 @@ import { expect } from 'chai';
import { spec } from 'modules/beopBidAdapter.js';
import { newBidder } from 'src/adapters/bidderFactory.js';
import { config } from 'src/config.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
const utils = require('src/utils');
const ENDPOINT = 'https://hb.beop.io/bid';
@@ -92,18 +94,27 @@ describe('BeOp Bid Adapter tests', () => {
bidRequests.push(validBid);
it('should build the request', function () {
- config.setConfig({'currency': {'adServerCurrency': 'USD'}});
- const request = spec.buildRequests(bidRequests, {});
- const payload = JSON.parse(request.data);
- const url = request.url;
- expect(url).to.equal(ENDPOINT);
- expect(payload.pid).to.exist;
- expect(payload.pid).to.equal('5a8af500c9e77c00017e4cad');
- expect(payload.gdpr_applies).to.exist;
- expect(payload.gdpr_applies).to.equals(false);
- expect(payload.slts[0].name).to.exist;
- expect(payload.slts[0].name).to.equal('bellow-article');
- expect(payload.slts[0].flr).to.equal(10);
+ const bidderRequest = {
+ refererInfo: {
+ page: 'https://example.com'
+ }
+ };
+ setCurrencyConfig({ adServerCurrency: 'USD' })
+
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ const request = spec.buildRequests(bidRequests, res);
+ const payload = JSON.parse(request.data);
+ const url = request.url;
+ expect(url).to.equal(ENDPOINT);
+ expect(payload.pid).to.exist;
+ expect(payload.pid).to.equal('5a8af500c9e77c00017e4cad');
+ expect(payload.gdpr_applies).to.exist;
+ expect(payload.gdpr_applies).to.equals(false);
+ expect(payload.slts[0].name).to.exist;
+ expect(payload.slts[0].name).to.equal('bellow-article');
+ expect(payload.slts[0].flr).to.equal(10);
+ setCurrencyConfig({});
+ });
});
it('should call the endpoint with GDPR consent and pageURL info if found', function () {
@@ -327,4 +338,16 @@ describe('BeOp Bid Adapter tests', () => {
expect(payload.eids[0].source).to.equal('provider.com');
});
})
+
+ describe('Ensure first party cookie is well managed', function () {
+ let bidRequests = [];
+
+ it(`should generate a new uuid`, function () {
+ let bid = Object.assign({}, validBid);
+ bidRequests.push(bid);
+ const request = spec.buildRequests(bidRequests, {});
+ const payload = JSON.parse(request.data);
+ expect(payload.fg).to.exist;
+ })
+ })
});
diff --git a/test/spec/modules/bidmaticBidAdapter_spec.js b/test/spec/modules/bidmaticBidAdapter_spec.js
index dcf35d032ea..225c2a6dce6 100644
--- a/test/spec/modules/bidmaticBidAdapter_spec.js
+++ b/test/spec/modules/bidmaticBidAdapter_spec.js
@@ -1,8 +1,9 @@
import { expect } from 'chai';
-import { END_POINT, spec } from 'modules/bidmaticBidAdapter.js';
+import { END_POINT, SYNC_URL, spec, createUserSyncs } from 'modules/bidmaticBidAdapter.js';
import { deepClone, deepSetValue, mergeDeep } from '../../../src/utils';
const expectedImp = {
+ 'secure': 1,
'id': '2eb89f0f062afe',
'banner': {
'topframe': 0,
@@ -164,6 +165,46 @@ describe('Bidmatic Bid Adapter', () => {
});
})
+ describe('syncs creation', () => {
+ const syncOptions = { iframeEnabled: true };
+
+ it('should not operate without syncs enabled', () => {
+ const syncs = createUserSyncs({});
+ expect(syncs).to.eq(undefined);
+ });
+
+ it('should call uniq and unused sources only', () => {
+ const sources = { 111: 0, 222: 0, 333: 1 }
+ const syncs = createUserSyncs(sources, syncOptions);
+
+ expect(syncs.length).to.eq(2);
+
+ expect(syncs[0].type).to.eq('iframe');
+ expect(syncs[0].url).to.eq(`${SYNC_URL}?aid=111`);
+ expect(syncs[1].type).to.eq('iframe');
+ expect(syncs[1].url).to.eq(`${SYNC_URL}?aid=222`);
+
+ expect(sources[111]).to.eq(1);
+ expect(sources[222]).to.eq(1);
+
+ const syncs2 = createUserSyncs(sources, syncOptions);
+ expect(syncs2.length).to.eq(0);
+ });
+
+ it('should add consent info', () => {
+ const [{ url: syncUrl }] = createUserSyncs(
+ { 111: 0 },
+ syncOptions,
+ { gdprApplies: true, consentString: '111' },
+ 'yyy',
+ { gppString: '222', applicableSections: [1, 2] });
+
+ expect(syncUrl.includes('gdpr=1&gdpr_consent=111')).to.eq(true);
+ expect(syncUrl.includes('usp=yyy')).to.eq(true);
+ expect(syncUrl.includes('gpp=222&gpp_sid=1,2')).to.eq(true);
+ });
+ })
+
describe('response interpreter', () => {
const SERVER_RESPONSE = {
'body': {
diff --git a/test/spec/modules/bidtheatreBidAdapter_spec.js b/test/spec/modules/bidtheatreBidAdapter_spec.js
new file mode 100644
index 00000000000..4842c43d1f0
--- /dev/null
+++ b/test/spec/modules/bidtheatreBidAdapter_spec.js
@@ -0,0 +1,266 @@
+import { expect } from 'chai';
+import { spec, ENDPOINT_URL, BIDDER_CODE, DEFAULT_CURRENCY } from 'modules/bidtheatreBidAdapter.js';
+import { BANNER, VIDEO } from 'src/mediaTypes.js';
+import { deepClone } from 'src/utils.js';
+
+const VALID_PUBLISHER_ID = '73b20b3a-12a0-4869-b54e-8d42b55786ee';
+const STATIC_IMP_ID = '3263e5dec855c5';
+const BID_PRICE = 5.112871170043945;
+const AUCTION_PRICE_MACRO = '${AUCTION_PRICE}';
+
+const BANNER_BID_REQUEST = [
+ {
+ 'bidder': BIDDER_CODE,
+ 'params': {
+ 'publisherId': VALID_PUBLISHER_ID
+ },
+ 'bidId': STATIC_IMP_ID,
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [
+ [
+ 980,
+ 240
+ ]
+ ]
+ }
+ },
+ 'sizes': [
+ [
+ 980,
+ 240
+ ]
+ ]
+ }
+];
+
+const BANNER_BIDDER_REQUEST = {'bidderCode': BIDDER_CODE, 'bids': BANNER_BID_REQUEST};
+
+const BANNER_BID_RESPONSE = {
+ 'cur': 'USD',
+ 'seatbid': [
+ {
+ 'seat': '5',
+ 'bid': [
+ {
+ 'ext': {
+ 'usersync_urls': [
+ 'https://match.adsby.bidtheatre.com/usersync?gdpr=1&gdpr_consent=CONSENT_STRING'
+ ]
+ },
+ 'crid': '1915538',
+ 'h': 240,
+ 'adm': "",
+ 'mtype': 1,
+ 'adid': '1915538',
+ 'adomain': [
+ 'bidtheatre.com'
+ ],
+ 'price': BID_PRICE,
+ 'cat': [
+ 'IAB3-1'
+ ],
+ 'w': 980,
+ 'id': STATIC_IMP_ID,
+ 'impid': STATIC_IMP_ID,
+ 'cid': 'c154375'
+ }
+ ]
+ }
+ ]
+};
+
+const VIDEO_BID_REQUEST = [
+ {
+ 'bidder': BIDDER_CODE,
+ 'params': {
+ 'publisherId': VALID_PUBLISHER_ID
+ },
+ 'bidId': STATIC_IMP_ID,
+ 'mediaTypes': {
+ 'video': {
+ 'playerSize': [
+ [
+ 1280,
+ 720
+ ]
+ ],
+ 'context': 'instream'
+ }
+ },
+ 'sizes': [[1280, 720]]
+ }
+];
+
+const VIDEO_BIDDER_REQUEST = {'bidderCode': BIDDER_CODE, 'bids': VIDEO_BID_REQUEST};
+
+const VIDEO_BID_RESPONSE = {
+ 'cur': 'USD',
+ 'seatbid': [
+ {
+ 'seat': '5',
+ 'bid': [
+ {
+ 'ext': {
+ 'usersync_urls': [
+ 'https://match.adsby.bidtheatre.com/usersync?gdpr=0&gdpr_consent='
+ ]
+ },
+ 'crid': '1922926',
+ 'h': 720,
+ 'mtype': 2,
+ 'nurl': 'https://adsby.bidtheatre.com/video?z=27025;a=1922926;ex=36;es=http%3A%2F%2F127.0.0.1%3A8080;eb=3672319;xs=940400838;so=1;tag=unspec_1280_720;kuid=05914b22-88cb-4c5d-9f7c-f133fdf9669a;wp=${AUCTION_PRICE};su=127.0.0.1%3A8080;iab=vast2;dealId=;ma=eyJjZCI6ZmFsc2UsInN0IjozLCJtbGF0Ijo1OS4yNywibW9yZyI6InRlbGlhIG5ldHdvcmsgc2VydmljZXMiLCJtbHNjb3JlIjowLjg2MDcwMDU0NzY5NTE1OTksIm16aXAiOiIxMjggMzUiLCJiaXAiOiI4MS4yMjcuODIuMjgiLCJhZ2lkIjozNTYyNzAyLCJtbG1vZGVsIjoibWFzdGVyX21sX2Nsa181MzYiLCJ1YSI6Ik1vemlsbGFcLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdFwvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lXC8xMzAuMC4wLjAgU2FmYXJpXC81MzcuMzYiLCJtbG9uIjoxOC4xMywibXJlZ2lvbiI6ImFiIiwiZHQiOjEsIm1jaXR5Ijoic2thcnBuYWNrIiwicGFnZXVybCI6Imh0dHA6XC9cLzEyNy4wLjAuMTo4MDgwXC92aWRlby5odG1sP3BianNfZGVidWc9dHJ1ZSIsImltcGlkIjoieDM2X2FzeC1iLXMyXzQxNDMzMTA0MTIyMzUyNTU3NDgiLCJtY291bnRyeSI6InN3ZSIsInRzIjoxNzMxNTA3NTY5Njg3fQ%3D%3D;cd=0;cb0=;impId=x36_asx-b-s2_4143310412235255748;gdpr=1;gdpr_consent=CP-S4UAP-S4UACGABBENAzEsAP_gAEPgAAAAKKtV_H__bW1r8X73aft0eY1P9_j77sQxBhfJE-4FzLvW_JwXx2ExNA36tqIKmRIEu3bBIQNlHJDUTVCgaogVryDMakWcoTNKJ6BkiFMRO2dYCF5vmwtj-QKY5vr993dx2D-t_dv83dzyz4VHn3a5_2e0WJCdA58tDfv9bROb-9IPd_58v4v0_F_rE2_eT1l_tevp7D9-ct87_XW-9_fff79Ll9-goqAWYaFRAHWBISEGgYRQIAVBWEBFAgAAABIGiAgBMGBTsDAJdYSIAQAoABggBAACjIAEAAAEACEQAQAFAgAAgECgABAAgEAgAIGAAEAFgIBAACA6BCmBBAoFgAkZkRCmBCFAkEBLZUIJAECCuEIRZ4AEAiJgoAAAAACsAAQFgsDiSQEqEggS4g2gAAIAEAghAqEEnJgACBI2WoPBE2jK0gDQ04SAAAAA.YAAAAAAAAAAA',
+ 'adid': '1922926',
+ 'adomain': [
+ 'bidtheatre.com'
+ ],
+ 'price': BID_PRICE,
+ 'cat': [
+ 'IAB3-1'
+ ],
+ 'w': 1280,
+ 'id': STATIC_IMP_ID,
+ 'impid': STATIC_IMP_ID,
+ 'cid': 'c154375'
+ }
+ ]
+ }
+ ]
+}
+
+describe('BidtheatreAdapter', function () {
+ describe('isBidRequestValid', function () {
+ let bid = {
+ 'bidder': BIDDER_CODE,
+ 'params': {
+ 'publisherId': VALID_PUBLISHER_ID
+ },
+ 'sizes': [[980, 240]]
+ };
+
+ it('should return true when required param found and of correct type', function () {
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
+ });
+
+ it('should return false when required param is not passed', function () {
+ let invalidBid = Object.assign({}, bid);
+ delete invalidBid.params;
+ invalidBid.params = {
+
+ };
+ expect(spec.isBidRequestValid(invalidBid)).to.equal(false);
+ });
+
+ it('should return false when required param of incorrect data type', function () {
+ let invalidBid = Object.assign({}, bid);
+ delete invalidBid.params;
+ invalidBid.params = {
+ 'publisherId': 12345
+ };
+ expect(spec.isBidRequestValid(invalidBid)).to.equal(false);
+ });
+
+ it('should return false when required param of incorrect length', function () {
+ let invalidBid = Object.assign({}, bid);
+ delete invalidBid.params;
+ invalidBid.params = {
+ 'publisherId': '73b20b3a-12a0-4869-b54e-8d42b55786e'
+ };
+ expect(spec.isBidRequestValid(invalidBid)).to.equal(false);
+ });
+ });
+
+ describe('buildRequests', function () {
+ it('should include correct http method, correct url and existing data', function () {
+ const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST);
+ expect(request[0].method).to.equal('POST');
+ expect(request[0].url).to.equal(ENDPOINT_URL);
+ expect(request[0].data).to.exist;
+ });
+
+ it('should include required bid param in request', function () {
+ const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST);
+ const data = request[0].data;
+ expect(data.imp[0].ext.bidder.publisherId).to.equal(VALID_PUBLISHER_ID);
+ });
+
+ it('should include imp array in request', function () {
+ const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST);
+ const data = request[0].data;
+ expect(data).to.have.property('imp').that.is.an('array').with.lengthOf.at.least(1);
+ expect(data.imp[0]).to.be.an('object');
+ });
+ });
+
+ describe('interpretResponse', function () {
+ it('should have exactly one bid in banner response', function () {
+ const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST);
+ const bids = spec.interpretResponse({body: BANNER_BID_RESPONSE}, request[0]);
+ expect(bids).to.be.an('array').with.lengthOf(1);
+ expect(bids[0]).to.be.an('object');
+ });
+
+ it('should have currency set to USD in banner response', function () {
+ const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST);
+ const bids = spec.interpretResponse({body: BANNER_BID_RESPONSE}, request[0]);
+ expect(bids[0].currency).to.be.a('string').and.to.equal(DEFAULT_CURRENCY);
+ });
+
+ it('should have ad in response and auction price macros replaced in banner response', function () {
+ const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST);
+ const bids = spec.interpretResponse({body: BANNER_BID_RESPONSE}, request[0]);
+ const ad = bids[0].ad;
+ expect(ad).to.exist;
+ expect(ad).to.be.a('string');
+ expect(ad).to.include('&wp=' + BID_PRICE + '&');
+ expect(ad).to.not.include(AUCTION_PRICE_MACRO);
+ });
+
+ if (FEATURES.VIDEO) {
+ it('should have exactly one bid in video response', function () {
+ const request = spec.buildRequests(VIDEO_BID_REQUEST, VIDEO_BIDDER_REQUEST);
+ const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request[0]);
+ expect(bids).to.be.an('array').with.lengthOf(1);
+ expect(bids[0]).to.be.an('object');
+ });
+
+ it('should have currency set to USD in video response', function () {
+ const request = spec.buildRequests(VIDEO_BID_REQUEST, VIDEO_BIDDER_REQUEST);
+ const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request[0]);
+ expect(bids[0].currency).to.be.a('string').and.to.equal(DEFAULT_CURRENCY);
+ });
+
+ it('should have vastUrl in response and auction price macros replaced in video response', function () {
+ const request = spec.buildRequests(VIDEO_BID_REQUEST, VIDEO_BIDDER_REQUEST);
+ const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request[0]);
+ const vastUrl = bids[0].vastUrl;
+ expect(vastUrl).to.exist;
+ expect(vastUrl).to.be.a('string');
+ expect(vastUrl).to.include(';wp=' + BID_PRICE + ';');
+ expect(vastUrl).to.not.include(AUCTION_PRICE_MACRO);
+ });
+ }
+ });
+
+ describe('getUserSyncs', function () {
+ const bidResponse = deepClone(BANNER_BID_RESPONSE);
+ const bidResponseSyncURL = bidResponse.seatbid[0].bid[0].ext.usersync_urls[0];
+
+ const gdprConsent = {
+ gdprApplies: true,
+ consentString: 'CONSENT_STRING'
+ };
+
+ it('should return empty when pixel is disallowed', function () {
+ expect(spec.getUserSyncs({ pixelEnabled: false }, bidResponse, gdprConsent)).to.be.empty;
+ });
+
+ it('should return empty when no server response is present', function () {
+ expect(spec.getUserSyncs({ pixelEnabled: true }, [], gdprConsent)).to.be.empty;
+ });
+
+ it('should return usersync url when pixel is allowed and present in bid response', function () {
+ expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: bidResponse}], gdprConsent)).to.deep.equal([{ type: 'image', url: bidResponseSyncURL }]);
+ });
+ });
+});
diff --git a/test/spec/modules/bridBidAdapter_spec.js b/test/spec/modules/bridBidAdapter_spec.js
index 7503c748999..fdda6d840e8 100644
--- a/test/spec/modules/bridBidAdapter_spec.js
+++ b/test/spec/modules/bridBidAdapter_spec.js
@@ -1,4 +1,5 @@
import { spec } from '../../../modules/bridBidAdapter.js'
+import { SYNC_URL } from '../../../libraries/targetVideoUtils/constants.js';
describe('Brid Bid Adapter', function() {
const videoRequest = [{
@@ -126,4 +127,23 @@ describe('Brid Bid Adapter', function() {
expect(payload.regs.ext.gdpr).to.be.undefined;
expect(payload.regs.ext.us_privacy).to.equal(uspConsentString);
});
+
+ it('Test userSync have only one object and it should have a property type=iframe', function () {
+ let userSync = spec.getUserSyncs({ iframeEnabled: true });
+ expect(userSync).to.be.an('array');
+ expect(userSync.length).to.be.equal(1);
+ expect(userSync[0]).to.have.property('type');
+ expect(userSync[0].type).to.be.equal('iframe');
+ });
+
+ it('Test userSync valid sync url for iframe', function () {
+ let [userSync] = spec.getUserSyncs({ iframeEnabled: true }, {}, {consentString: 'anyString'});
+ expect(userSync.url).to.contain(SYNC_URL + 'load-cookie.html?endpoint=brid&gdpr=0&gdpr_consent=anyString');
+ expect(userSync.type).to.be.equal('iframe');
+ });
+
+ it('Test userSyncs iframeEnabled=false', function () {
+ let userSyncs = spec.getUserSyncs({iframeEnabled: false});
+ expect(userSyncs).to.have.lengthOf(0);
+ });
});
diff --git a/test/spec/modules/cadentApertureMXBidAdapter_spec.js b/test/spec/modules/cadentApertureMXBidAdapter_spec.js
index 4f0f2cf8f20..d8686e3c667 100644
--- a/test/spec/modules/cadentApertureMXBidAdapter_spec.js
+++ b/test/spec/modules/cadentApertureMXBidAdapter_spec.js
@@ -48,49 +48,11 @@ describe('cadent_aperture_mx Adapter', function () {
'auctionId': '1d1a01234a475'
};
let noBid = {};
- let otherBid = {
- 'bidder': 'emxdigital',
- 'params': {
- 'tagid': '25251'
- },
- 'mediaTypes': {
- 'banner': {
- 'sizes': [[300, 250]]
- }
- },
- 'adUnitCode': 'adunit-code',
- 'sizes': [
- [300, 250],
- [300, 600]
- ],
- 'bidId': '30b31c2501de1e',
- 'bidderRequestId': '22edbae3120bf6',
- 'auctionId': '1d1a01234a475'
- };
- let noMediaSizeBid = {
- 'bidder': 'emxdigital',
- 'params': {
- 'tagid': '25251'
- },
- 'mediaTypes': {
- 'banner': {}
- },
- 'adUnitCode': 'adunit-code',
- 'sizes': [
- [300, 250],
- [300, 600]
- ],
- 'bidId': '30b31c2501de1e',
- 'bidderRequestId': '22edbae3120bf6',
- 'auctionId': '1d1a01234a475'
- };
it('should return true when required params found', function () {
expect(spec.isBidRequestValid(bid)).to.equal(true);
expect(spec.isBidRequestValid(badBid)).to.equal(false);
expect(spec.isBidRequestValid(noBid)).to.equal(false);
- expect(spec.isBidRequestValid(otherBid)).to.equal(false);
- expect(spec.isBidRequestValid(noMediaSizeBid)).to.equal(false);
});
});
diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js
index bf4557d7a8a..780c81ebe9f 100644
--- a/test/spec/modules/carodaBidAdapter_spec.js
+++ b/test/spec/modules/carodaBidAdapter_spec.js
@@ -3,6 +3,8 @@ import { assert } from 'chai';
import { spec } from 'modules/carodaBidAdapter.js';
import { config } from 'src/config.js';
import { createEidsArray } from 'modules/userId/eids.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
describe('Caroda adapter', function () {
let bids = [];
@@ -185,12 +187,14 @@ describe('Caroda adapter', function () {
});
it('should send currency if defined', function () {
- config.setConfig({ currency: { adServerCurrency: 'EUR' } });
+ setCurrencyConfig({ adServerCurrency: 'EUR' });
let validBidRequests = [{ params: {} }];
- let refererInfo = { page: 'page' };
- let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo })[0].data);
-
- assert.deepEqual(request.currency, 'EUR');
+ const bidderRequest = { refererInfo: { page: 'page' } };
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ let request = JSON.parse(spec.buildRequests(validBidRequests, res)[0].data);
+ assert.deepEqual(request.currency, 'EUR');
+ setCurrencyConfig({});
+ });
});
it('should pass extended ids', function () {
@@ -301,11 +305,15 @@ describe('Caroda adapter', function () {
});
it('should request floor price in adserver currency', function () {
- config.setConfig({ currency: { adServerCurrency: 'DKK' } });
const validBidRequests = [ getBidWithFloor() ];
- const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data);
- assert.equal(imp.bidfloor, undefined);
- assert.equal(imp.bidfloorcur, 'DKK');
+ setCurrencyConfig({ adServerCurrency: 'DKK' });
+ const bidderRequest = { refererInfo: { page: 'page' } };
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ const imp = JSON.parse(spec.buildRequests(validBidRequests, res)[0].data);
+ assert.equal(imp.bidfloor, undefined);
+ assert.equal(imp.bidfloorcur, 'DKK');
+ setCurrencyConfig({});
+ });
});
it('should add correct floor values', function () {
diff --git a/test/spec/modules/contxtfulBidAdapter_spec.js b/test/spec/modules/contxtfulBidAdapter_spec.js
index 02cb3ccef8a..14b4b94f062 100644
--- a/test/spec/modules/contxtfulBidAdapter_spec.js
+++ b/test/spec/modules/contxtfulBidAdapter_spec.js
@@ -400,22 +400,10 @@ describe('contxtful bid adapter', function () {
expect(userSyncs2).to.have.lengthOf(0);
});
- describe('on timeout callback', () => {
- it('will never call server if sampling is 0 with sendBeacon available', () => {
+ describe('onTimeout callback', () => {
+ it('will always call server with sendBeacon available', () => {
config.setConfig({
- contxtful: {customer: CUSTOMER, version: VERSION, 'sampling': {'onTimeout': 0.0}},
- });
-
- const beaconStub = sandbox.stub(ajax, 'sendBeacon').returns(true);
- const ajaxStub = sandbox.stub(ajax, 'ajax');
- expect(spec.onTimeout({'customData': 'customvalue'})).to.not.throw;
- expect(beaconStub.called).to.be.false;
- expect(ajaxStub.called).to.be.false;
- });
-
- it('will always call server if sampling is 1 with sendBeacon available', () => {
- config.setConfig({
- contxtful: {customer: CUSTOMER, version: VERSION, 'sampling': {'onTimeout': 1.0}},
+ contxtful: {customer: CUSTOMER, version: VERSION},
});
const beaconStub = sandbox.stub(ajax, 'sendBeacon').returns(true);
@@ -425,9 +413,9 @@ describe('contxtful bid adapter', function () {
expect(ajaxStub.called).to.be.false;
});
- it('will always call server if sampling is 1 with sendBeacon not available', () => {
+ it('will always call server with sendBeacon not available', () => {
config.setConfig({
- contxtful: {customer: CUSTOMER, version: VERSION, 'sampling': {'onTimeout': 1.0}},
+ contxtful: {customer: CUSTOMER, version: VERSION},
});
const ajaxStub = sandbox.stub(ajax, 'ajax');
@@ -440,9 +428,9 @@ describe('contxtful bid adapter', function () {
});
describe('on onBidderError callback', () => {
- it('will always call server if sampling is 1', () => {
+ it('will always call server', () => {
config.setConfig({
- contxtful: {customer: CUSTOMER, version: VERSION, 'sampling': {'onBidderError': 1.0}},
+ contxtful: {customer: CUSTOMER, version: VERSION},
});
const ajaxStub = sandbox.stub(ajax, 'ajax');
@@ -468,9 +456,9 @@ describe('contxtful bid adapter', function () {
});
describe('on onBidBillable callback', () => {
- it('will always call server', () => {
+ it('will always call server when sampling rate is configured to be 1.0', () => {
config.setConfig({
- contxtful: {customer: CUSTOMER, version: VERSION},
+ contxtful: {customer: CUSTOMER, version: VERSION, sampling: {onBidBillable: 1.0}},
});
const ajaxStub = sandbox.stub(ajax, 'ajax');
const beaconStub = sandbox.stub(ajax, 'sendBeacon').returns(false);
@@ -481,9 +469,9 @@ describe('contxtful bid adapter', function () {
});
describe('on onAdRenderSucceeded callback', () => {
- it('will always call server', () => {
+ it('will always call server when sampling rate is configured to be 1.0', () => {
config.setConfig({
- contxtful: {customer: CUSTOMER, version: VERSION},
+ contxtful: {customer: CUSTOMER, version: VERSION, sampling: {onAdRenderSucceeded: 1.0}},
});
const ajaxStub = sandbox.stub(ajax, 'ajax');
const beaconStub = sandbox.stub(ajax, 'sendBeacon').returns(false);
diff --git a/test/spec/modules/contxtfulRtdProvider_spec.js b/test/spec/modules/contxtfulRtdProvider_spec.js
index ad79b051393..e31ef554da0 100644
--- a/test/spec/modules/contxtfulRtdProvider_spec.js
+++ b/test/spec/modules/contxtfulRtdProvider_spec.js
@@ -1,7 +1,12 @@
import { contxtfulSubmodule, extractParameters } from '../../../modules/contxtfulRtdProvider.js';
import { expect } from 'chai';
import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js';
+import { getStorageManager } from '../../../src/storageManager.js';
+import { MODULE_TYPE_UID } from '../../../src/activities/modules.js';
import * as events from '../../../src/events';
+import Sinon from 'sinon';
+
+const MODULE_NAME = 'contxtful';
const VERSION = 'v1';
const CUSTOMER = 'CUSTOMER';
@@ -10,7 +15,7 @@ const CONTXTFUL_CONNECTOR_ENDPOINT = `https://api.receptivity.io/${VERSION}/preb
const RX_FROM_SESSION_STORAGE = { ReceptivityState: 'Receptive', test_info: 'rx_from_session_storage' };
const RX_FROM_API = { ReceptivityState: 'Receptive', test_info: 'rx_from_engine' };
-const RX_API_MOCK = { receptivity: sinon.stub(), };
+const RX_API_MOCK = { receptivity: sinon.stub(), receptivityBatched: sinon.stub() };
const RX_CONNECTOR_MOCK = {
fetchConfig: sinon.stub(),
rxApiBuilder: sinon.stub(),
@@ -19,13 +24,6 @@ const RX_CONNECTOR_MOCK = {
const TIMEOUT = 10;
const RX_CONNECTOR_IS_READY_EVENT = new CustomEvent('rxConnectorIsReady', { detail: {[CUSTOMER]: RX_CONNECTOR_MOCK}, bubbles: true });
-function writeToStorage(requester, timeDiff) {
- let rx = RX_FROM_SESSION_STORAGE;
- let exp = new Date().getTime() + timeDiff;
- let item = { rx, exp, };
- sessionStorage.setItem(requester, JSON.stringify(item),);
-}
-
function buildInitConfig(version, customer) {
return {
name: 'contxtful',
@@ -44,12 +42,17 @@ describe('contxtfulRtdProvider', function () {
let loadExternalScriptTag;
let eventsEmitSpy;
+ const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME });
+
beforeEach(() => {
loadExternalScriptTag = document.createElement('script');
loadExternalScriptStub.callsFake((_url, _moduleName) => loadExternalScriptTag);
RX_API_MOCK.receptivity.reset();
- RX_API_MOCK.receptivity.callsFake((tagId) => RX_FROM_API);
+ RX_API_MOCK.receptivity.callsFake(() => RX_FROM_API);
+
+ RX_API_MOCK.receptivityBatched.reset();
+ RX_API_MOCK.receptivityBatched.callsFake((bidders) => bidders.reduce((accumulator, bidder) => { accumulator[bidder] = RX_FROM_API; return accumulator; }, {}));
RX_CONNECTOR_MOCK.fetchConfig.reset();
RX_CONNECTOR_MOCK.fetchConfig.callsFake((tagId) => new Promise((resolve, reject) => resolve({ tag_id: tagId })));
@@ -249,7 +252,7 @@ describe('contxtfulRtdProvider', function () {
setTimeout(() => {
let targetingData = contxtfulSubmodule.getTargetingData(adUnits, config);
- expect(targetingData, description).to.deep.equal(expected);
+ expect(targetingData, description).to.deep.equal(expected, description);
done();
}, TIMEOUT);
});
@@ -322,22 +325,18 @@ describe('contxtfulRtdProvider', function () {
];
theories.forEach(([adUnits, expected, _description]) => {
- // TODO: commented out because of rule violations
- /*
it('uses non-expired info from session storage and adds receptivity to the ad units using session storage', function (done) {
- let config = buildInitConfig(VERSION, CUSTOMER);
// Simulate that there was a write to sessionStorage in the past.
- writeToStorage(config.params.customer, +100);
+ storage.setDataInSessionStorage(CUSTOMER, JSON.stringify({exp: new Date().getTime() + 1000, rx: RX_FROM_SESSION_STORAGE}))
+
+ let config = buildInitConfig(VERSION, CUSTOMER);
contxtfulSubmodule.init(config);
- setTimeout(() => {
- expect(contxtfulSubmodule.getTargetingData(adUnits, config)).to.deep.equal(
- expected
- );
- done();
- }, TIMEOUT);
+ let targetingData = contxtfulSubmodule.getTargetingData(adUnits, config);
+ expect(targetingData).to.deep.equal(expected);
+
+ done();
});
- */
});
});
@@ -360,13 +359,15 @@ describe('contxtfulRtdProvider', function () {
theories.forEach(([adUnits, expected, _description]) => {
it('ignores expired info from session storage and does not forward the info to ad units', function (done) {
- let config = buildInitConfig(VERSION, CUSTOMER);
// Simulate that there was a write to sessionStorage in the past.
- writeToStorage(config.params.customer, -100);
+ storage.setDataInSessionStorage(CUSTOMER, JSON.stringify({exp: new Date().getTime() - 100, rx: RX_FROM_SESSION_STORAGE}));
+
+ let config = buildInitConfig(VERSION, CUSTOMER);
contxtfulSubmodule.init(config);
- expect(contxtfulSubmodule.getTargetingData(adUnits, config)).to.deep.equal(
- expected
- );
+
+ let targetingData = contxtfulSubmodule.getTargetingData(adUnits, config);
+ expect(targetingData).to.deep.equal(expected);
+
done();
});
});
@@ -428,42 +429,39 @@ describe('contxtfulRtdProvider', function () {
},
};
- let expectedOrtb2 = {
- user: {
- data: [
- {
- name: 'contxtful',
- ext: {
- rx: RX_FROM_API,
- params: {
- ev: config.params?.version,
- ci: config.params?.customer,
- },
- },
- },
- ],
+ let expectedData = {
+ name: 'contxtful',
+ ext: {
+ rx: RX_FROM_API,
+ params: {
+ ev: config.params?.version,
+ ci: config.params?.customer,
+ },
},
};
setTimeout(() => {
- const onDone = () => undefined;
- contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDone, config);
- let actualOrtb2 = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]];
- expect(actualOrtb2).to.deep.equal(expectedOrtb2);
+ const onDoneSpy = sinon.spy();
+ contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, config);
+
+ let data = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]].user.data[0];
+
+ expect(data.name).to.deep.equal(expectedData.name);
+ expect(data.ext.rx).to.deep.equal(expectedData.ext.rx);
+ expect(data.ext.params).to.deep.equal(expectedData.ext.params);
done();
}, TIMEOUT);
});
});
describe('getBidRequestData', function () {
- // TODO: commented out because of rule violations
- /*
it('uses non-expired info from session storage and adds receptivity to the reqBidsConfigObj', function (done) {
let config = buildInitConfig(VERSION, CUSTOMER);
+
// Simulate that there was a write to sessionStorage in the past.
- writeToStorage(config.params.bidders[0], +100);
+ let bidder = config.params.bidders[0];
- contxtfulSubmodule.init(config);
+ storage.setDataInSessionStorage(`${config.params.customer}_${bidder}`, JSON.stringify({exp: new Date().getTime() + 1000, rx: RX_FROM_SESSION_STORAGE}));
let reqBidsConfigObj = {
ortb2Fragments: {
@@ -472,33 +470,26 @@ describe('contxtfulRtdProvider', function () {
},
};
- let expectedOrtb2 = {
- user: {
- data: [
- {
- name: 'contxtful',
- ext: {
- rx: RX_FROM_SESSION_STORAGE,
- params: {
- ev: config.params?.version,
- ci: config.params?.customer,
- },
- },
- },
- ],
- },
- };
+ contxtfulSubmodule.init(config);
// Since the RX_CONNECTOR_IS_READY_EVENT event was not dispatched, the RX engine is not loaded.
+ contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, () => {}, config);
+
setTimeout(() => {
- const noOp = () => undefined;
- contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, noOp, buildInitConfig(VERSION, CUSTOMER));
- let actualOrtb2 = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]];
- expect(actualOrtb2).to.deep.equal(expectedOrtb2);
+ let ortb2BidderFragment = reqBidsConfigObj.ortb2Fragments.bidder[bidder];
+ let userData = ortb2BidderFragment.user.data;
+ let contxtfulData = userData[0];
+
+ expect(contxtfulData.name).to.be.equal('contxtful');
+ expect(contxtfulData.ext.rx).to.deep.equal(RX_FROM_SESSION_STORAGE);
+ expect(contxtfulData.ext.params).to.deep.equal({
+ ev: config.params.version,
+ ci: config.params.customer,
+ });
+
done();
}, TIMEOUT);
});
- */
});
describe('getBidRequestData', function () {
@@ -520,7 +511,7 @@ describe('contxtfulRtdProvider', function () {
const onDoneSpy = sinon.spy();
contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, config);
expect(onDoneSpy.callCount).to.equal(1);
- expect(RX_API_MOCK.receptivity.callCount).to.equal(1);
+ expect(RX_API_MOCK.receptivityBatched.callCount).to.equal(1);
done();
}, TIMEOUT);
});
@@ -539,29 +530,127 @@ describe('contxtfulRtdProvider', function () {
},
};
- let ortb2 = {
- user: {
- data: [
- {
- name: 'contxtful',
- ext: {
- rx: RX_FROM_API,
- params: {
- ev: config.params?.version,
- ci: config.params?.customer,
- },
- },
- },
- ],
+ let expectedData = {
+ name: 'contxtful',
+ ext: {
+ rx: RX_FROM_API,
+ params: {
+ ev: config.params?.version,
+ ci: config.params?.customer,
+ },
},
};
setTimeout(() => {
const onDoneSpy = sinon.spy();
contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, config);
- expect(reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]]).to.deep.equal(ortb2);
+
+ let data = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]].user.data[0];
+
+ expect(data.name).to.deep.equal(expectedData.name);
+ expect(data.ext.rx).to.deep.equal(expectedData.ext.rx);
+ expect(data.ext.params).to.deep.equal(expectedData.ext.params);
done();
}, TIMEOUT);
});
+
+ describe('before rxApi is loaded', function () {
+ const moveEventTheories = [
+ [
+ new PointerEvent('pointermove', { clientX: 1, clientY: 2 }),
+ { x: 1, y: 2 },
+ 'pointer move',
+ ]
+ ];
+
+ moveEventTheories.forEach(([event, expected, _description]) => {
+ it('adds move event', function (done) {
+ let config = buildInitConfig(VERSION, CUSTOMER);
+ contxtfulSubmodule.init(config);
+
+ window.dispatchEvent(event);
+
+ let reqBidsConfigObj = {
+ ortb2Fragments: {
+ global: {},
+ bidder: {},
+ },
+ };
+
+ setTimeout(() => {
+ const onDoneSpy = sinon.spy();
+ contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, config);
+
+ let ext = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]].user.data[0].ext;
+
+ let events = JSON.parse(atob(ext.events));
+
+ expect(events.ui.position.x).to.be.deep.equal(expected.x);
+ expect(events.ui.position.y).to.be.deep.equal(expected.y);
+ expect(Sinon.match.number.test(events.ui.position.timestampMs)).to.be.true;
+ done();
+ }, TIMEOUT);
+ });
+ });
+
+ it('adds screen event', function (done) {
+ let config = buildInitConfig(VERSION, CUSTOMER);
+ contxtfulSubmodule.init(config);
+
+ // Cannot change the window size from JS
+ // So we take the current size as expectation
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+
+ let reqBidsConfigObj = {
+ ortb2Fragments: {
+ global: {},
+ bidder: {},
+ },
+ };
+
+ setTimeout(() => {
+ const onDoneSpy = sinon.spy();
+ contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, config);
+
+ let ext = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]].user.data[0].ext;
+
+ let events = JSON.parse(atob(ext.events));
+
+ expect(events.ui.screen.topLeft).to.be.deep.equal({ x: 0, y: 0 }, 'screen top left');
+ expect(events.ui.screen.width).to.be.deep.equal(width, 'screen width');
+ expect(events.ui.screen.height).to.be.deep.equal(height, 'screen height');
+ expect(Sinon.match.number.test(events.ui.screen.timestampMs), 'screen timestamp').to.be.true;
+ done();
+ }, TIMEOUT);
+ });
+ })
});
+
+ describe('after rxApi is loaded', function () {
+ it('does not add event', function (done) {
+ let config = buildInitConfig(VERSION, CUSTOMER);
+ contxtfulSubmodule.init(config);
+ window.dispatchEvent(RX_CONNECTOR_IS_READY_EVENT);
+
+ let reqBidsConfigObj = {
+ ortb2Fragments: {
+ global: {},
+ bidder: {},
+ },
+ };
+
+ setTimeout(() => {
+ const onDoneSpy = sinon.spy();
+ contxtfulSubmodule.getBidRequestData(reqBidsConfigObj, onDoneSpy, config);
+
+ let ext = reqBidsConfigObj.ortb2Fragments.bidder[config.params.bidders[0]].user.data[0].ext;
+
+ let events = ext.events;
+
+ expect(events).to.be.undefined;
+ done();
+ }, TIMEOUT);
+ });
+ })
});
diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js
index e96867f4e84..8685bc1266f 100644
--- a/test/spec/modules/currency_spec.js
+++ b/test/spec/modules/currency_spec.js
@@ -3,7 +3,7 @@ import {
getCurrencyRates
} from 'test/fixtures/fixtures.js';
-import { getGlobal } from 'src/prebidGlobal.js';
+import {getGlobal} from 'src/prebidGlobal.js';
import {
setConfig,
@@ -13,9 +13,12 @@ import {
responseReady
} from 'modules/currency.js';
import {createBid} from '../../../src/bidfactory.js';
-import { EVENTS, STATUS, REJECTION_REASON } from '../../../src/constants.js';
+import * as utils from 'src/utils.js';
+import {EVENTS, STATUS, REJECTION_REASON} from '../../../src/constants.js';
import {server} from '../../mocks/xhr.js';
import * as events from 'src/events.js';
+import { enrichFPD } from '../../../src/fpd/enrichment.js';
+import {requestBidsHook} from '../../../modules/currency.js';
var assert = require('chai').assert;
var expect = require('chai').expect;
@@ -522,4 +525,67 @@ describe('currency', function () {
expect(innerBid.currency).to.equal('CNY');
});
});
+
+ describe('enrichFpd', function() {
+ function fpd(ortb2 = {}) {
+ return enrichFPD(Promise.resolve(ortb2));
+ }
+ it('should set adServerCurrency on ortb', function () {
+ fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates()));
+ setConfig({ adServerCurrency: 'EUR' });
+ return fpd({}).then((ortb) => {
+ expect(ortb.ext.prebid.adServerCurrency).to.eql('EUR')
+ })
+ })
+ });
+
+ describe('auctionDelay param', () => {
+ const continueAuction = sinon.stub();
+ let logWarnSpy;
+
+ beforeEach(function() {
+ sandbox = sinon.sandbox.create();
+ clock = sinon.useFakeTimers(1046952000000); // 2003-03-06T12:00:00Z
+ logWarnSpy = sinon.spy(utils, 'logWarn');
+ });
+
+ afterEach(function () {
+ clock.runAll();
+ sandbox.restore();
+ clock.restore();
+ utils.logWarn.restore();
+ continueAuction.resetHistory();
+ });
+
+ it('should delay auction start when auctionDelay set in module config', () => {
+ setConfig({auctionDelay: 2000, adServerCurrency: 'USD'});
+ const reqBidsConfigObj = {
+ auctionId: '128937'
+ };
+ requestBidsHook(continueAuction, reqBidsConfigObj);
+ clock.tick(1000);
+ expect(continueAuction.notCalled).to.be.true;
+ });
+
+ it('should start auction when auctionDelay time passed', () => {
+ setConfig({auctionDelay: 2000, adServerCurrency: 'USD'});
+ const reqBidsConfigObj = {
+ auctionId: '128937'
+ };
+ requestBidsHook(continueAuction, reqBidsConfigObj);
+ clock.tick(3000);
+ expect(logWarnSpy.calledOnce).to.equal(true);
+ expect(continueAuction.calledOnce).to.be.true;
+ });
+
+ it('should run auction if rates were fetched before auctionDelay time', () => {
+ setConfig({auctionDelay: 3000, adServerCurrency: 'USD'});
+ const reqBidsConfigObj = {
+ auctionId: '128937'
+ };
+ fakeCurrencyFileServer.respond();
+ requestBidsHook(continueAuction, reqBidsConfigObj);
+ expect(continueAuction.calledOnce).to.be.true;
+ });
+ });
});
diff --git a/test/spec/modules/dianomiBidAdapter_spec.js b/test/spec/modules/dianomiBidAdapter_spec.js
index b1ba5f60540..ef9283d3dad 100644
--- a/test/spec/modules/dianomiBidAdapter_spec.js
+++ b/test/spec/modules/dianomiBidAdapter_spec.js
@@ -3,6 +3,8 @@ import { assert } from 'chai';
import { spec } from 'modules/dianomiBidAdapter.js';
import { config } from 'src/config.js';
import { createEidsArray } from 'modules/userId/eids.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
describe('Dianomi adapter', () => {
let bids = [];
@@ -267,12 +269,14 @@ describe('Dianomi adapter', () => {
});
it('should send currency if defined', () => {
- config.setConfig({ currency: { adServerCurrency: 'EUR' } });
+ setCurrencyConfig({ adServerCurrency: 'EUR' })
let validBidRequests = [{ params: { smartadId: 1234 } }];
let refererInfo = { page: 'page' };
- let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data);
-
- assert.deepEqual(request.cur, ['EUR']);
+ return addFPDToBidderRequest({ refererInfo }).then(res => {
+ let request = JSON.parse(spec.buildRequests(validBidRequests, res).data);
+ assert.deepEqual(request.cur, ['EUR']);
+ setCurrencyConfig({});
+ });
});
it('should pass supply chain object', () => {
@@ -394,12 +398,18 @@ describe('Dianomi adapter', () => {
});
it('should request floor price in adserver currency', () => {
- config.setConfig({ currency: { adServerCurrency: 'GBP' } });
- const validBidRequests = [getBidWithFloor()];
- let imp = getRequestImps(validBidRequests)[0];
-
- assert.equal(imp.bidfloor, undefined);
- assert.equal(imp.bidfloorcur, 'GBP');
+ setCurrencyConfig({ adServerCurrency: 'GBP' })
+ let validBidRequests = [getBidWithFloor()];
+ let refererInfo = { page: 'page' };
+ return addFPDToBidderRequest({ refererInfo }).then(res => {
+ let imp = JSON.parse(
+ spec.buildRequests(validBidRequests, res).data
+ ).imp[0];
+
+ assert.equal(imp.bidfloor, undefined);
+ assert.equal(imp.bidfloorcur, 'GBP');
+ setCurrencyConfig({});
+ });
});
it('should add correct floor values', () => {
diff --git a/test/spec/modules/dsp_genieeBidAdapter_spec.js b/test/spec/modules/dsp_genieeBidAdapter_spec.js
index 6b2286a5fe5..b708acffc0b 100644
--- a/test/spec/modules/dsp_genieeBidAdapter_spec.js
+++ b/test/spec/modules/dsp_genieeBidAdapter_spec.js
@@ -1,6 +1,8 @@
import { expect } from 'chai';
import { spec } from 'modules/dsp_genieeBidAdapter.js';
import { config } from 'src/config';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
describe('Geniee adapter tests', () => {
const validBidderRequest = {
@@ -38,7 +40,8 @@ describe('Geniee adapter tests', () => {
ext: {
test: 1
},
- id: 'bid-id'
+ id: 'bid-id',
+ secure: 1
}
],
test: 1
@@ -73,13 +76,16 @@ describe('Geniee adapter tests', () => {
config.resetConfig();
});
it('uncomfortable (currency)', () => {
- config.setConfig({ currency: { adServerCurrency: 'TWD' } });
- const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest);
- expect(request).deep.equal({
- method: 'GET',
- url: 'https://rt.gsspat.jp/prebid_uncomfortable',
+ setCurrencyConfig({ adServerCurrency: 'TWD' });
+ return addFPDToBidderRequest(validBidderRequest).then(res => {
+ const request = spec.buildRequests(validBidderRequest.bids, res);
+ expect(request).deep.equal({
+ method: 'GET',
+ url: 'https://rt.gsspat.jp/prebid_uncomfortable',
+ });
+ setCurrencyConfig({});
+ config.resetConfig();
});
- config.resetConfig();
});
});
describe('interpretResponse function test', () => {
diff --git a/test/spec/modules/ehealthcaresolutionsBidAdapter_spec.js b/test/spec/modules/ehealthcaresolutionsBidAdapter_spec.js
new file mode 100644
index 00000000000..c420c387598
--- /dev/null
+++ b/test/spec/modules/ehealthcaresolutionsBidAdapter_spec.js
@@ -0,0 +1,322 @@
+import { expect } from 'chai';
+import { spec } from '../../../modules/ehealthcaresolutionsBidAdapter.js';
+import * as utils from '../../../src/utils.js';
+
+describe('ehealthcaresolutions adapter', function () {
+ let bannerRequest, nativeRequest;
+ let bannerResponse, nativeResponse, invalidBannerResponse, invalidNativeResponse;
+
+ beforeEach(function () {
+ bannerRequest = [
+ {
+ bidder: 'ehealthcaresolutions',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ params: {
+ placement_id: 111520,
+ bid_floor: 0.5
+ }
+ }
+ ];
+ nativeRequest = [
+ {
+ bidder: 'ehealthcaresolutions',
+ mediaTypes: {
+ native: {
+ title: { required: true, len: 100 },
+ image: { required: true, sizes: [300, 250] },
+ sponsored: { required: false },
+ clickUrl: { required: true },
+ desc: { required: true },
+ icon: { required: false, sizes: [50, 50] },
+ cta: { required: false }
+ }
+ },
+ params: {
+ placement_id: 111519,
+ bid_floor: 1
+ }
+ }
+ ];
+ bannerResponse = {
+ 'body': {
+ 'id': '006ac3b3-67f0-43bf-a33a-388b2f869fef',
+ 'seatbid': [{
+ 'bid': [{
+ 'id': '049d07ed-c07e-4890-9f19-5cf41406a42d',
+ 'impid': '286e606ac84a09',
+ 'price': 0.11,
+ 'adid': '368853',
+ 'adm': "",
+ 'adomain': ['google.com'],
+ 'iurl': 'https://cdn.ehealthcaresolutions.com/1_368853_1.png',
+ 'cid': '468133/368853',
+ 'crid': '368853',
+ 'w': 300,
+ 'h': 250,
+ 'cat': ['IAB7-19']
+ }],
+ 'seat': 'ehealthcaresolutions',
+ 'group': 0
+ }],
+ 'cur': 'USD',
+ 'bidid': 'BIDDER_-1'
+ }
+ };
+ nativeResponse = {
+ 'body': {
+ 'id': '453ade66-9113-4944-a674-5bbdcb9808ac',
+ 'seatbid': [{
+ 'bid': [{
+ 'id': '652c9a4c-66ea-4579-998b-cefe7b4cfecd',
+ 'impid': '2c3875bdbb1893',
+ 'price': 1.1,
+ 'adid': '368852',
+ 'adm': '{\"native\":{\"ver\":\"1.1\",\"assets\": [{\"id\":1,\"required\":1,\"title\":{\"text\":\"Integrative Approaches: Merging Traditional and Alternative \"}},{\"id\":2,\"required\":1,\"img\":{\"url\":\"https://cdn.ehealthcaresolutions.com/1_368852_0.png\",\"w\":500,\"h\":300,\"type\":\"3\"}},{\"id\":3,\"required\":0,\"data\":{\"value\":\"Diabetes In Control. A free weekly diabetes newsletter for Medical Professionals.\"}},{\"id\":4,\"required\":1,\"data\":{\"value\":\"Integrative Approaches: Merging Traditional and Alternative \"}},{\"id\":6,\"required\":1,\"data\":{\"value\":\"URL\"}}],\"link\":{\"url\":\"https://r.ehealthcaresolutions.com/adx-rtb-d/servlet/WebF_AdManager.AdLinkManager?qs=H4sIAAAAAAAAAx2U2QHDIAxDVwKDr3F84P1HqNLPtMSRpSeG9RiPXH+5a474KzO/47YX7UoP50m61fLujlNjb76/8ZiblkimHq5nL/ZRedp3031x1tnk55LjSNN6h9/Zq+qmaLLuWTl74m1ZJKnb+m2OtQm/3L4sb933pM92qMOgjJ41MYmPXKnndRVKs+9bfSEumoZIFpTXuXbCP+WXuzl725E3O+9odi5OJrnBzhwjx9+UnFN3nTNt1/HY5aeljKtvZYpoJHNXr8BWa8ysKQY7ZmNA3DHK2qRwY7+zLu+xm9z5eheJ4Pv2usSptvO3p7JHrnXn0T5yVWdccp9Yz7hhoz2iu2zqsXsGFZ9hh14J6yU4TkJ0BgnOY8tY3tS+n2qsw7xZfKuanSNbAo+9nkJ83i20+FwhfbJeDVOllXsdxmDWauYcSRgS9+yG5qHwUDjAxxA0iZnOjlsnI+y09+ATeTEwbAVGgp0Qu/ceP0kjUvpu1Ty7O9MoegfrmLPxdjUh3mJL+XhARby+Ax8iBckf6BQdn9W+DMlvmlzYLuLlIy7YociFOIvXvEiYYCMboVk8BLHbnw3Zmr5us3xbjtXL67L96F15acJXkM5BOmTaUbBkYGdCI+Et8XmlpbuE3xVQwmxryc2y4wP3ByuuP8GogPZz8OpPaBv8diWWUTrC2nnLhdNUrJRTKc9FepDvwHTDwfbbMCTSb4LhUIFkyFrw/i7GtkPi6NCCai6N47TgNsTnzZWRoVtOSLq7FsLiF29y0Gj0GHVPVYG3QOPS7Swc3UuiFAQZJx3YvpHA2geUgVBASMEL4vcDi2Dw3NPtBSC4EQEvH/uMILu6WyUwraywTeVpoqoHTqOoD84FzReKoWemJy6jyuiBieGlQIe6wY2elTaMOwEUFF5NagzPj6nauc0+aXzQN3Q72hxFAgtfORK60RRAHYZLYymIzSJcXLgRFsqrb1UoD+5Atq7TWojaLTfOyUvH9EeJvZEOilQAXrf/ALoI8ZhABQAA\"},\"imptrackers\":[\"https://i.ehealthcaresolutions.com/adx-rtb-d/servlet/WebF_AdManager.ImpCounter?price=${AUCTION_PRICE}&ids=111519,16703,468132,368852,211356,233,13,16704,1&cb=1728409547&ap=5.00000&vd=223.233.85.189,14,8&nm=0.00&GUIDs=[adx_guid],652c9a4c-66ea-4579-998b-cefe7b4cfecd,652c9a4c-66ea-4579-998b-cefe7b4cfecd,999999,-1_&info=2,-1,IN&adx_custom=&adx_custom_ex=~~~-1~~~0&cat=-1&ref=https%3A%2F%2Fqa-jboss.audiencelogy.com%2Ftn_native_prod.html\",\"https://i.ehealthcaresolutions.com/adx-rtb-d/servlet/WebF_AdManager.ImpTracker?price=${AUCTION_PRICE}&ids=111519,16703,468132,368852,211356,233,13,16704,1&cb=1728409547&ap=5.00000&vd=223.233.85.189,14,8&nm=0.00&GUIDs=[adx_guid],652c9a4c-66ea-4579-998b-cefe7b4cfecd,652c9a4c-66ea-4579-998b-cefe7b4cfecd,999999,-1_&info=2,-1,IN&adx_custom=&adx_custom_ex=~~~-1~~~0&cat=-1&ref=\",\"https://rtb-east.ehealthcaresolutions.com:9001/beacon?uid=44636f6605b06ec6d4389d6efb7e5054&cc=468132&fccap=5&nid=1\"]}}',
+ 'adomain': ['www.diabetesincontrol.com'],
+ 'iurl': 'https://cdn.ehealthcaresolutions.com/1_368852_0.png',
+ 'cid': '468132/368852',
+ 'crid': '368852',
+ 'cat': ['IAB7']
+ }],
+ 'seat': 'ehealthcaresolutions',
+ 'group': 0
+ }],
+ 'cur': 'USD',
+ 'bidid': 'BIDDER_-1'
+ }
+ };
+ invalidBannerResponse = {
+ 'body': {
+ 'id': '006ac3b3-67f0-43bf-a33a-388b2f869fef',
+ 'seatbid': [{
+ 'bid': [{
+ 'id': '049d07ed-c07e-4890-9f19-5cf41406a42d',
+ 'impid': '286e606ac84a09',
+ 'price': 0.11,
+ 'adid': '368853',
+ 'adm': 'invalid response',
+ 'adomain': ['google.com'],
+ 'iurl': 'https://cdn.ehealthcaresolutions.com/1_368853_1.png',
+ 'cid': '468133/368853',
+ 'crid': '368853',
+ 'w': 300,
+ 'h': 250,
+ 'cat': ['IAB7-19']
+ }],
+ 'seat': 'ehealthcaresolutions',
+ 'group': 0
+ }],
+ 'cur': 'USD',
+ 'bidid': 'BIDDER_-1'
+ }
+ };
+ invalidNativeResponse = {
+ 'body': {
+ 'id': '453ade66-9113-4944-a674-5bbdcb9808ac',
+ 'seatbid': [{
+ 'bid': [{
+ 'id': '652c9a4c-66ea-4579-998b-cefe7b4cfecd',
+ 'impid': '2c3875bdbb1893',
+ 'price': 1.1,
+ 'adid': '368852',
+ 'adm': 'invalid response',
+ 'adomain': ['www.diabetesincontrol.com'],
+ 'iurl': 'https://cdn.ehealthcaresolutions.com/1_368852_0.png',
+ 'cid': '468132/368852',
+ 'crid': '368852',
+ 'cat': ['IAB7']
+ }],
+ 'seat': 'ehealthcaresolutions',
+ 'group': 0
+ }],
+ 'cur': 'USD',
+ 'bidid': 'BIDDER_-1'
+ }
+ };
+ });
+
+ describe('validations', function () {
+ it('isBidValid : placement_id is passed', function () {
+ let bid = {
+ bidder: 'ehealthcaresolutions',
+ params: {
+ placement_id: 111520
+ }
+ },
+ isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equals(true);
+ });
+ it('isBidValid : placement_id is not passed', function () {
+ let bid = {
+ bidder: 'ehealthcaresolutions',
+ params: {
+ width: 300,
+ height: 250,
+ domain: '',
+ bid_floor: 0.5
+ }
+ },
+ isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equals(false);
+ });
+ });
+ describe('Validate Banner Request', function () {
+ it('Immutable bid request validate', function () {
+ let _Request = utils.deepClone(bannerRequest),
+ bidRequest = spec.buildRequests(bannerRequest);
+ expect(bannerRequest).to.deep.equal(_Request);
+ });
+ it('Validate bidder connection', function () {
+ let _Request = spec.buildRequests(bannerRequest);
+ expect(_Request.url).to.equal('https://rtb.ehealthcaresolutions.com/hb');
+ expect(_Request.method).to.equal('POST');
+ expect(_Request.options.contentType).to.equal('application/json');
+ });
+ it('Validate bid request : Impression', function () {
+ let _Request = spec.buildRequests(bannerRequest);
+ let data = JSON.parse(_Request.data);
+ // expect(data.at).to.equal(1); // auction type
+ expect(data[0].imp[0].id).to.equal(bannerRequest[0].bidId);
+ expect(data[0].placementId).to.equal(111520);
+ });
+ it('Validate bid request : ad size', function () {
+ let _Request = spec.buildRequests(bannerRequest);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].imp[0].banner).to.be.a('object');
+ expect(data[0].imp[0].banner.w).to.equal(300);
+ expect(data[0].imp[0].banner.h).to.equal(250);
+ });
+ it('Validate bid request : user object', function () {
+ let _Request = spec.buildRequests(bannerRequest);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].user).to.be.a('object');
+ expect(data[0].user.id).to.be.a('string');
+ });
+ it('Validate bid request : CCPA Check', function () {
+ let bidRequest = {
+ uspConsent: '1NYN'
+ };
+ let _Request = spec.buildRequests(bannerRequest, bidRequest);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].regs.ext.us_privacy).to.equal('1NYN');
+ // let _bidRequest = {};
+ // let _Request1 = spec.buildRequests(request, _bidRequest);
+ // let data1 = JSON.parse(_Request1.data);
+ // expect(data1.regs).to.equal(undefined);
+ });
+ });
+ describe('Validate banner response ', function () {
+ it('Validate bid response : valid bid response', function () {
+ let _Request = spec.buildRequests(bannerRequest);
+ let bResponse = spec.interpretResponse(bannerResponse, _Request);
+ expect(bResponse).to.be.an('array').with.length.above(0);
+ expect(bResponse[0].requestId).to.equal(bannerResponse.body.seatbid[0].bid[0].impid);
+ expect(bResponse[0].width).to.equal(bannerResponse.body.seatbid[0].bid[0].w);
+ expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h);
+ expect(bResponse[0].currency).to.equal('USD');
+ expect(bResponse[0].netRevenue).to.equal(false);
+ expect(bResponse[0].mediaType).to.equal('banner');
+ expect(bResponse[0].meta.advertiserDomains).to.deep.equal(['google.com']);
+ expect(bResponse[0].ttl).to.equal(300);
+ expect(bResponse[0].creativeId).to.equal(bannerResponse.body.seatbid[0].bid[0].crid);
+ expect(bResponse[0].dealId).to.equal(bannerResponse.body.seatbid[0].bid[0].dealId);
+ });
+ it('Invalid bid response check ', function () {
+ let bRequest = spec.buildRequests(bannerRequest);
+ let response = spec.interpretResponse(invalidBannerResponse, bRequest);
+ expect(response[0].ad).to.equal('invalid response');
+ });
+ });
+ describe('Validate Native Request', function () {
+ it('Immutable bid request validate', function () {
+ let _Request = utils.deepClone(nativeRequest),
+ bidRequest = spec.buildRequests(nativeRequest);
+ expect(nativeRequest).to.deep.equal(_Request);
+ });
+ it('Validate bidder connection', function () {
+ let _Request = spec.buildRequests(nativeRequest);
+ expect(_Request.url).to.equal('https://rtb.ehealthcaresolutions.com/hb');
+ expect(_Request.method).to.equal('POST');
+ expect(_Request.options.contentType).to.equal('application/json');
+ });
+ it('Validate bid request : Impression', function () {
+ let _Request = spec.buildRequests(nativeRequest);
+ let data = JSON.parse(_Request.data);
+ // expect(data.at).to.equal(1); // auction type
+ expect(data[0].imp[0].id).to.equal(nativeRequest[0].bidId);
+ expect(data[0].placementId).to.equal(111519);
+ });
+ it('Validate bid request : user object', function () {
+ let _Request = spec.buildRequests(nativeRequest);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].user).to.be.a('object');
+ expect(data[0].user.id).to.be.a('string');
+ });
+ it('Validate bid request : CCPA Check', function () {
+ let bidRequest = {
+ uspConsent: '1NYN'
+ };
+ let _Request = spec.buildRequests(nativeRequest, bidRequest);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].regs.ext.us_privacy).to.equal('1NYN');
+ // let _bidRequest = {};
+ // let _Request1 = spec.buildRequests(request, _bidRequest);
+ // let data1 = JSON.parse(_Request1.data);
+ // expect(data1.regs).to.equal(undefined);
+ });
+ });
+ describe('Validate native response ', function () {
+ it('Validate bid response : valid bid response', function () {
+ let _Request = spec.buildRequests(nativeRequest);
+ let bResponse = spec.interpretResponse(nativeResponse, _Request);
+ expect(bResponse).to.be.an('array').with.length.above(0);
+ expect(bResponse[0].requestId).to.equal(nativeResponse.body.seatbid[0].bid[0].impid);
+ // expect(bResponse[0].width).to.equal(bannerResponse.body.seatbid[0].bid[0].w);
+ // expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h);
+ expect(bResponse[0].currency).to.equal('USD');
+ expect(bResponse[0].netRevenue).to.equal(false);
+ expect(bResponse[0].mediaType).to.equal('native');
+ expect(bResponse[0].native.clickUrl).to.be.a('string').and.not.be.empty;
+ expect(bResponse[0].native.impressionTrackers).to.be.an('array').with.length.above(0);
+ expect(bResponse[0].native.title).to.be.a('string').and.not.be.empty;
+ expect(bResponse[0].native.image.url).to.be.a('string').and.not.be.empty;
+ expect(bResponse[0].meta.advertiserDomains).to.deep.equal(['www.diabetesincontrol.com']);
+ expect(bResponse[0].ttl).to.equal(300);
+ expect(bResponse[0].creativeId).to.equal(nativeResponse.body.seatbid[0].bid[0].crid);
+ expect(bResponse[0].dealId).to.equal(nativeResponse.body.seatbid[0].bid[0].dealId);
+ });
+ });
+ describe('GPP and coppa', function () {
+ it('Request params check with GPP Consent', function () {
+ let bidderReq = { gppConsent: { gppString: 'gpp-string-test', applicableSections: [5] } };
+ let _Request = spec.buildRequests(bannerRequest, bidderReq);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].regs.gpp).to.equal('gpp-string-test');
+ expect(data[0].regs.gpp_sid[0]).to.equal(5);
+ });
+ it('Request params check with GPP Consent read from ortb2', function () {
+ let bidderReq = {
+ ortb2: {
+ regs: {
+ gpp: 'gpp-test-string',
+ gpp_sid: [5]
+ }
+ }
+ };
+ let _Request = spec.buildRequests(bannerRequest, bidderReq);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].regs.gpp).to.equal('gpp-test-string');
+ expect(data[0].regs.gpp_sid[0]).to.equal(5);
+ });
+ it(' Bid request should have coppa flag if its true', () => {
+ let bidderReq = { ortb2: { regs: { coppa: 1 } } };
+ let _Request = spec.buildRequests(bannerRequest, bidderReq);
+ let data = JSON.parse(_Request.data);
+ expect(data[0].regs.coppa).to.equal(1);
+ });
+ });
+});
diff --git a/test/spec/modules/equativBidAdapter_spec.js b/test/spec/modules/equativBidAdapter_spec.js
index c52507a000b..9f767a3cd4e 100644
--- a/test/spec/modules/equativBidAdapter_spec.js
+++ b/test/spec/modules/equativBidAdapter_spec.js
@@ -1,9 +1,20 @@
import { BANNER } from 'src/mediaTypes.js';
import { getBidFloor } from 'libraries/equativUtils/equativUtils.js'
import { converter, spec, storage } from 'modules/equativBidAdapter.js';
+import * as utils from '../../../src/utils.js';
describe('Equativ bid adapter tests', () => {
- const DEFAULT_BID_REQUESTS = [
+ let sandBox;
+
+ beforeEach(() => {
+ sandBox = sinon.createSandbox();
+ sandBox.stub(utils, 'logError');
+ sandBox.stub(utils, 'logWarn');
+ });
+
+ afterEach(() => sandBox.restore());
+
+ const DEFAULT_BANNER_BID_REQUESTS = [
{
adUnitCode: 'eqtv_42',
bidId: 'abcd1234',
@@ -25,12 +36,56 @@ describe('Equativ bid adapter tests', () => {
tid: 'zsfgzzg',
},
},
- },
+ }
+ ];
+
+ const DEFAULT_VIDEO_BID_REQUESTS = [
+ {
+ adUnitCode: 'eqtv_43',
+ bidId: 'efgh5678',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ playerSize: [[640, 480]],
+ pos: 3,
+ skip: 1,
+ linearity: 1,
+ minduration: 10,
+ maxduration: 30,
+ minbitrate: 300,
+ maxbitrate: 600,
+ w: 640,
+ h: 480,
+ playbackmethod: [1],
+ api: [3],
+ mimes: ['video/x-flv', 'video/mp4'],
+ // protocols: [2, 3], // used in older adapter ... including as comment for reference
+ startdelay: 42,
+ battr: [13, 14],
+ placement: 1,
+ },
+ },
+ bidder: 'equativ',
+ params: {
+ networkId: 111,
+ },
+ requestId: 'abcd1234',
+ ortb2Imp: {
+ ext: {
+ tid: 'zsgzgzz',
+ },
+ },
+ }
];
- const DEFAULT_BIDDER_REQUEST = {
+ const DEFAULT_BANNER_BIDDER_REQUEST = {
+ bidderCode: 'equativ',
+ bids: DEFAULT_BANNER_BID_REQUESTS,
+ };
+
+ const DEFAULT_VIDEO_BIDDER_REQUEST = {
bidderCode: 'equativ',
- bids: DEFAULT_BID_REQUESTS,
+ bids: DEFAULT_VIDEO_BID_REQUESTS,
};
const SAMPLE_RESPONSE = {
@@ -62,25 +117,18 @@ describe('Equativ bid adapter tests', () => {
},
};
- // const RESPONSE_WITH_DSP_PIXELS = {
- // ...SAMPLE_RESPONSE,
- // body: {
- // dspPixels: ['1st-pixel', '2nd-pixel', '3rd-pixel']
- // }
- // };
-
describe('buildRequests', () => {
- it('should build correct request using ORTB converter', () => {
+ it('should build correct requests using ORTB converter', () => {
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
);
const dataFromConverter = converter.toORTB({
- bidderRequest: DEFAULT_BIDDER_REQUEST,
- bidRequests: DEFAULT_BID_REQUESTS,
+ bidderRequest: DEFAULT_BANNER_BIDDER_REQUEST,
+ bidRequests: DEFAULT_BANNER_BID_REQUESTS,
});
- expect(request).to.deep.equal({
- data: { ...dataFromConverter, id: request.data.id },
+ expect(request[0]).to.deep.equal({
+ data: { ...dataFromConverter, id: request[0].data.id },
method: 'POST',
url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169',
});
@@ -88,10 +136,10 @@ describe('Equativ bid adapter tests', () => {
it('should add ext.bidder to imp object when siteId is defined', () => {
const bidRequests = [
- { ...DEFAULT_BID_REQUESTS[0], params: { siteId: 123 } },
+ { ...DEFAULT_BANNER_BID_REQUESTS[0], params: { siteId: 123 } },
];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.imp[0].ext.bidder).to.deep.equal({
siteId: 123,
});
@@ -99,10 +147,10 @@ describe('Equativ bid adapter tests', () => {
it('should add ext.bidder to imp object when pageId is defined', () => {
const bidRequests = [
- { ...DEFAULT_BID_REQUESTS[0], params: { pageId: 123 } },
+ { ...DEFAULT_BANNER_BID_REQUESTS[0], params: { pageId: 123 } },
];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.imp[0].ext.bidder).to.deep.equal({
pageId: 123,
});
@@ -110,33 +158,33 @@ describe('Equativ bid adapter tests', () => {
it('should add ext.bidder to imp object when formatId is defined', () => {
const bidRequests = [
- { ...DEFAULT_BID_REQUESTS[0], params: { formatId: 123 } },
+ { ...DEFAULT_BANNER_BID_REQUESTS[0], params: { formatId: 123 } },
];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.imp[0].ext.bidder).to.deep.equal({
formatId: 123,
});
});
it('should not add ext.bidder to imp object when siteId, pageId, formatId are not defined', () => {
- const bidRequests = [{ ...DEFAULT_BID_REQUESTS[0], params: {} }];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidRequests = [{ ...DEFAULT_BANNER_BID_REQUESTS[0], params: {} }];
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.imp[0].ext.bidder).to.be.undefined;
});
it('should add site.publisher.id param', () => {
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
expect(request.data.site.publisher.id).to.equal(111);
});
it('should pass ortb2.site.publisher.id', () => {
const bidRequests = [{
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
ortb2: {
site: {
publisher: {
@@ -146,28 +194,28 @@ describe('Equativ bid adapter tests', () => {
}
}];
delete bidRequests[0].params;
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.site.publisher.id).to.equal(98);
});
it('should pass networkId as site.publisher.id', () => {
const bidRequests = [{
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
ortb2: {
site: {
publisher: {}
}
}
}];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.site.publisher.id).to.equal(111);
});
it('should pass ortb2.app.publisher.id', () => {
const bidRequests = [{
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
ortb2: {
app: {
publisher: {
@@ -177,28 +225,28 @@ describe('Equativ bid adapter tests', () => {
}
}];
delete bidRequests[0].params;
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.app.publisher.id).to.equal(27);
});
it('should pass networkId as app.publisher.id', () => {
const bidRequests = [{
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
ortb2: {
app: {
publisher: {}
}
}
}];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.app.publisher.id).to.equal(111);
});
it('should pass ortb2.dooh.publisher.id', () => {
const bidRequests = [{
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
ortb2: {
dooh: {
publisher: {
@@ -208,55 +256,55 @@ describe('Equativ bid adapter tests', () => {
}
}];
delete bidRequests[0].params;
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.dooh.publisher.id).to.equal(35);
});
it('should pass networkId as dooh.publisher.id', () => {
const bidRequests = [{
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
ortb2: {
dooh: {
publisher: {}
}
}
}];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.dooh.publisher.id).to.equal(111);
});
it('should send default floor of 0.0', () => {
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
expect(request.data.imp[0]).to.have.property('bidfloor').that.eq(0.0);
});
it('should send secure connection', () => {
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
expect(request.data.imp[0]).to.have.property('secure').that.eq(1);
});
it('should have tagid', () => {
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
- expect(request.data.imp[0]).to.have.property('tagid').that.eq(DEFAULT_BID_REQUESTS[0].adUnitCode);
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
+ expect(request.data.imp[0]).to.have.property('tagid').that.eq(DEFAULT_BANNER_BID_REQUESTS[0].adUnitCode);
});
it('should remove dt', () => {
const bidRequests = [
- { ...DEFAULT_BID_REQUESTS[0], ortb2Imp: { dt: 1728377558235 } }
+ { ...DEFAULT_BANNER_BID_REQUESTS[0], ortb2Imp: { dt: 1728377558235 } }
];
- const bidderRequest = { ...DEFAULT_BIDDER_REQUEST, bids: bidRequests };
- const request = spec.buildRequests(bidRequests, bidderRequest);
+ const bidderRequest = { ...DEFAULT_BANNER_BIDDER_REQUEST, bids: bidRequests };
+ const request = spec.buildRequests(bidRequests, bidderRequest)[0];
expect(request.data.imp[0]).to.not.have.property('dt');
});
@@ -268,9 +316,9 @@ describe('Equativ bid adapter tests', () => {
getCookieStub.callsFake(cookieName => cookieData[cookieName]);
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
expect(request.data.user).to.have.property('buyeruid').that.eq(cookieData['eqt_pid']);
@@ -282,9 +330,9 @@ describe('Equativ bid adapter tests', () => {
getCookieStub.callsFake(() => null);
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
expect(request.data).to.not.have.property('user');
@@ -296,25 +344,163 @@ describe('Equativ bid adapter tests', () => {
getCookieStub.callsFake(() => undefined);
const bidRequest = {
- ...DEFAULT_BIDDER_REQUEST,
+ ...DEFAULT_BANNER_BIDDER_REQUEST,
ortb2: {
user: {
buyeruid: 'buyeruid-provided-by-publisher'
}
}
};
- const request = spec.buildRequests([ DEFAULT_BID_REQUESTS[0] ], bidRequest);
+ const request = spec.buildRequests([ DEFAULT_BANNER_BID_REQUESTS[0] ], bidRequest)[0];
expect(request.data.user.buyeruid).to.deep.eq(bidRequest.ortb2.user.buyeruid);
getCookieStub.restore();
});
+
+ it('should build a video request properly under normal circumstances', () => {
+ // ASSEMBLE
+ if (FEATURES.VIDEO) {
+ // ACT
+ const request = spec.buildRequests(DEFAULT_VIDEO_BID_REQUESTS, {})[0].data;
+
+ // ASSERT
+ expect(request.imp[0]).to.have.property('video');
+
+ const videoObj = request.imp[0].video;
+
+ expect(videoObj).to.have.property('api').and.to.deep.equal([3]);
+ expect(videoObj).to.have.property('battr').and.to.deep.equal([13, 14]);
+ expect(videoObj).to.have.property('linearity').and.to.equal(1);
+ expect(videoObj).to.have.property('mimes').and.to.deep.equal(['video/x-flv', 'video/mp4']);
+ expect(videoObj).to.have.property('minbitrate').and.to.equal(300);
+ expect(videoObj).to.have.property('maxbitrate').and.to.equal(600);
+ expect(videoObj).to.have.property('minduration').and.to.equal(10);
+ expect(videoObj).to.have.property('maxduration').and.to.equal(30);
+ expect(videoObj).to.have.property('placement').and.to.equal(1);
+ expect(videoObj).to.have.property('playbackmethod').and.to.deep.equal([1]);
+ expect(videoObj).to.have.property('pos').and.to.equal(3);
+ expect(videoObj).to.have.property('skip').and.to.equal(1);
+ expect(videoObj).to.have.property('startdelay').and.to.equal(42);
+ expect(videoObj).to.have.property('w').and.to.equal(640);
+ expect(videoObj).to.have.property('h').and.to.equal(480);
+ expect(videoObj).not.to.have.property('ext');
+ }
+ });
+
+ it('should read and pass ortb2Imp.rwdd', () => {
+ // ASSEMBLE
+ if (FEATURES.VIDEO) {
+ const bidRequestsWithOrtb2ImpRwdd = [
+ {
+ ...DEFAULT_VIDEO_BID_REQUESTS[0],
+ ortb2Imp: {
+ rwdd: 1
+ }
+ }
+ ];
+ // ACT
+ const request = spec.buildRequests(bidRequestsWithOrtb2ImpRwdd, {})[0].data;
+
+ // ASSERT
+ expect(request.imp[0]).to.have.property('rwdd').and.to.equal(1);
+ }
+ });
+
+ it('should read mediaTypes.video.ext.rewarded and pass as rwdd', () => {
+ // ASSEMBLE
+ if (FEATURES.VIDEO) {
+ const bidRequestsWithExtReworded = [
+ {
+ ...DEFAULT_VIDEO_BID_REQUESTS[0],
+ mediaTypes: {
+ video: {
+ ext: {
+ rewarded: 1
+ }
+ }
+ }
+ }
+ ];
+ // ACT
+ const request = spec.buildRequests(bidRequestsWithExtReworded, {})[0].data;
+
+ // ASSERT
+ expect(request.imp[0]).to.have.property('rwdd').and.to.equal(1);
+ }
+ });
+
+ it('should prioritize ortb2Imp.rwdd over mediaTypes.video.ext.rewarded', () => {
+ // ASSEMBLE
+ if (FEATURES.VIDEO) {
+ const bidRequestsWithBothRewordedParams = [
+ {
+ ...DEFAULT_VIDEO_BID_REQUESTS[0],
+ mediaTypes: {
+ video: {
+ ext: {
+ rewarded: 1
+ }
+ }
+ },
+ ortb2Imp: {
+ rwdd: 2
+ }
+ }
+ ];
+ // ACT
+ const request = spec.buildRequests(bidRequestsWithBothRewordedParams, {})[0].data;
+
+ // ASSERT
+ expect(request.imp[0]).to.have.property('rwdd').and.to.equal(2);
+ }
+ });
+
+ it('should warn about missing required properties for video requests', () => {
+ // ASSEMBLE
+ const missingRequiredVideoRequest = DEFAULT_VIDEO_BID_REQUESTS[0];
+
+ // removing required properties
+ delete missingRequiredVideoRequest.mediaTypes.video.mimes;
+ delete missingRequiredVideoRequest.mediaTypes.video.placement;
+
+ const bidRequests = [ missingRequiredVideoRequest ];
+ const bidderRequest = { ...DEFAULT_VIDEO_BIDDER_REQUEST, bids: bidRequests };
+
+ // ACT
+ spec.buildRequests(bidRequests, bidderRequest);
+
+ // ASSERT
+ expect(utils.logWarn.callCount).to.equal(2);
+ expect(utils.logWarn.getCall(0).args[0]).to.satisfy(arg => arg.includes('"mimes" is missing'));
+ expect(utils.logWarn.getCall(1).args[0]).to.satisfy(arg => arg.includes('"placement" is missing'));
+ });
+
+ it('should not send a video request when it has an empty body and no other impressions with any media types are defined', () => {
+ // ASSEMBLE
+ const emptyVideoRequest = {
+ ...DEFAULT_VIDEO_BID_REQUESTS[0],
+ mediaTypes: {
+ video: {}
+ }
+ };
+ const bidRequests = [ emptyVideoRequest ];
+ const bidderRequest = { ...DEFAULT_VIDEO_BIDDER_REQUEST, bids: bidRequests };
+
+ // ACT
+ const request = spec.buildRequests(bidRequests, bidderRequest);
+
+ // ASSERT
+ expect(utils.logError.calledOnce).to.equal(true);
+ expect(utils.logError.args[0][0]).to.satisfy(arg => arg.includes('No request'));
+ expect(request).to.be.undefined;
+ });
});
describe('getBidFloor', () => {
it('should return floor of 0.0 if floor module not available', () => {
const bid = {
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
getFloor: false,
};
expect(getBidFloor(bid)).to.deep.eq(0.0);
@@ -330,7 +516,7 @@ describe('Equativ bid adapter tests', () => {
it('should return proper min floor', () => {
const bid = {
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
getFloor: data => {
if (data.size[0] === 300 && data.size[1] === 250) {
return { floor: 1.13 };
@@ -346,7 +532,7 @@ describe('Equativ bid adapter tests', () => {
it('should return global media type floor if no rule for size', () => {
const bid = {
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
getFloor: data => {
if (data.size[0] === 728 && data.size[1] === 90) {
return { floor: 1.13 };
@@ -362,7 +548,7 @@ describe('Equativ bid adapter tests', () => {
it('should return floor of 0 if no rule for size', () => {
const bid = {
- ...DEFAULT_BID_REQUESTS[0],
+ ...DEFAULT_BANNER_BID_REQUESTS[0],
getFloor: data => {
if (data.size[0] === 728 && data.size[1] === 90) {
return { floor: 1.13 };
@@ -466,9 +652,9 @@ describe('Equativ bid adapter tests', () => {
describe('interpretResponse', () => {
it('should return data returned by ORTB converter', () => {
const request = spec.buildRequests(
- DEFAULT_BID_REQUESTS,
- DEFAULT_BIDDER_REQUEST
- );
+ DEFAULT_BANNER_BID_REQUESTS,
+ DEFAULT_BANNER_BIDDER_REQUEST
+ )[0];
const bids = spec.interpretResponse(SAMPLE_RESPONSE, request);
expect(bids).to.deep.equal(
converter.fromORTB({
diff --git a/test/spec/modules/gameraRtdProvider_spec.js b/test/spec/modules/gameraRtdProvider_spec.js
new file mode 100644
index 00000000000..63029d85545
--- /dev/null
+++ b/test/spec/modules/gameraRtdProvider_spec.js
@@ -0,0 +1,223 @@
+import { submodule } from 'src/hook.js';
+import { getGlobal } from 'src/prebidGlobal.js';
+import * as utils from 'src/utils.js';
+import { subModuleObj } from 'modules/gameraRtdProvider.js';
+
+describe('gameraRtdProvider', function () {
+ let logErrorSpy;
+
+ beforeEach(function () {
+ logErrorSpy = sinon.spy(utils, 'logError');
+ });
+
+ afterEach(function () {
+ logErrorSpy.restore();
+ });
+
+ describe('subModuleObj', function () {
+ it('should have the correct module name', function () {
+ expect(subModuleObj.name).to.equal('gamera');
+ });
+
+ it('successfully instantiates and returns true', function () {
+ expect(subModuleObj.init()).to.equal(true);
+ });
+ });
+
+ describe('getBidRequestData', function () {
+ const reqBidsConfigObj = {
+ adUnits: [{
+ code: 'test-div',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ ortb2Imp: {
+ ext: {
+ data: {
+ pbadslot: 'homepage-top-rect',
+ adUnitSpecificAttribute: '123',
+ }
+ }
+ },
+ bids: [{ bidder: 'test' }]
+ }],
+ ortb2Fragments: {
+ global: {
+ site: {
+ name: 'example',
+ domain: 'page.example.com',
+ // OpenRTB 2.5 spec / Content Taxonomy
+ cat: ['IAB2'],
+ sectioncat: ['IAB2-2'],
+ pagecat: ['IAB2-2'],
+
+ page: 'https://page.example.com/here.html',
+ ref: 'https://ref.example.com',
+ keywords: 'power tools, drills',
+ search: 'drill',
+ content: {
+ userrating: '4',
+ data: [{
+ name: 'www.dataprovider1.com', // who resolved the segments
+ ext: {
+ segtax: 7, // taxonomy used to encode the segments
+ cids: ['iris_c73g5jq96mwso4d8']
+ },
+ // the bare minimum are the IDs. These IDs are the ones from the new IAB Content Taxonomy v3
+ segment: [{ id: '687' }, { id: '123' }]
+ }]
+ },
+ ext: {
+ data: { // fields that aren't part of openrtb 2.6
+ pageType: 'article',
+ category: 'repair'
+ }
+ }
+ },
+ // this is where the user data is placed
+ user: {
+ keywords: 'a,b',
+ data: [{
+ name: 'dataprovider.com',
+ ext: {
+ segtax: 4
+ },
+ segment: [{
+ id: '1'
+ }]
+ }],
+ ext: {
+ data: {
+ registered: true,
+ interests: ['cars']
+ }
+ }
+ }
+ }
+ }
+ };
+
+ let callback;
+
+ beforeEach(function () {
+ callback = sinon.spy();
+ window.gamera = undefined;
+ });
+
+ it('should queue command when gamera.getPrebidSegments is not available', function () {
+ subModuleObj.getBidRequestData(reqBidsConfigObj, callback);
+
+ expect(window.gamera).to.exist;
+ expect(window.gamera.cmd).to.be.an('array');
+ expect(window.gamera.cmd.length).to.equal(1);
+ expect(callback.called).to.be.false;
+
+ // our callback should be executed if command queue is flushed
+ window.gamera.cmd.forEach(command => command());
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should call enrichAuction directly when gamera.getPrebidSegments is available', function () {
+ window.gamera = {
+ getPrebidSegments: () => ({})
+ };
+
+ subModuleObj.getBidRequestData(reqBidsConfigObj, callback);
+
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should handle errors gracefully', function () {
+ window.gamera = {
+ getPrebidSegments: () => {
+ throw new Error('Test error');
+ }
+ };
+
+ subModuleObj.getBidRequestData(reqBidsConfigObj, callback);
+
+ expect(logErrorSpy.calledWith('gameraRtdProvider', 'Error getting segments:')).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ describe('segment enrichment', function () {
+ const mockSegments = {
+ user: {
+ data: [{
+ name: 'gamera.ai',
+ ext: {
+ segtax: 4,
+ },
+ segment: [{ id: 'user-1' }]
+ }]
+ },
+ site: {
+ keywords: 'gamera,article,keywords',
+ content: {
+ data: [{
+ name: 'gamera.ai',
+ ext: {
+ segtax: 7,
+ },
+ segment: [{ id: 'site-1' }]
+ }]
+ }
+ },
+ adUnits: {
+ 'test-div': {
+ key: 'value',
+ ext: {
+ data: {
+ gameraSegment: 'ad-1',
+ }
+ }
+ }
+ }
+ };
+
+ beforeEach(function () {
+ window.gamera = {
+ getPrebidSegments: () => mockSegments
+ };
+ });
+
+ it('should enrich ortb2Fragments with user data', function () {
+ subModuleObj.getBidRequestData(reqBidsConfigObj, callback);
+
+ expect(reqBidsConfigObj.ortb2Fragments.global.user.data).to.deep.include(mockSegments.user.data[0]);
+
+ // check if existing attributes are not overwritten
+ expect(reqBidsConfigObj.ortb2Fragments.global.user.data[0].ext.segtax).to.equal(4);
+ expect(reqBidsConfigObj.ortb2Fragments.global.user.data[0].segment[0].id).to.equal('1');
+ expect(reqBidsConfigObj.ortb2Fragments.global.user.keywords).to.equal('a,b');
+ expect(reqBidsConfigObj.ortb2Fragments.global.user.ext.data.registered).to.equal(true);
+ });
+
+ it('should enrich ortb2Fragments with site data', function () {
+ subModuleObj.getBidRequestData(reqBidsConfigObj, callback);
+
+ expect(reqBidsConfigObj.ortb2Fragments.global.site.content.data).to.deep.include(mockSegments.site.content.data[0]);
+ expect(reqBidsConfigObj.ortb2Fragments.global.site.keywords).to.equal('gamera,article,keywords');
+
+ // check if existing attributes are not overwritten
+ expect(reqBidsConfigObj.ortb2Fragments.global.site.content.data[0].ext.segtax).to.equal(7);
+ expect(reqBidsConfigObj.ortb2Fragments.global.site.content.data[0].segment[0].id).to.equal('687');
+ expect(reqBidsConfigObj.ortb2Fragments.global.site.ext.data.category).to.equal('repair');
+ expect(reqBidsConfigObj.ortb2Fragments.global.site.content.userrating).to.equal('4');
+ });
+
+ it('should enrich adUnits with segment data', function () {
+ subModuleObj.getBidRequestData(reqBidsConfigObj, callback);
+
+ expect(reqBidsConfigObj.adUnits[0].ortb2Imp.key).to.equal('value');
+ expect(reqBidsConfigObj.adUnits[0].ortb2Imp.ext.data.gameraSegment).to.equal('ad-1');
+
+ // check if existing attributes are not overwritten
+ expect(reqBidsConfigObj.adUnits[0].ortb2Imp.ext.data.adUnitSpecificAttribute).to.equal('123');
+ expect(reqBidsConfigObj.adUnits[0].ortb2Imp.ext.data.pbadslot).to.equal('homepage-top-rect');
+ });
+ });
+ });
+});
diff --git a/test/spec/modules/hadronIdSystem_spec.js b/test/spec/modules/hadronIdSystem_spec.js
index 899dc640dc1..70aaf06bcc8 100644
--- a/test/spec/modules/hadronIdSystem_spec.js
+++ b/test/spec/modules/hadronIdSystem_spec.js
@@ -1,15 +1,15 @@
-import { hadronIdSubmodule, storage } from 'modules/hadronIdSystem.js';
-import { server } from 'test/mocks/xhr.js';
-import * as utils from 'src/utils.js';
+import {hadronIdSubmodule, storage, LS_TAM_KEY} from 'modules/hadronIdSystem.js';
+import {server} from 'test/mocks/xhr.js';
import {attachIdSystem} from '../../../modules/userId/index.js';
import {createEidsArray} from '../../../modules/userId/eids.js';
import {expect} from 'chai/index.mjs';
describe('HadronIdSystem', function () {
- describe('getId', function() {
+ const HADRON_TEST = 'tstCachedHadronId1';
+ describe('getId', function () {
let getDataFromLocalStorageStub;
- beforeEach(function() {
+ beforeEach(function () {
getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
});
@@ -17,42 +17,27 @@ describe('HadronIdSystem', function () {
getDataFromLocalStorageStub.restore();
});
- it('gets a hadronId', function() {
+ it('gets a cached hadronid', function () {
const config = {
params: {}
};
- const callbackSpy = sinon.spy();
- const callback = hadronIdSubmodule.getId(config).callback;
- callback(callbackSpy);
- const request = server.requests[0];
- expect(request.url).to.match(/^https:\/\/id\.hadron\.ad\.gt\/api\/v1\/pbhid/);
- request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ hadronId: 'testHadronId1' }));
- expect(callbackSpy.lastCall.lastArg).to.deep.equal({ id: { hadronId: 'testHadronId1' } });
- });
-
- it('gets a cached hadronid', function() {
- const config = {
- params: {}
- };
- getDataFromLocalStorageStub.withArgs('auHadronId').returns('tstCachedHadronId1');
-
+ getDataFromLocalStorageStub.withArgs(LS_TAM_KEY).returns(HADRON_TEST);
const result = hadronIdSubmodule.getId(config);
- expect(result).to.deep.equal({ id: { hadronId: 'tstCachedHadronId1' } });
+ expect(result).to.deep.equal({id: HADRON_TEST});
});
- it('allows configurable id url', function() {
+ it('allows configurable id url', function () {
const config = {
params: {
url: 'https://hadronid.publync.com'
}
};
+ getDataFromLocalStorageStub.withArgs(LS_TAM_KEY).returns(null);
const callbackSpy = sinon.spy();
const callback = hadronIdSubmodule.getId(config).callback;
callback(callbackSpy);
const request = server.requests[0];
expect(request.url).to.match(/^https:\/\/hadronid\.publync\.com\//);
- request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ hadronId: 'testHadronId1' }));
- expect(callbackSpy.lastCall.lastArg).to.deep.equal({ id: { hadronId: 'testHadronId1' } });
});
});
@@ -60,7 +45,7 @@ describe('HadronIdSystem', function () {
before(() => {
attachIdSystem(hadronIdSubmodule);
});
- it('hadronId', function() {
+ it('hadronId', function () {
const userId = {
hadronId: 'some-random-id-value'
};
diff --git a/test/spec/modules/hadronRtdProvider_spec.js b/test/spec/modules/hadronRtdProvider_spec.js
index 140855194c5..46877f246b5 100644
--- a/test/spec/modules/hadronRtdProvider_spec.js
+++ b/test/spec/modules/hadronRtdProvider_spec.js
@@ -1,13 +1,20 @@
import {config} from 'src/config.js';
-import {HADRONID_LOCAL_NAME, RTD_LOCAL_NAME, addRealTimeData, getRealTimeData, hadronSubmodule, storage} from 'modules/hadronRtdProvider.js';
+import {
+ HADRONID_LOCAL_NAME,
+ RTD_LOCAL_NAME,
+ addRealTimeData,
+ getRealTimeData,
+ hadronSubmodule,
+ storage
+} from 'modules/hadronRtdProvider.js';
import {server} from 'test/mocks/xhr.js';
const responseHeader = {'Content-Type': 'application/json'};
-describe('hadronRtdProvider', function() {
+describe('hadronRtdProvider', function () {
let getDataFromLocalStorageStub;
- beforeEach(function() {
+ beforeEach(function () {
config.resetConfig();
getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
});
@@ -16,19 +23,19 @@ describe('hadronRtdProvider', function() {
getDataFromLocalStorageStub.restore();
});
- describe('hadronSubmodule', function() {
+ describe('hadronSubmodule', function () {
it('successfully instantiates', function () {
- expect(hadronSubmodule.init()).to.equal(true);
+ expect(hadronSubmodule.init()).to.equal(true);
});
});
- describe('Add Real-Time Data', function() {
- it('merges ortb2 data', function() {
+ describe('Add Real-Time Data', function () {
+ it('merges ortb2 data', function () {
let rtdConfig = {};
const setConfigUserObj1 = {
name: 'www.dataprovider1.com',
- ext: { taxonomyname: 'iab_audience_taxonomy' },
+ ext: {taxonomyname: 'iab_audience_taxonomy'},
segment: [{
id: '1776'
}]
@@ -36,7 +43,7 @@ describe('hadronRtdProvider', function() {
const setConfigUserObj2 = {
name: 'www.dataprovider2.com',
- ext: { taxonomyname: 'iab_audience_taxonomy' },
+ ext: {taxonomyname: 'iab_audience_taxonomy'},
segment: [{
id: '1914'
}]
@@ -123,12 +130,12 @@ describe('hadronRtdProvider', function() {
expect(ortb2Config.site.content.data).to.deep.include.members([setConfigSiteObj1, rtdSiteObj1]);
});
- it('merges ortb2 data without duplication', function() {
+ it('merges ortb2 data without duplication', function () {
let rtdConfig = {};
const userObj1 = {
name: 'www.dataprovider1.com',
- ext: { taxonomyname: 'iab_audience_taxonomy' },
+ ext: {taxonomyname: 'iab_audience_taxonomy'},
segment: [{
id: '1776'
}]
@@ -136,7 +143,7 @@ describe('hadronRtdProvider', function() {
const userObj2 = {
name: 'www.dataprovider2.com',
- ext: { taxonomyname: 'iab_audience_taxonomy' },
+ ext: {taxonomyname: 'iab_audience_taxonomy'},
segment: [{
id: '1914'
}]
@@ -195,12 +202,12 @@ describe('hadronRtdProvider', function() {
expect(ortb2Config.site.content.data).to.have.lengthOf(1);
});
- it('merges bidder-specific ortb2 data', function() {
+ it('merges bidder-specific ortb2 data', function () {
let rtdConfig = {};
const configUserObj1 = {
name: 'www.dataprovider1.com',
- ext: { segtax: 3 },
+ ext: {segtax: 3},
segment: [{
id: '1776'
}]
@@ -208,7 +215,7 @@ describe('hadronRtdProvider', function() {
const configUserObj2 = {
name: 'www.dataprovider2.com',
- ext: { segtax: 3 },
+ ext: {segtax: 3},
segment: [{
id: '1914'
}]
@@ -216,7 +223,7 @@ describe('hadronRtdProvider', function() {
const configUserObj3 = {
name: 'www.dataprovider1.com',
- ext: { segtax: 3 },
+ ext: {segtax: 3},
segment: [{
id: '2003'
}]
@@ -372,12 +379,12 @@ describe('hadronRtdProvider', function() {
expect(ortb2Config.site.content.data).to.deep.include.members([configSiteObj2, rtdSiteObj2]);
});
- it('merges bidder-specific ortb2 data without duplication', function() {
+ it('merges bidder-specific ortb2 data without duplication', function () {
let rtdConfig = {};
const userObj1 = {
name: 'www.dataprovider1.com',
- ext: { segtax: 3 },
+ ext: {segtax: 3},
segment: [{
id: '1776'
}]
@@ -385,7 +392,7 @@ describe('hadronRtdProvider', function() {
const userObj2 = {
name: 'www.dataprovider2.com',
- ext: { segtax: 3 },
+ ext: {segtax: 3},
segment: [{
id: '1914'
}]
@@ -393,7 +400,7 @@ describe('hadronRtdProvider', function() {
const userObj3 = {
name: 'www.dataprovider1.com',
- ext: { segtax: 3 },
+ ext: {segtax: 3},
segment: [{
id: '2003'
}]
@@ -501,10 +508,10 @@ describe('hadronRtdProvider', function() {
expect(ortb2Config.site.content.data).to.have.lengthOf(2);
});
- it('allows publisher defined rtd ortb2 logic', function() {
+ it('allows publisher defined rtd ortb2 logic', function () {
const rtdConfig = {
params: {
- handleRtd: function(bidConfig, rtd, rtdConfig, pbConfig) {
+ handleRtd: function (bidConfig, rtd, rtdConfig, pbConfig) {
if (rtd.ortb2.user.data[0].segment[0].id == '1776') {
pbConfig.setConfig({ortb2: rtd.ortb2});
} else {
@@ -518,7 +525,7 @@ describe('hadronRtdProvider', function() {
const rtdUserObj1 = {
name: 'www.dataprovider.com',
- ext: { taxonomyname: 'iab_audience_taxonomy' },
+ ext: {taxonomyname: 'iab_audience_taxonomy'},
segment: [{
id: '1776'
}]
@@ -564,10 +571,10 @@ describe('hadronRtdProvider', function() {
expect(config.getConfig().ortb2).to.deep.equal({});
});
- it('allows publisher defined adunit logic', function() {
+ it('allows publisher defined adunit logic', function () {
const rtdConfig = {
params: {
- handleRtd: function(bidConfig, rtd, rtdConfig, pbConfig) {
+ handleRtd: function (bidConfig, rtd, rtdConfig, pbConfig) {
var adUnits = bidConfig.adUnits;
for (var i = 0; i < adUnits.length; i++) {
var adUnit = adUnits[i];
@@ -629,8 +636,8 @@ describe('hadronRtdProvider', function() {
});
});
- describe('Get Real-Time Data', function() {
- it('gets rtd from local storage cache', function() {
+ describe('Get Real-Time Data', function () {
+ it('gets rtd from local storage cache', function () {
const rtdConfig = {
params: {
segmentCache: true
@@ -665,12 +672,12 @@ describe('hadronRtdProvider', function() {
};
getDataFromLocalStorageStub.withArgs(RTD_LOCAL_NAME).returns(JSON.stringify(cachedRtd));
-
- getRealTimeData(bidConfig, () => {}, rtdConfig, {});
- expect(bidConfig.ortb2Fragments.global.user.data).to.deep.include.members([rtdUserObj1]);
+ getRealTimeData(bidConfig, () => {
+ expect(bidConfig.ortb2Fragments.global.user.data).to.deep.include.members([rtdUserObj1]);
+ }, rtdConfig, {});
});
- it('gets real-time data via async request', function() {
+ it('gets real-time data via async request', function () {
const setConfigSiteObj1 = {
name: 'www.audigent.com',
ext: {
@@ -736,16 +743,14 @@ describe('hadronRtdProvider', function() {
};
getDataFromLocalStorageStub.withArgs(HADRONID_LOCAL_NAME).returns('testHadronId1');
- getRealTimeData(bidConfig, () => {}, rtdConfig, {});
-
- let request = server.requests[0];
- let postData = JSON.parse(request.requestBody);
- expect(postData.config).to.have.deep.property('publisherId', 'testPub1');
- expect(postData.userIds).to.have.deep.property('hadronId', 'testHadronId1');
-
- request.respond(200, responseHeader, JSON.stringify(data));
-
- expect(bidConfig.ortb2Fragments.global.user.data).to.deep.include.members([rtdUserObj1]);
+ getRealTimeData(bidConfig, () => {
+ let request = server.requests[0];
+ let postData = JSON.parse(request.requestBody);
+ expect(postData.config).to.have.deep.property('publisherId', 'testPub1');
+ expect(postData.userIds).to.have.deep.property('hadronId', 'testHadronId1');
+ request.respond(200, responseHeader, JSON.stringify(data));
+ expect(bidConfig.ortb2Fragments.global.user.data).to.deep.include.members([rtdUserObj1]);
+ }, rtdConfig, {});
});
});
});
diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js
index 0c0789ced48..46e07bacbe4 100644
--- a/test/spec/modules/improvedigitalBidAdapter_spec.js
+++ b/test/spec/modules/improvedigitalBidAdapter_spec.js
@@ -238,7 +238,7 @@ describe('Improve Digital Adapter Tests', function () {
sinon.assert.match(payload.imp, [
sinon.match({
id: '33e9500b21129f',
- secure: 0,
+ secure: 1,
ext: {
bidder: {
placementId: 1053688,
@@ -265,7 +265,7 @@ describe('Improve Digital Adapter Tests', function () {
sinon.assert.match(payload.imp, [
sinon.match({
id: '33e9500b21129f',
- secure: 0,
+ secure: 1,
ext: {
bidder: {
placementId: 1053688,
diff --git a/test/spec/modules/inmobiBidAdapter_spec.js b/test/spec/modules/inmobiBidAdapter_spec.js
new file mode 100644
index 00000000000..a7074a2eed8
--- /dev/null
+++ b/test/spec/modules/inmobiBidAdapter_spec.js
@@ -0,0 +1,1965 @@
+import { expect } from 'chai';
+import {
+ spec,
+} from 'modules/inmobiBidAdapter.js';
+import * as utils from 'src/utils.js';
+import * as ajax from 'src/ajax.js';
+import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js';
+import { hook } from '../../../src/hook';
+import { config } from '../../../src/config.js';
+import { syncAddFPDToBidderRequest } from '../../helpers/fpd';
+import 'modules/consentManagementTcf.js';
+import 'modules/consentManagementUsp.js';
+import 'modules/consentManagementGpp.js';
+import 'modules/priceFloors.js';
+import sinon from 'sinon';
+
+// constants
+const GVLID = 333;
+export const ADAPTER_VERSION = 1.0;
+const BIDDER_CODE = 'inmobi';
+export const EVENT_ENDPOINT = 'https://sync.inmobi.com';
+
+describe('The inmobi bidding adapter', function () {
+ let utilsMock, sandbox, ajaxStub, fetchStub; ;
+
+ beforeEach(function () {
+ // mock objects
+ utilsMock = sinon.mock(utils);
+ sandbox = sinon.sandbox.create();
+ ajaxStub = sandbox.stub(ajax, 'ajax');
+ fetchStub = sinon.stub(global, 'fetch').resolves(new Response('OK'));
+ });
+
+ afterEach(function () {
+ utilsMock.restore();
+ sandbox.restore();
+ ajaxStub.restore();
+ fetchStub.restore();
+ });
+
+ describe('onBidWon', function () {
+ // existence test
+ it('onBidWon function should be defined', function () {
+ expect(spec.onBidWon).to.exist.and.to.be.a('function');
+ });
+
+ it('It should invoke onBidWon, resolving the eventType and domain', function () {
+ const bid = {
+ bidder: 'inmobi',
+ width: 300,
+ height: 250,
+ adId: '330a22bdea4cac1',
+ mediaType: 'banner',
+ cpm: 0.28,
+ ad: 'inmobiAd',
+ requestId: '418b37f85e772c1',
+ adUnitCode: 'div-gpt-ad-1460505748561-01',
+ size: '350x250',
+ adserverTargeting: {
+ hb_bidder: 'prebid',
+ hb_adid: '330a22bdea4cac1',
+ hb_pb: '0.20',
+ hb_size: '350x250'
+ },
+ meta: {
+ loggingPercentage: 100
+ }
+ };
+ spec.onBidWon(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onBidWon`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid.meta
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(1);
+ const fetchArgs = fetchStub.getCall(0).args;
+ /*
+ index fetch parameter
+ 0 -> URL
+ 1 -> options (method, headers, body, etc.)
+ */
+ expect(fetchArgs[0]).to.equal(expectedUrl);
+ const actualPayload = fetchArgs[1]?.body;
+ expect(actualPayload).to.equal(expectedPayload);
+ expect(fetchArgs[1]).to.deep.include({
+ method: 'POST',
+ credentials: 'include',
+ keepalive: true,
+ });
+ expect(fetchArgs[1]?.headers).to.deep.equal({
+ 'Content-Type': 'text/plain',
+ });
+ });
+
+ it('onBidWon should not be called when loggingPercentage is set to 0', function () {
+ const bid = {
+ bidder: 'inmobi',
+ width: 300,
+ height: 250,
+ adId: '330a22bdea4cac1',
+ mediaType: 'banner',
+ cpm: 0.28,
+ ad: 'inmobiAd',
+ requestId: '418b37f85e772c1',
+ adUnitCode: 'div-gpt-ad-1460505748561-01',
+ size: '350x250',
+ adserverTargeting: {
+ hb_bidder: 'prebid',
+ hb_adid: '330a22bdea4cac1',
+ hb_pb: '0.20',
+ hb_size: '350x250'
+ },
+ meta: {
+ loggingPercentage: 0
+ }
+ };
+ spec.onBidWon(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onBidWon`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid.meta
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+
+ it('onBidWon should not be called if the bid data is null', function () {
+ // Call onBidWon with null data
+ spec.onBidWon(null);
+ // Assert that ajax was not called since bid data is null
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+ });
+
+ describe('onBidderError', function () {
+ it('onBidderError function should be defined', function () {
+ expect(spec.onBidderError).to.exist.and.to.be.a('function');
+ });
+
+ it('onBidderError should not be called if the bid data is null', function () {
+ // Call onBidError with null data
+ spec.onBidderError(null);
+ // Assert that ajax was not called since bid data is null
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+
+ it('onBidderError should be called with the eventType', function () {
+ const bid = {
+ error: 'error', // Assuming this will be a mock or reference to an actual XMLHttpRequest object
+ bidderRequest: {
+ auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917',
+ auctionStart: 1579746300522,
+ bidder: 'inmobi',
+ bidderRequestId: '15246a574e859f'
+ }
+ };
+ spec.onBidderError(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onBidderError`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(1);
+ const fetchArgs = fetchStub.getCall(0).args;
+ /*
+ index fetch parameter
+ 0 -> URL
+ 1 -> options (method, headers, body, etc.)
+ */
+ expect(fetchArgs[0]).to.equal(expectedUrl);
+ const actualPayload = fetchArgs[1]?.body;
+ expect(actualPayload).to.equal(expectedPayload);
+ expect(fetchArgs[1]).to.deep.include({
+ method: 'POST',
+ credentials: 'include',
+ keepalive: true,
+ });
+ expect(fetchArgs[1]?.headers).to.deep.equal({
+ 'Content-Type': 'text/plain',
+ });
+ });
+ });
+
+ describe('onAdRenderSucceeded', function () {
+ // existence test
+ it('onAdRenderSucceeded function should be defined', function () {
+ expect(spec.onAdRenderSucceeded).to.exist.and.to.be.a('function');
+ });
+
+ it('should invoke onAdRenderSucceeded, resolving the eventType and domain', function () {
+ const bid = {
+ bidder: 'inmobi',
+ width: 300,
+ height: 250,
+ adId: '330a22bdea4cac1',
+ mediaType: 'banner',
+ cpm: 0.28,
+ ad: 'inmobiAd',
+ requestId: '418b37f85e772c1',
+ adUnitCode: 'div-gpt-ad-1460505748561-01',
+ size: '350x250',
+ adserverTargeting: {
+ hb_bidder: 'prebid',
+ hb_adid: '330a22bdea4cac1',
+ hb_pb: '0.20',
+ hb_size: '350x250'
+ },
+ meta: {
+ loggingPercentage: 100
+ }
+ };
+ spec.onAdRenderSucceeded(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onAdRenderSucceeded`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid.meta
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(1);
+ const fetchArgs = fetchStub.getCall(0).args;
+ /*
+ index fetch parameter
+ 0 -> URL
+ 1 -> options (method, headers, body, etc.)
+ */
+ expect(fetchArgs[0]).to.equal(expectedUrl);
+ const actualPayload = fetchArgs[1]?.body;
+ expect(actualPayload).to.equal(expectedPayload);
+ expect(fetchArgs[1]).to.deep.include({
+ method: 'POST',
+ credentials: 'include',
+ keepalive: true,
+ });
+ expect(fetchArgs[1]?.headers).to.deep.equal({
+ 'Content-Type': 'text/plain',
+ });
+ });
+
+ it('onAdRenderSucceeded should not be called when loggingPercentage is 0', function () {
+ const bid = {
+ bidder: 'inmobi',
+ width: 300,
+ height: 250,
+ adId: '330a22bdea4cac1',
+ mediaType: 'banner',
+ cpm: 0.28,
+ ad: 'inmobiAd',
+ requestId: '418b37f85e772c1',
+ adUnitCode: 'div-gpt-ad-1460505748561-01',
+ size: '350x250',
+ adserverTargeting: {
+ hb_bidder: 'prebid',
+ hb_adid: '330a22bdea4cac1',
+ hb_pb: '0.20',
+ hb_size: '350x250'
+ },
+ meta: {
+ loggingPercentage: 0
+ }
+ };
+ spec.onAdRenderSucceeded(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onAdRenderSucceeded`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid.meta
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+
+ it('onAdRenderSucceeded should not be called if the bid data is null', function () {
+ // Call onAdRenderSucceeded with null data
+ spec.onAdRenderSucceeded(null);
+ // Assert that ajax was not called since bid data is null
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+ });
+
+ describe('onTimeout', function () {
+ // existence test
+ it('onTimeout function should be defined', function () {
+ expect(spec.onTimeout).to.exist.and.to.be.a('function');
+ });
+
+ it('should invoke onTimeout, resolving the eventType and domain', function () {
+ const bid = [
+ {
+ bidder: 'inmobi',
+ bidId: '51ef8751f9aead1',
+ adUnitCode: 'div-gpt-ad-14605057481561-0',
+ timeout: 3000,
+ auctionId: '18fd8b8b0bd7517'
+ }
+ ];
+ spec.onTimeout(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onTimeout`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(1);
+ const fetchArgs = fetchStub.getCall(0).args;
+ /*
+ index fetch parameter
+ 0 -> URL
+ 1 -> options (method, headers, body, etc.)
+ */
+ expect(fetchArgs[0]).to.equal(expectedUrl);
+ const actualPayload = fetchArgs[1]?.body;
+ expect(actualPayload).to.equal(expectedPayload);
+ expect(fetchArgs[1]).to.deep.include({
+ method: 'POST',
+ credentials: 'include',
+ keepalive: true,
+ });
+ expect(fetchArgs[1]?.headers).to.deep.equal({
+ 'Content-Type': 'text/plain',
+ });
+ });
+
+ it('onTimeout should not be called if the bid data is null', function () {
+ // Call onTimeout with null data
+ spec.onTimeout(null);
+ // Assert that ajax was not called since bid data is null
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+ });
+
+ describe('onSetTargeting', function () {
+ // existence test
+ it('The onSetTargeting function should be defined', function () {
+ expect(spec.onSetTargeting).to.exist.and.to.be.a('function');
+ });
+
+ it('should invoke onSetTargeting, resolving the eventType and domain', function () {
+ const bid = {
+ bidder: 'inmobi',
+ width: 300,
+ height: 250,
+ adId: '330a22bdea4cac1',
+ mediaType: 'banner',
+ cpm: 0.28,
+ ad: 'inmobiAd',
+ requestId: '418b37f85e7721c',
+ adUnitCode: 'div-gpt-ad-1460505748561-01',
+ size: '350x250',
+ adserverTargeting: {
+ hb_bidder: 'prebid',
+ hb_adid: '330a22bdea4cac1',
+ hb_pb: '0.20',
+ hb_size: '350x250'
+ },
+ meta: {
+ loggingPercentage: 100
+ }
+ };
+ spec.onSetTargeting(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onSetTargeting`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid.meta
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(1);
+ const fetchArgs = fetchStub.getCall(0).args;
+ /*
+ index fetch parameter
+ 0 -> URL
+ 1 -> options (method, headers, body, etc.)
+ */
+ expect(fetchArgs[0]).to.equal(expectedUrl);
+ const actualPayload = fetchArgs[1]?.body;
+ expect(actualPayload).to.equal(expectedPayload);
+ expect(fetchArgs[1]).to.deep.include({
+ method: 'POST',
+ credentials: 'include',
+ keepalive: true,
+ });
+ expect(fetchArgs[1]?.headers).to.deep.equal({
+ 'Content-Type': 'text/plain',
+ });
+ });
+
+ it('onSetTargeting should not be called when loggingPercentage is 0', function () {
+ const bid = {
+ bidder: 'inmobi',
+ width: 300,
+ height: 250,
+ adId: '330a22bdea4cac1',
+ mediaType: 'banner',
+ cpm: 0.28,
+ ad: 'inmobiAd',
+ requestId: '418b37f85e7721c',
+ adUnitCode: 'div-gpt-ad-1460505748561-01',
+ size: '350x250',
+ adserverTargeting: {
+ hb_bidder: 'prebid',
+ hb_adid: '330a22bdea4cac1',
+ hb_pb: '0.20',
+ hb_size: '350x250'
+ },
+ meta: {
+ loggingPercentage: 0
+ }
+ };
+ spec.onSetTargeting(bid);
+ // expected url and payload
+ const expectedUrl = `${EVENT_ENDPOINT}/report/onSetTargeting`;
+ const expectedPayload = JSON.stringify({
+ domain: location.hostname,
+ eventPayload: bid.meta
+ });
+ // assert statements
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+
+ it('onSetTargeting should not be called if the bid data is null', function () {
+ // Call onSetTargeting with null data
+ spec.onSetTargeting(null);
+ // Assert that ajax was not called since bid data is null
+ expect(fetchStub.callCount).to.be.equal(0);
+ });
+ });
+
+ describe('isBidRequestValid', function () {
+ it('should return false when an invalid bid is provided', function () {
+ const bid = {
+ bidder: 'inmobi',
+ };
+ const isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equal(false);
+ });
+
+ it('should return true when the bid contains a PLC', function () {
+ const bid = {
+ bidder: 'inmobi',
+ params: {
+ plc: '123a',
+ },
+ };
+ const isValid = spec.isBidRequestValid(bid);
+ expect(isValid).to.equal(true);
+ });
+ });
+
+ describe('buildRequests', function () {
+ before(() => {
+ hook.ready();
+ })
+ afterEach(function () {
+ config.resetConfig();
+ });
+ const bidderRequest = {
+ refererInfo: {
+ page: 'inmobi',
+ topmostLocation: 'inmobi'
+ },
+ timeout: 3000,
+ gdprConsent: {
+ gdprApplies: true,
+ consentString: 'consentDataString',
+ vendorData: {
+ vendorConsents: {
+ '333': 1
+ },
+ },
+ apiVersion: 1,
+ },
+ };
+
+ it('request should build with correct plc', function () {
+ const bidRequests = [
+ {
+ bidId: 'bidId',
+ bidder: 'inmobi',
+ adUnitCode: 'bid-12',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123'
+ }
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp[0].ext.bidder.plc).to.deep.equal('123');
+ });
+
+ it('request should build with correct imp', function () {
+ const expectedMetric = {
+ url: 'https://inmobi.com'
+ }
+ const bidRequests = [{
+ bidId: 'bidId',
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ ortb2Imp: {
+ instl: 1,
+ metric: expectedMetric,
+ ext: {
+ gpid: 'gpid_inmobi'
+ },
+ rwdd: 1
+ },
+ params: {
+ plc: '123ai',
+ bidfloor: 4.66,
+ bidfloorcur: 'USD'
+ }
+ }];
+
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp).to.have.lengthOf(1);
+ expect(ortbRequest.imp[0].id).to.deep.equal('bidId');
+ expect(ortbRequest.imp[0].tagid).to.deep.equal('impId');
+ expect(ortbRequest.imp[0].instl).to.equal(1);
+ expect(ortbRequest.imp[0].bidfloor).to.equal(4.66);
+ expect(ortbRequest.imp[0].bidfloorcur).to.equal('USD');
+ expect(ortbRequest.imp[0].metric).to.deep.equal(expectedMetric);
+ expect(ortbRequest.imp[0].secure).to.equal(0);
+ expect(ortbRequest.imp[0].ext.gpid).to.equal('gpid_inmobi');
+ expect(ortbRequest.imp[0].rwdd).to.equal(1);
+ });
+
+ it('request should build with proper site data', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '1234a',
+ },
+ },
+ ];
+ const ortb2 = {
+ site: {
+ name: 'raapchikgames.com',
+ domain: 'raapchikgames.com',
+ keywords: 'test1, test2',
+ cat: ['IAB2'],
+ pagecat: ['IAB3'],
+ sectioncat: ['IAB4'],
+ page: 'https://raapchikgames.com',
+ ref: 'inmobi.com',
+ privacypolicy: 1,
+ content: {
+ url: 'https://raapchikgames.com/games1'
+ }
+ }
+ };
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest({ ...bidderRequest, ortb2 })).data;
+ expect(ortbRequest.site.domain).to.equal('raapchikgames.com');
+ expect(ortbRequest.site.publisher.domain).to.equal('inmobi');
+ expect(ortbRequest.site.page).to.equal('https://raapchikgames.com');
+ expect(ortbRequest.site.name).to.equal('raapchikgames.com');
+ expect(ortbRequest.site.keywords).to.equal('test1, test2');
+ expect(ortbRequest.site.cat).to.deep.equal(['IAB2']);
+ expect(ortbRequest.site.pagecat).to.deep.equal(['IAB3']);
+ expect(ortbRequest.site.sectioncat).to.deep.equal(['IAB4']);
+ expect(ortbRequest.site.ref).to.equal('inmobi.com');
+ expect(ortbRequest.site.privacypolicy).to.equal(1);
+ expect(ortbRequest.site.content.url).to.equal('https://raapchikgames.com/games1')
+ });
+
+ it('request should build with proper device data', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '1234a',
+ },
+ },
+ ];
+ const ortb2 = {
+ device: {
+ dnt: 0,
+ ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
+ ip: '195.199.250.144',
+ h: 919,
+ w: 1920,
+ language: 'hu',
+ lmt: 1,
+ js: 1,
+ connectiontype: 0,
+ hwv: '5S',
+ model: 'iphone',
+ mccmnc: '310-005',
+ geo: {
+ lat: 40.0964439,
+ lon: -75.3009142
+ }
+ }
+ };
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest({ ...bidderRequest, ortb2 })).data;
+ expect(ortbRequest.device.dnt).to.equal(0);
+ expect(ortbRequest.device.lmt).to.equal(1);
+ expect(ortbRequest.device.js).to.equal(1);
+ expect(ortbRequest.device.connectiontype).to.equal(0);
+ expect(ortbRequest.device.ua).to.equal('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36');
+ expect(ortbRequest.device.ip).to.equal('195.199.250.144');
+ expect(ortbRequest.device.h).to.equal(919);
+ expect(ortbRequest.device.w).to.equal(1920);
+ expect(ortbRequest.device.language).to.deep.equal('hu');
+ expect(ortbRequest.device.hwv).to.deep.equal('5S');
+ expect(ortbRequest.device.model).to.deep.equal('iphone');
+ expect(ortbRequest.device.mccmnc).to.deep.equal('310-005');
+ expect(ortbRequest.device.geo.lat).to.deep.equal(40.0964439);
+ expect(ortbRequest.device.geo.lon).to.deep.equal(-75.3009142);
+ });
+
+ it('should properly build a request with source object', function () {
+ const expectedSchain = { id: 'prebid' };
+ const ortb2 = {
+ source: {
+ pchain: 'inmobi',
+ schain: expectedSchain
+ }
+ };
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ bidId: 'bidId',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '124',
+ },
+ },
+ ];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest({ ...bidderRequest, ortb2 })).data;
+ expect(ortbRequest.source.ext.schain).to.deep.equal(expectedSchain);
+ expect(ortbRequest.source.pchain).to.equal('inmobi');
+ });
+
+ it('should properly user object', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123'
+ }
+ },
+ ];
+ const br = {
+ ...bidderRequest,
+ ortb2: {
+ user: {
+ yob: 2002,
+ keyowrds: 'test test',
+ gender: 'M',
+ customdata: 'test no',
+ geo: {
+ lat: 40.0964439,
+ lon: -75.3009142
+ },
+ ext: {
+ eids: [
+ {
+ source: 'inmobi.com',
+ uids: [{
+ id: 'iid',
+ atype: 1
+ }]
+ }
+ ]
+ }
+ }
+ }
+ }
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(br));
+ const ortbRequest = request.data;
+ expect(ortbRequest.user.yob).to.deep.equal(2002);
+ expect(ortbRequest.user.keyowrds).to.deep.equal('test test');
+ expect(ortbRequest.user.gender).to.deep.equal('M');
+ expect(ortbRequest.user.customdata).to.deep.equal('test no');
+ expect(ortbRequest.user.geo.lat).to.deep.equal(40.0964439);
+ expect(ortbRequest.user.geo.lon).to.deep.equal(-75.3009142);
+ expect(ortbRequest.user.ext.eids).to.deep.equal([
+ {
+ source: 'inmobi.com',
+ uids: [{
+ id: 'iid',
+ atype: 1
+ }]
+ }
+ ]);
+ });
+
+ it('should properly build a request regs object', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '1234a',
+ },
+ },
+ ];
+ const ortb2 = {
+ regs: {
+ coppa: 1,
+ gpp: 'gpp_consent_string',
+ gpp_sid: [0, 1, 2],
+ us_privacy: 'yes us privacy applied'
+ }
+ };
+
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest({ ...bidderRequest, ortb2 })).data;
+ expect(ortbRequest.regs.coppa).to.equal(1);
+ expect(ortbRequest.regs.ext.gpp).to.equal('gpp_consent_string');
+ expect(ortbRequest.regs.ext.gpp_sid).to.deep.equal([0, 1, 2]);
+ expect(ortbRequest.regs.ext.us_privacy).to.deep.equal('yes us privacy applied');
+ });
+
+ it('gdpr test', function () {
+ // using privacy params from global bidder Request
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '1234a',
+ },
+ },
+ ];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.regs.ext.gdpr).to.deep.equal(1);
+ expect(ortbRequest.user.ext.consent).to.equal('consentDataString');
+ });
+
+ it('should properly set tmax if available', function () {
+ // using tmax from global bidder Request
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'bid-1',
+ transactionId: 'trans-1',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123'
+ }
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.tmax).to.equal(bidderRequest.timeout);
+ });
+
+ it('should properly build a request with bcat field', function () {
+ const bcat = ['IAB1', 'IAB2'];
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123a',
+ },
+ },
+ ];
+ const bidderRequest = {
+ ortb2: {
+ bcat
+ }
+ };
+
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.bcat).to.deep.equal(bcat);
+ });
+
+ it('should properly build a request with badv field', function () {
+ const badv = ['ford.com'];
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '12ea',
+ },
+ },
+ ];
+ const bidderRequest = {
+ ortb2: {
+ badv
+ }
+ };
+
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.badv).to.deep.equal(badv);
+ });
+
+ it('should properly build a request with bapp field', function () {
+ const bapp = ['raapchik.com'];
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123a',
+ },
+ },
+ ];
+ const bidderRequest = {
+ ortb2: {
+ bapp
+ }
+ };
+
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.bapp).to.deep.equal(bapp);
+ });
+
+ it('banner request test', function () {
+ const bidderRequest = {};
+ const bidRequests = [
+ {
+ bidId: 'bidId',
+ bidder: 'inmobi',
+ adUnitCode: 'bid-12',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]],
+ pos: 1,
+ topframe: 0,
+ }
+ },
+ params: {
+ plc: '123'
+ },
+ ortb2Imp: {
+ banner: {
+ api: [1, 2],
+ mimes: ['image/jpg', 'image/gif']
+ }
+ }
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp[0].banner).not.to.be.null;
+ expect(ortbRequest.imp[0].banner.format[0].w).to.equal(320);
+ expect(ortbRequest.imp[0].banner.format[0].h).to.equal(50);
+ expect(ortbRequest.imp[0].banner.pos).to.equal(1);
+ expect(ortbRequest.imp[0].banner.topframe).to.equal(0);
+ expect(ortbRequest.imp[0].banner.api).to.deep.equal([1, 2]);
+ expect(ortbRequest.imp[0].banner.mimes).to.deep.equal(['image/jpg', 'image/gif']);
+ });
+
+ it('banner request test with sizes > 1', function () {
+ const bidderRequest = {};
+ const bidRequests = [
+ {
+ bidId: 'bidId',
+ bidder: 'inmobi',
+ adUnitCode: 'bid-12',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50], [720, 50]]
+ }
+ },
+ params: {
+ plc: '123'
+ }
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp[0].banner).not.to.be.null;
+ expect(ortbRequest.imp[0].banner.format[0].w).to.equal(320);
+ expect(ortbRequest.imp[0].banner.format[0].h).to.equal(50);
+ expect(ortbRequest.imp[0].banner.format[1].w).to.equal(720);
+ expect(ortbRequest.imp[0].banner.format[1].h).to.equal(50);
+ });
+
+ if (FEATURES.VIDEO) {
+ it('video request test', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'bid-123',
+ sizes: [[640, 360]],
+ mediaTypes: {
+ video: {
+ context: 'inbanner',
+ playerSize: [640, 360],
+ mimes: ['video/mp4', 'video/x-flv'],
+ maxduration: 30,
+ api: [1, 2],
+ protocols: [2, 3],
+ plcmt: 3,
+ w: 640,
+ h: 360,
+ linearity: 1,
+ skipmin: 30,
+ skipafter: 30,
+ minbitrate: 10000,
+ maxbitrate: 48000,
+ delivery: [1, 2, 3],
+ pos: 1,
+ playbackend: 1,
+ adPodDurationSec: 30,
+ durationRangeSec: [1, 30],
+ skip: 1,
+ minduration: 5,
+ startdelay: 5,
+ playbackmethod: [1, 3],
+ placement: 2
+ }
+ },
+ params: {
+ plc: '123'
+ },
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp).to.have.lengthOf(1);
+ expect(ortbRequest.imp[0].video.skip).to.equal(1);
+ expect(ortbRequest.imp[0].video.minduration).to.equal(5);
+ expect(ortbRequest.imp[0].video.startdelay).to.equal(5);
+ expect(ortbRequest.imp[0].video.playbackmethod).to.deep.equal([1, 3]);
+ expect(ortbRequest.imp[0].video.placement).to.equal(2);
+ expect(ortbRequest.imp[0].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']);
+ expect(ortbRequest.imp[0].video.maxduration).to.equal(30);
+ expect(ortbRequest.imp[0].video.api).to.deep.equal([1, 2]);
+ expect(ortbRequest.imp[0].video.protocols).to.deep.equal([2, 3]);
+ expect(ortbRequest.imp[0].video.plcmt).to.equal(3);
+ expect(ortbRequest.imp[0].video.w).to.equal(640);
+ expect(ortbRequest.imp[0].video.h).to.equal(360);
+ expect(ortbRequest.imp[0].video.linearity).to.equal(1);
+ expect(ortbRequest.imp[0].video.skipmin).to.equal(30);
+ expect(ortbRequest.imp[0].video.skipafter).to.equal(30);
+ expect(ortbRequest.imp[0].video.minbitrate).to.equal(10000);
+ expect(ortbRequest.imp[0].video.maxbitrate).to.equal(48000);
+ expect(ortbRequest.imp[0].video.delivery).to.deep.equal([1, 2, 3]);
+ expect(ortbRequest.imp[0].video.pos).to.equal(1);
+ expect(ortbRequest.imp[0].video.playbackend).to.equal(1);
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('video request with player size > 1 ', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'bid-123',
+ sizes: [[640, 360], [480, 320]],
+ mediaTypes: {
+ video: {
+ context: 'inbanner',
+ playerSize: [[640, 360], [480, 320]],
+ mimes: ['video/mp4', 'video/x-flv'],
+ maxduration: 30,
+ api: [1, 2],
+ protocols: [2, 3],
+ plcmt: 3,
+ w: 640,
+ h: 360,
+ linearity: 1,
+ skipmin: 30,
+ skipafter: 30,
+ minbitrate: 10000,
+ maxbitrate: 48000,
+ delivery: [1, 2, 3],
+ pos: 1,
+ playbackend: 1,
+ adPodDurationSec: 30,
+ durationRangeSec: [1, 30],
+ skip: 1,
+ minduration: 5,
+ startdelay: 5,
+ playbackmethod: [1, 3],
+ placement: 2
+ }
+ },
+ params: {
+ plc: '123'
+ },
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp).to.have.lengthOf(1);
+ expect(ortbRequest.imp[0].video.w).to.be.equal(640);
+ expect(ortbRequest.imp[0].video.h).to.be.equal(360);
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('video request test when skip is 0', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'bid-123',
+ sizes: [[640, 360]],
+ mediaTypes: {
+ video: {
+ context: 'inbanner',
+ playerSize: [640, 360],
+ mimes: ['video/mp4', 'video/x-flv'],
+ maxduration: 30,
+ api: [1, 2],
+ protocols: [2, 3],
+ plcmt: 3,
+ w: 640,
+ h: 360,
+ linearity: 1,
+ skipmin: 30,
+ skipafter: 30,
+ minbitrate: 10000,
+ maxbitrate: 48000,
+ delivery: [1, 2, 3],
+ pos: 1,
+ playbackend: 1,
+ adPodDurationSec: 30,
+ durationRangeSec: [1, 30],
+ skip: 0,
+ minduration: 5,
+ startdelay: 5,
+ playbackmethod: [1, 3],
+ placement: 2
+ }
+ },
+ params: {
+ plc: '123'
+ },
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp).to.have.lengthOf(1);
+ expect(ortbRequest.imp[0].video.skip).to.equal(0);
+ });
+ }
+
+ if (FEATURES.NATIVE) {
+ it('native request test without assests', function () {
+ const bidRequests = [
+ {
+ mediaTypes: {
+ native: {}
+ },
+ params: {
+ 'plc': '123a'
+ }
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+ expect(ortbRequest.imp[0].native).to.be.undefined;
+ });
+ }
+
+ if (FEATURES.NATIVE) {
+ it('native request with assets', function () {
+ const assets = [{
+ required: 1,
+ id: 1,
+ img: {
+ type: 3,
+ wmin: 100,
+ hmin: 100,
+ }
+ },
+ {
+ required: 1,
+ id: 2,
+ title: {
+ len: 140,
+ }
+ },
+ {
+ required: 1,
+ id: 3,
+ data: {
+ type: 1,
+ }
+ }];
+ const bidRequests = [
+ {
+ mediaTypes: {
+ native: {}
+ },
+ nativeOrtbRequest: {
+ assets: assets
+ },
+ params: {}
+ },
+ ];
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const ortbRequest = request.data;
+
+ expect(ortbRequest.imp[0].native.request).to.not.be.null;
+ const nativeRequest = JSON.parse(ortbRequest.imp[0].native.request);
+ expect(nativeRequest).to.have.property('assets');
+ expect(nativeRequest.assets).to.deep.equal(assets);
+ });
+ }
+
+ it('should properly build a request when coppa flag is true', function () {
+ const bidRequests = [];
+ const bidderRequest = {};
+ config.setConfig({ coppa: true });
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.regs.coppa).to.equal(1);
+ });
+
+ it('should properly build a request when coppa flag is false', function () {
+ const bidRequests = [];
+ const bidderRequest = {};
+ config.setConfig({ coppa: false });
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.regs.coppa).to.equal(0);
+ });
+
+ it('should properly build a request when coppa flag is not defined', function () {
+ const bidRequests = [];
+ const bidderRequest = {};
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.regs?.coppa).to.be.undefined;
+ });
+
+ it('build a banner request with bidFloor', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50], [720, 90]]
+ }
+ },
+ params: {
+ plc: '123a',
+ bidfloor: 1,
+ bidfloorcur: 'USD'
+ }
+ }
+ ];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp[0].bidfloor).to.deep.equal(1);
+ expect(ortbRequest.imp[0].bidfloorcur).to.deep.equal('USD');
+ });
+
+ it('build a banner request with getFloor', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123a'
+ },
+ getFloor: inputParams => {
+ return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' };
+ }
+ }
+ ];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp[0].bidfloor).equal(1.23);
+ expect(ortbRequest.imp[0].bidfloorcur).equal('USD');
+ });
+
+ if (FEATURES.VIDEO) {
+ it('build a video request with bidFloor', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ video: {
+ playerSize: [[480, 320], [720, 480]]
+ }
+ },
+ params: {
+ plc: '123a',
+ bidfloor: 1,
+ bidfloorcur: 'USD'
+ }
+ }
+ ];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp[0].bidfloor).to.equal(1);
+ expect(ortbRequest.imp[0].bidfloorcur).to.equal('USD');
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('build a video request with getFloor', function () {
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ video: {
+ playerSize: [[480, 320], [720, 480]]
+ }
+ },
+ params: {
+ plc: '123a'
+ },
+ getFloor: inputParams => {
+ return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' };
+ }
+ }
+ ];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp[0].bidfloor).to.equal(1.23);
+ expect(ortbRequest.imp[0].bidfloorcur).to.equal('USD');
+ });
+ }
+
+ if (FEATURES.NATIVE && FEATURES.VIDEO) {
+ it('build a mutli format request with getFloor', function () {
+ const assets = [{
+ required: 1,
+ id: 1,
+ img: {
+ type: 3,
+ wmin: 100,
+ hmin: 100,
+ }
+ },
+ {
+ required: 1,
+ id: 2,
+ title: {
+ len: 140,
+ }
+ },
+ {
+ required: 1,
+ id: 3,
+ data: {
+ type: 1,
+ }
+ }];
+ const bidRequests = [
+ {
+ bidder: 'inmobi',
+ adUnitCode: 'impId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50], [720, 90]]
+ },
+ video: {
+ playerSize: [640, 480],
+ },
+ native: {
+
+ }
+ },
+ nativeOrtbRequest: {
+ assets: assets
+ },
+ params: {
+ plc: '12456'
+ },
+ getFloor: inputParams => {
+ return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' };
+ }
+ },
+ ];
+ const bidderRequest = {};
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp[0].banner).not.to.be.undefined;
+ expect(ortbRequest.imp[0].video).not.to.be.undefined;
+ expect(ortbRequest.imp[0].native.request).not.to.be.undefined;
+ expect(ortbRequest.imp[0].bidfloor).to.deep.equal(1.23);
+ expect(ortbRequest.imp[0].bidfloorcur).to.deep.equal('USD');
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('build a multi imp request', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ video: {
+ context: 'inbanner',
+ playerSize: [640, 360],
+ mimes: ['video/mp4', 'video/x-flv'],
+ maxduration: 30,
+ api: [1, 2],
+ protocols: [2, 3],
+ plcmt: 3,
+ w: 640,
+ h: 360,
+ linearity: 1,
+ skipmin: 30,
+ skipafter: 30,
+ minbitrate: 10000,
+ maxbitrate: 48000,
+ delivery: [1, 2, 3],
+ pos: 1,
+ playbackend: 1,
+ adPodDurationSec: 30,
+ durationRangeSec: [1, 30],
+ skip: 1,
+ minduration: 5,
+ startdelay: 5,
+ playbackmethod: [1, 3],
+ placement: 2
+ }
+ },
+ params: {
+ plc: '123'
+ },
+ }, {
+ adUnitCode: 'impId',
+ bidId: 'bidId2',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123',
+ }
+ }];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp).to.have.lengthOf(2);
+ expect(ortbRequest.imp[0].video.skip).to.equal(1);
+ expect(ortbRequest.imp[0].video.minduration).to.equal(5);
+ expect(ortbRequest.imp[0].video.startdelay).to.equal(5);
+ expect(ortbRequest.imp[0].video.playbackmethod).to.deep.equal([1, 3]);
+ expect(ortbRequest.imp[0].video.placement).to.equal(2);
+ // banner test
+ expect(ortbRequest.imp[1].banner).not.to.be.undefined;
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('build a multi format request', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ video: {
+ context: 'inbanner',
+ playerSize: [640, 360],
+ mimes: ['video/mp4', 'video/x-flv'],
+ maxduration: 30,
+ api: [1, 2],
+ protocols: [2, 3],
+ plcmt: 3,
+ w: 640,
+ h: 360,
+ linearity: 1,
+ skipmin: 30,
+ skipafter: 30,
+ minbitrate: 10000,
+ maxbitrate: 48000,
+ delivery: [1, 2, 3],
+ pos: 1,
+ playbackend: 1,
+ adPodDurationSec: 30,
+ durationRangeSec: [1, 30],
+ skip: 1,
+ minduration: 5,
+ startdelay: 5,
+ playbackmethod: [1, 3],
+ placement: 2
+ },
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123'
+ },
+ }];
+ const ortbRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)).data;
+ expect(ortbRequest.imp).to.have.lengthOf(1);
+ expect(ortbRequest.imp[0].video.skip).to.equal(1);
+ expect(ortbRequest.imp[0].video.minduration).to.equal(5);
+ expect(ortbRequest.imp[0].video.startdelay).to.equal(5);
+ expect(ortbRequest.imp[0].video.playbackmethod).to.deep.equal([1, 3]);
+ expect(ortbRequest.imp[0].video.placement).to.equal(2);
+ // banner test
+ expect(ortbRequest.imp[0].banner).not.to.be.undefined;
+ });
+ }
+ });
+
+ describe('getUserSyncs', function () {
+ const syncEndPoint = 'https://sync.inmobi.com/prebidjs?';
+
+ it('should return empty if iframe disabled and pixelEnabled is false', function () {
+ const res = spec.getUserSyncs({});
+ expect(res).to.deep.equal([]);
+ });
+
+ it('should return user sync if iframe enabled is true', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true });
+ expect(res).to.deep.equal([{ type: 'iframe', url: syncEndPoint }]);
+ });
+
+ it('should return user sync if iframe enabled is true and gdpr not present', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false });
+ expect(res).to.deep.equal([{ type: 'iframe', url: `${syncEndPoint}` }]);
+ });
+
+ it('should return user sync if iframe enabled is true and gdpr = 1 and gdpr consent present', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'GDPR_CONSENT' });
+ expect(res).to.deep.equal([{ type: 'iframe', url: `${syncEndPoint}gdpr=1&gdpr_consent=GDPR_CONSENT` }]);
+ });
+
+ it('should return user sync if iframe enabled is true and gdpr = 1 , gdpr consent present and usp_consent present', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'GDPR_CONSENT' }, 'USP_CONSENT');
+ expect(res).to.deep.equal([{ type: 'iframe', url: `${syncEndPoint}gdpr=1&gdpr_consent=GDPR_CONSENT&us_privacy=USP_CONSENT` }]);
+ });
+
+ it('should return user sync if iframe enabled is true usp_consent present and gppConsent present', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: undefined }, 'USP_CONSENT', { gppString: 'GPP_STRING', applicableSections: [32, 51] });
+ expect(res).to.deep.equal([{ type: 'iframe', url: `${syncEndPoint}us_privacy=USP_CONSENT&gpp=GPP_STRING&gpp_sid=32%2C51` }]);
+ });
+
+ const responses = [{
+ body: {
+ ext: {
+ prebidjs: {
+ urls: [
+ {
+ url: 'https://sync.inmobi.com/prebidjs'
+ },
+ {
+ url: 'https://eus.rubiconproject.com/usync.html?p=test'
+ }
+ ]
+ }
+ }
+ }
+ }];
+
+ it('should return urls from response when iframe enabled is false and pixel enabled', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, responses);
+ expect(res).to.deep.equal([
+ { type: 'image', url: 'https://sync.inmobi.com/prebidjs' },
+ { type: 'image', url: 'https://eus.rubiconproject.com/usync.html?p=test' }
+ ])
+ });
+
+ it('should return urls from response when iframe enabled is false and pixel enabled and empty responses', function () {
+ const responses = [];
+ const res = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, responses);
+ expect(res).to.deep.equal([
+ { type: 'image', url: `${syncEndPoint}` }
+ ])
+ });
+
+ it('should return urls from response when iframe enabled is false and pixel enabled and no response', function () {
+ const responses = undefined;
+ const res = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, responses);
+ expect(res).to.deep.equal([
+ { type: 'image', url: `${syncEndPoint}` }
+ ])
+ });
+
+ it('should return urls from response when iframe enabled is false and all consent parameters present', function () {
+ const res = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, responses, { gdprApplies: true, consentString: 'GDPR_CONSENT' }, 'USP_CONSENT', { gppString: 'GPP_STRING', applicableSections: [32, 51] });
+ expect(res).to.deep.equal([
+ { type: 'image', url: 'https://sync.inmobi.com/prebidjs?gdpr=1&gdpr_consent=GDPR_CONSENT&us_privacy=USP_CONSENT&gpp=GPP_STRING&gpp_sid=32%2C51' },
+ { type: 'image', url: 'https://eus.rubiconproject.com/usync.html?p=test&gdpr=1&gdpr_consent=GDPR_CONSENT&us_privacy=USP_CONSENT&gpp=GPP_STRING&gpp_sid=32%2C51' }
+ ])
+ });
+ });
+
+ describe('interpretResponse', function () {
+ const bidderRequest = {
+ refererInfo: {
+ page: 'https://raapchikgames.com',
+ topmostLocation: 'https://raapchikgames.com'
+ },
+ timeout: 3000,
+ gdprConsent: {
+ gdprApplies: true,
+ consentString: 'consentDataString',
+ vendorData: {
+ vendorConsents: {
+ '333': 1
+ },
+ },
+ apiVersion: 1,
+ },
+ };
+ function mockResponse(winningBidId, mediaType) {
+ return {
+ id: '95d08af8-2d50-4d75-a411-8ecd9224970e',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ id: '20dd72ed-930f-1000-e56f-07c37a793f30',
+ impid: winningBidId,
+ price: 1.1645,
+ adomain: ['advertiserDomain.sandbox.inmobi.com'],
+ crid: '88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301',
+ w: 320,
+ h: 50,
+ adm: 'test-ad',
+ mtype: mediaType,
+ nurl: 'https://et-l.w.inmobi.com/c.asm/',
+ api: 3,
+ cat: [],
+ ext: {
+ loggingPercentage: 100,
+ prebid: {
+ meta: {
+ advertiserDomains: ['advertiserDomain.sandbox.inmobi.com'],
+ networkName: 'inmobi'
+ }
+ }
+ }
+ }
+ ]
+ }
+ ],
+ ext: {
+ prebidjs: {
+ urls: [
+ { url: 'https://sync.inmobi.com/prebidjs' },
+ { url: 'https://eus.rubiconproject.com/usync.html?p=test' }
+ ]
+ }
+ }
+ };
+ };
+
+ function mockResponseNative(winningBidId, mediaType) {
+ return {
+ id: '95d08af8-2d50-4d75-a411-8ecd9224970e',
+ cur: 'USD',
+ seatbid: [
+ {
+ bid: [
+ {
+ id: '20dd72ed-930f-1000-e56f-07c37a793f30',
+ impid: winningBidId,
+ price: 1.1645,
+ adomain: ['advertiserDomain.sandbox.inmobi.com'],
+ crid: '88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301',
+ w: 320,
+ h: 50,
+ adm: '{"native":{"ver":"1.2","assets":[{"img":{"w":100,"h":100,"type":3,"url":"https://supply.inmobicdn.net/sandbox-prod-assets/Native_testAd.png"},"id":1,"required":1},{"id":2,"title":{"len":140,"text":"Native-Title-InMobi-Sandbox"},"required":1},{"data":{"type":1,"value":""},"id":3,"required":1},{"data":{"type":2,"value":"InMobi native test - Subtitle"},"id":4,"required":0},{"img":{"w":20,"h":20,"type":1,"url":"https://supply.inmobicdn.net/sandbox-prod-assets/inmobi-Logo-150x150.png"},"id":5,"required":0}],"link":{"clicktrackers":["https://c-eus.w.inmobi.com/"],"url":"https://www.inmobi.com"},"eventtrackers":[{"method":1,"event":1,"url":"https://et-eus.w.inmobi.com/"}]}}',
+ mtype: mediaType,
+ nurl: 'https://et-l.w.inmobi.com/c.asm/',
+ api: 3,
+ cat: [],
+ ext: {
+ loggingPercentage: 100,
+ prebid: {
+ meta: {
+ advertiserDomains: ['advertiserDomain.sandbox.inmobi.com'],
+ networkName: 'inmobi'
+ }
+ }
+ }
+ }
+ ]
+ }
+ ],
+ ext: {
+ prebidjs: {
+ urls: [
+ { url: 'https://sync.inmobi.com/prebidjs' },
+ { url: 'https://eus.rubiconproject.com/usync.html?p=test' }
+ ]
+ }
+ }
+ };
+ };
+
+ it('returns an empty array when bid response is empty', function () {
+ const bidRequests = [];
+ const response = {};
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse(response, request);
+ expect(bids).to.have.lengthOf(0);
+ });
+
+ it('should return an empty array when there is no bid response', function () {
+ const bidRequests = [];
+ const response = { seatbid: [] };
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids).to.have.lengthOf(0);
+ });
+
+ it('return banner response', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '124a',
+ }
+ }];
+ const response = mockResponse('bidId', 1);
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids).to.have.length(1);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].mediaType).to.deep.equal('banner');
+ expect(bids[0].requestId).to.deep.equal('bidId');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30');
+ expect(bids[0].cpm).to.equal(1.1645);
+ expect(bids[0].width).to.equal(320);
+ expect(bids[0].height).to.equal(50);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.loggingPercentage).to.equal(100);
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ });
+
+ it('bid response when banner wins among two ad units', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ mimes: ['video/mpeg'],
+ playerSize: [640, 320],
+ protocols: [5, 6],
+ maxduration: 30,
+ api: [1, 2]
+ }
+ },
+ params: {
+ plc: '123',
+ },
+ }, {
+ adUnitCode: 'impId',
+ bidId: 'bidId2',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123',
+ }
+ }];
+ const response = mockResponse('bidId2', 1);
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].mediaType).to.deep.equal('banner');
+ expect(bids[0].requestId).to.deep.equal('bidId2');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30');
+ expect(bids[0].cpm).to.equal(1.1645);
+ expect(bids[0].width).to.equal(320);
+ expect(bids[0].height).to.equal(50);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.loggingPercentage).to.equal(100);
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ });
+
+ if (FEATURES.VIDEO) {
+ it('return instream video response', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ mimes: ['video/mpeg'],
+ playerSize: [640, 320],
+ protocols: [5, 6],
+ maxduration: 30,
+ api: [1, 2]
+ }
+ },
+ params: {
+ plc: '123a1',
+ },
+ }];
+ const response = mockResponse('bidId', 2);
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids).to.have.lengthOf(1);
+ expect(bids[0].mediaType).to.equal(VIDEO);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].requestId).to.deep.equal('bidId');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30');
+ expect(bids[0].cpm).to.equal(1.1645);
+ expect(bids[0].playerWidth).to.equal(640);
+ expect(bids[0].playerHeight).to.equal(320);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.loggingPercentage).to.equal(100);
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ expect(bids[0].vastUrl).to.equal('https://et-l.w.inmobi.com/c.asm/');
+ expect(bids[0].vastXml).to.equal('test-ad');
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('return video outstream response', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ video: {
+ context: 'outstream',
+ mimes: ['video/mpeg'],
+ playerSize: [640, 320],
+ protocols: [5, 6],
+ maxduration: 30,
+ api: [1, 2]
+ }
+ },
+ params: {
+ plc: '123a1',
+ },
+ }];
+ const response = mockResponse('bidId', 2);
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids).to.have.lengthOf(1);
+ expect(bids[0].mediaType).to.equal(VIDEO);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].requestId).to.deep.equal('bidId');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30');
+ expect(bids[0].cpm).to.equal(1.1645);
+ expect(bids[0].playerWidth).to.equal(640);
+ expect(bids[0].playerHeight).to.equal(320);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.loggingPercentage).to.equal(100);
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ expect(bids[0].vastUrl).to.equal('https://et-l.w.inmobi.com/c.asm/');
+ expect(bids[0].vastXml).to.equal('test-ad');
+ });
+ }
+
+ if (FEATURES.VIDEO) {
+ it('bid response when video wins among two ad units', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ mimes: ['video/mpeg'],
+ playerSize: [640, 320],
+ protocols: [5, 6],
+ maxduration: 30,
+ api: [1, 2]
+ }
+ },
+ params: {
+ plc: '123',
+ },
+ }, {
+ adUnitCode: 'impId',
+ bidId: 'bidId2',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123',
+ }
+ }];
+ const response = mockResponse('bidId', 2);
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids).to.have.lengthOf(1);
+ expect(bids[0].mediaType).to.equal(VIDEO);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].requestId).to.deep.equal('bidId');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30');
+ expect(bids[0].cpm).to.equal(1.1645);
+ expect(bids[0].playerWidth).to.equal(640);
+ expect(bids[0].playerHeight).to.equal(320);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.loggingPercentage).to.equal(100);
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ expect(bids[0].vastUrl).to.equal('https://et-l.w.inmobi.com/c.asm/');
+ expect(bids[0].vastXml).to.equal('test-ad');
+ });
+ }
+
+ if (FEATURES.NATIVE) {
+ it('should correctly parse a native bid response', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ params: {
+ plc: '123',
+ },
+ native: true,
+ bidder: 'inmobi',
+ mediaTypes: {
+ native: {
+ image: {
+ required: true,
+ sizes: [120, 60],
+ sendId: true,
+ sendTargetingKeys: false
+ }
+ }
+ }
+ }];
+ const response = mockResponseNative('bidId', 4);
+ const expectedAdmNativeOrtb = JSON.parse(response.seatbid[0].bid[0].adm).native;
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+
+ expect(bids).to.have.lengthOf(1);
+ expect(bids[0].mediaType).to.equal(NATIVE);
+ // testing
+ expect(bids[0].requestId).to.deep.equal('bidId');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30')
+ expect(bids[0].cpm).to.deep.equal(1.1645);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].width).to.deep.equal(320);
+ expect(bids[0].height).to.deep.equal(50);
+ expect(bids[0].ad).to.equal(undefined);
+ expect(bids[0].native.ortb).not.to.be.null;
+ expect(bids[0].native.ortb).to.deep.equal(expectedAdmNativeOrtb);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ });
+ }
+
+ if (FEATURES.NATIVE) {
+ it('should correctly parse a native bid response when there are two ad units', function () {
+ const bidRequests = [{
+ adUnitCode: 'impId',
+ bidId: 'bidId',
+ params: {
+ plc: '123',
+ },
+ native: true,
+ bidder: 'inmobi',
+ mediaTypes: {
+ native: {
+ image: {
+ required: true,
+ sizes: [120, 60],
+ sendId: true,
+ sendTargetingKeys: false
+ }
+ }
+ }
+ },
+ {
+ adUnitCode: 'impId',
+ bidId: 'bidId2',
+ mediaTypes: {
+ banner: {
+ sizes: [[320, 50]]
+ }
+ },
+ params: {
+ plc: '123',
+ }
+ }];
+ const response = mockResponseNative('bidId', 4);
+ const expectedAdmNativeOrtb = JSON.parse(response.seatbid[0].bid[0].adm).native;
+ const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest));
+ const bids = spec.interpretResponse({ body: response }, request);
+ expect(bids).to.have.lengthOf(1);
+ expect(bids[0].mediaType).to.equal(NATIVE);
+ // testing
+ expect(bids[0].requestId).to.deep.equal('bidId');
+ expect(bids[0].seatBidId).to.deep.equal('20dd72ed-930f-1000-e56f-07c37a793f30')
+ expect(bids[0].cpm).to.deep.equal(1.1645);
+ expect(bids[0].currency).to.deep.equal('USD');
+ expect(bids[0].width).to.deep.equal(320);
+ expect(bids[0].height).to.deep.equal(50);
+ expect(bids[0].ad).to.equal(undefined);
+ expect(bids[0].native.ortb).not.to.be.null;
+ expect(bids[0].native.ortb).to.deep.equal(expectedAdmNativeOrtb);
+ expect(bids[0].creativeId).to.deep.equal('88b37efcd5f34d368e60317c706942a4ef6f6976eb394046958fd0e8a7b75301');
+ expect(bids[0].meta.advertiserDomains[0]).to.deep.equal('advertiserDomain.sandbox.inmobi.com');
+ expect(bids[0].meta.prebid.meta.networkName).to.deep.equal('inmobi');
+ });
+ }
+ });
+});
diff --git a/test/spec/modules/intentIqAnalyticsAdapter_spec.js b/test/spec/modules/intentIqAnalyticsAdapter_spec.js
index 2391c550da2..5ba0686af0b 100644
--- a/test/spec/modules/intentIqAnalyticsAdapter_spec.js
+++ b/test/spec/modules/intentIqAnalyticsAdapter_spec.js
@@ -1,15 +1,16 @@
import { expect } from 'chai';
import iiqAnalyticsAnalyticsAdapter from 'modules/intentIqAnalyticsAdapter.js';
import * as utils from 'src/utils.js';
-import * as detectBrowserUtils from '../../../libraries/detectBrowserUtils/detectBrowserUtils';
import { server } from 'test/mocks/xhr.js';
import { config } from 'src/config.js';
import { EVENTS } from 'src/constants.js';
import * as events from 'src/events.js';
import { getStorageManager } from 'src/storageManager.js';
import sinon from 'sinon';
-import { REPORTER_ID, getReferrer, preparePayload } from '../../../modules/intentIqAnalyticsAdapter';
+import { REPORTER_ID, preparePayload } from '../../../modules/intentIqAnalyticsAdapter';
import {FIRST_PARTY_KEY, VERSION} from '../../../libraries/intentIqConstants/intentIqConstants.js';
+import * as detectBrowserUtils from '../../../libraries/intentIqUtils/detectBrowserUtils.js';
+import {getReferrer, appendVrrefAndFui} from '../../../libraries/intentIqUtils/getRefferer.js';
const partner = 10;
const defaultData = '{"pcid":"f961ffb1-a0e1-4696-a9d2-a21d815bd344", "group": "A"}';
@@ -89,7 +90,8 @@ describe('IntentIQ tests all', function () {
dataInLs: null,
eidl: null,
lsIdsInitialized: false,
- manualWinReportEnabled: false
+ manualWinReportEnabled: false,
+ domainName: null
};
if (iiqAnalyticsAnalyticsAdapter.track.restore) {
iiqAnalyticsAnalyticsAdapter.track.restore();
@@ -115,13 +117,16 @@ describe('IntentIQ tests all', function () {
it('IIQ Analytical Adapter bid win report', function () {
localStorage.setItem(FIRST_PARTY_KEY, defaultData);
+ getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({href: 'http://localhost:9876/'});
+ const expectedVrref = encodeURIComponent(getWindowLocationStub().href);
events.emit(EVENTS.BID_WON, wonRequest);
expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain('https://reports.intentiq.com/report?pid=' + partner + '&mct=1');
- expect(request.url).to.contain(`&jsver=${version}&vrref=${encodeURIComponent('http://localhost:9876/')}`);
+ expect(request.url).to.contain(`&jsver=${version}`);
+ expect(request.url).to.contain(`&vrref=${expectedVrref}`);
expect(request.url).to.contain('&payload=');
expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
});
@@ -132,13 +137,15 @@ describe('IntentIQ tests all', function () {
it('should handle BID_WON event with group configuration from local storage', function () {
localStorage.setItem(FIRST_PARTY_KEY, '{"pcid":"testpcid", "group": "B"}');
+ const expectedVrref = encodeURIComponent('http://localhost:9876/');
events.emit(EVENTS.BID_WON, wonRequest);
expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain('https://reports.intentiq.com/report?pid=' + partner + '&mct=1');
- expect(request.url).to.contain(`&jsver=${version}&vrref=${encodeURIComponent('http://localhost:9876/')}`);
+ expect(request.url).to.contain(`&jsver=${version}`);
+ expect(request.url).to.contain(`&vrref=${expectedVrref}`);
expect(request.url).to.contain('iiqid=testpcid');
});
@@ -153,9 +160,11 @@ describe('IntentIQ tests all', function () {
const dataToSend = preparePayload(wonRequest);
const base64String = btoa(JSON.stringify(dataToSend));
const payload = `[%22${base64String}%22]`;
- expect(request.url).to.equal(
- `https://reports.intentiq.com/report?pid=${partner}&mct=1&iiqid=${defaultDataObj.pcid}&agid=${REPORTER_ID}&jsver=${version}&vrref=${getReferrer()}&source=pbjs&payload=${payload}&uh=`
+ const expectedUrl = appendVrrefAndFui(
+ `https://reports.intentiq.com/report?pid=${partner}&mct=1&iiqid=${defaultDataObj.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&payload=${payload}&uh=`,
+ iiqAnalyticsAnalyticsAdapter.initOptions.domainName
);
+ expect(request.url).to.equal(expectedUrl);
expect(dataToSend.pcid).to.equal(defaultDataObj.pcid)
});
@@ -250,8 +259,69 @@ describe('IntentIQ tests all', function () {
expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain(`https://reports.intentiq.com/report?pid=${partner}&mct=1`);
- expect(request.url).to.contain(`&jsver=${version}&vrref=${encodeURIComponent('http://localhost:9876/')}`);
+ expect(request.url).to.contain(`&jsver=${version}`);
+ expect(request.url).to.contain(`&vrref=${encodeURIComponent('http://localhost:9876/')}`);
expect(request.url).to.contain('&payload=');
expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
});
+
+ const testCasesVrref = [
+ {
+ description: 'domainName matches window.top.location.href',
+ getWindowSelf: {},
+ getWindowTop: { location: { href: 'http://example.com/page' } },
+ getWindowLocation: { href: 'http://example.com/page' },
+ domainName: 'example.com',
+ expectedVrref: encodeURIComponent('http://example.com/page'),
+ shouldContainFui: false
+ },
+ {
+ description: 'domainName does not match window.top.location.href',
+ getWindowSelf: {},
+ getWindowTop: { location: { href: 'http://anotherdomain.com/page' } },
+ getWindowLocation: { href: 'http://anotherdomain.com/page' },
+ domainName: 'example.com',
+ expectedVrref: encodeURIComponent('example.com'),
+ shouldContainFui: false
+ },
+ {
+ description: 'domainName is missing, only fui=1 is returned',
+ getWindowSelf: {},
+ getWindowTop: { location: { href: '' } },
+ getWindowLocation: { href: '' },
+ domainName: null,
+ expectedVrref: '',
+ shouldContainFui: true
+ },
+ {
+ description: 'domainName is missing',
+ getWindowSelf: {},
+ getWindowTop: { location: { href: 'http://example.com/page' } },
+ getWindowLocation: { href: 'http://example.com/page' },
+ domainName: null,
+ expectedVrref: encodeURIComponent('http://example.com/page'),
+ shouldContainFui: false
+ },
+ ];
+
+ testCasesVrref.forEach(({ description, getWindowSelf, getWindowTop, getWindowLocation, domainName, expectedVrref, shouldContainFui }) => {
+ it(`should append correct vrref when ${description}`, function () {
+ getWindowSelfStub = sinon.stub(utils, 'getWindowSelf').returns(getWindowSelf);
+ getWindowTopStub = sinon.stub(utils, 'getWindowTop').returns(getWindowTop);
+ getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns(getWindowLocation);
+
+ const url = 'https://reports.intentiq.com/report?pid=10';
+ const modifiedUrl = appendVrrefAndFui(url, domainName);
+ const urlObj = new URL(modifiedUrl);
+
+ const vrref = encodeURIComponent(urlObj.searchParams.get('vrref') || '');
+ const fui = urlObj.searchParams.get('fui');
+
+ expect(vrref).to.equal(expectedVrref);
+ expect(urlObj.searchParams.has('fui')).to.equal(shouldContainFui);
+ if (shouldContainFui) {
+ expect(fui).to.equal('1');
+ }
+ });
+ });
});
diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js
index cc3495aeb11..b95c3603e3a 100644
--- a/test/spec/modules/intentIqIdSystem_spec.js
+++ b/test/spec/modules/intentIqIdSystem_spec.js
@@ -2,10 +2,11 @@ import { expect } from 'chai';
import { intentIqIdSubmodule, storage } from 'modules/intentIqIdSystem.js';
import * as utils from 'src/utils.js';
import { server } from 'test/mocks/xhr.js';
-import { decryptData, handleClientHints, handleGPPData, readData } from '../../../modules/intentIqIdSystem';
+import { decryptData, handleClientHints, readData } from '../../../modules/intentIqIdSystem';
+import {getGppValue} from '../../../libraries/intentIqUtils/getGppValue.js';
import { gppDataHandler, uspDataHandler } from '../../../src/consentHandler';
import { clearAllCookies } from '../../helpers/cookies';
-import { detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/detectBrowserUtils/detectBrowserUtils';
+import { detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/intentIqUtils/detectBrowserUtils';
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY} from '../../../libraries/intentIqConstants/intentIqConstants.js';
const partner = 10;
@@ -252,7 +253,7 @@ describe('IntentIQ tests', function () {
JSON.stringify({ pid: 'test_pid', data: 'test_personid', ls: false })
);
expect(callBackSpy.calledOnce).to.be.true;
- expect(callBackSpy.args[0][0]).to.deep.equal({});
+ expect(callBackSpy.args[0][0]).to.deep.equal({eids: []});
});
it('send addition parameters if were found in localstorage', function () {
@@ -306,36 +307,39 @@ describe('IntentIQ tests', function () {
expect(logErrorStub.called).to.be.true;
});
- describe('handleGPPData', function () {
- it('should convert array of objects to a single JSON string', function () {
- const input = [
- { key1: 'value1' },
- { key2: 'value2' }
- ];
- const expectedOutput = JSON.stringify({ key1: 'value1', key2: 'value2' });
- const result = handleGPPData(input);
- expect(result).to.equal(expectedOutput);
- });
-
- it('should convert a single object to a JSON string', function () {
- const input = { key1: 'value1', key2: 'value2' };
- const expectedOutput = JSON.stringify(input);
- const result = handleGPPData(input);
- expect(result).to.equal(expectedOutput);
- });
-
- it('should handle empty object', function () {
- const input = {};
- const expectedOutput = JSON.stringify(input);
- const result = handleGPPData(input);
- expect(result).to.equal(expectedOutput);
- });
-
- it('should handle empty array', function () {
- const input = [];
- const expectedOutput = JSON.stringify({});
- const result = handleGPPData(input);
- expect(result).to.equal(expectedOutput);
+ describe('getGppValue', function () {
+ const testCases = [
+ {
+ description: 'should return gppString and gpi=0 when GPP data exists',
+ input: { gppString: '{"key1":"value1","key2":"value2"}' },
+ expectedOutput: { gppString: '{"key1":"value1","key2":"value2"}', gpi: 0 }
+ },
+ {
+ description: 'should return empty gppString and gpi=1 when GPP data does not exist',
+ input: null,
+ expectedOutput: { gppString: '', gpi: 1 }
+ },
+ {
+ description: 'should return empty gppString and gpi=1 when gppString is not set',
+ input: {},
+ expectedOutput: { gppString: '', gpi: 1 }
+ },
+ {
+ description: 'should handle GPP data with empty string',
+ input: { gppString: '' },
+ expectedOutput: { gppString: '', gpi: 1 }
+ }
+ ];
+
+ testCases.forEach(({ description, input, expectedOutput }) => {
+ it(description, function () {
+ sinon.stub(gppDataHandler, 'getConsentData').returns(input);
+
+ const result = getGppValue();
+ expect(result).to.deep.equal(expectedOutput);
+
+ gppDataHandler.getConsentData.restore();
+ });
});
});
@@ -431,54 +435,36 @@ describe('IntentIQ tests', function () {
expect(firstPartyData.uspapi_value).to.equal(uspData);
});
- it('should set cmpData.gpp and cmpData.gpp_sid if gppData exists and has parsedSections with usnat', function () {
- const gppData = {
- parsedSections: {
- usnat: { key1: 'value1', key2: 'value2' }
- },
- applicableSections: ['usnat']
+ it('should create a request with gpp data if gppData exists and has gppString', function () {
+ const mockGppValue = {
+ gppString: '{"key1":"value1","key2":"value2"}',
+ gpi: 0
};
- gppDataHandlerStub.returns(gppData);
- let callBackSpy = sinon.spy();
- let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback;
- submoduleCallback(callBackSpy);
- let request = server.requests[0];
- expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid=');
- request.respond(
- 200,
- responseHeader,
- JSON.stringify({ pid: 'test_pid', data: 'test_personid', ls: false, isOptedOut: false })
- );
- expect(callBackSpy.calledOnce).to.be.true;
-
- const firstPartyData = JSON.parse(localStorage.getItem(FIRST_PARTY_KEY));
- expect(firstPartyData.gpp_value).to.equal(JSON.stringify({ key1: 'value1', key2: 'value2' }));
- });
-
- it('should handle gppData without usnat in parsedSections', function () {
- const gppData = {
- parsedSections: {
- euconsent: { key1: 'value1' }
- },
- applicableSections: ['euconsent']
+ const mockConfig = {
+ params: { partner: partner },
+ enabledStorageTypes: ['localStorage']
};
- gppDataHandlerStub.returns(gppData);
+
+ gppDataHandlerStub.returns(mockGppValue);
let callBackSpy = sinon.spy();
- let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback;
+ let submoduleCallback = intentIqIdSubmodule.getId(mockConfig).callback;
submoduleCallback(callBackSpy);
+
let request = server.requests[0];
- expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=10&pt=17&dpn=1&iiqidtype=2&iiqpcid=');
+
request.respond(
200,
responseHeader,
- JSON.stringify({ pid: 'test_pid', data: 'test_personid', ls: false, isOptedOut: true })
+ JSON.stringify({ pid: 'test_pid', data: 'test_personid', ls: true })
);
+
+ expect(request.url).to.contain(`&gpp=${encodeURIComponent(mockGppValue.gppString)}`);
expect(callBackSpy.calledOnce).to.be.true;
const firstPartyData = JSON.parse(localStorage.getItem(FIRST_PARTY_KEY));
- expect(firstPartyData.gpp_value).to.equal('');
+ expect(firstPartyData.gpp_string_value).to.equal(mockGppValue.gppString);
});
});
diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js
index 74c0a1f5967..47b89bb5956 100644
--- a/test/spec/modules/koblerBidAdapter_spec.js
+++ b/test/spec/modules/koblerBidAdapter_spec.js
@@ -4,6 +4,8 @@ import {newBidder} from 'src/adapters/bidderFactory.js';
import {config} from 'src/config.js';
import * as utils from 'src/utils.js';
import {getRefererInfo} from 'src/refererDetection.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
function createBidderRequest(auctionId, timeout, pageUrl, addGdprConsent) {
const gdprConsent = addGdprConsent ? {
@@ -598,7 +600,7 @@ describe('KoblerAdapter', function () {
cur: 'USD'
}
};
- const bids = spec.interpretResponse(responseWithTwoBids)
+ const bids = spec.interpretResponse(responseWithTwoBids, {})
const expectedBids = [
{
@@ -665,25 +667,30 @@ describe('KoblerAdapter', function () {
});
it('Should trigger pixel with replaced nurl if nurl is not empty', function () {
- config.setConfig({
- 'currency': {
- 'adServerCurrency': 'NOK'
- }
- });
- spec.onBidWon({
- originalCpm: 1.532,
- cpm: 8.341,
- currency: 'NOK',
- nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}',
- adserverTargeting: {
+ setCurrencyConfig({ adServerCurrency: 'NOK' });
+ let validBidRequests = [{ params: {} }];
+ let refererInfo = { page: 'page' };
+ const bidderRequest = { refererInfo };
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ JSON.parse(spec.buildRequests(validBidRequests, res).data);
+ const bids = spec.interpretResponse({ body: { seatbid: [{ bid: [{
+ originalCpm: 1.532,
+ price: 8.341,
+ currency: 'NOK',
+ nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}',
+ }]}]}}, { bidderRequest: res });
+ const bidToWon = bids[0];
+ bidToWon.adserverTargeting = {
hb_pb: 8
}
- });
+ spec.onBidWon(bidToWon);
- expect(utils.triggerPixel.callCount).to.be.equal(1);
- expect(utils.triggerPixel.firstCall.args[0]).to.be.equal(
- 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=8.341&sp_cur=NOK&asp=8&asp_cur=NOK'
- );
+ expect(utils.triggerPixel.callCount).to.be.equal(1);
+ expect(utils.triggerPixel.firstCall.args[0]).to.be.equal(
+ 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=8.341&sp_cur=NOK&asp=8&asp_cur=NOK'
+ );
+ setCurrencyConfig({});
+ });
});
});
diff --git a/test/spec/modules/liveIntentExternalIdSystem_spec.js b/test/spec/modules/liveIntentExternalIdSystem_spec.js
index f87819ec85e..3e49eb5fc4b 100644
--- a/test/spec/modules/liveIntentExternalIdSystem_spec.js
+++ b/test/spec/modules/liveIntentExternalIdSystem_spec.js
@@ -54,7 +54,7 @@ describe('LiveIntentExternalId', function() {
},
{
clientRef: {},
- sourceEvent: { hash: '123' },
+ sourceEvent: { emailHash: '123' },
type: 'collect'
}])
});
@@ -120,7 +120,7 @@ describe('LiveIntentExternalId', function() {
expect(window.liQHub[1]).to.eql({
clientRef: {},
- sourceEvent: { hash: '58131bc547fb87af94cebdaf3102321f' },
+ sourceEvent: { emailHash: '58131bc547fb87af94cebdaf3102321f' },
type: 'collect'
})
@@ -179,7 +179,7 @@ describe('LiveIntentExternalId', function() {
},
{
clientRef: {},
- sourceEvent: { hash: '123' },
+ sourceEvent: { emailHash: '123' },
type: 'collect'
}])
});
@@ -205,7 +205,36 @@ describe('LiveIntentExternalId', function() {
},
{
clientRef: {},
- sourceEvent: { hash: '123' },
+ sourceEvent: { emailHash: '123' },
+ type: 'collect'
+ }])
+ });
+
+ it('should include the identifier data if it is present in config', function() {
+ const configParams = {
+ params: {
+ ...defaultConfigParams.params,
+ distributorId: 'did-1111',
+ emailHash: '123',
+ ipv4: 'foov4',
+ ipv6: 'foov6',
+ userAgent: 'bar'
+ }
+ }
+ liveIntentExternalIdSubmodule.decode({}, configParams);
+ expect(window.liQHub).to.eql([{
+ clientDetails: { name: 'prebid', version: '$prebid.version$' },
+ clientRef: {},
+ collectSettings: { timeout: DEFAULT_AJAX_TIMEOUT },
+ consent: {},
+ integration: { distributorId: 'did-1111', publisherId: defaultConfigParams.params.publisherId, type: 'custom' },
+ partnerCookies: new Set(),
+ resolveSettings: { identityPartner: 'did-1111', timeout: DEFAULT_AJAX_TIMEOUT },
+ type: 'register_client'
+ },
+ {
+ clientRef: {},
+ sourceEvent: { emailHash: '123', ipv4: 'foov4', ipv6: 'foov6', userAgent: 'bar' },
type: 'collect'
}])
});
diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js
index ea289186144..50f51bd3dc8 100644
--- a/test/spec/modules/liveIntentIdSystem_spec.js
+++ b/test/spec/modules/liveIntentIdSystem_spec.js
@@ -369,6 +369,21 @@ describe('LiveIntentId', function() {
expect(callBackSpy.calledOnce).to.be.true;
});
+ it('should include ip4,ip6,userAgent if it\'s present', function(done) {
+ liveIntentIdSubmodule.getId({ params: {
+ ...defaultConfigParams,
+ ipv4: 'foov4',
+ ipv6: 'foov6',
+ userAgent: 'boo'
+ }});
+ setTimeout(() => {
+ let request = rpRequests()[0];
+ expect(request.url).to.match(/^https:\/\/rp\.liadm\.com\/j?.*pip=.*&pip6=.*$/)
+ expect(request.requestHeaders['X-LI-Provided-User-Agent']).to.be.eq('boo')
+ done();
+ }, 300);
+ });
+
it('should send an error when the cookie jar throws an unexpected error', function() {
getCookieStub.throws('CookieError', 'A message');
liveIntentIdSubmodule.getId({ params: defaultConfigParams });
diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js
index 5ab00859d81..7a3cd26dbe7 100644
--- a/test/spec/modules/livewrappedBidAdapter_spec.js
+++ b/test/spec/modules/livewrappedBidAdapter_spec.js
@@ -3,6 +3,8 @@ import {spec, storage} from 'modules/livewrappedBidAdapter.js';
import {config} from 'src/config.js';
import * as utils from 'src/utils.js';
import { NATIVE, VIDEO } from 'src/mediaTypes.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
describe('Livewrapped adapter tests', function () {
let sandbox,
@@ -1178,50 +1180,21 @@ describe('Livewrapped adapter tests', function () {
sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false);
sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true);
- let origGetConfig = config.getConfig;
- sandbox.stub(config, 'getConfig').callsFake(function (key) {
- if (key === 'currency.adServerCurrency') {
- return 'EUR';
- }
- return origGetConfig.apply(config, arguments);
- });
-
+ setCurrencyConfig({ adServerCurrency: 'EUR' });
let testbidRequest = clone(bidderRequest);
let bids = testbidRequest.bids.map(b => {
b.getFloor = function () { return { floor: 10, currency: 'EUR' }; }
return b;
});
- let result = spec.buildRequests(bids, testbidRequest);
- let data = JSON.parse(result.data);
-
- expect(result.url).to.equal('https://lwadm.com/ad');
-
- let expectedQuery = {
- auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C',
- publisherId: '26947112-2289-405D-88C1-A7340C57E63E',
- userId: 'user id',
- url: 'https://www.domain.com',
- seats: {'dsp': ['seat 1']},
- version: '1.4',
- width: 100,
- height: 100,
- cookieSupport: true,
- flrCur: 'EUR',
- adRequests: [{
- adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37',
- callerAdUnitId: 'panorama_d_1',
- bidId: '2ffb201a808da7',
- rtbData: {
- ext: {
- tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D'
- },
- },
- formats: [{width: 980, height: 240}, {width: 980, height: 120}],
- flr: 10
- }]
- };
- expect(data).to.deep.equal(expectedQuery);
+ return addFPDToBidderRequest(testbidRequest).then(res => {
+ let result = spec.buildRequests(bids, res);
+ let data = JSON.parse(result.data);
+ expect(result.url).to.equal('https://lwadm.com/ad');
+ expect(data.adRequests[0].flr).to.eql(10)
+ expect(data.flrCur).to.eql('EUR')
+ setCurrencyConfig({});
+ });
});
it('getFloor returns valid floor - default currency', function() {
diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js
index fbd4ba7c000..27efef9df50 100644
--- a/test/spec/modules/lotamePanoramaIdSystem_spec.js
+++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js
@@ -2,7 +2,6 @@ import {
lotamePanoramaIdSubmodule,
storage,
} from 'modules/lotamePanoramaIdSystem.js';
-import { uspDataHandler } from 'src/adapterManager.js';
import * as utils from 'src/utils.js';
import { server } from 'test/mocks/xhr.js';
import sinon from 'sinon';
@@ -19,7 +18,6 @@ describe('LotameId', function() {
let setLocalStorageStub;
let removeFromLocalStorageStub;
let timeStampStub;
- let uspConsentDataStub;
let requestHost;
const nowTimestamp = new Date().getTime();
@@ -34,7 +32,6 @@ describe('LotameId', function() {
'removeDataFromLocalStorage'
);
timeStampStub = sinon.stub(utils, 'timestamp').returns(nowTimestamp);
- uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData');
if (navigator.userAgent && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
requestHost = 'https://c.ltmsphrcl.net/id';
} else {
@@ -50,7 +47,6 @@ describe('LotameId', function() {
setLocalStorageStub.restore();
removeFromLocalStorageStub.restore();
timeStampStub.restore();
- uspConsentDataStub.restore();
});
describe('caching initial data received from the remote server', function () {
@@ -451,70 +447,6 @@ describe('LotameId', function() {
});
});
- describe('when gdpr applies and falls back to eupubconsent cookie', function () {
- let request;
- let callBackSpy = sinon.spy();
- let consentData = {
- gdprApplies: true,
- consentString: undefined
- };
-
- beforeEach(function () {
- getCookieStub
- .withArgs('eupubconsent-v2')
- .returns('consentGiven');
-
- let submoduleCallback = lotamePanoramaIdSubmodule.getId({}, consentData).callback;
- submoduleCallback(callBackSpy);
-
- // the contents of the response don't matter for this
- request = server.requests[0];
- request.respond(200, responseHeader, '');
- });
-
- it('should call the remote server when getId is called', function () {
- expect(callBackSpy.calledOnce).to.be.true;
- });
-
- it('should pass the gdpr consent string back', function() {
- expect(request.url).to.be.eq(
- `${requestHost}?gdpr_applies=true&gdpr_consent=consentGiven`
- );
- });
- });
-
- describe('when gdpr applies and falls back to euconsent cookie', function () {
- let request;
- let callBackSpy = sinon.spy();
- let consentData = {
- gdprApplies: true,
- consentString: undefined
- };
-
- beforeEach(function () {
- getCookieStub
- .withArgs('euconsent-v2')
- .returns('consentGiven');
-
- let submoduleCallback = lotamePanoramaIdSubmodule.getId({}, consentData).callback;
- submoduleCallback(callBackSpy);
-
- // the contents of the response don't matter for this
- request = server.requests[0];
- request.respond(200, responseHeader, '');
- });
-
- it('should call the remote server when getId is called', function () {
- expect(callBackSpy.calledOnce).to.be.true;
- });
-
- it('should pass the gdpr consent string back', function() {
- expect(request.url).to.be.eq(
- `${requestHost}?gdpr_applies=true&gdpr_consent=consentGiven`
- );
- });
- });
-
describe('when gdpr applies but no consent string is available', function () {
let request;
let callBackSpy = sinon.spy();
@@ -543,64 +475,6 @@ describe('LotameId', function() {
});
});
- describe('when no consentData and falls back to eupubconsent cookie', function () {
- let request;
- let callBackSpy = sinon.spy();
- let consentData;
-
- beforeEach(function () {
- getCookieStub
- .withArgs('eupubconsent-v2')
- .returns('consentGiven');
-
- let submoduleCallback = lotamePanoramaIdSubmodule.getId({}, consentData).callback;
- submoduleCallback(callBackSpy);
-
- // the contents of the response don't matter for this
- request = server.requests[0];
- request.respond(200, responseHeader, '');
- });
-
- it('should call the remote server when getId is called', function () {
- expect(callBackSpy.calledOnce).to.be.true;
- });
-
- it('should pass the gdpr consent string back', function() {
- expect(request.url).to.be.eq(
- `${requestHost}?gdpr_consent=consentGiven`
- );
- });
- });
-
- describe('when no consentData and falls back to euconsent cookie', function () {
- let request;
- let callBackSpy = sinon.spy();
- let consentData;
-
- beforeEach(function () {
- getCookieStub
- .withArgs('euconsent-v2')
- .returns('consentGiven');
-
- let submoduleCallback = lotamePanoramaIdSubmodule.getId({}, consentData).callback;
- submoduleCallback(callBackSpy);
-
- // the contents of the response don't matter for this
- request = server.requests[0];
- request.respond(200, responseHeader, '');
- });
-
- it('should call the remote server when getId is called', function () {
- expect(callBackSpy.calledOnce).to.be.true;
- });
-
- it('should pass the gdpr consent string back', function() {
- expect(request.url).to.be.eq(
- `${requestHost}?gdpr_consent=consentGiven`
- );
- });
- });
-
describe('when no consentData and no cookies', function () {
let request;
let callBackSpy = sinon.spy();
@@ -809,7 +683,6 @@ describe('LotameId', function() {
let callBackSpy = sinon.spy();
beforeEach(function () {
- uspConsentDataStub.returns('1NNN');
let submoduleCallback = lotamePanoramaIdSubmodule.getId(
{
params: {
@@ -840,12 +713,6 @@ describe('LotameId', function() {
expect(callBackSpy.calledOnce).to.be.true;
});
- it('should pass the usp consent string and client id back', function () {
- expect(request.url).to.be.eq(
- `${requestHost}?gdpr_applies=false&us_privacy=1NNN&c=1234`
- );
- });
-
it('should NOT set an expiry for the client', function () {
sinon.assert.neverCalledWith(
setCookieStub,
diff --git a/test/spec/modules/mediaimpactBidAdapter_spec.js b/test/spec/modules/mediaimpactBidAdapter_spec.js
index 806f0adeabe..5bf088c0334 100644
--- a/test/spec/modules/mediaimpactBidAdapter_spec.js
+++ b/test/spec/modules/mediaimpactBidAdapter_spec.js
@@ -1,6 +1,7 @@
import {expect} from 'chai';
import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/mediaimpactBidAdapter.js';
import {newBidder} from 'src/adapters/bidderFactory.js';
+import * as miUtils from 'libraries/mediaImpactUtils/index.js';
const BIDDER_CODE = 'mediaimpact';
@@ -117,7 +118,7 @@ describe('MediaimpactAdapter', function () {
describe('joinSizesToString', function () {
it('success convert sizes list to string', function () {
- const sizesStr = spec.joinSizesToString([[300, 250], [300, 600]]);
+ const sizesStr = miUtils.joinSizesToString([[300, 250], [300, 600]]);
expect(sizesStr).to.equal('300x250|300x600');
});
});
@@ -238,7 +239,7 @@ describe('MediaimpactAdapter', function () {
let ajaxStub;
beforeEach(() => {
- ajaxStub = sinon.stub(spec, 'postRequest')
+ ajaxStub = sinon.stub(miUtils, 'postRequest')
})
afterEach(() => {
diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js
index 7c3cf88dace..8a298acae80 100644
--- a/test/spec/modules/medianetAnalyticsAdapter_spec.js
+++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js
@@ -210,7 +210,7 @@ describe('Media.net Analytics Adapter', function() {
expect(noBidLog.pvnm).to.have.ordered.members(['-2', 'bidder1', 'bidder1', 'medianet']);
expect(noBidLog.mtype).to.have.ordered.members([banner, banner, banner, banner]);
expect(noBidLog.status).to.have.ordered.members(['1', '2', '2', '2']);
- expect(noBidLog.size).to.have.ordered.members(['', '', '', '']);
+ expect(noBidLog.size).to.have.ordered.members(['300x100|300x250', '300x100', '300x250', '300x250|300x100']);
expect(noBidLog.szs).to.have.ordered.members(['300x100|300x250', '300x100', '300x250', '300x250|300x100']);
});
@@ -225,7 +225,7 @@ describe('Media.net Analytics Adapter', function() {
it('should have winner log in standard auction', function() {
medianetAnalytics.clearlogsQueue();
performStandardAuctionWithWinner();
- let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner);
+ let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner === '1');
medianetAnalytics.clearlogsQueue();
expect(winnerLog.length).to.equal(1);
@@ -234,7 +234,7 @@ describe('Media.net Analytics Adapter', function() {
it('should have correct values in winner log', function() {
medianetAnalytics.clearlogsQueue();
performStandardAuctionWithWinner();
- let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner);
+ let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner === '1');
medianetAnalytics.clearlogsQueue();
expect(winnerLog[0]).to.include({
@@ -258,7 +258,7 @@ describe('Media.net Analytics Adapter', function() {
it('should have correct bid floor data in winner log', function() {
medianetAnalytics.clearlogsQueue();
performAuctionWithFloorConfig();
- let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner);
+ let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner === '1');
medianetAnalytics.clearlogsQueue();
expect(winnerLog[0]).to.include({
@@ -309,32 +309,32 @@ describe('Media.net Analytics Adapter', function() {
it('should pick winning bid if multibids with same request id', function() {
performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_DIFF_CPM);
- let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
+ let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
medianetAnalytics.clearlogsQueue();
const reversedResponseArray = [].concat(MOCK.BIDS_SAME_REQ_DIFF_CPM).reverse();
performStandardAuctionMultiBidWithSameRequestId(reversedResponseArray);
- winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
+ winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
});
it('should pick winning bid if multibids with same request id and same time to respond', function() {
performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_DIFF_CPM_SAME_TIME);
- let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
+ let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
medianetAnalytics.clearlogsQueue();
});
it('should pick winning bid if multibids with same request id and equal cpm', function() {
performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_EQUAL_CPM);
- let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
+ let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
medianetAnalytics.clearlogsQueue();
const reversedResponseArray = [].concat(MOCK.BIDS_SAME_REQ_EQUAL_CPM).reverse();
performStandardAuctionMultiBidWithSameRequestId(reversedResponseArray);
- winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
+ winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
});
diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js
index d3eab769f4a..5f2efc7864f 100644
--- a/test/spec/modules/medianetBidAdapter_spec.js
+++ b/test/spec/modules/medianetBidAdapter_spec.js
@@ -531,6 +531,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -622,6 +632,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -714,6 +734,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -807,6 +837,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -901,6 +941,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -1008,6 +1058,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -1104,6 +1164,16 @@ let VALID_BID_REQUEST = [{
'w': 1000,
'h': 1000
},
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
+ }
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
'imp': [
@@ -1640,6 +1710,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -1747,6 +1827,16 @@ let VALID_BID_REQUEST = [{
'screen': {
'w': 1000,
'h': 1000
+ },
+ 'vcoords': {
+ 'top_left': {
+ 'x': 50,
+ 'y': 100
+ },
+ 'bottom_right': {
+ 'x': 490,
+ 'y': 880
+ }
}
},
'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d',
@@ -1829,6 +1919,10 @@ describe('Media.net bid adapter', function () {
let sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
+ sandbox.stub(window.top, 'innerHeight').value(780)
+ sandbox.stub(window.top, 'innerWidth').value(440)
+ sandbox.stub(window.top, 'scrollY').value(100)
+ sandbox.stub(window.top, 'scrollX').value(50)
});
afterEach(function () {
diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js
index 2b05a1f0830..afd96091d84 100644
--- a/test/spec/modules/missenaBidAdapter_spec.js
+++ b/test/spec/modules/missenaBidAdapter_spec.js
@@ -1,10 +1,13 @@
import { expect } from 'chai';
import { spec, storage } from 'modules/missenaBidAdapter.js';
import { BANNER } from '../../../src/mediaTypes.js';
+import { config } from 'src/config.js';
+import * as autoplay from 'libraries/autoplayDetection/autoplay.js';
const REFERRER = 'https://referer';
const REFERRER2 = 'https://referer2';
const COOKIE_DEPRECATION_LABEL = 'test';
+const API_KEY = 'PA-XXXXXX';
describe('Missena Adapter', function () {
$$PREBID_GLOBAL$$.bidderSettings = {
@@ -12,6 +15,9 @@ describe('Missena Adapter', function () {
storageAllowed: true,
},
};
+ let sandbox = sinon.sandbox.create();
+ sandbox.stub(config, 'getConfig').withArgs('coppa').returns(true);
+ sandbox.stub(autoplay, 'isAutoplayEnabled').returns(false);
const bidId = 'abc';
const bid = {
@@ -25,7 +31,7 @@ describe('Missena Adapter', function () {
},
},
params: {
- apiKey: 'PA-34745704',
+ apiKey: API_KEY,
placement: 'sticky',
formats: ['sticky-banner'],
},
@@ -52,7 +58,7 @@ describe('Missena Adapter', function () {
sizes: [[1, 1]],
mediaTypes: { banner: { sizes: [[1, 1]] } },
params: {
- apiKey: 'PA-34745704',
+ apiKey: API_KEY,
placement: 'sticky',
formats: ['sticky-banner'],
},
@@ -69,6 +75,7 @@ describe('Missena Adapter', function () {
topmostLocation: REFERRER,
canonicalUrl: 'https://canonical',
},
+ ortb2: { regs: { coppa: 1 } },
};
const bids = [bid, bidWithoutFloor];
@@ -107,6 +114,15 @@ describe('Missena Adapter', function () {
const payload = JSON.parse(request.data);
const payloadNoFloor = JSON.parse(requests[1].data);
+ it('should send disabled autoplay', function () {
+ expect(payload.autoplay).to.equal(0);
+ });
+
+ it('should contain coppa', function () {
+ expect(payload.coppa).to.equal(1);
+ });
+ sandbox.restore();
+
it('should contain uspConsent', function () {
expect(payload.us_privacy).to.equal('IDO');
});
@@ -128,11 +144,11 @@ describe('Missena Adapter', function () {
});
it('should send placement', function () {
- expect(payload.placement).to.equal('sticky');
+ expect(payload.params.placement).to.equal('sticky');
});
it('should send formats', function () {
- expect(payload.formats).to.eql(['sticky-banner']);
+ expect(payload.params.formats).to.eql(['sticky-banner']);
});
it('should send referer information to the request', function () {
@@ -157,6 +173,11 @@ describe('Missena Adapter', function () {
expect(payload.ik).to.equal(window.msna_ik);
});
+ it('should send screen', function () {
+ expect(payload.screen.width).to.equal(screen.width);
+ expect(payload.screen.height).to.equal(screen.height);
+ });
+
getDataFromLocalStorageStub.restore();
getDataFromLocalStorageStub = sinon.stub(
storage,
@@ -284,7 +305,7 @@ describe('Missena Adapter', function () {
expect(userSync.length).to.be.equal(1);
expect(userSync[0].type).to.be.equal('iframe');
- expect(userSync[0].url).to.be.equal(syncFrameUrl);
+ expect(userSync[0].url).to.be.equal(`${syncFrameUrl}?t=${API_KEY}`);
});
it('should return empty array when iframeEnabled is false', function () {
@@ -297,7 +318,7 @@ describe('Missena Adapter', function () {
gdprApplies: true,
consentString,
});
- const expectedUrl = `${syncFrameUrl}?gdpr=1&gdpr_consent=${consentString}`;
+ const expectedUrl = `${syncFrameUrl}?t=${API_KEY}&gdpr=1&gdpr_consent=${consentString}`;
expect(userSync.length).to.be.equal(1);
expect(userSync[0].type).to.be.equal('iframe');
expect(userSync[0].url).to.be.equal(expectedUrl);
@@ -307,7 +328,7 @@ describe('Missena Adapter', function () {
gdprApplies: false,
consentString,
});
- const expectedUrl = `${syncFrameUrl}?gdpr=0&gdpr_consent=${consentString}`;
+ const expectedUrl = `${syncFrameUrl}?t=${API_KEY}&gdpr=0&gdpr_consent=${consentString}`;
expect(userSync.length).to.be.equal(1);
expect(userSync[0].type).to.be.equal('iframe');
expect(userSync[0].url).to.be.equal(expectedUrl);
diff --git a/test/spec/modules/mobianRtdProvider_spec.js b/test/spec/modules/mobianRtdProvider_spec.js
index 088e5559a17..796a79e4e1c 100644
--- a/test/spec/modules/mobianRtdProvider_spec.js
+++ b/test/spec/modules/mobianRtdProvider_spec.js
@@ -1,11 +1,49 @@
import { expect } from 'chai';
import sinon from 'sinon';
-import { mobianBrandSafetySubmodule, MOBIAN_URL } from 'modules/mobianRtdProvider.js';
import * as ajax from 'src/ajax.js';
+import * as gptUtils from 'libraries/gptUtils/gptUtils.js';
+import {
+ CONTEXT_KEYS,
+ extendBidRequestConfig,
+ fetchContextData,
+ getConfig,
+ getContextData,
+ makeDataFromResponse,
+ setTargeting,
+} from 'modules/mobianRtdProvider.js';
describe('Mobian RTD Submodule', function () {
let ajaxStub;
let bidReqConfig;
+ let setKeyValueSpy;
+
+ const mockResponse = JSON.stringify({
+ meta: {
+ url: 'https://example.com',
+ has_results: true
+ },
+ results: {
+ ap: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] },
+ mobianContentCategories: [],
+ mobianEmotions: ['affection'],
+ mobianGenres: [],
+ mobianRisk: 'low',
+ mobianSentiment: 'positive',
+ mobianThemes: [],
+ mobianTones: [],
+ }
+ });
+
+ const mockContextData = {
+ apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] },
+ categories: [],
+ emotions: ['affection'],
+ genres: [],
+ risk: 'low',
+ sentiment: 'positive',
+ themes: [],
+ tones: [],
+ }
beforeEach(function () {
bidReqConfig = {
@@ -19,255 +57,188 @@ describe('Mobian RTD Submodule', function () {
}
}
};
+
+ setKeyValueSpy = sinon.spy(gptUtils, 'setKeyValue');
});
afterEach(function () {
ajaxStub.restore();
+ setKeyValueSpy.restore();
});
- it('should set key-value pairs when server responds with valid data', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success(JSON.stringify({
- meta: {
- url: 'https://example.com',
- has_results: true
- },
- results: {
- mobianRisk: 'low',
- mobianSentiment: 'positive',
- mobianContentCategories: [],
- mobianEmotions: ['joy'],
- mobianThemes: [],
- mobianTones: [],
- mobianGenres: [],
- ap: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }
- }
- }));
- });
-
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({
- risk: 'low',
- contentCategories: [],
- sentiment: 'positive',
- emotions: ['joy'],
- themes: [],
- tones: [],
- genres: [],
- apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }
- });
- expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({
- mobianRisk: 'low',
- mobianContentCategories: [],
- mobianSentiment: 'positive',
- mobianEmotions: ['joy'],
- mobianThemes: [],
- mobianTones: [],
- mobianGenres: [],
- apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }
+ describe('fetchContextData', function () {
+ it('should return fetched context data', async function () {
+ ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
+ callbacks.success(mockResponse);
});
- });
- });
- it('should handle response with content categories, multiple emotions, and ap values', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success(JSON.stringify({
- meta: {
- url: 'https://example.com',
- has_results: true
- },
- results: {
- mobianRisk: 'medium',
- mobianSentiment: 'negative',
- mobianContentCategories: ['arms', 'crime'],
- mobianEmotions: ['anger', 'fear'],
- mobianThemes: ['conflict', 'international relations'],
- mobianTones: ['factual', 'serious'],
- mobianGenres: ['news', 'political_analysis'],
- ap: { a0: [100], a1: [200, 300], p0: [400, 500], p1: [600] }
- }
- }));
+ const contextData = await fetchContextData();
+ expect(contextData).to.deep.equal(mockResponse);
});
+ });
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({
- risk: 'medium',
- contentCategories: ['arms', 'crime'],
- sentiment: 'negative',
- emotions: ['anger', 'fear'],
- themes: ['conflict', 'international relations'],
- tones: ['factual', 'serious'],
- genres: ['news', 'political_analysis'],
- apValues: { a0: [100], a1: [200, 300], p0: [400, 500], p1: [600] }
- });
- expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({
- mobianRisk: 'medium',
- mobianContentCategories: ['arms', 'crime'],
- mobianSentiment: 'negative',
- mobianEmotions: ['anger', 'fear'],
- mobianThemes: ['conflict', 'international relations'],
- mobianTones: ['factual', 'serious'],
- mobianGenres: ['news', 'political_analysis'],
- apValues: { a0: [100], a1: [200, 300], p0: [400, 500], p1: [600] }
+ describe('makeDataFromResponse', function () {
+ it('should format context data response', async function () {
+ ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
+ callbacks.success(mockResponse);
});
+
+ const data = makeDataFromResponse(mockResponse);
+ expect(data).to.deep.equal(mockContextData);
});
});
- it('should return empty object when server responds with has_results: false', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success(JSON.stringify({
- meta: {
- url: 'https://example.com',
- has_results: false
- },
- results: {}
- }));
- });
+ describe('getContextData', function () {
+ it('should return formatted context data', async function () {
+ ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
+ callbacks.success(mockResponse);
+ });
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({});
- expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.not.have.any.keys(
- 'mobianRisk', 'mobianContentCategories', 'mobianSentiment', 'mobianEmotions', 'mobianThemes', 'mobianTones', 'mobianGenres', 'apValues'
- );
+ const data = await getContextData();
+ expect(data).to.deep.equal(mockContextData);
});
});
- it('should return empty object when server response is not valid JSON', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success('unexpected output not even of the right type');
- });
- const originalConfig = JSON.parse(JSON.stringify(bidReqConfig));
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({});
- // Check that bidReqConfig hasn't been modified
- expect(bidReqConfig).to.deep.equal(originalConfig);
+ describe('setTargeting', function () {
+ it('should set targeting key-value pairs as per config', function () {
+ const parsedConfig = {
+ prefix: 'mobian',
+ publisherTargeting: ['apValues', 'emotions', 'risk', 'sentiment', 'themes', 'tones', 'genres'],
+ };
+ setTargeting(parsedConfig, mockContextData);
+
+ expect(setKeyValueSpy.callCount).to.equal(6);
+ expect(setKeyValueSpy.calledWith('mobian_ap_a1', [2313, 12])).to.equal(true);
+ expect(setKeyValueSpy.calledWith('mobian_ap_p0', [1231231, 212])).to.equal(true);
+ expect(setKeyValueSpy.calledWith('mobian_ap_p1', [231, 419])).to.equal(true);
+ expect(setKeyValueSpy.calledWith('mobian_emotions', ['affection'])).to.equal(true);
+ expect(setKeyValueSpy.calledWith('mobian_risk', 'low')).to.equal(true);
+ expect(setKeyValueSpy.calledWith('mobian_sentiment', 'positive')).to.equal(true);
+
+ expect(setKeyValueSpy.calledWith('mobian_ap_a0')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_themes')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_tones')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_genres')).to.equal(false);
+ });
+
+ it('should not set key-value pairs if context data is empty', function () {
+ const parsedConfig = {
+ prefix: 'mobian',
+ publisherTargeting: ['apValues', 'emotions', 'risk', 'sentiment', 'themes', 'tones', 'genres'],
+ };
+ setTargeting(parsedConfig, {});
+
+ expect(setKeyValueSpy.callCount).to.equal(0);
+ });
+
+ it('should only set key-value pairs for the keys specified in config', function () {
+ const parsedConfig = {
+ prefix: 'mobian',
+ publisherTargeting: ['emotions', 'risk'],
+ };
+
+ setTargeting(parsedConfig, mockContextData);
+
+ expect(setKeyValueSpy.callCount).to.equal(2);
+ expect(setKeyValueSpy.calledWith('mobian_emotions', ['affection'])).to.equal(true);
+ expect(setKeyValueSpy.calledWith('mobian_risk', 'low')).to.equal(true);
+
+ expect(setKeyValueSpy.calledWith('mobian_ap_a0')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_ap_a1')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_ap_p0')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_ap_p1')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_themes')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_tones')).to.equal(false);
+ expect(setKeyValueSpy.calledWith('mobian_genres')).to.equal(false);
});
});
- it('should handle error response', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.error();
+ describe('extendBidRequestConfig', function () {
+ it('should extend bid request config with context data', function () {
+ const extendedConfig = extendBidRequestConfig(bidReqConfig, mockContextData);
+ expect(extendedConfig.ortb2Fragments.global.site.ext.data).to.deep.equal(mockContextData);
});
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({});
- });
- });
+ it('should not override existing data', function () {
+ bidReqConfig.ortb2Fragments.global.site.ext.data = {
+ existing: 'data'
+ };
- it('should use default values when fields are missing in the response', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success(JSON.stringify({
- meta: {
- url: 'https://example.com',
- has_results: true
- },
- results: {
- mobianRisk: 'high'
- // Missing other fields
- }
- }));
+ const extendedConfig = extendBidRequestConfig(bidReqConfig, mockContextData);
+ expect(extendedConfig.ortb2Fragments.global.site.ext.data).to.deep.equal({
+ existing: 'data',
+ ...mockContextData
+ });
});
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({
- risk: 'high',
- contentCategories: [],
- sentiment: 'unknown',
- emotions: [],
- themes: [],
- tones: [],
- genres: [],
- apValues: {}
- });
- expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({
- mobianRisk: 'high',
- mobianContentCategories: [],
- mobianSentiment: 'unknown',
- mobianEmotions: [],
- mobianThemes: [],
- mobianTones: [],
- mobianGenres: [],
- apValues: {}
- });
+ it('should create data object if missing', function () {
+ delete bidReqConfig.ortb2Fragments.global.site.ext.data;
+ const extendedConfig = extendBidRequestConfig(bidReqConfig, mockContextData);
+ expect(extendedConfig.ortb2Fragments.global.site.ext.data).to.deep.equal(mockContextData);
});
});
- it('should handle response with only ap values', function () {
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success(JSON.stringify({
- meta: {
- url: 'https://example.com',
- has_results: true
- },
- results: {
- ap: { a0: [1, 2], a1: [3, 4], p0: [5, 6], p1: [7, 8] }
+ describe('getConfig', function () {
+ it('should return config with correct keys', function () {
+ const config = getConfig({
+ name: 'mobianBrandSafety',
+ params: {
+ prefix: 'mobiantest',
+ publisherTargeting: ['apValues'],
+ advertiserTargeting: ['emotions'],
}
- }));
+ });
+ expect(config).to.deep.equal({
+ prefix: 'mobiantest',
+ publisherTargeting: ['apValues'],
+ advertiserTargeting: ['emotions'],
+ });
});
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, {}, {}).then((result) => {
- expect(result).to.deep.equal({
- risk: 'unknown',
- contentCategories: [],
- sentiment: 'unknown',
- emotions: [],
- themes: [],
- tones: [],
- genres: [],
- apValues: { a0: [1, 2], a1: [3, 4], p0: [5, 6], p1: [7, 8] }
+ it('should set default values for configs not set', function () {
+ const config = getConfig({
+ name: 'mobianBrandSafety',
+ params: {
+ publisherTargeting: ['apValues'],
+ }
});
- expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({
- mobianRisk: 'unknown',
- mobianContentCategories: [],
- mobianSentiment: 'unknown',
- mobianEmotions: [],
- mobianThemes: [],
- mobianTones: [],
- mobianGenres: [],
- apValues: { a0: [1, 2], a1: [3, 4], p0: [5, 6], p1: [7, 8] }
+ expect(config).to.deep.equal({
+ prefix: 'mobian',
+ publisherTargeting: ['apValues'],
+ advertiserTargeting: [],
});
});
- });
- it('should set key-value pairs when ext object is missing', function () {
- bidReqConfig = {
- ortb2Fragments: {
- global: {
- site: {}
- }
- }
- };
+ it('should set default values if not provided', function () {
+ const config = getConfig({});
+ expect(config).to.deep.equal({
+ prefix: 'mobian',
+ publisherTargeting: [],
+ advertiserTargeting: [],
+ });
+ });
- ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function(url, callbacks) {
- callbacks.success(JSON.stringify({
- meta: {
- url: 'https://example.com',
- has_results: true
- },
- results: {
- mobianRisk: 'low',
- mobianSentiment: 'positive',
- mobianContentCategories: [],
- mobianEmotions: ['joy'],
- mobianThemes: [],
- mobianTones: [],
- mobianGenres: [],
- ap: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }
- }
- }));
+ it('should set default values if no config is provided', function () {
+ const config = getConfig();
+ expect(config).to.deep.equal({
+ prefix: 'mobian',
+ publisherTargeting: [],
+ advertiserTargeting: [],
+ });
});
- return mobianBrandSafetySubmodule.getBidRequestData(bidReqConfig, () => {}, {}).then(() => {
- expect(bidReqConfig.ortb2Fragments.global.site.ext).to.exist;
- expect(bidReqConfig.ortb2Fragments.global.site.ext.data).to.deep.include({
- mobianRisk: 'low',
- mobianContentCategories: [],
- mobianSentiment: 'positive',
- mobianEmotions: ['joy'],
- mobianThemes: [],
- mobianTones: [],
- mobianGenres: [],
- apValues: { a0: [], a1: [2313, 12], p0: [1231231, 212], p1: [231, 419] }
+ it('should set all tarteging values if value is true', function () {
+ const config = getConfig({
+ name: 'mobianBrandSafety',
+ params: {
+ publisherTargeting: true,
+ advertiserTargeting: true,
+ }
+ });
+ expect(config).to.deep.equal({
+ prefix: 'mobian',
+ publisherTargeting: CONTEXT_KEYS,
+ advertiserTargeting: CONTEXT_KEYS,
});
});
});
diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js
index 75fb357b196..349051cb48e 100644
--- a/test/spec/modules/nativoBidAdapter_spec.js
+++ b/test/spec/modules/nativoBidAdapter_spec.js
@@ -221,6 +221,7 @@ describe('interpretResponse', function () {
meta: {
advertiserDomains: ['test.com'],
},
+ mediaType: 'banner',
},
]
@@ -681,16 +682,24 @@ describe('hasProtocol', () => {
describe('addProtocol', () => {
it('www.testpage.com', () => {
- expect(addProtocol('www.testpage.com')).to.be.equal('https://www.testpage.com')
+ expect(addProtocol('www.testpage.com')).to.be.equal(
+ 'https://www.testpage.com'
+ )
})
it('//www.testpage.com', () => {
- expect(addProtocol('//www.testpage.com')).to.be.equal('https://www.testpage.com')
+ expect(addProtocol('//www.testpage.com')).to.be.equal(
+ 'https://www.testpage.com'
+ )
})
it('http://www.testpage.com', () => {
- expect(addProtocol('http://www.testpage.com')).to.be.equal('http://www.testpage.com')
+ expect(addProtocol('http://www.testpage.com')).to.be.equal(
+ 'http://www.testpage.com'
+ )
})
it('https://www.testpage.com', () => {
- expect(addProtocol('https://www.testpage.com')).to.be.equal('https://www.testpage.com')
+ expect(addProtocol('https://www.testpage.com')).to.be.equal(
+ 'https://www.testpage.com'
+ )
})
})
@@ -786,7 +795,7 @@ describe('RequestData', () => {
describe('UserEIDs', () => {
const userEids = new UserEIDs()
- const eids = [{ 'testId': 1111 }]
+ const eids = [{ testId: 1111 }]
describe('processBidRequestData', () => {
it('Processes bid request without eids', () => {
@@ -810,7 +819,7 @@ describe('UserEIDs', () => {
expect(qs).to.include('ntv_pb_eid=')
try {
expect(JSON.parse(value)).to.be.equal(eids)
- } catch (err) { }
+ } catch (err) {}
})
})
})
@@ -828,12 +837,83 @@ describe('buildRequestUrl', () => {
})
it('Returns baseUrl + QS params if QS strings passed', () => {
- const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', 'ntv_foo=bar'])
- expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`)
+ const url = buildRequestUrl(baseUrl, [
+ 'ntv_ptd=123456&ntv_test=true',
+ 'ntv_foo=bar',
+ ])
+ expect(url).to.be.equal(
+ `${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`
+ )
})
it('Returns baseUrl + QS params if mixed QS strings passed', () => {
- const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', '', '', 'ntv_foo=bar'])
- expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`)
+ const url = buildRequestUrl(baseUrl, [
+ 'ntv_ptd=123456&ntv_test=true',
+ '',
+ '',
+ 'ntv_foo=bar',
+ ])
+ expect(url).to.be.equal(
+ `${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`
+ )
+ })
+})
+
+describe('Prebid Video', function () {
+ it('should handle video bid requests', function () {
+ const videoBidRequest = {
+ bidder: 'nativo',
+ params: {
+ video: {
+ mimes: ['video/mp4'],
+ protocols: [2, 3, 5, 6],
+ playbackmethod: [1, 2],
+ skip: 1,
+ skipafter: 5,
+ },
+ },
+ }
+
+ const isValid = spec.isBidRequestValid(videoBidRequest)
+ expect(isValid).to.be.true
+ })
+})
+
+describe('Prebid Native', function () {
+ it('should handle native bid requests', function () {
+ const nativeBidRequest = {
+ bidder: 'nativo',
+ params: {
+ native: {
+ title: {
+ required: true,
+ len: 80,
+ },
+ image: {
+ required: true,
+ sizes: [150, 50],
+ },
+ sponsoredBy: {
+ required: true,
+ },
+ clickUrl: {
+ required: true,
+ },
+ privacyLink: {
+ required: false,
+ },
+ body: {
+ required: true,
+ },
+ icon: {
+ required: true,
+ sizes: [50, 50],
+ },
+ },
+ },
+ }
+
+ const isValid = spec.isBidRequestValid(nativeBidRequest)
+ expect(isValid).to.be.true
})
})
diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js
index 0ba2635c87a..a5c5f5f714d 100644
--- a/test/spec/modules/nextMillenniumBidAdapter_spec.js
+++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import {
getImp,
+ getSourceObj,
replaceUsersyncMacros,
setConsentStrings,
setOrtb2Parameters,
@@ -19,6 +20,7 @@ describe('nextMillenniumBidAdapterTests', () => {
bid: {
mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}},
adUnitCode: 'test-banner-1',
+ bidId: 'e36ea395f67f',
},
mediaTypes: {
@@ -31,7 +33,7 @@ describe('nextMillenniumBidAdapterTests', () => {
},
expected: {
- id: 'test-banner-1',
+ id: 'e36ea395f67f',
bidfloorcur: 'EUR',
bidfloor: 1.11,
ext: {prebid: {storedrequest: {id: '123'}}},
@@ -47,6 +49,7 @@ describe('nextMillenniumBidAdapterTests', () => {
bid: {
mediaTypes: {video: {playerSize: [400, 300], api: [2], placement: 1, plcmt: 1}},
adUnitCode: 'test-video-1',
+ bidId: 'e36ea395f67f',
},
mediaTypes: {
@@ -58,7 +61,7 @@ describe('nextMillenniumBidAdapterTests', () => {
},
expected: {
- id: 'test-video-1',
+ id: 'e36ea395f67f',
bidfloorcur: 'USD',
ext: {prebid: {storedrequest: {id: '234'}}},
video: {
@@ -79,7 +82,7 @@ describe('nextMillenniumBidAdapterTests', () => {
postBody: {ext: {nextMillennium: {refresh_counts: {}, elemOffsets: {}}}},
bid: {
mediaTypes: {video: {w: 640, h: 480}},
- adUnitCode: 'test-video-2',
+ bidId: 'e36ea395f67f',
},
mediaTypes: {
@@ -91,7 +94,7 @@ describe('nextMillenniumBidAdapterTests', () => {
},
expected: {
- id: 'test-video-2',
+ id: 'e36ea395f67f',
bidfloorcur: 'USD',
ext: {prebid: {storedrequest: {id: '234'}}},
video: {w: 640, h: 480, mimes: ['video/mp4', 'video/x-ms-wmv', 'application/javascript']},
@@ -108,6 +111,112 @@ describe('nextMillenniumBidAdapterTests', () => {
}
});
+ describe('function getSourceObj', () => {
+ const dataTests = [
+ {
+ title: 'schain is empty',
+ validBidRequests: [{}],
+ bidderRequest: {},
+ expected: undefined,
+ },
+
+ {
+ title: 'schain is validBidReequest',
+ bidderRequest: {},
+ validBidRequests: [{
+ schain: {
+ validation: 'strict',
+ config: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [{asi: 'test.test', sid: '00001', hp: 1}],
+ },
+ },
+ }],
+
+ expected: {
+ schain: {
+ validation: 'strict',
+ config: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [{asi: 'test.test', sid: '00001', hp: 1}],
+ },
+ },
+ },
+ },
+
+ {
+ title: 'schain is bidderReequest.ortb2.source.schain',
+ bidderRequest: {
+ ortb2: {
+ source: {
+ schain: {
+ validation: 'strict',
+ config: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [{asi: 'test.test', sid: '00001', hp: 1}],
+ },
+ },
+ },
+ },
+ },
+
+ validBidRequests: [{}],
+ expected: {
+ schain: {
+ validation: 'strict',
+ config: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [{asi: 'test.test', sid: '00001', hp: 1}],
+ },
+ },
+ },
+ },
+
+ {
+ title: 'schain is bidderReequest.ortb2.source.ext.schain',
+ bidderRequest: {
+ ortb2: {
+ source: {
+ ext: {
+ schain: {
+ validation: 'strict',
+ config: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [{asi: 'test.test', sid: '00001', hp: 1}],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ validBidRequests: [{}],
+ expected: {
+ schain: {
+ validation: 'strict',
+ config: {
+ ver: '1.0',
+ complete: 1,
+ nodes: [{asi: 'test.test', sid: '00001', hp: 1}],
+ },
+ },
+ },
+ },
+ ];
+
+ for (let {title, validBidRequests, bidderRequest, expected} of dataTests) {
+ it(title, () => {
+ const source = getSourceObj(validBidRequests, bidderRequest);
+ expect(source).to.deep.equal(expected);
+ });
+ }
+ });
+
describe('function setConsentStrings', () => {
const dataTests = [
{
@@ -682,17 +791,17 @@ describe('nextMillenniumBidAdapterTests', () => {
describe('Check ext.next_mil_imps', function() {
const expectedNextMilImps = [
{
- impId: 'nmi-test-0',
+ impId: 'bid1234',
nextMillennium: {refresh_count: 1},
},
{
- impId: 'nmi-test-1',
+ impId: 'bid1235',
nextMillennium: {refresh_count: 1},
},
{
- impId: 'nmi-test-2',
+ impId: 'bid1236',
nextMillennium: {refresh_count: 1},
},
];
@@ -948,7 +1057,6 @@ describe('nextMillenniumBidAdapterTests', () => {
expected: {
id: 'mock-uuid',
- bidIds: {'test-div': 'bid1234', 'test-div-2': 'bid1235'},
impSize: 2,
requestSize: 1,
domain: 'example.com',
@@ -961,7 +1069,6 @@ describe('nextMillenniumBidAdapterTests', () => {
it(title, () => {
const request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.length).to.equal(expected.requestSize);
- expect(request[0].bidIds).to.deep.equal(expected.bidIds);
const requestData = JSON.parse(request[0].data);
expect(requestData.id).to.equal(expected.id);
@@ -983,7 +1090,7 @@ describe('nextMillenniumBidAdapterTests', () => {
bid: [
{
id: '7457329903666272789-0',
- impid: 'ad-unit-0',
+ impid: '700ce0a43f72',
price: 0.5,
adm: 'Hello! It\'s a test ad!',
adid: '96846035-0',
@@ -994,7 +1101,7 @@ describe('nextMillenniumBidAdapterTests', () => {
{
id: '7457329903666272789-1',
- impid: 'ad-unit-1',
+ impid: '700ce0a43f73',
price: 0.7,
adm: 'https://some_vast_host.com/vast.xml',
adid: '96846035-1',
@@ -1006,7 +1113,7 @@ describe('nextMillenniumBidAdapterTests', () => {
{
id: '7457329903666272789-2',
- impid: 'ad-unit-3',
+ impid: '700ce0a43f74',
price: 1.0,
adm: '',
adid: '96846035-3',
@@ -1022,19 +1129,10 @@ describe('nextMillenniumBidAdapterTests', () => {
},
},
- bidRequest: {
- bidIds: {
- 'ad-unit-0': 'bid-id-0',
- 'ad-unit-1': 'bid-id-1',
- 'ad-unit-2': 'bid-id-2',
- 'ad-unit-3': 'bid-id-3',
- },
- },
-
expected: [
{
title: 'banner',
- requestId: 'bid-id-0',
+ requestId: '700ce0a43f72',
creativeId: '96846035-0',
ad: 'Hello! It\'s a test ad!',
vastUrl: undefined,
@@ -1047,7 +1145,7 @@ describe('nextMillenniumBidAdapterTests', () => {
{
title: 'video - vastUrl',
- requestId: 'bid-id-1',
+ requestId: '700ce0a43f73',
creativeId: '96846035-1',
ad: undefined,
vastUrl: 'https://some_vast_host.com/vast.xml',
@@ -1060,7 +1158,7 @@ describe('nextMillenniumBidAdapterTests', () => {
{
title: 'video - vastXml',
- requestId: 'bid-id-3',
+ requestId: '700ce0a43f74',
creativeId: '96846035-3',
ad: undefined,
vastUrl: undefined,
diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js
index faaab727b17..5dc60b25ab0 100644
--- a/test/spec/modules/openxBidAdapter_spec.js
+++ b/test/spec/modules/openxBidAdapter_spec.js
@@ -1489,7 +1489,8 @@ describe('OpenxRtbAdapter', function () {
},
ext: {
divid: 'adunit-code',
- }
+ },
+ secure: 1
}
}
});
diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js
index ceb3446b570..41f1609f74f 100644
--- a/test/spec/modules/priceFloors_spec.js
+++ b/test/spec/modules/priceFloors_spec.js
@@ -2401,3 +2401,87 @@ describe('the price floors module', function () {
})
});
});
+
+describe('setting null as rule value', () => {
+ const nullFloorData = {
+ modelVersion: 'basic model',
+ modelWeight: 10,
+ modelTimestamp: 1606772895,
+ currency: 'USD',
+ schema: {
+ delimiter: '|',
+ fields: ['mediaType', 'size']
+ },
+ values: {
+ 'banner|600x300': null,
+ }
+ };
+
+ const basicBidRequest = {
+ bidder: 'rubicon',
+ adUnitCode: 'test_div_1',
+ auctionId: '1234-56-789',
+ transactionId: 'tr_test_div_1',
+ adUnitId: 'tr_test_div_1',
+ };
+
+ it('should validate for null values', function () {
+ let data = utils.deepClone(nullFloorData);
+ data.floorsSchemaVersion = 1;
+ expect(isFloorsDataValid(data)).to.to.equal(true);
+ });
+
+ it('getFloor should not return numeric value if null set as value', function () {
+ const bidRequest = { ...basicBidRequest, getFloor };
+ const basicFloorConfig = {
+ enabled: true,
+ auctionDelay: 0,
+ endpoint: {},
+ enforcement: {
+ enforceJS: true,
+ enforcePBS: false,
+ floorDeals: false,
+ bidAdjustment: true
+ },
+ data: nullFloorData
+ }
+ _floorDataForAuction[bidRequest.auctionId] = basicFloorConfig;
+
+ let inputParams = {mediaType: 'banner', size: [600, 300]};
+ expect(bidRequest.getFloor(inputParams)).to.deep.equal(null);
+ })
+
+ it('getFloor should not return numeric value if null set as value - external floor provider', function () {
+ const basicFloorConfig = {
+ enabled: true,
+ auctionDelay: 0,
+ endpoint: {},
+ enforcement: {
+ enforceJS: true,
+ enforcePBS: false,
+ floorDeals: false,
+ bidAdjustment: true
+ },
+ data: nullFloorData
+ }
+ server.respondWith(JSON.stringify(nullFloorData));
+ let exposedAdUnits;
+
+ handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider', endpoint: {url: 'http://www.fakefloorprovider.json/'}});
+
+ const adUnits = [{
+ cod: 'test_div_1',
+ mediaTypes: {banner: { sizes: [[600, 300]] }, native: {}},
+ bids: [{bidder: 'someBidder', adUnitCode: 'test_div_1'}, {bidder: 'someOtherBidder', adUnitCode: 'test_div_1'}]
+ }];
+
+ requestBidsHook(config => exposedAdUnits = config.adUnits, {
+ auctionId: basicBidRequest.auctionId,
+ adUnits
+ });
+
+ let inputParams = {mediaType: 'banner', size: [600, 300]};
+
+ expect(exposedAdUnits[0].bids[0].getFloor(inputParams)).to.deep.equal(null);
+ });
+})
diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js
index 71b22e25272..834db255e01 100644
--- a/test/spec/modules/pubmaticBidAdapter_spec.js
+++ b/test/spec/modules/pubmaticBidAdapter_spec.js
@@ -1,5 +1,5 @@
import { expect } from 'chai';
-import { spec, checkVideoPlacement, _getDomainFromURL, assignDealTier, prepareMetaObject, getDeviceConnectionType } from 'modules/pubmaticBidAdapter.js';
+import { spec, checkVideoPlacement, _getDomainFromURL, assignDealTier, prepareMetaObject, getDeviceConnectionType, setIBVField, setTTL } from 'modules/pubmaticBidAdapter.js';
import * as utils from 'src/utils.js';
import { config } from 'src/config.js';
import { createEidsArray } from 'modules/userId/eids.js';
@@ -3463,7 +3463,7 @@ describe('PubMatic adapter', function () {
expect(response[0].dealId).to.equal(bidResponses.body.seatbid[0].bid[0].dealid);
expect(response[0].currency).to.equal('USD');
expect(response[0].netRevenue).to.equal(true);
- expect(response[0].ttl).to.equal(300);
+ expect(response[0].ttl).to.equal(360);
expect(response[0].meta.networkId).to.equal(123);
expect(response[0].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-987');
expect(response[0].meta.buyerId).to.equal('seat-id');
@@ -3488,7 +3488,7 @@ describe('PubMatic adapter', function () {
expect(response[1].dealId).to.equal(bidResponses.body.seatbid[1].bid[0].dealid);
expect(response[1].currency).to.equal('USD');
expect(response[1].netRevenue).to.equal(true);
- expect(response[1].ttl).to.equal(300);
+ expect(response[1].ttl).to.equal(360);
expect(response[1].meta.networkId).to.equal(422);
expect(response[1].adserverTargeting.hb_buyid_pubmatic).to.equal('BUYER-ID-789');
expect(response[1].meta.buyerId).to.equal(832);
@@ -3579,6 +3579,33 @@ describe('PubMatic adapter', function () {
expect(response[0].renderer).to.not.exist;
});
+ it('should set ibv field in bid.ext when bid.ext.ibv exists', function() {
+ let request = spec.buildRequests(bidRequests, {
+ auctionId: 'new-auction-id'
+ });
+
+ let copyOfBidResponse = utils.deepClone(bannerBidResponse);
+ let bidExt = utils.deepClone(copyOfBidResponse.body.seatbid[0].bid[0].ext);
+ copyOfBidResponse.body.seatbid[0].bid[0].ext = Object.assign(bidExt, {
+ ibv: true
+ });
+
+ let response = spec.interpretResponse(copyOfBidResponse, request);
+ expect(response[0].ext.ibv).to.equal(true);
+ expect(response[0].meta.mediaType).to.equal('video');
+ });
+
+ it('should not set ibv field when bid.ext does not exist ', function() {
+ let request = spec.buildRequests(bidRequests, {
+ auctionId: 'new-auction-id'
+ });
+
+ let response = spec.interpretResponse(bannerBidResponse, request);
+ expect(response[0].ext).to.not.exist;
+ expect(response[0].meta).to.exist;
+ expect(response[0].meta.mediaType).to.equal('banner');
+ });
+
if (FEATURES.VIDEO) {
it('should check for valid video mediaType in case of multiformat request', function() {
let request = spec.buildRequests(videoBidRequests, {
@@ -3878,10 +3905,12 @@ describe('PubMatic adapter', function () {
// dchain: 'dc',
// demandSource: 'ds',
// secondaryCatIds: ['secondaryCatIds']
- }
+ },
};
- const br = {};
+ const br = {
+ mediaType: 'video'
+ };
prepareMetaObject(br, bid, null);
expect(br.meta.networkId).to.equal(6); // dspid
expect(br.meta.buyerId).to.equal('12'); // adid
@@ -3900,6 +3929,7 @@ describe('PubMatic adapter', function () {
expect(br.meta.advertiserDomains).to.be.an('array').with.length.above(0); // adomain
expect(br.meta.clickUrl).to.equal('mystartab.com'); // adomain
expect(br.meta.dsa).to.equal(dsa); // dsa
+ expect(br.meta.mediaType).to.equal('video'); // mediaType
});
it('Should be empty, when ext and adomain is absent in bid object', function () {
@@ -4176,6 +4206,58 @@ describe('PubMatic adapter', function () {
expect(data.imp[0]['banner']['battr']).to.equal(undefined);
});
});
+
+ describe('setIBVField', function() {
+ it('should set ibv field in newBid.ext when bid.ext.ibv exists', function() {
+ const bid = {
+ ext: {
+ ibv: true
+ }
+ };
+ const newBid = {};
+ setIBVField(bid, newBid);
+ expect(newBid.ext).to.exist;
+ expect(newBid.ext.ibv).to.equal(true);
+ expect(newBid.meta).to.exist;
+ expect(newBid.meta.mediaType).to.equal('video');
+ });
+
+ it('should not set ibv field when bid.ext.ibv does not exist', function() {
+ const bid = {
+ ext: {}
+ };
+ const newBid = {};
+ setIBVField(bid, newBid);
+ expect(newBid.ext).to.not.exist;
+ expect(newBid.meta).to.not.exist;
+ });
+
+ it('should not set ibv field when bid.ext does not exist', function() {
+ const bid = {};
+ const newBid = {};
+ setIBVField(bid, newBid);
+ expect(newBid.ext).to.not.exist;
+ expect(newBid.meta).to.not.exist;
+ });
+
+ it('should preserve existing newBid.ext properties', function() {
+ const bid = {
+ ext: {
+ ibv: true
+ }
+ };
+ const newBid = {
+ ext: {
+ existingProp: 'should remain'
+ }
+ };
+ setIBVField(bid, newBid);
+ expect(newBid.ext.existingProp).to.equal('should remain');
+ expect(newBid.ext.ibv).to.equal(true);
+ expect(newBid.meta).to.exist;
+ expect(newBid.meta.mediaType).to.equal('video');
+ });
+ });
});
if (FEATURES.VIDEO) {
@@ -4301,4 +4383,49 @@ describe('PubMatic adapter', function () {
expect(response[0].bidderCode).to.equal('groupm');
});
});
+
+ describe('setTTL', function() {
+ it('should set ttl field in newBid.ttl when bid.exp exists', function() {
+ const bid = {
+ exp: 200
+ };
+ const newBid = {};
+ setTTL(bid, newBid);
+ expect(newBid.ttl).to.equal(200);
+ });
+
+ it('should set ttl as 360 mediatype banner', function() {
+ const bid = {};
+ const newBid = {
+ mediaType: 'banner'
+ };
+ setTTL(bid, newBid);
+ expect(newBid.ttl).to.equal(360);
+ });
+
+ it('should set ttl as 1800 mediatype video', function() {
+ const bid = {};
+ const newBid = {
+ mediaType: 'video'
+ };
+ setTTL(bid, newBid);
+ expect(newBid.ttl).to.equal(1800);
+ });
+
+ it('should set ttl as 1800 mediatype native', function() {
+ const bid = {};
+ const newBid = {
+ mediaType: 'native'
+ };
+ setTTL(bid, newBid);
+ expect(newBid.ttl).to.equal(1800);
+ });
+
+ it('should set ttl as 360 as default if all condition fails', function() {
+ const bid = {};
+ const newBid = {};
+ setTTL(bid, newBid);
+ expect(newBid.ttl).to.equal(360);
+ });
+ });
});
diff --git a/test/spec/modules/qortexRtdProvider_spec.js b/test/spec/modules/qortexRtdProvider_spec.js
index c1ae25e6104..b5006e45d56 100644
--- a/test/spec/modules/qortexRtdProvider_spec.js
+++ b/test/spec/modules/qortexRtdProvider_spec.js
@@ -4,18 +4,13 @@ import { EVENTS } from '../../../src/constants.js';
import {loadExternalScript} from 'src/adloader.js';
import {
qortexSubmodule as module,
- getContext,
- getGroupConfig,
- generateAnalyticsEventObject,
- generateAnalyticsHostUrl,
addContextToRequests,
setContextData,
loadScriptTag,
initializeModuleData,
setGroupConfigData,
- saveContextAdded,
- initializeBidEnrichment,
- getContextAddedEntry
+ requestContextData,
+ windowPostMessageReceived
} from '../../../modules/qortexRtdProvider';
import {server} from '../../mocks/xhr.js';
import { cloneDeep } from 'lodash';
@@ -27,12 +22,10 @@ describe('qortexRtdProvider', () => {
const defaultApiHost = 'https://demand.qortex.ai';
const defaultGroupId = 'test';
-
const validBidderArray = ['qortex', 'test'];
const validTagConfig = {
videoContainer: 'my-video-container'
}
-
const validModuleConfig = {
params: {
groupId: defaultGroupId,
@@ -58,7 +51,6 @@ describe('qortexRtdProvider', () => {
const emptyModuleConfig = {
params: {}
}
-
const validImpressionEvent = {
detail: {
uid: 'uid123',
@@ -76,17 +68,25 @@ describe('qortexRtdProvider', () => {
type: 'qx-impression'
}
}
+ const QortexPostMessageInitialized = {
+ target: 'QORTEX-PREBIDJS-RTD-MODULE',
+ message: 'CX-BID-ENRICH-INITIALIZED',
+ params: {groupConfig: {data: true}}
+ }
+ const QortexPostMessageContext = {
+ target: 'QORTEX-PREBIDJS-RTD-MODULE',
+ message: 'DISPATCH-CONTEXT',
+ params: {context: {data: true}}
+ }
const invalidTypeQortexEvent = {
detail: {
type: 'invalid-type'
}
}
-
const responseHeaders = {
'content-type': 'application/json',
'access-control-allow-origin': '*'
};
-
const contextResponseObj = {
content: {
id: '123456',
@@ -98,7 +98,6 @@ describe('qortexRtdProvider', () => {
}
}
const contextResponse = JSON.stringify(contextResponseObj);
-
const validGroupConfigResponseObj = {
groupId: defaultGroupId,
active: true,
@@ -107,7 +106,6 @@ describe('qortexRtdProvider', () => {
prebidReportingPercentage: 100
}
const validGroupConfigResponse = JSON.stringify(validGroupConfigResponseObj);
-
const inactiveGroupConfigResponseObj = {
groupId: defaultGroupId,
active: false,
@@ -115,7 +113,6 @@ describe('qortexRtdProvider', () => {
PrebidReportingPercentage: 100
}
const inactiveGroupConfigResponse = JSON.stringify(inactiveGroupConfigResponseObj);
-
const noEnrichmentGroupConfigResponseObj = {
groupId: defaultGroupId,
active: true,
@@ -123,7 +120,6 @@ describe('qortexRtdProvider', () => {
prebidBidEnrichmentPercentage: 0,
prebidReportingPercentage: 100
}
-
const reqBidsConfig = {
auctionId: '1234',
adUnits: [{
@@ -151,37 +147,6 @@ describe('qortexRtdProvider', () => {
})
describe('init', () => {
- it('returns true for valid config object', (done) => {
- const result = module.init(validModuleConfig);
- expect(server.requests.length).to.be.eql(1)
- const groupConfigReq = server.requests[0];
- groupConfigReq.respond(200, responseHeaders, validGroupConfigResponse);
- setTimeout(() => {
- expect(result).to.be.true;
- done()
- }, 500)
- })
-
- it('logs warning when group config does not pass setup conditions', (done) => {
- const result = module.init(validModuleConfig);
- expect(server.requests.length).to.be.eql(1)
- const groupConfigReq = server.requests[0];
- groupConfigReq.respond(200, responseHeaders, inactiveGroupConfigResponse);
- setTimeout(() => {
- expect(logWarnSpy.calledWith('Group config is not configured for qortex bid enrichment')).to.be.true;
- done()
- }, 500)
- })
-
- it('logs warning when group config request errors', (done) => {
- const result = module.init(validModuleConfig);
- server.requests[0].respond(404, responseHeaders, inactiveGroupConfigResponse);
- setTimeout(() => {
- expect(logWarnSpy.calledWith('No Group Config found')).to.be.true;
- done()
- }, 500)
- })
-
it('will not initialize bid enrichment if it is disabled', () => {
module.init(bidEnrichmentDisabledModuleConfig);
expect(logWarnSpy.calledWith('Bid Enrichment Function has been disabled in module configuration')).to.be.true;
@@ -289,63 +254,23 @@ describe('qortexRtdProvider', () => {
expect(logWarnSpy.calledOnce).to.be.true;
})
- it('will call callback if getContext does not throw', (done) => {
- const cb = function () {
- expect(logWarnSpy.calledOnce).to.be.false;
- done();
- }
- module.getBidRequestData(reqBidsConfig, cb);
- server.requests[0].respond(200, responseHeaders, contextResponse);
- })
-
- it('will log message call callback if context data has already been collected', (done) => {
- setContextData(contextResponseObj);
- module.getBidRequestData(reqBidsConfig, callbackSpy);
- setTimeout(() => {
- expect(server.requests.length).to.be.eql(0);
- expect(logMessageSpy.calledWith('Adding Content object from existing context data')).to.be.true;
- done();
- }, 250)
- })
-
- it('will catch and log error and fire callback', (done) => {
- module.getBidRequestData(reqBidsConfig, callbackSpy);
- server.requests[0].respond(404, responseHeaders, JSON.stringify({}));
- setTimeout(() => {
- expect(logWarnSpy.calledWith('Returned error status code: 404')).to.be.eql(true);
- expect(callbackSpy.calledOnce).to.be.true;
- done();
- }, 250)
- })
-
- it('will not request context if group config toggle is false', (done) => {
- setGroupConfigData(inactiveGroupConfigResponseObj);
+ it('will not request context if prebid disable toggle is true', (done) => {
+ initializeModuleData(bidEnrichmentDisabledModuleConfig);
const cb = function () {
expect(server.requests.length).to.be.eql(0);
expect(logWarnSpy.called).to.be.true;
- expect(logWarnSpy.calledWith('Bid enrichment disabled at group config')).to.be.true;
+ expect(logWarnSpy.calledWith('Bid enrichment disabled at prebid config')).to.be.true;
done();
}
module.getBidRequestData(reqBidsConfig, cb);
})
- it('Logs warning for network error', (done) => {
- saveContextAdded(reqBidsConfig);
- const testData = {auctionId: reqBidsConfig.auctionId, data: 'data'};
- module.onAuctionEndEvent(testData);
- server.requests[0].respond(500, responseHeaders, JSON.stringify({}));
- setTimeout(() => {
- expect(logWarnSpy.calledWith('Returned error status code: 500')).to.be.eql(true);
- done();
- }, 200)
- })
-
- it('will not request context if prebid disable toggle is true', (done) => {
- initializeModuleData(bidEnrichmentDisabledModuleConfig);
+ it('will request to add context when ad units present and enabled', (done) => {
const cb = function () {
+ setContextData(null);
expect(server.requests.length).to.be.eql(0);
expect(logWarnSpy.called).to.be.true;
- expect(logWarnSpy.calledWith('Bid enrichment disabled at prebid config')).to.be.true;
+ expect(logWarnSpy.calledWith('No context data received at this time')).to.be.true;
done();
}
module.getBidRequestData(reqBidsConfig, cb);
@@ -363,69 +288,24 @@ describe('qortexRtdProvider', () => {
setGroupConfigData(null);
})
- it('Properly sends analytics event with valid config', (done) => {
- saveContextAdded(reqBidsConfig);
+ it('Properly sends analytics event with valid config', () => {
const testData = {auctionId: reqBidsConfig.auctionId, data: 'data'};
module.onAuctionEndEvent(testData);
- const request = server.requests[0];
- expect(request.url).to.be.eql('https://events.qortex.ai/api/v1/player-event');
- server.requests[0].respond(200, responseHeaders, JSON.stringify({}));
- setTimeout(() => {
- expect(logMessageSpy.calledWith('Qortex analytics event sent')).to.be.true
- done();
- }, 200)
- })
-
- it('Logs warning for rejected analytics request', (done) => {
- const invalidPercentageConfig = cloneDeep(validGroupConfigResponseObj);
- invalidPercentageConfig.prebidReportingPercentage = -1;
- setGroupConfigData(invalidPercentageConfig);
- const testData = {data: 'data'};
- module.onAuctionEndEvent(testData);
- expect(server.requests.length).to.be.eql(0);
- setTimeout(() => {
- expect(logWarnSpy.calledWith('Current request did not meet analytics percentage threshold, cancelling sending event')).to.be.true
- done();
- }, 200)
})
})
- describe('getContext', () => {
- beforeEach(() => {
- initializeModuleData(validModuleConfig);
- })
-
- afterEach(() => {
- initializeModuleData(emptyModuleConfig);
- })
-
- it('returns a promise', () => {
- const result = getContext();
- expect(result).to.be.a('promise');
+ describe('requestContextData', () => {
+ before(() => {
+ setContextData({data: true});
})
- it('uses request url generated from initialize function in config and resolves to content object data', (done) => {
- let requestUrl = `${validModuleConfig.params.apiUrl}/api/v1/prebid/${validModuleConfig.params.groupId}/page/lookup`;
- const ctx = getContext()
- const request = server.requests[0]
- request.respond(200, responseHeaders, contextResponse);
- ctx.then(response => {
- expect(server.requests.length).to.be.eql(1);
- expect(request.url).to.be.eql(requestUrl);
- expect(response).to.be.eql(contextResponseObj.content);
- done();
- });
+ after(() => {
+ setContextData(null);
})
- it('returns null when necessary', (done) => {
- const ctx = getContext()
- server.requests[0].respond(202, responseHeaders, JSON.stringify({}))
- ctx.then(response => {
- expect(response).to.be.null;
- expect(server.requests.length).to.be.eql(1);
- expect(logWarnSpy.called).to.be.false;
- done();
- });
+ it('Will log properly when context data already available', () => {
+ requestContextData();
+ expect(logMessageSpy.calledWith('Context data already retrieved.')).to.be.true;
})
})
@@ -460,16 +340,6 @@ describe('qortexRtdProvider', () => {
expect(reqBidsConfig.ortb2Fragments.bidder).to.be.eql({});
})
- it('saves context added entry with skipped flag if valid request does not meet threshold', () => {
- initializeModuleData(validModuleConfig);
- setContextData(contextResponseObj.content);
- setGroupConfigData(noEnrichmentGroupConfigResponseObj);
- addContextToRequests(reqBidsConfig);
- const contextAdded = getContextAddedEntry(reqBidsConfig.auctionId);
- expect(contextAdded).to.not.be.null;
- expect(contextAdded.contextSkipped).to.eql(true);
- })
-
it('adds site.content only to global ortb2 when bidders array is omitted', () => {
const omittedBidderArrayConfig = cloneDeep(validModuleConfig);
delete omittedBidderArrayConfig.params.bidders;
@@ -515,116 +385,24 @@ describe('qortexRtdProvider', () => {
})
})
- describe('generateAnalyticsEventObject', () => {
- let qortexSessionInfo;
- beforeEach(() => {
- qortexSessionInfo = initializeModuleData(validModuleConfig);
- setGroupConfigData(validGroupConfigResponseObj);
- })
-
- afterEach(() => {
- initializeModuleData(emptyModuleConfig);
- setGroupConfigData(null);
- })
-
- it('returns expected object', () => {
- const testEventType = 'TEST';
- const testSubType = 'TEST_SUBTYPE';
- const testData = {data: 'data'};
-
- const result = generateAnalyticsEventObject(testEventType, testSubType, testData);
-
- expect(result.sessionId).to.be.eql(qortexSessionInfo.sessionId);
- expect(result.groupId).to.be.eql(qortexSessionInfo.groupId);
- expect(result.eventType).to.be.eql(testEventType);
- expect(result.subType).to.be.eql(testSubType);
- expect(result.eventOriginSource).to.be.eql('RTD');
- expect(result.data).to.be.eql(testData);
- })
- })
-
- describe('generateAnalyticsHostUrl', () => {
- it('will use qortex analytics host when appropriate', () => {
- const hostUrl = generateAnalyticsHostUrl(defaultApiHost);
- expect(hostUrl).to.be.eql('https://events.qortex.ai/api/v1/player-event');
- })
-
- it('will use qortex stage analytics host when appropriate', () => {
- const hostUrl = generateAnalyticsHostUrl('https://stg-demand.qortex.ai');
- expect(hostUrl).to.be.eql('https://stg-events.qortex.ai/api/v1/player-event');
- })
-
- it('will default to dev analytics host when appropriate', () => {
- const hostUrl = generateAnalyticsHostUrl('https://dev-demand.qortex.ai');
- expect(hostUrl).to.be.eql('https://dev-events.qortex.ai/api/v1/player-event');
- })
- })
-
- describe('getGroupConfig', () => {
- let sessionInfo;
-
- beforeEach(() => {
- sessionInfo = initializeModuleData(validModuleConfig);
- })
-
- afterEach(() => {
- initializeModuleData(emptyModuleConfig);
- setGroupConfigData(null);
- setContextData(null);
- server.reset();
- })
-
- it('returns a promise', () => {
- const result = getGroupConfig();
- expect(result).to.be.a('promise');
- })
-
- it('processes group config response in valid conditions', (done) => {
- const result = getGroupConfig();
- const request = server.requests[0]
- request.respond(200, responseHeaders, validGroupConfigResponse);
- result.then(response => {
- expect(request.url).to.be.eql(sessionInfo.groupConfigUrl);
- expect(response.groupId).to.be.eql(validGroupConfigResponseObj.groupId);
- expect(response.active).to.be.eql(validGroupConfigResponseObj.active);
- expect(response.prebidBidEnrichment).to.be.eql(validGroupConfigResponseObj.prebidBidEnrichment);
- expect(response.prebidReportingPercentage).to.be.eql(validGroupConfigResponseObj.prebidReportingPercentage);
- done();
- })
- })
- })
-
describe('initializeBidEnrichment', () => {
beforeEach(() => {
initializeModuleData(validModuleConfig);
setGroupConfigData(validGroupConfigResponseObj);
setContextData(null);
- server.reset();
})
afterEach(() => {
- initializeModuleData(emptyModuleConfig);
setGroupConfigData(null);
setContextData(null);
- server.reset();
})
- it('sets context data if applicable', (done) => {
- initializeBidEnrichment();
- server.requests[0].respond(200, responseHeaders, contextResponse);
- setTimeout(() => {
- expect(logMessageSpy.calledWith('Contextual record Received from Qortex API')).to.be.true;
- done()
- }, 250)
+ it('processes incoming qortex component "initialize" message', () => {
+ windowPostMessageReceived({data: QortexPostMessageInitialized})
})
- it('logs warning if no record has been made', (done) => {
- initializeBidEnrichment();
- server.requests[0].respond(202, responseHeaders, JSON.stringify({}));
- setTimeout(() => {
- expect(logWarnSpy.calledWith('Contexual record is not yet complete at this time')).to.be.true;
- done();
- }, 250)
+ it('processes incoming qortex component "context" message', () => {
+ windowPostMessageReceived({data: QortexPostMessageContext})
})
})
})
diff --git a/test/spec/modules/rewardedInterestIdSystem_spec.js b/test/spec/modules/rewardedInterestIdSystem_spec.js
new file mode 100644
index 00000000000..b6ce1e03f76
--- /dev/null
+++ b/test/spec/modules/rewardedInterestIdSystem_spec.js
@@ -0,0 +1,190 @@
+import sinon from 'sinon';
+import {expect} from 'chai';
+import * as utils from 'src/utils.js';
+import {attachIdSystem} from 'modules/userId';
+import {createEidsArray} from 'modules/userId/eids';
+import {
+ MODULE_NAME,
+ SOURCE,
+ getRewardedInterestApi,
+ watchRewardedInterestApi,
+ getRewardedInterestId,
+ apiNotAvailable,
+ rewardedInterestIdSubmodule
+} from 'modules/rewardedInterestIdSystem.js';
+
+describe('rewardedInterestIdSystem', () => {
+ const mockUserId = 'rewarded_interest_id';
+ const mockApi = {
+ getApiVersion: () => '1.0',
+ getIdentityToken: () => Promise.resolve(mockUserId)
+ };
+ const errorApiNotFound = `${MODULE_NAME} module: Rewarded Interest API not found`;
+ const errorIdFetch = `${MODULE_NAME} module: ID fetch encountered an error`;
+ let mockReadySate = 'complete';
+ let logErrorSpy;
+ let callbackSpy;
+
+ before(() => {
+ Object.defineProperty(document, 'readyState', {
+ get() {
+ return mockReadySate;
+ },
+ });
+ });
+
+ beforeEach(() => {
+ logErrorSpy = sinon.spy(utils, 'logError');
+ callbackSpy = sinon.spy();
+ });
+
+ afterEach(() => {
+ mockReadySate = 'complete';
+ delete window.__riApi;
+ logErrorSpy.restore();
+ });
+
+ describe('getRewardedInterestApi', () => {
+ it('should return Rewarded Interest Api if exists', () => {
+ expect(getRewardedInterestApi()).to.be.undefined;
+ window.__riApi = {};
+ expect(getRewardedInterestApi()).to.be.undefined;
+ window.__riApi.getIdentityToken = mockApi.getIdentityToken;
+ expect(getRewardedInterestApi()).to.deep.equal(window.__riApi);
+ });
+ });
+
+ describe('watchRewardedInterestApi', () => {
+ it('should execute callback when __riApi is set', () => {
+ watchRewardedInterestApi(callbackSpy);
+ expect(window.__riApi).to.be.undefined;
+ window.__riApi = mockApi;
+ expect(callbackSpy.calledOnceWithExactly(mockApi)).to.be.true;
+ expect(getRewardedInterestApi()).to.deep.equal(mockApi);
+ });
+ });
+
+ describe('getRewardedInterestId', () => {
+ it('should get id from API and pass it to callback', async () => {
+ await getRewardedInterestId(mockApi, callbackSpy);
+ expect(callbackSpy.calledOnceWithExactly(mockUserId)).to.be.true;
+ });
+ });
+
+ describe('apiNotAvailable', () => {
+ it('should call callback without ID and log error', () => {
+ apiNotAvailable(callbackSpy);
+ expect(callbackSpy.calledOnceWithExactly()).to.be.true;
+ expect(logErrorSpy.calledOnceWithExactly(errorApiNotFound)).to.be.true;
+ });
+ });
+
+ describe('rewardedInterestIdSubmodule.name', () => {
+ it('should expose the name of the submodule', () => {
+ expect(rewardedInterestIdSubmodule).to.be.an.instanceof(Object);
+ expect(rewardedInterestIdSubmodule.name).to.equal(MODULE_NAME);
+ });
+ });
+
+ describe('rewardedInterestIdSubmodule.decode', () => {
+ it('should wrap the given value inside an object literal', () => {
+ expect(rewardedInterestIdSubmodule.decode(mockUserId)).to.deep.equal({ [MODULE_NAME]: mockUserId });
+ expect(rewardedInterestIdSubmodule.decode('')).to.be.undefined;
+ expect(rewardedInterestIdSubmodule.decode(null)).to.be.undefined;
+ });
+ });
+
+ describe('rewardedInterestIdSubmodule.getId', () => {
+ it('should return object with callback property', () => {
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ expect(idResponse).to.be.an.instanceof(Object);
+ expect(idResponse).to.have.property('callback');
+ expect(idResponse.callback).to.be.a('function');
+ });
+
+ it('API not found, window loaded', async () => {
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ idResponse.callback(callbackSpy);
+ await Promise.resolve();
+ expect(callbackSpy.calledOnceWithExactly()).to.be.true;
+ expect(logErrorSpy.calledOnceWithExactly(errorApiNotFound)).to.be.true;
+ });
+
+ it('API not found, window not loaded', async () => {
+ mockReadySate = 'loading';
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ idResponse.callback(callbackSpy);
+ window.dispatchEvent(new Event('load'));
+ expect(callbackSpy.calledOnceWithExactly()).to.be.true;
+ expect(logErrorSpy.calledOnceWithExactly(errorApiNotFound)).to.be.true;
+ });
+
+ it('API is set before getId, getIdentityToken return error', async () => {
+ const error = Error();
+ window.__riApi = {getIdentityToken: () => Promise.reject(error)};
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ idResponse.callback(callbackSpy);
+ await window.__riApi.getIdentityToken().catch(() => {});
+ expect(callbackSpy.calledOnceWithExactly()).to.be.true;
+ expect(logErrorSpy.calledOnceWithExactly(errorIdFetch, error)).to.be.true;
+ });
+
+ it('API is set after getId, getIdentityToken return error', async () => {
+ const error = Error();
+ mockReadySate = 'loading';
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ idResponse.callback(callbackSpy);
+ window.__riApi = {getIdentityToken: () => Promise.reject(error)};
+ await window.__riApi.getIdentityToken().catch(() => {});
+ expect(callbackSpy.calledOnceWithExactly()).to.be.true;
+ expect(logErrorSpy.calledOnceWithExactly(errorIdFetch, error)).to.be.true;
+ });
+
+ it('API is set before getId, getIdentityToken return user ID', async () => {
+ window.__riApi = mockApi;
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ idResponse.callback(callbackSpy);
+ await mockApi.getIdentityToken();
+ expect(callbackSpy.calledOnceWithExactly(mockUserId)).to.be.true;
+ });
+
+ it('API is set after getId, getIdentityToken return user ID', async () => {
+ mockReadySate = 'loading';
+ const idResponse = rewardedInterestIdSubmodule.getId();
+ idResponse.callback(callbackSpy);
+ window.__riApi = mockApi;
+ window.dispatchEvent(new Event('load'));
+ await window.__riApi.getIdentityToken().catch(() => {});
+ expect(callbackSpy.calledOnceWithExactly(mockUserId)).to.be.true;
+ });
+ });
+
+ describe('rewardedInterestIdSubmodule.eids', () => {
+ it('should expose the eids of the submodule', () => {
+ expect(rewardedInterestIdSubmodule).to.have.property('eids');
+ expect(rewardedInterestIdSubmodule.eids).to.be.a('object');
+ expect(rewardedInterestIdSubmodule.eids).to.deep.equal({
+ [MODULE_NAME]: {
+ source: SOURCE,
+ atype: 3,
+ },
+ });
+ });
+
+ it('createEidsArray', () => {
+ attachIdSystem(rewardedInterestIdSubmodule);
+ const eids = createEidsArray({
+ [MODULE_NAME]: mockUserId
+ });
+ expect(eids).to.be.a('array');
+ expect(eids.length).to.equal(1);
+ expect(eids[0]).to.deep.equal({
+ source: SOURCE,
+ uids: [{
+ id: mockUserId,
+ atype: 3,
+ }]
+ });
+ });
+ });
+});
diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js
index a1baaf52a77..26d226b65f3 100644
--- a/test/spec/modules/richaudienceBidAdapter_spec.js
+++ b/test/spec/modules/richaudienceBidAdapter_spec.js
@@ -258,7 +258,7 @@ describe('Richaudience adapter tests', function () {
currency: 'USD',
ttl: 300,
dealId: 'dealId',
- adomain: 'richaudience.com'
+ adomain: ['richaudience.com']
}
};
@@ -274,7 +274,7 @@ describe('Richaudience adapter tests', function () {
ttl: 300,
vastXML: '',
dealId: 'dealId',
- adomain: 'richaudience.com'
+ adomain: ['richaudience.com']
}
};
@@ -710,7 +710,7 @@ describe('Richaudience adapter tests', function () {
expect(bid.currency).to.equal('USD');
expect(bid.ttl).to.equal(300);
expect(bid.dealId).to.equal('dealId');
- expect(bid.meta).to.equal('richaudience.com');
+ expect(bid.meta.advertiserDomains[0]).to.equal('richaudience.com');
});
it('no banner media response inestream', function () {
@@ -739,7 +739,7 @@ describe('Richaudience adapter tests', function () {
expect(bid.currency).to.equal('USD');
expect(bid.ttl).to.equal(300);
expect(bid.dealId).to.equal('dealId');
- expect(bid.meta).to.equal('richaudience.com');
+ expect(bid.meta.advertiserDomains[0]).to.equal('richaudience.com');
});
it('no banner media response outstream', function () {
diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js
index 1b91586dfb5..aff6c6df171 100644
--- a/test/spec/modules/rubiconBidAdapter_spec.js
+++ b/test/spec/modules/rubiconBidAdapter_spec.js
@@ -710,6 +710,28 @@ describe('the rubicon adapter', function () {
expect(data.get('o_cdep')).to.equal('3');
});
+ it('should correctly send ip signal when ortb2.device.ip is provided', () => {
+ const ipRequest = utils.deepClone(bidderRequest);
+ ipRequest.bids[0].ortb2 = { device: { ip: '123.45.67.89' } };
+
+ let [request] = spec.buildRequests(ipRequest.bids, ipRequest);
+ let data = new URLSearchParams(request.data);
+
+ // Verify if 'ip' is correctly added to the request data
+ expect(data.get('ip')).to.equal('123.45.67.89');
+ });
+
+ it('should correctly send ipv6 signal when ortb2.device.ipv6 is provided', () => {
+ const ipv6Request = utils.deepClone(bidderRequest);
+ ipv6Request.bids[0].ortb2 = { device: { ipv6: '2001:db8::ff00:42:8329' } };
+
+ let [request] = spec.buildRequests(ipv6Request.bids, ipv6Request);
+ let data = new URLSearchParams(request.data);
+
+ // Verify if 'ipv6' is correctly added to the request data
+ expect(data.get('ipv6')).to.equal('2001:db8::ff00:42:8329');
+ });
+
it('ad engine query params should be ordered correctly', function () {
sandbox.stub(Math, 'random').callsFake(() => 0.1);
let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
@@ -1332,173 +1354,60 @@ describe('the rubicon adapter', function () {
});
describe('user id config', function () {
- it('should send tpid_tdid when userIdAsEids contains unifiedId', function () {
- const clonedBid = utils.deepClone(bidderRequest.bids[0]);
- clonedBid.userId = {
- tdid: 'abcd-efgh-ijkl-mnop-1234'
- };
- clonedBid.userIdAsEids = [
- {
- 'source': 'adserver.org',
- 'uids': [
- {
- 'id': 'abcd-efgh-ijkl-mnop-1234',
- 'atype': 1,
- 'ext': {
- 'rtiPartner': 'TDID'
- }
- }
- ]
- }
- ];
- let [request] = spec.buildRequests([clonedBid], bidderRequest);
- let data = new URLSearchParams(request.data);
-
- expect(data.get('tpid_tdid')).to.equal('abcd-efgh-ijkl-mnop-1234');
- expect(data.get('eid_adserver.org')).to.equal('abcd-efgh-ijkl-mnop-1234');
- });
-
- describe('LiveIntent support', function () {
- it('should send tpid_liveintent.com when userIdAsEids contains liveintentId', function () {
- const clonedBid = utils.deepClone(bidderRequest.bids[0]);
- clonedBid.userId = {
- lipb: {
- lipbid: '0000-1111-2222-3333',
- segments: ['segA', 'segB']
- }
- };
- clonedBid.userIdAsEids = [
- {
- 'source': 'liveintent.com',
- 'uids': [
- {
- 'id': '0000-1111-2222-3333',
- 'atype': 3
- }
- ],
- 'ext': {
- 'segments': [
- 'segA',
- 'segB'
- ]
- }
- }
- ];
- let [request] = spec.buildRequests([clonedBid], bidderRequest);
- let data = new URLSearchParams(request.data);
-
- expect(data.get('tpid_liveintent.com')).to.equal('0000-1111-2222-3333');
- expect(data.get('eid_liveintent.com')).to.equal('0000-1111-2222-3333');
- expect(data.get('tg_v.LIseg')).to.equal('segA,segB');
- });
-
- it('should send tg_v.LIseg when userIdAsEids contains liveintentId with ext.segments as array', function () {
- const clonedBid = utils.deepClone(bidderRequest.bids[0]);
- clonedBid.userId = {
- lipb: {
- lipbid: '1111-2222-3333-4444',
- segments: ['segD', 'segE']
- }
- };
- clonedBid.userIdAsEids = [
- {
- 'source': 'liveintent.com',
- 'uids': [
- {
- 'id': '1111-2222-3333-4444',
- 'atype': 3
- }
- ],
- 'ext': {
- 'segments': [
- 'segD',
- 'segE'
- ]
- }
- }
- ]
- let [request] = spec.buildRequests([clonedBid], bidderRequest);
- const unescapedData = unescape(request.data);
-
- expect(unescapedData.indexOf('&tpid_liveintent.com=1111-2222-3333-4444&') !== -1).to.equal(true);
- expect(unescapedData.indexOf('&tg_v.LIseg=segD,segE&') !== -1).to.equal(true);
- });
- });
-
- describe('LiveRamp support', function () {
- it('should send x_liverampidl when userIdAsEids contains liverampId', function () {
- const clonedBid = utils.deepClone(bidderRequest.bids[0]);
- clonedBid.userId = {
- idl_env: '1111-2222-3333-4444'
- };
- clonedBid.userIdAsEids = [
- {
- 'source': 'liveramp.com',
- 'uids': [
- {
- 'id': '1111-2222-3333-4444',
- 'atype': 3
- }
- ]
- }
- ]
- let [request] = spec.buildRequests([clonedBid], bidderRequest);
- let data = new URLSearchParams(request.data);
-
- expect(data.get('x_liverampidl')).to.equal('1111-2222-3333-4444');
- });
- });
-
describe('pubcid support', function () {
- it('should send eid_pubcid.org when userIdAsEids contains pubcid', function () {
+ it('should send eid_pubcid.org when ortb2.user.ext.eids contains pubcid', function () {
const clonedBid = utils.deepClone(bidderRequest.bids[0]);
clonedBid.userId = {
pubcid: '1111'
};
- clonedBid.userIdAsEids = [
- {
- 'source': 'pubcid.org',
- 'uids': [
- {
- 'id': '1111',
- 'atype': 1
- }
- ]
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ 'source': 'pubcid.org',
+ 'uids': [{
+ 'id': '1111',
+ 'atype': 1
+ }]
+ }]
+ }
}
- ]
+ };
let [request] = spec.buildRequests([clonedBid], bidderRequest);
let data = new URLSearchParams(request.data);
- expect(data.get('eid_pubcid.org')).to.equal('1111^1');
+ expect(data.get('eid_pubcid.org')).to.equal('1111^1^^^^^');
});
});
describe('Criteo support', function () {
- it('should send eid_criteo.com when userIdAsEids contains criteo', function () {
+ it('should send eid_criteo.com when ortb2.user.ext.eids contains criteo', function () {
const clonedBid = utils.deepClone(bidderRequest.bids[0]);
clonedBid.userId = {
criteoId: '1111'
};
- clonedBid.userIdAsEids = [
- {
- 'source': 'criteo.com',
- 'uids': [
- {
- 'id': '1111',
- 'atype': 1
- }
- ]
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ 'source': 'criteo.com',
+ 'uids': [{
+ 'id': '1111',
+ 'atype': 1
+ }]
+ }]
+ }
}
- ]
+ };
let [request] = spec.buildRequests([clonedBid], bidderRequest);
let data = new URLSearchParams(request.data);
- expect(data.get('eid_criteo.com')).to.equal('1111^1');
+ expect(data.get('eid_criteo.com')).to.equal('1111^1^^^^^');
});
});
describe('pubProvidedId support', function () {
- it('should send pubProvidedId when userIdAsEids contains pubProvidedId ids', function () {
+ it('should send pubProvidedId when ortb2.user.ext.eids contains pubProvidedId ids', function () {
const clonedBid = utils.deepClone(bidderRequest.bids[0]);
clonedBid.userId = {
pubProvidedId: [{
@@ -1516,27 +1425,27 @@ describe('the rubicon adapter', function () {
}]
}]
};
- clonedBid.userIdAsEids = [
- {
- 'source': 'example.com',
- 'uids': [
- {
- 'id': '11111',
- 'ext': {
- 'stype': 'ppuid'
- }
- }
- ]
- },
- {
- 'source': 'id-partner.com',
- 'uids': [
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ 'source': 'example.com',
+ 'uids': [{
+ 'id': '11111',
+ 'ext': {
+ 'stype': 'ppuid'
+ }
+ }]
+ },
{
- 'id': '222222'
- }
- ]
+ 'source': 'id-partner.com',
+ 'uids': [{
+ 'id': '222222'
+ }]
+ }]
+ }
}
- ];
+ };
let [request] = spec.buildRequests([clonedBid], bidderRequest);
let data = new URLSearchParams(request.data);
@@ -1545,7 +1454,7 @@ describe('the rubicon adapter', function () {
});
describe('ID5 support', function () {
- it('should send ID5 id when userIdAsEids contains ID5', function () {
+ it('should send ID5 id when ortb2.user.ext.eids contains ID5', function () {
const clonedBid = utils.deepClone(bidderRequest.bids[0]);
clonedBid.userId = {
id5id: {
@@ -1555,24 +1464,26 @@ describe('the rubicon adapter', function () {
}
}
};
- clonedBid.userIdAsEids = [
- {
- 'source': 'id5-sync.com',
- 'uids': [
- {
- 'id': '11111',
- 'atype': 1,
- 'ext': {
- 'linkType': '22222'
- }
- }
- ]
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ 'source': 'id5-sync.com',
+ 'uids': [{
+ 'id': '11111',
+ 'atype': 1,
+ 'ext': {
+ 'linkType': '22222'
+ }
+ }]
+ }]
+ }
}
- ];
+ };
let [request] = spec.buildRequests([clonedBid], bidderRequest);
let data = new URLSearchParams(request.data);
- expect(data.get('eid_id5-sync.com')).to.equal('11111^1^22222');
+ expect(data.get('eid_id5-sync.com')).to.equal('11111^1^^^^^');
});
});
@@ -1580,36 +1491,82 @@ describe('the rubicon adapter', function () {
it('should send user id with generic format', function () {
const clonedBid = utils.deepClone(bidderRequest.bids[0]);
// Hardcoding userIdAsEids since createEidsArray returns empty array if source not found in eids.js
- clonedBid.userIdAsEids = [{
- source: 'catchall',
- uids: [{
- id: '11111',
- atype: 2
- }]
- }]
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ 'source': 'catchall',
+ 'uids': [{
+ 'id': '11111',
+ 'atype': 2
+ }]
+ }]
+ }
+ }
+ };
let [request] = spec.buildRequests([clonedBid], bidderRequest);
let data = new URLSearchParams(request.data);
- expect(data.get('eid_catchall')).to.equal('11111^2');
+ expect(data.get('eid_catchall')).to.equal('11111^2^^^^^');
});
it('should send rubiconproject special case', function () {
const clonedBid = utils.deepClone(bidderRequest.bids[0]);
// Hardcoding userIdAsEids since createEidsArray returns empty array if source not found in eids.js
- clonedBid.userIdAsEids = [{
- source: 'rubiconproject.com',
- uids: [{
- id: 'some-cool-id',
- atype: 3
- }]
- }]
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ source: 'rubiconproject.com',
+ uids: [{
+ id: 'some-cool-id',
+ atype: 3
+ }]
+ }]
+ }
+ }
+ };
let [request] = spec.buildRequests([clonedBid], bidderRequest);
let data = new URLSearchParams(request.data);
- expect(data.get('eid_rubiconproject.com')).to.equal('some-cool-id');
+ expect(data.get('eid_rubiconproject.com')).to.equal('some-cool-id^3^^^^^');
});
- });
+ describe('Full eidValue format validation', function () {
+ it('should send complete eidValue in the format uid^atype^third^inserter^matcher^mm^rtipartner', function () {
+ const clonedBid = utils.deepClone(bidderRequest.bids[0]);
+ // Simulating a full EID object with multiple fields
+ clonedBid.ortb2 = {
+ user: {
+ ext: {
+ eids: [{
+ source: 'example.com',
+ uids: [{
+ id: '11111', // UID
+ atype: 2, // atype
+ ext: {
+ rtipartner: 'rtipartner123', // rtipartner
+ stype: 'ppuid' // stype
+ }
+ }],
+ inserter: 'inserter123', // inserter
+ matcher: 'matcher123', // matcher
+ mm: 'mm123' // mm
+ }]
+ }
+ }
+ };
+
+ let [request] = spec.buildRequests([clonedBid], bidderRequest);
+ let data = new URLSearchParams(request.data);
+ // Expected format: uid^atype^third^inserter^matcher^mm^rtipartner
+ const expectedEidValue = '11111^2^^inserter123^matcher123^mm123^rtipartner123';
+
+ // Check if the generated EID value matches the expected format
+ expect(data.get('eid_example.com')).to.equal(expectedEidValue);
+ });
+ });
+ });
describe('Config user.id support', function () {
it('should send ppuid when config defines user.id', function () {
config.setConfig({user: {id: '123'}});
@@ -2871,6 +2828,36 @@ describe('the rubicon adapter', function () {
expect(slotParams['tg_i.tax10']).is.equal('2,3');
expect(slotParams['tg_v.tax404']).is.equal(undefined);
});
+
+ it('should add p_site.mobile if mobile is a number in ortb2.site', function () {
+ // Set up a bidRequest with mobile property as a number
+ const localBidderRequest = Object.assign({}, bidderRequest);
+ localBidderRequest.bids[0].ortb2 = {
+ site: {
+ mobile: 1 // Valid mobile value (number)
+ }
+ };
+
+ // Call the function
+ const slotParams = spec.createSlotParams(localBidderRequest.bids[0], localBidderRequest);
+ // Check that p_site.mobile was added to the slotParams with the correct value
+ expect(slotParams['p_site.mobile']).to.equal(1);
+ });
+ it('should not add p_site.mobile if mobile is not a number in ortb2.site', function () {
+ // Set up a bidRequest with mobile property as a string (invalid value)
+ const localBidderRequest = Object.assign({}, bidderRequest);
+ localBidderRequest.bids[0].ortb2 = {
+ site: {
+ mobile: 'not-a-number' // Invalid mobile value (string)
+ }
+ };
+
+ // Call the function
+ const slotParams = spec.createSlotParams(localBidderRequest.bids[0], localBidderRequest);
+
+ // Check that p_site.mobile is not added to the slotParams
+ expect(slotParams['p_site.mobile']).to.be.undefined;
+ });
});
describe('classifiedAsVideo', function () {
@@ -3784,6 +3771,71 @@ describe('the rubicon adapter', function () {
expect(bids[0].cpm).to.be.equal(0);
});
+ it('should use ads.emulated_format if defined for bid.meta.mediaType', function () {
+ let response = {
+ 'status': 'ok',
+ 'account_id': 14062,
+ 'site_id': 70608,
+ 'zone_id': 530022,
+ 'size_id': 15,
+ 'alt_size_ids': [
+ 43
+ ],
+ 'tracking': '',
+ 'inventory': {},
+ 'ads': [
+ {
+ 'status': 'ok',
+ 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c',
+ 'size_id': '15',
+ 'ad_id': '6',
+ 'advertiser': 7,
+ 'network': 8,
+ 'creative_id': 'crid-9',
+ 'type': 'script',
+ 'script': 'alert(\'foo\')',
+ 'campaign_id': 10,
+ 'cpm': 0.811,
+ 'emulated_format': 'video',
+ 'targeting': [
+ {
+ 'key': 'rpfl_14062',
+ 'values': [
+ '15_tier_all_test'
+ ]
+ }
+ ]
+ },
+ {
+ 'status': 'ok',
+ 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d',
+ 'size_id': '43',
+ 'ad_id': '7',
+ 'advertiser': 7,
+ 'network': 8,
+ 'creative_id': 'crid-9',
+ 'type': 'script',
+ 'script': 'alert(\'foo\')',
+ 'campaign_id': 10,
+ 'cpm': 0.911,
+ 'targeting': [
+ {
+ 'key': 'rpfl_14062',
+ 'values': [
+ '43_tier_all_test'
+ ]
+ }
+ ]
+ }
+ ]
+ };
+ let bids = spec.interpretResponse({body: response}, {
+ bidRequest: bidderRequest.bids[0]
+ });
+ expect(bids[0].meta.mediaType).to.equal('banner');
+ expect(bids[1].meta.mediaType).to.equal('video');
+ });
+
describe('singleRequest enabled', function () {
it('handles bidRequest of type Array and returns associated adUnits', function () {
const overrideMap = [];
@@ -4145,6 +4197,7 @@ describe('the rubicon adapter', function () {
const bid = bids[0];
bid.adUnitCode = 'outstream_video1_placement';
const adUnit = document.createElement('div');
+ const adUnitSelector = `#${bid.adUnitCode}`
adUnit.id = bid.adUnitCode;
document.body.appendChild(adUnit);
@@ -4158,7 +4211,7 @@ describe('the rubicon adapter', function () {
label: undefined,
placement: {
align: 'left',
- attachTo: adUnit,
+ attachTo: adUnitSelector,
position: 'append',
},
vastUrl: 'https://test.com/vast.xml',
@@ -4214,6 +4267,7 @@ describe('the rubicon adapter', function () {
const bid = bids[0];
bid.adUnitCode = 'outstream_video1_placement';
const adUnit = document.createElement('div');
+ const adUnitSelector = `#${bid.adUnitCode}`
adUnit.id = bid.adUnitCode;
document.body.appendChild(adUnit);
@@ -4227,7 +4281,7 @@ describe('the rubicon adapter', function () {
label: undefined,
placement: {
align: 'left',
- attachTo: adUnit,
+ attachTo: adUnitSelector,
position: 'append',
},
vastUrl: 'https://test.com/vast.xml',
diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js
index 14b2740497d..db19d71f23f 100644
--- a/test/spec/modules/seedtagBidAdapter_spec.js
+++ b/test/spec/modules/seedtagBidAdapter_spec.js
@@ -49,15 +49,13 @@ function createInStreamSlotConfig(mediaType) {
return getSlotConfigs(mediaType, {
publisherId: PUBLISHER_ID,
adUnitId: ADUNIT_ID,
- placement: 'inStream',
});
}
-const createBannerSlotConfig = (placement, mediatypes) => {
+const createBannerSlotConfig = (mediatypes) => {
return getSlotConfigs(mediatypes || { banner: {} }, {
publisherId: PUBLISHER_ID,
adUnitId: ADUNIT_ID,
- placement,
});
};
@@ -72,49 +70,69 @@ describe('Seedtag Adapter', function () {
describe('isBidRequestValid method', function () {
describe('returns true', function () {
describe('when banner slot config has all mandatory params', () => {
- const placements = ['inBanner', 'inImage', 'inScreen', 'inArticle'];
- placements.forEach((placement) => {
- it(placement + 'should be valid', function () {
+ it('should be valid', function () {
+ const isBidRequestValid = spec.isBidRequestValid(
+ createBannerSlotConfig()
+ );
+ expect(isBidRequestValid).to.equal(true);
+ });
+
+ it('should be valid when has display and video mediatypes, and video context is outstream',
+ function () {
const isBidRequestValid = spec.isBidRequestValid(
- createBannerSlotConfig(placement)
+ createBannerSlotConfig({
+ banner: {},
+ video: {
+ context: 'outstream',
+ playerSize: [[600, 200]],
+ },
+ })
);
expect(isBidRequestValid).to.equal(true);
- });
-
- it(
- placement +
- ' should be valid when has display and video mediatypes, and video context is outstream',
- function () {
- const isBidRequestValid = spec.isBidRequestValid(
- createBannerSlotConfig(placement, {
- banner: {},
- video: {
- context: 'outstream',
- playerSize: [[600, 200]],
- },
- })
- );
- expect(isBidRequestValid).to.equal(true);
- }
- );
+ }
+ );
- it(
- placement +
- " shouldn't be valid when has display and video mediatypes, and video context is instream",
- function () {
- const isBidRequestValid = spec.isBidRequestValid(
- createBannerSlotConfig(placement, {
- banner: {},
- video: {
- context: 'instream',
- playerSize: [[600, 200]],
- },
- })
- );
- expect(isBidRequestValid).to.equal(false);
- }
- );
- });
+ it('should be valid when has only video mediatypes, and video context is outstream',
+ function () {
+ const isBidRequestValid = spec.isBidRequestValid(
+ createBannerSlotConfig({
+ video: {
+ context: 'outstream',
+ playerSize: [[600, 200]],
+ },
+ })
+ );
+ expect(isBidRequestValid).to.equal(true);
+ }
+ );
+ it('should be valid when has display and video mediatypes, and video context is instream',
+ function () {
+ const isBidRequestValid = spec.isBidRequestValid(
+ createBannerSlotConfig({
+ banner: {},
+ video: {
+ context: 'instream',
+ playerSize: [[600, 200]],
+ },
+ })
+ );
+ expect(isBidRequestValid).to.equal(false);
+ }
+ );
+ it("shouldn't be valid when has display and video mediatypes, and video context is instream",
+ function () {
+ const isBidRequestValid = spec.isBidRequestValid(
+ createBannerSlotConfig({
+ banner: {},
+ video: {
+ context: 'instream',
+ playerSize: [[600, 200]],
+ },
+ })
+ );
+ expect(isBidRequestValid).to.equal(false);
+ }
+ );
});
describe('when video slot has all mandatory params', function () {
it('should return true, when video context is instream', function () {
@@ -127,7 +145,7 @@ describe('Seedtag Adapter', function () {
const isBidRequestValid = spec.isBidRequestValid(slotConfig);
expect(isBidRequestValid).to.equal(true);
});
- it('should return true, when video context is instream and mediatype is video and banner', function () {
+ it('should return false, when video context is instream and mediatype is video and banner', function () {
const slotConfig = createInStreamSlotConfig({
video: {
context: 'instream',
@@ -136,33 +154,6 @@ describe('Seedtag Adapter', function () {
banner: {},
});
const isBidRequestValid = spec.isBidRequestValid(slotConfig);
- expect(isBidRequestValid).to.equal(true);
- });
- it('should return false, when video context is instream, but placement is not inStream', function () {
- const slotConfig = getSlotConfigs(
- {
- video: {
- context: 'instream',
- playerSize: [[600, 200]],
- },
- },
- {
- publisherId: PUBLISHER_ID,
- adUnitId: ADUNIT_ID,
- placement: 'inBanner',
- }
- );
- const isBidRequestValid = spec.isBidRequestValid(slotConfig);
- expect(isBidRequestValid).to.equal(false);
- });
- it('should return false, when video context is outstream', function () {
- const slotConfig = createInStreamSlotConfig({
- video: {
- context: 'outstream',
- playerSize: [[600, 200]],
- },
- });
- const isBidRequestValid = spec.isBidRequestValid(slotConfig);
expect(isBidRequestValid).to.equal(false);
});
});
@@ -176,7 +167,6 @@ describe('Seedtag Adapter', function () {
const isBidRequestValid = spec.isBidRequestValid(
createSlotConfig({
adUnitId: ADUNIT_ID,
- placement: 'inBanner',
})
);
expect(isBidRequestValid).to.equal(false);
@@ -185,26 +175,6 @@ describe('Seedtag Adapter', function () {
const isBidRequestValid = spec.isBidRequestValid(
createSlotConfig({
publisherId: PUBLISHER_ID,
- placement: 'inBanner',
- })
- );
- expect(isBidRequestValid).to.equal(false);
- });
- it('does not have the placement.', function () {
- const isBidRequestValid = spec.isBidRequestValid(
- createSlotConfig({
- publisherId: PUBLISHER_ID,
- adUnitId: ADUNIT_ID,
- })
- );
- expect(isBidRequestValid).to.equal(false);
- });
- it('does not have a the correct placement.', function () {
- const isBidRequestValid = spec.isBidRequestValid(
- createSlotConfig({
- publisherId: PUBLISHER_ID,
- adUnitId: ADUNIT_ID,
- placement: 'another_thing',
})
);
expect(isBidRequestValid).to.equal(false);
@@ -224,17 +194,7 @@ describe('Seedtag Adapter', function () {
);
expect(isBidRequestValid).to.equal(false);
});
- it('is outstream ', function () {
- const isBidRequestValid = spec.isBidRequestValid(
- createInStreamSlotConfig({
- video: {
- context: 'outstream',
- playerSize: [[600, 200]],
- },
- })
- );
- expect(isBidRequestValid).to.equal(false);
- });
+
describe('order does not matter', function () {
it('when video is not the first slot', function () {
const isBidRequestValid = spec.isBidRequestValid(
@@ -262,12 +222,10 @@ describe('Seedtag Adapter', function () {
const mandatoryDisplayParams = {
publisherId: PUBLISHER_ID,
adUnitId: ADUNIT_ID,
- placement: 'inBanner',
};
const mandatoryVideoParams = {
publisherId: PUBLISHER_ID,
adUnitId: ADUNIT_ID,
- placement: 'inStream',
};
const validBidRequests = [
getSlotConfigs({ banner: {} }, mandatoryDisplayParams),
diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js
index 302a5fa1aa6..f9d300d84e4 100644
--- a/test/spec/modules/smaatoBidAdapter_spec.js
+++ b/test/spec/modules/smaatoBidAdapter_spec.js
@@ -174,6 +174,7 @@ describe('smaatoBidAdapterTest', () => {
id: 'bidId',
banner: BANNER_OPENRTB_IMP,
tagid: 'adspaceId',
+ secure: 1
}
]);
});
diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js
index 0935b6c8404..f6993b433ad 100644
--- a/test/spec/modules/smartadserverBidAdapter_spec.js
+++ b/test/spec/modules/smartadserverBidAdapter_spec.js
@@ -4,6 +4,8 @@ import { config } from 'src/config.js';
import { deepClone } from 'src/utils.js';
import { getBidFloor } from 'libraries/equativUtils/equativUtils.js'
import { spec } from 'modules/smartadserverBidAdapter.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
// Default params with optional ones
describe('Smart bid adapter tests', function () {
@@ -249,10 +251,8 @@ describe('Smart bid adapter tests', function () {
];
it('Verify build request', function () {
+ setCurrencyConfig({ adServerCurrency: 'EUR' });
config.setConfig({
- 'currency': {
- 'adServerCurrency': 'EUR'
- },
ortb2: {
'user': {
'data': sellerDefinedAudience
@@ -265,30 +265,32 @@ describe('Smart bid adapter tests', function () {
}
});
- const request = spec.buildRequests(DEFAULT_PARAMS);
- expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
- expect(request[0]).to.have.property('method').and.to.equal('POST');
- const requestContent = JSON.parse(request[0].data);
-
- expect(requestContent).to.have.property('siteid').and.to.equal('1234');
- expect(requestContent).to.have.property('pageid').and.to.equal('5678');
- expect(requestContent).to.have.property('formatid').and.to.equal('90');
- expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
- expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42);
- expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid');
- expect(requestContent).to.have.property('tagId').and.to.equal('sas_42');
- expect(requestContent).to.have.property('sizes');
- expect(requestContent.sizes[0]).to.have.property('w').and.to.equal(300);
- expect(requestContent.sizes[0]).to.have.property('h').and.to.equal(250);
- expect(requestContent.sizes[1]).to.have.property('w').and.to.equal(300);
- expect(requestContent.sizes[1]).to.have.property('h').and.to.equal(200);
- expect(requestContent).to.not.have.property('pageDomain');
- expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
- expect(requestContent).to.have.property('buid').and.to.equal('7569');
- expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
- expect(requestContent).to.have.property('ckid').and.to.equal(42);
- expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience);
- expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext);
+ return addFPDToBidderRequest(DEFAULT_PARAMS[0]).then(res => {
+ const request = spec.buildRequests(DEFAULT_PARAMS, res);
+ expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
+ expect(request[0]).to.have.property('method').and.to.equal('POST');
+ const requestContent = JSON.parse(request[0].data);
+ expect(requestContent).to.have.property('siteid').and.to.equal('1234');
+ expect(requestContent).to.have.property('pageid').and.to.equal('5678');
+ expect(requestContent).to.have.property('formatid').and.to.equal('90');
+ expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
+ expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42);
+ expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid');
+ expect(requestContent).to.have.property('tagId').and.to.equal('sas_42');
+ expect(requestContent).to.have.property('sizes');
+ expect(requestContent.sizes[0]).to.have.property('w').and.to.equal(300);
+ expect(requestContent.sizes[0]).to.have.property('h').and.to.equal(250);
+ expect(requestContent.sizes[1]).to.have.property('w').and.to.equal(300);
+ expect(requestContent.sizes[1]).to.have.property('h').and.to.equal(200);
+ expect(requestContent).to.not.have.property('pageDomain');
+ expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
+ expect(requestContent).to.have.property('buid').and.to.equal('7569');
+ expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
+ expect(requestContent).to.have.property('ckid').and.to.equal(42);
+ expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience);
+ expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext);
+ setCurrencyConfig({});
+ });
});
it('Verify parse response with no ad', function () {
@@ -648,10 +650,8 @@ describe('Smart bid adapter tests', function () {
};
it('Verify instream video build request', function () {
+ setCurrencyConfig({ adServerCurrency: 'EUR' });
config.setConfig({
- 'currency': {
- 'adServerCurrency': 'EUR'
- },
ortb2: {
'user': {
'data': sellerDefinedAudience
@@ -663,29 +663,33 @@ describe('Smart bid adapter tests', function () {
}
}
});
- const request = spec.buildRequests(INSTREAM_DEFAULT_PARAMS);
- expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
- expect(request[0]).to.have.property('method').and.to.equal('POST');
- const requestContent = JSON.parse(request[0].data);
- expect(requestContent).to.have.property('siteid').and.to.equal('1234');
- expect(requestContent).to.have.property('pageid').and.to.equal('5678');
- expect(requestContent).to.have.property('formatid').and.to.equal('90');
- expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
- expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42);
- expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid');
- expect(requestContent).to.have.property('tagId').and.to.equal('sas_42');
- expect(requestContent).to.not.have.property('pageDomain');
- expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
- expect(requestContent).to.have.property('buid').and.to.equal('7569');
- expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
- expect(requestContent).to.have.property('ckid').and.to.equal(42);
- expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience);
- expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext);
- expect(requestContent).to.have.property('isVideo').and.to.equal(true);
- expect(requestContent).to.have.property('videoData');
- expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6);
- expect(requestContent.videoData).to.have.property('playerWidth').and.to.equal(640);
- expect(requestContent.videoData).to.have.property('playerHeight').and.to.equal(480);
+
+ return addFPDToBidderRequest(INSTREAM_DEFAULT_PARAMS[0]).then(res => {
+ const request = spec.buildRequests(INSTREAM_DEFAULT_PARAMS, res);
+ expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
+ expect(request[0]).to.have.property('method').and.to.equal('POST');
+ const requestContent = JSON.parse(request[0].data);
+ expect(requestContent).to.have.property('siteid').and.to.equal('1234');
+ expect(requestContent).to.have.property('pageid').and.to.equal('5678');
+ expect(requestContent).to.have.property('formatid').and.to.equal('90');
+ expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
+ expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42);
+ expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid');
+ expect(requestContent).to.have.property('tagId').and.to.equal('sas_42');
+ expect(requestContent).to.not.have.property('pageDomain');
+ expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
+ expect(requestContent).to.have.property('buid').and.to.equal('7569');
+ expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
+ expect(requestContent).to.have.property('ckid').and.to.equal(42);
+ expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience);
+ expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext);
+ expect(requestContent).to.have.property('isVideo').and.to.equal(true);
+ expect(requestContent).to.have.property('videoData');
+ expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6);
+ expect(requestContent.videoData).to.have.property('playerWidth').and.to.equal(640);
+ expect(requestContent.videoData).to.have.property('playerHeight').and.to.equal(480);
+ setCurrencyConfig({});
+ });
});
it('Verify instream parse response', function () {
@@ -989,10 +993,8 @@ describe('Smart bid adapter tests', function () {
};
it('Verify outstream video build request', function () {
+ setCurrencyConfig({ adServerCurrency: 'EUR' });
config.setConfig({
- 'currency': {
- 'adServerCurrency': 'EUR'
- },
ortb2: {
'user': {
'data': sellerDefinedAudience
@@ -1004,29 +1006,33 @@ describe('Smart bid adapter tests', function () {
}
}
});
- const request = spec.buildRequests(OUTSTREAM_DEFAULT_PARAMS);
- expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
- expect(request[0]).to.have.property('method').and.to.equal('POST');
- const requestContent = JSON.parse(request[0].data);
- expect(requestContent).to.have.property('siteid').and.to.equal('1234');
- expect(requestContent).to.have.property('pageid').and.to.equal('5678');
- expect(requestContent).to.have.property('formatid').and.to.equal('91');
- expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
- expect(requestContent).to.have.property('bidfloor').and.to.equal(0.43);
- expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid-outstream');
- expect(requestContent).to.have.property('tagId').and.to.equal('sas_43');
- expect(requestContent).to.not.have.property('pageDomain');
- expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
- expect(requestContent).to.have.property('buid').and.to.equal('7579');
- expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
- expect(requestContent).to.have.property('ckid').and.to.equal(43);
- expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience);
- expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext);
- expect(requestContent).to.have.property('isVideo').and.to.equal(false);
- expect(requestContent).to.have.property('videoData');
- expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(7);
- expect(requestContent.videoData).to.have.property('playerWidth').and.to.equal(800);
- expect(requestContent.videoData).to.have.property('playerHeight').and.to.equal(600);
+
+ return addFPDToBidderRequest(OUTSTREAM_DEFAULT_PARAMS[0]).then(res => {
+ const request = spec.buildRequests(OUTSTREAM_DEFAULT_PARAMS, res);
+ expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
+ expect(request[0]).to.have.property('method').and.to.equal('POST');
+ const requestContent = JSON.parse(request[0].data);
+ expect(requestContent).to.have.property('siteid').and.to.equal('1234');
+ expect(requestContent).to.have.property('pageid').and.to.equal('5678');
+ expect(requestContent).to.have.property('formatid').and.to.equal('91');
+ expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
+ expect(requestContent).to.have.property('bidfloor').and.to.equal(0.43);
+ expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid-outstream');
+ expect(requestContent).to.have.property('tagId').and.to.equal('sas_43');
+ expect(requestContent).to.not.have.property('pageDomain');
+ expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
+ expect(requestContent).to.have.property('buid').and.to.equal('7579');
+ expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
+ expect(requestContent).to.have.property('ckid').and.to.equal(43);
+ expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience);
+ expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext);
+ expect(requestContent).to.have.property('isVideo').and.to.equal(false);
+ expect(requestContent).to.have.property('videoData');
+ expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(7);
+ expect(requestContent.videoData).to.have.property('playerWidth').and.to.equal(800);
+ expect(requestContent.videoData).to.have.property('playerHeight').and.to.equal(600);
+ setCurrencyConfig({});
+ });
});
it('Verify outstream parse response', function () {
diff --git a/test/spec/modules/smarthubBidAdapter_spec.js b/test/spec/modules/smarthubBidAdapter_spec.js
index eb5fe58093d..302195ea944 100644
--- a/test/spec/modules/smarthubBidAdapter_spec.js
+++ b/test/spec/modules/smarthubBidAdapter_spec.js
@@ -139,11 +139,11 @@ describe('SmartHubBidAdapter', function () {
});
it('Returns valid URL', function () {
- expect(serverRequest.url).to.equal(`https://prebid.smart-hub.io/pbjs?partnerName=testname`);
+ expect(serverRequest.url).to.equal(`https://prebid.attekmi.com/pbjs?partnerName=testname`);
});
it('Returns valid URL if alias', function () {
- expect(requestAlias.url).to.equal(`https://${bidderAlias}-prebid.smart-hub.io/pbjs`);
+ expect(requestAlias.url).to.equal(`https://${bidderAlias}-prebid.attekmi.com/pbjs`);
});
it('Returns general data valid', function () {
diff --git a/test/spec/modules/snigelBidAdapter_spec.js b/test/spec/modules/snigelBidAdapter_spec.js
index 828aec9491c..69ab85ba825 100644
--- a/test/spec/modules/snigelBidAdapter_spec.js
+++ b/test/spec/modules/snigelBidAdapter_spec.js
@@ -181,6 +181,50 @@ describe('snigelBidAdapter', function () {
expect(data.placements[2].refresh.count).to.equal(1);
expect(data.placements[2].refresh.time).to.be.greaterThanOrEqual(0);
});
+
+ it('should increment auction counter upon every request', function () {
+ const bidderRequest = makeBidderRequest({});
+
+ let request = spec.buildRequests([], bidderRequest);
+ expect(request).to.have.property('data');
+ let data = JSON.parse(request.data);
+ const previousCounter = data.counter;
+
+ request = spec.buildRequests([], bidderRequest);
+ expect(request).to.have.property('data');
+ data = JSON.parse(request.data);
+ expect(data.counter).to.equal(previousCounter + 1);
+ });
+
+ it('should increment placement counter for each placement', function () {
+ const bidderRequest = Object.assign({}, BASE_BIDDER_REQUEST);
+ const topLeaderboard = makeBidRequest({adUnitCode: 'top_leaderboard', params: {placement: 'ros'}});
+ const bottomLeaderboard = makeBidRequest({adUnitCode: 'bottom_leaderboard', params: {placement: 'ros'}});
+ const sidebar = makeBidRequest({adUnitCode: 'sidebar', params: {placement: 'other'}});
+
+ let request = spec.buildRequests([topLeaderboard, bottomLeaderboard, sidebar], bidderRequest);
+ expect(request).to.have.property('data');
+ let data = JSON.parse(request.data);
+ const previousCounters = {};
+ data.placements.forEach((placement) => {
+ previousCounters[placement.name] = Math.max(previousCounters[placement.name] || 0, placement.counter);
+ });
+
+ request = spec.buildRequests([topLeaderboard, bottomLeaderboard, sidebar], bidderRequest);
+ expect(request).to.have.property('data');
+ data = JSON.parse(request.data);
+ expect(data).to.have.property('placements');
+ expect(data.placements.length).to.equal(3);
+ expect(data.placements[0].id).to.equal('top_leaderboard');
+ expect(previousCounters).to.have.property(data.placements[0].name);
+ expect(data.placements[0].counter).to.equal(previousCounters[data.placements[0].name] + 1);
+ expect(data.placements[1].id).to.equal('bottom_leaderboard');
+ expect(previousCounters).to.have.property(data.placements[1].name);
+ expect(data.placements[1].counter).to.equal(previousCounters[data.placements[1].name] + 2);
+ expect(data.placements[2].id).to.equal('sidebar');
+ expect(previousCounters).to.have.property(data.placements[2].name);
+ expect(data.placements[2].counter).to.equal(previousCounters[data.placements[2].name] + 1);
+ });
});
describe('interpretResponse', function () {
@@ -347,7 +391,7 @@ describe('snigelBidAdapter', function () {
expect(sync.url).to.equal(`https://somesyncurl?gdpr=1&gdpr_consent=${DUMMY_GDPR_CONSENT_STRING}`);
});
- it('should omit session ID if no device access', function() {
+ it('should omit session ID if no device access', function () {
const bidderRequest = makeBidderRequest();
const unregisterRule = registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'denyAccess', () => {
return {allow: false, reason: 'no consent'};
@@ -373,9 +417,9 @@ describe('snigelBidAdapter', function () {
},
vendor: {
consents: {[spec.gvlid]: true},
- }
+ },
},
- }
+ },
});
let request = spec.buildRequests([], baseBidderRequest);
expect(request).to.have.property('data');
@@ -388,25 +432,14 @@ describe('snigelBidAdapter', function () {
data = JSON.parse(request.data);
expect(data.gdprConsent).to.be.false;
- bidderRequest = {...baseBidderRequest, ...{gdprConsent: {vendorData: {vendor: {consents: {[spec.gvlid]: false}}}}}};
+ bidderRequest = {
+ ...baseBidderRequest,
+ ...{gdprConsent: {vendorData: {vendor: {consents: {[spec.gvlid]: false}}}}},
+ };
request = spec.buildRequests([], bidderRequest);
expect(request).to.have.property('data');
data = JSON.parse(request.data);
expect(data.gdprConsent).to.be.false;
});
-
- it('should increment auction counter upon every request', function() {
- const bidderRequest = makeBidderRequest({});
-
- let request = spec.buildRequests([], bidderRequest);
- expect(request).to.have.property('data');
- let data = JSON.parse(request.data);
- const previousCounter = data.counter;
-
- request = spec.buildRequests([], bidderRequest);
- expect(request).to.have.property('data');
- data = JSON.parse(request.data);
- expect(data.counter).to.equal(previousCounter + 1);
- });
});
});
diff --git a/test/spec/modules/sparteoBidAdapter_spec.js b/test/spec/modules/sparteoBidAdapter_spec.js
index e0b7349cb92..ec90d4c7eeb 100644
--- a/test/spec/modules/sparteoBidAdapter_spec.js
+++ b/test/spec/modules/sparteoBidAdapter_spec.js
@@ -59,6 +59,7 @@ const VALID_REQUEST_BANNER = {
url: REQUEST_URL,
data: {
'imp': [{
+ 'secure': 1,
'id': '1a2b3c4d',
'banner': {
'format': [{
@@ -81,7 +82,8 @@ const VALID_REQUEST_BANNER = {
'publisher': {
'ext': {
'params': {
- 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf'
+ 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf',
+ 'pbjsVersion': '$prebid.version$'
}
}
}
@@ -95,6 +97,7 @@ const VALID_REQUEST_VIDEO = {
url: REQUEST_URL,
data: {
'imp': [{
+ 'secure': 1,
'id': '5e6f7g8h',
'video': {
'w': 640,
@@ -123,7 +126,8 @@ const VALID_REQUEST_VIDEO = {
'publisher': {
'ext': {
'params': {
- 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf'
+ 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf',
+ 'pbjsVersion': '$prebid.version$'
}
}
}
@@ -137,6 +141,7 @@ const VALID_REQUEST = {
url: REQUEST_URL,
data: {
'imp': [{
+ 'secure': 1,
'id': '1a2b3c4d',
'banner': {
'format': [{
@@ -155,6 +160,7 @@ const VALID_REQUEST = {
}
}
}, {
+ 'secure': 1,
'id': '5e6f7g8h',
'video': {
'w': 640,
@@ -183,7 +189,8 @@ const VALID_REQUEST = {
'publisher': {
'ext': {
'params': {
- 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf'
+ 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf',
+ 'pbjsVersion': '$prebid.version$'
}
}
}
diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js
index 623faab5f1e..ceaad85faac 100644
--- a/test/spec/modules/sspBCBidAdapter_spec.js
+++ b/test/spec/modules/sspBCBidAdapter_spec.js
@@ -4,7 +4,8 @@ import * as utils from 'src/utils.js';
const BIDDER_CODE = 'sspBC';
const BIDDER_URL = 'https://ssp.wp.pl/bidder/';
-const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync';
+const SYNC_URL_IFRAME = 'https://ssp.wp.pl/bidder/usersync';
+const SYNC_URL_IMAGE = 'https://ssp.wp.pl/v1/sync/pixel';
describe('SSPBC adapter', function () {
function prepareTestData() {
@@ -649,6 +650,25 @@ describe('SSPBC adapter', function () {
expect(extAssets1).to.have.property('pbsize').that.equals('750x200_1')
expect(extAssets2).to.have.property('pbsize').that.equals('750x200_1')
});
+
+ it('should send supply chain data', function () {
+ const supplyChain = {
+ ver: '1.0',
+ complete: 1,
+ nodes: [
+ {
+ asi: 'first-seller.com',
+ sid: '00001',
+ hp: 1
+ },
+ ]
+ }
+ const bidWithSupplyChain = Object.assign(bids[0], { schain: supplyChain });
+ const requestWithSupplyChain = spec.buildRequests([bidWithSupplyChain], bidRequest);
+ const payloadWithSupplyChain = requestWithSupplyChain ? JSON.parse(requestWithSupplyChain.data) : { site: false, imp: false };
+
+ expect(payloadWithSupplyChain.source).to.have.property('schain').that.has.keys('ver', 'complete', 'nodes');
+ });
});
describe('interpretResponse', function () {
@@ -741,13 +761,18 @@ describe('SSPBC adapter', function () {
let syncResultImage = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true });
let syncResultNone = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false });
- it('should provide correct url, if frame sync is allowed', function () {
+ it('should provide correct iframe url, if frame sync is allowed', function () {
expect(syncResultAll).to.have.length(1);
- expect(syncResultAll[0].url).to.have.string(SYNC_URL);
+ expect(syncResultAll[0].url).to.have.string(SYNC_URL_IFRAME);
+ });
+
+ it('should provide correct image url, if image sync is allowed', function () {
+ expect(syncResultImage).to.have.length(1);
+ expect(syncResultImage[0].url).to.have.string(SYNC_URL_IMAGE);
});
- it('should send no syncs, if frame sync is not allowed', function () {
- expect(syncResultImage).to.have.length(0);
+ it('should send no syncs, if no sync is allowed', function () {
+ expect(syncResultNone).to.have.length(0);
expect(syncResultNone).to.have.length(0);
});
});
diff --git a/test/spec/modules/ssp_genieeBidAdapter_spec.js b/test/spec/modules/ssp_genieeBidAdapter_spec.js
index b0bad74d59b..dbf8ded5199 100644
--- a/test/spec/modules/ssp_genieeBidAdapter_spec.js
+++ b/test/spec/modules/ssp_genieeBidAdapter_spec.js
@@ -3,7 +3,7 @@ import {
spec,
BANNER_ENDPOINT,
} from 'modules/ssp_genieeBidAdapter.js';
-import { config } from '../../../src/config.js';
+import { config } from 'src/config.js';
describe('ssp_genieeBidAdapter', function () {
const ZONE_ID = 1234567;
@@ -128,9 +128,7 @@ describe('ssp_genieeBidAdapter', function () {
describe('QueryStringParameters', function () {
it('should sets the value of the zoneid query to bid.params.zoneId', function () {
const request = spec.buildRequests([BANNER_BID]);
- expect(String(request[0].data.zoneid)).to.have.string(
- `${BANNER_BID.params.zoneId}`
- );
+ expect(request[0].data.zoneid).to.deep.equal(BANNER_BID.params.zoneId);
});
it('should sets the values for loc and referer queries when bidderRequest.refererInfo.referer has a value', function () {
@@ -138,12 +136,8 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([BANNER_BID], {
refererInfo: { legacy: { referer: referer }, ref: referer },
});
- expect(String(request[0].data.loc)).to.have.string(
- `${referer}`
- );
- expect(String(request[0].data.referer)).to.have.string(
- `${referer}`
- );
+ expect(request[0].data.loc).to.deep.equal(referer);
+ expect(request[0].data.referer).to.deep.equal(referer);
});
it('should makes the values of loc query and referer query geparams value when bidderRequest.refererInfo.referer is a falsy value', function () {
@@ -156,12 +150,8 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([
getGeparamsDefinedBid(BANNER_BID, { loc: loc, ref: referer }),
]);
- expect(String(request[0].data.loc)).to.have.string(
- `${encodeURIComponent(loc)}`
- );
- expect(String(request[0].data.referer)).to.have.string(
- `${encodeURIComponent(referer)}`
- );
+ expect(request[0].data.loc).to.deep.equal(encodeURIComponent(loc));
+ expect(request[0].data.referer).to.deep.equal(encodeURIComponent(referer));
});
it('should sets the value of the ct0 query to geparams.ct0', function () {
@@ -172,12 +162,12 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([
getGeparamsDefinedBid(BANNER_BID, { ct0: ct0 }),
]);
- expect(String(request[0].data.ct0)).to.have.string(`${ct0}`);
+ expect(request[0].data.ct0).to.deep.equal(ct0);
});
it('should replaces currency with JPY if there is no currency provided', function () {
const request = spec.buildRequests([BANNER_BID]);
- expect(String(request[0].data.cur)).to.have.string('JPY');
+ expect(request[0].data.cur).to.deep.equal('JPY');
});
it('should makes currency the value of params.currency when params.currency exists', function () {
@@ -191,8 +181,8 @@ describe('ssp_genieeBidAdapter', function () {
params: { ...BANNER_BID.params, currency: 'USD' },
},
]);
- expect(String(request[0].data.cur)).to.have.string('JPY');
- expect(String(request[1].data.cur)).to.have.string('USD');
+ expect(request[0].data.cur).to.deep.equal('JPY');
+ expect(request[1].data.cur).to.deep.equal('USD');
});
it('should makes invalidImpBeacon the value of params.invalidImpBeacon when params.invalidImpBeacon exists (in current version, this parameter is not necessary and ib is always `0`)', function () {
@@ -210,9 +200,9 @@ describe('ssp_genieeBidAdapter', function () {
params: { ...BANNER_BID.params },
},
]);
- expect(String(request[0].data.ib)).to.have.string('0');
- expect(String(request[1].data.ib)).to.have.string('0');
- expect(String(request[2].data.ib)).to.have.string('0');
+ expect(request[0].data.ib).to.deep.equal(0);
+ expect(request[1].data.ib).to.deep.equal(0);
+ expect(request[2].data.ib).to.deep.equal(0);
});
it('should not sets the value of the adtk query when geparams.lat does not exist', function () {
@@ -227,7 +217,7 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([
getGeparamsDefinedBid(BANNER_BID, { lat: 1 }),
]);
- expect(String(request[0].data.adtk)).to.have.string('0');
+ expect(request[0].data.adtk).to.deep.equal('0');
});
it('should sets the value of the adtk query to 1 when geparams.lat is falsy value', function () {
@@ -237,7 +227,7 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([
getGeparamsDefinedBid(BANNER_BID, { lat: 0 }),
]);
- expect(String(request[0].data.adtk)).to.have.string('1');
+ expect(request[0].data.adtk).to.deep.equal('1');
});
it('should sets the value of the idfa query to geparams.idfa', function () {
@@ -248,7 +238,7 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([
getGeparamsDefinedBid(BANNER_BID, { idfa: idfa }),
]);
- expect(String(request[0].data.idfa)).to.have.string(`${idfa}`);
+ expect(request[0].data.idfa).to.deep.equal(idfa);
});
it('should set the sw query to screen.height and the sh query to screen.width when screen.width is greater than screen.height', function () {
@@ -258,8 +248,8 @@ describe('ssp_genieeBidAdapter', function () {
return { width: width, height: height };
});
const request = spec.buildRequests([BANNER_BID]);
- expect(String(request[0].data.sw)).to.have.string(`${height}`);
- expect(String(request[0].data.sh)).to.have.string(`${width}`);
+ expect(request[0].data.sw).to.deep.equal(height);
+ expect(request[0].data.sh).to.deep.equal(width);
stub.restore();
});
@@ -270,8 +260,8 @@ describe('ssp_genieeBidAdapter', function () {
return { width: width, height: height };
});
const request = spec.buildRequests([BANNER_BID]);
- expect(String(request[0].data.sw)).to.have.string(`${width}`);
- expect(String(request[0].data.sh)).to.have.string(`${height}`);
+ expect(request[0].data.sw).to.deep.equal(width);
+ expect(request[0].data.sh).to.deep.equal(height);
stub.restore();
});
@@ -354,28 +344,18 @@ describe('ssp_genieeBidAdapter', function () {
const request = spec.buildRequests([
getGeparamsDefinedBid(BANNER_BID, { bundle: bundle }),
]);
- expect(String(request[0].data.apid)).to.have.string(`${bundle}`);
+ expect(request[0].data.apid).to.deep.equal(bundle);
});
- it('should not include the extuid query when it does not contain the imuid cookie', function () {
- const stub = sinon.stub(document, 'cookie').get(function () {
- return '';
- });
+ it('should not include the extuid query when bid.userId.imuid does not exist', function () {
const request = spec.buildRequests([BANNER_BID]);
expect(request[0].data).to.not.have.property('extuid');
- stub.restore();
});
- it('should include an extuid query when it contains an imuid cookie', function () {
+ it('should include an extuid query when bid.userId.imuid exists', function () {
const imuid = 'b.a4ad1d3eeb51e600';
- const stub = sinon.stub(document, 'cookie').get(function () {
- return `_im_uid.3929=${imuid}`;
- });
- const request = spec.buildRequests([BANNER_BID]);
- expect(String(request[0].data.extuid)).to.have.string(
- `${`im:${imuid}`}`
- );
- stub.restore();
+ const request = spec.buildRequests([{...BANNER_BID, userId: {imuid}}]);
+ expect(request[0].data.extuid).to.deep.equal(`im:${imuid}`);
});
});
});
@@ -415,7 +395,7 @@ describe('ssp_genieeBidAdapter', function () {
};
const request = spec.buildRequests([BANNER_BID])[0];
const result = spec.interpretResponse({ body: response }, request);
- expect(result[0]).to.have.deep.equal(expectedBanner);
+ expect(result[0]).to.deep.equal(expectedBanner);
});
});
});
diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js
index 3cd9626885d..d186b0d5cd0 100644
--- a/test/spec/modules/stroeerCoreBidAdapter_spec.js
+++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js
@@ -935,6 +935,28 @@ describe('stroeerCore bid adapter', function () {
assert.deepEqual(serverRequestInfo.data.bids[0].sfp, {});
assert.isUndefined(serverRequestInfo.data.bids[1].sfp);
});
+
+ it('should add the ortb2 site extension', () => {
+ const bidReq = buildBidderRequest();
+
+ const ortb2 = {
+ site: {
+ domain: 'example.com',
+ ext: {
+ data: {
+ abc: '123'
+ }
+ }
+ }
+ };
+
+ bidReq.ortb2 = utils.deepClone(ortb2);
+
+ const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq);
+
+ const sentOrtb2 = serverRequestInfo.data.ortb2;
+ assert.deepEqual(sentOrtb2, {site: {ext: ortb2.site.ext}})
+ });
});
});
});
@@ -1000,6 +1022,16 @@ describe('stroeerCore bid adapter', function () {
assert.deepPropertyVal(result[0].meta, 'dsa', dsaResponse);
assert.propertyVal(result[1].meta, 'dsa', undefined);
});
+
+ it('should add campaignType to meta object', () => {
+ const response = buildBidderResponse();
+ response.bids[1] = Object.assign(response.bids[1], {campaignType: 'RTB'});
+
+ const result = spec.interpretResponse({body: response});
+
+ assert.propertyVal(result[0].meta, 'campaignType', undefined);
+ assert.propertyVal(result[1].meta, 'campaignType', 'RTB');
+ });
});
describe('get user syncs entry point', () => {
diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js
index bcf388a67e2..5db0c8cf306 100644
--- a/test/spec/modules/taboolaBidAdapter_spec.js
+++ b/test/spec/modules/taboolaBidAdapter_spec.js
@@ -181,6 +181,7 @@ describe('Taboola Adapter', function () {
const expectedData = {
'imp': [{
'id': res.data.imp[0].id,
+ 'secure': 1,
'banner': {
format: [{
w: displayBidRequestParams.sizes[0][0],
diff --git a/test/spec/modules/targetVideoBidAdapter_spec.js b/test/spec/modules/targetVideoBidAdapter_spec.js
index 442d7e7ef0b..61df5413862 100644
--- a/test/spec/modules/targetVideoBidAdapter_spec.js
+++ b/test/spec/modules/targetVideoBidAdapter_spec.js
@@ -1,4 +1,5 @@
import { spec } from '../../../modules/targetVideoBidAdapter.js'
+import { SYNC_URL } from '../../../libraries/targetVideoUtils/constants.js';
describe('TargetVideo Bid Adapter', function() {
const bidder = 'targetVideo';
@@ -237,4 +238,23 @@ describe('TargetVideo Bid Adapter', function() {
expect(payload.regs.ext.us_privacy).to.equal(uspConsentString);
expect(payload.regs.ext.gdpr).to.equal(1);
});
+
+ it('Test userSync have only one object and it should have a property type=iframe', function () {
+ let userSync = spec.getUserSyncs({ iframeEnabled: true });
+ expect(userSync).to.be.an('array');
+ expect(userSync.length).to.be.equal(1);
+ expect(userSync[0]).to.have.property('type');
+ expect(userSync[0].type).to.be.equal('iframe');
+ });
+
+ it('Test userSync valid sync url for iframe', function () {
+ let [userSync] = spec.getUserSyncs({ iframeEnabled: true }, {}, {consentString: 'anyString'});
+ expect(userSync.url).to.contain(SYNC_URL + 'load-cookie.html?endpoint=targetvideo&gdpr=0&gdpr_consent=anyString');
+ expect(userSync.type).to.be.equal('iframe');
+ });
+
+ it('Test userSyncs iframeEnabled=false', function () {
+ let userSyncs = spec.getUserSyncs({iframeEnabled: false});
+ expect(userSyncs).to.have.lengthOf(0);
+ });
});
diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js
index 6ebe533e260..c4f333e56ac 100644
--- a/test/spec/modules/userId_spec.js
+++ b/test/spec/modules/userId_spec.js
@@ -8,6 +8,7 @@ import {
init,
PBJS_USER_ID_OPTOUT_NAME,
startAuctionHook,
+ addUserIdsHook,
requestDataDeletion,
setStoredValue,
setSubmoduleRegistry,
@@ -27,6 +28,7 @@ import {sharedIdSystemSubmodule} from 'modules/sharedIdSystem.js';
import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js';
import * as mockGpt from '../integration/faker/googletag.js';
import 'src/prebid.js';
+import {startAuction} from 'src/prebid';
import {hook} from '../../../src/hook.js';
import {mockGdprConsent} from '../../helpers/consentData.js';
import {getPPID} from '../../../src/adserver.js';
@@ -175,6 +177,8 @@ describe('User ID', function () {
afterEach(() => {
sandbox.restore();
config.resetConfig();
+ startAuction.getHooks({hook: startAuctionHook}).remove();
+ startAuction.getHooks({hook: addUserIdsHook}).remove();
});
after(() => {
@@ -2423,6 +2427,58 @@ describe('User ID', function () {
})
})
});
+
+ describe('submodules not added', () => {
+ const eid = {
+ source: 'example.com',
+ uids: [{id: '1234', atype: 3}]
+ };
+ let adUnits;
+ let startAuctionStub;
+ function saHook(fn, ...args) {
+ return startAuctionStub(...args);
+ }
+ beforeEach(() => {
+ adUnits = [{code: 'au1', bids: [{bidder: 'sampleBidder'}]}];
+ startAuctionStub = sinon.stub();
+ startAuction.before(saHook);
+ config.resetConfig();
+ });
+ afterEach(() => {
+ startAuction.getHooks({hook: saHook}).remove();
+ })
+
+ it('addUserIdsHook', function (done) {
+ addUserIdsHook(function () {
+ adUnits.forEach(unit => {
+ unit.bids.forEach(bid => {
+ expect(bid).to.have.deep.nested.property('userIdAsEids.0.source');
+ expect(bid).to.have.deep.nested.property('userIdAsEids.0.uids.0.id');
+ expect(bid.userIdAsEids[0].source).to.equal('example.com');
+ expect(bid.userIdAsEids[0].uids[0].id).to.equal('1234');
+ });
+ });
+ done();
+ }, {
+ adUnits,
+ ortb2Fragments: {
+ global: {user: {ext: {eids: [eid]}}},
+ bidder: {}
+ }
+ });
+ });
+
+ it('should add userIdAsEids and merge ortb2.user.ext.eids even if no User ID submodules', () => {
+ init(config);
+ config.setConfig({
+ ortb2: {user: {ext: {eids: [eid]}}}
+ })
+ expect(startAuction.getHooks({hook: startAuctionHook}).length).equal(0);
+ expect(startAuction.getHooks({hook: addUserIdsHook}).length).equal(1);
+ $$PREBID_GLOBAL$$.requestBids({adUnits});
+ sinon.assert.calledWith(startAuctionStub, sinon.match.hasNested('adUnits[0].bids[0].userIdAsEids[0]', eid));
+ });
+ });
});
describe('handles config with ESP configuration in user sync object', function() {
diff --git a/test/spec/modules/utiqIdSystem_spec.js b/test/spec/modules/utiqIdSystem_spec.js
index 62754d39fa3..ef8e4efc5c5 100644
--- a/test/spec/modules/utiqIdSystem_spec.js
+++ b/test/spec/modules/utiqIdSystem_spec.js
@@ -4,6 +4,7 @@ import { storage } from 'modules/utiqIdSystem.js';
describe('utiqIdSystem', () => {
const utiqPassKey = 'utiqPass';
+ const netIdKey = 'netid_utiq_adtechpass';
const getStorageData = (idGraph) => {
if (!idGraph) {
@@ -185,4 +186,42 @@ describe('utiqIdSystem', () => {
});
});
});
+
+ describe('utiq getUtiqFromStorage', () => {
+ afterEach(() => {
+ storage.removeDataFromLocalStorage(utiqPassKey);
+ });
+
+ it(`correctly set utiqPassKey as adtechpass utiq value for ${netIdKey} empty`, (done) => {
+ // given
+ storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData({
+ 'domain': 'TEST DOMAIN',
+ 'atid': 'TEST ATID',
+ }))); // setting idGraph
+ storage.setDataInLocalStorage(netIdKey, ''); // setting an empty value
+
+ // when
+ const response = utiqIdSubmodule.getId();
+
+ // then
+ expect(response.id.utiq).to.be.equal('TEST ATID');
+ done();
+ });
+
+ it(`correctly set netIdAdtechpass as adtechpass utiq value for ${netIdKey} settled`, (done) => {
+ // given
+ storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData({
+ 'domain': 'TEST DOMAIN',
+ 'atid': 'TEST ATID',
+ }))); // setting idGraph
+ storage.setDataInLocalStorage(netIdKey, 'testNetIdValue'); // setting a correct value
+
+ // when
+ const response = utiqIdSubmodule.getId();
+
+ // then
+ expect(response.id.utiq).to.be.equal('testNetIdValue');
+ done();
+ });
+ });
});
diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js
index b1cfa606d84..e7f153fa237 100644
--- a/test/spec/modules/vdoaiBidAdapter_spec.js
+++ b/test/spec/modules/vdoaiBidAdapter_spec.js
@@ -1,11 +1,19 @@
import {assert, expect} from 'chai';
import {spec} from 'modules/vdoaiBidAdapter.js';
import {newBidder} from 'src/adapters/bidderFactory.js';
+import { config } from 'src/config.js';
-const ENDPOINT_URL = 'https://prebid.vdo.ai/auction';
+const ENDPOINT_URL = 'https://prebid-v2.vdo.ai/auction';
describe('vdoaiBidAdapter', function () {
const adapter = newBidder(spec);
+ const sandbox = sinon.sandbox.create();
+ beforeEach(function() {
+ sandbox.stub(config, 'getConfig').withArgs('coppa').returns(true);
+ });
+ afterEach(function() {
+ sandbox.restore();
+ });
describe('isBidRequestValid', function () {
let bid = {
'bidder': 'vdoai',
@@ -20,16 +28,33 @@ describe('vdoaiBidAdapter', function () {
'bidderRequestId': '1234asdf1234asdf',
'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120'
};
+ let invalidParams = {
+ 'bidder': 'vdoai',
+ 'params': {
+ placementId: false
+ },
+ 'adUnitCode': 'adunit-code',
+ 'sizes': [
+ [300, 250]
+ ],
+ 'bidId': '1234asdf1234',
+ 'bidderRequestId': '1234asdf1234asdf',
+ 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120'
+ };
it('should return true where required params found', function () {
expect(spec.isBidRequestValid(bid)).to.equal(true);
});
+ it('should return false where required params not found', function () {
+ expect(spec.isBidRequestValid(invalidParams)).to.equal(false);
+ });
});
describe('buildRequests', function () {
let bidRequests = [
{
'bidder': 'vdoai',
'params': {
- placementId: 'testPlacementId'
+ placementId: 'testPlacementId',
+ bidFloor: 0.1
},
'sizes': [
[300, 250]
@@ -37,16 +62,84 @@ describe('vdoaiBidAdapter', function () {
'bidId': '23beaa6af6cdde',
'bidderRequestId': '19c0c1efdf37e7',
'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1',
- 'mediaTypes': 'banner'
+ 'mediaType': 'banner',
+ 'adUnitCode': '1234',
+ 'mediaTypes': {
+ banner: {
+ sizes: [300, 250]
+ }
+ }
+ },
+ {
+ 'bidder': 'vdoai',
+ 'params': {
+ placementId: 'testPlacementId',
+ bidFloor: 0.1
+ },
+ 'width': '300',
+ 'height': '200',
+ 'bidId': 'bidId123',
+ 'referer': 'www.example.com',
+ 'mediaType': 'video',
+ 'mediaTypes': {
+ video: {
+ context: 'instream',
+ playerSize: [[640, 360]]
+ }
+ }
}
];
let bidderRequests = {
+ timeout: 3000,
'refererInfo': {
'numIframes': 0,
'reachedTop': true,
'referer': 'https://example.com',
- 'stack': ['https://example.com']
+ 'stack': ['https://example.com'],
+ 'page': 'example.com',
+ 'ref': 'example2.com'
+ },
+ 'ortb2': {
+ source: {
+ tid: 123456789
+ },
+ 'site': {
+ 'domain': 'abc.com',
+ 'publisher': {
+ 'domain': 'abc.com'
+ }
+ }
+ },
+ 'ortb2Imp': {
+ ext: {
+ tid: '12345',
+ gpid: '1234'
+ }
+ },
+ gdprConsent: {
+ consentString: 'abc',
+ gdprApplies: true,
+ addtlConsent: 'xyz'
+ },
+ gppConsent: {
+ gppString: 'abcd',
+ applicableSections: ''
+ },
+ uspConsent: {
+ uspConsent: '12345'
+ },
+ userIdAsEids: {},
+ schain: {
+ 'ver': '1.0',
+ 'complete': 1,
+ 'nodes': [
+ {
+ 'asi': 'vdo.ai',
+ 'sid': '4359',
+ 'hp': 1
+ }
+ ]
}
};
@@ -57,6 +150,44 @@ describe('vdoaiBidAdapter', function () {
it('attaches source and version to endpoint URL as query params', function () {
expect(request[0].url).to.equal(ENDPOINT_URL);
});
+ it('should contain all keys', function() {
+ expect(request[0].data.pageInfo).to.include.all.keys('location', 'referrer', 'stack', 'numIframes', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'xOffset', 'yOffset', 'version');
+ })
+ it('should return empty array if no valid bid was passed', function () {
+ expect(spec.buildRequests([], bidderRequests)).to.be.empty;
+ });
+ it('should not send invalid schain', function () {
+ delete bidderRequests.schain.nodes[0].asi;
+ let result = spec.buildRequests(bidRequests, bidderRequests);
+ expect(result[0].data.schain).to.be.undefined;
+ });
+ it('should not send invalid schain', function () {
+ delete bidderRequests.schain;
+ let result = spec.buildRequests(bidRequests, bidderRequests);
+ expect(result[0].data.schain).to.be.undefined;
+ });
+ it('should check for correct sizes', function () {
+ delete bidRequests[1].mediaTypes.video.playerSize;
+ let result = spec.buildRequests(bidRequests, bidderRequests);
+ expect(result[1].data.playerSize).to.be.empty;
+ });
+ it('should not pass undefined in GDPR string', function () {
+ delete bidderRequests.gdprConsent;
+ let result = spec.buildRequests(bidRequests, bidderRequests);
+ expect(result[0].data.gdprConsent).to.be.undefined;
+ });
+
+ it('should not pass undefined in gppConsent', function () {
+ delete bidderRequests.gppConsent;
+ let result = spec.buildRequests(bidRequests, bidderRequests);
+ expect(result[0].data.gppConsent).to.be.undefined;
+ });
+
+ it('should not pass undefined in uspConsent', function () {
+ delete bidderRequests.uspConsent;
+ let result = spec.buildRequests(bidRequests, bidderRequests);
+ expect(result[0].data.uspConsent).to.be.undefined;
+ });
});
describe('interpretResponse', function () {
@@ -75,29 +206,31 @@ describe('vdoaiBidAdapter', function () {
}
];
let serverResponse = {
- body: {
- 'vdoCreative': 'I am an ad
',
- 'price': 4.2,
- 'adid': '12345asdfg',
- 'currency': 'EUR',
- 'statusMessage': 'Bid available',
- 'requestId': 'bidId123',
- 'width': 300,
- 'height': 250,
- 'netRevenue': true,
- 'adDomain': ['text.abc']
+ body:
+ {
+ 'price': 2,
+ 'adid': 'test-ad',
+ 'adomain': [
+ 'text.abc'
+ ],
+ 'w': 300,
+ 'h': 250,
+ 'vdoCreative': 'I am an ad
',
+ 'bidId': '31d1375caab87a',
+ 'mediaType': 'banner'
}
};
+
it('should get the correct bid response', function () {
let expectedResponse = [{
- 'requestId': 'bidId123',
- 'cpm': 4.2,
+ 'requestId': '31d1375caab87a',
+ 'cpm': 2,
'width': 300,
'height': 250,
- 'creativeId': '12345asdfg',
- 'currency': 'EUR',
+ 'creativeId': 'test-ad',
+ 'currency': 'USD',
'netRevenue': true,
- 'ttl': 3000,
+ 'ttl': 60,
'ad': 'I am an ad
',
'meta': {
'advertiserDomains': ['text.abc']
@@ -112,9 +245,9 @@ describe('vdoaiBidAdapter', function () {
let serverResponse = {
body: {
'vdoCreative': '',
- 'price': 4.2,
+ 'price': 2,
'adid': '12345asdfg',
- 'currency': 'EUR',
+ 'currency': 'USD',
'statusMessage': 'Bid available',
'requestId': 'bidId123',
'width': 300,
@@ -133,7 +266,13 @@ describe('vdoaiBidAdapter', function () {
'height': '200',
'bidId': 'bidId123',
'referer': 'www.example.com',
- 'mediaType': 'video'
+ 'mediaType': 'video',
+ 'mediaTypes': {
+ video: {
+ context: 'instream',
+ playerSize: [[640, 360]]
+ }
+ }
}
}
];
@@ -142,5 +281,131 @@ describe('vdoaiBidAdapter', function () {
expect(result[0]).to.have.property('vastXml');
expect(result[0]).to.have.property('mediaType', 'video');
});
+ it('should not return invalid responses', function() {
+ serverResponse.body.w = 0;
+ let result = spec.interpretResponse(serverResponse, bidRequest[0]);
+ expect(result).to.be.empty;
+ });
+ it('should not return invalid responses with invalid height', function() {
+ serverResponse.body.w = 300;
+ serverResponse.body.h = 0;
+ let result = spec.interpretResponse(serverResponse, bidRequest[0]);
+ expect(result).to.be.empty;
+ });
+ it('should not return invalid responses with invalid cpm', function() {
+ serverResponse.body.h = 250;
+ serverResponse.body.price = 0;
+ let result = spec.interpretResponse(serverResponse, bidRequest[0]);
+ expect(result).to.be.empty;
+ });
+ it('should not return invalid responses with invalid creative ID', function() {
+ serverResponse.body.price = 2;
+ serverResponse.body.adid = undefined;
+ let result = spec.interpretResponse(serverResponse, bidRequest[0]);
+ expect(result).to.be.empty;
+ });
+ });
+ describe('getUserSyncs', function() {
+ it('should return correct sync urls', function() {
+ let serverResponse = [{
+ body: {
+ 'vdoCreative': '',
+ 'price': 2,
+ 'adid': '12345asdfg',
+ 'currency': 'USD',
+ 'statusMessage': 'Bid available',
+ 'requestId': 'bidId123',
+ 'width': 300,
+ 'height': 250,
+ 'netRevenue': true,
+ 'mediaType': 'video',
+ 'cookiesync': {
+ 'status': 'no_cookie',
+ 'bidder_status': [
+ {
+ 'bidder': 'vdoai',
+ 'no_cookie': true,
+ 'usersync': {
+ 'url': 'https://rtb.vdo.ai/setuid/',
+ 'type': 'iframe'
+ }
+ }
+ ]
+ }
+ }
+ }];
+
+ let syncUrls = spec.getUserSyncs({
+ iframeEnabled: true
+ }, serverResponse);
+ expect(syncUrls[0].url).to.be.equal(serverResponse[0].body.cookiesync.bidder_status[0].usersync.url);
+
+ syncUrls = spec.getUserSyncs({
+ iframeEnabled: false
+ }, serverResponse);
+ expect(syncUrls[0]).to.be.undefined;
+ });
+ it('should not return invalid sync urls', function() {
+ let serverResponse = [{
+ body: {
+ 'vdoCreative': '',
+ 'price': 2,
+ 'adid': '12345asdfg',
+ 'currency': 'USD',
+ 'statusMessage': 'Bid available',
+ 'requestId': 'bidId123',
+ 'width': 300,
+ 'height': 250,
+ 'netRevenue': true,
+ 'mediaType': 'video',
+ 'cookiesync': {
+ 'status': 'no_cookie',
+ 'bidder_status': [
+ ]
+ }
+ }
+ }];
+
+ var syncUrls = spec.getUserSyncs({
+ iframeEnabled: true
+ }, serverResponse);
+ expect(syncUrls[0]).to.be.undefined;
+
+ delete serverResponse[0].body.cookiesync.bidder_status;
+ syncUrls = spec.getUserSyncs({
+ iframeEnabled: true
+ }, serverResponse);
+ expect(syncUrls[0]).to.be.undefined;
+
+ delete serverResponse[0].body.cookiesync;
+ syncUrls = spec.getUserSyncs({
+ iframeEnabled: true
+ }, serverResponse);
+ expect(syncUrls[0]).to.be.undefined;
+
+ delete serverResponse[0].body;
+ syncUrls = spec.getUserSyncs({
+ iframeEnabled: true
+ }, serverResponse);
+ expect(syncUrls[0]).to.be.undefined;
+ });
+ });
+
+ describe('onTimeout', function() {
+ it('should run without errors', function() {
+ spec.onTimeout();
+ });
+ });
+
+ describe('onBidWon', function() {
+ it('should run without errors', function() {
+ spec.onBidWon();
+ });
+ });
+
+ describe('onSetTargeting', function() {
+ it('should run without errors', function() {
+ spec.onSetTargeting();
+ });
});
});
diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js
index db928e4d802..923ff7e86b2 100755
--- a/test/spec/modules/visxBidAdapter_spec.js
+++ b/test/spec/modules/visxBidAdapter_spec.js
@@ -5,6 +5,8 @@ import { newBidder } from 'src/adapters/bidderFactory.js';
import * as utils from 'src/utils.js';
import { makeSlot } from '../integration/faker/googletag.js';
import { mergeDeep } from '../../../src/utils.js';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency.js';
+import { addFPDToBidderRequest } from '../../helpers/fpd.js';
describe('VisxAdapter', function () {
const adapter = newBidder(spec);
@@ -355,8 +357,7 @@ describe('VisxAdapter', function () {
});
it('should add currency from currency.bidderCurrencyDefault', function () {
- const getConfigStub = sinon.stub(config, 'getConfig').callsFake(
- arg => arg === 'currency.bidderCurrencyDefault.visx' ? 'GBP' : 'USD');
+ config.setConfig({currency: {bidderCurrencyDefault: {visx: 'GBP'}}})
const request = spec.buildRequests(bidRequests, bidderRequest);
const payload = parseRequest(request.url);
expect(payload).to.be.an('object');
@@ -410,66 +411,22 @@ describe('VisxAdapter', function () {
}
});
- getConfigStub.restore();
+ config.resetConfig();
});
it('should add currency from currency.adServerCurrency', function () {
- const getConfigStub = sinon.stub(config, 'getConfig').callsFake(
- arg => arg === 'currency.bidderCurrencyDefault.visx' ? '' : 'USD');
- const request = spec.buildRequests(bidRequests, bidderRequest);
- const payload = parseRequest(request.url);
- expect(payload).to.be.an('object');
- expect(payload).to.have.property('auids', '903535,903535,903536,903537');
-
- const postData = request.data;
- expect(postData).to.be.an('object');
- expect(postData).to.deep.equal({
- 'id': '22edbae2733bf6',
- 'imp': expectedFullImps,
- 'tmax': 3000,
- 'cur': ['USD'],
- 'source': {'ext': {'wrapperType': 'Prebid_js', 'wrapperVersion': '$prebid.version$'}},
- 'site': {
- 'domain': 'localhost:9999',
- 'publisher': {
- 'domain': 'localhost:9999'
- },
- 'page': 'http://localhost:9999/integrationExamples/gpt/hello_world.html'
- },
- 'device': {
- 'w': 1259,
- 'h': 934,
- 'dnt': 0,
- 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
- 'language': 'tr',
- 'sua': {
- 'source': 1,
- 'platform': {
- 'brand': 'macOS'
- },
- 'browsers': [
- {
- 'brand': 'Chromium',
- 'version': [ '124' ]
- },
- {
- 'brand': 'Google Chrome',
- 'version': [ '124' ]
- },
- {
- 'brand': 'Not-A.Brand',
- 'version': [ '99' ]
- }
- ],
- 'mobile': 0
- },
- 'ext': {
- 'cdep': 'treatment_1.1'
- }
- },
+ setCurrencyConfig({ adServerCurrency: 'USD' })
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ const request = spec.buildRequests(bidRequests, res);
+ const payload = parseRequest(request.url);
+ expect(payload).to.be.an('object');
+ expect(payload).to.have.property('auids', '903535,903535,903536,903537');
+
+ const postData = request.data;
+ expect(postData).to.be.an('object');
+ expect(postData.cur).to.deep.equal(['USD']);
+ setCurrencyConfig({})
});
-
- getConfigStub.restore();
});
it('if gdprConsent is present payload must have gdpr params', function () {
diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js
index 5f4ada06c65..65752837b23 100644
--- a/test/spec/modules/voxBidAdapter_spec.js
+++ b/test/spec/modules/voxBidAdapter_spec.js
@@ -1,6 +1,7 @@
import { expect } from 'chai'
import { spec } from 'modules/voxBidAdapter.js'
-import {config} from 'src/config.js'
+import { setConfig as setCurrencyConfig } from '../../../modules/currency'
+import { addFPDToBidderRequest } from '../../helpers/fpd'
function getSlotConfigs(mediaTypes, params) {
return {
@@ -247,14 +248,17 @@ describe('VOX Adapter', function() {
})
it('should request floor price in adserver currency', function () {
- const configCurrency = 'DKK'
- config.setConfig({ currency: { adServerCurrency: configCurrency } })
- const request = spec.buildRequests([ getBidWithFloor() ], bidderRequest)
- const data = JSON.parse(request.data)
- data.bidRequests.forEach(bid => {
- expect(bid.floorInfo.currency).to.equal(configCurrency)
- })
- })
+ const configCurrency = 'DKK';
+ setCurrencyConfig({ adServerCurrency: configCurrency });
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ const request = spec.buildRequests([ getBidWithFloor() ], res)
+ const data = JSON.parse(request.data)
+ data.bidRequests.forEach(bid => {
+ expect(bid.floorInfo.currency).to.equal(configCurrency)
+ })
+ setCurrencyConfig({});
+ });
+ });
function getBidWithFloor(floor) {
return {
diff --git a/test/spec/modules/wurflRtdProvider_spec.js b/test/spec/modules/wurflRtdProvider_spec.js
index 434abfc4e22..0ac324ef8b2 100644
--- a/test/spec/modules/wurflRtdProvider_spec.js
+++ b/test/spec/modules/wurflRtdProvider_spec.js
@@ -3,6 +3,7 @@ import {
enrichBidderRequest,
lowEntropyData,
wurflSubmodule,
+ makeOrtb2DeviceType,
} from 'modules/wurflRtdProvider';
import * as ajaxModule from 'src/ajax';
import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js';
@@ -10,50 +11,49 @@ import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js';
describe('wurflRtdProvider', function () {
describe('wurflSubmodule', function () {
const altHost = 'http://example.local/wurfl.js';
+
const wurfl_pbjs = {
- low_entropy_caps: ['complete_device_name', 'form_factor', 'is_mobile'],
- caps: [
- 'advertised_browser',
- 'advertised_browser_version',
- 'advertised_device_os',
- 'advertised_device_os_version',
- 'brand_name',
- 'complete_device_name',
- 'form_factor',
- 'is_app_webview',
- 'is_full_desktop',
- 'is_mobile',
- 'is_robot',
- 'is_smartphone',
- 'is_smarttv',
- 'is_tablet',
- 'manufacturer_name',
- 'marketing_name'
- ],
+ low_entropy_caps: ['is_mobile', 'complete_device_name', 'form_factor'],
+ caps: ['advertised_browser', 'advertised_browser_version', 'advertised_device_os', 'advertised_device_os_version', 'ajax_support_javascript', 'brand_name', 'complete_device_name', 'density_class', 'form_factor', 'is_android', 'is_app_webview', 'is_connected_tv', 'is_full_desktop', 'is_ios', 'is_mobile', 'is_ott', 'is_phone', 'is_robot', 'is_smartphone', 'is_smarttv', 'is_tablet', 'manufacturer_name', 'marketing_name', 'max_image_height', 'max_image_width', 'model_name', 'physical_screen_height', 'physical_screen_width', 'pixel_density', 'pointing_method', 'resolution_height', 'resolution_width'],
authorized_bidders: {
- 'bidder1': [0, 1, 2, 3, 4, 5, 6, 7, 10, 13, 15],
- 'bidder2': [5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
+ bidder1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
+ bidder2: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21, 25, 28, 30, 31]
}
}
-
const WURFL = {
- advertised_browser: 'Chrome',
- advertised_browser_version: '125.0.6422.76',
- advertised_device_os: 'Linux',
- advertised_device_os_version: '6.5.0',
+ advertised_browser: 'Chrome Mobile',
+ advertised_browser_version: '130.0.0.0',
+ advertised_device_os: 'Android',
+ advertised_device_os_version: '6.0',
+ ajax_support_javascript: !0,
brand_name: 'Google',
- complete_device_name: 'Google Chrome',
- form_factor: 'Desktop',
+ complete_device_name: 'Google Nexus 5',
+ density_class: '3.0',
+ form_factor: 'Feature Phone',
+ is_android: !0,
is_app_webview: !1,
- is_full_desktop: !0,
- is_mobile: !1,
+ is_connected_tv: !1,
+ is_full_desktop: !1,
+ is_ios: !1,
+ is_mobile: !0,
+ is_ott: !1,
+ is_phone: !0,
is_robot: !1,
is_smartphone: !1,
is_smarttv: !1,
is_tablet: !1,
- manufacturer_name: '',
+ manufacturer_name: 'LG',
marketing_name: '',
- }
+ max_image_height: 640,
+ max_image_width: 360,
+ model_name: 'Nexus 5',
+ physical_screen_height: 110,
+ physical_screen_width: 62,
+ pixel_density: 443,
+ pointing_method: 'touchscreen',
+ resolution_height: 1920,
+ resolution_width: 1080
+ };
// expected analytics values
const expectedStatsURL = 'https://prebid.wurflcloud.com/v1/prebid/stats';
@@ -61,11 +61,11 @@ describe('wurflRtdProvider', function () {
let sandbox;
- beforeEach(function() {
+ beforeEach(function () {
sandbox = sinon.createSandbox();
window.WURFLPromises = {
- init: new Promise(function(resolve, reject) { resolve({ WURFL, wurfl_pbjs }) }),
- complete: new Promise(function(resolve, reject) { resolve({ WURFL, wurfl_pbjs }) }),
+ init: new Promise(function (resolve, reject) { resolve({ WURFL, wurfl_pbjs }) }),
+ complete: new Promise(function (resolve, reject) { resolve({ WURFL, wurfl_pbjs }) }),
};
});
@@ -85,6 +85,9 @@ describe('wurflRtdProvider', function () {
]
}],
ortb2Fragments: {
+ global: {
+ device: {},
+ },
bidder: {},
}
};
@@ -99,56 +102,113 @@ describe('wurflRtdProvider', function () {
expectedURL.searchParams.set('mode', 'prebid');
const callback = () => {
- expect(reqBidsConfigObj.ortb2Fragments.bidder).to.deep.equal({
+ const v = {
bidder1: {
device: {
+ make: 'Google',
+ model: 'Nexus 5',
+ devicetype: 1,
+ os: 'Android',
+ osv: '6.0',
+ hwv: 'Nexus 5',
+ h: 1920,
+ w: 1080,
+ ppi: 443,
+ pxratio: '3.0',
+ js: true,
ext: {
wurfl: {
- advertised_browser: 'Chrome',
- advertised_browser_version: '125.0.6422.76',
- advertised_device_os: 'Linux',
- advertised_device_os_version: '6.5.0',
+ advertised_browser: 'Chrome Mobile',
+ advertised_browser_version: '130.0.0.0',
+ advertised_device_os: 'Android',
+ advertised_device_os_version: '6.0',
+ ajax_support_javascript: !0,
brand_name: 'Google',
- complete_device_name: 'Google Chrome',
- form_factor: 'Desktop',
+ complete_device_name: 'Google Nexus 5',
+ density_class: '3.0',
+ form_factor: 'Feature Phone',
is_app_webview: !1,
+ is_connected_tv: !1,
+ is_full_desktop: !1,
+ is_mobile: !0,
+ is_ott: !1,
+ is_phone: !0,
is_robot: !1,
+ is_smartphone: !1,
+ is_smarttv: !1,
is_tablet: !1,
+ manufacturer_name: 'LG',
marketing_name: '',
+ max_image_height: 640,
+ max_image_width: 360,
+ model_name: 'Nexus 5',
+ physical_screen_height: 110,
+ physical_screen_width: 62,
+ pixel_density: 443,
+ pointing_method: 'touchscreen',
+ resolution_height: 1920,
+ resolution_width: 1080
},
},
},
},
bidder2: {
device: {
+ make: 'Google',
+ model: 'Nexus 5',
+ devicetype: 1,
+ os: 'Android',
+ osv: '6.0',
+ hwv: 'Nexus 5',
+ h: 1920,
+ w: 1080,
+ ppi: 443,
+ pxratio: '3.0',
+ js: true,
ext: {
wurfl: {
- complete_device_name: 'Google Chrome',
- form_factor: 'Desktop',
+ advertised_device_os: 'Android',
+ advertised_device_os_version: '6.0',
+ ajax_support_javascript: !0,
+ brand_name: 'Google',
+ complete_device_name: 'Google Nexus 5',
+ density_class: '3.0',
+ form_factor: 'Feature Phone',
+ is_android: !0,
is_app_webview: !1,
- is_full_desktop: !0,
- is_mobile: !1,
- is_robot: !1,
- is_smartphone: !1,
- is_smarttv: !1,
+ is_connected_tv: !1,
+ is_full_desktop: !1,
+ is_ios: !1,
+ is_mobile: !0,
+ is_ott: !1,
+ is_phone: !0,
is_tablet: !1,
- manufacturer_name: '',
+ manufacturer_name: 'LG',
+ model_name: 'Nexus 5',
+ pixel_density: 443,
+ resolution_height: 1920,
+ resolution_width: 1080
},
},
},
},
bidder3: {
device: {
+ make: 'Google',
+ model: 'Nexus 5',
ext: {
wurfl: {
- complete_device_name: 'Google Chrome',
- form_factor: 'Desktop',
- is_mobile: !1,
+ complete_device_name: 'Google Nexus 5',
+ form_factor: 'Feature Phone',
+ is_mobile: !0,
+ model_name: 'Nexus 5',
+ brand_name: 'Google',
},
},
},
},
- });
+ };
+ expect(reqBidsConfigObj.ortb2Fragments.bidder).to.deep.equal(v);
done();
};
@@ -245,12 +305,16 @@ describe('wurflRtdProvider', function () {
complete_device_name: 'Apple iPhone X',
form_factor: 'Smartphone',
is_mobile: !0,
+ brand_name: 'Apple',
+ model_name: 'iPhone X',
};
const lowEntropyCaps = ['complete_device_name', 'form_factor', 'is_mobile'];
const expectedData = {
complete_device_name: 'Apple iPhone',
form_factor: 'Smartphone',
is_mobile: !0,
+ brand_name: 'Apple',
+ model_name: 'iPhone',
};
const result = lowEntropyData(wjsData, lowEntropyCaps);
expect(result).to.deep.equal(expectedData);
@@ -289,6 +353,9 @@ describe('wurflRtdProvider', function () {
it('should enrich the bidder request with WURFL data', () => {
const reqBidsConfigObj = {
ortb2Fragments: {
+ global: {
+ device: {},
+ },
bidder: {
exampleBidder: {
device: {
@@ -321,4 +388,66 @@ describe('wurflRtdProvider', function () {
});
});
});
+
+ describe('makeOrtb2DeviceType', function () {
+ it('should return 1 when wurflData is_mobile and is_phone is true', function () {
+ const wurflData = { is_mobile: true, is_phone: true, is_tablet: false };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(1);
+ });
+
+ it('should return 1 when wurflData is_mobile and is_tablet is true', function () {
+ const wurflData = { is_mobile: true, is_phone: false, is_tablet: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(1);
+ });
+
+ it('should return 6 when wurflData is_mobile but is_phone and is_tablet are false', function () {
+ const wurflData = { is_mobile: true, is_phone: false, is_tablet: false };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(6);
+ });
+
+ it('should return 2 when wurflData is_full_desktop is true', function () {
+ const wurflData = { is_full_desktop: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(2);
+ });
+
+ it('should return 3 when wurflData is_connected_tv is true', function () {
+ const wurflData = { is_connected_tv: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(3);
+ });
+
+ it('should return 4 when wurflData is_phone is true and is_mobile is false or undefined', function () {
+ const wurflData = { is_phone: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(4);
+ });
+
+ it('should return 5 when wurflData is_tablet is true and is_mobile is false or undefined', function () {
+ const wurflData = { is_tablet: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(5);
+ });
+
+ it('should return 7 when wurflData is_ott is true', function () {
+ const wurflData = { is_ott: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.equal(7);
+ });
+
+ it('should return undefined when wurflData is_mobile is true but is_phone and is_tablet are missing', function () {
+ const wurflData = { is_mobile: true };
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.be.undefined;
+ });
+
+ it('should return undefined when no conditions are met', function () {
+ const wurflData = {};
+ const result = makeOrtb2DeviceType(wurflData);
+ expect(result).to.be.undefined;
+ });
+ });
});
diff --git a/test/spec/modules/yandexAnalyticsAdapter_spec.js b/test/spec/modules/yandexAnalyticsAdapter_spec.js
index ca9b29d13a5..a7a850b7cf2 100644
--- a/test/spec/modules/yandexAnalyticsAdapter_spec.js
+++ b/test/spec/modules/yandexAnalyticsAdapter_spec.js
@@ -1,7 +1,8 @@
import * as sinon from 'sinon';
-import yandexAnalytics, { EVENTS_TO_TRACK } from 'modules/yandexAnalyticsAdapter.js';
+import yandexAnalytics, { EVENTS_TO_TRACK, PBJS_INIT_EVENT_NAME } from 'modules/yandexAnalyticsAdapter.js';
import * as log from '../../../src/utils.js'
import * as events from '../../../src/events.js';
+import * as globalUtils from '../../../src/prebidGlobal.js';
describe('Yandex analytics adapter testing', () => {
const sandbox = sinon.createSandbox();
@@ -11,6 +12,13 @@ describe('Yandex analytics adapter testing', () => {
let onEvent;
const counterId = 123;
const counterWindowKey = 'yaCounter123';
+ const prebidVersion = '123.0';
+ const prebidInitEvent = {
+ event: PBJS_INIT_EVENT_NAME,
+ data: {
+ version: prebidVersion,
+ },
+ }
beforeEach(() => {
yandexAnalytics.counters = {};
@@ -19,6 +27,9 @@ describe('Yandex analytics adapter testing', () => {
yandexAnalytics.oneCounterInited = false;
clock = sinon.useFakeTimers();
logError = sandbox.stub(log, 'logError');
+ sandbox.stub(globalUtils, 'getGlobal').returns({
+ version: prebidVersion,
+ });
sandbox.stub(log, 'logInfo');
getEvents = sandbox.stub(events, 'getEvents').returns([]);
onEvent = sandbox.stub(events, 'on');
@@ -86,12 +97,15 @@ describe('Yandex analytics adapter testing', () => {
eventType: 'Some_untracked_event',
}
]);
- const eventsToSend = [{
- event: EVENTS_TO_TRACK[0],
- data: {
- eventType: EVENTS_TO_TRACK[0],
+ const eventsToSend = [
+ prebidInitEvent,
+ {
+ event: EVENTS_TO_TRACK[0],
+ data: {
+ eventType: EVENTS_TO_TRACK[0],
+ }
}
- }];
+ ];
yandexAnalytics.enableAnalytics({
options: {
@@ -139,9 +153,12 @@ describe('Yandex analytics adapter testing', () => {
clock.tick(2001);
const [ sentEvents ] = counterPbjsMethod.getCall(0).args;
- chai.expect(sentEvents).to.deep.equal([{
- event,
- data: {},
- }]);
+ chai.expect(sentEvents).to.deep.equal([
+ prebidInitEvent,
+ {
+ event,
+ data: {},
+ }
+ ]);
});
});
diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js
index 140be4121ec..e6b2f5cc1f0 100644
--- a/test/spec/modules/yandexBidAdapter_spec.js
+++ b/test/spec/modules/yandexBidAdapter_spec.js
@@ -1,8 +1,9 @@
import { assert, expect } from 'chai';
import { NATIVE_ASSETS, spec } from 'modules/yandexBidAdapter.js';
import * as utils from 'src/utils.js';
-import { config } from '../../../src/config';
+import { setConfig as setCurrencyConfig } from '../../../modules/currency';
import { BANNER, NATIVE } from '../../../src/mediaTypes';
+import { addFPDToBidderRequest } from '../../helpers/fpd';
describe('Yandex adapter', function () {
describe('isBidRequestValid', function () {
@@ -125,19 +126,21 @@ describe('Yandex adapter', function () {
});
it('should send currency if defined', function () {
- config.setConfig({
- currency: {
- adServerCurrency: 'USD'
- }
+ setCurrencyConfig({
+ adServerCurrency: 'USD'
});
const bannerRequest = getBidRequest();
- const requests = spec.buildRequests([bannerRequest], bidderRequest);
- const { url } = requests[0];
- const parsedRequestUrl = utils.parseUrl(url);
- const { search: query } = parsedRequestUrl
- expect(query['ssp-cur']).to.equal('USD');
+ return addFPDToBidderRequest(bidderRequest).then(res => {
+ const requests = spec.buildRequests([bannerRequest], res);
+ const { url } = requests[0];
+ const parsedRequestUrl = utils.parseUrl(url);
+ const { search: query } = parsedRequestUrl
+
+ expect(query['ssp-cur']).to.equal('USD');
+ setCurrencyConfig({});
+ });
});
it('should send eids and ortb2 user data if defined', function() {
diff --git a/test/spec/ortbConverter/common_spec.js b/test/spec/ortbConverter/common_spec.js
index d2d61e6778c..4c2f41749a4 100644
--- a/test/spec/ortbConverter/common_spec.js
+++ b/test/spec/ortbConverter/common_spec.js
@@ -1,5 +1,5 @@
import {DEFAULT_PROCESSORS} from '../../../libraries/ortbConverter/processors/default.js';
-import {BID_RESPONSE} from '../../../src/pbjsORTB.js';
+import {BID_RESPONSE, IMP} from '../../../src/pbjsORTB.js';
describe('common processors', () => {
describe('bid response properties', () => {
@@ -26,4 +26,19 @@ describe('common processors', () => {
})
})
})
+ describe('bid imp fpd', () => {
+ const impFpd = DEFAULT_PROCESSORS[IMP].secure.fn;
+
+ it('should set secure as 1 if publisher did not set it', () => {
+ const imp = {};
+ impFpd(imp);
+ expect(imp.secure).to.eql(1);
+ });
+
+ it('should not overwrite secure if set by publisher', () => {
+ const imp = {secure: 0};
+ impFpd(imp);
+ expect(imp.secure).to.eql(0);
+ });
+ })
})
diff --git a/test/spec/unit/adRendering_spec.js b/test/spec/unit/adRendering_spec.js
index bb9ba9215cf..373dba95b3a 100644
--- a/test/spec/unit/adRendering_spec.js
+++ b/test/spec/unit/adRendering_spec.js
@@ -16,6 +16,7 @@ import {config} from 'src/config.js';
import {VIDEO} from '../../../src/mediaTypes.js';
import {auctionManager} from '../../../src/auctionManager.js';
import adapterManager from '../../../src/adapterManager.js';
+import {filters} from 'src/targeting.js';
describe('adRendering', () => {
let sandbox;
@@ -309,6 +310,26 @@ describe('adRendering', () => {
sinon.assert.notCalled(doRenderStub);
})
});
+
+ describe('when bid has already expired', () => {
+ let isBidNotExpiredStub = sinon.stub(filters, 'isBidNotExpired');
+ beforeEach(() => {
+ isBidNotExpiredStub.returns(false);
+ });
+ afterEach(() => {
+ isBidNotExpiredStub.restore();
+ })
+ it('should emit EXPIRED_RENDER', () => {
+ handleRender({adId, bidResponse});
+ sinon.assert.calledWith(events.emit, EVENTS.EXPIRED_RENDER, bidResponse);
+ sinon.assert.called(doRenderStub);
+ });
+ it('should skip rendering if suppressExpiredRender', () => {
+ config.setConfig({auctionOptions: {suppressExpiredRender: true}});
+ handleRender({adId, bidResponse});
+ sinon.assert.notCalled(doRenderStub);
+ })
+ });
})
})
diff --git a/test/spec/unit/core/ajax_spec.js b/test/spec/unit/core/ajax_spec.js
index 8140123d9fc..a7ecf88d14d 100644
--- a/test/spec/unit/core/ajax_spec.js
+++ b/test/spec/unit/core/ajax_spec.js
@@ -185,28 +185,30 @@ describe('toFetchRequest', () => {
});
});
- describe('browsingTopics', () => {
- Object.entries({
- 'browsingTopics = true': [{browsingTopics: true}, true],
- 'browsingTopics = false': [{browsingTopics: false}, false],
- 'browsingTopics is undef': [{}, false]
- }).forEach(([t, [opts, shouldBeSet]]) => {
- describe(`when options has ${t}`, () => {
- const sandbox = sinon.createSandbox();
- afterEach(() => {
- sandbox.restore();
- });
+ describe('chrome options', () => {
+ ['browsingTopics', 'adAuctionHeaders'].forEach(option => {
+ Object.entries({
+ [`${option} = true`]: [{[option]: true}, true],
+ [`${option} = false`]: [{[option]: false}, false],
+ [`${option} undef`]: [{}, false]
+ }).forEach(([t, [opts, shouldBeSet]]) => {
+ describe(`when options has ${t}`, () => {
+ const sandbox = sinon.createSandbox();
+ afterEach(() => {
+ sandbox.restore();
+ });
- it(`should ${!shouldBeSet ? 'not ' : ''}be set when in a secure context`, () => {
- sandbox.stub(window, 'isSecureContext').get(() => true);
- toFetchRequest(EXAMPLE_URL, null, opts);
- sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {browsingTopics: shouldBeSet ? true : undefined});
- });
- it(`should not be set when not in a secure context`, () => {
- sandbox.stub(window, 'isSecureContext').get(() => false);
- toFetchRequest(EXAMPLE_URL, null, opts);
- sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {browsingTopics: undefined});
- });
+ it(`should ${!shouldBeSet ? 'not ' : ''}be set when in a secure context`, () => {
+ sandbox.stub(window, 'isSecureContext').get(() => true);
+ toFetchRequest(EXAMPLE_URL, null, opts);
+ sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {[option]: shouldBeSet ? true : undefined});
+ });
+ it(`should not be set when not in a secure context`, () => {
+ sandbox.stub(window, 'isSecureContext').get(() => false);
+ toFetchRequest(EXAMPLE_URL, null, opts);
+ sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {[option]: undefined});
+ });
+ })
})
})
})
diff --git a/test/spec/unit/core/bidderSettings_spec.js b/test/spec/unit/core/bidderSettings_spec.js
index ece18040d1e..7e6446b456b 100644
--- a/test/spec/unit/core/bidderSettings_spec.js
+++ b/test/spec/unit/core/bidderSettings_spec.js
@@ -19,6 +19,13 @@ describe('ScopedSettings', () => {
expect(settings.get('scope', 'key')).to.equal('value');
});
+ it('can retrieve nested settings', () => {
+ data = {
+ scope: {outer: {key: 'value'}}
+ }
+ expect(settings.get('scope', 'outer.key')).to.equal('value');
+ })
+
it('should fallback to fallback scope', () => {
data = {
fallback: {
diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js
index f3d0c6df442..2296379ca43 100644
--- a/test/spec/unit/core/targeting_spec.js
+++ b/test/spec/unit/core/targeting_spec.js
@@ -861,6 +861,51 @@ describe('targeting tests', function () {
});
});
+ describe('targetingControls.alwaysIncludeDeals with enableSendAllBids', function () {
+ beforeEach(function() {
+ enableSendAllBids = true;
+ });
+
+ it('includes bids w/o deal when enableSendAllBids and alwaysIncludeDeals set to true', function () {
+ config.setConfig({
+ enableSendAllBids: true,
+ targetingControls: {
+ alwaysIncludeDeals: true
+ }
+ });
+
+ let bid5 = utils.deepClone(bid1);
+ bid5.adserverTargeting = {
+ hb_pb: '3.0',
+ hb_adid: '111111',
+ hb_bidder: 'pubmatic',
+ foobar: '300x250'
+ };
+ bid5.bidder = bid5.bidderCode = 'pubmatic';
+ bid5.cpm = 3.0; // winning bid!
+ delete bid5.dealId; // no deal with winner
+ bidsReceived.push(bid5);
+
+ const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']);
+
+ // Pubmatic wins but no deal. But enableSendAllBids is true.
+ // So Pubmatic is passed through
+ expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({
+ 'hb_bidder': 'pubmatic',
+ 'hb_adid': '111111',
+ 'hb_pb': '3.0',
+ 'foobar': '300x250',
+ 'hb_pb_pubmatic': '3.0',
+ 'hb_adid_pubmatic': '111111',
+ 'hb_bidder_pubmatic': 'pubmatic',
+ 'hb_deal_rubicon': '1234',
+ 'hb_pb_rubicon': '0.53',
+ 'hb_adid_rubicon': '148018fe5e',
+ 'hb_bidder_rubicon': 'rubicon'
+ });
+ });
+ });
+
it('selects the top bid when enableSendAllBids true', function () {
enableSendAllBids = true;
let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']);
diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js
index 60363ad359d..e1f5b3b5b88 100644
--- a/test/spec/unit/pbjs_api_spec.js
+++ b/test/spec/unit/pbjs_api_spec.js
@@ -28,7 +28,7 @@ import {generateUUID} from '../../../src/utils.js';
import {getCreativeRenderer} from '../../../src/creativeRenderers.js';
import {BID_STATUS, EVENTS, GRANULARITY_OPTIONS, PB_LOCATOR, TARGETING_KEYS} from 'src/constants.js';
import {getBidToRender} from '../../../src/adRendering.js';
-import { setBattrForAdUnit } from '../../../src/prebid.js';
+import {setBattrForAdUnit} from '../../../src/prebid.js';
var assert = require('chai').assert;
var expect = require('chai').expect;
@@ -237,9 +237,21 @@ describe('Unit: Prebid Module', function () {
getBidToRender.getHooks({hook: getBidToRenderHook}).remove();
});
- it('should insert a locator frame on the page', () => {
- $$PREBID_GLOBAL$$.processQueue();
- expect(window.frames[PB_LOCATOR]).to.exist;
+ describe('processQueue', () => {
+ it('should insert a locator frame on the page', () => {
+ $$PREBID_GLOBAL$$.processQueue();
+ expect(window.frames[PB_LOCATOR]).to.exist;
+ });
+
+ ['cmd', 'que'].forEach(prop => {
+ it(`should patch ${prop}.push`, () => {
+ $$PREBID_GLOBAL$$[prop].push = false;
+ $$PREBID_GLOBAL$$.processQueue();
+ let ran = false;
+ $$PREBID_GLOBAL$$[prop].push(() => { ran = true; });
+ expect(ran).to.be.true;
+ })
+ })
})
describe('and global adUnits', () => {
@@ -262,10 +274,10 @@ describe('Unit: Prebid Module', function () {
beforeEach(() => {
$$PREBID_GLOBAL$$.requestBids.before(deferringHook, 99);
- $$PREBID_GLOBAL$$.adUnits.splice(0, $$PREBID_GLOBAL$$.adUnits.length, ...startingAdUnits);
hookRan = new Promise((resolve) => {
done = resolve;
});
+ $$PREBID_GLOBAL$$.adUnits.splice(0, $$PREBID_GLOBAL$$.adUnits.length, ...startingAdUnits);
});
afterEach(() => {
@@ -1319,6 +1331,16 @@ describe('Unit: Prebid Module', function () {
});
});
+ it('should emit AD_RENDER_SUCCEEDED', () => {
+ sandbox.stub(events, 'emit');
+ pushBidResponseToAuction({
+ ad: ""
+ });
+ return renderAd(document, bidId).then(() => {
+ sinon.assert.calledWith(events.emit, EVENTS.AD_RENDER_SUCCEEDED, sinon.match({adId: bidId}));
+ });
+ });
+
it('should not render videos', function () {
pushBidResponseToAuction({
mediatype: 'video'
@@ -3531,7 +3553,7 @@ describe('Unit: Prebid Module', function () {
if (FEATURES.VIDEO) {
describe('markWinningBidAsUsed', function () {
const adUnitCode = '/19968336/header-bid-tag-0';
- let winningBid;
+ let winningBid, markedBid;
beforeEach(() => {
const bidsReceived = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode);
@@ -3540,20 +3562,56 @@ describe('Unit: Prebid Module', function () {
// mark the bid and verify the state has changed to RENDERED
winningBid = targeting.getWinningBids(adUnitCode)[0];
auction.getAuctionId = function() { return winningBid.auctionId };
+ sandbox.stub(events, 'emit');
+ markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids,
+ bid => bid.adId === winningBid.adId);
})
afterEach(() => {
resetAuction();
})
- it('marks the bid object as used for the given adUnitCode/adId combination', function () {
- // make sure the auction has "state" and does not reload the fixtures
- $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode, adId: winningBid.adId });
- const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids,
- bid => bid.adId === winningBid.adId);
-
+ function checkBidRendered() {
expect(markedBid.status).to.equal(BID_STATUS.RENDERED);
- });
+ }
+
+ Object.entries({
+ 'analytics=true': {
+ mark(options = {}) {
+ $$PREBID_GLOBAL$$.markWinningBidAsUsed(Object.assign({analytics: true}, options))
+ },
+ checkBidWon() {
+ sinon.assert.calledWith(events.emit, EVENTS.BID_WON, markedBid);
+ }
+ },
+ 'analytics=false': {
+ mark(options = {}) {
+ $$PREBID_GLOBAL$$.markWinningBidAsUsed(options)
+ },
+ checkBidWon() {
+ sinon.assert.notCalled(events.emit)
+ }
+ }
+ }).forEach(([t, {mark, checkBidWon}]) => {
+ describe(`when ${t}`, () => {
+ it('marks the bid object as used for the given adUnitCode/adId combination', function () {
+ mark({ adUnitCode, adId: winningBid.adId });
+ checkBidRendered();
+ checkBidWon();
+ });
+ it('marks the winning bid object as used for the given adUnitCode', function () {
+ mark({ adUnitCode });
+ checkBidRendered();
+ checkBidWon();
+ });
+
+ it('marks a bid object as used for the given adId', function () {
+ mark({ adId: winningBid.adId });
+ checkBidRendered();
+ checkBidWon();
+ });
+ })
+ })
it('try and mark the bid object, but fail because we supplied the wrong adId', function () {
$$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode, adId: 'miss' });
@@ -3562,24 +3620,6 @@ describe('Unit: Prebid Module', function () {
expect(markedBid.status).to.not.equal(BID_STATUS.RENDERED);
});
-
- it('marks the winning bid object as used for the given adUnitCode', function () {
- // make sure the auction has "state" and does not reload the fixtures
- $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adUnitCode });
- const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids,
- bid => bid.adId === winningBid.adId);
-
- expect(markedBid.status).to.equal(BID_STATUS.RENDERED);
- });
-
- it('marks a bid object as used for the given adId', function () {
- // make sure the auction has "state" and does not reload the fixtures
- $$PREBID_GLOBAL$$.markWinningBidAsUsed({ adId: winningBid.adId });
- const markedBid = find($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode).bids,
- bid => bid.adId === winningBid.adId);
-
- expect(markedBid.status).to.equal(BID_STATUS.RENDERED);
- });
});
}