diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 8984252f4c3..a3246cffd6d 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -22,7 +22,7 @@ version-resolver: patch: labels: - 'patch' - default: patch + default: minor template: | ## In This Release $CHANGES diff --git a/allowedModules.js b/allowedModules.js index d8e8b69f593..2d23b35c501 100644 --- a/allowedModules.js +++ b/allowedModules.js @@ -12,7 +12,6 @@ module.exports = { 'modules': [ ...sharedWhiteList, 'criteo-direct-rsa-validate', - 'jsencrypt', 'crypto-js', 'live-connect' // Maintained by LiveIntent : https://github.com/liveintent-berlin/live-connect/ ], diff --git a/integrationExamples/gpt/idImportLibrary_example.html b/integrationExamples/gpt/idImportLibrary_example.html index a3ef3f168c0..f35b571e8f9 100644 --- a/integrationExamples/gpt/idImportLibrary_example.html +++ b/integrationExamples/gpt/idImportLibrary_example.html @@ -47,6 +47,7 @@ params: { pid: '14', // Set your real identityLink placement ID here // notUse3P: true // true/false - If you do not want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will be fired. By default this propertt is undefined and 3p request will be fired.}, + }, storage: { type: 'html5', name: 'idl_env', diff --git a/modules/.submodules.json b/modules/.submodules.json index 0a10044a78e..74ebc021a6d 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,6 +36,7 @@ ], "rtdModule": [ "browsiRtdProvider", + "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index 1961dba1f10..01c59616dc0 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -77,7 +77,10 @@ export const spec = { currency: A4G_CURRENCY, netRevenue: true, ttl: A4G_TTL, - ad: response.ad + ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length > 0 ? response.adomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index 689e7d02124..a3d78a69d91 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -116,6 +116,10 @@ export const spec = { netRevenue: response.netRevenue, ttl: response.ttl, ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length ? response.adomain : [], + mediaType: 'banner' + } }; bidResponses.push(bidResponse); } diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 66653567dab..5598dc5d224 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -3,33 +3,29 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { loadExternalScript } from '../src/adloader.js'; -import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; -import sha256 from 'crypto-js/sha256.js'; +import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { createEidsArray } from './userId/eids.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; - -export const BIDDER_CODE = 'adagio'; -export const LOG_PREFIX = 'Adagio:'; -export const VERSION = '2.10.0'; -export const FEATURES_VERSION = '1'; +const BIDDER_CODE = 'adagio'; +const LOG_PREFIX = 'Adagio:'; +export const VERSION = '2.11.0'; +const FEATURES_VERSION = '1'; export const ENDPOINT = 'https://mp.4dex.io/prebid'; -export const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; -export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; -export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; -export const GVLID = 617; +const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; +const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; +const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; +const GVLID = 617; export const storage = getStorageManager(GVLID, 'adagio'); export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js'; -export const MAX_SESS_DURATION = 30 * 60 * 1000; -export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0 -jFSzIbGJJyg3cKqvtE6A0iaz9PkIdJIvSSSNrmJv+lRGKPEyRA/VnzJIieL39Ngl -t0b0lsHN+W4n9kitS/DZ/xnxWK/9vxhv0ZtL1LL/rwR5Mup7rmJbNtDoNBw4TIGj -pV6EP3MTLosuUEpLaQIDAQAB ------END PUBLIC KEY-----`; +const MAX_SESS_DURATION = 30 * 60 * 1000; +const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp'; +const ADAGIO_PUBKEY_E = 65537; +const CURRENCY = 'USD'; +const DEFAULT_FLOOR = 0.1; // This provide a whitelist and a basic validation // of OpenRTB 2.5 options used by the Adagio SSP. @@ -81,10 +77,7 @@ export function adagioScriptFromLocalStorageCb(ls) { const hash = r[2]; const content = r[3]; - var jsEncrypt = new JSEncrypt(); - jsEncrypt.setPublicKey(ADAGIO_PUBKEY); - - if (jsEncrypt.verify(content, hash, sha256)) { + if (verify(content, hash, ADAGIO_PUBKEY, ADAGIO_PUBKEY_E)) { utils.logInfo(`${LOG_PREFIX} start script.`); Function(ls)(); // eslint-disable-line no-new-func } else { @@ -524,19 +517,8 @@ function autoDetectAdUnitElementId(adUnitCode) { function autoDetectEnvironment() { const device = _features.getDevice(); - let environment; - switch (device) { - case 2: - environment = 'desktop'; - break; - case 4: - environment = 'mobile'; - break; - case 5: - environment = 'tablet'; - break; - }; - return environment; + const map = { 2: 'desktop', 4: 'mobile', 5: 'tablet' }; + return map[device] || 'unknown'; }; function supportIObs() { @@ -653,25 +635,12 @@ function _getGdprConsent(bidderRequest) { allowAuctionWithoutConsent } = bidderRequest.gdprConsent; - const consent = {}; - - if (apiVersion !== undefined) { - consent.apiVersion = apiVersion; - } - - if (consentString !== undefined) { - consent.consentString = consentString; - } - - if (gdprApplies !== undefined) { - consent.consentRequired = (gdprApplies) ? 1 : 0; - } - - if (allowAuctionWithoutConsent !== undefined) { - consent.allowAuctionWithoutConsent = allowAuctionWithoutConsent ? 1 : 0; - } - - return consent; + return utils.cleanObj({ + apiVersion, + consentString, + consentRequired: gdprApplies ? 1 : 0, + allowAuctionWithoutConsent: allowAuctionWithoutConsent ? 1 : 0 + }); } function _getCoppa() { @@ -685,9 +654,7 @@ function _getUspConsent(bidderRequest) { } function _getSchain(bidRequest) { - if (utils.deepAccess(bidRequest, 'schain')) { - return bidRequest.schain; - } + return utils.deepAccess(bidRequest, 'schain'); } function _getEids(bidRequest) { @@ -854,6 +821,48 @@ function _parseNativeBidResponse(bid) { bid.native = native } +function _getFloors(bidRequest) { + if (!utils.isFn(bidRequest.getFloor)) { + return false; + } + + const floors = []; + + const getAndPush = (mediaType, size) => { + const info = bidRequest.getFloor({ + currency: CURRENCY, + mediaType, + size: [] + }); + + floors.push(utils.cleanObj({ + mt: mediaType, + s: utils.isArray(size) ? `${size[0]}x${size[1]}` : undefined, + f: (!isNaN(info.floor) && info.currency === CURRENCY) ? info.floor : DEFAULT_FLOOR + })); + } + + Object.keys(bidRequest.mediaTypes).forEach(mediaType => { + if (SUPPORTED_MEDIA_TYPES.indexOf(mediaType) !== -1) { + const sizeProp = mediaType === VIDEO ? 'playerSize' : 'sizes'; + + if (bidRequest.mediaTypes[mediaType][sizeProp] && bidRequest.mediaTypes[mediaType][sizeProp].length) { + if (utils.isArray(bidRequest.mediaTypes[mediaType][sizeProp][0])) { + bidRequest.mediaTypes[mediaType][sizeProp].forEach(size => { + getAndPush(mediaType, [size[0], size[1]]); + }); + } else { + getAndPush(mediaType, [bidRequest.mediaTypes[mediaType][sizeProp][0], bidRequest.mediaTypes[mediaType][sizeProp][1]]); + } + } else { + getAndPush(mediaType, '*'); + } + } + }); + + return floors; +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -949,6 +958,9 @@ export const spec = { const adUnits = utils._map(validBidRequests, (bidRequest) => { bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); + // Handle priceFloors module + bidRequest.floors = _getFloors(bidRequest); + if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { _buildVideoBidRequest(bidRequest); } @@ -958,10 +970,14 @@ export const spec = { // Group ad units by organizationId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - adUnit.params.organizationId = adUnit.params.organizationId.toString(); + const adUnitCopy = utils.deepClone(adUnit); + adUnitCopy.params.organizationId = adUnitCopy.params.organizationId.toString(); + + // remove useless props + delete adUnitCopy.floorData; - groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []; - groupedAdUnits[adUnit.params.organizationId].push(adUnit); + groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || []; + groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy); return groupedAdUnits; }, {}); diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index 8b3550e6108..5893891eba6 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -5,10 +5,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { - NATIVE + NATIVE, BANNER, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; + +const { getConfig } = config; const BIDDER_CODE = 'adf'; const GVLID = 50; @@ -45,28 +48,58 @@ const NATIVE_PARAMS = { name: 'data' } }; +const OUTSTREAM_RENDERER_URL = 'https://s2.adform.net/banners/scripts/video/outstream/render.js'; export const spec = { code: BIDDER_CODE, aliases: BIDDER_ALIAS, gvlid: GVLID, - supportedMediaTypes: [ NATIVE ], + supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], isBidRequestValid: bid => !!bid.params.mid, buildRequests: (validBidRequests, bidderRequest) => { - const page = bidderRequest.refererInfo.referer; + let app, site; + + const commonFpd = getConfig('ortb2') || {}; + let { user } = commonFpd; + + if (typeof getConfig('app') === 'object') { + app = getConfig('app') || {}; + if (commonFpd.app) { + utils.mergeDeep(app, commonFpd.app); + } + } else { + site = getConfig('site') || {}; + if (commonFpd.site) { + utils.mergeDeep(site, commonFpd.site); + } + + if (!site.page) { + site.page = bidderRequest.refererInfo.referer; + } + } + + const device = getConfig('device') || {}; + device.w = device.w || window.innerWidth; + device.h = device.h || window.innerHeight; + device.ua = device.ua || navigator.userAgent; + const adxDomain = setOnAny(validBidRequests, 'params.adxDomain') || 'adx.adform.net'; - const ua = navigator.userAgent; + const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = validBidRequests[0].transactionId; // ??? check with ssp + const tid = validBidRequests[0].transactionId; const test = setOnAny(validBidRequests, 'params.test'); - const publisher = setOnAny(validBidRequests, 'params.publisher'); - const siteId = setOnAny(validBidRequests, 'params.siteId'); - const currency = config.getConfig('currency.adServerCurrency'); + const currency = getConfig('currency.adServerCurrency'); const cur = currency && [ currency ]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); const imp = validBidRequests.map((bid, id) => { bid.netRevenue = pt; + + const imp = { + id: id + 1, + tagid: bid.params.mid + }; + const assets = utils._map(bid.nativeParams, (bidParams, key) => { const props = NATIVE_PARAMS[key]; const asset = { @@ -102,21 +135,51 @@ export const spec = { } }).filter(Boolean); - return { - id: id + 1, - tagid: bid.params.mid, - native: { + if (assets.length) { + imp.native = { request: { assets } - } - }; + }; + + bid.mediaType = NATIVE; + return imp; + } + + const bannerParams = utils.deepAccess(bid, 'mediaTypes.banner'); + + if (bannerParams && bannerParams.sizes) { + const sizes = utils.parseSizesInput(bannerParams.sizes); + const format = sizes.map(size => { + const [ width, height ] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); + + imp.banner = { + format + }; + bid.mediaType = BANNER; + + return imp; + } + + const videoParams = utils.deepAccess(bid, 'mediaTypes.video'); + if (videoParams) { + imp.video = videoParams; + bid.mediaType = VIDEO; + + return imp; + } }); const request = { id: bidderRequest.auctionId, - site: { id: siteId, page, publisher }, - device: { ua }, + site, + app, + user, + device, source: { tid, fd: 1 }, ext: { pt }, cur, @@ -128,8 +191,8 @@ export const spec = { request.test = 1; } if (utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { - request.user = { ext: { consent: bidderRequest.gdprConsent.consentString } }; - request.regs = { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies & 1 } }; + utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + utils.deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); } if (bidderRequest.uspConsent) { @@ -164,16 +227,35 @@ export const spec = { return bids.map((bid, id) => { const bidResponse = bidResponses[id]; if (bidResponse) { - return { + const result = { requestId: bid.bidId, cpm: bidResponse.price, creativeId: bidResponse.crid, ttl: 360, netRevenue: bid.netRevenue === 'net', currency: cur, - mediaType: NATIVE, - native: parseNative(bidResponse) + mediaType: bid.mediaType, + width: bidResponse.w, + height: bidResponse.h, + dealId: bidResponse.dealid, + meta: { + mediaType: bid.mediaType, + advertiserDomains: bidResponse.adomain + } }; + + if (bidResponse.native) { + result.native = parseNative(bidResponse); + } else { + result[ bid.mediaType === VIDEO ? 'vastXml' : 'ad' ] = bidResponse.adm; + } + + if (!bid.renderer && bid.mediaType === VIDEO && utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + result.renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); + result.renderer.setRender(renderer); + } + + return result; } }).filter(Boolean); } @@ -212,3 +294,9 @@ function setOnAny(collection, key) { function flatten(arr) { return [].concat(...arr); } + +function renderer(bid) { + bid.renderer.push(() => { + window.Adform.renderOutstream(bid); + }); +} diff --git a/modules/adfBidAdapter.md b/modules/adfBidAdapter.md index 190aa91ea57..10264e6f486 100644 --- a/modules/adfBidAdapter.md +++ b/modules/adfBidAdapter.md @@ -7,15 +7,12 @@ Maintainer: Scope.FL.Scripts@adform.com # Description Module that connects to Adform demand sources to fetch bids. -Only native format is supported. Using OpenRTB standard. Previous adapter name - adformOpenRTB. +Banner, video and native formats are supported. Using OpenRTB standard. Previous adapter name - adformOpenRTB. # Test Parameters ``` - var adUnits = [ + var adUnits = [{ code: '/19968336/prebid_native_example_1', - sizes: [ - [360, 360] - ], mediaTypes: { native: { image: { @@ -44,16 +41,36 @@ Only native format is supported. Using OpenRTB standard. Previous adapter name - bids: [{ bidder: 'adf', params: { - mid: 606169, // required - adxDomain: 'adx.adform.net', // optional - siteId: '23455', // optional - priceType: 'gross' // optional, default is 'net' - publisher: { // optional block - id: "2706", - name: "Publishers Name", - domain: "publisher.com" - } + mid: 606169, // required + adxDomain: 'adx.adform.net' // optional + } + }] + }, { + code: '/19968336/prebid_banner_example_1', + mediaTypes: { + banner: { + sizes: [[ 300, 250 ]] + } + } + bids: [{ + bidder: 'adf', + params: { + mid: 1038466 + } + }] + }, { + code: '/19968336/prebid_video_example_1', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + } + bids: [{ + bidder: 'adf', + params: { + mid: 822732 } }] - ]; + }]; ``` diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 0de750c773f..dc56ed6abbb 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -103,13 +103,17 @@ function buildBid(tag) { requestId: tag.impid, bidderCode: spec.code, cpm: tag.bid, - width: tag.w, - height: tag.h, creativeId: tag.crid, currency: 'USD', ttl: 720, netRevenue: true }; + if (tag.w) { + bid.width = tag.w; + } + if (tag.h) { + bid.height = tag.h; + } if (tag.tag) { bid.ad = tag.tag; bid.mediaType = BANNER; @@ -117,9 +121,31 @@ function buildBid(tag) { bid.vastUrl = tag.vast_url; bid.mediaType = VIDEO; } + fillBidMeta(bid, tag); return bid; } +function fillBidMeta(bid, tag) { + if (utils.isStr(tag.agencyName)) { + utils.deepSetValue(bid, 'meta.agencyName', tag.agencyName); + } + if (utils.isNumber(tag.advertiserId)) { + utils.deepSetValue(bid, 'meta.advertiserId', tag.advertiserId); + } + if (utils.isStr(tag.advertiserName)) { + utils.deepSetValue(bid, 'meta.advertiserName', tag.advertiserName); + } + if (utils.isArray(tag.advertiserDomains)) { + utils.deepSetValue(bid, 'meta.advertiserDomains', tag.advertiserDomains); + } + if (utils.isStr(tag.primaryCatId)) { + utils.deepSetValue(bid, 'meta.primaryCatId', tag.primaryCatId); + } + if (utils.isArray(tag.secondaryCatIds)) { + utils.deepSetValue(bid, 'meta.secondaryCatIds', tag.secondaryCatIds); + } +} + function getBidFloor(bid, mediaType, sizes) { var floor; var size = sizes.length === 1 ? sizes[0] : '*'; diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index e4b04a27d42..b18b2333bac 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -68,7 +68,8 @@ export const spec = { {code: 'bcm'}, {code: 'engageadx'}, {code: 'converge_digital', gvlid: 248}, - {code: 'adomega'} + {code: 'adomega'}, + {code: 'denakop'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 8752e37a96f..5e10219188d 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -1,30 +1,61 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'adnuntius'; const ENDPOINT_URL = 'https://delivery.adnuntius.com/i?tzo='; +const GVLID = 855; + +const checkSegment = function (segment) { + if (utils.isStr(segment)) return segment; + if (segment.id) return segment.id +} + +const getSegmentsFromOrtb = function (ortb2) { + const userData = utils.deepAccess(ortb2, 'user.data'); + let segments = []; + if (userData) { + userData.forEach(userdat => { + if (userdat.segment) { + segments.push(...userdat.segment.filter(checkSegment).map(checkSegment)); + } + }); + } + return segments +} export const spec = { code: BIDDER_CODE, - + gvlid: GVLID, + supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return !!(bid.bidId || (bid.params.member && bid.params.invCode)); }, - buildRequests: function (validBidRequests) { + buildRequests: function (validBidRequests, bidderRequest) { const networks = {}; const bidRequests = {}; const requests = []; + const ortb2 = config.getConfig('ortb2'); + const segments = getSegmentsFromOrtb(ortb2); const tzo = new Date().getTimezoneOffset(); + const gdprApplies = utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); + const consentString = utils.deepAccess(bidderRequest, 'gdprConsent.consentString'); + const reqConsent = (gdprApplies !== undefined) ? '&consentString=' + consentString : ''; + const reqSegments = (segments.length > 0) ? '&segments=' + segments.join(',') : ''; for (var i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i] const network = bid.params.network || 'network'; + const targeting = bid.params.targeting || {}; + bidRequests[network] = bidRequests[network] || []; bidRequests[network].push(bid); networks[network] = networks[network] || {}; networks[network].adUnits = networks[network].adUnits || []; - networks[network].adUnits.push({ ...bid.params.targeting, auId: bid.params.auId, targetId: bid.bidId }); + networks[network].adUnits.push({ ...targeting, auId: bid.params.auId, targetId: bid.bidId }); } const networkKeys = Object.keys(networks) @@ -32,7 +63,7 @@ export const spec = { const network = networkKeys[j]; requests.push({ method: 'POST', - url: ENDPOINT_URL + tzo + '&format=json', + url: ENDPOINT_URL + tzo + '&format=json' + reqSegments + reqConsent, data: JSON.stringify(networks[network]), bid: bidRequests[network] }); diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js index b75d1972f5a..7ce9c7ae8f2 100644 --- a/modules/adoceanBidAdapter.js +++ b/modules/adoceanBidAdapter.js @@ -80,7 +80,10 @@ function interpretResponse(placementResponse, bidRequest, bids) { width: parseInt(placementResponse.width, 10), netRevenue: false, ttl: parseInt(placementResponse.ttl), - creativeId: placementResponse.crid + creativeId: placementResponse.crid, + meta: { + advertiserDomains: placementResponse.adomain || [] + } }; bids.push(bid); diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 502ef05b0d5..ddd9531eb43 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -74,7 +74,7 @@ function isBanner(mediaTypes) { } function isVideo(mediaTypes) { - return isPlainObject(mediaTypes) && isPlainObject(mediaTypes.video); + return isPlainObject(mediaTypes) && 'video' in mediaTypes; } function validateBanner(banner) { @@ -104,13 +104,16 @@ function validateMediaSizes(mediaSize) { function validateParameters(parameters, adUnit) { if (isVideo(adUnit.mediaTypes)) { if (!isPlainObject(parameters)) return false; + if (!isPlainObject(adUnit.mediaTypes.video)) return false; if (!validateVideoParameters(parameters.video, adUnit)) return false; } return true; } -function validateVideoParameters(video, adUnit) { +function validateVideoParameters(videoParams, adUnit) { + const video = adUnit.mediaTypes.video; + if (!video) return false; if (!isArray(video.mimes)) return false; @@ -124,9 +127,9 @@ function validateVideoParameters(video, adUnit) { if (video.protocols.length === 0) return false; if (!video.protocols.every(isNumber)) return false; - if (isInstream(adUnit.mediaTypes.video)) { - if (!video.instreamContext) return false; - if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(video.instreamContext) === -1) return false; + if (isInstream(video)) { + if (!videoParams.instreamContext) return false; + if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(videoParams.instreamContext) === -1) return false; } return true; @@ -203,7 +206,7 @@ function generateImpressionsFromAdUnit(acc, adUnit) { if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params)); if (mediaType === 'video') return acc.concat({id: impId, video: generateVideoFromAdUnit(data, params), pmp, ext}); - if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data, params), pmp, ext}); + if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data), pmp, ext}); }, []); return acc.concat(imps); @@ -226,25 +229,52 @@ function generateBannerFromAdUnit(impId, data, params) { function generateVideoFromAdUnit(data, params) { const {playerSize} = data; + const video = data + const hasPlayerSize = isArray(playerSize) && playerSize.length > 0; - const {position, video = {}} = params; const {minDuration, maxDuration, protocols} = video; const size = {width: hasPlayerSize ? playerSize[0][0] : null, height: hasPlayerSize ? playerSize[0][1] : null}; const duration = {min: isNumber(minDuration) ? minDuration : null, max: isNumber(maxDuration) ? maxDuration : null}; + const startdelay = computeStartDelay(data, params); return { mimes: SUPPORTED_VIDEO_MIMES, + skip: video.skippable || 0, w: size.width, h: size.height, - startdelay: computeStartDelay(data, params), + startdelay: startdelay, + linearity: video.linearity || null, minduration: duration.min, maxduration: duration.max, protocols, - pos: position || 0 + api: getApi(protocols), + format: hasPlayerSize ? playerSize.map(s => { + return {w: s[0], h: s[1]}; + }) : null, + pos: video.position || 0 }; } +function getApi(protocols) { + let defaultValue = [2]; + let listProtocols = [ + {key: 'VPAID_1_0', value: 1}, + {key: 'VPAID_2_0', value: 2}, + {key: 'MRAID_1', value: 3}, + {key: 'ORMMA', value: 4}, + {key: 'MRAID_2', value: 5}, + {key: 'MRAID_3', value: 6}, + ]; + if (protocols) { + return listProtocols.filter(p => { + return protocols.indexOf(p.key) !== -1; + }).map(p => p.value) + } else { + return defaultValue; + } +} + function isInstream(video) { return isPlainObject(video) && (video.context === 'instream'); } @@ -263,7 +293,7 @@ function computeStartDelay(data, params) { return null; } -function generateNativeFromAdUnit(data, params) { +function generateNativeFromAdUnit(data) { const {type} = data; const presetFormatter = type && NATIVE_PRESET_FORMATTERS[data.type]; const nativeFields = presetFormatter ? presetFormatter(data) : data; @@ -500,6 +530,10 @@ function generateAdFromBid(bid, bidResponse, serverRequest) { mediaType: bid.ext.adot.media_type, }; + if (bid.adomain) { + base.meta = { advertiserDomains: bid.adomain }; + } + if (isBidANative(bid)) return {...base, native: formatNativeData(bid)}; const size = getSizeFromBid(bid, impressionData); diff --git a/modules/adotBidAdapter.md b/modules/adotBidAdapter.md index 98a4a1a5970..894a592ec18 100644 --- a/modules/adotBidAdapter.md +++ b/modules/adotBidAdapter.md @@ -47,23 +47,22 @@ const adUnit = { context: 'outstream', // Video dimensions supported by the video ad unit. // Each ad unit size is formatted as follows: [width, height]. - playerSize: [[300, 250]] + playerSize: [[300, 250]], + // Content MIME types supported by the ad unit. + mimes: ['video/mp4'], + // Minimum accepted video ad duration (in seconds). + minDuration: 5, + // Maximum accepted video ad duration (in seconds). + maxDuration: 35, + // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, + // section 5.8). + protocols: [2, 3] } }, bids: [{ bidder: 'adot', params: { - video: { - // Content MIME types supported by the ad unit. - mimes: ['video/mp4'], - // Minimum accepted video ad duration (in seconds). - minDuration: 5, - // Maximum accepted video ad duration (in seconds). - maxDuration: 35, - // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, - // section 5.8). - protocols: [2, 3] - } + video: {} } }] } @@ -82,23 +81,22 @@ const adUnit = { context: 'instream', // Video dimensions supported by the video ad unit. // Each ad unit size is formatted as follows: [width, height]. - playerSize: [[300, 250]] + playerSize: [[300, 250]], + // Content MIME types supported by the ad unit. + mimes: ['video/mp4'], + // Minimum accepted video ad duration (in seconds). + minDuration: 5, + // Maximum accepted video ad duration (in seconds). + maxDuration: 35, + // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, + // section 5.8). + protocols: [2, 3] } }, bids: [{ bidder: 'adot', params: { video: { - // Content MIME types supported by the ad unit. - mimes: ['video/mp4'], - // Minimum accepted video ad duration (in seconds). - minDuration: 5, - // Maximum accepted video ad duration (in seconds). - maxDuration: 35, - // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, - // section 5.8). - protocols: [2, 3], - // Instream video context. Must be either 'pre-roll', 'mid-roll' or 'post-roll'. instreamContext: 'pre-roll' } } diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index b22addec54f..1779ba94371 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -173,7 +173,10 @@ function createBid(bidResponse, bidRequest) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 300 + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === BANNER) { diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 9ca4b95dfd2..d21931a6dcb 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -248,7 +248,10 @@ function createBid(bidResponse, bidRequest) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 300 + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === BANNER) { diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index d5b348ee29b..b70f7cf3ce6 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -1,20 +1,169 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js' +import { config } from '../src/config.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js' import * as utils from '../src/utils.js'; export const BIDDER_CODE = 'aduptech'; -export const PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; -export const ENDPOINT_URL = 'https://rtb.d.adup-tech.com/prebid/' + PUBLISHER_PLACEHOLDER + '_bid'; +export const ENDPOINT_URL_PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; +export const ENDPOINT_URL = 'https://rtb.d.adup-tech.com/prebid/' + ENDPOINT_URL_PUBLISHER_PLACEHOLDER + '_bid'; export const ENDPOINT_METHOD = 'POST'; +/** + * Internal utitlity functions + */ +export const internal = { + + /** + * Extracts the GDPR information from given bidderRequest + * + * @param {BidderRequest} bidderRequest + * @returns {null|Object.} + */ + extractGdpr: (bidderRequest) => { + if (bidderRequest && bidderRequest.gdprConsent) { + return { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + }; + } + + return null; + }, + + /** + * Extracts the pageUrl from given bidderRequest.refererInfo or gobal "pageUrl" config or from (top) window location + * + * @param {BidderRequest} bidderRequest + * @returns {string} + */ + extractPageUrl: (bidderRequest) => { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl')) { + return bidderRequest.refererInfo.canonicalUrl; + } + + if (config && config.getConfig('pageUrl')) { + return config.getConfig('pageUrl'); + } + + try { + return utils.getWindowTop().location.href; + } catch (e) { + return utils.getWindowSelf().location.href; + } + }, + + /** + * Extracts the referrer based on given bidderRequest.refererInfo or from (top) document referrer + * + * @param {BidderRequest} bidderRequest + * @returns {string} + */ + extractReferrer: (bidderRequest) => { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.referer')) { + return bidderRequest.refererInfo.referer; + } + + try { + return utils.getWindowTop().document.referrer; + } catch (e) { + return utils.getWindowSelf().document.referrer; + } + }, + + /** + * Extracts banner config from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractBannerConfig: (bidRequest) => { + const sizes = utils.getAdUnitSizes(bidRequest); + if (Array.isArray(sizes) && sizes.length > 0) { + return { sizes: sizes }; + } + + return null; + }, + + /** + * Extracts native config from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractNativeConfig: (bidRequest) => { + if (bidRequest && utils.deepAccess(bidRequest, 'mediaTypes.native')) { + return bidRequest.mediaTypes.native; + } + + return null; + }, + + /** + * Extracts the bidder params from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractParams: (bidRequest) => { + if (bidRequest && bidRequest.params) { + return bidRequest.params + } + + return null; + }, + + /** + * Group given array of bidRequests by params.publisher + * + * @param {BidRequest[]} bidRequests + * @returns {Object.} + */ + groupBidRequestsByPublisher: (bidRequests) => { + const groupedBidRequests = {}; + + if (!bidRequests || bidRequests.length === 0) { + return groupedBidRequests; + } + + bidRequests.forEach((bidRequest) => { + const publisher = internal.extractParams(bidRequest).publisher; + if (!publisher) { + return; + } + + if (!groupedBidRequests[publisher]) { + groupedBidRequests[publisher] = []; + } + + groupedBidRequests[publisher].push(bidRequest); + }); + + return groupedBidRequests; + }, + + /** + * Build ednpoint url based on given publisher code + * + * @param {string} publisher + * @returns {string} + */ + buildEndpointUrl: (publisher) => { + return ENDPOINT_URL.replace(ENDPOINT_URL_PUBLISHER_PLACEHOLDER, encodeURIComponent(publisher)); + }, +} + +/** + * The bid adapter definition + */ export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], /** * Validate given bid request * - * @param {*} bidRequest + * @param {BidRequest[]} bidRequest * @returns {boolean} */ isBidRequestValid: (bidRequest) => { @@ -22,12 +171,13 @@ export const spec = { return false; } - const sizes = extractSizesFromBidRequest(bidRequest); - if (!sizes || sizes.length === 0) { + // banner or native config has to be set + if (!internal.extractBannerConfig(bidRequest) && !internal.extractNativeConfig(bidRequest)) { return false; } - const params = extractParamsFromBidRequest(bidRequest); + // publisher and placement param has to be set + const params = internal.extractParams(bidRequest); if (!params || !params.publisher || !params.placement) { return false; } @@ -38,152 +188,124 @@ export const spec = { /** * Build real bid requests * - * @param {*} validBidRequests - * @param {*} bidderRequest - * @returns {*[]} + * @param {BidRequest[]} validBidRequests + * @param {BidderRequest} bidderRequest + * @returns {Object[]} */ buildRequests: (validBidRequests, bidderRequest) => { - const bidRequests = []; - const gdpr = extractGdprFromBidderRequest(bidderRequest); + const requests = []; + + // stop here on invalid or empty data + if (!bidderRequest || !validBidRequests || validBidRequests.length === 0) { + return requests; + } + + // collect required data + const auctionId = bidderRequest.auctionId; + const pageUrl = internal.extractPageUrl(bidderRequest); + const referrer = internal.extractReferrer(bidderRequest); + const gdpr = internal.extractGdpr(bidderRequest); - validBidRequests.forEach((bidRequest) => { - bidRequests.push({ - url: ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequest.params.publisher)), + // group bid requests by publisher + const groupedBidRequests = internal.groupBidRequestsByPublisher(validBidRequests); + + // build requests + for (const publisher in groupedBidRequests) { + const request = { + url: internal.buildEndpointUrl(publisher), method: ENDPOINT_METHOD, data: { + auctionId: auctionId, + pageUrl: pageUrl, + referrer: referrer, + imp: [] + } + }; + + // add gdpr data + if (gdpr) { + request.data.gdpr = gdpr; + } + + // handle multiple bids per request + groupedBidRequests[publisher].forEach((bidRequest) => { + const bid = { bidId: bidRequest.bidId, - auctionId: bidRequest.auctionId, transactionId: bidRequest.transactionId, adUnitCode: bidRequest.adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequest), - referrer: extractTopWindowReferrerFromBidRequest(bidRequest), - sizes: extractSizesFromBidRequest(bidRequest), - params: extractParamsFromBidRequest(bidRequest), - gdpr: gdpr + params: internal.extractParams(bidRequest) + }; + + // add banner config + const bannerConfig = internal.extractBannerConfig(bidRequest); + if (bannerConfig) { + bid.banner = bannerConfig; + } + + // add native config + const nativeConfig = internal.extractNativeConfig(bidRequest); + if (nativeConfig) { + bid.native = nativeConfig; } + + request.data.imp.push(bid); }); - }); - return bidRequests; + requests.push(request); + } + + return requests; }, /** * Handle bid response * - * @param {*} response - * @returns {*[]} + * @param {Object} response + * @returns {Object[]} */ interpretResponse: (response) => { const bidResponses = []; - if (!response.body || !response.body.bid || !response.body.creative) { + // stop here on invalid or empty data + if (!response || !utils.deepAccess(response, 'body.bids') || response.body.bids.length === 0) { return bidResponses; } - bidResponses.push({ - requestId: response.body.bid.bidId, - cpm: response.body.bid.price, - netRevenue: response.body.bid.net, - currency: response.body.bid.currency, - ttl: response.body.bid.ttl, - creativeId: response.body.creative.id, - width: response.body.creative.width, - height: response.body.creative.height, - ad: response.body.creative.html - }); - - return bidResponses; - } -}; - -/** - * Extracts the possible ad unit sizes from given bid request - * - * @param {*} bidRequest - * @returns {number[]} - */ -export function extractSizesFromBidRequest(bidRequest) { - // since pbjs 3.0 - if (bidRequest && utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes')) { - return bidRequest.mediaTypes.banner.sizes; - - // for backward compatibility - } else if (bidRequest && bidRequest.sizes) { - return bidRequest.sizes; - - // fallback - } else { - return []; - } -} - -/** - * Extracts the custom params from given bid request - * - * @param {*} bidRequest - * @returns {*} - */ -export function extractParamsFromBidRequest(bidRequest) { - if (bidRequest && bidRequest.params) { - return bidRequest.params - } else { - return null; - } -} - -/** - * Extracts the GDPR information from given bidder request - * - * @param {*} bidderRequest - * @returns {*} - */ -export function extractGdprFromBidderRequest(bidderRequest) { - let gdpr = null; - - if (bidderRequest && bidderRequest.gdprConsent) { - gdpr = { - consentString: bidderRequest.gdprConsent.consentString, - consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - }; - } + // parse multiple bids per response + response.body.bids.forEach((bid) => { + if (!bid || !bid.bid || !bid.creative) { + return; + } - return gdpr; -} + const bidResponse = { + requestId: bid.bid.bidId, + cpm: bid.bid.price, + netRevenue: bid.bid.net, + currency: bid.bid.currency, + ttl: bid.bid.ttl, + creativeId: bid.creative.id, + meta: { + advertiserDomains: bid.creative.advertiserDomains + } + } -/** - * Extracts the page url from given bid request or use the (top) window location as fallback - * - * @param {*} bidRequest - * @returns {string} - */ -export function extractTopWindowUrlFromBidRequest(bidRequest) { - if (bidRequest && utils.deepAccess(bidRequest, 'refererInfo.canonicalUrl')) { - return bidRequest.refererInfo.canonicalUrl; - } + if (bid.creative.html) { + bidResponse.mediaType = BANNER; + bidResponse.ad = bid.creative.html; + bidResponse.width = bid.creative.width; + bidResponse.height = bid.creative.height; + } - try { - return window.top.location.href; - } catch (e) { - return window.location.href; - } -} + if (bid.creative.native) { + bidResponse.mediaType = NATIVE; + bidResponse.native = bid.creative.native; + } -/** - * Extracts the referrer from given bid request or use the (top) document referrer as fallback - * - * @param {*} bidRequest - * @returns {string} - */ -export function extractTopWindowReferrerFromBidRequest(bidRequest) { - if (bidRequest && utils.deepAccess(bidRequest, 'refererInfo.referer')) { - return bidRequest.refererInfo.referer; - } + bidResponses.push(bidResponse); + }); - try { - return window.top.document.referrer; - } catch (e) { - return window.document.referrer; + return bidResponses; } -} +}; registerBidder(spec); diff --git a/modules/aduptechBidAdapter.md b/modules/aduptechBidAdapter.md index 281fe24cf9d..25034cecbe8 100644 --- a/modules/aduptechBidAdapter.md +++ b/modules/aduptechBidAdapter.md @@ -1,35 +1,86 @@ -# Overview -``` -Module Name: AdUp Technology Bid Adapter -Module Type: Bidder Adapter -Maintainers: - - steffen.anders@adup-tech.com - - sebastian.briesemeister@adup-tech.com - - marten.lietz@adup-tech.com -``` +# AdUp Technology Bid Adapter -# Description +## Description Connects to AdUp Technology demand sources to fetch bids. -Please use ```aduptech``` as bidder code. Only banner formats are supported. -The AdUp Technology bidding adapter requires setup and approval before beginning. -For more information visit [www.adup-tech.com](https://www.adup-tech.com/en) or contact [info@adup-tech.com](mailto:info@adup-tech.com). +**Note:** The bid adapter requires correct setup and approval, including an existing publisher account. For more information visit [www.adup-tech.com](https://www.adup-tech.com/en) or contact [info@adup-tech.com](mailto:info@adup-tech.com). + + +## Overview +- Module Name: AdUp Technology Bid Adapter +- Module Type: Bidder Adapter +- Maintainers: + - [steffen.anders@adup-tech.com](mailto:steffen.anders@adup-tech.com) + - [sebastian.briesemeister@adup-tech.com](mailto:sebastian.briesemeister@adup-tech.com) + - [marten.lietz@adup-tech.com](mailto:marten.lietz@adup-tech.com) +- Bidder code: `aduptech` +- Supported media types: `banner`, `native` + +## Paramters +| Name | Scope | Description | Example | +| :--- | :---- | :---------- | :------ | +| `publisher` | required | Unique publisher identifier | `'prebid'` | +| `placement` | required | Unique placement identifier per publisher | `'1234'` | +| `query` | optional | Semicolon separated list of keywords | `'urlaub;ibiza;mallorca'` | +| `adtest` | optional | Deactivates tracking of impressions and clicks. **Should only be used for testing purposes!** | `true` | + -# Test Parameters +## Examples + +### Banner ```js var adUnits = [ { - code: 'banner', + code: "example1", mediaTypes: { banner: { sizes: [[300, 250], [300, 600]], } }, bids: [{ - bidder: 'aduptech', + bidder: "aduptech", + params: { + publisher: "prebid", + placement: "12345" + } + }] + } +]; +``` + +### Native +```js +var adUnits = [ + { + code: "example2", + mediaTypes: { + native: { + image: { + required: true, + sizes: [150, 150] + }, + title: { + required: true + }, + body: { + required: true + }, + clickUrl: { + required: true + }, + displayUrl: { + required: true + }, + sponsoredBy: { + required: true + } + } + }, + bids: [{ + bidder: "aduptech", params: { - publisher: 'prebid', - placement: '12345' + publisher: "prebid", + placement: "12345" } }] } diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 00c7c02dc72..74ce62950f8 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -78,6 +78,10 @@ export const spec = { if (nativeReq.type === 'image') { nativeReq = Object.assign({}, NATIVE_IMAGE, nativeReq); } + // click url is always mandatory even if not specified by publisher + nativeReq.clickUrl = { + required: true + }; accumulator[bidReq.bidId].Native = nativeReq; } if (mediatype === VIDEO) { @@ -329,6 +333,9 @@ function getVideoAd(response) { } function getNativeAssets(response, nativeConfig) { + if (typeof response.Native === 'object') { + return response.Native; + } const native = {}; var adJson = {}; @@ -362,59 +369,55 @@ function getNativeAssets(response, nativeConfig) { } Object.keys(nativeConfig).map(function(key, index) { - if (typeof response.Native === 'object') { - native[key] = response.Native[key]; - } else { - switch (key) { - case 'title': - native[key] = textsJson.TITLE; - break; - case 'body': - native[key] = textsJson.DESCRIPTION; - break; - case 'cta': - native[key] = textsJson.CALLTOACTION; - break; - case 'sponsoredBy': - native[key] = adJson.Content.Preview.Sponsor.Name; - break; - case 'image': - // main image requested size - const imgSize = nativeConfig.image.sizes || []; - if (!imgSize.length) { - imgSize[0] = response.Width || 300; - imgSize[1] = response.Height || 250; + switch (key) { + case 'title': + native[key] = textsJson.TITLE; + break; + case 'body': + native[key] = textsJson.DESCRIPTION; + break; + case 'cta': + native[key] = textsJson.CALLTOACTION; + break; + case 'sponsoredBy': + native[key] = adJson.Content.Preview.Sponsor.Name; + break; + case 'image': + // main image requested size + const imgSize = nativeConfig.image.sizes || []; + if (!imgSize.length) { + imgSize[0] = response.Width || 300; + imgSize[1] = response.Height || 250; + } + + native[key] = { + url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), + width: imgSize[0], + height: imgSize[1] + }; + break; + case 'icon': + if (adJson.HasSponsorImage) { + // icon requested size + const iconSize = nativeConfig.icon.sizes || []; + if (!iconSize.length) { + iconSize[0] = 50; + iconSize[1] = 50; } native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), - width: imgSize[0], - height: imgSize[1] + url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), + width: iconSize[0], + height: iconSize[1] }; - break; - case 'icon': - if (adJson.HasSponsorImage) { - // icon requested size - const iconSize = nativeConfig.icon.sizes || []; - if (!iconSize.length) { - iconSize[0] = 50; - iconSize[1] = 50; - } - - native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), - width: iconSize[0], - height: iconSize[1] - }; - } - break; - case 'privacyIcon': - native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); - break; - case 'privacyLink': - native[key] = adJson.Content.Preview.Credit.Url; - break; - } + } + break; + case 'privacyIcon': + native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); + break; + case 'privacyLink': + native[key] = adJson.Content.Preview.Credit.Url; + break; } }); @@ -446,7 +449,8 @@ function createBid(response, bidRequests) { creativeId: response.CreativeID, cpm: response.Price, netRevenue: true, - currency: CURRENCY + currency: CURRENCY, + meta: response.Meta || { advertiserDomains: [] } }; if (request && request.Native) { diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index 6b83c40897e..b37d105da1a 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -201,6 +201,10 @@ function interpretResponse(serverResponse, bidRequest) { bidResponse.vastUrl = window.URL.createObjectURL(blob); bidResponse.vastXml = xmlStr; bidResponse.mediaType = VIDEO; + bidResponse.meta = { + advertiserDomains: [] + }; + if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') { bidResponse.renderer = newRenderer(bidRequest); } bidResponses.push(bidResponse); @@ -230,7 +234,10 @@ function getSyncData(xml, options) { if (data && data.trackers && data.trackers.length) { data = data.trackers; for (var j = 0; j < data.length; j++) { - if (typeof data[j] === 'object' && typeof data[j].url === 'string' && data[j].e === 'inventory') { + if (typeof data[j] === 'object' && + typeof data[j].url === 'string' && + (data[j].e === 'inventory' || data[j].e === 'sync') + ) { if (data[j].t == 1 && options.pixelEnabled) { ret.push({url: data[j].url, type: 'image'}); } else { diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 14b529f4973..03e4ac9021a 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -366,7 +366,7 @@ export const spec = { let tagName = item.match(tagNameRegExp)[0]; let url = item.match(srcRegExp)[2]; - if (tagName && tagName) { + if (tagName && url) { pixelsItems.push({ type: tagName === SYNC_TYPES.IMAGE.TAG ? SYNC_TYPES.IMAGE.TYPE : SYNC_TYPES.IFRAME.TYPE, url: url diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 62ae3f54125..c0431ddf923 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -55,13 +55,13 @@ export const spec = { let eids; let geo; let test; + let bids = []; - var bids = JSON.parse(JSON.stringify(validBidRequests)) - bidderConfig = CONFIG[bids[0].bidder]; + bidderConfig = CONFIG[validBidRequests[0].bidder]; test = config.getConfig('debug'); - bids.forEach(bidReq => { + validBidRequests.forEach(bidReq => { siteId = siteId || bidReq.params.siteId; if (bidReq.schain) { @@ -95,6 +95,13 @@ export const spec = { } bySlotTargetKey[bidReq.adUnitCode] = targetKey; bidReq.targetKey = targetKey; + + let bidFloor = getBidFloor(bidReq); + if (bidFloor) { + bidReq.bidFloor = bidFloor; + } + + bids.push(JSON.parse(JSON.stringify(bidReq))); }); const payload = {}; @@ -169,23 +176,30 @@ export const spec = { const bidResponses = []; serverBids.forEach(bid => { + const dealId = bid.dealId || ''; const bidResponse = { requestId: bid.requestId, cpm: bid.cpm, width: bid.width, height: bid.height, creativeId: bid.creativeId, - dealId: bid.dealId, currency: bid.currency, netRevenue: bid.netRevenue, ttl: bid.ttl, mediaType: bid.mediaType }; + if (dealId.length > 0) { + bidResponse.dealId = dealId; + } if (bid.vastXml) { bidResponse.vastXml = utils.replaceAuctionPrice(bid.vastXml, bid.cpm); } else { bidResponse.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); } + bidResponse.meta = {}; + if (bid.meta && bid.meta.advertiserDomains && utils.isArray(bid.meta.advertiserDomains)) { + bidResponse.meta.advertiserDomains = bid.meta.advertiserDomains; + } bidResponses.push(bidResponse); }); return bidResponses; @@ -336,4 +350,26 @@ export function validateGeoObject(geo) { return true; } +/** + * Get bid floor from Price Floors Module + * + * @param {Object} bid + * @returns {float||null} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return (bid.params.floorPrice) ? bid.params.floorPrice : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/apacdexBidAdapter.md b/modules/apacdexBidAdapter.md index b88190cda94..fa8c727d709 100644 --- a/modules/apacdexBidAdapter.md +++ b/modules/apacdexBidAdapter.md @@ -11,7 +11,7 @@ Maintainer: ken@apacdex.com Connects to APAC Digital Exchange for bids. Apacdex bid adapter supports Banner and Video (Instream and Outstream) ads. -# Test Parameters +# Sample Banner Ad Unit ``` var adUnits = [ { @@ -26,6 +26,7 @@ var adUnits = [ bidder: 'apacdex', params: { siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared } } ] @@ -33,7 +34,7 @@ var adUnits = [ ]; ``` -# Video Test Parameters +# Sample Video Ad Unit: Instream ``` var videoAdUnit = { code: 'test-div', @@ -41,7 +42,17 @@ var videoAdUnit = { mediaTypes: { video: { playerSize: [[640, 480]], - context: 'instream' + context: "instream" + api: [2], + placement: 1, + skip: 1, + linearity: 1, + minduration: 1, + maxduration: 120, + mimes: ["video/mp4", "video/x-flv", "video/x-ms-wmv", "application/vnd.apple.mpegurl", "application/x-mpegurl", "video/3gpp", "video/mpeg", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v", "video/ms-asf", video/x-msvideo"], + playbackmethod: [6], + startdelay: 0, + protocols: [1, 2, 3, 4, 5, 6] }, }, bids: [ @@ -49,8 +60,45 @@ var videoAdUnit = { bidder: 'apacdex', params: { siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared } } ] }; -``` \ No newline at end of file +``` +mediaTypes.video object reference to section 3.2.7 Object: Video in the OpenRTB 2.5 document +You must review all video parameters to ensure validity for your player and DSPs + +# Sample Video Ad Unit: Outstream +``` +var videoAdUnit = { + code: 'test-div', + sizes: [[410, 231]], + mediaTypes: { + video: { + playerSize: [[410, 231]], + context: "outstream" + api: [2], + placement: 5, + linearity: 1, + minduration: 1, + maxduration: 120, + mimes: ["video/mp4", "video/x-flv", "video/x-ms-wmv", "application/vnd.apple.mpegurl", "application/x-mpegurl", "video/3gpp", "video/mpeg", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v", "video/ms-asf", video/x-msvideo"], + playbackmethod: [6], + startdelay: 0, + protocols: [1, 2, 3, 4, 5, 6] + }, + }, + bids: [ + { + bidder: 'apacdex', + params: { + siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared + } + } + ] +}; +``` +mediaTypes.video object reference to section 3.2.7 Object: Video in the OpenRTB 2.5 document +You must review all video parameters to ensure validity for your player and DSPs \ No newline at end of file diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index d28bf391aa5..404a8c04d14 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -244,10 +244,12 @@ export const spec = { if (bidRequests[0].userId) { let eids = []; + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.flocId.id`), 'chrome.com', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.criteoId`), 'criteo.com', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.netId`), 'netid.de', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.idl_env`), 'liveramp.com', null); addUserId(eids, utils.deepAccess(bidRequests[0], `userId.tdid`), 'adserver.org', 'TDID'); + addUserId(eids, utils.deepAccess(bidRequests[0], `userId.uid2.id`), 'uidapi.com', 'UID2'); if (eids.length) { payload.eids = eids; diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index da5f385b0da..43df7eef9ae 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -71,13 +71,15 @@ export const spec = { let firstSize = getFirstSize(sizes); let context = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); let responseType = getVideoBidParam(bidRequest, 'responseType') || 'both'; + let responseMeta = Object.assign({ mediaType: VIDEO, advertiserDomains: [] }, response.meta); let bidResponse = { requestId: bidRequest.bidId, bidderCode: spec.code, cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, - creativeId: response.crid || response.cmpId, + creativeId: response.crid, + meta: responseMeta, renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null, mediaType: VIDEO, currency: CURRENCY, @@ -103,6 +105,7 @@ export const spec = { .filter(bid => bid.adm) .map((bid) => { let request = find(bidRequest, req => req.adUnitCode === bid.slot); + let responseMeta = Object.assign({ mediaType: BANNER, advertiserDomains: [] }, bid.meta); return { requestId: request.bidId, bidderCode: spec.code, @@ -111,6 +114,7 @@ export const spec = { cpm: bid.price, width: bid.w, height: bid.h, + meta: responseMeta, mediaType: BANNER, currency: CURRENCY, netRevenue: true, diff --git a/modules/bluebillywigBidAdapter.js b/modules/bluebillywigBidAdapter.js index cac993b0f8c..3d4eeb058b3 100644 --- a/modules/bluebillywigBidAdapter.js +++ b/modules/bluebillywigBidAdapter.js @@ -8,7 +8,7 @@ import { createEidsArray } from './userId/eids.js'; const DEV_MODE = window.location.search.match(/bbpbs_debug=true/); -// Blue Billywig Constants +// Blue Billywig Constants const BB_CONSTANTS = { BIDDER_CODE: 'bluebillywig', AUCTION_URL: '$$URL_STARTpbs.bluebillywig.com/openrtb2/auction?pub=$$PUBLICATION', @@ -122,7 +122,8 @@ const BB_HELPERS = { if (!bidObject.vastUrl && bid.nurl && !bid.adm) { // ad markup is on win notice url, and adm is ommited according to OpenRTB 2.5 bidObject.vastUrl = bid.nurl; } - + bidObject.meta = bid.meta || {}; + if (bid.adomain) { bidObject.meta.advertiserDomains = bid.adomain; } return bidObject; }, }; diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 58c4e907328..2034a59242e 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -4,8 +4,8 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; const BIDDER_CODE = 'bridgewell'; -const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb=' + Math.random(); -const BIDDER_VERSION = '0.0.3'; +const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb='; +const BIDDER_VERSION = '1.1.0'; export const spec = { code: BIDDER_CODE, @@ -37,7 +37,12 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const adUnits = []; + var bidderUrl = REQUEST_ENDPOINT + Math.random(); + var userIds; + utils._each(validBidRequests, function (bid) { + userIds = bid.userId; + if (bid.params.cid) { adUnits.push({ cid: bid.params.cid, @@ -47,7 +52,8 @@ export const spec = { banner: { sizes: bid.sizes } - } + }, + userIds: userIds || {} }); } else { adUnits.push({ @@ -58,7 +64,8 @@ export const spec = { banner: { sizes: bid.sizes } - } + }, + userIds: userIds || {} }); } }); @@ -70,7 +77,7 @@ export const spec = { return { method: 'POST', - url: REQUEST_ENDPOINT, + url: bidderUrl, data: { version: { prebid: '$prebid.version$', diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 5539004bdcd..531238c7400 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -58,6 +58,7 @@ export const spec = { let placement = { placementId: bid.params.placement_id, bidId: bid.bidId, + floor: {}, }; if (bid.mediaTypes.hasOwnProperty(BANNER)) { @@ -87,6 +88,23 @@ export const spec = { placement['playbackmethod'] = bid.mediaTypes.video.playbackmethod; } } + + if (typeof bid.getFloor === 'function') { + let floorInfo = {}; + + for (let size of placement['sizes']) { + floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: placement['traffic'], + size: size, + }); + + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') { + placement.floor[`${size[0]}x${size[1]}`] = parseFloat(floorInfo.floor); + } + } + } + if (bid.schain) { placement.schain = bid.schain; } @@ -105,10 +123,26 @@ export const spec = { if (response && Array.isArray(response) && response.length > 0) { for (let i = 0; i < response.length; i++) { if (response[i].cpm > 0) { + const tempResponse = { + requestId: response[i].requestId, + width: response[i].width, + height: response[i].height, + cpm: response[i].cpm, + mediaType: response[i].mediaType, + creativeId: response[i].creativeId, + currency: response[i].currency, + netRevenue: response[i].netRevenue, + ttl: response[i].ttl, + ad: response[i].ad, + meta: { + advertiserDomains: response[i].adomain && response[i].adomain.length ? response[i].adomain : [], + } + }; + if (response[i].mediaType && response[i].mediaType === 'video') { response[i].vastXml = response[i].ad; } - bidResponse.push(response[i]); + bidResponse.push(tempResponse); } } } diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index 2aad211b186..8299a2331cb 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -46,7 +46,7 @@ function buildRequests(bidReqs, bidderRequest) { }, tagid: String(bid.adUnitCode) }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + const bidFloor = _getBidFloor(bid); if (bidFloor) { imp.bidfloor = bidFloor; } @@ -122,7 +122,10 @@ function interpretResponse(serverResponse) { netRevenue: true, mediaType: BANNER, ad: _getAdMarkup(brightcomBid), - ttl: 60 + ttl: 60, + meta: { + advertiserDomains: brightcomBid && brightcomBid.adomain ? brightcomBid.adomain : [] + } }); }); } @@ -248,4 +251,20 @@ function _getPercentInView(element, topWin, { w, h } = {}) { return 0; } +function _getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 37b6fdc3e98..2160e539040 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -94,12 +94,12 @@ function _buildBid (bid) { } } - placement.video.protocols = utils.deepAccess(bid, 'params.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS - placement.video.mimes = utils.deepAccess(bid, 'params.video.mimes') || SUPPORTED_VIDEO_MIMES - placement.video.playbackmethod = utils.deepAccess(bid, 'params.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS - placement.video.skip = utils.deepAccess(bid, 'params.video.skip') || 0 - if (placement.video.skip === 1 && utils.deepAccess(bid, 'params.video.skipafter')) { - placement.video.skipafter = utils.deepAccess(bid, 'params.video.skipafter') + placement.video.protocols = utils.deepAccess(bid, 'mediaTypes.video.protocols') || utils.deepAccess(bid, 'params.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS + placement.video.mimes = utils.deepAccess(bid, 'mediaTypes.video.mimes') || utils.deepAccess(bid, 'params.video.mimes') || SUPPORTED_VIDEO_MIMES + placement.video.playbackmethod = utils.deepAccess(bid, 'mediaTypes.video.playbackmethod') || utils.deepAccess(bid, 'params.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS + placement.video.skip = utils.deepAccess(bid, 'mediaTypes.video.skip') || utils.deepAccess(bid, 'params.video.skip') || 0 + if (placement.video.skip === 1 && (utils.deepAccess(bid, 'mediaTypes.video.skipafter') || utils.deepAccess(bid, 'params.video.skipafter'))) { + placement.video.skipafter = utils.deepAccess(bid, 'mediaTypes.video.skipafter') || utils.deepAccess(bid, 'params.video.skipafter') } } diff --git a/modules/ccxBidAdapter.md b/modules/ccxBidAdapter.md index 740457dd099..7d86507bccb 100644 --- a/modules/ccxBidAdapter.md +++ b/modules/ccxBidAdapter.md @@ -32,67 +32,21 @@ Module that connects to Clickonometrics's demand sources mediaTypes: { video: { playerSize: [1920, 1080] - + protocols: [2, 3, 5, 6], //default + mimes: ["video/mp4", "video/x-flv"], //default + playbackmethod: [1, 2, 3, 4], //default + skip: 1, //default 0 + skipafter: 5 //delete this key if skip = 0 } }, bids: [ { bidder: "ccx", params: { - placementId: 3287742, - //following options are not required, default values will be used. Uncomment if you want to use custom values - /*video: { - //check OpenRTB documentation for following options description - protocols: [2, 3, 5, 6], //default - mimes: ["video/mp4", "video/x-flv"], //default - playbackmethod: [1, 2, 3, 4], //default - skip: 1, //default 0 - skipafter: 5 //delete this key if skip = 0 - }*/ + placementId: 3287742 } } ] } ]; - -# Pre 1.0 Support - - var adUnits = [ - { - code: 'test-banner', - mediaType: 'banner', - sizes: [300, 250], - bids: [ - { - bidder: "ccx", - params: { - placementId: 3286844 - } - } - ] - }, - { - code: 'test-video', - mediaType: 'video', - sizes: [1920, 1080] - bids: [ - { - bidder: "ccx", - params: { - placementId: 3287742, - //following options are not required, default values will be used. Uncomment if you want to use custom values - /*video: { - //check OpenRTB documentation for following options description - protocols: [2, 3, 5, 6], //default - mimes: ["video/mp4", "video/x-flv"], //default - playbackmethod: [1, 2, 3, 4], //default - skip: 1, //default 0 - skipafter: 5 //delete this key if skip = 0 - }*/ - } - } - ] - } - - ]; \ No newline at end of file diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 43f5cc01ccf..a6241980e06 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -85,7 +85,11 @@ export const spec = { height: response.height, creativeId: response.creativeId, ttl: response.ttl, - ad: response.ad + ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length ? response.adomain : [], + mediaType: response.mediaType + } }; bidResponses.push(bidResponse); diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 4fa2a56a004..111b6ac10e8 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -124,6 +124,7 @@ export const spec = { bid.width = decision.width; bid.height = decision.height; bid.dealid = decision.dealid || null; + bid.meta = { advertiserDomains: decision && decision.adomain ? decision.adomain : [] }; bid.ad = retrieveAd(decision); bid.currency = 'USD'; bid.creativeId = decision.adId; diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 806f276fb72..5af399365bd 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -62,7 +62,7 @@ export const spec = { let bidurl = URL; const conversantImps = validBidRequests.map(function(bid) { - const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); + const bidfloor = getBidFloor(bid); siteId = utils.getBidIdParameter('site_id', bid.params) || siteId; pubcidName = utils.getBidIdParameter('pubcid_name', bid.params) || pubcidName; @@ -378,4 +378,28 @@ function readStoredValue(key) { return storedValue; } +/** + * Get the floor price from bid.params for backward compatibility. + * If not found, then check floor module. + * @param bid A valid bid object + * @returns {*|number} floor price + */ +function getBidFloor(bid) { + let floor = utils.getBidIdParameter('bidfloor', bid.params); + + if (!floor && utils.isFn(bid.getFloor)) { + const floorObj = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + + if (utils.isPlainObject(floorObj) && !isNaN(floorObj.floor) && floorObj.currency === 'USD') { + floor = floorObj.floor; + } + } + + return floor +} + registerBidder(spec); diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index b00a3eae659..47d2eb2652f 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -68,7 +68,7 @@ export const spec = { id: bidRequest.bidId, tagid: bidRequest.adUnitCode, secure: window.location.protocol == 'https:' - } + }; if (utils.deepAccess(bidRequest, `mediaTypes.banner`)) { let sizes = bidRequest.mediaTypes.banner.sizes; @@ -76,7 +76,7 @@ export const spec = { imp.banner = { w: sizes[0][0], h: sizes[0][1] - } + }; } else if (sizes.length > 1) { imp.banner = { format: sizes.map(size => ({ w: size[0], h: size[1] })) @@ -181,7 +181,7 @@ export const spec = { if (VIDEO_PARAMS.indexOf(k) > -1) { imp.video[k] = bidRequest.params.video[k]; } - }) + }); } } let host = bidRequest.params.host; diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js new file mode 100644 index 00000000000..58cec36a6b9 --- /dev/null +++ b/modules/dgkeywordRtdProvider.js @@ -0,0 +1,134 @@ +/** + * This module adds dgkeyword provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will get keywords from 1plux profile api. + * This module can work only with AppNexusBidAdapter. + * @module modules/dgkeywordProvider + * @requires module:modules/realTimeData + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +/** + * get keywords from api server. and set keywords. + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} moduleConfig + * @param {Object} userConsent + */ +export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { + const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; + const PROFILE_TIMEOUT_MS = 1000; + const timeout = (moduleConfig && moduleConfig.params && moduleConfig.params.timeout && Number(moduleConfig.params.timeout) > 0) ? Number(moduleConfig.params.timeout) : PROFILE_TIMEOUT_MS; + const url = (moduleConfig && moduleConfig.params && moduleConfig.params.url) ? moduleConfig.params.url : URL + encodeURIComponent(window.location.href); + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + let isFinish = false; + utils.logMessage('[dgkeyword sub module]', adUnits, timeout); + let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); + if (setKeywordTargetBidders.length <= 0) { + utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); + callback(); + } else { + utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); + utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); + ajax(url, { + success: function(response) { + const res = JSON.parse(response); + if (!isFinish) { + utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); + if (res) { + let keywords = {}; + if (res['s'] != null && res['s'].length > 0) { + keywords['opeaud'] = res['s']; + } + if (res['t'] != null && res['t'].length > 0) { + keywords['opectx'] = res['t']; + } + if (Object.keys(keywords).length > 0) { + const targetBidKeys = {} + for (let bid of setKeywordTargetBidders) { + // set keywords to params + bid.params.keywords = keywords; + if (!targetBidKeys[bid.bidder]) { + targetBidKeys[bid.bidder] = true; + } + } + + if (!reqBidsConfigObj._ignoreSetOrtb2) { + // set keywrods to ortb2 + let addOrtb2 = {}; + utils.deepSetValue(addOrtb2, 'site.keywords', keywords); + utils.deepSetValue(addOrtb2, 'user.keywords', keywords); + const ortb2 = {ortb2: addOrtb2}; + reqBidsConfigObj.setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); + } + } + } + isFinish = true; + } + callback(); + }, + error: function(errorStatus) { + // error occur + utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); + callback(); + } + }, null, { + withCredentials: true, + contentType: 'application/json', + }); + setTimeout(function () { + if (!isFinish) { + // profile api timeout + utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); + isFinish = true; + } + callback(); + }, timeout); + } +} + +/** + * get all bidder which hava {dgkeyword: true} in params + * @param {Object} adUnits + */ +export function getTargetBidderOfDgKeywords(adUnits) { + let setKeywordTargetBidders = []; + for (let adUnit of adUnits) { + for (let bid of adUnit.bids) { + if (bid.params && bid.params['dgkeyword'] === true) { + delete bid.params['dgkeyword']; + setKeywordTargetBidders.push(bid); + } + } + } + return setKeywordTargetBidders; +} + +/** @type {RtdSubmodule} */ +export const dgkeywordSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'dgkeyword', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getBidRequestData: getDgKeywordsAndSet, + init: init, +}; + +function init(moduleConfig) { + return true; +} + +function registerSubModule() { + submodule('realTimeData', dgkeywordSubmodule); +} +registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md new file mode 100644 index 00000000000..314475b45a8 --- /dev/null +++ b/modules/dgkeywordRtdProvider.md @@ -0,0 +1,29 @@ + ## Integration + +1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: + +``` +gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." +``` + +2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` + +```javascript +var DGKEYWORD_TIMEOUT = 1000; +pbjs.setConfig({ + realTimeData: { + auctionDelay: DGKEYWORD_TIMEOUT, + dataProviders: [{ + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DGKEYWORD_TIMEOUT + } + }] + } +}); +``` diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 41a5af6d703..bcdcd7393f7 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -223,7 +223,7 @@ export const spec = { utils._each(validBidRequests, function (bid) { let tagid = utils.getBidIdParameter('tagid', bid.params); - let bidfloor = parseFloat(utils.getBidIdParameter('bidfloor', bid.params)) || 0; + let bidfloor = parseFloat(getBidFloor(bid)) || 0; let isVideo = !!bid.mediaTypes.video; let data = { id: bid.bidId, @@ -287,6 +287,14 @@ export const spec = { bidResponse = emxAdapter.formatVideoResponse(bidResponse, Object.assign({}, emxBid), bidRequest); } bidResponse.mediaType = (isVideo ? VIDEO : BANNER); + + // support for adomain in prebid 5.0 + if (emxBid.adomain && emxBid.adomain.length) { + bidResponse.meta = { + advertiserDomains: emxBid.adomain + }; + } + emxBidResponses.push(bidResponse); }); } @@ -312,4 +320,22 @@ export const spec = { return syncs; } }; + +// support floors module in prebid 5.0 +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return parseFloat(utils.getBidIdParameter('bidfloor', bid.params)); + } + + let floor = bid.getFloor({ + currency: DEFAULT_CUR, + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index 98a0e290575..dd96353dea3 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -82,9 +82,12 @@ export const spec = { if (bidderRequest && bidderRequest.uspConsent) { params.ccpa = bidderRequest.uspConsent; } - const userIds = (getGlobal()).getUserIds(); - for (var id in userIds) { - params['e_' + id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]); + + if ((getGlobal()).getUserIds && typeof (getGlobal()).getUserIds === 'function') { + const userIds = (getGlobal()).getUserIds(); + for (var id in userIds) { + params['e_' + id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]); + } } } @@ -114,6 +117,11 @@ export const spec = { netRevenue: NET_REVENUE, currency: DOLLARS, }; + if (ad.adom) { + bidResponse.meta = { + advertiserDomains: ad.adom + }; + } bidResponses.push(bidResponse); }); } diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index 42c991a17a4..8a1b25cec70 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -1,5 +1,5 @@ -'use strict'; - +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; @@ -57,10 +57,40 @@ export const spec = { data: bidderRequest, bids: validBidRequests, netRevenue: netRevenue, + metaData: getMetaData(), bidder: 'etarget', gdpr: gdprObject }; + function getMetaData() { + var mts = {}; + var hmetas = document.getElementsByTagName('meta'); + var wnames = ['title', 'og:title', 'description', 'og:description', 'og:url', 'base', 'keywords']; + try { + for (var k in hmetas) { + if (typeof hmetas[k] == 'object') { + var mname = hmetas[k].name || hmetas[k].getAttribute('property'); + var mcont = hmetas[k].content; + if (!!mname && mname != 'null' && !!mcont) { + if (wnames.indexOf(mname) >= 0) { + if (!mts[mname]) { + mts[mname] = []; + } + mts[mname].push(mcont); + } + } + } + } + mts['title'] = [(document.getElementsByTagName('title')[0] || []).innerHTML]; + mts['base'] = [(document.getElementsByTagName('base')[0] || {}).href]; + mts['referer'] = [document.location.href]; + mts['ortb2'] = (config.getConfig('ortb2') || {}); + } catch (e) { + mts.error = e; + } + return mts; + } + function formRequestUrl(reqData) { var key; var url = []; @@ -107,9 +137,14 @@ export const spec = { bidObject.gdpr = bidRequest.gdpr.gdpr; bidObject.gdpr_consent = bidRequest.gdpr.gdpr_consent; } + + if (bid.adomain) { + utils.deepSetValue(bidObject, 'meta.advertiserDomains', Array.isArray(bid.adomain) ? bid.adomain : [bid.adomain]); + } bidRespones.push(bidObject); } } + return bidRespones; function verifySize(adItem, validSizes) { diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 77589cd9071..df33369d6ad 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -95,7 +95,10 @@ export const spec = { referrer: responses[i].Referrer, ad: responses[i].Ad, vastUrl: responses[i].VastUrl, - mediaType: responses[i].MediaType + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index 5eac4069b21..bf57e63d53c 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -94,6 +94,10 @@ export const spec = { ttl: res.ttl || 300 }; + if (res.adomains) { + utils.deepSetValue(bid, 'meta.advertiserDomains', Array.isArray(res.adomains) ? res.adomains : [res.adomains]); + } + return [bid]; }, diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 955aeff7168..d4aceaea162 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -347,10 +347,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { height: serverBid.h, creativeId: serverBid.auid, // bid.bidId, currency: 'USD', - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: serverBid && serverBid.adomain ? serverBid.adomain : [] + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] }, dealId: serverBid.dealid }; diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index af1e9f84f43..4ab8464b115 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -24,6 +24,8 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; +const VIDEO_KEYS = ['mimes', 'protocols', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype']; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ VIDEO ], @@ -39,11 +41,12 @@ export const spec = { !bid.params.secid || !utils.isStr(bid.params.secid) || !bid.params.pubid || !utils.isStr(bid.params.pubid); + const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const { protocols = video.protocols, mimes = video.mimes } = utils.deepAccess(bid, 'params.video') || {}; if (!invalid) { - invalid = !bid.params.video || !bid.params.video.protocols || !bid.params.video.mimes; + invalid = !protocols || !mimes; } if (!invalid) { - const {protocols, mimes} = bid.params.video; invalid = !utils.isArray(mimes) || !mimes.length || mimes.filter((it) => !(it && utils.isStr(it))).length; if (!invalid) { invalid = !utils.isArray(protocols) || !protocols.length || protocols.filter((it) => !(utils.isNumber(it) && it > 0 && !(it % 1))).length; @@ -63,7 +66,7 @@ export const spec = { const requests = []; bids.forEach(bid => { - const {params, bidderRequestId, sizes} = bid; + const { params, bidderRequestId, sizes } = bid; const payload = { sizes: utils.parseSizesInput(sizes).join(','), r: bidderRequestId, @@ -91,11 +94,32 @@ export const spec = { } } + const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const paramsVideo = Object.assign({}, params.video); + VIDEO_KEYS.forEach((key) => { + if (!(key in paramsVideo) && key in video) { + paramsVideo[key] = video[key]; + } + }); + + if (!paramsVideo.size && video.playerSize && video.playerSize.length === 2) { + paramsVideo.size = video.playerSize.join('x'); + } + + if (!('mind' in paramsVideo) && 'minduration' in video) { + paramsVideo.mind = video.minduration; + } + if (!('maxd' in paramsVideo) && 'maxduration' in video) { + paramsVideo.maxd = video.maxduration; + } + + const paramsToSend = Object.assign({}, params, {video: paramsVideo}); + requests.push({ method: 'POST', url: ENDPOINT_URL + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, ''), bid: bid, - data: params // content + data: paramsToSend // content }); }); @@ -139,11 +163,14 @@ export const spec = { height: serverBid.h, creativeId: serverBid.auid || bid.bidderRequestId, currency: 'USD', - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, dealId: serverBid.dealid, vastXml: serverBid.adm, mediaType: VIDEO, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, adResponse: { content: serverBid.adm } diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index e7281086a92..15f8acd824f 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -78,7 +78,10 @@ function buildBid(bidData) { creativeId: bidData.bidId, currency: bidData.currency, netRevenue: true, - ttl: TTL + ttl: TTL, + meta: { + advertiserDomains: bidData.advertiserDomains || [], + } }; if (bidData.placement === PLACEMENT_TYPE_VIDEO) { diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 6a662361492..8ffe29e091f 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -1,6 +1,6 @@ # ID5 Universal ID -The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://wiki.id5.io/x/BIAZ). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. +The ID5 Universal ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 Universal ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 Universal ID and detailed integration docs, please visit [our documentation](https://support.id5.io/portal/en/kb/articles/prebid-js-user-id-module). We also recommend that you sign up for our [release notes](https://id5.io/universal-id/release-notes) to stay up-to-date with any changes to the implementation of the ID5 Universal ID in Prebid. ## ID5 Universal ID Registration @@ -48,7 +48,7 @@ pbjs.setConfig({ | name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | | params | Required | Object | Details for the ID5 Universal ID. | | | params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | -| params.pd | Optional | String | Publisher-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/x/BIAZ) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | +| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | | params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | | params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | | params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index d4d26b1e017..18011359a6d 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -8,10 +8,11 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 5, + PREBID_VERSION: 6, METHOD: 'GET', INVIBES_VENDOR_ID: 436, - USERID_PROVIDERS: ['pubcid', 'pubProvidedId'] + USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'], + META_TAXONOMY: ['networkId', 'networkName', 'agencyId', 'agencyName', 'advertiserId', 'advertiserName', 'advertiserDomains', 'brandId', 'brandName', 'primaryCatId', 'secondaryCatIds', 'mediaType'] }; const storage = getStorageManager(CONSTANTS.INVIBES_VENDOR_ID); @@ -168,12 +169,6 @@ function handleResponse(responseObj, bidRequests) { responseObj = responseObj.body || responseObj; responseObj = responseObj.videoAdContentResult || responseObj; - let bidModel = responseObj.BidModel; - if (typeof bidModel !== 'object') { - utils.logInfo('Invibes Adapter - Bidding is not configured'); - return []; - } - if (typeof invibes.bidResponse === 'object') { utils.logInfo('Invibes Adapter - Bid response already received. Invibes only responds to one bid request per user visit'); return []; @@ -181,51 +176,83 @@ function handleResponse(responseObj, bidRequests) { invibes.bidResponse = responseObj; - let ads = responseObj.Ads; + const bidResponses = []; + for (let i = 0; i < bidRequests.length; i++) { + let bidRequest = bidRequests[i]; + + let requestPlacement = null; + if (responseObj.AdPlacements != null) { + for (let j = 0; j < responseObj.AdPlacements.length; j++) { + let bidModel = responseObj.AdPlacements[j].BidModel; + if (bidModel != null && bidModel.PlacementId == bidRequest.params.placementId) { + requestPlacement = responseObj.AdPlacements[j]; + break; + } + } + } else { + let bidModel = responseObj.BidModel; + if (bidModel != null && bidModel.PlacementId == bidRequest.params.placementId) { + requestPlacement = responseObj; + } + } + let bid = createBid(bidRequest, requestPlacement); + if (bid !== null) { + bidResponses.push(bid); + } + } + + return bidResponses; +} + +function createBid(bidRequest, requestPlacement) { + if (requestPlacement === null || requestPlacement.BidModel === null) { + utils.logInfo('Invibes Adapter - Placement not configured for bidding ' + bidRequest.params.placementId); + return null; + } + + let bidModel = requestPlacement.BidModel; + let ads = requestPlacement.Ads; if (!Array.isArray(ads) || ads.length < 1) { - if (responseObj.AdReason != null) { - utils.logInfo('Invibes Adapter - ' + responseObj.AdReason); + if (requestPlacement.AdReason != null) { + utils.logInfo('Invibes Adapter - No ads ' + requestPlacement.AdReason); } utils.logInfo('Invibes Adapter - No ads available'); - return []; + return null; } let ad = ads[0]; + let size = getBiggerSize(bidRequest.sizes); - if (bidModel.PlacementId == null) { - utils.logInfo('Invibes Adapter - No Placement Id in response'); - return []; - } - - const bidResponses = []; - for (let i = 0; i < bidRequests.length; i++) { - let bidRequest = bidRequests[i]; + const now = Date.now(); + utils.logInfo('Bid auction started at ' + bidModel.AuctionStartTime + ' . Invibes registered the bid at ' + now + ' ; bid request took a total of ' + (now - bidModel.AuctionStartTime) + ' ms.'); - if (bidModel.PlacementId == bidRequest.params.placementId) { - let size = getBiggerSize(bidRequest.sizes); - - bidResponses.push({ - requestId: bidRequest.bidId, - cpm: ad.BidPrice, - width: bidModel.Width || size[0], - height: bidModel.Height || size[1], - creativeId: ad.VideoExposedId, - currency: bidModel.Currency || CONSTANTS.DEFAULT_CURRENCY, - netRevenue: true, - ttl: CONSTANTS.TIME_TO_LIVE, - ad: renderCreative(bidModel) - }); + return { + requestId: bidRequest.bidId, + cpm: ad.BidPrice, + width: bidModel.Width || size[0], + height: bidModel.Height || size[1], + creativeId: ad.VideoExposedId, + currency: bidModel.Currency || CONSTANTS.DEFAULT_CURRENCY, + netRevenue: true, + ttl: CONSTANTS.TIME_TO_LIVE, + ad: renderCreative(bidModel), + meta: addMeta(bidModel.Meta) + }; +} - const now = Date.now(); - ivLogger.info('Bid auction started at ' + bidModel.AuctionStartTime + ' . Invibes registered the bid at ' + now + ' ; bid request took a total of ' + (now - bidModel.AuctionStartTime) + ' ms.'); - } else { - utils.logInfo('Invibes Adapter - Incorrect Placement Id: ' + bidRequest.params.placementId); +function addMeta(bidModelMeta) { + var meta = {}; + if (bidModelMeta != null) { + for (let i = 0; i < CONSTANTS.META_TAXONOMY.length; i++) { + if (bidModelMeta.hasOwnProperty(CONSTANTS.META_TAXONOMY[i])) { + meta[CONSTANTS.META_TAXONOMY[i]] = bidModelMeta[CONSTANTS.META_TAXONOMY[i]]; + } } } - return bidResponses; + return meta; } function generateRandomId() { @@ -357,17 +384,6 @@ function getCappedCampaignsAsString() { .join(','); } -const noop = function () { -}; - -function initLogger() { - if (storage.hasLocalStorage() && localStorage.InvibesDEBUG) { - return window.console; - } - - return {info: noop, error: noop, log: noop, warn: noop, debug: noop}; -} - function buildSyncUrl() { let syncUrl = _customUserSync || CONSTANTS.SYNC_ENDPOINT; syncUrl += '?visitId=' + invibes.visitId; @@ -392,7 +408,7 @@ function readGdprConsent(gdprConsent) { if (!gdprConsent.vendorData.gdprApplies || gdprConsent.vendorData.hasGlobalConsent) { var index; - for (index = 0; index < invibes.purposes; ++index) { + for (index = 0; index < invibes.purposes.length; ++index) { invibes.purposes[index] = true; } @@ -448,7 +464,13 @@ function tryCopyValueToArray(value, target, length) { } if (value.hasOwnProperty(prop)) { - target[i] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + let parsedProp = parseInt(prop); + if (isNaN(parsedProp)) { + target[i] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + } else { + target[parsedProp - 1] = !((value[prop] === false || value[prop] === 'false' || value[prop] == null)); + } + i++; } } @@ -515,8 +537,6 @@ function getVendorLegitimateInterest(vendorData) { return {}; } -const ivLogger = initLogger(); - /// Local domain cookie management ===================== invibes.Uid = { generate: function () { diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 64a3237e02a..4837c9c061f 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -2,7 +2,6 @@ import * as utils from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; -import isInteger from 'core-js-pure/features/number/is-integer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'ix'; @@ -23,6 +22,33 @@ const PRICE_TO_DOLLAR_FACTOR = { const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; +// determines which eids we send and the rtiPartner field in ext +const SOURCE_RTI_MAPPING = { + 'liveramp.com': 'idl', + 'netid.de': 'NETID', + 'neustar.biz': 'fabrickId', + 'zeotap.com': 'zeotapIdPlus', + 'uidapi.com': 'UID2', + 'adserver.org': 'TDID' +}; + +const PROVIDERS = [ + 'britepoolid', + 'id5id', + 'lipbid', + 'haloId', + 'criteoId', + 'lotamePanoramaId', + 'merkleId', + 'parrableId', + 'connectid', + 'sharedid', + 'tapadId', + 'quantcastId', + 'pubcid', + 'TDID', + 'flocId' +] /** * Transform valid bid request config object to banner impression object that will be sent to ad server. @@ -86,6 +112,11 @@ function bidToVideoImp(bid) { return imp; } +/** + * Converts an incoming PBJS bid to an IX Impression + * @param {object} bid PBJS bid object + * @returns {object} IX impression object + */ function bidToImp(bid) { const imp = {}; @@ -101,6 +132,10 @@ function bidToImp(bid) { imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`; } + const dfpAdUnitCode = utils.deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot'); + if (dfpAdUnitCode) { + imp.ext.dfp_ad_unit_code = dfpAdUnitCode; + } return imp; } @@ -179,6 +214,8 @@ function _applyFloor(bid, imp, mediaType) { */ function parseBid(rawBid, currency, bidRequest) { const bid = {}; + const isValidExpiry = !!((utils.deepAccess(rawBid, 'exp') && utils.isInteger(rawBid.exp))); + const dealID = utils.deepAccess(rawBid, 'dealid') || utils.deepAccess(rawBid, 'ext.dealid'); if (PRICE_TO_DOLLAR_FACTOR.hasOwnProperty(currency)) { bid.cpm = rawBid.price / PRICE_TO_DOLLAR_FACTOR[currency]; @@ -188,7 +225,10 @@ function parseBid(rawBid, currency, bidRequest) { bid.requestId = rawBid.impid; - bid.dealId = utils.deepAccess(rawBid, 'ext.dealid'); + if (dealID) { + bid.dealId = dealID; + } + bid.netRevenue = NET_REVENUE; bid.currency = currency; bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-'; @@ -199,13 +239,13 @@ function parseBid(rawBid, currency, bidRequest) { bid.width = bidRequest.video.w; bid.height = bidRequest.video.h; bid.mediaType = VIDEO; - bid.ttl = VIDEO_TIME_TO_LIVE; + bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else { bid.ad = rawBid.adm; bid.width = rawBid.w; bid.height = rawBid.h; bid.mediaType = BANNER; - bid.ttl = BANNER_TIME_TO_LIVE; + bid.ttl = isValidExpiry ? rawBid.exp : BANNER_TIME_TO_LIVE; } bid.meta = {}; @@ -226,7 +266,7 @@ function parseBid(rawBid, currency, bidRequest) { * @return {boolean} True if this is a valid size format, and false otherwise. */ function isValidSize(size) { - return Array.isArray(size) && size.length === 2 && isInteger(size[0]) && isInteger(size[1]); + return Array.isArray(size) && size.length === 2 && utils.isInteger(size[0]) && utils.isInteger(size[1]); } /** @@ -285,35 +325,37 @@ function getBidRequest(id, impressions) { * From the userIdAsEids array, filter for the ones our adserver can use, and modify them * for our purposes, e.g. add rtiPartner * @param {array} allEids userIdAsEids passed in by prebid + * @param {object} flocId flocId passed in by prebid * @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter * identity info from IX Library) */ -function getEidInfo(allEids) { - // determines which eids we send and the rtiPartner field in ext - var sourceRTIMapping = { - 'liveramp.com': 'idl', - 'netid.de': 'NETID', - 'neustar.biz': 'fabrickId', - 'zeotap.com': 'zeotapIdPlus', - 'uidapi.com': 'UID2' - }; - var toSend = []; - var seenSources = {}; +function getEidInfo(allEids, flocData) { + let toSend = []; + let seenSources = {}; if (utils.isArray(allEids)) { - for (var i = 0; i < allEids.length; i++) { - if (sourceRTIMapping[allEids[i].source] && utils.deepAccess(allEids[i], 'uids.0')) { - seenSources[allEids[i].source] = 1; - allEids[i].uids[0].ext = { - rtiPartner: sourceRTIMapping[allEids[i].source] + for (const eid of allEids) { + if (SOURCE_RTI_MAPPING[eid.source] && utils.deepAccess(eid, 'uids.0')) { + seenSources[eid.source] = true; + eid.uids[0].ext = { + rtiPartner: SOURCE_RTI_MAPPING[eid.source] }; - delete allEids[i].uids[0].atype; - toSend.push(allEids[i]); + delete eid.uids[0].atype; + toSend.push(eid); } } } - return { toSend: toSend, seenSources: seenSources }; -} + const isValidFlocId = flocData && flocData.id && flocData.version; + if (isValidFlocId) { + const flocEid = { + 'source': 'chrome.com', + 'uids': [{ 'id': flocData.id, 'ext': { 'rtiPartner': 'flocId', 'ver': flocData.version } }] + }; + toSend.push(flocEid); + seenSources['chrome.com'] = true; + } + return { toSend, seenSources }; +} /** * Builds a request object to be sent to the ad server based on bid requests. * @@ -327,10 +369,9 @@ function getEidInfo(allEids) { function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Always use secure HTTPS protocol. let baseUrl = SECURE_BID_URL; - // Get ids from Prebid User ID Modules - var eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids')); - var userEids = eidInfo.toSend; + let eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids'), utils.deepAccess(validBidRequests, '0.userId.flocId')); + let userEids = eidInfo.toSend; // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded // and if the data for the partner exist @@ -357,7 +398,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const r = {}; // Since bidderRequestId are the same for different bid request, just use the first one. - r.id = validBidRequests[0].bidderRequestId; + r.id = validBidRequests[0].bidderRequestId.toString(); r.site = {}; r.ext = {}; @@ -424,6 +465,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } } + if (config.getConfig('coppa')) { + utils.deepSetValue(r, 'regs.coppa', 1); + } + const payload = {}; // Parse additional runtime configs. @@ -570,23 +615,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { function _getUserIds(bidRequest) { const userIds = bidRequest.userId || {}; - const PROVIDERS = [ - 'britepoolid', - 'id5id', - 'lipbid', - 'haloId', - 'criteoId', - 'lotamePanoramaId', - 'merkleId', - 'parrableId', - 'connectid', - 'sharedid', - 'tapadId', - 'quantcastId', - 'pubcid' - ] - - return PROVIDERS.filter(provider => utils.deepAccess(userIds, provider)) + return PROVIDERS.filter(provider => userIds[provider]); } /** diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index 7c6e0027482..db6c9032e65 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -240,6 +240,16 @@ export const spec = { let rendererScript = (oneBid.osparams.script ? oneBid.osparams.script : JX_OUTSTREAM_RENDERER_URL); bnd.renderer = createRenderer_(oneBid, rendererScript, jxOutstreamRender_); } + // a note on advertiserDomains: our adserver is not responding in + // openRTB-type json. so there is no need to copy from 'adomain' over + // to meta: advertiserDomains + // However, we will just make sure the property is there. + if (!bnd.meta) { + bnd.meta = {}; + } + if (!bnd.meta.advertiserDomains) { + bnd.meta.advertiserDomains = []; + } bidResponses.push(bnd); }); if (response.body.setids) { diff --git a/modules/jwplayerRtdProvider.md b/modules/jwplayerRtdProvider.md index 0723e8cbb6c..7fb1bb13d74 100644 --- a/modules/jwplayerRtdProvider.md +++ b/modules/jwplayerRtdProvider.md @@ -2,7 +2,7 @@ The purpose of this Real Time Data Provider is to allow publishers to target aga having to integrate with the Player Bidding product. This prebid module makes JW Player's video ad targeting information accessible to Bid Adapters. -#Usage for Publishers: +# Usage for Publishers: Compile the JW Player RTD Provider into your Prebid build: @@ -30,7 +30,7 @@ Lastly, include the content's media ID and/or the player's ID in the matching Ad ```javascript const adUnit = { code: '/19968336/prebid_native_example_1', - ... + ..., ortb2Imp: { ext: { data: { @@ -51,10 +51,12 @@ pbjs.que.push(function() { }); }); ``` +**Note**: The player ID is the ID of the HTML div element used when instantiating the player. +You can retrieve this ID by calling `player.id`, where player is the JW Player instance variable. **Note**: You may also include `jwTargeting` information in the prebid config's `ortb2.site.ext.data`. Information provided in the adUnit will always supersede, and information in the config will be used as a fallback. -##Prefetching +## Prefetching In order to prefetch targeting information for certain media, include the media IDs in the `jwplayerDataProvider` var and set `waitForIt` to `true`: ```javascript @@ -76,7 +78,7 @@ realTimeData = { }; ``` -#Usage for Bid Adapters: +# Usage for Bid Adapters: Implement the `buildRequests` function. When it is called, the `bidRequests` param will be an array of bids. Each bid for which targeting information was found will conform to the following object structure: @@ -118,6 +120,6 @@ To view an example: **Note:** the mediaIds in the example are placeholder values; replace them with your existing IDs. -#Maintainer info +# Maintainer info Maintained by JW Player. For any questions, comments or feedback please contact Karim Mourra, karim@jwplayer.com diff --git a/modules/koblerBidAdapter.js b/modules/koblerBidAdapter.js index cc5b374af95..59d1639a329 100644 --- a/modules/koblerBidAdapter.js +++ b/modules/koblerBidAdapter.js @@ -27,11 +27,14 @@ export const buildRequests = function (validBidRequests, bidderRequest) { }; export const interpretResponse = function (serverResponse) { + const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; const res = serverResponse.body; const bids = [] if (res) { res.seatbid.forEach(sb => { sb.bid.forEach(b => { + const adWithCorrectCurrency = b.adm + .replace(/\${AUCTION_PRICE_CURRENCY}/g, adServerPriceCurrency); bids.push({ requestId: b.impid, cpm: b.price, @@ -42,7 +45,7 @@ export const interpretResponse = function (serverResponse) { dealId: b.dealid, netRevenue: true, ttl: TIME_TO_LIVE_IN_SECONDS, - ad: b.adm, + ad: adWithCorrectCurrency, nurl: b.nurl, meta: { advertiserDomains: b.adomain @@ -56,10 +59,14 @@ export const interpretResponse = function (serverResponse) { export const onBidWon = function (bid) { const cpm = bid.cpm || 0; + const cpmCurrency = bid.currency || SUPPORTED_CURRENCY; const adServerPrice = utils.deepAccess(bid, 'adserverTargeting.hb_pb', 0); + const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; if (utils.isStr(bid.nurl) && bid.nurl !== '') { const winNotificationUrl = utils.replaceAuctionPrice(bid.nurl, cpm) - .replace(/\${AD_SERVER_PRICE}/g, adServerPrice); + .replace(/\${AUCTION_PRICE_CURRENCY}/g, cpmCurrency) + .replace(/\${AD_SERVER_PRICE}/g, adServerPrice) + .replace(/\${AD_SERVER_PRICE_CURRENCY}/g, adServerPriceCurrency); utils.triggerPixel(winNotificationUrl); } }; diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index 80c40b39f9a..4e30519c6d3 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -66,7 +66,10 @@ export const spec = { currency: bid.currency, netRevenue: bid.netRevenue, ad: bid.ad, - ttl: bid.ttl + ttl: bid.ttl, + meta: { + advertiserDomains: bid.adomain && Array.isArray(bid.adomain) ? bid.adomain : [] + } }; }); }, diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 7deffe6c07a..4e600135e0b 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -95,7 +95,10 @@ export const spec = { referrer: responses[i].Referrer, ad: responses[i].Ad, vastUrl: responses[i].VastUrl, - mediaType: responses[i].MediaType + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/modules/medianetBidAdapter.md b/modules/medianetBidAdapter.md index b465d678fc9..fbe967122e9 100644 --- a/modules/medianetBidAdapter.md +++ b/modules/medianetBidAdapter.md @@ -79,17 +79,16 @@ var videoAdUnit = { mediaTypes: { video: { context: "instream", - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4'], + placement: 1 } }, bids: [{ bidder: 'medianet', params: { cid: '8CUX0H51C', - video: { - mimes: ['video/mp4'], - placement: 1 - }, + crid: '776755783', // Site member is to be used only for testing site: { page: 'http://smoketesting.net/prebidtest/', @@ -111,8 +110,10 @@ var videoAdUnit = { code: 'video1', mediaTypes: { video: { - context: "outstream", - playerSize: [640, 480] + context: "outstream", + playerSize: [640, 480], + mimes: ['video/mp4'], + placement: 1 } }, /** @@ -128,10 +129,7 @@ var videoAdUnit = { bidder: 'medianet', params: { cid: '8CUX0H51C', - video: { - mimes: ['video/mp4'], - placement: 1 - }, + crid: '776755783', // Site member is to be used only for testing site: { page: 'http://smoketesting.net/prebidtest/', diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 84783eb0991..a586157bf20 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -101,6 +101,9 @@ export const spec = { mediasquare: { 'bidder': value['bidder'], 'code': value['code'] + }, + meta: { + 'advertiserDomains': value['adomain'] } }; if ('native' in value) { diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index d396bd4d495..fc0925bf2ca 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -54,9 +54,24 @@ export const spec = { if (!pageUrl) pageUrl = bidderRequest.refererInfo.referer + // Build adUnit data + const adUnitData = { + adUnits: validBidRequests.map((adUnit) => { + return { + adUnitCode: adUnit.adUnitCode, + mediaTypes: adUnit.mediaTypes, + } + }), + } + + // Build QS Params let params = [ { key: 'ntv_ptd', value: placementIds.toString() }, { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + { + key: 'ntv_ppc', + value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 + }, { key: 'ntv_url', value: encodeURIComponent(pageUrl), diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 051202cab97..3aabd8f0635 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.9'; +window.nobidVersion = '1.3.0'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -298,6 +298,9 @@ function nobidInterpretResponse(response, bidRequest) { if (bid.videoCacheKey) { bidResponse.videoCacheKey = bid.videoCacheKey; } + if (bid.meta) { + bidResponse.meta = bid.meta; + } bidResponses.push(bidResponse); } diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js new file mode 100644 index 00000000000..0b4982bc8dc --- /dev/null +++ b/modules/oguryBidAdapter.js @@ -0,0 +1,135 @@ +'use strict'; + +import { BANNER } from '../src/mediaTypes.js'; +import { getAdUnitSizes, logWarn, isFn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'ogury'; +const DEFAULT_TIMEOUT = 1000; +const BID_HOST = 'https://webmobile.presage.io/api/header-bidding-request'; + +function isBidRequestValid(bid) { + const adUnitSizes = getAdUnitSizes(bid); + + const isValidSizes = Boolean(adUnitSizes) && adUnitSizes.length > 0; + const isValidAdUnitId = !!bid.params.adUnitId; + const isValidAssetKey = !!bid.params.assetKey; + + return (isValidSizes && isValidAdUnitId && isValidAssetKey); +} + +function buildRequests(validBidRequests, bidderRequest) { + const openRtbBidRequestBanner = { + id: bidderRequest.auctionId, + tmax: DEFAULT_TIMEOUT, + at: 2, + regs: { + ext: { + gdpr: 1 + }, + }, + site: { + domain: location.hostname + }, + user: { + ext: { + consent: '' + } + }, + imp: [] + }; + + if (bidderRequest.hasOwnProperty('gdprConsent') && + bidderRequest.gdprConsent.hasOwnProperty('gdprApplies')) { + openRtbBidRequestBanner.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + } + + if (bidderRequest.hasOwnProperty('gdprConsent') && + bidderRequest.gdprConsent.hasOwnProperty('consentString') && + bidderRequest.gdprConsent.consentString.length > 0) { + openRtbBidRequestBanner.user.ext.consent = bidderRequest.gdprConsent.consentString + } + + validBidRequests.forEach((bidRequest) => { + const sizes = getAdUnitSizes(bidRequest) + .map(size => ({ w: size[0], h: size[1] })); + + if (bidRequest.hasOwnProperty('mediaTypes') && + bidRequest.mediaTypes.hasOwnProperty('banner')) { + openRtbBidRequestBanner.site.id = bidRequest.params.assetKey; + + openRtbBidRequestBanner.imp.push({ + id: bidRequest.bidId, + tagid: bidRequest.params.adUnitId, + bidfloor: getFloor(bidRequest), + banner: { + format: sizes + } + }); + } + }); + + return { + method: 'POST', + url: BID_HOST, + data: openRtbBidRequestBanner + }; +} + +function interpretResponse(openRtbBidResponse) { + if (!openRtbBidResponse || + !openRtbBidResponse.body || + typeof openRtbBidResponse.body != 'object' || + Object.keys(openRtbBidResponse.body).length === 0) { + logWarn('no response or body is malformed'); + return []; + } + + const bidResponses = []; + + openRtbBidResponse.body.seatbid.forEach((seatbid) => { + seatbid.bid.forEach((bid) => { + let bidResponse = { + requestId: bid.impid, + cpm: bid.price, + currency: 'USD', + width: bid.w, + height: bid.h, + creativeId: bid.id, + netRevenue: true, + ttl: 60, + meta: { + advertiserDomains: bid.adomain + } + }; + + bidResponse.ad = bid.adm; + + bidResponses.push(bidResponse); + }); + }); + return bidResponses; +} + +function getFloor(bid) { + if (!isFn(bid.getFloor)) { + return 0; + } + let floorResult = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: '*' + }); + return floorResult.currency === 'USD' ? floorResult.floor : 0; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + getFloor +} + +registerBidder(spec); diff --git a/modules/oguryBidAdapter.md b/modules/oguryBidAdapter.md new file mode 100644 index 00000000000..00896762dc4 --- /dev/null +++ b/modules/oguryBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +``` +Module Name: Ogury Bidder Adapter +Module Type: Bidder Adapter +Maintainer: web.inventory@ogury.co +``` + +# Description + +Module that connects to Ogury's SSP solution +Ogury bid adapter supports Banner media type. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: "ogury", + params: { + assetKey: 'OGY-CA41D116484F', + adUnitId: '2c4d61d0-90aa-0139-0cda-0242ac120004' + } + } + ] + } + ]; +``` diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index e57c9d12ff7..dfedbd156a9 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.1.0', + VERSION: '3.1.1', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -17,26 +17,52 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { + // Bidder code validation if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { return false; } - // Video validations - if (typeof bid.params.video === 'undefined' || typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight == 'undefined' || typeof bid.params.video.mimes == 'undefined') { - return false; + // E2E test skip validations + if (bid.params && bid.params.video && bid.params.video.e2etest) { + return true; } + // MediaTypes Video / Banner validation + if (typeof bid.mediaTypes.video === 'undefined' && typeof bid.mediaTypes.banner === 'undefined') { + utils.logError('Failed validation: adUnit mediaTypes.video OR mediaTypes.banner not declared'); + return false; + }; - // Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support if (bid.mediaTypes.video) { - if (bid.mediaTypes.video.context === 'outstream' && bid.params.video.display === 1) { + // Player size validation + if (typeof bid.mediaTypes.video.playerSize === 'undefined') { + if (bid.params.video && (typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight === 'undefined')) { + utils.logError('Failed validation: Player size not declared in either mediaTypes.playerSize OR bid.params.video.plauerWidth & bid.params.video.playerHeight.'); + return false; + }; + }; + // Mimes validation + if (typeof bid.mediaTypes.video.mimes === 'undefined') { + if (!bid.params.video || typeof bid.params.video.mimes === 'undefined') { + utils.logError('Failed validation: adUnit mediaTypes.mimes OR params.video.mimes not declared'); + return false; + }; + }; + // Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support + if (bid.mediaTypes.video.context === 'outstream' && bid.params.video && bid.params.video.display === 1) { + utils.logError('Failed validation: Dynamic Ad Placement cannot be used with context Outstream (params.video.display=1)'); return false; - } - } else if (bid.mediaTypes.banner && !bid.params.video.display) { + }; + }; + + // DAP Validation + if (bid.mediaTypes.banner && bid.params.video && !bid.params.video.display) { + utils.logError('Failed validation: If you are trying to use Dynamic Ad Placement you must pass params.video.display=1'); return false; - } + }; - // Pub Id validation + // Publisher Id (Exchange) validation if (typeof bid.params.pubId === 'undefined') { + utils.logError('Failed validation: Adapter cannot send requests without bid.params.pubId'); return false; } @@ -49,7 +75,7 @@ export const spec = { * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(bids, bidRequest) { + buildRequests: function (bids, bidRequest) { let consentData = bidRequest ? bidRequest.gdprConsent : null; return bids.map(bid => { @@ -100,7 +126,10 @@ export const spec = { currency: response.cur, ttl: (bidRequest.params.video.ttl > 0 && bidRequest.params.video.ttl <= 3600) ? bidRequest.params.video.ttl : 300, netRevenue: true, - adUnitCode: bidRequest.adUnitCode + adUnitCode: bidRequest.adUnitCode, + meta: { + advertiserDomains: bid.adomain + } }; bidResponse.mediaType = (bidRequest.mediaTypes.banner) ? 'banner' : 'video' @@ -194,40 +223,54 @@ function getRequestData(bid, consentData, bidRequest) { if (bid.params.video.display == undefined || bid.params.video.display != 1) { bidData.imp[0].video = { - mimes: bid.params.video.mimes, - w: bid.params.video.playerWidth, - h: bid.params.video.playerHeight, - pos: bid.params.video.position, + linearity: 1 + }; + if (bid.params.video.playerWidth && bid.params.video.playerHeight) { + bidData.imp[0].video.w = bid.params.video.playerWidth; + bidData.imp[0].video.h = bid.params.video.playerHeight; + } else { + const playerSize = getSize(bid.mediaTypes.video.playerSize); + bidData.imp[0].video.w = playerSize.width; + bidData.imp[0].video.h = playerSize.height; + }; + if (bid.params.video.mimes) { + bidData.imp[0].video.mimes = bid.params.video.mimes; + } else { + bidData.imp[0].video.mimes = bid.mediaTypes.video.mimes; }; - if (bid.params.video.maxbitrate) { - bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate + if (bid.mediaTypes.video.maxbitrate || bid.params.video.maxbitrate) { + bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate || bid.mediaTypes.video.maxbitrate; } - if (bid.params.video.maxduration) { - bidData.imp[0].video.maxduration = bid.params.video.maxduration + if (bid.mediaTypes.video.maxduration || bid.params.video.maxduration) { + bidData.imp[0].video.maxduration = bid.params.video.maxduration || bid.mediaTypes.video.maxduration; } - if (bid.params.video.minduration) { - bidData.imp[0].video.minduration = bid.params.video.minduration + if (bid.mediaTypes.video.minduration || bid.params.video.minduration) { + bidData.imp[0].video.minduration = bid.params.video.minduration || bid.mediaTypes.video.minduration; } - if (bid.params.video.api) { - bidData.imp[0].video.api = bid.params.video.api + if (bid.mediaTypes.video.api || bid.params.video.api) { + bidData.imp[0].video.api = bid.params.video.api || bid.mediaTypes.video.api; } - if (bid.params.video.delivery) { - bidData.imp[0].video.delivery = bid.params.video.delivery + if (bid.mediaTypes.video.delivery || bid.params.video.delivery) { + bidData.imp[0].video.delivery = bid.params.video.delivery || bid.mediaTypes.video.delivery; } - if (bid.params.video.position) { - bidData.imp[0].video.pos = bid.params.video.position + if (bid.mediaTypes.video.position || bid.params.video.position) { + bidData.imp[0].video.pos = bid.params.video.position || bid.mediaTypes.video.position; } - if (bid.params.video.playbackmethod) { - bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod + if (bid.mediaTypes.video.playbackmethod || bid.params.video.playbackmethod) { + bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod || bid.mediaTypes.video.playbackmethod; } - if (bid.params.video.placement) { - bidData.imp[0].video.placement = bid.params.video.placement + if (bid.mediaTypes.video.placement || bid.params.video.placement) { + bidData.imp[0].video.placement = bid.params.video.placement || bid.mediaTypes.video.placement; } if (bid.params.video.rewarded) { bidData.imp[0].ext.rewarded = bid.params.video.rewarded } - bidData.imp[0].video.linearity = 1; - bidData.imp[0].video.protocols = bid.params.video.protocols || [2, 5]; + if (bid.mediaTypes.video.linearity || bid.params.video.linearity) { + bidData.imp[0].video.linearity = bid.params.video.linearity || bid.mediaTypes.video.linearity || 1; + } + if (bid.mediaTypes.video.protocols || bid.params.video.protocols) { + bidData.imp[0].video.protocols = bid.params.video.protocols || bid.mediaTypes.video.protocols || [2, 5]; + } } else if (bid.params.video.display == 1) { getFloorRequestObject.mediaType = 'banner'; bidData.imp[0].banner = { @@ -307,7 +350,7 @@ function getRequestData(bid, consentData, bidRequest) { } } if (bid.params.video.e2etest) { - utils.logMessage('+++ oneVideoBidAdapter: E2E test mode enabled. \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); + utils.logMessage('E2E test mode enabled: \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); bidData.imp[0].bidfloor = null; bidData.imp[0].video.w = 300; bidData.imp[0].video.h = 250; diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index a42433e80b5..c1762ac0cd3 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -3,12 +3,18 @@ **Module Name**: One Video Bidder Adapter **Module Type**: Bidder Adapter **Maintainer**: deepthi.neeladri.sravana@verizonmedia.com + adam.browning@verizonmedia.com # Description Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to fetch bids. - +# Prebid.js V5.0 Support +The oneVideo adapter now reads `mediaTypes.video` for mandatory parameters such as `playerSize` & `mimes`. +Note: You can use the `bid.params.video` object to specify explicit overrides for whatever is declared in `mediaTypes.video`. +Important: You must pass `bid.params.video = {}` as bare minimum for the adapter to work. # Integration Examples: -## Instream Video adUnit example & parameters + +## Instream Video adUnit using mediaTypes.video +*Note:* By default, the adapter will read the mandatory parameters from mediaTypes.video. *Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. ``` var adUnits = [ @@ -17,7 +23,18 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], } }, bids: [ @@ -25,21 +42,10 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1,5], sid: YOUR_VSSP_ORG_ID, hp: 1, rewarded: 1, - placement: 1, inventoryid: 123, - minduration: 10, - maxduration: 30, ttl: 300, custom: { key1: "value1", @@ -59,16 +65,16 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to } ] ``` -## Outstream Video adUnit example & parameters -*Note:* The Video SSP ad server will load it's own Outstream Renderer (player) as a fallback if no player is defined on the publisher page. The Outstream player will inject into the div id that has an identical adUnit code. +## Instream Video adUnit using params.video overrides +*Note:* If the mandatory parameters are not specified in mediaTypes.video the adapter will read check to see if overrides are set in params.video. Decalring values using params.video will always override the settings in mediaTypes.video. +*Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. ``` var adUnits = [ { code: 'video1', mediaTypes: { video: { - context: 'outstream', - playerSize: [480, 640] + context: 'instream', } }, bids: [ @@ -76,21 +82,74 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, + playerWidth: 640, + playerHeight: 480, mimes: ['video/mp4', 'application/javascript'], protocols: [2,5], api: [2], position: 1, delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, playbackmethod: [1,5], + protocols: [2,5], + api: [2], sid: YOUR_VSSP_ORG_ID, hp: 1, rewarded: 1, - placement: 1, inventoryid: 123, + ttl: 300, + custom: { + key1: "value1", + key2: 123345 + } + }, + bidfloor: 0.5, + site: { + id: 1, + page: 'https://verizonmedia.com', + referrer: 'https://verizonmedia.com' + }, + pubId: 'HBExchange' + } + } + ] + } + ] +``` +## Outstream Video adUnit example & parameters +*Note:* The Video SSP ad server will load it's own Outstream Renderer (player) as a fallback if no player is defined on the publisher page. The Outstream player will inject into the div id that has an identical adUnit code. +``` + var adUnits = [ + { + code: 'video1', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], minduration: 10, maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], + + } + }, + bids: [ + { + bidder: 'oneVideo', + params: { + video: { + sid: YOUR_VSSP_ORG_ID, + hp: 1, + rewarded: 1, ttl: 250 }, bidfloor: 0.5, @@ -116,7 +175,8 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mediaTypes: { video: { context: "instream", - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], } }, bids: [ @@ -124,11 +184,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - position: 1, - display: 1 + ttl: 250 }, bidfloor: 0.5, site: { @@ -190,6 +246,7 @@ var adUnits = [ video: { context: "instream", playerSize: [480, 640] + mimes: ['video/mp4', 'application/javascript'], } }, bids: [ @@ -197,12 +254,8 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 300, - playerHeight: 250, - mimes: ['video/mp4', 'application/javascript'], e2etest: true - }, - pubId: 'YOUR_PUBLISHER_ID' + } } } ] @@ -226,7 +279,10 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], } }, bids: [ @@ -234,12 +290,7 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - sid: + sid: 123456 }, bidfloor: 0.5, site: { @@ -279,7 +330,10 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], } }, bids: [ @@ -287,18 +341,14 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], + ttl: 250 }, bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', referrer: 'https://verizonmedia.com' - }, + }, pubId: 'HBExchange' } } @@ -315,18 +365,16 @@ const adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [1, 2], } }, bids: [{ bidder: 'oneVideo', params: { video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [1, 2], ttl: 300, content: { id: "1234", @@ -373,18 +421,16 @@ const adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [1, 2], } }, bids: [{ bidder: 'oneVideo', params: { video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [1, 2], ttl: 300 }, bidfloor: 0.5, diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 16b8096646f..0974f9db4cf 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -101,7 +101,8 @@ function interpretResponse(serverResponse, bidderRequest) { netRevenue: bid.netRevenue || false, mediaType: bid.mediaType, meta: { - mediaType: bid.mediaType + mediaType: bid.mediaType, + advertiserDomains: bid.adomain }, ttl: bid.ttl || 300 }; diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index 55fca29fbf3..548c0170c05 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -46,7 +46,7 @@ function buildRequests(bidReqs, bidderRequest) { }, tagid: String(bid.adUnitCode) }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + const bidFloor = _getBidFloor(bid); if (bidFloor) { imp.bidfloor = bidFloor; } @@ -117,7 +117,10 @@ function interpretResponse(serverResponse) { netRevenue: true, mediaType: BANNER, ad: _getAdMarkup(onomagicBid), - ttl: 60 + ttl: 60, + meta: { + advertiserDomains: onomagicBid && onomagicBid.adomain ? onomagicBid.adomain : [] + } }); }); } @@ -243,4 +246,20 @@ function _getPercentInView(element, topWin, { w, h } = {}) { return 0; } +function _getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 8a455f6fa25..a398a20a5c5 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -2,8 +2,12 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import includes from 'core-js-pure/features/array/includes.js' const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; +const VIDEO_TARGETING = ['startdelay', 'mimes', 'minduration', 'maxduration', + 'startdelay', 'skippable', 'playbackmethod', 'api', 'protocols', 'boxingallowed', + 'linearity', 'delivery', 'protocol', 'placement', 'minbitrate', 'maxbitrate']; const BIDDER_CODE = 'openx'; const BIDDER_CONFIG = 'hb_pb'; const BIDDER_VERSION = '3.0.3'; @@ -326,7 +330,12 @@ function buildOXBannerRequest(bids, bidderRequest) { let auids = utils._map(bids, bid => bid.params.unit); queryParams.aus = utils._map(bids, bid => utils.parseSizesInput(bid.mediaTypes.banner.sizes).join(',')).join('|'); - queryParams.divIds = utils._map(bids, bid => encodeURIComponent(bid.adUnitCode)).join(','); + queryParams.divids = utils._map(bids, bid => encodeURIComponent(bid.adUnitCode)).join(','); + // gpid + queryParams.aucs = utils._map(bids, function (bid) { + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + return encodeURIComponent(gpid || '') + }).join(','); if (auids.some(auid => auid)) { queryParams.auid = auids.join(','); @@ -382,6 +391,7 @@ function buildOXVideoRequest(bid, bidderRequest) { } function generateVideoParameters(bid, bidderRequest) { + const videoMediaType = utils.deepAccess(bid, `mediaTypes.video`); let queryParams = buildCommonQueryParamsFromBids([bid], bidderRequest); let oxVideoConfig = utils.deepAccess(bid, 'params.video') || {}; let context = utils.deepAccess(bid, 'mediaTypes.video.context'); @@ -401,16 +411,35 @@ function generateVideoParameters(bid, bidderRequest) { height = parseInt(playerSize[1], 10); } - Object.keys(oxVideoConfig).forEach(function (key) { - if (key === 'openrtb') { - oxVideoConfig[key].w = width || oxVideoConfig[key].w; - oxVideoConfig[key].v = height || oxVideoConfig[key].v; - queryParams[key] = JSON.stringify(oxVideoConfig[key]); - } else if (!(key in queryParams) && key !== 'url') { - // only allow video-related attributes - queryParams[key] = oxVideoConfig[key]; - } - }); + let openRtbParams = {w: width, h: height}; + + // legacy openrtb params could be in video, openrtb, or video.openrtb + let legacyParams = bid.params.video || bid.params.openrtb || {}; + if (legacyParams.openrtb) { + legacyParams = legacyParams.openrtb; + } + // support for video object or full openrtb object + if (utils.isArray(legacyParams.imp)) { + legacyParams = legacyParams.imp[0].video; + } + Object.keys(legacyParams) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => openRtbParams[param] = legacyParams[param]); + + // 5.0 openrtb video params + Object.keys(videoMediaType) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => openRtbParams[param] = videoMediaType[param]); + + let openRtbReq = { + imp: [ + { + video: openRtbParams + } + ] + } + + queryParams['openrtb'] = JSON.stringify(openRtbReq); queryParams.auid = bid.params.unit; // override prebid config with openx config if available @@ -429,6 +458,11 @@ function generateVideoParameters(bid, bidderRequest) { queryParams.vtest = 1; } + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + queryParams.aucs = encodeURIComponent(gpid) + } + // each video bid makes a separate request enrichQueryWithFloors(queryParams, VIDEO, [bid]); diff --git a/modules/openxBidAdapter.md b/modules/openxBidAdapter.md index 965b8ee1948..68e41a93b18 100644 --- a/modules/openxBidAdapter.md +++ b/modules/openxBidAdapter.md @@ -98,7 +98,7 @@ pbjs.setConfig({ ``` # Additional Details -[Banner Ads](https://docs.openx.com/Content/developers/containers/prebid-adapter.html) +[Banner Ads](https://docs.openx.com/publishers/prebid-adapter-web/) -[Video Ads](https://docs.openx.com/Content/developers/containers/prebid-video-adapter.html) +[Video Ads](https://docs.openx.com/publishers/prebid-adapter-video/) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index e01746af487..4a7d686a7bc 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,5 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; +import * as utils from '../src/utils.js'; const storageManager = getStorageManager(); @@ -32,6 +33,8 @@ export const spec = { referer = bidderRequest.refererInfo.referer || ''; } + bidRequest.params.bidfloor = getBidFloor(bidRequest); + const ret = { url: `${hostname}/bid`, method: 'POST', @@ -70,6 +73,13 @@ export const spec = { } bidResponse[requiredKey] = bid[requiredKey]; } + + if (Array.isArray(bid.advertiserDomains)) { + bidResponse.meta = { + advertiserDomains: bid.advertiserDomains + } + } + bidResponses.push(bidResponse); }); } @@ -77,4 +87,25 @@ export const spec = { }, }; +/** + * Get bid floor from Price Floors Module + * @param {Object} bid + * @returns {float||undefined} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidfloor; + } + + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { + return floor.floor; + } + return undefined; +} + registerBidder(spec); diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js index c0af09c2b50..052122e95f1 100644 --- a/modules/outbrainBidAdapter.js +++ b/modules/outbrainBidAdapter.js @@ -79,6 +79,14 @@ export const spec = { imp: imps, bcat: bcat, badv: badv, + ext: { + prebid: { + channel: { + name: 'pbjs', + version: '$prebid.version$' + } + } + } }; if (test) { diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 826dd9a933a..36ac7070ec1 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -34,25 +34,36 @@ function deserializeParrableId(parrableIdStr) { values.forEach(function(value) { const pair = value.split(':'); - // unpack a value of 1 as true - parrableId[pair[0]] = +pair[1] === 1 ? true : pair[1]; + if (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) { // unpack a value of 0 or 1 as boolean + parrableId[pair[0]] = Boolean(+pair[1]); + } else if (!isNaN(pair[1])) { // convert to number if is a number + parrableId[pair[0]] = +pair[1] + } else { + parrableId[pair[0]] = pair[1] + } }); return parrableId; } -function serializeParrableId(parrableId) { +function serializeParrableId(parrableIdAndParams) { let components = []; - if (parrableId.eid) { - components.push('eid:' + parrableId.eid); + if (parrableIdAndParams.eid) { + components.push('eid:' + parrableIdAndParams.eid); } - if (parrableId.ibaOptout) { + if (parrableIdAndParams.ibaOptout) { components.push('ibaOptout:1'); } - if (parrableId.ccpaOptout) { + if (parrableIdAndParams.ccpaOptout) { components.push('ccpaOptout:1'); } + if (parrableIdAndParams.tpcSupport !== undefined) { + const tpcSupportComponent = parrableIdAndParams.tpcSupport === true ? 'tpc:1' : 'tpc:0'; + const tpcUntil = `tpcUntil:${parrableIdAndParams.tpcUntil}`; + components.push(tpcSupportComponent); + components.push(tpcUntil); + } return components.join(','); } @@ -84,14 +95,21 @@ function encodeBase64UrlSafe(base64) { function readCookie() { const parrableIdStr = storage.getCookie(PARRABLE_COOKIE_NAME); if (parrableIdStr) { - return deserializeParrableId(decodeURIComponent(parrableIdStr)); + const parsedCookie = deserializeParrableId(decodeURIComponent(parrableIdStr)); + const { tpc, tpcUntil, ...parrableId } = parsedCookie; + let { eid, ibaOptout, ccpaOptout, ...params } = parsedCookie; + + if ((Date.now() / 1000) >= tpcUntil) { + params.tpc = undefined; + } + return { parrableId, params }; } return null; } -function writeCookie(parrableId) { - if (parrableId) { - const parrableIdStr = encodeURIComponent(serializeParrableId(parrableId)); +function writeCookie(parrableIdAndParams) { + if (parrableIdAndParams) { + const parrableIdStr = encodeURIComponent(serializeParrableId(parrableIdAndParams)); storage.setCookie(PARRABLE_COOKIE_NAME, parrableIdStr, getExpirationDate(), 'lax'); } } @@ -175,10 +193,14 @@ function shouldFilterImpression(configParams, parrableId) { return isBlocked() || !isAllowed(); } +function epochFromTtl(ttl) { + return Math.floor((Date.now() / 1000) + ttl); +} + function fetchId(configParams, gdprConsentData) { if (!isValidConfig(configParams)) return; - let parrableId = readCookie(); + let { parrableId, params } = readCookie() || {}; if (!parrableId) { parrableId = readLegacyCookies(); migrateLegacyCookies(parrableId); @@ -188,12 +210,13 @@ function fetchId(configParams, gdprConsentData) { return null; } - const eid = (parrableId) ? parrableId.eid : null; + const eid = parrableId ? parrableId.eid : null; const refererInfo = getRefererInfo(); + const tpcSupport = params ? params.tpc : null const uspString = uspDataHandler.getConsentData(); const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies); const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || ''; - const partners = configParams.partners || configParams.partner + const partners = configParams.partners || configParams.partner; const trackers = typeof partners === 'string' ? partners.split(',') : partners; @@ -203,7 +226,8 @@ function fetchId(configParams, gdprConsentData) { trackers, url: refererInfo.referer, prebidVersion: '$prebid.version$', - isIframe: utils.inIframe() + isIframe: utils.inIframe(), + tpcSupport }; const searchParams = { @@ -229,6 +253,7 @@ function fetchId(configParams, gdprConsentData) { const callbacks = { success: response => { let newParrableId = parrableId ? utils.deepClone(parrableId) : {}; + let newParams = {}; if (response) { try { let responseObj = JSON.parse(response); @@ -242,12 +267,16 @@ function fetchId(configParams, gdprConsentData) { if (responseObj.ibaOptout === true) { newParrableId.ibaOptout = true; } + if (responseObj.tpcSupport !== undefined) { + newParams.tpcSupport = responseObj.tpcSupport; + newParams.tpcUntil = epochFromTtl(responseObj.tpcSupportTtl); + } } } catch (error) { utils.logError(error); cb(); } - writeCookie(newParrableId); + writeCookie({ ...newParrableId, ...newParams }); cb(newParrableId); } else { utils.logError('parrableId: ID fetch returned an empty result'); diff --git a/modules/permutiveRtdProvider.md b/modules/permutiveRtdProvider.md index 3738c6e8be7..39f9a2aaaa5 100644 --- a/modules/permutiveRtdProvider.md +++ b/modules/permutiveRtdProvider.md @@ -20,7 +20,7 @@ pbjs.setConfig({ name: 'permutive', waitForIt: true, // should be true if there's an `auctionDelay` params: { - acBidders: ['appnexus', 'rubicon', 'ozone'] + acBidders: ['appnexus'] } }] }, @@ -31,16 +31,16 @@ pbjs.setConfig({ ## Supported Bidders The below bidders are currently support by the Permutive RTD module. Please reach out to your Permutive Account Manager to request support for any additional bidders. -| Bidder | ID | First-party segments | Audience Connector | +| Bidder | ID | Custom First-Party Segments | Audience Connector ("acBidders") | | ----------- | ---------- | -------------------- | ------------------ | | Xandr | `appnexus` | Yes | Yes | -| Magnite | `rubicon` | Yes | Yes | +| Magnite | `rubicon` | Yes | No | | Ozone | `ozone` | No | Yes | | TrustX | `trustx` | No | Yes | * **First-party segments:** When enabling the respective Activation for a segment in Permutive, this module will automatically attach that segment to the bid request. There is no need to enable individual bidders in the module configuration, it will automatically reflect which SSP integrations you have enabled in Permutive. Permutive segments will be sent in the `permutive` key-value. -* **Audience Connector:** You'll need to define which bidder should receive Audience Connector segments. You need to include the `ID` of any bidder in the `acBidders` array. Audience Connector segments will be sent in the `p_standard` key-value. +* **Audience Connector:** You'll need to define which bidder should receive Audience Connector segments. You need to include the `ID` of any bidder in the `acBidders` array. Audience Connector segments will be sent in the `p_standard` key-value. The segments produced by Audience Connector are not supported for PMPs at this time. ## Parameters @@ -66,7 +66,7 @@ pbjs.setConfig({ name: 'permutive', waitForIt: true, params: { - acBidders: ['appnexus', 'rubicon'], + acBidders: ['appnexus'], maxSegs: 450, overwrites: { rubicon: function (bid, data, acEnabled, utils, defaultFn) { diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8fd7f07d8d7..0a3cb8197aa 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -594,7 +594,7 @@ const OPEN_RTB_PROTOCOL = { } if (!utils.isEmpty(videoParams)) { - if (videoParams.context === 'outstream' && !adUnit.renderer) { + if (videoParams.context === 'outstream' && (!videoParams.renderer || !adUnit.renderer)) { // Don't push oustream w/o renderer to request object. utils.logError('Outstream bid without renderer cannot be sent to Prebid Server.'); } else { diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index ff934204b43..b78b1fc96ba 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -48,7 +48,8 @@ const VIDEO_CUSTOM_PARAMS = { 'linearity': DATA_TYPES.NUMBER, 'placement': DATA_TYPES.NUMBER, 'minbitrate': DATA_TYPES.NUMBER, - 'maxbitrate': DATA_TYPES.NUMBER + 'maxbitrate': DATA_TYPES.NUMBER, + 'skip': DATA_TYPES.NUMBER } const NATIVE_ASSETS = { @@ -167,6 +168,12 @@ const BB_RENDERER = { } }; +const MEDIATYPE = [ + BANNER, + VIDEO, + NATIVE +] + let publisherId = 0; let isInvalidNativeRequest = false; let NATIVE_ASSET_ID_TO_KEY_MAP = {}; @@ -531,7 +538,7 @@ function _createBannerRequest(bid) { } function _createVideoRequest(bid) { - var videoData = bid.params.video; + var videoData = utils.mergeDeep(utils.deepAccess(bid.mediaTypes, 'video'), bid.params.video); var videoObj; if (videoData !== UNDEFINED) { @@ -549,11 +556,6 @@ function _createVideoRequest(bid) { videoObj.w = parseInt(bid.mediaTypes.video.playerSize[0], 10); videoObj.h = parseInt(bid.mediaTypes.video.playerSize[1], 10); } - if (bid.params.video.hasOwnProperty('skippable')) { - videoObj.ext = { - 'video_skippable': bid.params.video.skippable ? 1 : 0 - }; - } } else { videoObj = UNDEFINED; utils.logWarn(LOG_WARN_PREFIX + 'Error: Video config params missing for adunit: ' + bid.params.adUnit + ' with mediaType set as video. Ignoring video impression in the adunit.'); @@ -581,6 +583,28 @@ function _addPMPDealsInImpression(impObj, bid) { } } +function _addDealCustomTargetings(imp, bid) { + var dctr = ''; + var dctrLen; + if (bid.params.dctr) { + dctr = bid.params.dctr; + if (utils.isStr(dctr) && dctr.length > 0) { + var arr = dctr.split('|'); + dctr = ''; + arr.forEach(val => { + dctr += (val.length > 0) ? (val.trim() + '|') : ''; + }); + dctrLen = dctr.length; + if (dctr.substring(dctrLen, dctrLen - 1) === '|') { + dctr = dctr.substring(0, dctrLen - 1); + } + imp.ext['key_val'] = dctr.trim() + } else { + utils.logWarn(LOG_WARN_PREFIX + 'Ignoring param : dctr with value : ' + dctr + ', expects string-value, found empty or non-string value'); + } + } +} + function _createImpressionObject(bid, conf) { var impObj = {}; var bannerObj; @@ -602,7 +626,7 @@ function _createImpressionObject(bid, conf) { }; _addPMPDealsInImpression(impObj, bid); - + _addDealCustomTargetings(impObj, bid); if (bid.hasOwnProperty('mediaTypes')) { for (mediaTypes in bid.mediaTypes) { switch (mediaTypes) { @@ -779,22 +803,28 @@ function _handleEids(payload, validBidRequests) { } } -function _checkMediaType(adm, newBid) { +function _checkMediaType(bid, newBid) { // Create a regex here to check the strings - var admStr = ''; - var videoRegex = new RegExp(/VAST\s+version/); - if (adm.indexOf('span class="PubAPIAd"') >= 0) { - newBid.mediaType = BANNER; - } else if (videoRegex.test(adm)) { - newBid.mediaType = VIDEO; + if (bid.ext && bid.ext['BidType'] != undefined) { + newBid.mediaType = MEDIATYPE[bid.ext.BidType]; } else { - try { - admStr = JSON.parse(adm.replace(/\\/g, '')); - if (admStr && admStr.native) { - newBid.mediaType = NATIVE; + utils.logInfo(LOG_WARN_PREFIX + 'bid.ext.BidType does not exist, checking alternatively for mediaType') + var adm = bid.adm; + var admStr = ''; + var videoRegex = new RegExp(/VAST\s+version/); + if (adm.indexOf('span class="PubAPIAd"') >= 0) { + newBid.mediaType = BANNER; + } else if (videoRegex.test(adm)) { + newBid.mediaType = VIDEO; + } else { + try { + admStr = JSON.parse(adm.replace(/\\/g, '')); + if (admStr && admStr.native) { + newBid.mediaType = NATIVE; + } + } catch (e) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: Cannot parse native reponse for ad response: ' + adm); } - } catch (e) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: Cannot parse native reponse for ad response: ' + adm); } } } @@ -884,38 +914,6 @@ function _blockedIabCategoriesValidation(payload, blockedIabCategories) { } } -function _handleDealCustomTargetings(payload, dctrArr, validBidRequests) { - var dctr = ''; - var dctrLen; - // set dctr value in site.ext, if present in validBidRequests[0], else ignore - if (dctrArr.length > 0) { - if (validBidRequests[0].params.hasOwnProperty('dctr')) { - dctr = validBidRequests[0].params.dctr; - if (utils.isStr(dctr) && dctr.length > 0) { - var arr = dctr.split('|'); - dctr = ''; - arr.forEach(val => { - dctr += (val.length > 0) ? (val.trim() + '|') : ''; - }); - dctrLen = dctr.length; - if (dctr.substring(dctrLen, dctrLen - 1) === '|') { - dctr = dctr.substring(0, dctrLen - 1); - } - payload.site.ext = { - key_val: dctr.trim() - } - } else { - utils.logWarn(LOG_WARN_PREFIX + 'Ignoring param : dctr with value : ' + dctr + ', expects string-value, found empty or non-string value'); - } - if (dctrArr.length > 1) { - utils.logWarn(LOG_WARN_PREFIX + 'dctr value found in more than 1 adunits. Value from 1st adunit will be picked. Ignoring values from subsequent adunits'); - } - } else { - utils.logWarn(LOG_WARN_PREFIX + 'dctr value not found in 1st adunit, ignoring values from subsequent adunits'); - } - } -} - function _assignRenderer(newBid, request) { let bidParams, context, adUnitCode; if (request.bidderRequest && request.bidderRequest.bids) { @@ -931,7 +929,16 @@ function _assignRenderer(newBid, request) { newBid.renderer = BB_RENDERER.newRenderer(newBid.rendererCode, adUnitCode); } } -}; +} + +function isNonEmptyArray(test) { + if (utils.isArray(test) === true) { + if (test.length > 0) { + return true; + } + } + return false; +} export const spec = { code: BIDDER_CODE, @@ -950,22 +957,25 @@ export const spec = { return false; } // video ad validation - if (bid.params.hasOwnProperty('video')) { - if (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, mimes is mandatory and must specify atlease 1 mime value. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + // bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array + let mediaTypesVideoMimes = utils.deepAccess(bid.mediaTypes, 'video.mimes'); + let paramsVideoMimes = utils.deepAccess(bid, 'params.video.mimes'); + if (isNonEmptyArray(mediaTypesVideoMimes) === false && isNonEmptyArray(paramsVideoMimes) === false) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: For video ads, bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array. Call to OpenBid will not be sent for ad unit:' + JSON.stringify(bid)); return false; } - if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { - if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { - utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); - return false; - } - if (bid.mediaTypes[VIDEO].context === 'outstream' && !utils.isStr(bid.params.outstreamAU) && !bid.hasOwnProperty('renderer') && !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { - utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); - return false; - } - } else { - utils.logError(`${LOG_WARN_PREFIX}: mediaTypes or mediaTypes.video is not specified. Rejecting bid: `, bid); + + if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { + utils.logError(`${LOG_WARN_PREFIX}: no context specified in bid. Rejecting bid: `, bid); + return false; + } + + if (bid.mediaTypes[VIDEO].context === 'outstream' && + !utils.isStr(bid.params.outstreamAU) && + !bid.hasOwnProperty('renderer') && + !bid.mediaTypes[VIDEO].hasOwnProperty('renderer')) { + utils.logError(`${LOG_WARN_PREFIX}: for "outstream" bids either outstreamAU parameter must be provided or ad unit supplied renderer is required. Rejecting bid: `, bid); return false; } } @@ -1090,7 +1100,6 @@ export const spec = { utils.deepSetValue(payload, 'regs.coppa', 1); } - _handleDealCustomTargetings(payload, dctrArr, validBidRequests); _handleEids(payload, validBidRequests); _blockedIabCategoriesValidation(payload, blockedIabCategories); _handleFlocId(payload, validBidRequests); @@ -1164,7 +1173,7 @@ export const spec = { if (parsedRequest.imp && parsedRequest.imp.length > 0) { parsedRequest.imp.forEach(req => { if (bid.impid === req.id) { - _checkMediaType(bid.adm, newBid); + _checkMediaType(bid, newBid); switch (newBid.mediaType) { case BANNER: break; diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index f74d79a3dc5..adea33fc3b9 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -119,7 +119,8 @@ function bidResponseAvailable(request, response) { adId: id, ttl: idToBidMap[id].exp || DEFAULT_BID_TTL, netRevenue: DEFAULT_NET_REVENUE, - currency: bidResponse.cur || DEFAULT_CURRENCY + currency: bidResponse.cur || DEFAULT_CURRENCY, + meta: { advertiserDomains: idToBidMap[id].adomain || [] } }; if (idToImpMap[id].video) { // for outstream, a renderer is specified @@ -154,7 +155,7 @@ function impression(slot) { 'native': nativeImpression(slot), tagid: slot.params.ct.toString(), video: video(slot), - bidfloor: slot.params.bidfloor, + bidfloor: bidFloor(slot), ext: ext(slot), }; } @@ -192,7 +193,11 @@ function parseSizes(slot) { */ function video(slot) { if (slot.params.video) { - return Object.assign({}, slot.params.video, {battr: slot.params.battr}); + return Object.assign({}, + slot.params.video, // previously supported as bidder param + slot.mediaTypes && slot.mediaTypes.video ? slot.mediaTypes.video : {}, // params on mediaTypes.video + {battr: slot.params.battr} + ); } return null; } @@ -519,4 +524,19 @@ function nativeResponse(imp, bid) { return null; } +function bidFloor(slot) { + let floor = slot.params.bidfloor; + if (utils.isFn(slot.getFloor)) { + const floorData = slot.getFloor({ + mediaType: slot.mediaTypes.banner ? 'banner' : slot.mediaTypes.video ? 'video' : 'Native', + size: '*', + currency: DEFAULT_CURRENCY, + }); + if (floorData && floorData.floor) { + floor = floorData.floor; + } + } + return floor; +} + registerBidder(spec); diff --git a/modules/pulsepointBidAdapter.md b/modules/pulsepointBidAdapter.md index 36d4ef6cce5..7f4b7e6b611 100644 --- a/modules/pulsepointBidAdapter.md +++ b/modules/pulsepointBidAdapter.md @@ -45,23 +45,21 @@ Please use ```pulsepoint``` as the bidder code. mediaTypes: { video: { playerSize: [640, 480], - context: 'outstream' + context: 'outstream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + linearity: 1, + mimes: ["video/mp4", "video/ogg", "video/webm"], + pos: 3 } }, bids: [{ bidder: 'pulsepoint', params: { cp: 512379, - ct: 505642, - video: { - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - linearity: 1, - mimes: ["video/mp4", "video/ogg", "video/webm"], - pos: 3 - } + ct: 505642 } }], renderer: { @@ -74,7 +72,12 @@ Please use ```pulsepoint``` as the bidder code. mediaTypes: { video: { playerSize: [640, 480], - context: 'instream' + context: 'instream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + protocols: [2,3,5] } }, bids: [{ @@ -82,14 +85,6 @@ Please use ```pulsepoint``` as the bidder code. params: { cp: 512379, ct: 694973, - video: { - battr: [1,3], - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - protocols: [2,3,5] - } } }] }]; diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 7ed5e5c984c..c9a86f73910 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -28,7 +28,8 @@ export const spec = { let payload = { requestId: bidderRequest.bidderRequestId, bids, - referer: bidderRequest.refererInfo.referer + referer: bidderRequest.refererInfo.referer, + schain: validBidRequests[0].schain } if (bidderRequest && bidderRequest.gdprConsent) { @@ -74,6 +75,9 @@ export const spec = { bid.vastXml = bid.ad; } + bid.meta = {}; + bid.meta.advertiserDomains = bid.adomain || []; + bids.push(bid); }) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 92709b7c047..f69f8c5c6e2 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.3'; +const ADAPTER_VERSION = '1.0.4'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -112,6 +112,10 @@ function interpretResponse(serverResponse, bidRequest) { ttl: body.ttl || DEFAULT_TTL, netRevenue: true, mediaType: mediaType, + meta: { + advertiserDomains: body.adomain || [], + mediaType: VIDEO + } }; if (mediaType === VIDEO) { bidResponse.vastXml = body.vast; diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 5e2a5e1bff5..4b556e83236 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -97,7 +97,8 @@ export const spec = { netRevenue: response.netRevenue, currency: response.currency, ttl: response.ttl, - dealId: response.dealId, + meta: response.adomain, + dealId: response.dealId }; if (response.media_type === 'video') { diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index c25d833a71c..f4e99a2e2a0 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -46,7 +46,7 @@ function mapMediaType(seedtagMediaType) { } function hasVideoMediaType(bid) { - return !!bid.mediaTypes && !!bid.mediaTypes.video + return (!!bid.mediaTypes && !!bid.mediaTypes.video) || (!!bid.params && !!bid.params.video) } function hasMandatoryParams(params) { @@ -58,13 +58,12 @@ function hasMandatoryParams(params) { ); } -function hasMandatoryVideoParams(mediaTypes) { - const isVideoInStream = - !!mediaTypes.video && mediaTypes.video.context === 'instream'; - const isPlayerSize = - !!utils.deepAccess(mediaTypes, 'video.playerSize') && - utils.isArray(utils.deepAccess(mediaTypes, 'video.playerSize')); - return isVideoInStream && isPlayerSize; +function hasMandatoryVideoParams(bid) { + const videoParams = getVideoParams(bid) + + return hasVideoMediaType(bid) && !!videoParams.playerSize && + utils.isArray(videoParams.playerSize) && + videoParams.playerSize.length > 0; } function buildBidRequests(validBidRequests) { @@ -91,18 +90,33 @@ function buildBidRequests(validBidRequests) { } if (hasVideoMediaType(validBidRequest)) { - bidRequest.videoParams = params.video || {}; - bidRequest.videoParams.w = - validBidRequest.mediaTypes.video.playerSize[0][0]; - bidRequest.videoParams.h = - validBidRequest.mediaTypes.video.playerSize[0][1]; + bidRequest.videoParams = getVideoParams(validBidRequest) } return bidRequest; }) } -function buildBid(seedtagBid) { +/** + * return video param (global or overrided per bidder) + */ +function getVideoParams(validBidRequest) { + const videoParams = validBidRequest.mediaTypes.video || {}; + if (videoParams.playerSize) { + videoParams.w = videoParams.playerSize[0][0]; + videoParams.h = videoParams.playerSize[0][1]; + } + + const bidderVideoParams = (validBidRequest.params && validBidRequest.params.video) || {} + // override video params from seedtag bidder params + Object.keys(bidderVideoParams).forEach(key => { + videoParams[key] = validBidRequest.params.video[key] + }) + + return videoParams +} + +function buildBidResponse(seedtagBid) { const mediaType = mapMediaType(seedtagBid.mediaType); const bid = { requestId: seedtagBid.bidId, @@ -114,7 +128,10 @@ function buildBid(seedtagBid) { netRevenue: true, mediaType: mediaType, ttl: seedtagBid.ttl, - nurl: seedtagBid.nurl + nurl: seedtagBid.nurl, + meta: { + advertiserDomains: seedtagBid && seedtagBid.adomain && seedtagBid.adomain.length > 0 ? seedtagBid.adomain : [] + } }; if (mediaType === VIDEO) { @@ -152,7 +169,7 @@ export const spec = { */ isBidRequestValid(bid) { return hasVideoMediaType(bid) - ? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid.mediaTypes) + ? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid) : hasMandatoryParams(bid.params); }, @@ -197,7 +214,7 @@ export const spec = { const serverBody = serverResponse.body; if (serverBody && serverBody.bids && utils.isArray(serverBody.bids)) { return utils._map(serverBody.bids, function(bid) { - return buildBid(bid); + return buildBidResponse(bid); }); } else { return []; diff --git a/modules/seedtagBidAdapter.md b/modules/seedtagBidAdapter.md index 627ff8333ad..f4249fb2e89 100644 --- a/modules/seedtagBidAdapter.md +++ b/modules/seedtagBidAdapter.md @@ -46,7 +46,19 @@ var adUnits = [{ mediaTypes: { video: { context: 'instream', // required - playerSize: [600, 300] // required + playerSize: [600, 300], // required + mimes: ['video/mp4'], // recommended + minduration: 5, // optional + maxduration: 60, // optional + boxingallowed: 1, // optional + skip: 1, // optional + startdelay: 1, // optional + linearity: 1, // optional + battr: [1, 2], // optional + maxbitrate: 10, // optional + playbackmethod: [1], // optional + delivery: [1], // optional + placement: 1, // optional } }, bids: [ @@ -57,9 +69,10 @@ var adUnits = [{ adUnitId: '0000', // required placement: 'video', // required adPosition: 0, // optional - // Video object as specified in OpenRTB 2.5 - video: { - mimes: ['video/mp4'], // recommended + video: { // optional + context: 'instream', // optional + playerSize: [600, 300], // optional + mimes: ['video/mp4'], // optional minduration: 5, // optional maxduration: 60, // optional boxingallowed: 1, // optional diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 68ccde0da46..59cf2a3a035 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; -const VERSION = '3.3.2'; +const VERSION = '3.4.0'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -57,8 +57,9 @@ export const sharethroughAdapterSpec = { query.schain = JSON.stringify(bidRequest.schain); } - if (bidRequest.bidfloor) { - query.bidfloor = parseFloat(bidRequest.bidfloor); + const floor = getFloor(bidRequest); + if (floor) { + query.bidfloor = floor; } if (bidRequest.params.badv) { @@ -292,4 +293,18 @@ function getProtocol() { return document.location.protocol; } +function getFloor(bid) { + if (utils.isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: bid.sizes.map(size => ({ w: size[0], h: size[1] })) + }); + if (utils.isPlainObject(floorInfo) && !isNaN(floorInfo.floor) && floorInfo.currency === 'USD') { + return parseFloat(floorInfo.floor); + } + } + return null; +} + registerBidder(sharethroughAdapterSpec); diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index fbb4c14e5f8..719d78892ce 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -5,75 +5,22 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; -const CLIENT = 'prebid_js_$prebid.version$_1.1' - -/** -* Transform BidRequest to OpenRTB-formatted BidRequest Object -* @param {Array} validBidRequests -* @param {any} bidderRequest -* @returns {string} -*/ -const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { - /** - * Turn incoming prebid sizes into openRtb format mapping. - * @param {*} sizes in format [[10, 10], [20, 20]] - * @returns array of openRtb format mappings [{w: 10, h: 10}, {w: 20, h: 20}] - */ - const parseSizes = (sizes) => { - return sizes.map((size) => { - return {w: size[0], h: size[1]}; - }) - } - - const imp = validBidRequests.map(br => { - const bannerMediaType = utils.deepAccess(br, 'mediaTypes.banner'); - const videoMediaType = utils.deepAccess(br, 'mediaTypes.video'); - let result = { - id: br.bidId, - tagid: utils.deepAccess(br, 'params.adspaceId') - } - - if (bannerMediaType) { - const sizes = parseSizes(utils.getAdUnitSizes(br)); - result.banner = { - w: sizes[0].w, - h: sizes[0].h, - format: sizes - } - } - - if (videoMediaType) { - result.video = { - mimes: videoMediaType.mimes, - minduration: videoMediaType.minduration, - startdelay: videoMediaType.startdelay, - linearity: videoMediaType.linearity, - w: videoMediaType.playerSize[0][0], - h: videoMediaType.playerSize[0][1], - maxduration: videoMediaType.maxduration, - skip: videoMediaType.skip, - protocols: videoMediaType.protocols, - ext: { - rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 - }, - skipmin: videoMediaType.skipmin, - api: videoMediaType.api - } - } - - return result; - }); +const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.2' +const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const request = { id: bidderRequest.auctionId, at: 1, - imp, + imp: [{ + id: bidRequest.bidId, + tagid: utils.deepAccess(bidRequest, 'params.adspaceId') + }], cur: ['USD'], tmax: bidderRequest.timeout, site: { id: window.location.hostname, publisher: { - id: utils.deepAccess(validBidRequests[0], 'params.publisherId') + id: utils.deepAccess(bidRequest, 'params.publisherId') }, domain: window.location.hostname, page: window.location.href, @@ -94,12 +41,40 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { ext: {} }, ext: { - client: CLIENT + client: SMAATO_CLIENT } }; - let ortb2 = config.getConfig('ortb2') || {}; + if (utils.deepAccess(bidRequest, 'mediaTypes.banner')) { + const sizes = utils.getAdUnitSizes(bidRequest).map((size) => ({w: size[0], h: size[1]})); + request.imp[0].banner = { + w: sizes[0].w, + h: sizes[0].h, + format: sizes + } + } + const videoMediaType = utils.deepAccess(bidRequest, 'mediaTypes.video'); + if (videoMediaType) { + request.imp[0].video = { + mimes: videoMediaType.mimes, + minduration: videoMediaType.minduration, + startdelay: videoMediaType.startdelay, + linearity: videoMediaType.linearity, + w: videoMediaType.playerSize[0][0], + h: videoMediaType.playerSize[0][1], + maxduration: videoMediaType.maxduration, + skip: videoMediaType.skip, + protocols: videoMediaType.protocols, + ext: { + rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 + }, + skipmin: videoMediaType.skipmin, + api: videoMediaType.api + } + } + + let ortb2 = config.getConfig('ortb2') || {}; Object.assign(request.user, ortb2.user); Object.assign(request.site, ortb2.site); @@ -112,20 +87,19 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (utils.deepAccess(validBidRequests[0], 'params.app')) { - const geo = utils.deepAccess(validBidRequests[0], 'params.app.geo'); + if (utils.deepAccess(bidRequest, 'params.app')) { + const geo = utils.deepAccess(bidRequest, 'params.app.geo'); utils.deepSetValue(request, 'device.geo', geo); - const ifa = utils.deepAccess(validBidRequests[0], 'params.app.ifa') + const ifa = utils.deepAccess(bidRequest, 'params.app.ifa') utils.deepSetValue(request, 'device.ifa', ifa); } - const eids = utils.deepAccess(validBidRequests[0], 'userIdAsEids'); + const eids = utils.deepAccess(bidRequest, 'userIdAsEids'); if (eids && eids.length) { utils.deepSetValue(request, 'user.ext.eids', eids); } - utils.logInfo('[SMAATO] OpenRTB Request:', request); - return JSON.stringify(request); + return request } export const spec = { @@ -133,41 +107,41 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], /** - * Determines whether or not 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. - */ + * Determines whether or not 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: (bid) => { return typeof bid.params === 'object' && - typeof bid.params.publisherId === 'string' && - typeof bid.params.adspaceId === 'string'; + typeof bid.params.publisherId === 'string' && + typeof bid.params.adspaceId === 'string'; }, - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ buildRequests: (validBidRequests, bidderRequest) => { - utils.logInfo('[SMAATO] Client version:', CLIENT); - return { - method: 'POST', - url: validBidRequests[0].params.endpoint || SMAATO_ENDPOINT, - data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest), - options: { - withCredentials: true, - crossOrigin: true, - } - }; + utils.logInfo('[SMAATO] Client version:', SMAATO_CLIENT); + + return validBidRequests.map((validBidRequest) => { + const openRtbBidRequest = buildOpenRtbBidRequest(validBidRequest, bidderRequest); + utils.logInfo('[SMAATO] OpenRTB Request:', openRtbBidRequest); + + return { + method: 'POST', + url: validBidRequest.params.endpoint || SMAATO_ENDPOINT, + data: JSON.stringify(openRtbBidRequest), + options: { + withCredentials: true, + crossOrigin: true, + } + }; + }); }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: (serverResponse, bidRequest) => { // response is empty (HTTP 204) if (utils.isEmpty(serverResponse.body)) { @@ -235,15 +209,14 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - const syncs = [] - return syncs; + return []; } } registerBidder(spec); diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index b2c48b58f82..46e2055c8f0 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -46,14 +46,6 @@ export const spec = { utils.logError(BIDDER_CODE + ': siteId is not present in bidder params'); return false; } - if (!utils.getBidIdParameter('bidfloor', bid.params)) { - utils.logError(BIDDER_CODE + ': bidfloor is not present in bidder params'); - return false; - } - if (!utils.getBidIdParameter('bidfloorcur', bid.params)) { - utils.logError(BIDDER_CODE + ': bidfloorcur is not present in bidder params'); - return false; - } if (utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { if (!utils.getBidIdParameter('outstream_options', bid.params)) { utils.logError(BIDDER_CODE + ': outstream_options parameter is not defined'); @@ -85,8 +77,8 @@ export const spec = { const smartxRequests = bidRequests.map(function (bid) { const tagId = utils.getBidIdParameter('tagId', bid.params); const publisherId = utils.getBidIdParameter('publisherId', bid.params); - const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); - const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params); + const bidfloor = getBidFloor(bid) || 0; + const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params) || 'EUR'; const siteId = utils.getBidIdParameter('siteId', bid.params); const domain = utils.getBidIdParameter('domain', bid.params); const cat = utils.getBidIdParameter('cat', bid.params); @@ -161,7 +153,7 @@ export const spec = { const at = utils.getBidIdParameter('at', bid.params) || 2; - const cur = utils.getBidIdParameter('cur', bid.params) || ['EUR']; + const cur = utils.getBidIdParameter('cur', bid.params) || 'EUR'; const requestPayload = { id: utils.generateUUID(), @@ -226,6 +218,8 @@ export const spec = { } } + // Todo: USER ID MODULE + requestPayload.user = { ext: userExt, data: targetingarr @@ -412,4 +406,30 @@ function outstreamRender(bid) { } } } + +/** + * Get the floor price from bid.params for backward compatibility. + * If not found, then check floor module. + * @param bid A valid bid object + * @returns {*|number} floor price + */ +function getBidFloor(bid) { + let floor = utils.getBidIdParameter('bidfloor', bid.params); + let floorcur = utils.getBidIdParameter('bidfloorcur', bid.params) || 'EUR'; + + if (!floor && utils.isFn(bid.getFloor)) { + const floorObj = bid.getFloor({ + currency: floorcur, + mediaType: '*', + size: '*' + }); + + if (utils.isPlainObject(floorObj) && !isNaN(floorObj.floor) && floorObj.currency === floorcur) { + floor = floorObj.floor; + } + } + + return floor; +} + registerBidder(spec); diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index e5bd76eba9f..eb6009079ac 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -1,5 +1,5 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, deepClone } from '../src/utils.js'; +import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, deepClone, getGptSlotInfoForAdUnitCode } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; @@ -60,11 +60,11 @@ export const spec = { if (/^[\/]?[\d]+[[\/].+[\/]?]?$/.test(slotIdentifier)) { slotIdentifier = slotIdentifier.charAt(0) === '/' ? slotIdentifier : '/' + slotIdentifier; return { - [`${slotIdentifier}|${bid.bidId}`]: `${_validateSize(bid)}${_validateFloor(bid)}` + [`${slotIdentifier}|${bid.bidId}`]: `${_validateSize(bid)}${_validateFloor(bid)}${_validateGPID(bid)}` } } else if (/^[0-9a-fA-F]{20}$/.test(slotIdentifier) && slotIdentifier.length === 20) { return { - [bid.bidId]: `${slotIdentifier}|${_validateSize(bid)}${_validateFloor(bid)}` + [bid.bidId]: `${slotIdentifier}|${_validateSize(bid)}${_validateFloor(bid)}${_validateGPID(bid)}` } } else { logError(`The ad unit code or Sonobi Placement id for slot ${bid.bidId} is invalid`); @@ -199,6 +199,10 @@ export const spec = { width = 1, height = 1 ] = bid.sbi_size.split('x'); + let aDomains = []; + if (bid.sbi_adomain) { + aDomains = [bid.sbi_adomain] + } const bids = { requestId: bidId, cpm: Number(bid.sbi_mouse), @@ -209,7 +213,10 @@ export const spec = { creativeId: bid.sbi_crid || bid.sbi_aid, aid: bid.sbi_aid, netRevenue: true, - currency: 'USD' + currency: 'USD', + meta: { + advertiserDomains: aDomains + } }; if (bid.sbi_dozer) { @@ -302,6 +309,15 @@ function _validateFloor (bid) { return ''; } +function _validateGPID(bid) { + const gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot') || deepAccess(getGptSlotInfoForAdUnitCode(bid.adUnitCode), 'gptSlot') || bid.params.ad_unit; + + if (gpid) { + return `|gpid=${gpid}` + } + return '' +} + const _creative = (mediaType, referer) => (sbiDc, sbiAid) => { if (mediaType === 'video' || mediaType === 'outstream') { return _videoCreative(sbiDc, sbiAid, referer) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 391f3a05721..d166a01a1da 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -9,7 +9,7 @@ const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.8'; +const BIDDER_VERSION = '4.9'; const W = window; const { navigator } = W; const oneCodeDetection = {}; @@ -102,25 +102,29 @@ const applyClientHints = ortbRequest => { ortbRequest.user = Object.assign(ortbRequest.user, { data }); }; -function applyGdpr(bidderRequest, ortbRequest) { - if (bidderRequest && bidderRequest.gdprConsent) { - consentApiVersion = bidderRequest.gdprConsent.apiVersion; - ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': bidderRequest.gdprConsent.gdprApplies ? 1 : 0 }); - ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': bidderRequest.gdprConsent.consentString }); +/** + * Add GDPR data to oRTB request + * Store conset API version (will be required by user sync) + */ +const applyGdpr = (bidderRequest, ortbRequest) => { + const { gdprConsent } = bidderRequest; + if (gdprConsent) { + const { apiVersion, gdprApplies, consentString } = gdprConsent; + consentApiVersion = apiVersion; + ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': gdprApplies ? 1 : 0 }); + ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': consentString }); } } -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i], key); - - if (result) { - return result; - } - } -} +/** + * Get value for first occurence of key within the collection + */ +const setOnAny = (collection, key) => collection.reduce((prev, next) => prev || utils.deepAccess(next, key), false); -function sendNotification(payload) { +/** + * Send payload to notification endpoint + */ +const sendNotification = payload => { ajax(NOTIFY_URL, null, JSON.stringify(payload), { withCredentials: false, method: 'POST', @@ -132,7 +136,7 @@ function sendNotification(payload) { * @param {object} slot Ad Unit Params by Prebid * @returns {object} Banner by OpenRTB 2.5 §3.2.6 */ -function mapBanner(slot) { +const mapBanner = slot => { if (slot.mediaType === 'banner' || utils.deepAccess(slot, 'mediaTypes.banner') || (!slot.mediaType && !slot.mediaTypes)) { @@ -141,8 +145,6 @@ function mapBanner(slot) { h: size[1], })); - // override - tylko 1szy wymiar - // format = format.slice(0, 1); return { format, id: slot.bidId, @@ -150,7 +152,7 @@ function mapBanner(slot) { } } -function mapImpression(slot) { +const mapImpression = slot => { const { adUnitCode, bidId, params } = slot; const { id, siteId } = params || {}; const imp = { @@ -160,16 +162,18 @@ function mapImpression(slot) { tagid: adUnitCode, }; - const bidfloor = (slot.params && slot.params.bidFloor) ? parseFloat(slot.params.bidFloor) : undefined; - - if (bidfloor) { - imp.bidfloor = bidfloor; + // Check floorprices for this imp + if (typeof slot.getFloor === 'function') { + // sspBC adapter accepts only floor per imp - check for maximum value for requested ad sizes + imp.bidfloor = slot.sizes.reduce((prev, next) => { + const currentFloor = slot.getFloor({ mediaType: 'banner', size: next }).floor; + return prev > currentFloor ? prev : currentFloor; + }, 0); } - return imp; } -function renderCreative(site, auctionId, bid, seat, request) { +const renderCreative = (site, auctionId, bid, seat, request) => { let gam; const mcad = { @@ -292,7 +296,7 @@ const spec = { return { method: 'POST', - url: BIDDER_URL + '?cs=' + cookieSupport() + '&bdver=' + BIDDER_VERSION + '&pbver=' + pbver + '&inver=0', + url: `${BIDDER_URL}?cs=${cookieSupport()}&bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, data: JSON.stringify(payload), bidderRequest, }; @@ -315,11 +319,11 @@ const spec = { seat = seatbid.seat; seatbid.bid.forEach(serverBid => { // get data from bid response - const { adomain, crid, impid, exp, ext, price, w, h } = serverBid; + const { adomain, crid = `mcad_${bidderRequest.auctionId}_${site.slot}`, impid, exp = 300, ext, price, w, h } = serverBid; const bidRequest = bidderRequest.bids.filter(b => { - const { bidId, params } = b; - const { id, siteId } = params || {}; + const { bidId, params = {} } = b; + const { id, siteId } = params; const currentBidId = id && siteId ? id : 'bidid-' + bidId; return currentBidId === impid; })[0]; @@ -348,10 +352,10 @@ const spec = { const bid = { requestId: bidId, - creativeId: crid || 'mcad_' + bidderRequest.auctionId + '_' + site.slot, + creativeId: crid, cpm: price, currency: response.cur, - ttl: exp || 300, + ttl: exp, width: w, height: h, bidderCode: BIDDER_CODE, @@ -365,14 +369,7 @@ const spec = { }; if (bid.cpm > 0) { - // check bidFloor (if present in params) - const { bidFloor } = params || {}; - - if (!bidFloor || bid.cpm >= bidFloor) { - bids.push(bid); - } else { - utils.logWarn('Discarding bid due to bidFloor setting', bid.cpm, bidFloor); - } + bids.push(bid); } } else { utils.logWarn('Discarding response - no matching request / site id', serverBid.impid); @@ -384,13 +381,14 @@ const spec = { return bids; }, getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { + if (syncOptions.iframeEnabled && consentApiVersion != 1) { return [{ type: 'iframe', - url: SYNC_URL + '?tcf=' + consentApiVersion, + url: `${SYNC_URL}?tcf=${consentApiVersion}`, }]; + } else { + utils.logWarn('sspBC adapter requires iframe based user sync.'); } - utils.logWarn('sspBC adapter requires iframe based user sync.'); }, onTimeout(timeoutData) { diff --git a/modules/sspBCBidAdapter.md b/modules/sspBCBidAdapter.md index f22e8e6c458..1678f3be594 100644 --- a/modules/sspBCBidAdapter.md +++ b/modules/sspBCBidAdapter.md @@ -20,7 +20,6 @@ Optional parameters: - domain - page - tmax -- bidFloor - test # Test Parameters diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index 41fdb72e76e..854f58df8fe 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -9,7 +9,7 @@ const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_TTL = 600; const SUBLIME_ANTENNA = 'antenna.ayads.co'; -const SUBLIME_VERSION = '0.7.1'; +const SUBLIME_VERSION = '0.7.2'; /** * Identify the current device type @@ -207,6 +207,12 @@ function interpretResponse(serverResponse, bidRequest) { sspname: response.sspname || null }; + // We don't support advertiserDomains atm + if (response.advertiserDomains) { + // Creating a stub for Prebid.js 5.0 compliance + bidResponse.meta = Object.assign({}, bidResponse.meta, { advertiserDomains: [] }); + } + bidResponses.push(bidResponse); } diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index aaff637c790..7694f5d838e 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -14,6 +14,12 @@ const BLOCKED_AD_SIZES = [ '1x1', '1x2' ]; +const SUPPORTED_USER_ID_SOURCES = [ + 'liveramp.com', // Liveramp IdentityLink + 'nextroll.com', // NextRoll XID + 'verizonmedia.com', // Verizon Media ConnectID + 'pubcid.org' // PubCommon ID +]; export const spec = { code: 'synacormedia', supportedMediaTypes: [ BANNER, VIDEO ], @@ -91,6 +97,14 @@ export const spec = { deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + // User ID + if (validBidReqs[0] && validBidReqs[0].userIdAsEids && Array.isArray(validBidReqs[0].userIdAsEids)) { + const eids = this.processEids(validBidReqs[0].userIdAsEids); + if (eids.length) { + deepSetValue(openRtbBidRequest, 'user.ext.eids', eids); + } + } + if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', @@ -104,6 +118,16 @@ export const spec = { } }, + processEids: function(userIdAsEids) { + const eids = []; + userIdAsEids.forEach(function(eid) { + if (SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) > -1) { + eids.push(eid); + } + }); + return eids; + }, + buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) { let format = []; let imps = []; diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 566795a204b..48e0abc5c7f 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,7 +8,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10514'; +const TAPPX_BIDDER_VERSION = '0.1.10526'; const TYPE_CNN = 'prebidjs'; const VIDEO_SUPPORT = ['instream']; @@ -79,7 +79,7 @@ export const spec = { // GDPR & CCPA if (gdprConsent) { - url += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + url += '&gdpr_optin=' + (gdprConsent.gdprApplies ? 1 : 0); url += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); } if (uspConsent) { @@ -120,7 +120,7 @@ function validBasic(bid) { } let classicEndpoint = true - if ((new RegExp(`^(vz.*|zz.*)\.*$`, 'i')).test(bid.params.host)) { + if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { classicEndpoint = false } @@ -398,8 +398,8 @@ function getHostInfo(validBidRequests) { domainInfo.domain = hostParam.split('/', 1)[0]; - let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\.pub\.tappx\.com$`, 'i'); - let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\.[a-z]{3}\.tappx\.com$`, 'i'); + let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); + let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); if (regexNewEndpoints.test(domainInfo.domain)) { domainInfo.newEndpoint = true; diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index 37d54e60cd2..e9e030ce33f 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -53,6 +53,9 @@ export const spec = { bid.creativeId = result.creativeId; bid.currency = result.currency; bid.netRevenue = result.netRevenue; + if (result.adomain) { + bid.meta = {advertiserDomains: result.adomain} + } bidResponses.push(bid); } } diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 9f8de30a0d4..247a1f6d048 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -179,7 +179,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst) { currency: 'USD', netRevenue: newFormat ? false : priceType !== 'gross', ttl: TIME_TO_LIVE, - dealId: serverBid.dealid + dealId: serverBid.dealid, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, }; if (serverBid.content_type === 'video') { bidResponse.vastXml = serverBid.adm; diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 734aba97789..685ffdb42b7 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -8,6 +8,7 @@ const COOKIE_NAME = 'ucf_uid'; const VER = 'ADGENT_PREBID-2018011501'; const BIDDER_CODE = 'ucfunnel'; const GVLID = 607; +const CURRENCY = 'USD'; const VIDEO_CONTEXT = { INSTREAM: 0, OUSTREAM: 2 @@ -210,12 +211,41 @@ function getSupplyChain(schain) { return supplyChain; } +function getMediaType(mediaTypes) { + if (mediaTypes != null && mediaTypes.banner) { + return 'banner'; + } else if (mediaTypes != null && mediaTypes.video) { + return 'video'; + } else if (mediaTypes != null && mediaTypes.native) { + return 'native' + } + return 'banner'; +} + +function getFloor(bid, size, mediaTypes) { + if (bid.params.bidfloor) { + return bid.params.bidfloor; + } + if (typeof bid.getFloor === 'function') { + var bidFloor = bid.getFloor({ + currency: CURRENCY, + mediaType: getMediaType(mediaTypes), + size: (size) ? [ size[0], size[1] ] : '*', + }); + if (bidFloor.currency === CURRENCY) { + return bidFloor.floor; + } + } + return undefined; +} + function getRequestData(bid, bidderRequest) { const size = parseSizes(bid); const language = navigator.language; const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; const userIdTdid = (bid.userId && bid.userId.tdid) ? bid.userId.tdid : ''; const supplyChain = getSupplyChain(bid.schain); + const bidFloor = getFloor(bid, size, bid.mediaTypes); // general bid data let bidData = { ver: VER, @@ -225,9 +255,13 @@ function getRequestData(bid, bidderRequest) { dnt: dnt, adid: bid.params.adid, tdid: userIdTdid, - schain: supplyChain, - fp: bid.params.bidfloor + schain: supplyChain }; + + if (bidFloor) { + bidData.fp = bidFloor; + } + addUserId(bidData, bid.userId); try { bidData.host = window.top.location.hostname; diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 8368077a627..6268774bc12 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -120,6 +120,9 @@ export const spec = { currency: 'USD', netRevenue: false, ttl: mid.ttl || 60, + meta: { + advertiserDomains: mid.advertiser_domains || [] + } }; if (bidResponse.cpm <= 0) { diff --git a/modules/userId/index.js b/modules/userId/index.js index 199934f4cdb..3f235d15dd1 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -277,6 +277,7 @@ function makeStoredConsentDataHash(consentData) { storedConsentData.gdprApplies = consentData.gdprApplies; storedConsentData.apiVersion = consentData.apiVersion; } + return utils.cyrb53Hash(JSON.stringify(storedConsentData)); } @@ -306,17 +307,17 @@ function getStoredConsentData() { } /** - * test if the consent object stored locally matches the current consent data. - * if there is nothing in storage, return true and we'll do an actual comparison next time. - * this way, we don't force a refresh for every user when this code rolls out + * test if the consent object stored locally matches the current consent data. if they + * don't match or there is nothing stored locally, it means a refresh of the user id + * submodule is needed * @param storedConsentData * @param consentData * @returns {boolean} */ function storedConsentDataMatchesConsentData(storedConsentData, consentData) { return ( - typeof storedConsentData === 'undefined' || - storedConsentData === null || + typeof storedConsentData !== 'undefined' && + storedConsentData !== null && storedConsentData === makeStoredConsentDataHash(consentData) ); } diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 5d78a447572..5cf4a6c2128 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -36,7 +36,7 @@ pbjs.setConfig({ name: "id5Id", params: { partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id - pd: "some-pd-string" // See https://wiki.id5.io/x/BIAZ for details + pd: "some-pd-string" // See https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5 for details }, storage: { type: "html5", // ID5 requires html5 @@ -107,7 +107,7 @@ pbjs.setConfig({ type: "cookie", name: "merkleId", expires: 30 - } + } },{ name: 'uid2' } @@ -126,7 +126,7 @@ pbjs.setConfig({ },{ name: 'flocId', params: { - token: "Registered token or default sharedid.org token" // Default sharedid.org token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" + token: "Registered token or default sharedid.org token" // Default sharedid.org token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9" } }], syncDelay: 5000, @@ -192,7 +192,7 @@ pbjs.setConfig({ name: 'id5Id', params: { partner: 173, // Set your real ID5 partner ID here for production, please ask for one at https://id5.io/universal-id - pd: 'some-pd-string' // See https://wiki.id5.io/x/BIAZ for details + pd: 'some-pd-string' // See https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5 for details }, storage: { type: 'html5', @@ -224,7 +224,7 @@ pbjs.setConfig({ type: "html5", name: "merkleId", expires: 30 - } + } }, { name: 'admixerId', params: { diff --git a/modules/viewdeosDXBidAdapter.js b/modules/viewdeosDXBidAdapter.js index 22c16ab5b34..212759642f5 100644 --- a/modules/viewdeosDXBidAdapter.js +++ b/modules/viewdeosDXBidAdapter.js @@ -190,7 +190,10 @@ function createBid(bidResponse, mediaType, bidderParams) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 3600 + ttl: 3600, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === DISPLAY) { diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 63f4121724e..12004cc35af 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -10,8 +10,6 @@ const ENDPOINT_URL = BASE_URL + '/hb'; const TIME_TO_LIVE = 360; const DEFAULT_CUR = 'EUR'; const ADAPTER_SYNC_URL = BASE_URL + '/push_sync'; -const TRACK_WIN_URL = BASE_URL + '/track/win'; -const TRACK_PENDING_URL = BASE_URL + '/track/pending'; const TRACK_TIMEOUT_URL = BASE_URL + '/track/bid_timeout'; const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', @@ -191,11 +189,15 @@ export const spec = { }, onSetTargeting: function(bid) { // Call '/track/pending' with the corresponding bid.requestId - utils.triggerPixel(TRACK_PENDING_URL + '?requestId=' + bid.requestId); + if (bid.ext && bid.ext.events && bid.ext.events.pending) { + utils.triggerPixel(bid.ext.events.pending); + } }, onBidWon: function(bid) { // Call '/track/win' with the corresponding bid.requestId - utils.triggerPixel(TRACK_WIN_URL + '?requestId=' + bid.requestId); + if (bid.ext && bid.ext.events && bid.ext.events.win) { + utils.triggerPixel(bid.ext.events.win); + } }, onTimeout: function(timeoutData) { // Call '/track/bid_timeout' with timeout data @@ -240,9 +242,13 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses, bidsWithout currency: reqCurrency, netRevenue: true, ttl: TIME_TO_LIVE, - dealId: serverBid.dealid + dealId: serverBid.dealid, }; + if (serverBid.ext && serverBid.ext.prebid) { + bidResponse.ext = serverBid.ext.prebid; + } + if (!_isVideoBid(bid)) { bidResponse.ad = serverBid.adm; } else { diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 73df9bb8b9b..ba469c3c7ed 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -70,7 +70,10 @@ function buildBid(bidData) { netRevenue: true, mediaType: BANNER, ttl: TTL, - content: bidData.content + content: bidData.content, + meta: { + advertiserDomains: bidData.advertiserDomains || [], + } }; if (bidData.placement === 'video') { diff --git a/modules/vuukleBidAdapter.js b/modules/vuukleBidAdapter.js index e9770b5e62e..7a83bf4e331 100644 --- a/modules/vuukleBidAdapter.js +++ b/modules/vuukleBidAdapter.js @@ -55,8 +55,12 @@ export const spec = { currency: res.currency || 'USD', netRevenue: true, ttl: TIME_TO_LIVE, - ad: res.ad + ad: res.ad, + meta: { + advertiserDomains: Array.isArray(res.adomain) ? res.adomain : [] + } }; + return [bidResponse]; }, } diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 36f93f60c9e..fa1ab3a90b3 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -12,8 +12,8 @@ const NET_REVENUE = true; const BANNER_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; const VIDEO_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebidvideo'; const OUTSTREAM_VIDEO_PLAYER_URL = 'https://prebid-outstream.yieldmo.com/bundle.js'; -const OPENRTB_VIDEO_BIDPARAMS = ['placement', 'startdelay', 'skipafter', - 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos']; +const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', + 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable']; const OPENRTB_VIDEO_SITEPARAMS = ['name', 'domain', 'cat', 'keywords']; const LOCAL_WINDOW = utils.getWindowTop(); const DEFAULT_PLAYBACK_METHOD = 2; @@ -335,7 +335,6 @@ function openRtbRequest(bidRequests, bidderRequest) { * @return Object OpenRTB's 'imp' (impression) object */ function openRtbImpression(bidRequest) { - const videoReq = utils.deepAccess(bidRequest, 'mediaTypes.video'); const size = extractPlayerSize(bidRequest); const imp = { id: bidRequest.bidId, @@ -347,23 +346,27 @@ function openRtbImpression(bidRequest) { video: { w: size[0], h: size[1], - mimes: videoReq.mimes, linearity: 1 } }; + const mediaTypesParams = utils.deepAccess(bidRequest, 'mediaTypes.video'); + Object.keys(mediaTypesParams) + .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) + .forEach(param => imp.video[param] = mediaTypesParams[param]); + const videoParams = utils.deepAccess(bidRequest, 'params.video'); Object.keys(videoParams) .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) .forEach(param => imp.video[param] = videoParams[param]); - if (videoParams.skippable) imp.video.skip = 1; - if (videoParams.placement !== 1) { - imp.video = { - ...imp.video, - startdelay: DEFAULT_START_DELAY, - playbackmethod: [ DEFAULT_PLAYBACK_METHOD ] - } + if (imp.video.skippable) { + imp.video.skip = 1; + delete imp.video.skippable; + } + if (imp.video.placement !== 1) { + imp.video.startdelay = DEFAULT_START_DELAY; + imp.video.playbackmethod = [ DEFAULT_PLAYBACK_METHOD ]; } return imp; } @@ -476,51 +479,68 @@ function validateVideoParams(bid) { const isDefined = val => typeof val !== 'undefined'; const validate = (fieldPath, validateCb, errorCb, errorCbParam) => { - const value = utils.deepAccess(bid, fieldPath); - if (!validateCb(value)) { - errorCb(fieldPath, value, errorCbParam); + if (fieldPath.indexOf('video') === 0) { + const valueFieldPath = 'params.' + fieldPath; + const mediaFieldPath = 'mediaTypes.' + fieldPath; + const valueParams = utils.deepAccess(bid, valueFieldPath); + const mediaTypesParams = utils.deepAccess(bid, mediaFieldPath); + const hasValidValueParams = validateCb(valueParams); + const hasValidMediaTypesParams = validateCb(mediaTypesParams); + + if (hasValidValueParams) return valueParams; + else if (hasValidMediaTypesParams) return hasValidMediaTypesParams; + else { + if (!hasValidValueParams) errorCb(valueFieldPath, valueParams, errorCbParam); + else if (!hasValidMediaTypesParams) errorCb(mediaFieldPath, mediaTypesParams, errorCbParam); + } + return valueParams || mediaTypesParams; + } else { + const value = utils.deepAccess(bid, fieldPath); + if (!validateCb(value)) { + errorCb(fieldPath, value, errorCbParam); + } + return value; } - return value; } try { + validate('video.context', val => !utils.isEmpty(val), paramRequired); + validate('params.placementId', val => !utils.isEmpty(val), paramRequired); - validate('mediaTypes.video.playerSize', val => utils.isArrayOfNums(val, 2) || + validate('video.playerSize', val => utils.isArrayOfNums(val, 2) || (utils.isArray(val) && val.every(v => utils.isArrayOfNums(v, 2))), paramInvalid, 'array of 2 integers, ex: [640,480] or [[640,480]]'); - validate('mediaTypes.video.mimes', val => isDefined(val), paramRequired); - validate('mediaTypes.video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, + validate('video.mimes', val => isDefined(val), paramRequired); + validate('video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, 'array of strings, ex: ["video/mp4"]'); - validate('params.video', val => !utils.isEmpty(val), paramRequired); - - const placement = validate('params.video.placement', val => isDefined(val), paramRequired); - validate('params.video.placement', val => val >= 1 && val <= 5, paramInvalid); + const placement = validate('video.placement', val => isDefined(val), paramRequired); + validate('video.placement', val => val >= 1 && val <= 5, paramInvalid); if (placement === 1) { - validate('params.video.startdelay', val => isDefined(val), + validate('video.startdelay', val => isDefined(val), (field, v) => paramRequired(field, v, 'placement == 1')); - validate('params.video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); + validate('video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); } - validate('params.video.protocols', val => isDefined(val), paramRequired); - validate('params.video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + validate('video.protocols', val => isDefined(val), paramRequired); + validate('video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), paramInvalid, 'array of numbers, ex: [2,3]'); - validate('params.video.api', val => isDefined(val), paramRequired); - validate('params.video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + validate('video.api', val => isDefined(val), paramRequired); + validate('video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), paramInvalid, 'array of numbers, ex: [2,3]'); - validate('params.video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, + validate('video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, 'array of integers, ex: [2,6]'); - validate('params.video.maxduration', val => isDefined(val), paramRequired); - validate('params.video.maxduration', val => utils.isInteger(val), paramInvalid); - validate('params.video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); - validate('params.video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); - validate('params.video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); - validate('params.video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.maxduration', val => isDefined(val), paramRequired); + validate('video.maxduration', val => utils.isInteger(val), paramInvalid); + validate('video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); + validate('video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); validate('params.badv', val => !isDefined(val) || utils.isArray(val), paramInvalid, 'array of strings, ex: ["ford.com","pepsi.com"]'); validate('params.bcat', val => !isDefined(val) || utils.isArray(val), paramInvalid, diff --git a/modules/yieldmoBidAdapter.md b/modules/yieldmoBidAdapter.md index 54be295a1a1..040fbbec486 100644 --- a/modules/yieldmoBidAdapter.md +++ b/modules/yieldmoBidAdapter.md @@ -96,3 +96,8 @@ var videoAdUnit = [{ }] }]; ``` + +Please also note, that we support the following OpenRTB params: +'mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', +'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable'. +They can be specified in `mediaTypes.video` or in `bids[].params.video`. diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 450608a82f4..c956df55a1f 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -5,7 +5,8 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'zeta_global_ssp'; const ENDPOINT_URL = 'https://ssp.disqus.com/bid'; -const USER_SYNC_URL = 'https://ssp.disqus.com/match'; +const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; +const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; const DEFAULT_CUR = 'USD'; const TTL = 200; const NET_REV = true; @@ -42,7 +43,7 @@ export const spec = { const secure = 1; // treat all requests as secure const request = validBidRequests[0]; const params = request.params; - let impData = { + const impData = { id: request.bidId, secure: secure, banner: buildBanner(request) @@ -58,7 +59,7 @@ export const spec = { app: params.app ? params.app : {}, ext: { tags: params.tags ? params.tags : {}, - sid: params.sid ? params.sid : {} + sid: params.sid ? params.sid : undefined } }; @@ -83,6 +84,7 @@ export const spec = { } }; } + provideEids(request, payload); return { method: 'POST', url: ENDPOINT_URL, @@ -123,23 +125,38 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @param gdprConsent The GDPR consent parameters - * @param uspConsent The USP consent parameters - * @return {UserSync[]} The user syncs which should be dropped. + * Register User Sync. */ - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + let syncurl = ''; + + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + + // CCPA + if (uspConsent) { + syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); + } + + // coppa compliance + if (config.getConfig('coppa') === true) { + syncurl += '&coppa=1'; + } + if (syncOptions.iframeEnabled) { - syncs.push({ + return [{ type: 'iframe', - url: USER_SYNC_URL - }); + url: USER_SYNC_URL_IFRAME + syncurl + }]; + } else { + return [{ + type: 'image', + url: USER_SYNC_URL_IMAGE + syncurl + }]; } - return syncs; } } @@ -156,4 +173,10 @@ function buildBanner(request) { }; } +function provideEids(request, payload) { + if (Array.isArray(request.userIdAsEids) && request.userIdAsEids.length > 0) { + utils.deepSetValue(payload, 'user.ext.eids', request.userIdAsEids); + } +} + registerBidder(spec); diff --git a/package-lock.json b/package-lock.json index 3c4e59cdba7..d59e11b038e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.39.0-pre", + "version": "4.43.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -15084,11 +15084,6 @@ } } }, - "jsencrypt": { - "version": "3.0.0-rc.1", - "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.0.0-rc.1.tgz", - "integrity": "sha512-gcvGaqerlUJy1Kq6tNgPYteVEoWNemu+9hBe2CdsCIz4rVcwjoTQ72iD1W76/PRMlnkzG0yVh7nwOOMOOUfKmg==" - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", diff --git a/package.json b/package.json index ecf293403b7..a5b73921884 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.40.0", + "version": "4.42.1", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { @@ -17,7 +17,7 @@ "header bidding", "prebid" ], - "globalVarName": "pbjs", + "globalVarName": "fsprebid", "author": "the prebid.js contributors", "license": "Apache-2.0", "engines": { @@ -110,7 +110,6 @@ "dset": "2.0.1", "express": "^4.15.4", "fun-hooks": "^0.9.9", - "jsencrypt": "^3.0.0-rc.1", "just-clone": "^1.0.2", "live-connect-js": "2.0.0" } diff --git a/src/auctionManager.js b/src/auctionManager.js index 1aca1277aa8..bbafd7426d5 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -15,6 +15,8 @@ * @property {function(): Object} createAuction - creates auction instance and stores it for future reference * @property {function(): Object} findBidByAdId - find bid received by adId. This function will be called by $$PREBID_GLOBAL$$.renderAd * @property {function(): Object} getStandardBidderAdServerTargeting - returns standard bidder targeting for all the adapters. Refer http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.bidderSettings for more details + * @property {function(Object): void} addWinningBid - add a winning bid to an auction based on auctionId + * @property {function(): void} clearAllAuctions - clear all auctions for testing */ import { uniques, flatten, logWarn } from './utils.js'; @@ -113,6 +115,10 @@ export function newAuctionManager() { return _auctions.length && _auctions[_auctions.length - 1].getAuctionId() }; + auctionManager.clearAllAuctions = function() { + _auctions.length = 0; + } + function _addAuction(auction) { _auctions.push(auction); } diff --git a/src/config.js b/src/config.js index 7cb1a81955d..00cf1efd854 100644 --- a/src/config.js +++ b/src/config.js @@ -265,7 +265,7 @@ export function newConfig() { } for (let k of Object.keys(val)) { - if (k !== 'secondaryBidders') { + if (k !== 'secondaryBidders' && k !== 'suppressStaleRender') { utils.logWarn(`Auction Options given an incorrect param: ${k}`) return false } @@ -277,6 +277,11 @@ export function newConfig() { utils.logWarn(`Auction Options ${k} must be only string`); return false } + } else if (k === 'suppressStaleRender') { + if (!utils.isBoolean(val[k])) { + utils.logWarn(`Auction Options ${k} must be of type boolean`); + return false; + } } } return true; diff --git a/src/constants.json b/src/constants.json index e6b9687f911..c43e88cf75f 100644 --- a/src/constants.json +++ b/src/constants.json @@ -39,7 +39,8 @@ "AD_RENDER_FAILED": "adRenderFailed", "TCF2_ENFORCEMENT": "tcf2Enforcement", "AUCTION_DEBUG": "auctionDebug", - "BID_VIEWABLE": "bidViewable" + "BID_VIEWABLE": "bidViewable", + "STALE_RENDER": "staleRender" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", diff --git a/src/prebid.js b/src/prebid.js index 2211e8ec2b5..f58c97ec581 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,7 @@ const events = require('./events.js'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, STALE_RENDER } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -390,63 +390,75 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id, options) { try { // lookup ad by ad Id const bid = auctionManager.findBidByAdId(id); + if (bid) { - // replace macros according to openRTB with price paid = bid.cpm - bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); - bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); - - // replacing clickthrough if submitted - if (options && options.clickThrough) { - const { clickThrough } = options; - bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); - bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + let shouldRender = true; + if (bid && bid.status === CONSTANTS.BID_STATUS.RENDERED) { + utils.logWarn(`Ad id ${bid.adId} has been rendered before`); + events.emit(STALE_RENDER, bid); + if (utils.deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + shouldRender = false; + } } - // save winning bids - auctionManager.addWinningBid(bid); - - // emit 'bid won' event here - events.emit(BID_WON, bid); - - const { height, width, ad, mediaType, adUrl, renderer } = bid; - - const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); - utils.insertElement(creativeComment, doc, 'body'); - - if (isRendererRequired(renderer)) { - executeRenderer(renderer, bid); - } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { - const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; - emitAdRenderFail({ reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id }); - } else if (ad) { - // will check if browser is firefox and below version 67, if so execute special doc.open() - // for details see: https://github.com/prebid/Prebid.js/pull/3524 - // TODO remove this browser specific code at later date (when Firefox < 67 usage is mostly gone) - if (navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox/') > -1) { - const firefoxVerRegx = /firefox\/([\d\.]+)/; - let firefoxVer = navigator.userAgent.toLowerCase().match(firefoxVerRegx)[1]; // grabs the text in the 1st matching group - if (firefoxVer && parseInt(firefoxVer, 10) < 67) { - doc.open('text/html', 'replace'); + if (shouldRender) { + // replace macros according to openRTB with price paid = bid.cpm + bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); + bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); + + // replacing clickthrough if submitted + if (options && options.clickThrough) { + const {clickThrough} = options; + bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); + bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + } + + // save winning bids + auctionManager.addWinningBid(bid); + + // emit 'bid won' event here + events.emit(BID_WON, bid); + + const {height, width, ad, mediaType, adUrl, renderer} = bid; + + const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + utils.insertElement(creativeComment, doc, 'body'); + + if (isRendererRequired(renderer)) { + executeRenderer(renderer, bid); + } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { + const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; + emitAdRenderFail({reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id}); + } else if (ad) { + // will check if browser is firefox and below version 67, if so execute special doc.open() + // for details see: https://github.com/prebid/Prebid.js/pull/3524 + // TODO remove this browser specific code at later date (when Firefox < 67 usage is mostly gone) + if (navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox/') > -1) { + const firefoxVerRegx = /firefox\/([\d\.]+)/; + let firefoxVer = navigator.userAgent.toLowerCase().match(firefoxVerRegx)[1]; // grabs the text in the 1st matching group + if (firefoxVer && parseInt(firefoxVer, 10) < 67) { + doc.open('text/html', 'replace'); + } } + doc.write(ad); + doc.close(); + setRenderSize(doc, width, height); + utils.callBurl(bid); + } else if (adUrl) { + const iframe = utils.createInvisibleIframe(); + iframe.height = height; + iframe.width = width; + iframe.style.display = 'inline'; + iframe.style.overflow = 'hidden'; + iframe.src = adUrl; + + utils.insertElement(iframe, doc, 'body'); + setRenderSize(doc, width, height); + utils.callBurl(bid); + } else { + const message = `Error trying to write ad. No ad for bid response id: ${id}`; + emitAdRenderFail({reason: NO_AD, message, bid, id}); } - doc.write(ad); - doc.close(); - setRenderSize(doc, width, height); - utils.callBurl(bid); - } else if (adUrl) { - const iframe = utils.createInvisibleIframe(); - iframe.height = height; - iframe.width = width; - iframe.style.display = 'inline'; - iframe.style.overflow = 'hidden'; - iframe.src = adUrl; - - utils.insertElement(iframe, doc, 'body'); - setRenderSize(doc, width, height); - utils.callBurl(bid); - } else { - const message = `Error trying to write ad. No ad for bid response id: ${id}`; - emitAdRenderFail({ reason: NO_AD, message, bid, id }); } } else { const message = `Error trying to write ad. Cannot find ad by given id : ${id}`; diff --git a/src/secureCreatives.js b/src/secureCreatives.js index def1a9abdbb..a172ec62630 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -6,19 +6,21 @@ import events from './events.js'; import { fireNativeTrackers, getAssetMessage, getAllAssetsMessage } from './native.js'; import constants from './constants.json'; -import { logWarn, replaceAuctionPrice } from './utils.js'; +import { logWarn, replaceAuctionPrice, deepAccess } from './utils.js'; import { auctionManager } from './auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; import includes from 'core-js-pure/features/array/includes.js'; +import { config } from './config.js'; const BID_WON = constants.EVENTS.BID_WON; +const STALE_RENDER = constants.EVENTS.STALE_RENDER; export function listenMessagesFromCreative() { window.addEventListener('message', receiveMessage, false); } -function receiveMessage(ev) { +export function receiveMessage(ev) { var key = ev.message ? 'message' : 'data'; var data = {}; try { @@ -33,6 +35,14 @@ function receiveMessage(ev) { }); if (adObject && data.message === 'Prebid Request') { + if (adObject.status === constants.BID_STATUS.RENDERED) { + logWarn(`Ad id ${adObject.adId} has been rendered before`); + events.emit(STALE_RENDER, adObject); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } + _sendAdToCreative(adObject, ev); // save winning bids diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 8161d9f1827..9492db6e849 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -233,7 +233,7 @@ describe('config API', function () { expect(logWarnSpy.called).to.equal(false); }); - it('sets auctionOptions', function () { + it('sets auctionOptions secondaryBidders', function () { const auctionOptionsConfig = { 'secondaryBidders': ['rubicon', 'appnexus'] } @@ -241,6 +241,14 @@ describe('config API', function () { expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig); }); + it('sets auctionOptions suppressStaleRender', function () { + const auctionOptionsConfig = { + 'suppressStaleRender': 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); @@ -257,6 +265,15 @@ describe('config API', function () { assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); + it('should log warning for invalid auctionOptions suppress stale render', function () { + setConfig({ auctionOptions: { + 'suppressStaleRender': 'test', + }}); + expect(logWarnSpy.calledOnce).to.equal(true); + const warning = 'Auction Options suppressStaleRender 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/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index cb05fa62ab6..a9ead9fd021 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -161,7 +161,11 @@ describe('a4gAdapterTests', function () { ad: 'test ad', currency: 'USD', ttl: 120, - netRevenue: true + netRevenue: true, + meta: { + advertiserDomains: [] + } + } ]; const result = spec.interpretResponse(bidResponse, bidRequest); @@ -181,7 +185,10 @@ describe('a4gAdapterTests', function () { ad: 'test ad', currency: 'USD', ttl: 120, - netRevenue: true + netRevenue: true, + meta: { + advertiserDomains: [] + } } ]; const result = spec.interpretResponse(bidResponseWithoutCrid, bidRequest); @@ -201,7 +208,8 @@ describe('a4gAdapterTests', function () { 'currency', 'netRevenue', 'ttl', - 'ad' + 'ad', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -215,5 +223,13 @@ describe('a4gAdapterTests', function () { expect(result[0].requestId).to.not.equal(result[0].adId); }) + + it('advertiserDomains is included when sent by server', function () { + bidResponse.body[0].adomain = ['test_adomain']; + let response = spec.interpretResponse(bidResponse, bidRequest); + expect(Object.keys(response[0].meta)).to.include.members(['advertiserDomains']); + expect(response[0].meta.advertiserDomains).to.deep.equal(['test_adomain']); + delete bidResponse.body[0].adomain; + }); }); }); diff --git a/test/spec/modules/adWMGBidAdapter_spec.js b/test/spec/modules/adWMGBidAdapter_spec.js index 8b927ace84c..db536ca14e2 100644 --- a/test/spec/modules/adWMGBidAdapter_spec.js +++ b/test/spec/modules/adWMGBidAdapter_spec.js @@ -209,7 +209,8 @@ describe('adWMGBidAdapter', function () { 'ttl': 300, 'creativeId': 'creative-id', 'netRevenue': true, - 'currency': 'USD' + 'currency': 'USD', + 'adomain': ['testdomain.com'] } }; }); @@ -219,7 +220,7 @@ describe('adWMGBidAdapter', function () { expect(responses).to.be.an('array').that.is.not.empty; let response = responses[0]; - expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'meta', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency'); expect(response.requestId).to.equal('request-id'); expect(response.cpm).to.equal(100); @@ -230,6 +231,8 @@ describe('adWMGBidAdapter', function () { expect(response.creativeId).to.equal('creative-id'); expect(response.netRevenue).to.be.true; expect(response.currency).to.equal('USD'); + expect(response.meta.advertiserDomains[0]).to.equal('testdomain.com'); + expect(response.meta.mediaType).to.equal('banner'); }); it('should return an empty array when serverResponse is empty', () => { diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 6ee42e47950..4b66a96be16 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -579,6 +579,7 @@ describe('Adagio bid adapter', () => { const expected = { consentString, + allowAuctionWithoutConsent: 0, consentRequired: 1, apiVersion: 2 }; @@ -615,6 +616,7 @@ describe('Adagio bid adapter', () => { const expected = { consentString, consentRequired: 0, + allowAuctionWithoutConsent: 0, apiVersion: 2 }; @@ -738,6 +740,77 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.user.eids).to.be.empty; }); }); + + describe('with priceFloors module', function() { + it('should get and set floor by mediatype and sizes', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + playerSize: [600, 480] + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + + // delete the computed `sizes` prop as we are based on mediaTypes only. + delete bid01.sizes + + bid01.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(3); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'banner', s: '300x250'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'banner', s: '300x600'}); + expect(requests[0].data.adUnits[0].floors[2]).to.deep.equal({f: 1, mt: 'video', s: '600x480'}); + }); + + it('should get and set floor by mediatype if no size provided (ex native, video)', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + }, + native: { + body: { required: true } + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + bid01.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(2); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'video'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'native'}); + }); + + it('should get and set floor with default value if no floors found', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + bid01.getFloor = () => { + return { floor: NaN, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(1); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 0.1, mt: 'video'}); + }); + }); }); describe('interpretResponse()', function() { diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index 2c141d31bf8..ae92af32a1e 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -35,11 +35,13 @@ describe('Adf adapter', function () { }); describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); it('should send request with correct structure', function () { let validBidRequests = [{ bidId: 'bidId', params: { - siteId: 'siteId', adxDomain: '10.8.57.207' } }]; @@ -53,7 +55,7 @@ describe('Adf adapter', function () { describe('user privacy', function () { it('should send GDPR Consent data to adform if gdprApplies', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); @@ -63,7 +65,7 @@ describe('Adf adapter', function () { }); it('should send gdpr as number', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { referer: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); @@ -72,7 +74,7 @@ describe('Adf adapter', function () { }); it('should send CCPA Consent data to adform', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', test: 1 } }]; + let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; let bidderRequest = { uspConsent: '1YA-', refererInfo: { referer: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); @@ -118,7 +120,7 @@ describe('Adf adapter', function () { it('should add test and is_debug to request, if test is set in parameters', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', test: 1 } + params: { test: 1 } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); @@ -151,26 +153,66 @@ describe('Adf adapter', function () { }); it('should send info about device', function () { + config.setConfig({ + device: { w: 100, h: 100 } + }); let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId' } + params: { mid: '1000' } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); assert.equal(request.device.ua, navigator.userAgent); + assert.equal(request.device.w, 100); + assert.equal(request.device.h, 100); + }); + + it('should send app info', function () { + config.setConfig({ + app: { id: 'appid' }, + ortb2: { app: { name: 'appname' } } + }); + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' } + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); + + assert.equal(request.app.id, 'appid'); + assert.equal(request.app.name, 'appname'); + assert.equal(request.site, undefined); }); + it('should send info about the site', function () { + config.setConfig({ + site: { + id: '123123', + publisher: { + domain: 'publisher.domain.com' + } + }, + ortb2: { + site: { + publisher: { + name: 'publisher\'s name' + } + } + } + }); let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', publisher: {id: '123123', domain: 'publisher.domain.com', name: 'publisher\'s name'} } + params: { mid: '1000' } }]; let refererInfo = { referer: 'page' }; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo }).data); assert.deepEqual(request.site, { page: refererInfo.referer, - publisher: validBidRequests[0].params.publisher, - id: validBidRequests[0].params.siteId + publisher: { + domain: 'publisher.domain.com', + name: 'publisher\'s name' + }, + id: '123123' }); }); @@ -213,7 +255,7 @@ describe('Adf adapter', function () { it('should send correct priceType value', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', priceType: 'net' } + params: { priceType: 'net' } }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data); @@ -237,13 +279,16 @@ describe('Adf adapter', function () { it('should add incrementing values of id', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId' } + params: { mid: '1000' }, + mediaTypes: {video: {}} }, { bidId: 'bidId2', - params: { siteId: 'siteId' } + params: { mid: '1000' }, + mediaTypes: {video: {}} }, { bidId: 'bidId3', - params: { siteId: 'siteId' } + params: { mid: '1000' }, + mediaTypes: {video: {}} }]; let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; @@ -253,21 +298,106 @@ describe('Adf adapter', function () { }); it('should add mid', function () { - let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId', mid: 1000 } }, - { bidId: 'bidId2', params: { siteId: 'siteId', mid: 1001 } }, - { bidId: 'bidId3', params: { siteId: 'siteId', mid: 1002 } }]; + let validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }, + { bidId: 'bidId2', params: {mid: 1001}, mediaTypes: {video: {}} }, + { bidId: 'bidId3', params: {mid: 1002}, mediaTypes: {video: {}} }]; let imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; for (let i = 0; i < 3; i++) { assert.equal(imps[i].tagid, validBidRequests[i].params.mid); } }); + describe('multiple media types', function () { + it('should use single media type for bidding', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + video: {} + } + }, { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaTypes: { + video: {}, + native: {} + } + }, { + bidId: 'bidId2', + params: { mid: 1000 }, + nativeParams: { + title: { required: true, len: 140 } + }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + native: {} + } + }]; + let [ banner, video, native ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp; + + assert.ok(banner.banner); + assert.equal(banner.video, undefined); + assert.equal(banner.native, undefined); + assert.ok(video.video); + assert.equal(video.banner, undefined); + assert.equal(video.native, undefined); + assert.ok(native.native); + assert.equal(native.video, undefined); + assert.equal(native.banner, undefined); + }); + }); + + describe('banner', function () { + it('should convert sizes to openrtb format', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + } + } + }]; + let { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0]; + assert.deepEqual(banner, { + format: [ { w: 100, h: 100 }, { w: 200, h: 300 } ] + }); + }); + }); + + describe('video', function () { + it('should pass video mediatype config', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: 1000 }, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + } + } + }]; + let { video } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { referer: 'page' } }).data).imp[0]; + assert.deepEqual(video, { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + }); + }); + }); + describe('native', function () { describe('assets', function () { it('should set correct asset id', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -283,7 +413,7 @@ describe('Adf adapter', function () { it('should add required key if it is necessary', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -303,7 +433,7 @@ describe('Adf adapter', function () { it('should map img and data assets', function () { let validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: true, sizes: [150, 50] }, @@ -330,7 +460,7 @@ describe('Adf adapter', function () { it('should flatten sizes and utilise first pair', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { sizes: [[200, 300], [100, 200]] @@ -348,7 +478,7 @@ describe('Adf adapter', function () { it('should utilise aspect_ratios', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { aspect_ratios: [{ @@ -380,7 +510,7 @@ describe('Adf adapter', function () { it('should not throw error if aspect_ratios config is not defined', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { aspect_ratios: [] @@ -398,7 +528,7 @@ describe('Adf adapter', function () { it('should expect any dimensions if min_width not passed', function () { const validBidRequests = [{ bidId: 'bidId', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { image: { aspect_ratios: [{ @@ -441,7 +571,7 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -450,7 +580,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId2', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -482,7 +612,7 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -491,7 +621,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId2', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -500,7 +630,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId3', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -509,7 +639,7 @@ describe('Adf adapter', function () { }, { bidId: 'bidId4', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -543,7 +673,9 @@ describe('Adf adapter', function () { assets: [], link: { url: 'link' }, imptrackers: ['imptrackers url1', 'imptrackers url2'] - } + }, + dealid: 'deal-id', + adomain: [ 'demo.com' ] } ] }], @@ -555,7 +687,8 @@ describe('Adf adapter', function () { bids: [ { bidId: 'bidId1', - params: { siteId: 'siteId', mid: 1000 }, + params: { mid: 1000 }, + mediaType: 'native', nativeParams: { title: { required: true, len: 140 }, image: { required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif'] }, @@ -574,6 +707,9 @@ describe('Adf adapter', function () { assert.deepEqual(bids[0].netRevenue, false); assert.deepEqual(bids[0].currency, serverResponse.body.cur); assert.deepEqual(bids[0].mediaType, 'native'); + assert.deepEqual(bids[0].meta.mediaType, 'native'); + assert.deepEqual(bids[0].meta.advertiserDomains, [ 'demo.com' ]); + assert.deepEqual(bids[0].dealId, 'deal-id'); }); it('should set correct native params', function () { const bid = [ @@ -678,5 +814,96 @@ describe('Adf adapter', function () { const result = spec.interpretResponse(serverResponse, bidRequest)[0]; assert.ok(!result); }); + + describe('banner', function () { + it('should set ad content on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '' }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaType: 'banner' + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].ad, ''); + }); + }); + + describe('video', function () { + it('should set vastXml on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '' }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaType: 'video' + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].vastXml, ''); + }); + + it('should add renderer for outstream bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ impid: '1', adm: '' }, { impid: '2', adm: '' }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: { mid: 1000 }, + mediaType: 'video', + mediaTypes: { + video: { + context: 'outstream' + } + } + }, + { + bidId: 'bidId2', + params: { mid: 1000 }, + mediaType: 'video', + mediaTypes: { + video: { + constext: 'instream' + } + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.ok(bids[0].renderer); + assert.equal(bids[1].renderer, undefined); + }); + }); }); }); diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index d3c9a2cf816..c4ad134711a 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -107,7 +107,12 @@ describe('AdkernelAdn adapter', function () { bid: 5.0, tag: '', w: 300, - h: 250 + h: 250, + advertiserId: 777, + advertiserName: 'advertiser', + agencyName: 'agency', + advertiserDomains: ['example.com'], + primaryCatId: 'IAB1-1', }, { id: 'ad-unit-2', impid: '31d798477126c4', @@ -115,7 +120,12 @@ describe('AdkernelAdn adapter', function () { bid: 2.5, tag: '', w: 300, - h: 250 + h: 250, + advertiserId: 777, + advertiserName: 'advertiser', + agencyName: 'agency', + advertiserDomains: ['example.com'], + secondaryCatIds: ['IAB1-4', 'IAB8-16', 'IAB25-5'] }, { id: 'video_wrapper', impid: '57d602ad1c9545', @@ -375,6 +385,11 @@ describe('AdkernelAdn adapter', function () { expect(resp).to.have.property('mediaType', 'banner'); expect(resp).to.have.property('ad'); expect(resp.ad).to.have.string(''); + expect(resp.meta.advertiserId).to.be.eql(777); + expect(resp.meta.advertiserName).to.be.eql('advertiser'); + expect(resp.meta.agencyName).to.be.eql('agency'); + expect(resp.meta.advertiserDomains).to.be.eql(['example.com']); + expect(resp.meta.primaryCatId).to.be.eql('IAB1-1'); }); it('should return fully-initialized video bid-response', function () { diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 62073fc6aaa..9c9bf0e9914 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -2,10 +2,17 @@ import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' import { spec } from 'modules/adnuntiusBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; describe('adnuntiusBidAdapter', function () { + afterEach(function () { + config.resetConfig(); + }); const tzo = new Date().getTimezoneOffset(); const ENDPOINT_URL = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json`; + // const ENDPOINT_URL_SEGMENTS_ = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json`; + const ENDPOINT_URL_SEGMENTS = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json&segments=segment1,segment2,segment3`; + const ENDPOINT_URL_CONSENT = `https://delivery.adnuntius.com/i?tzo=${tzo}&format=json&consentString=consentString`; const adapter = newBidder(spec); const bidRequests = [ @@ -116,6 +123,74 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('data'); expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\"}]}'); }); + + it('should pass segments if available in config', function () { + config.setBidderConfig({ + bidders: ['adnuntius', 'other'], + config: { + ortb2: { + user: { + data: [{ + name: 'adnuntius', + segment: [{ id: 'segment1' }, { id: 'segment2' }] + }, + { + name: 'other', + segment: ['segment3'] + }], + } + } + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests)); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); + }); + + it('should skip segments in config if not either id or array of strings', function () { + config.setBidderConfig({ + bidders: ['adnuntius', 'other'], + config: { + ortb2: { + user: { + data: [{ + name: 'adnuntius', + segment: [{ id: 'segment1' }, { id: 'segment2' }, { id: 'segment3' }] + }, + { + name: 'other', + segment: [{ + notright: 'segment4' + }] + }], + } + } + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidRequests)); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); + }); + }); + + describe('user privacy', function () { + it('should send GDPR Consent data if gdprApplies', function () { + let request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); + }); + + it('should not send GDPR Consent data if gdprApplies equals undefined', function () { + let request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + }); }); describe('interpretResponse', function () { diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 2b4b7d711e1..43316cd7483 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -150,7 +150,8 @@ describe('AdoceanAdapter', function () { 'width': '300', 'height': '250', 'crid': '0af345b42983cc4bc0', - 'ttl': '300' + 'ttl': '300', + 'adomain': ['adocean.pl'] } ], 'headers': { @@ -186,7 +187,10 @@ describe('AdoceanAdapter', function () { 'ad': '', 'creativeId': '0af345b42983cc4bc0', 'ttl': 300, - 'netRevenue': false + 'netRevenue': false, + 'meta': { + 'advertiserDomains': ['adocean.pl'] + } } ]; @@ -197,6 +201,8 @@ describe('AdoceanAdapter', function () { resultKeys.forEach(function(k) { if (k === 'ad') { expect(result[0][k]).to.match(/$/); + } else if (k === 'meta') { + expect(result[0][k]).to.deep.equal(expectedResponse[0][k]); } else { expect(result[0][k]).to.equal(expectedResponse[0][k]); } diff --git a/test/spec/modules/adotBidAdapter_spec.js b/test/spec/modules/adotBidAdapter_spec.js index d580cd763a4..40605b17b20 100644 --- a/test/spec/modules/adotBidAdapter_spec.js +++ b/test/spec/modules/adotBidAdapter_spec.js @@ -25,19 +25,16 @@ describe('Adot Adapter', function () { bidder: 'adot', bidderRequestId: 'bid_request_id', bidId: 'bid_id', - params: { + params: {}, + mediaTypes: { video: { + context: 'outstream', + playerSize: [[300, 250]], mimes: ['video/mp4'], minDuration: 5, maxDuration: 30, protocols: [2, 3] } - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[300, 250]] - } } }, @@ -48,17 +45,17 @@ describe('Adot Adapter', function () { bidId: 'bid_id', params: { video: { - instreamContext: 'pre-roll', - mimes: ['video/mp4'], - minDuration: 5, - maxDuration: 30, - protocols: [2, 3] + instreamContext: 'pre-roll' } }, mediaTypes: { video: { context: 'instream', - playerSize: [[300, 250]] + playerSize: [[300, 250]], + mimes: ['video/mp4'], + minDuration: 5, + maxDuration: 30, + protocols: [2, 3] } } }, @@ -557,6 +554,7 @@ describe('Adot Adapter', function () { price: 1.5, h: 350, w: 300, + adomain: ['adot'], ext: { adot: { media_type: 'banner' @@ -568,6 +566,7 @@ describe('Adot Adapter', function () { crid: 'creative_id_2', adm: 'creative_data_2_${AUCTION_PRICE}', nurl: 'win_notice_url_2_${AUCTION_PRICE}', + adomain: ['adot'], price: 2.5, h: 400, w: 350, @@ -913,14 +912,14 @@ describe('Adot Adapter', function () { it('should return true when given an ad unit without minimum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = undefined; + adUnit.mediaTypes.video.minDuration = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(true); }); it('should return true when given an ad unit without maximum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = undefined; + adUnit.mediaTypes.video.maxDuration = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(true); }); @@ -941,84 +940,84 @@ describe('Adot Adapter', function () { it('should return false when given an ad unit without video parameters', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video = undefined; + adUnit.mediaTypes.video = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with invalid video parameters', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video = 'bad_bidder_parameters'; + adUnit.mediaTypes.video = 'bad_bidder_parameters'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit without mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = undefined; + adUnit.mediaTypes.video.mimes = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = 'bad_mime_types'; + adUnit.mediaTypes.video.mimes = 'bad_mime_types'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an empty mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = []; + adUnit.mediaTypes.video.mimes = []; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid mime types parameter value', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = [200]; + adUnit.mediaTypes.video.mimes = [200]; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid minimum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = 'bad_min_duration'; + adUnit.mediaTypes.video.minDuration = 'bad_min_duration'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid maximum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = 'bad_max_duration'; + adUnit.mediaTypes.video.maxDuration = 'bad_max_duration'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit without protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = undefined; + adUnit.mediaTypes.video.protocols = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = 'bad_protocols'; + adUnit.mediaTypes.video.protocols = 'bad_protocols'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an empty protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = []; + adUnit.mediaTypes.video.protocols = []; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid protocols parameter value', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = ['bad_protocols_value']; + adUnit.mediaTypes.video.protocols = ['bad_protocols_value']; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); @@ -1452,13 +1451,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid pre-roll instream ad unit', function () { @@ -1476,13 +1475,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(0); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid mid-roll instream ad unit', function () { @@ -1500,13 +1499,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(-1); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid post-roll instream ad unit', function () { @@ -1524,13 +1523,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(-2); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without player size', function () { @@ -1548,13 +1547,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.equal(null); expect(serverRequests[0].data.imp[0].video.h).to.equal(null); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit with an empty player size', function () { @@ -1572,13 +1571,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.equal(null); expect(serverRequests[0].data.imp[0].video.h).to.equal(null); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit with multiple player sizes', function () { @@ -1596,18 +1595,18 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without minimum duration', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = undefined; + adUnit.mediaTypes.video.minDuration = undefined; const adUnits = [adUnit]; @@ -1620,18 +1619,18 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); expect(serverRequests[0].data.imp[0].video.minduration).to.equal(null); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without maximum duration', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = undefined; + adUnit.mediaTypes.video.maxDuration = undefined; const adUnits = [adUnit]; @@ -1644,13 +1643,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); expect(serverRequests[0].data.imp[0].video.maxduration).to.equal(null); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with two impressions when given two valid ad units with different impression identifiers', function () { @@ -1671,31 +1670,31 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); expect(serverRequests[0].data.imp[1]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[1].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[0].data.imp[1].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[1].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[1].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[1].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[1].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[1].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[1].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[0].data.imp[1].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[0].data.imp[1].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[0].data.imp[1].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[1].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[1].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); it('should return a server request with one overridden impression when given two valid ad units with identical identifiers', function () { const adUnit_1 = utils.deepClone(examples.adUnit_video_outstream); - adUnit_1.params.video.minDuration = 10; + adUnit_1.mediaTypes.video.minDuration = 10; const adUnit_2 = utils.deepClone(examples.adUnit_video_outstream); - adUnit_2.params.video.minDuration = 15; + adUnit_2.mediaTypes.video.minDuration = 15; const adUnits = [adUnit_1, adUnit_2]; @@ -1708,13 +1707,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); it('should return two server requests with one impression when given two valid ad units with different bid request identifiers', function () { @@ -1735,25 +1734,25 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); expect(serverRequests[1].data).to.exist.and.to.be.an('object'); expect(serverRequests[1].data.imp).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); expect(serverRequests[1].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[1].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[1].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[1].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[1].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[1].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[1].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[1].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[1].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[1].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[1].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[1].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[1].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[1].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); }); @@ -2121,6 +2120,48 @@ describe('Adot Adapter', function () { expect(ads[1].renderer).to.equal(null); }); + it('should return two ads when given a valid server response with two bids that contains adomain', function () { + const serverRequest = examples.serverRequest_banner_twoImps; + + const serverResponse = examples.serverResponse_banner_twoBids; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); + const adm2WithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[1].adm, serverResponse.body.seatbid[0].bid[1].price); + + expect(ads).to.be.an('array').and.to.have.length(2); + + expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); + expect(ads[0].adUrl).to.equal(null); + expect(ads[0].vastXml).to.equal(null); + expect(ads[0].vastUrl).to.equal(null); + expect(ads[0].meta.advertiserDomains[0]).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].adomain[0]) + expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); + expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[0].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[0].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[0].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(ads[0].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + expect(ads[0].renderer).to.equal(null); + expect(ads[1].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[1].bidId); + expect(ads[1].meta.advertiserDomains[0]).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].adomain[0]) + expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(adm2WithAuctionPriceReplaced); + expect(ads[1].adUrl).to.equal(null); + expect(ads[1].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].crid); + expect(ads[1].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].price); + expect(ads[1].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[1].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[1].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[1].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].h); + expect(ads[1].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].w); + expect(ads[1].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + expect(ads[1].renderer).to.equal(null); + }); + it('should return no ad when not given a server response', function () { const ads = spec.interpretResponse(null); @@ -3084,20 +3125,6 @@ describe('Adot Adapter', function () { expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].renderer).to.be.an('object'); }); - - it('should append a command to the ad rendering queue when executing the renderer', function (done) { - const serverRequest = examples.serverRequest_video_outstream; - const serverResponse = examples.serverResponse_video_outstream; - - const [ad] = spec.interpretResponse(serverResponse, serverRequest); - - this.spyAdRenderingQueue(ad); - - executeAdRenderer(ad, () => { - expect(ad.renderer.push.calledOnce).to.equal(true); - expect(ad.renderer.push.firstCall.args[0]).to.exist.and.to.be.a('function'); - }, done); - }); }); }); diff --git a/test/spec/modules/adtargetBidAdapter_spec.js b/test/spec/modules/adtargetBidAdapter_spec.js index 5a867e7dd52..d1221d24022 100644 --- a/test/spec/modules/adtargetBidAdapter_spec.js +++ b/test/spec/modules/adtargetBidAdapter_spec.js @@ -45,7 +45,8 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 + 'cpm': 0.9, + 'adomain': ['a.com'] }] }; const SERVER_DISPLAY_RESPONSE = { @@ -107,7 +108,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -120,7 +124,10 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } }]; describe('adtargetBidAdapter', () => { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index e6916997133..4cfb367efb3 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -80,9 +80,9 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 - } - ] + 'cpm': 0.9, + 'adomain': ['a.com'] + }] }; const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; const SERVER_DISPLAY_RESPONSE = { @@ -163,7 +163,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -177,7 +180,11 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } + }]; describe('adtelligentBidAdapter', () => { diff --git a/test/spec/modules/aduptechBidAdapter_spec.js b/test/spec/modules/aduptechBidAdapter_spec.js index 1e39e0cfc8b..362cd3e506a 100644 --- a/test/spec/modules/aduptechBidAdapter_spec.js +++ b/test/spec/modules/aduptechBidAdapter_spec.js @@ -1,180 +1,280 @@ import { expect } from 'chai'; import { BIDDER_CODE, - PUBLISHER_PLACEHOLDER, - ENDPOINT_URL, ENDPOINT_METHOD, - spec, - extractGdprFromBidderRequest, - extractParamsFromBidRequest, - extractSizesFromBidRequest, - extractTopWindowReferrerFromBidRequest, - extractTopWindowUrlFromBidRequest + internal, + spec } from '../../../modules/aduptechBidAdapter.js'; +import { config } from '../../../src/config.js'; +import * as utils from '../../../src/utils.js'; +import { BANNER, NATIVE } from '../../../src/mediaTypes.js' import { newBidder } from '../../../src/adapters/bidderFactory.js'; describe('AduptechBidAdapter', () => { - describe('extractGdprFromBidderRequest', () => { - it('should handle empty bidder request', () => { - const bidderRequest = null; - expect(extractGdprFromBidderRequest(bidderRequest)).to.be.null; - }); + describe('internal', () => { + describe('extractGdpr', () => { + it('should handle empty bidderRequest', () => { + expect(internal.extractGdpr(null)).to.be.null; + expect(internal.extractGdpr({})).to.be.null; + }); - it('should handle missing gdprConsent in bidder request', () => { - const bidderRequest = {}; - expect(extractGdprFromBidderRequest(bidderRequest)).to.be.null; - }); + it('should extract bidderRequest.gdprConsent', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: false + } + }; - it('should handle gdprConsent in bidder request', () => { - const bidderRequest = { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true - } - }; + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }); + }); + + it('should handle missing bidderRequest.gdprConsent.gdprApplies', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString' + } + }; - expect(extractGdprFromBidderRequest(bidderRequest)).to.deep.equal({ - consentString: bidderRequest.gdprConsent.consentString, - consentRequired: true + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: true + }); }); - }); - }); - describe('extractParamsFromBidRequest', () => { - it('should handle empty bid request', () => { - const bidRequest = null; - expect(extractParamsFromBidRequest(bidRequest)).to.be.null; - }); + it('should handle invalid bidderRequest.gdprConsent.gdprApplies', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: 'foobar' + } + }; - it('should handle missing params in bid request', () => { - const bidRequest = {}; - expect(extractParamsFromBidRequest(bidRequest)).to.be.null; + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: true + }); + }); }); - it('should handle params in bid request', () => { - const bidRequest = { - params: { - foo: '123', - bar: 456 - } - }; - expect(extractParamsFromBidRequest(bidRequest)).to.deep.equal(bidRequest.params); - }); - }); + describe('extractPageUrl', () => { + let origPageUrl; - describe('extractSizesFromBidRequest', () => { - it('should handle empty bid request', () => { - const bidRequest = null; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal([]); - }); + beforeEach(() => { + // remember original pageUrl in config + origPageUrl = config.getConfig('pageUrl'); - it('should handle missing sizes in bid request', () => { - const bidRequest = {}; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal([]); - }); + // unset pageUrl in config + config.setConfig({ pageUrl: null }); + }); + + afterEach(() => { + // set original pageUrl to config + config.setConfig({ pageUrl: origPageUrl }); + }); + + it('should handle empty or missing data', () => { + expect(internal.extractPageUrl(null)).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({})).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: {} })).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: { canonicalUrl: null } })).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: { canonicalUrl: '' } })).to.equal(utils.getWindowTop().location.href); + }); + + it('should use "pageUrl" from config', () => { + config.setConfig({ pageUrl: 'http://page.url' }); - it('should handle sizes in bid request', () => { - const bidRequest = { - mediaTypes: { - banner: { - sizes: [[12, 34], [56, 78]] + expect(internal.extractPageUrl({})).to.equal(config.getConfig('pageUrl')); + }); + + it('should use bidderRequest.refererInfo.canonicalUrl', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://canonical.url' } - } - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner.sizes); - }); + }; - it('should handle sizes in bid request (backward compatibility)', () => { - const bidRequest = { - sizes: [[12, 34], [56, 78]] - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.sizes); - }); + expect(internal.extractPageUrl(bidderRequest)).to.equal(bidderRequest.refererInfo.canonicalUrl); + }); - it('should prefer sizes in mediaTypes.banner', () => { - const bidRequest = { - sizes: [[12, 34]], - mediaTypes: { - banner: { - sizes: [[56, 78]] + it('should prefer bidderRequest.refererInfo.canonicalUrl over "pageUrl" from config', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://canonical.url' } - } - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner.sizes); - }); - }); + }; - describe('extractTopWindowReferrerFromBidRequest', () => { - it('should use fallback if bid request is empty', () => { - const bidRequest = null; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + config.setConfig({ pageUrl: 'http://page.url' }); - it('should use fallback if refererInfo in bid request is missing', () => { - const bidRequest = {}; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); + expect(internal.extractPageUrl(bidderRequest)).to.equal(bidderRequest.refererInfo.canonicalUrl); + }); }); - it('should use fallback if refererInfo.referer in bid request is missing', () => { - const bidRequest = { - refererInfo: {} - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + describe('extractReferrer', () => { + it('should handle empty or missing data', () => { + expect(internal.extractReferrer(null)).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({})).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: {} })).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: { referer: null } })).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: { referer: '' } })).to.equal(utils.getWindowTop().document.referrer); + }); - it('should use fallback if refererInfo.referer in bid request is empty', () => { - const bidRequest = { - refererInfo: { - referer: '' - } - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + it('hould use bidderRequest.refererInfo.referer', () => { + const bidderRequest = { + refererInfo: { + referer: 'foobar' + } + }; - it('should use refererInfo.referer from bid request ', () => { - const bidRequest = { - refererInfo: { - referer: 'foobar' - } - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(bidRequest.refererInfo.referer); + expect(internal.extractReferrer(bidderRequest)).to.equal(bidderRequest.refererInfo.referer); + }); }); - }); - describe('extractTopWindowUrlFromBidRequest', () => { - it('should use fallback if bid request is empty', () => { - const bidRequest = null; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractParams', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractParams(null)).to.be.null; + expect(internal.extractParams({})).to.be.null; + }); + + it('should extract bidRequest.params', () => { + const bidRequest = { + params: { + foo: '123', + bar: 456 + } + }; + expect(internal.extractParams(bidRequest)).to.deep.equal(bidRequest.params); + }); }); - it('should use fallback if refererInfo in bid request is missing', () => { - const bidRequest = {}; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractBannerConfig', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractBannerConfig(null)).to.be.null; + expect(internal.extractBannerConfig({})).to.be.null; + }); + + it('should extract bidRequest.mediaTypes.banner', () => { + const bidRequest = { + mediaTypes: { + banner: { + sizes: [[12, 34], [56, 78]] + } + } + }; + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner); + }); + + it('should extract bidRequest.sizes (backward compatibility)', () => { + const bidRequest = { + sizes: [[12, 34], [56, 78]] + }; + + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal({sizes: bidRequest.sizes}); + }); }); - it('should use fallback if refererInfo.canonicalUrl in bid request is missing', () => { - const bidRequest = { - refererInfo: {} - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractNativeConfig', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractNativeConfig(null)).to.be.null; + expect(internal.extractNativeConfig({})).to.be.null; + }); + + it('should extract bidRequest.mediaTypes.native', () => { + const bidRequest = { + mediaTypes: { + native: { + image: { + required: true + }, + title: { + required: true + } + } + } + }; + + expect(internal.extractNativeConfig(bidRequest)).to.deep.equal(bidRequest.mediaTypes.native); + }); }); - it('should use fallback if refererInfo.canonicalUrl in bid request is empty', () => { - const bidRequest = { - refererInfo: { - canonicalUrl: '' - } - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('groupBidRequestsByPublisher', () => { + it('should handle empty bidRequests', () => { + expect(internal.groupBidRequestsByPublisher(null)).to.deep.equal({}); + expect(internal.groupBidRequestsByPublisher([])).to.deep.equal({}) + }); + + it('should group given bidRequests by params.publisher', () => { + const bidRequests = [ + { + mediaTypes: { + banner: { + sizes: [[100, 100]] + } + }, + params: { + publisher: 'publisher1', + placement: '1111' + } + }, + { + mediaTypes: { + banner: { + sizes: [[200, 200]] + } + }, + params: { + publisher: 'publisher2', + placement: '2222' + } + }, + { + mediaTypes: { + banner: { + sizes: [[300, 300]] + } + }, + params: { + publisher: 'publisher3', + placement: '3333' + } + }, + { + mediaTypes: { + banner: { + sizes: [[400, 400]] + } + }, + params: { + publisher: 'publisher1', + placement: '4444' + } + } + ]; + + expect(internal.groupBidRequestsByPublisher(bidRequests)).to.deep.equal({ + publisher1: [ + bidRequests[0], + bidRequests[3] + ], + publisher2: [ + bidRequests[1], + ], + publisher3: [ + bidRequests[2], + ], + }); + }); }); - it('should use refererInfo.canonicalUrl from bid request ', () => { - const bidRequest = { - refererInfo: { - canonicalUrl: 'foobar' - } - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(bidRequest.refererInfo.canonicalUrl); + describe('buildEndpointUrl', () => { + it('should build endpoint url based on given publisher code', () => { + expect(internal.buildEndpointUrl(1234)).to.be.equal('https://rtb.d.adup-tech.com/prebid/1234_bid'); + expect(internal.buildEndpointUrl('foobar')).to.be.equal('https://rtb.d.adup-tech.com/prebid/foobar_bid'); + expect(internal.buildEndpointUrl('foo bar')).to.be.equal('https://rtb.d.adup-tech.com/prebid/foo%20bar_bid'); + }); }); }); @@ -185,42 +285,31 @@ describe('AduptechBidAdapter', () => { adapter = newBidder(spec); }); - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); + describe('code', () => { + it('should be correct', () => { + expect(adapter.getSpec().code).to.equal(BIDDER_CODE); }); }); - describe('isBidRequestValid', () => { - it('should return true when necessary information is given', () => { - expect(spec.isBidRequestValid({ - mediaTypes: { - banner: { - sizes: [[100, 200]] - } - }, - params: { - publisher: 'test', - placement: '1234' - } - })).to.be.true; + describe('supportedMediaTypes', () => { + it('should be correct', () => { + expect(adapter.getSpec().supportedMediaTypes).to.deep.equal([BANNER, NATIVE]); }); + }); - it('should return true when necessary information is given (backward compatibility)', () => { - expect(spec.isBidRequestValid({ - sizes: [[100, 200]], - params: { - publisher: 'test', - placement: '1234' - } - })).to.be.true; + describe('inherited functions', () => { + it('should exist and be a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); }); + }); - it('should return false on empty bid', () => { + describe('isBidRequestValid', () => { + it('should be false on empty bid', () => { + expect(spec.isBidRequestValid(null)).to.be.false; expect(spec.isBidRequestValid({})).to.be.false; }); - it('should return false on missing sizes', () => { + it('should be false if mediaTypes.banner and mediaTypes.native is missing', () => { expect(spec.isBidRequestValid({ params: { publisher: 'test', @@ -229,63 +318,65 @@ describe('AduptechBidAdapter', () => { })).to.be.false; }); - it('should return false on empty sizes', () => { + it('should be false if params missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { - sizes: [] + sizes: [[100, 200]] } }, - params: { - publisher: 'test', - placement: '1234' - } })).to.be.false; }); - it('should return false on empty sizes (backward compatibility)', () => { + it('should be false if params is invalid', () => { expect(spec.isBidRequestValid({ - sizes: [], - params: { - publisher: 'test', - placement: '1234' - } + mediaTypes: { + banner: { + sizes: [[100, 200]] + } + }, + params: 'bar' })).to.be.false; }); - it('should return false on missing params', () => { + it('should be false if params is empty', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, + params: {} })).to.be.false; }); - it('should return false on invalid params', () => { + it('should be false if params.publisher is missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, - params: 'bar' + params: { + placement: '1234' + } })).to.be.false; }); - it('should return false on empty params', () => { + it('should be false if params.placement is missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, - params: {} + params: { + publisher: 'test' + } })).to.be.false; }); - it('should return false on missing publisher', () => { + it('should be true if mediaTypes.banner is given', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { @@ -293,34 +384,62 @@ describe('AduptechBidAdapter', () => { } }, params: { + publisher: 'test', placement: '1234' } - })).to.be.false; + })).to.be.true; }); - it('should return false on missing placement', () => { + it('should be true if mediaTypes.native is given', () => { expect(spec.isBidRequestValid({ mediaTypes: { - banner: { - sizes: [[100, 200]] + native: { + image: { + required: true + }, + title: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } } }, params: { - publisher: 'test' + publisher: 'test', + placement: '1234' } - })).to.be.false; + })).to.be.true; }); }); describe('buildRequests', () => { - it('should send one bid request per ad unit to the endpoint via POST', () => { - const bidRequests = [ + it('should handle empty validBidRequests', () => { + expect(spec.buildRequests(null)).to.deep.equal([]); + expect(spec.buildRequests([])).to.deep.equal([]); + }); + + it('should build one request per publisher', () => { + const bidderRequest = { + auctionId: 'auctionId123', + refererInfo: { + canonicalUrl: 'http://crazy.canonical.url', + referer: 'http://crazy.referer.url' + }, + gdprConsent: { + consentString: 'consentString123', + gdprApplies: true + } + }; + + const validBidRequests = [ { - bidder: BIDDER_CODE, bidId: 'bidId1', adUnitCode: 'adUnitCode1', transactionId: 'transactionId1', - auctionId: 'auctionId1', mediaTypes: { banner: { sizes: [[100, 200], [300, 400]] @@ -332,170 +451,197 @@ describe('AduptechBidAdapter', () => { } }, { - bidder: BIDDER_CODE, bidId: 'bidId2', adUnitCode: 'adUnitCode2', transactionId: 'transactionId2', - auctionId: 'auctionId2', mediaTypes: { banner: { - sizes: [[500, 600]] + sizes: [[100, 200]] } }, params: { - publisher: 'publisher2', + publisher: 'publisher1', placement: 'placement2' } - } - ]; - - const result = spec.buildRequests(bidRequests); - expect(result.length).to.equal(2); - - expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[0].params.publisher)); - expect(result[0].method).to.equal(ENDPOINT_METHOD); - expect(result[0].data).to.deep.equal({ - bidId: bidRequests[0].bidId, - auctionId: bidRequests[0].auctionId, - transactionId: bidRequests[0].transactionId, - adUnitCode: bidRequests[0].adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequests[0]), - referrer: extractTopWindowReferrerFromBidRequest(bidRequests[0]), - sizes: extractSizesFromBidRequest(bidRequests[0]), - params: extractParamsFromBidRequest(bidRequests[0]), - gdpr: null - }); - - expect(result[1].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[1].params.publisher)); - expect(result[1].method).to.equal(ENDPOINT_METHOD); - expect(result[1].data).to.deep.equal({ - bidId: bidRequests[1].bidId, - auctionId: bidRequests[1].auctionId, - transactionId: bidRequests[1].transactionId, - adUnitCode: bidRequests[1].adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequests[1]), - referrer: extractTopWindowReferrerFromBidRequest(bidRequests[1]), - sizes: extractSizesFromBidRequest(bidRequests[1]), - params: extractParamsFromBidRequest(bidRequests[1]), - gdpr: null - }); - }); - - it('should pass gdpr informations', () => { - const bidderRequest = { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true - } - }; - - const bidRequests = [ + }, { - bidder: BIDDER_CODE, bidId: 'bidId3', adUnitCode: 'adUnitCode3', transactionId: 'transactionId3', - auctionId: 'auctionId3', mediaTypes: { - banner: { - sizes: [[100, 200], [300, 400]] + native: { + image: { + required: true + }, + title: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } } }, params: { - publisher: 'publisher3', + publisher: 'publisher2', placement: 'placement3' } } ]; - const result = spec.buildRequests(bidRequests, bidderRequest); - expect(result.length).to.equal(1); - expect(result[0].data.gdpr).to.deep.equal(extractGdprFromBidderRequest(bidderRequest)); - }); - - it('should encode publisher param in endpoint url', () => { - const bidRequests = [ + expect(spec.buildRequests(validBidRequests, bidderRequest)).to.deep.equal([ { - bidder: BIDDER_CODE, - bidId: 'bidId1', - adUnitCode: 'adUnitCode1', - transactionId: 'transactionId1', - auctionId: 'auctionId1', - mediaTypes: { - banner: { - sizes: [[100, 200]] - } - }, - params: { - publisher: 'crazy publisher key äÖÜ', - placement: 'placement1' + url: internal.buildEndpointUrl(validBidRequests[0].params.publisher), + method: ENDPOINT_METHOD, + data: { + auctionId: bidderRequest.auctionId, + pageUrl: bidderRequest.refererInfo.canonicalUrl, + referrer: bidderRequest.refererInfo.referer, + gdpr: { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }, + imp: [ + { + bidId: validBidRequests[0].bidId, + transactionId: validBidRequests[0].transactionId, + adUnitCode: validBidRequests[0].adUnitCode, + params: validBidRequests[0].params, + banner: validBidRequests[0].mediaTypes.banner + }, + { + bidId: validBidRequests[1].bidId, + transactionId: validBidRequests[1].transactionId, + adUnitCode: validBidRequests[1].adUnitCode, + params: validBidRequests[1].params, + banner: validBidRequests[1].mediaTypes.banner + } + ] } }, - ]; - - const result = spec.buildRequests(bidRequests); - expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequests[0].params.publisher))); - }); - - it('should handle empty bidRequests', () => { - expect(spec.buildRequests([])).to.deep.equal([]); - }); - }); - - describe('interpretResponse', () => { - it('should correctly interpret the server response', () => { - const serverResponse = { - body: { - bid: { - bidId: 'bidId1', - price: 0.12, - net: true, - currency: 'EUR', - ttl: 123 - }, - creative: { - id: 'creativeId1', - width: 100, - height: 200, - html: '
Hello World
' - } - } - }; - - const result = spec.interpretResponse(serverResponse); - expect(result).to.deep.equal([ { - requestId: serverResponse.body.bid.bidId, - cpm: serverResponse.body.bid.price, - netRevenue: serverResponse.body.bid.net, - currency: serverResponse.body.bid.currency, - ttl: serverResponse.body.bid.ttl, - creativeId: serverResponse.body.creative.id, - width: serverResponse.body.creative.width, - height: serverResponse.body.creative.height, - ad: serverResponse.body.creative.html + url: internal.buildEndpointUrl(validBidRequests[2].params.publisher), + method: ENDPOINT_METHOD, + data: { + auctionId: bidderRequest.auctionId, + pageUrl: bidderRequest.refererInfo.canonicalUrl, + referrer: bidderRequest.refererInfo.referer, + gdpr: { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }, + imp: [ + { + bidId: validBidRequests[2].bidId, + transactionId: validBidRequests[2].transactionId, + adUnitCode: validBidRequests[2].adUnitCode, + params: validBidRequests[2].params, + native: validBidRequests[2].mediaTypes.native + } + ] + } } ]); }); + }); + describe('interpretResponse', () => { it('should handle empty serverResponse', () => { + expect(spec.interpretResponse(null)).to.deep.equal([]); expect(spec.interpretResponse({})).to.deep.equal([]); + expect(spec.interpretResponse({ body: {} })).to.deep.equal([]); + expect(spec.interpretResponse({ body: { bids: [] } })).to.deep.equal([]); }); - it('should handle missing bid', () => { - expect(spec.interpretResponse({ + it('should correctly interpret the server response', () => { + const serverResponse = { body: { - creative: {} + bids: [ + { + bid: { + bidId: 'bidId1', + price: 0.12, + net: true, + currency: 'EUR', + ttl: 123 + }, + creative: { + id: 'creativeId1', + advertiserDomains: ['advertiser1.com', 'advertiser2.org'], + width: 100, + height: 200, + html: '
Hello World
' + } + }, + { + bid: { + bidId: 'bidId2', + price: 0.99, + net: false, + currency: 'USD', + ttl: 465 + }, + creative: { + id: 'creativeId2', + advertiserDomains: ['advertiser3.com'], + native: { + title: 'Ad title', + body: 'Ad description', + displayUrl: 'Ad display url', + clickUrl: 'http://click.url/ad.html', + image: { + url: 'https://image.url/ad.png', + width: 123, + height: 456 + }, + sponsoredBy: 'Ad sponsored by', + impressionTrackers: [ + 'https://impression.tracking.url/1', + 'https://impression.tracking.url/2', + ], + privacyLink: 'https://example.com/privacy', + privacyIcon: 'https://example.com/icon.png' + } + } + }, + null, // should be skipped + {} // should be skipped + ] } - })).to.deep.equal([]); - }); + }; - it('should handle missing creative', () => { - expect(spec.interpretResponse({ - body: { - bid: {} + expect(spec.interpretResponse(serverResponse)).to.deep.equal([ + { + requestId: serverResponse.body.bids[0].bid.bidId, + cpm: serverResponse.body.bids[0].bid.price, + netRevenue: serverResponse.body.bids[0].bid.net, + currency: serverResponse.body.bids[0].bid.currency, + ttl: serverResponse.body.bids[0].bid.ttl, + creativeId: serverResponse.body.bids[0].creative.id, + meta: { + advertiserDomains: serverResponse.body.bids[0].creative.advertiserDomains + }, + mediaType: BANNER, + width: serverResponse.body.bids[0].creative.width, + height: serverResponse.body.bids[0].creative.height, + ad: serverResponse.body.bids[0].creative.html + }, + { + requestId: serverResponse.body.bids[1].bid.bidId, + cpm: serverResponse.body.bids[1].bid.price, + netRevenue: serverResponse.body.bids[1].bid.net, + currency: serverResponse.body.bids[1].bid.currency, + ttl: serverResponse.body.bids[1].bid.ttl, + creativeId: serverResponse.body.bids[1].creative.id, + meta: { + advertiserDomains: serverResponse.body.bids[1].creative.advertiserDomains + }, + mediaType: NATIVE, + native: serverResponse.body.bids[1].creative.native } - })).to.deep.equal([]); + ]); }); }); }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index c432ad1b32d..e6e95ea5423 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -290,6 +290,19 @@ describe('Adyoulike Adapter', function () { 'Placement': 'placement_0' } ]; + + const testMetaObject = { + 'networkId': 123, + 'advertiserId': '3', + 'advertiserName': 'foobar', + 'advertiserDomains': ['foobar.com'], + 'brandId': '345', + 'brandName': 'Foo', + 'primaryCatId': '34', + 'secondaryCatIds': ['IAB-222', 'IAB-333'], + 'mediaType': 'banner' + }; + const admSample = "\u003cscript id=\"ayl-prebid-a11a121205932e75e622af275681965d\"\u003e\n(function(){\n\twindow.isPrebid = true\n\tvar prebidResults = /*PREBID*/{\"OnEvents\":{\"CLICK\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelCLICK.com/fake\"}],\"IMPRESSION\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelIMP.com/fake\"},{\"Kind\":\"JAVASCRIPT_URL\",\"Url\":\"https://testJsIMP.com/fake.js\"}]},\"Disabled\":false,\"Attempt\":\"a11a121205932e75e622af275681965d\",\"ApiPrefix\":\"https://fo-api.omnitagjs.com/fo-api\",\"TrackingPrefix\":\"https://tracking.omnitagjs.com/tracking\",\"DynamicPrefix\":\"https://tag-dyn.omnitagjs.com/fo-dyn\",\"StaticPrefix\":\"https://fo-static.omnitagjs.com/fo-static\",\"BlobPrefix\":\"https://fo-api.omnitagjs.com/fo-api/blobs\",\"SspPrefix\":\"https://fo-ssp.omnitagjs.com/fo-ssp\",\"VisitorPrefix\":\"https://visitor.omnitagjs.com/visitor\",\"Trusted\":true,\"Placement\":\"e622af275681965d3095808561a1e510\",\"PlacementAccess\":\"ALL\",\"Site\":\"6e2df7a92203c3c7a25561ed63f25a27\",\"Lang\":\"EN\",\"SiteLogo\":null,\"HasSponsorImage\":false,\"ResizeIframe\":true,\"IntegrationConfig\":{\"Kind\":\"WIDGET\",\"Widget\":{\"ExtraStyleSheet\":\"\",\"Placeholders\":{\"Body\":{\"Color\":{\"R\":77,\"G\":21,\"B\":82,\"A\":100},\"BackgroundColor\":{\"R\":255,\"G\":255,\"B\":255,\"A\":100},\"FontFamily\":\"Lato\",\"Width\":\"100%\",\"Align\":\"\",\"BoxShadow\":true},\"CallToAction\":{\"Color\":{\"R\":26,\"G\":157,\"B\":212,\"A\":100}},\"Description\":{\"Length\":130},\"Image\":{\"Width\":600,\"Height\":600,\"Lowres\":false,\"Raw\":false},\"Size\":{\"Height\":\"250px\",\"Width\":\"300px\"},\"Sponsor\":{\"Color\":{\"R\":35,\"G\":35,\"B\":35,\"A\":100},\"Label\":true,\"WithoutLogo\":false},\"Title\":{\"Color\":{\"R\":219,\"G\":181,\"B\":255,\"A\":100}}},\"Selector\":{\"Kind\":\"CSS\",\"Css\":\"#ayl-prebid-a11a121205932e75e622af275681965d\"},\"Insertion\":\"AFTER\",\"ClickFormat\":true,\"Creative20\":true,\"WidgetKind\":\"CREATIVE_TEMPLATE_4\"}},\"Legal\":\"Sponsored\",\"ForcedCampaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"ForcedTrack\":\"\",\"ForcedCreative\":\"\",\"ForcedSource\":\"\",\"DisplayMode\":\"DEFAULT\",\"Campaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"CampaignAccess\":\"ALL\",\"CampaignKind\":\"AD_TRAFFIC\",\"DataSource\":\"LOCAL\",\"DataSourceUrl\":\"\",\"DataSourceOnEventsIsolated\":false,\"DataSourceWithoutCookie\":false,\"Content\":{\"Preview\":{\"Thumbnail\":{\"Image\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://tag-dyn.omnitagjs.com/fo-dyn/native/preview/image?key=fd4362d35bb174d6f1c80d4bb5643c22\\u0026kind=INTERNAL\\u0026ztop=0.000000\\u0026zleft=0.000000\\u0026zwidth=0.333333\\u0026zheight=1.000000\\u0026width=[width]\\u0026height=[height]\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Text\":{\"CALLTOACTION\":\"Click here to learn more\",\"DESCRIPTION\":\"Considérant l'extrémité conjoncturelle, il serait bon d'anticiper toutes les voies de bon sens.\",\"SPONSOR\":\"Tested by\",\"TITLE\":\"Adserver Traffic Redirect Internal\"},\"Sponsor\":{\"Name\":\"QA Team\"},\"Credit\":{\"Logo\":{\"Resource\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Url\":\"https://blobs.omnitagjs.com/adchoice/\"}},\"Landing\":{\"Url\":\"https://www.w3.org/People/mimasa/test/xhtml/entities/entities-11.xhtml#lat1\",\"LegacyTracking\":false},\"ViewButtons\":{\"Close\":{\"Skip\":6000}},\"InternalContentFields\":{\"AnimatedImage\":false}},\"AdDomain\":\"adyoulike.com\",\"Opener\":\"REDIRECT\",\"PerformUITriggers\":[\"CLICK\"],\"RedirectionTarget\":\"TAB\"}/*PREBID*/;\n\tvar insertAds = function insertAds() {\insertAds();\n\t}\n})();\n\u003c/script\u003e"; const responseWithSinglePlacement = [ { @@ -298,8 +311,78 @@ describe('Adyoulike Adapter', function () { 'Ad': admSample, 'Price': 0.5, 'Height': 600, + 'Meta': testMetaObject } ]; + + const responseWithSingleNative = [{ + 'BidID': 'bid_id_0', + 'Placement': 'placement_0', + 'Native': { + 'body': 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + 'cta': 'Click here to learn more', + 'clickUrl': 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + 'image': { + 'height': 600, + 'url': 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + 'width': 300 + }, + 'privacyIcon': 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + 'privacyLink': 'https://blobs.omnitagjs.com/adchoice/', + 'sponsoredBy': 'QA Team', + 'title': 'Adserver Traffic Redirect Internal', + 'impressionTrackers': [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + ], + 'javascriptTrackers': [ + 'https://testJsIMP.com/fake.js' + ], + 'clickTrackers': [ + 'https://testPixelCLICK.com/fake' + ] + }, + 'Price': 0.5, + 'Height': 600, + }]; + + const nativeResult = [{ + cpm: 0.5, + creativeId: undefined, + currency: 'USD', + netRevenue: true, + requestId: 'bid_id_0', + ttl: 3600, + mediaType: 'native', + native: { + body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + clickTrackers: [ + 'https://testPixelCLICK.com/fake' + ], + clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + cta: 'Click here to learn more', + image: { + height: 600, + url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + width: 300, + }, + impressionTrackers: [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' + ], + javascriptTrackers: [ + 'https://testJsIMP.com/fake.js' + ], + privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + privacyLink: 'https://blobs.omnitagjs.com/adchoice/', + sponsoredBy: 'QA Team', + title: 'Adserver Traffic Redirect Internal', + }, + meta: testMetaObject + }]; + const responseWithMultiplePlacements = [ { 'BidID': 'bid_id_0', @@ -549,6 +632,7 @@ describe('Adyoulike Adapter', function () { expect(result[0].ad).to.equal(admSample); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(600); + expect(result[0].meta).to.deep.equal(testMetaObject); }); it('receive reponse with multiple placement', function () { @@ -568,47 +652,28 @@ describe('Adyoulike Adapter', function () { expect(result[1].height).to.equal(250); }); - it('receive reponse with Native ad', function () { + it('receive reponse with Native from ad markup', function () { serverResponse.body = responseWithSinglePlacement; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); - expect(result).to.deep.equal([{ - cpm: 0.5, - creativeId: undefined, - currency: 'USD', - netRevenue: true, - requestId: 'bid_id_0', - ttl: 3600, - mediaType: 'native', - native: { - body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', - clickTrackers: [ - 'https://testPixelCLICK.com/fake' - ], - clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', - cta: 'Click here to learn more', - image: { - height: 600, - url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', - width: 300, - }, - impressionTrackers: [ - 'https://testPixelIMP.com/fake', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' - ], - javascriptTrackers: [ - 'https://testJsIMP.com/fake.js' - ], - privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', - privacyLink: 'https://blobs.omnitagjs.com/adchoice/', - sponsoredBy: 'QA Team', - title: 'Adserver Traffic Redirect Internal', - } + expect(result).to.deep.equal(nativeResult); + }); + + it('receive reponse with Native ad', function () { + serverResponse.body = responseWithSingleNative; + let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); + + expect(result.length).to.equal(1); + + const noMeta = [...nativeResult]; + const metaBackup = noMeta[0].meta; + + // this test should return default meta object + noMeta[0].meta = { advertiserDomains: [] }; - }]); + expect(result).to.deep.equal(noMeta); }); }); }); diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js index 2e1fdb56201..b6d63fc2a8e 100644 --- a/test/spec/modules/aniviewBidAdapter_spec.js +++ b/test/spec/modules/aniviewBidAdapter_spec.js @@ -160,6 +160,7 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(bidResponse.currency).to.equal('USD'); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.mediaType).to.equal('video'); + expect(bidResponse.meta.advertiserDomains).to.be.an('array').that.is.empty; }); it('safely handles XML parsing failure from invalid bid response', function () { @@ -207,12 +208,16 @@ describe('ANIVIEW Bid Adapter Test', function () { }); describe('getUserSyncs', function () { - it('Check get sync pixels from response', function () { - let pixelUrl = 'https://sync.pixel.url/sync'; + let pixelUrl = 'https://sync.pixel.url/sync'; + function createBidResponse (pixelEvent, pixelType) { + let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; + return 'FORDFORD00:00:15'; + } + + it('Check get iframe sync pixels from response on inventory', function () { let pixelEvent = 'inventory'; let pixelType = '3'; - let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; - let bidResponse = 'FORDFORD00:00:15'; + let bidResponse = createBidResponse(pixelEvent, pixelType); let serverResponse = [ {body: bidResponse} ]; @@ -222,5 +227,19 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(pixel.url).to.equal(pixelUrl); expect(pixel.type).to.equal('iframe'); }); + + it('Check get image sync pixels from response on sync', function () { + let pixelEvent = 'sync'; + let pixelType = '1'; + let bidResponse = createBidResponse(pixelEvent, pixelType); + let serverResponse = [ + {body: bidResponse} + ]; + let syncPixels = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse); + expect(syncPixels.length).to.equal(1); + let pixel = syncPixels[0]; + expect(pixel.url).to.equal(pixelUrl); + expect(pixel.type).to.equal('image'); + }); }); }); diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index 3a71833bc3e..5f6a935c453 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec, validateGeoObject, getDomain } from '../../../modules/apacdexBidA import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; describe('ApacdexBidAdapter', function () { const adapter = newBidder(spec) @@ -200,7 +201,7 @@ describe('ApacdexBidAdapter', function () { 'bidder': 'apacdex', 'params': { 'siteId': '1a2b3c4d5e6f1a2b3c4d', - 'geo': {'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60} + 'geo': { 'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60 } }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -336,12 +337,50 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); }); - describe('debug test', function() { - beforeEach(function() { - config.setConfig({debug: true}); + it('should attach bidFloor param when either bid param floorPrice or getFloor function exists', function () { + let getFloorResponse = { currency: 'USD', floor: 3 }; + let singleBidRequest, request, payload = null; + + // 1 -> floorPrice not defined, getFloor not defined > empty + singleBidRequest = deepClone(bidRequest[0]); + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data; + expect(payload.bids[0].bidFloor).to.not.exist; + + // 2 -> floorPrice is defined, getFloor not defined > floorPrice is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.params = { + 'siteId': '1890909', + 'floorPrice': 0.5 + }; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(0.5); + + // 3 -> floorPrice is defined, getFloor is defined > getFloor is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.params = { + 'siteId': '1890909', + 'floorPrice': 0.5 + }; + singleBidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(3); + + // 4 -> floorPrice not defined, getFloor is defined > getFloor is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(3); + }); + describe('debug test', function () { + beforeEach(function () { + config.setConfig({ debug: true }); }); - afterEach(function() { - config.setConfig({debug: false}); + afterEach(function () { + config.setConfig({ debug: false }); }); it('should return a properly formatted request with pbjs_debug is true', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); @@ -514,7 +553,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }, { 'requestId': '30024615be22ef66a', @@ -527,7 +569,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }, { 'requestId': '1854b40107d6745c', @@ -540,7 +585,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'video' + 'mediaType': 'video', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } } ], 'pixel': [{ @@ -610,6 +658,7 @@ describe('ApacdexBidAdapter', function () { if (resp.mediaType === 'banner') { expect(resp.ad.indexOf('Apacdex AD')).to.be.greaterThan(0); } + expect(resp.meta.advertiserDomains).to.deep.equal(['https://example.com']); }); }); }); @@ -693,17 +742,17 @@ describe('ApacdexBidAdapter', function () { describe('getDomain', function () { it('should return valid domain from publisherDomain config', () => { let pageUrl = 'https://www.example.com/page/prebid/exam.html'; - config.setConfig({publisherDomain: pageUrl}); + config.setConfig({ publisherDomain: pageUrl }); expect(getDomain(pageUrl)).to.equal('example.com'); }); it('should return valid domain from pageUrl argument', () => { let pageUrl = 'https://www.example.com/page/prebid/exam.html'; - config.setConfig({publisherDomain: ''}); + config.setConfig({ publisherDomain: '' }); expect(getDomain(pageUrl)).to.equal('example.com'); }); it('should return undefined if pageUrl and publisherDomain not config', () => { let pageUrl; - config.setConfig({publisherDomain: ''}); + config.setConfig({ publisherDomain: '' }); expect(getDomain(pageUrl)).to.equal(pageUrl); }); }); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index c875cba12bc..4baecdd1ba8 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -873,9 +873,14 @@ describe('AppNexusAdapter', function () { const bidRequest = Object.assign({}, bidRequests[0], { userId: { tdid: 'sample-userid', + uid2: { id: 'sample-uid2-value' }, criteoId: 'sample-criteo-userid', netId: 'sample-netId-userid', - idl_env: 'sample-idl-userid' + idl_env: 'sample-idl-userid', + flocId: { + id: 'sample-flocid-value', + version: 'chrome.1.0' + } } }); @@ -892,6 +897,11 @@ describe('AppNexusAdapter', function () { id: 'sample-criteo-userid', }); + expect(payload.eids).to.deep.include({ + source: 'chrome.com', + id: 'sample-flocid-value' + }); + expect(payload.eids).to.deep.include({ source: 'netid.de', id: 'sample-netId-userid', @@ -900,7 +910,13 @@ describe('AppNexusAdapter', function () { expect(payload.eids).to.deep.include({ source: 'liveramp.com', id: 'sample-idl-userid' - }) + }); + + expect(payload.eids).to.deep.include({ + source: 'uidapi.com', + id: 'sample-uid2-value', + rti_partner: 'UID2' + }); }); it('should populate iab_support object at the root level if omid support is detected', function () { diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index fc74ec8a2aa..555f2c21262 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -673,6 +673,7 @@ describe('BeachfrontAdapter', function () { width: width, height: height, renderer: null, + meta: { mediaType: 'video', advertiserDomains: [] }, mediaType: 'video', currency: 'USD', netRevenue: true, @@ -680,26 +681,6 @@ describe('BeachfrontAdapter', function () { }); }); - it('should default to the legacy "cmpId" value for the creative ID', () => { - const width = 640; - const height = 480; - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { - video: { - playerSize: [ width, height ] - } - }; - const serverResponse = { - bidPrice: 5.00, - url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' - }; - const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - expect(bidResponse).to.deep.contain({ - creativeId: serverResponse.cmpId - }); - }); - it('should return only vast url if the response type is "nurl"', () => { const width = 640; const height = 480; @@ -761,6 +742,31 @@ describe('BeachfrontAdapter', function () { }); expect(bidResponse.renderer.render).to.be.a('function'); }); + + it('should return meta data for the bid response', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + meta: { + advertiserDomains: ['example.com'], + advertiserId: '123' + } + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.meta).to.deep.equal({ + mediaType: 'video', + advertiserDomains: ['example.com'], + advertiserId: '123' + }); + }); }); describe('for banner bids', function () { @@ -815,6 +821,7 @@ describe('BeachfrontAdapter', function () { cpm: serverResponse[ i ].price, width: serverResponse[ i ].w, height: serverResponse[ i ].h, + meta: { mediaType: 'banner', advertiserDomains: [] }, mediaType: 'banner', currency: 'USD', netRevenue: true, @@ -822,6 +829,32 @@ describe('BeachfrontAdapter', function () { }); } }); + + it('should return meta data for the bid response', () => { + bidRequests[0].mediaTypes = { + banner: { + sizes: [[ 300, 250 ], [ 728, 90 ]] + } + }; + const serverResponse = [{ + slot: bidRequests[0].adUnitCode, + adm: '
', + crid: 'crid_1', + price: 3.02, + w: 728, + h: 90, + meta: { + advertiserDomains: ['example.com'], + advertiserId: '123' + } + }]; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest: bidRequests }); + expect(bidResponse[0].meta).to.deep.equal({ + mediaType: 'banner', + advertiserDomains: ['example.com'], + advertiserId: '123' + }); + }); }); }); diff --git a/test/spec/modules/bluebillywigBidAdapter_spec.js b/test/spec/modules/bluebillywigBidAdapter_spec.js index 3aee6c69438..c831ddf6ddf 100644 --- a/test/spec/modules/bluebillywigBidAdapter_spec.js +++ b/test/spec/modules/bluebillywigBidAdapter_spec.js @@ -760,6 +760,10 @@ describe('BlueBillywigAdapter', () => { expect(bid.currency).to.equal(serverResponse.body.cur); expect(bid.ttl).to.equal(BB_CONSTANTS.DEFAULT_TTL); + expect(bid).to.have.property('meta'); + expect(bid.meta).to.have.property('advertiserDomains'); + expect(bid.meta.advertiserDomains[0]).to.equal('bluebillywig.com'); + expect(bid.publicationName).to.equal(validBidderRequest.bids[0].params.publicationName); expect(bid.rendererCode).to.equal(validBidderRequest.bids[0].params.rendererCode); expect(bid.accountId).to.equal(validBidderRequest.bids[0].params.accountId); diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 0860404355e..8da82c71820 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -2,6 +2,14 @@ import { expect } from 'chai'; import { spec } from 'modules/bridgewellBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +const userId = { + 'criteoId': 'vYlICF9oREZlTHBGRVdrJTJCUUJnc3U2ckNVaXhrV1JWVUZVSUxzZmJlcnJZR0ZxbVhFRnU5bDAlMkJaUWwxWTlNcmdEeHFrJTJGajBWVlV4T3lFQ0FyRVcxNyUyQlIxa0lLSlFhcWJpTm9PSkdPVkx0JTJCbzlQRTQlM0Q', + 'pubcid': '074864cb-3705-430e-9ff7-48ccf3c21b94', + 'sharedid': {'id': '01F61MX53D786DSB2WYD38ZVM7', 'third': '01F61MX53D786DSB2WYD38ZVM7'}, + 'uid2': {'id': 'eb33b0cb-8d35-1234-b9c0-1a31d4064777'}, + 'flocId': {'id': '12345', 'version': 'chrome.1.1'}, +} + describe('bridgewellBidAdapter', function () { const adapter = newBidder(spec); @@ -87,6 +95,7 @@ describe('bridgewellBidAdapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'userId': userId, }, { 'bidder': 'bridgewell', @@ -126,6 +135,7 @@ describe('bridgewellBidAdapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'userId': userId, } ]; @@ -142,10 +152,13 @@ describe('bridgewellBidAdapter', function () { expect(payload.adUnits).to.be.an('array'); expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { - expect(payload.adUnits[i]).to.have.property('ChannelID').that.is.a('string'); - expect(payload.adUnits[i]).to.not.have.property('cid'); - expect(payload.adUnits[i]).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); - expect(payload.adUnits[i]).to.have.property('requestId').and.to.equal('3150ccb55da321'); + let u = payload.adUnits[i]; + expect(u).to.have.property('ChannelID').that.is.a('string'); + expect(u).to.not.have.property('cid'); + expect(u).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); + expect(u).to.have.property('requestId').and.to.equal('3150ccb55da321'); + expect(u).to.have.property('userIds'); + expect(u.userIds).to.deep.equal(userId); } }); @@ -170,6 +183,7 @@ describe('bridgewellBidAdapter', function () { 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', + 'userId': userId }, ]; @@ -180,10 +194,13 @@ describe('bridgewellBidAdapter', function () { expect(payload.adUnits).to.be.an('array'); expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { - expect(payload.adUnits[i]).to.have.property('cid').that.is.a('number'); - expect(payload.adUnits[i]).to.not.have.property('ChannelID'); - expect(payload.adUnits[i]).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); - expect(payload.adUnits[i]).to.have.property('requestId').and.to.equal('3150ccb55da321'); + let u = payload.adUnits[i]; + expect(u).to.have.property('cid').that.is.a('number'); + expect(u).to.not.have.property('ChannelID'); + expect(u).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); + expect(u).to.have.property('requestId').and.to.equal('3150ccb55da321'); + expect(u).to.have.property('userIds'); + expect(u.userIds).to.deep.equal(userId); } }); diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 6c7ef816f4f..17f23c5acd3 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -103,6 +103,48 @@ describe('brightMountainMediaBidAdapter_spec', function () { let serverRequest = spec.buildRequests([bidBanner], bidderRequest); testServerRequestBody(serverRequest); + it('sends bidfloor param if present', function () { + bidBanner.getFloor = function () { + return { + currency: 'USD', + floor: 0.5, + } + }; + const request = spec.buildRequests([bidBanner], bidderRequest); + expect(request.data.placements[0].floor['300x250']).to.equal(0.5); + }); + + it('sends gdpr info if exists', function () { + const gdprConsent = { + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + gdprApplies: true + }; + + bidderRequest['gdprConsent'] = gdprConsent; + const request = spec.buildRequests([bidBanner], bidderRequest); + + expect(request.data.gdpr_require).to.exist.and.to.be.a('number'); + expect(request.data.gdpr_consent).to.exist.and.to.be.a('string'); + }); + + it('sends schain info if exists', function () { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + }; + bidBanner.schain = schain; + const request = spec.buildRequests([bidBanner], bidderRequest); + expect(request.data.placements[0].schain).to.be.an('object'); + }); + bidderRequest['bids'] = [bidVideo]; serverRequest = spec.buildRequests([bidVideo], bidderRequest); testServerRequestBody(serverRequest); @@ -129,6 +171,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); expect(dataItem.mediaType).to.be.a('string'); + expect(dataItem.meta.advertiserDomains[0]).to.be.a('string'); } }); } @@ -145,7 +188,8 @@ describe('brightMountainMediaBidAdapter_spec', function () { ttl: 1000, creativeId: '123asd', netRevenue: true, - currency: 'USD' + currency: 'USD', + adomain: ['adomain.com'], }] }; @@ -160,7 +204,8 @@ describe('brightMountainMediaBidAdapter_spec', function () { ttl: 1000, creativeId: '123asd', netRevenue: true, - currency: 'USD' + currency: 'USD', + adomain: ['adomain.com'], }] }; let serverResponses = spec.interpretResponse(resObjectBanner); diff --git a/test/spec/modules/brightcomBidAdapter_spec.js b/test/spec/modules/brightcomBidAdapter_spec.js index a89391d681e..b7d52c9f7d5 100644 --- a/test/spec/modules/brightcomBidAdapter_spec.js +++ b/test/spec/modules/brightcomBidAdapter_spec.js @@ -247,7 +247,8 @@ describe('brightcomBidAdapter', function() { 'nurl': '', 'adm': '', 'w': 300, - 'h': 250 + 'h': 250, + 'adomain': ['example.com'] }] }] } @@ -265,7 +266,10 @@ describe('brightcomBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); @@ -283,7 +287,10 @@ describe('brightcomBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); diff --git a/test/spec/modules/ccxBidAdapter_spec.js b/test/spec/modules/ccxBidAdapter_spec.js index ef86b391e39..d346a14d38a 100644 --- a/test/spec/modules/ccxBidAdapter_spec.js +++ b/test/spec/modules/ccxBidAdapter_spec.js @@ -434,4 +434,58 @@ describe('ccxAdapter', function () { expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.be.empty; }); }); + describe('mediaTypesVideoParams', function () { + it('Valid video mediaTypes', function () { + let bids = [ + { + adUnitCode: 'video', + auctionId: '0b9de793-8eda-481e-a548-c187d58b28d9', + bidId: '3u94t90ut39tt3t', + bidder: 'ccx', + bidderRequestId: '23ur20r239r2r', + mediaTypes: { + video: { + playerSize: [[640, 480]], + protocols: [2, 3, 5, 6], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [1, 2, 3, 4], + skip: 1, + skipafter: 5 + } + }, + params: { + placementId: 608 + }, + sizes: [[640, 480]], + transactionId: 'aefddd38-cfa0-48ab-8bdd-325de4bab5f9' + } + ]; + + let imps = [ + { + video: { + w: 640, + h: 480, + protocols: [2, 3, 5, 6], + mimes: ['video/mp4', 'video/x-flv'], + playbackmethod: [1, 2, 3, 4], + skip: 1, + skipafter: 5 + }, + id: '3u94t90ut39tt3t', + secure: 1, + ext: { + pid: 608 + } + } + ]; + + let bidsClone = utils.deepClone(bids); + + let response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + let data = JSON.parse(response.data); + + expect(data.imp).to.deep.have.same.members(imps); + }); + }); }); diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js index a2ce4cedea0..3755ddc4c4a 100644 --- a/test/spec/modules/cointrafficBidAdapter_spec.js +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -142,7 +142,9 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + mediaType: 'banner', + adomain: ['test.com'] } }; @@ -155,7 +157,13 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + meta: { + mediaType: 'banner', + advertiserDomains: [ + 'test.com', + ] + } }]; let result = spec.interpretResponse(serverResponse, bidRequest[0]); @@ -186,7 +194,9 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + mediaType: 'banner', + adomain: ['test.com'] } }; @@ -199,7 +209,13 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + meta: { + mediaType: 'banner', + advertiserDomains: [ + 'test.com', + ] + } }]; const getConfigStub = sinon.stub(config, 'getConfig').returns('USD'); diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index dbac3c0dc7c..657bc432d06 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -303,7 +303,43 @@ describe('ConnectAd Adapter', function () { }); describe('bid responses', function () { - it('should return complete bid response', function () { + it('should return complete bid response with adomain', function () { + const ADOMAINS = ['connectad.io']; + + let serverResponse = { + body: { + decisions: { + '2f95c00074b931': { + adId: '0', + adomain: ['connectad.io'], + contents: [ + { + body: '<<<---- Creative --->>>' + } + ], + height: '250', + width: '300', + pricing: { + clearPrice: 11.899999999999999 + } + } + } + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(11.899999999999999); + expect(bids[0].width).to.equal('300'); + expect(bids[0].height).to.equal('250'); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(ADOMAINS); + }); + + it('should return complete bid response with empty adomain', function () { + const ADOMAINS = []; + let serverResponse = { body: { decisions: { @@ -331,6 +367,7 @@ describe('ConnectAd Adapter', function () { expect(bids[0].width).to.equal('300'); expect(bids[0].height).to.equal('250'); expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(ADOMAINS); }); it('should return empty bid response', function () { diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 1c24fb7694a..96a7f419341 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -588,4 +588,77 @@ describe('Conversant adapter tests', function() { expect(payload).to.have.deep.nested.property('user.ext.fpc', 'fghijk'); }); }); + + describe('price floor module', function() { + let bidRequest; + beforeEach(function() { + bidRequest = [utils.deepClone(bidRequests[0])]; + delete bidRequest[0].params.bidfloor; + }); + + it('obtain floor from getFloor', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'USD', + floor: 3.21 + }; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 3.21); + }); + + it('obtain floor from params', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'USD', + floor: 3.21 + }; + }; + bidRequest[0].params.bidfloor = 0.6; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0.6); + }); + + it('unsupported currency', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'EUR', + floor: 1.23 + }; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + + it('bad floor value', function() { + bidRequest[0].getFloor = () => { + return { + currency: 'USD', + floor: 'test' + }; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + + it('empty floor object', function() { + bidRequest[0].getFloor = () => { + return {}; + }; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + + it('undefined floor result', function() { + bidRequest[0].getFloor = () => {}; + + const payload = spec.buildRequests(bidRequest).data; + expect(payload.imp[0]).to.have.property('bidfloor', 0); + }); + }); }); diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js new file mode 100644 index 00000000000..a145f429557 --- /dev/null +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -0,0 +1,351 @@ +import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; +import { cloneDeep } from 'lodash'; +import { server } from 'test/mocks/xhr.js'; +import { config } from 'src/config.js'; + +const DG_GET_KEYWORDS_TIMEOUT = 1950; +const IGNORE_SET_ORTB2 = true; +const DEF_CONFIG = { + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DG_GET_KEYWORDS_TIMEOUT, + }, +}; +const DUMMY_RESPONSE_HEADER = { 'Content-Type': 'application/json' }; +const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; +const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; +const SUCCESS_ORTB2 = { + ortb2: { + site: { keywords: SUCCESS_RESULT }, + user: { keywords: SUCCESS_RESULT }, + }, +}; + +describe('Digital Garage Keyword Module', function () { + it('should init and return always true', function () { + expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); + }); + + describe('dgkeyword target test', function () { + it('should have no target', function () { + const adUnits_no_target = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: false, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') + .that.is.empty; + }); + it('should have targets', function () { + const adUnits_targets = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + dgkeyword: true, + }, + }, + ], + }, + ]; + const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); + expect(targets[0].bidder).to.be.equal('dg2'); + expect(targets[0].params.placementId).to.be.equal(99999998); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].bidder).to.be.equal('dg'); + expect(targets[1].params.placementId).to.be.equal(99999996); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + }); + }); + + describe('get profile.', function () { + const AD_UNITS = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + it('should get profiles error(404).', function (done) { + let pbjs = cloneDeep(config); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pbjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(pbjs.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond(404); + }); + it('should get profiles timeout.', function (done) { + let pbjs = cloneDeep(config); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + moduleConfig.params.timeout = 10; + dgRtd.getDgKeywordsAndSet( + pbjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(pbjs.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + setTimeout(() => { + const request = server.requests[0]; + if (request) { + request.respond( + 200, + DUMMY_RESPONSE_HEADER, + JSON.stringify(DUMMY_RESPONSE) + ); + } + }, 1000); + }); + it('should get profiles ok(200).', function (done) { + let pbjs = cloneDeep(config); + pbjs.adUnits = cloneDeep(AD_UNITS); + if (IGNORE_SET_ORTB2) { + pbjs._ignoreSetOrtb2 = true; + } + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pbjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + if (!IGNORE_SET_ORTB2) { + expect(pbjs.getBidderConfig()).to.be.deep.equal({ + dg2: SUCCESS_ORTB2, + dg: SUCCESS_ORTB2, + }); + } + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond( + 200, + DUMMY_RESPONSE_HEADER, + JSON.stringify(DUMMY_RESPONSE) + ); + }); + }); +}); diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 855ffa0a0b7..0f82122b9c1 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -253,12 +253,38 @@ describe('emx_digital Adapter', function () { expect(queryParams[1]).to.match(new RegExp('^ts=\d*', 'g')); }); - it('builds with bid floor', function () { - const bidRequestWithBidFloor = utils.deepClone(bidderRequest.bids); - bidRequestWithBidFloor[0].params.bidfloor = 1; - const requestWithFloor = spec.buildRequests(bidRequestWithBidFloor, bidderRequest); + it('builds bidfloor value from bid param when getFloor function does not exist', function () { + const bidRequestWithFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithFloor[0].params.bidfloor = 1; + const requestWithFloor = spec.buildRequests(bidRequestWithFloor, bidderRequest); const data = JSON.parse(requestWithFloor.data); - expect(data.imp[0].bidfloor).to.equal(bidRequestWithBidFloor[0].params.bidfloor); + expect(data.imp[0].bidfloor).to.equal(bidRequestWithFloor[0].params.bidfloor); + }); + + it('builds bidfloor value from getFloor function when it exists', function () { + const floorResponse = { currency: 'USD', floor: 3 }; + const bidRequestWithGetFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithGetFloor[0].getFloor = () => floorResponse; + const requestWithGetFloor = spec.buildRequests(bidRequestWithGetFloor, bidderRequest); + const data = JSON.parse(requestWithGetFloor.data); + expect(data.imp[0].bidfloor).to.equal(3); + }); + + it('builds bidfloor value from getFloor when both floor and getFloor function exists', function () { + const floorResponse = { currency: 'USD', floor: 3 }; + const bidRequestWithBothFloors = utils.deepClone(bidderRequest.bids); + bidRequestWithBothFloors[0].params.bidfloor = 1; + bidRequestWithBothFloors[0].getFloor = () => floorResponse; + const requestWithBothFloors = spec.buildRequests(bidRequestWithBothFloors, bidderRequest); + const data = JSON.parse(requestWithBothFloors.data); + expect(data.imp[0].bidfloor).to.equal(3); + }); + + it('empty bidfloor value when floor and getFloor is not defined', function () { + const bidRequestWithoutFloor = utils.deepClone(bidderRequest.bids); + const requestWithoutFloor = spec.buildRequests(bidRequestWithoutFloor, bidderRequest); + const data = JSON.parse(requestWithoutFloor.data); + expect(data.imp[0].bidfloor).to.not.exist; }); it('builds request properly', function () { @@ -470,7 +496,8 @@ describe('emx_digital Adapter', function () { 'id': '987654321cba', 'price': 0.5, 'ttl': 300, - 'w': 300 + 'w': 300, + 'adomain': ['example.com'] }], 'seat': '1356' }, { @@ -498,7 +525,10 @@ describe('emx_digital Adapter', function () { 'netRevneue': true, 'mediaType': 'banner', 'ad': '', - 'ttl': 300 + 'ttl': 300, + 'meta': { + 'advertiserDomains': ['example.com'] + } }, { 'requestId': '12819a18-56e1-4256-b836-b69a10202668', 'cpm': 0.7, @@ -630,6 +660,14 @@ describe('emx_digital Adapter', function () { body: badAdmServerResponse })); }); + + it('returns valid advertiser domain', function () { + const bidResponse = utils.deepClone(serverResponse); + let result = spec.interpretResponse({body: bidResponse}); + expect(result[0].meta.advertiserDomains).to.deep.equal(expectedResponse[0].meta.advertiserDomains); + // case where adomains are not in request + expect(result[1].meta).to.not.exist; + }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 1d3a8344170..6aa191f29a5 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -26,6 +26,7 @@ describe('E-Planning Adapter', function () { const I_ID = '7854abc56248f873'; const CRID = '1234567890'; const TEST_ISV = 'leles.e-planning.net'; + const ADOMAIN = 'adomain.com'; const validBid = { 'bidder': 'eplanning', 'bidId': BID_ID, @@ -237,6 +238,39 @@ describe('E-Planning Adapter', function () { ] } }; + const responseWithAdomain = { + body: { + 'sI': { + 'k': '12345' + }, + 'sec': { + 'k': 'ROS' + }, + 'sp': [{ + 'k': CLEAN_ADUNIT_CODE, + 'a': [{ + 'adm': ADM, + 'id': '7854abc56248f874', + 'i': I_ID, + 'fi': '7854abc56248f872', + 'ip': '45621afd87462104', + 'w': W, + 'h': H, + 'crid': CRID, + 'pr': CPM, + 'adom': ADOMAIN + }], + }], + 'cs': [ + 'http://a-sync-url.com/', + { + 'u': 'http://another-sync-url.com/test.php?&partner=123456&endpoint=us-east', + 'ifr': true + } + ] + } + }; + const responseWithNoSpace = { body: { 'sI': { @@ -520,6 +554,25 @@ describe('E-Planning Adapter', function () { }; expect(bidResponse).to.deep.equal(expectedResponse); }); + + it('should pass advertiserDomains when present', function () { + const bidResponse = spec.interpretResponse(responseWithAdomain, { adUnitToBidId: { [CLEAN_ADUNIT_CODE]: BID_ID } })[0]; + const expectedResponse = { + requestId: BID_ID, + cpm: CPM, + width: W, + height: H, + ad: ADM, + ttl: 120, + creativeId: CRID, + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ADOMAIN + } + }; + expect(bidResponse).to.deep.equal(expectedResponse); + }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index 2dbf6cd68c5..e2310aee1fb 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -26,6 +26,11 @@ describe('etarget adapter', function () { assert.lengthOf(parsedUrl.items, 7); }); + it('should be an object', function () { + let request = spec.buildRequests(bids); + assert.isNotNull(request.metaData); + }); + it('should handle global request parameters', function () { let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); assert.equal(parsedUrl.path, 'https://sk.search.etargetnet.com/hb'); diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js index f0fb01f4398..96bf319dfd2 100644 --- a/test/spec/modules/gjirafaBidAdapter_spec.js +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -136,7 +136,8 @@ describe('gjirafaAdapterTest', () => { 'CreativeId': '123abc', 'NetRevenue': false, 'Currency': 'EUR', - 'TTL': 360 + 'TTL': 360, + 'ADomain': ['somedomain.com'] }], headers: {} }; @@ -156,7 +157,8 @@ describe('gjirafaAdapterTest', () => { 'referrer', 'ad', 'vastUrl', - 'mediaType' + 'mediaType', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -164,5 +166,20 @@ describe('gjirafaAdapterTest', () => { expect(keys.indexOf(key) !== -1).to.equal(true); }); }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(728); + expect(result[0].height).to.equal(90); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) }); }); diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index 52bb8460caa..d29e27554c2 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -106,6 +106,9 @@ describe('GmosspAdapter', function () { price: 20, w: 300, h: 250, + adomains: [ + 'test.com' + ], ad: '
', creativeId: '985ec572b32be309.76973017', cur: 'JPY', @@ -126,6 +129,11 @@ describe('GmosspAdapter', function () { currency: 'JPY', width: 300, height: 250, + meta: { + advertiserDomains: [ + 'test.com' + ] + }, ad: '
', creativeId: '985ec572b32be309.76973017', netRevenue: true, diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index e161d82fdd6..4f5f62f2cb8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -555,7 +555,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -615,7 +615,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -631,7 +631,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 2
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -647,7 +647,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 3
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -709,7 +709,7 @@ describe('TheMediaGrid Adapter', function () { 'height': 600, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -728,7 +728,7 @@ describe('TheMediaGrid Adapter', function () { 'height': undefined, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -862,7 +862,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -878,7 +878,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 2
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -894,7 +894,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 3
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -910,7 +910,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 4
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 2aec9713000..6d3a607c0c5 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -132,6 +132,43 @@ describe('TheMediaGridNM Adapter', function () { expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); }); + + it('should return true when required params is absent, but available in mediaTypes', function () { + const paramsList = [ + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + } + } + ]; + + const mediaTypes = { + video: { + mimes: ['video/mp4', 'video/x-ms-wmv'], + playerSize: [200, 300], + protocols: [1, 2, 3, 4, 5, 6] + } + }; + + paramsList.forEach((params) => { + const validBid = Object.assign({}, bid); + delete validBid.params; + validBid.params = params; + validBid.mediaTypes = mediaTypes; + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + }); }); describe('buildRequests', function () { @@ -198,6 +235,39 @@ describe('TheMediaGridNM Adapter', function () { }); }); + it('should attach valid params from mediaTypes', function () { + const mediaTypes = { + video: { + skipafter: 10, + minduration: 10, + maxduration: 100, + protocols: [1, 3, 4], + playerSize: [300, 250] + } + }; + const bidRequest = Object.assign({ mediaTypes }, bidRequests[0]); + const req = spec.buildRequests([bidRequest], bidderRequest)[0]; + const expectedVideo = { + 'skipafter': 10, + 'mind': 10, + 'maxd': 100, + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6], + 'size': '300x250' + }; + const expectedParams = Object.assign({}, bidRequest.params); + expectedParams.video = Object.assign(expectedParams.video, expectedVideo); + + expect(req.url).to.be.an('string'); + const payload = parseRequestUrl(req.url); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); + expect(payload).to.have.property('sizes', '300x250,300x600'); + expect(req.data).to.deep.equal(expectedParams); + }); + it('if gdprConsent is present payload must have gdpr params', function () { const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); expect(request.url).to.be.an('string'); @@ -235,7 +305,7 @@ describe('TheMediaGridNM Adapter', function () { describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '2'}, - {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300, adomain: ['my_domain.ru']}], 'seat': '2'}, {'bid': [{'price': 0, 'h': 250, 'w': 300}], 'seat': '2'}, {'bid': [{'price': 0, 'adm': '\n<\/Ad>\n<\/VAST>', 'h': 250, 'w': 300}], 'seat': '2'}, undefined, @@ -302,9 +372,12 @@ describe('TheMediaGridNM Adapter', function () { 'height': 250, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'meta': { + 'advertiserDomains': [] + }, 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } @@ -318,9 +391,12 @@ describe('TheMediaGridNM Adapter', function () { 'height': 600, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'meta': { + 'advertiserDomains': ['my_domain.ru'] + }, 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } @@ -352,6 +428,9 @@ describe('TheMediaGridNM Adapter', function () { 'bidId': '2bc598e42b6a', 'bidderRequestId': '39d74f5b71464', 'auctionId': '1cbd2feafe5e8b', + 'meta': { + 'advertiserDomains': [] + }, 'mediaTypes': { 'video': { 'context': 'instream' diff --git a/test/spec/modules/hybridBidAdapter_spec.js b/test/spec/modules/hybridBidAdapter_spec.js index d1899ef3d81..ffbc27293fb 100644 --- a/test/spec/modules/hybridBidAdapter_spec.js +++ b/test/spec/modules/hybridBidAdapter_spec.js @@ -255,7 +255,8 @@ describe('Hybrid.ai Adapter', function() { currency: 'USD', content: 'html', width: 100, - height: 100 + height: 100, + advertiserDomains: ['hybrid.ai'] } ] } @@ -269,6 +270,7 @@ describe('Hybrid.ai Adapter', function() { expect(bids[0].height).to.equal(100) expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].meta.advertiserDomains).to.deep.equal(['hybrid.ai']) expect(typeof bids[0].ad).to.equal('string') }) it('should return a In-Image bid', function() { diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index ee3624b5b16..ea3e1d6611b 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -733,9 +733,51 @@ describe('invibesBidAdapter:', function () { - ` + `, + meta: {} }]; + let multiResponse = { + AdPlacements: [{ + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }], + BidModel: { + BidVersion: 1, + PlacementId: '12345', + AuctionStartTime: Date.now(), + CreativeHtml: '' + } + }] + }; + + let invalidResponse = { + AdPlacements: [{ + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }] + }] + }; + + let responseWithMeta = { + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }], + BidModel: { + BidVersion: 1, + PlacementId: '12345', + AuctionStartTime: Date.now(), + CreativeHtml: '', + Meta: { + advertiserDomains: ['theadvertiser.com', 'theadvertiser_2.com'], + advertiserName: 'theadvertiser' + } + } + }; + context('when the response is not valid', function () { it('handles response with no bids requested', function () { let emptyResult = spec.interpretResponse({body: response}); @@ -781,29 +823,39 @@ describe('invibesBidAdapter:', function () { let emptyResult = spec.interpretResponse({BidModel: {}, Ads: [{BidPrice: 1}]}, {bidRequests}); expect(emptyResult).to.be.empty; }); + + it('handles response when bid model is missing', function () { + let emptyResult = spec.interpretResponse(invalidResponse); + expect(emptyResult).to.be.empty; + }); }); - context('when the response is valid', function () { - it('responds with a valid bid', function () { - // top.window.invibes.setCookie('a', 'b', 370); - // top.window.invibes.setCookie('c', 'd', 0); - let result = spec.interpretResponse({body: response}, {bidRequests}); + context('when the multiresponse is valid', function () { + it('responds with a valid multiresponse bid', function () { + let result = spec.interpretResponse({body: multiResponse}, {bidRequests}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('responds with a valid bid and uses logger', function () { - localStorage.InvibesDEBUG = true; + it('responds with a valid singleresponse bid', function () { let result = spec.interpretResponse({body: response}, {bidRequests}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('does not make multiple bids', function () { - localStorage.InvibesDEBUG = false; let result = spec.interpretResponse({body: response}, {bidRequests}); let secondResult = spec.interpretResponse({body: response}, {bidRequests}); expect(secondResult).to.be.empty; }); }); + + context('when the response has meta', function () { + it('responds with a valid bid, with the meta info', function () { + let result = spec.interpretResponse({body: responseWithMeta}, {bidRequests}); + expect(result[0].meta.advertiserName).to.equal('theadvertiser'); + expect(result[0].meta.advertiserDomains).to.contain('theadvertiser.com'); + expect(result[0].meta.advertiserDomains).to.contain('theadvertiser_2.com'); + }); + }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index e0d45913fd9..48c8e48e6af 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -462,9 +462,23 @@ describe('IndexexchangeAdapter', function () { ]; const DEFAULT_USERID_BID_DATA = { - lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4' + lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4', + flocId: {id: '1234', version: 'chrome.1.2'} }; + const DEFAULT_FLOC_USERID_PAYLOAD = [ + { + source: 'chrome.com', + uids: [{ + id: DEFAULT_USERID_BID_DATA.flocId.id, + ext: { + rtiPartner: 'flocId', + ver: DEFAULT_USERID_BID_DATA.flocId.version + } + }] + } + ]; + describe('inherited functions', function () { it('should exists and is a function', function () { const adapter = newBidder(spec); @@ -903,6 +917,96 @@ describe('IndexexchangeAdapter', function () { expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); }); + it('IX adapter reads floc id from prebid userId and adds it to eids when there is not other eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(1); + expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc id from prebid userId and appends it to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA); + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(6); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads empty floc obj from prebid userId it, floc is not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {'flocId': {}} + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc obj from prebid userId it version is missing, floc is not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {'flocId': {'id': 'abcd'}} + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc obj from prebid userId it ID is missing, floc is not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {'flocId': {'version': 'chrome.a.b.c'}} + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + + it('IX adapter reads floc id with empty id from prebid userId and it does not added to eids', function() { + const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID); + cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); + cloneValidBid[0].userId = {flocID: {id: '', ver: 'chrome.1.2.3'}}; + const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; + const payload = JSON.parse(request.data.r); + + expect(payload.user.eids).to.have.lengthOf(5); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]); + expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]); + expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]); + }); + it('We continue to send in IXL identity info and Prebid takes precedence over IXL', function () { validIdentityResponse = { AdserverOrgIp: { @@ -1117,7 +1221,7 @@ describe('IndexexchangeAdapter', function () { }); describe('buildRequests', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; + let request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const requestUrl = request.url; const requestMethod = request.method; const query = request.data; @@ -1147,9 +1251,36 @@ describe('IndexexchangeAdapter', function () { expect(query.nf).not.to.exist; }); + it('should send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot exists', function () { + const AD_UNIT_CODE = '/19968336/some-adunit-path'; + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam', + adslot: AD_UNIT_CODE + } + } + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + expect(dfp_ad_unit_code).to.equal(AD_UNIT_CODE); + }); + + it('should not send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot does not exists', function () { + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + + expect(dfp_ad_unit_code).to.not.exist; + }); + it('payload should have correct format and value', function () { const payload = JSON.parse(query.r); expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); + expect(payload.id).to.be.a('string'); expect(payload.site).to.exist; expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); @@ -1161,6 +1292,18 @@ describe('IndexexchangeAdapter', function () { expect(payload.imp).to.have.lengthOf(2); }); + it('payload should have correct format and value for r.id when bidderRequestId is a number ', function () { + const bidWithIntId = utils.deepClone(DEFAULT_BANNER_VALID_BID); + bidWithIntId[0].bidderRequestId = 123456; + + request = spec.buildRequests(bidWithIntId, DEFAULT_OPTION)[0]; + + const payload = JSON.parse(request.data.r); + expect(bidWithIntId[0].bidderRequestId).to.be.a('number'); + expect(payload.id).to.equal(bidWithIntId[0].bidderRequestId.toString()); + expect(payload.id).to.be.a('string'); + }); + it('payload should not include schain when not provided', function () { const payload = JSON.parse(queryWithoutSchain.r); expect(payload.source).to.not.exist; // source object currently only written for schain @@ -1859,7 +2002,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -1885,7 +2027,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -1912,7 +2053,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -1939,7 +2079,6 @@ describe('IndexexchangeAdapter', function () { currency: 'JPY', ttl: 300, netRevenue: true, - dealId: undefined, meta: { networkId: 50, brandId: 303325, @@ -1952,9 +2091,38 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); - it('should set dealId correctly', function () { + it('should prioritize bid[].dealid over bid[].ext.dealid ', function () { + const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); + bidResponse.seatbid[0].bid[0].ext.dealid = 'ext-deal'; + bidResponse.seatbid[0].bid[0].dealid = 'outter-deal'; + const expectedParse = [ + { + requestId: '1a2b3c4d', + cpm: 1, + creativeId: '12345', + width: 300, + height: 250, + mediaType: 'banner', + ad: '', + currency: 'USD', + ttl: 300, + netRevenue: true, + dealId: 'outter-deal', + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] + } + } + ]; + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + + expect(result[0].dealId).to.equal(expectedParse[0].dealId); + }); + + it('should not set bid[].dealid if dealid is not present', function () { const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); - bidResponse.seatbid[0].bid[0].ext.dealid = 'deal'; const expectedParse = [ { requestId: '1a2b3c4d', @@ -1967,7 +2135,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 300, netRevenue: true, - dealId: 'deal', meta: { networkId: 50, brandId: 303325, @@ -1980,6 +2147,34 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); + it('should use set bid[].ext.dealid if bid[].dealid is not present', function () { + const bidResponse = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); + bidResponse.seatbid[0].bid[0].ext.dealid = 'ext-deal'; + const expectedParse = [ + { + requestId: '1a2b3c4d', + cpm: 1, + creativeId: '12345', + width: 300, + height: 250, + mediaType: 'banner', + ad: '', + currency: 'USD', + ttl: 300, + dealId: 'ext-deal', + netRevenue: true, + meta: { + networkId: 50, + brandId: 303325, + brandName: 'OECTA', + advertiserDomains: ['www.abc.com'] + } + } + ]; + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + expect(result[0].dealId).to.deep.equal(expectedParse[0].dealId); + }); + it('should get correct bid response for video ad', function () { const expectedParse = [ { @@ -1992,7 +2187,6 @@ describe('IndexexchangeAdapter', function () { currency: 'USD', ttl: 3600, netRevenue: true, - dealId: undefined, vastUrl: 'www.abcd.com/vast', meta: { networkId: 51, @@ -2038,6 +2232,26 @@ describe('IndexexchangeAdapter', function () { expect(requestWithoutreferInfo.site.page).to.equal(options.refererInfo.referer); expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); + + it('should set bid[].ttl to seatbid[].bid[].exp value from response', function () { + const BANNER_RESPONSE_WITH_EXP = utils.deepClone(DEFAULT_BANNER_BID_RESPONSE); + const VIDEO_RESPONSE_WITH_EXP = utils.deepClone(DEFAULT_VIDEO_BID_RESPONSE); + VIDEO_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 200; + BANNER_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 100; + const bannerResult = spec.interpretResponse({ body: BANNER_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const videoResult = spec.interpretResponse({ body: VIDEO_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + + expect(bannerResult[0].ttl).to.equal(100); + expect(videoResult[0].ttl).to.equal(200); + }); + + it('should default bid[].ttl if seat[].bid[].exp is not in the resposne', function () { + const bannerResult = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const videoResult = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + + expect(bannerResult[0].ttl).to.equal(300); + expect(videoResult[0].ttl).to.equal(3600); + }); }); describe('bidrequest consent', function () { @@ -2144,5 +2358,27 @@ describe('IndexexchangeAdapter', function () { expect(utils.deepAccess(requestWithConsent, 'user.ext.consented_providers_settings')).to.not.exist; expect(utils.deepAccess(requestWithConsent, 'user.ext.consent')).to.not.exist; }); + + it('should set coppa to 1 in config when enabled', () => { + config.setConfig({ coppa: true }) + const bid = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const r = JSON.parse(bid[0].data.r); + + expect(r.regs.coppa).to.equal(1); + }); + it('should not set coppa in config when disabled', () => { + config.setConfig({ coppa: false }) + const bid = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const r = JSON.parse(bid[0].data.r); + + expect(r.regs.coppa).to.be.undefined; + }); + it('should not set coppa when not specified in config', () => { + config.resetConfig(); + const bid = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const r = JSON.parse(bid[0].data.r); + + expect(r.regs.coppa).to.be.undefined; + }); }); }); diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index 842f9e0ed30..ae58da30f64 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -281,7 +281,8 @@ describe('jixie Adapter', function () { }, 'vastUrl': 'https://ad.jixie.io/v1/video?creativeid=522' }, - // display ad returned here: + // display ad returned here: This one there is advertiserDomains + // in the response . Will be checked in the unit tests below { 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', 'jxBidId': '600c9ae6fda1acb-028d5dee-2c83-44e3-bed1-b75002475cdf', @@ -411,6 +412,9 @@ describe('jixie Adapter', function () { expect(result[0].ttl).to.equal(300) expect(result[0].vastUrl).to.include('https://ad.jixie.io/v1/video?creativeid=') expect(result[0].trackingUrlBase).to.include('sync') + // We will always make sure the meta->advertiserDomains property is there + // If no info it is an empty array. + expect(result[0].meta.advertiserDomains.length).to.equal(0) // display ad expect(result[1].requestId).to.equal('600c9ae6fda1acb') @@ -422,6 +426,7 @@ describe('jixie Adapter', function () { expect(result[1].netRevenue).to.equal(true) expect(result[1].ttl).to.equal(300) expect(result[1].ad).to.include('jxoutstream') + expect(result[1].meta.advertiserDomains.length).to.equal(3) expect(result[1].trackingUrlBase).to.include('sync') // should pick up about using alternative outstream renderer @@ -436,6 +441,7 @@ describe('jixie Adapter', function () { expect(result[2].vastXml).to.include('') expect(result[2].trackingUrlBase).to.include('sync'); expect(result[2].renderer.id).to.equal('demoslot4-div') + expect(result[2].meta.advertiserDomains.length).to.equal(0) expect(result[2].renderer.url).to.equal(JX_OTHER_OUTSTREAM_RENDERER_URL); // should know to use default outstream renderer @@ -450,6 +456,7 @@ describe('jixie Adapter', function () { expect(result[3].vastXml).to.include('') expect(result[3].trackingUrlBase).to.include('sync'); expect(result[3].renderer.id).to.equal('demoslot2-div') + expect(result[3].meta.advertiserDomains.length).to.equal(0) expect(result[3].renderer.url).to.equal(JX_OUTSTREAM_RENDERER_URL) setLocalStorageSpy.restore(); diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js index 725c9ece118..76c2c287989 100644 --- a/test/spec/modules/koblerBidAdapter_spec.js +++ b/test/spec/modules/koblerBidAdapter_spec.js @@ -513,6 +513,11 @@ describe('KoblerAdapter', function () { }); it('should generate bids from OpenRTB response', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'NOK' + } + }); const responseWithTwoBids = { body: { seatbid: [ @@ -521,12 +526,12 @@ describe('KoblerAdapter', function () { { impid: '6194ddef-89a4-404f-9efd-6b718fc23308', price: 7.981, - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + 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}', crid: 'edea9b03-3a57-41aa-9c00-abd673e22006', dealid: '', w: 320, h: 250, - adm: '', + adm: '', adomain: [ 'https://kobler.no' ] @@ -534,12 +539,12 @@ describe('KoblerAdapter', function () { { impid: '2ec0b40f-d3ca-4ba5-8ce3-48290565690f', price: 6.71234, - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', crid: 'fa2d5af7-2678-4204-9023-44c526160742', dealid: '2783483223432342', w: 580, h: 400, - adm: '', + adm: '', adomain: [ 'https://bid.kobler.no' ] @@ -563,8 +568,8 @@ describe('KoblerAdapter', function () { dealId: '', netRevenue: true, ttl: 600, - ad: '', - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + ad: '', + 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}', meta: { advertiserDomains: [ 'https://kobler.no' @@ -581,8 +586,8 @@ describe('KoblerAdapter', function () { dealId: '2783483223432342', netRevenue: true, ttl: 600, - ad: '', - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + ad: '', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', meta: { advertiserDomains: [ 'https://bid.kobler.no' @@ -617,9 +622,15 @@ describe('KoblerAdapter', function () { }); it('Should trigger pixel with replaced nurl if nurl is not empty', function () { + config.setConfig({ + 'currency': { + 'adServerCurrency': 'NOK' + } + }); spec.onBidWon({ cpm: 8.341, - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&asp=${AD_SERVER_PRICE}', + 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: { hb_pb: 8 } @@ -627,7 +638,7 @@ describe('KoblerAdapter', function () { 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&asp=8' + 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=8.341&sp_cur=NOK&asp=8&asp_cur=NOK' ); }); }); diff --git a/test/spec/modules/lockerdomeBidAdapter_spec.js b/test/spec/modules/lockerdomeBidAdapter_spec.js index 42e3f52e533..9e3d7981300 100644 --- a/test/spec/modules/lockerdomeBidAdapter_spec.js +++ b/test/spec/modules/lockerdomeBidAdapter_spec.js @@ -180,7 +180,8 @@ describe('LockerDomeAdapter', function () { currency: 'USD', netRevenue: true, ad: '', - ttl: 300 + ttl: 300, + adomain: ['example.com'] }, { requestId: '4510f2834773ce', @@ -191,7 +192,8 @@ describe('LockerDomeAdapter', function () { currency: 'USD', netRevenue: true, ad: '', - ttl: 300 + ttl: 300, + adomain: ['example.com'] }] } }; @@ -217,6 +219,9 @@ describe('LockerDomeAdapter', function () { expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.bids[0].netRevenue); expect(interpretedResponse[0].ad).to.equal(serverResponse.body.bids[0].ad); expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.bids[0].ttl); + expect(interpretedResponse[0]).to.have.property('meta'); + expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[0].meta.advertiserDomains).to.deep.equal(serverResponse.body.bids[0].adomain); expect(interpretedResponse[1].requestId).to.equal(serverResponse.body.bids[1].requestId); expect(interpretedResponse[1].cpm).to.equal(serverResponse.body.bids[1].cpm); @@ -227,6 +232,9 @@ describe('LockerDomeAdapter', function () { expect(interpretedResponse[1].netRevenue).to.equal(serverResponse.body.bids[1].netRevenue); expect(interpretedResponse[1].ad).to.equal(serverResponse.body.bids[1].ad); expect(interpretedResponse[1].ttl).to.equal(serverResponse.body.bids[1].ttl); + expect(interpretedResponse[1]).to.have.property('meta'); + expect(interpretedResponse[1].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[1].meta.advertiserDomains).to.deep.equal(serverResponse.body.bids[1].adomain); }); }); }); diff --git a/test/spec/modules/malltvBidAdapter_spec.js b/test/spec/modules/malltvBidAdapter_spec.js index ffe08ad1a5e..c31e91992f7 100644 --- a/test/spec/modules/malltvBidAdapter_spec.js +++ b/test/spec/modules/malltvBidAdapter_spec.js @@ -136,7 +136,8 @@ describe('malltvAdapterTest', () => { 'CreativeId': '123abc', 'NetRevenue': false, 'Currency': 'EUR', - 'TTL': 360 + 'TTL': 360, + 'ADomain': ['somedomain.com'] }], headers: {} }; @@ -156,7 +157,8 @@ describe('malltvAdapterTest', () => { 'referrer', 'ad', 'vastUrl', - 'mediaType' + 'mediaType', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -164,5 +166,20 @@ describe('malltvAdapterTest', () => { expect(keys.indexOf(key) !== -1).to.equal(true); }); }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) }); }); diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 796be3420e8..20e5588a99e 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -76,6 +76,7 @@ describe('MediaSquare bid adapter tests', function () { 'bidder': 'msqClassic', 'code': 'test/publishername_atf_desktop_rg_pave', 'bid_id': 'aaaa1234', + 'adomain': ['test.com'], }], }}; @@ -135,6 +136,9 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.mediasquare).to.exist; expect(bid.mediasquare.bidder).to.equal('msqClassic'); expect(bid.mediasquare.code).to.equal([DEFAULT_PARAMS[0].params.owner, DEFAULT_PARAMS[0].params.code].join('/')); + expect(bid.meta).to.exist; + expect(bid.meta.advertiserDomains).to.exist; + expect(bid.meta.advertiserDomains).to.have.lengthOf(1); }); it('Verifies bidder code', function () { diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index e1132bf1b74..6f489a65d3c 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -65,8 +65,10 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.be.a('string') expect(request.url).to.include('?') - expect(request.url).to.include('ntv_url') expect(request.url).to.include('ntv_ptd') + expect(request.url).to.include('ntv_pb_rid') + expect(request.url).to.include('ntv_ppc') + expect(request.url).to.include('ntv_url') }) }) }) diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 8dc15fbc89e..c44b0ce3fc2 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -257,12 +257,12 @@ describe('Nobid Adapter', function () { 'uids': [ { 'id': 'ID5_ID', - 'atype': 1, - 'ext': { - 'linkType': 0 - } + 'atype': 1 } - ] + ], + 'ext': { + 'linkType': 0 + } }, { 'source': 'adserver.org', @@ -284,7 +284,7 @@ describe('Nobid Adapter', function () { refererInfo: {referer: REFERER} } - it('should get user ids from eids', function () { + it('should criteo eid', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.sid).to.exist.and.to.equal(2); @@ -598,6 +598,47 @@ describe('Nobid Adapter', function () { }); }); + describe('interpretResponseWithMeta', function () { + const CREATIVE_ID_300x250 = 'CREATIVE-100'; + const ADUNIT_300x250 = 'ADUNIT-1'; + const ADMARKUP_300x250 = 'ADMARKUP-300x250'; + const PRICE_300x250 = 0.51; + const REQUEST_ID = '3db3773286ee59'; + const DEAL_ID = 'deal123'; + const ADOMAINS = ['adomain1', 'adomain2']; + let response = { + country: 'US', + ip: '68.83.15.75', + device: 'COMPUTER', + site: 2, + bids: [ + {id: 1, + bdrid: 101, + divid: ADUNIT_300x250, + dealid: DEAL_ID, + creativeid: CREATIVE_ID_300x250, + size: {'w': 300, 'h': 250}, + adm: ADMARKUP_300x250, + price: '' + PRICE_300x250, + meta: { + advertiserDomains: ADOMAINS + } + } + ] + }; + + it('should meta.advertiserDomains be respected', function () { + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result[0].meta.advertiserDomains).to.equal(ADOMAINS); + }); + }); + describe('buildRequestsWithSupplyChain', function () { const SITE_ID = 2; let bidRequests = [ diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js new file mode 100644 index 00000000000..d08bf2c8430 --- /dev/null +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -0,0 +1,312 @@ +import { expect } from 'chai'; +import { spec } from 'modules/oguryBidAdapter'; +import { deepClone } from 'src/utils.js'; + +const BID_HOST = 'https://webmobile.presage.io/api/header-bidding-request'; + +describe('OguryBidAdapter', function () { + let bidRequests; + let bidderRequest; + + bidRequests = [ + { + adUnitCode: 'adUnitCode', + auctionId: 'auctionId', + bidId: 'bidId', + bidder: 'ogury', + params: { + assetKey: 'OGY-assetkey', + adUnitId: 'adunitId', + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + getFloor: ({ size, currency, mediaType }) => { + const floorResult = { + currency: 'USD', + floor: 0 + }; + + if (mediaType === 'banner') { + floorResult.floor = 4; + } else { + floorResult.floor = 1000; + } + + return floorResult; + }, + transactionId: 'transactionId' + }, + { + adUnitCode: 'adUnitCode2', + auctionId: 'auctionId', + bidId: 'bidId2', + bidder: 'ogury', + params: { + assetKey: 'OGY-assetkey', + adUnitId: 'adunitId2' + }, + mediaTypes: { + banner: { + sizes: [[600, 500]] + } + }, + transactionId: 'transactionId2' + }, + ]; + + bidderRequest = { + auctionId: bidRequests[0].auctionId, + gdprConsent: {consentString: 'myConsentString', vendorData: {}, gdprApplies: true}, + }; + + describe('isBidRequestValid', function () { + it('should validate correct bid', () => { + let validBid = deepClone(bidRequests[0]); + + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('should not validate incorrect bid', () => { + let invalidBid = deepClone(bidRequests[0]); + delete invalidBid.sizes; + delete invalidBid.mediaTypes; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }); + + it('should not validate bid if adunit is not present', () => { + let invalidBid = deepClone(bidRequests[0]); + delete invalidBid.params.adUnitId; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }); + + it('should not validate bid if assetKet is not present', () => { + let invalidBid = deepClone(bidRequests[0]); + delete invalidBid.params.assetKey; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(false); + }); + + it('should validate bid if getFloor is not present', () => { + let invalidBid = deepClone(bidRequests[1]); + delete invalidBid.getFloor; + + let isValid = spec.isBidRequestValid(invalidBid); + expect(isValid).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const defaultTimeout = 1000; + const expectedRequestObject = { + id: bidRequests[0].auctionId, + at: 2, + tmax: defaultTimeout, + imp: [{ + id: bidRequests[0].bidId, + tagid: bidRequests[0].params.adUnitId, + bidfloor: 4, + banner: { + format: [{ + w: 300, + h: 250 + }] + } + }, { + id: bidRequests[1].bidId, + tagid: bidRequests[1].params.adUnitId, + bidfloor: 0, + banner: { + format: [{ + w: 600, + h: 500 + }] + } + }], + regs: { + ext: { + gdpr: 1 + }, + }, + site: { + id: bidRequests[0].params.assetKey, + domain: window.location.hostname, + }, + user: { + ext: { + consent: bidderRequest.gdprConsent.consentString + }, + } + }; + + it('sends bid request to ENDPOINT via POST', function () { + const validBidRequests = deepClone(bidRequests) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.url).to.equal(BID_HOST); + expect(request.method).to.equal('POST'); + }); + + it('bid request object should be conform', function () { + const validBidRequests = deepClone(bidRequests) + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestObject); + expect(request.data.regs.ext.gdpr).to.be.a('number'); + }); + + it('should not add gdpr infos if not present', () => { + const bidderRequestWithoutGdpr = { + ...bidderRequest, + gdprConsent: {}, + } + const expectedRequestObjectWithoutGdpr = { + ...expectedRequestObject, + regs: { + ext: { + gdpr: 1 + }, + }, + user: { + ext: { + consent: '' + }, + } + }; + + const validBidRequests = bidRequests + + const request = spec.buildRequests(validBidRequests, bidderRequestWithoutGdpr); + expect(request.data).to.deep.equal(expectedRequestObjectWithoutGdpr); + expect(request.data.regs.ext.gdpr).to.be.a('number'); + }); + + it('should handle bidFloor undefined', () => { + const expectedRequestWithUndefinedFloor = { + ...expectedRequestObject + }; + + const validBidRequests = deepClone(bidRequests); + validBidRequests[1] = { + ...validBidRequests[1], + getFloor: undefined + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestWithUndefinedFloor); + }); + + it('should handle bidFloor when is not function', () => { + const expectedRequestWithNotAFunctionFloor = { + ...expectedRequestObject + }; + + let validBidRequests = deepClone(bidRequests); + validBidRequests[1] = { + ...validBidRequests[1], + getFloor: 'getFloor' + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestWithNotAFunctionFloor); + }); + + it('should handle bidFloor when currency is not USD', () => { + const expectedRequestWithUnsupportedFloorCurrency = deepClone(expectedRequestObject) + expectedRequestWithUnsupportedFloorCurrency.imp[0].bidfloor = 0; + let validBidRequests = deepClone(bidRequests); + validBidRequests[0] = { + ...validBidRequests[0], + getFloor: ({ size, currency, mediaType }) => { + return { + currency: 'EUR', + floor: 4 + } + } + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request.data).to.deep.equal(expectedRequestWithUnsupportedFloorCurrency); + }); + }); + + describe('interpretResponse', function () { + let openRtbBidResponse = { + body: { + id: 'id_of_bid_response', + seatbid: [{ + bid: [{ + id: 'advertId', + impid: 'bidId', + price: 100, + nurl: 'url', + adm: `test creative
cookies
`, + adomain: ['renault.fr'], + w: 300, + h: 250 + }, { + id: 'advertId2', + impid: 'bidId2', + price: 150, + nurl: 'url2', + adm: `test creative
cookies
`, + adomain: ['peugeot.fr'], + w: 600, + h: 500 + }], + }] + } + }; + + it('should correctly interpret bidResponse', () => { + let expectedInterpretedBidResponse = [{ + requestId: openRtbBidResponse.body.seatbid[0].bid[0].impid, + cpm: openRtbBidResponse.body.seatbid[0].bid[0].price, + currency: 'USD', + width: openRtbBidResponse.body.seatbid[0].bid[0].w, + height: openRtbBidResponse.body.seatbid[0].bid[0].h, + ad: openRtbBidResponse.body.seatbid[0].bid[0].adm, + ttl: 60, + creativeId: openRtbBidResponse.body.seatbid[0].bid[0].id, + netRevenue: true, + meta: { + advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain + } + }, { + requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, + cpm: openRtbBidResponse.body.seatbid[0].bid[1].price, + currency: 'USD', + width: openRtbBidResponse.body.seatbid[0].bid[1].w, + height: openRtbBidResponse.body.seatbid[0].bid[1].h, + ad: openRtbBidResponse.body.seatbid[0].bid[1].adm, + ttl: 60, + creativeId: openRtbBidResponse.body.seatbid[0].bid[1].id, + netRevenue: true, + meta: { + advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain + } + }] + + let request = spec.buildRequests(bidRequests, bidderRequest); + let result = spec.interpretResponse(openRtbBidResponse, request); + + expect(result).to.deep.equal(expectedInterpretedBidResponse) + }); + + it('should return empty array if error during parsing', () => { + const wrongOpenRtbBidReponse = 'wrong data' + let request = spec.buildRequests(bidRequests, bidderRequest); + let result = spec.interpretResponse(wrongOpenRtbBidReponse, request); + + expect(result).to.be.instanceof(Array); + expect(result.length).to.equal(0) + }) + }); +}); diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 3f5304dce0a..5289203fd5b 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -59,11 +59,77 @@ describe('OneVideoBidAdapter', function () { }); describe('spec.isBidRequestValid', function () { - it('should return true when the required params are passed', function () { + it('should return false when mediaTypes video OR banner not declared', function () { + bidRequest.mediaTypes = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return true (skip validations) when e2etest = true', function () { + bidRequest.params.video = { + e2etest: true + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mediaTypes.video has all mandatory params', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + } + bidRequest.params.video = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when params.video has all override params instead of mediaTypes.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream' + }; + bidRequest.params.video = { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'] + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when playerWidth & playerHeight are passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + mimes: ['video/mp4', 'application/javascript'] + }; + bidRequest.params.video = { + playerWidth: 640, + playerHeight: 480, + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mimes is passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSizes: [640, 480] + }; + bidRequest.video = { + mimes: ['video/mp4', 'application/javascript'] + }; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the "video" param is missing', function () { + it('should return false when both mediaTypes.video and params.video Objects are missing', function () { + bidRequest.mediaTypes = {}; + bidRequest.params = { + pubId: 'brxd' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when both mediaTypes.video and params.video are missing mimes and player size', function () { + bidRequest.mediaTypes = { + video: { + context: 'instream' + } + }; bidRequest.params = { pubId: 'brxd' }; @@ -76,34 +142,16 @@ describe('OneVideoBidAdapter', function () { playerWidth: 480, playerHeight: 640, mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1, 5], - sid: 134, - rewarded: 1, - placement: 1, - inventoryid: 123 } }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); + it('should return true when the "pubId" param exists', function () { - bidRequest.params = { + bidRequest.mediaTypes = { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1, 5], - sid: 134, - rewarded: 1, - placement: 1, - inventoryid: 123 + playerSizes: [640, 480], + mimes: ['video/mp4', 'application/javascript'] }, pubId: 'brxd' }; @@ -216,7 +264,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.1.0'; + const VERSION = '3.1.1'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -640,7 +688,10 @@ describe('OneVideoBidAdapter', function () { adid: 123, crid: 2, price: 6.01, - adm: '' + adm: '', + adomain: [ + 'verizonmedia.com' + ], }] }], cur: 'USD' @@ -664,6 +715,9 @@ describe('OneVideoBidAdapter', function () { netRevenue: true, adUnitCode: bidRequest.adUnitCode, renderer: (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined, + meta: { + advertiserDomains: ['verizonmedia.com'] + } }; expect(bidResponse).to.deep.equal(o); }); diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index c1462c3814d..5a1f30a0de8 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -240,6 +240,7 @@ describe('onetag', function () { expect(dataItem.creativeId).to.be.a('string'); expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); + expect(dataItem.meta.advertiserDomains).to.be.an('array'); } }); it('Returns an empty array if response is not valid', function () { @@ -323,6 +324,7 @@ function getBannerVideoResponse() { currency: 'USD', requestId: 'banner', mediaType: BANNER, + adomain: [] }, { cpm: 13, @@ -334,7 +336,8 @@ function getBannerVideoResponse() { requestId: 'videoInstream', vastUrl: 'https://videoinstream.org', videoCacheKey: 'key', - mediaType: VIDEO + mediaType: VIDEO, + adomain: ['test_domain'] }, { cpm: 13, @@ -347,7 +350,8 @@ function getBannerVideoResponse() { requestId: 'videoOutstream', ad: '', rendererUrl: 'https://testRenderer', - mediaType: VIDEO + mediaType: VIDEO, + adomain: [] } ] } diff --git a/test/spec/modules/onomagicBidAdapter_spec.js b/test/spec/modules/onomagicBidAdapter_spec.js index 7c71c3e5764..6ddc0edd477 100644 --- a/test/spec/modules/onomagicBidAdapter_spec.js +++ b/test/spec/modules/onomagicBidAdapter_spec.js @@ -222,7 +222,8 @@ describe('onomagicBidAdapter', function() { 'nurl': '', 'adm': '', 'w': 300, - 'h': 250 + 'h': 250, + 'adomain': ['example.com'] }] }] } @@ -240,7 +241,10 @@ describe('onomagicBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); @@ -258,7 +262,10 @@ describe('onomagicBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 7391c8826bc..2a380277e55 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -341,7 +341,8 @@ describe('OpenxAdapter', function () { }, 'bidId': 'test-bid-id-1', 'bidderRequestId': 'test-bid-request-1', - 'auctionId': 'test-auction-1' + 'auctionId': 'test-auction-1', + 'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } } }, { 'bidder': 'openx', 'params': { @@ -356,7 +357,8 @@ describe('OpenxAdapter', function () { }, 'bidId': 'test-bid-id-2', 'bidderRequestId': 'test-bid-request-2', - 'auctionId': 'test-auction-2' + 'auctionId': 'test-auction-2', + 'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-1' } } } }]; const bidRequestsWithPlatform = [{ @@ -450,7 +452,12 @@ describe('OpenxAdapter', function () { it('should send the adunit codes', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.divIds).to.equal(`${encodeURIComponent(bidRequestsWithMediaTypes[0].adUnitCode)},${encodeURIComponent(bidRequestsWithMediaTypes[1].adUnitCode)}`); + expect(request[0].data.divids).to.equal(`${encodeURIComponent(bidRequestsWithMediaTypes[0].adUnitCode)},${encodeURIComponent(bidRequestsWithMediaTypes[1].adUnitCode)}`); + }); + + it('should send the gpids', function () { + const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); + expect(request[0].data.aucs).to.equal(`${encodeURIComponent('/12345/my-gpt-tag-0')},${encodeURIComponent('/12345/my-gpt-tag-1')}`); }); it('should send ad unit ids when any are defined', function () { @@ -1238,7 +1245,8 @@ describe('OpenxAdapter', function () { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' + 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e', + 'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } } }]; const mockBidderRequest = {refererInfo: {}}; @@ -1254,6 +1262,7 @@ describe('OpenxAdapter', function () { expect(dataParams.auid).to.equal('12345678'); expect(dataParams.vht).to.equal(480); expect(dataParams.vwd).to.equal(640); + expect(dataParams.aucs).to.equal(encodeURIComponent('/12345/my-gpt-tag-0')); }); it('shouldn\'t have the test parameter', function () { @@ -1313,45 +1322,97 @@ describe('OpenxAdapter', function () { expect(request[0].data.ju).to.not.equal(myUrl); }); - describe('when using the openRtb param', function () { - it('should covert the param to a JSON string', function () { - let myOpenRTBObject = {}; + describe('when using the openrtb video params', function () { + it('should parse legacy params.video.openrtb', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} const request = spec.buildRequests([videoBidRequest], mockBidderRequest); - expect(request[0].data.openrtb).to.equal(JSON.stringify(myOpenRTBObject)); + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); }); - it("should use the bidRequest's playerSize when it is available", function () { - const width = 200; - const height = 100; - const myOpenRTBObject = {v: height, w: width}; + it('should parse legacy params.openrtb', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; + videoBidRequest.params.openrtb = myOpenRTBObject; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse legacy params.video', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; + videoBidRequest.params.video = myOpenRTBObject; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse legacy params.video as full openrtb', function () { + let myOpenRTBObject = {imp: [{video: {mimes: ['application/javascript']}}]}; + videoBidRequest.params.video = myOpenRTBObject; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse legacy video.openrtb', function () { + let myOpenRTBObject = {mimes: ['application/javascript']}; + videoBidRequest.params.video = { + openrtb: myOpenRTBObject + }; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should omit filtered values for legacy', function () { + let myOpenRTBObject = {mimes: ['application/javascript'], dont: 'use'}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; + const expected = {imp: [{video: {w: 640, h: 480, mimes: ['application/javascript']}}]} + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + + expect(request[0].data.openrtb).to.equal(JSON.stringify(expected)); + }); + + it('should parse mediatypes.video', function () { + videoBidRequest.mediaTypes.video.mimes = ['application/javascript'] + videoBidRequest.mediaTypes.video.minduration = 15 const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); + expect(openRtbRequestParams.imp[0].video.mimes).to.eql(['application/javascript']); + expect(openRtbRequestParams.imp[0].video.minduration).to.equal(15); + }); - expect(openRtbRequestParams.w).to.not.equal(width); - expect(openRtbRequestParams.v).to.not.equal(height); + it('should filter mediatypes.video', function () { + videoBidRequest.mediaTypes.video.mimes = ['application/javascript'] + videoBidRequest.mediaTypes.video.minnothing = 15 + const request = spec.buildRequests([videoBidRequest], mockBidderRequest); + const openRtbRequestParams = JSON.parse(request[0].data.openrtb); + expect(openRtbRequestParams.imp[0].video.mimes).to.eql(['application/javascript']); + expect(openRtbRequestParams.imp[0].video.minnothing).to.equal(undefined); }); - it('should use the the openRTB\'s sizing when the bidRequest\'s playerSize is not available', function () { + it("should use the bidRequest's playerSize", function () { const width = 200; const height = 100; const myOpenRTBObject = {v: height, w: width}; videoBidRequest.params.video = { openrtb: myOpenRTBObject }; - videoBidRequest.mediaTypes.video.playerSize = undefined; - const request = spec.buildRequests([videoBidRequest], mockBidderRequest); const openRtbRequestParams = JSON.parse(request[0].data.openrtb); - expect(openRtbRequestParams.w).to.equal(width); - expect(openRtbRequestParams.v).to.equal(height); + expect(openRtbRequestParams.imp[0].video.w).to.equal(640); + expect(openRtbRequestParams.imp[0].video.h).to.equal(480); }); }); }); @@ -1465,7 +1526,7 @@ describe('OpenxAdapter', function () { const request = spec.buildRequests([multiformatBid], mockBidderRequest); const dataParams = request[0].data; - expect(dataParams.divIds).to.have.string(multiformatBid.adUnitCode); + expect(dataParams.divids).to.have.string(multiformatBid.adUnitCode); }); }); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index df551311c0b..42cc25ae04f 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -104,7 +104,7 @@ describe('orbidderBidAdapter', () => { expect(request.data.pageUrl).to.equal('https://localhost:9876/'); // expect(request.data.referrer).to.equal(''); Object.keys(defaultBidRequest).forEach((key) => { - expect(defaultBidRequest[key]).to.equal(request.data[key]); + expect(request.data[key]).to.deep.equal(defaultBidRequest[key]); }); }); @@ -189,6 +189,47 @@ describe('orbidderBidAdapter', () => { }); }); + it('should get correct bid response with advertiserDomains', () => { + const serverResponse = [ + { + 'width': 300, + 'height': 250, + 'creativeId': '29681110', + 'ad': '', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR', + 'advertiserDomains': ['cm.tavira.pt'] + } + ]; + + const expectedResponse = [ + { + 'requestId': '30b31c1838de1e', + 'cpm': 0.5, + 'creativeId': '29681110', + 'width': 300, + 'height': 250, + 'ttl': 60, + 'currency': 'EUR', + 'ad': '', + 'netRevenue': true, + 'meta': { + 'advertiserDomains': ['cm.tavira.pt'] + } + } + ]; + + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(expectedResponse.length); + Object.keys(expectedResponse[0]).forEach((key) => { + expect(result[0][key]).to.deep.equal(expectedResponse[0][key]); + }); + }); + it('handles broken server response', () => { const serverResponse = [ { diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index 4bdd657f419..a5f23240a7c 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -204,7 +204,14 @@ describe('Outbrain Adapter', function () { request: JSON.stringify(expectedNativeAssets) } } - ] + ], + ext: { + prebid: { + channel: { + name: 'pbjs', version: '$prebid.version$' + } + } + } } const res = spec.buildRequests([bidRequest], commonBidderRequest) expect(res.url).to.equal('https://bidder-url.com') @@ -244,7 +251,14 @@ describe('Outbrain Adapter', function () { ] } } - ] + ], + ext: { + prebid: { + channel: { + name: 'pbjs', version: '$prebid.version$' + } + } + } } const res = spec.buildRequests([bidRequest], commonBidderRequest) expect(res.url).to.equal('https://bidder-url.com') diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 3f517a66c68..256be7340b7 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -12,6 +12,7 @@ import { server } from 'test/mocks/xhr.js'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; +const EXPIRE_COOKIE_TIME = 864000000; const P_COOKIE_NAME = '_parrable_id'; const P_COOKIE_EID = '01.1563917337.test-eid'; const P_XHR_EID = '01.1588030911.test-new-eid' @@ -21,6 +22,7 @@ const P_CONFIG_MOCK = { partners: 'parrable_test_partner_123,parrable_test_partner_456' } }; +const RESPONSE_HEADERS = { 'Content-Type': 'application/json' }; function getConfigMock() { return { @@ -57,6 +59,11 @@ function serializeParrableId(parrableId) { if (parrableId.ccpaOptout) { str += ',ccpaOptout:1'; } + if (parrableId.tpc !== undefined) { + const tpcSupportComponent = parrableId.tpc === true ? 'tpc:1' : 'tpc:0'; + str += `,${tpcSupportComponent}`; + str += `,tpcUntil:${parrableId.tpcUntil}`; + } return str; } @@ -65,7 +72,7 @@ function writeParrableCookie(parrableId) { storage.setCookie( P_COOKIE_NAME, cookieValue, - (new Date(Date.now() + 5000).toUTCString()), + (new Date(Date.now() + EXPIRE_COOKIE_TIME).toUTCString()), 'lax' ); } @@ -125,7 +132,6 @@ describe('Parrable ID System', function() { { 'Content-Type': 'text/plain' }, JSON.stringify({ eid: P_XHR_EID }) ); - expect(callbackSpy.lastCall.lastArg).to.deep.equal({ eid: P_XHR_EID }); @@ -242,6 +248,143 @@ describe('Parrable ID System', function() { }) }); }); + + describe('third party cookie support status', function () { + let logErrorStub; + let callbackSpy = sinon.spy(); + + beforeEach(function() { + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + callbackSpy.resetHistory(); + removeParrableCookie(); + }); + + afterEach(function() { + logErrorStub.restore(); + }); + + describe('when getting tpcSupport from XHR response', function () { + let request; + let dateNowStub; + const dateNowMock = Date.now(); + const tpcSupportTtl = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + after(() => { + dateNowStub.restore(); + }); + + it('should set tpcSupport: true and tpcUntil in the cookie', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: true, tpcSupportTtl }) + ); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:1,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + + it('should set tpcSupport: false and tpcUntil in the cookie', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + ); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + + it('should not set tpcSupport in the cookie', function () { + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID }) + ); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID) + ); + }); + }); + + describe('when getting tpcSupport from cookie', function () { + let request; + let dateNowStub; + const dateNowMock = Date.now(); + const tpcSupportTtl = dateNowMock; + const tpcUntilExpired = 1; + + before(() => { + dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock); + }); + + after(() => { + dateNowStub.restore(); + }); + + it('should send tpcSupport in the XHR', function () { + writeParrableCookie({ + eid: P_COOKIE_EID, + tpc: true, + tpcUntil: (dateNowMock / 1000) + 1 + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.tpcSupport).to.equal(true); + }); + + it('should unset tpcSupport from cookie when tpcUntil reached', function () { + writeParrableCookie({ + eid: P_COOKIE_EID, + tpcSupport: true, + tpcUntil: tpcUntilExpired + }); + let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK); + callback(callbackSpy); + request = server.requests[0]; + + request.respond( + 200, + RESPONSE_HEADERS, + JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl }) + ); + + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(decodeBase64UrlSafe(queryParams.data))); + + expect(data.tpcSupport).to.equal(undefined); + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID + ',tpc:0,tpcUntil:' + Math.floor((dateNowMock / 1000) + tpcSupportTtl)) + ); + }); + }); + }); }); describe('parrableIdSystem.decode()', function() { @@ -529,7 +672,7 @@ describe('Parrable ID System', function() { }); }); - describe('partners parsing', () => { + describe('partners parsing', function () { let callbackSpy = sinon.spy(); const partnersTestCase = [ diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index e8948fdc2d3..80ce93858d5 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -791,6 +791,145 @@ describe('PubMatic adapter', function () { isValid = spec.isBidRequestValid(validBid); expect(isValid).to.equal(true); }); + + it('should check for context if video is present', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890' + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(true); + }) + + it('should return false if context is not present in video', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890' + }, + 'mediaTypes': { + 'video': { + 'w': 640, + 'h': 480, + 'protocols': [1, 2, 5], + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }, + isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equal(false); + }) + + it('bid.mediaTypes.video.mimes OR bid.params.video.mimes should be present and must be a non-empty array', function() { + let bid = { + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890', + 'video': {} + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }; + + delete bid.params.video.mimes; // Undefined + bid.mediaTypes.video.mimes = 'string'; // NOT array + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.params.video.mimes; // Undefined + delete bid.mediaTypes.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.params.video.mimes; // Undefined + bid.mediaTypes.video.mimes = ['video/flv']; // Valid + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.mediaTypes.video.mimes; // mediaTypes.video.mimes undefined + bid.params.video = {mimes: 'string'}; // NOT array + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.mediaTypes.video.mimes; // mediaTypes.video.mimes undefined + delete bid.params.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + + delete bid.mediaTypes.video.mimes; // mediaTypes.video.mimes undefined + bid.params.video.mimes = ['video/flv']; // Valid + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.mediaTypes.video.mimes; // Undefined + bid.params.video.mimes = ['video/flv']; // Valid + expect(spec.isBidRequestValid(bid)).to.equal(true); + + delete bid.mediaTypes.video.mimes; // Undefined + delete bid.params.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); }); describe('Request formation', function () { @@ -858,8 +997,6 @@ describe('PubMatic adapter', function () { expect(data.site.domain).to.be.a('string'); // domain should be set expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id - expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter - expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude @@ -879,6 +1016,7 @@ describe('PubMatic adapter', function () { expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); @@ -967,7 +1105,6 @@ describe('PubMatic adapter', function () { expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); - expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.site).to.not.exist; sandbox.restore(); }); @@ -994,7 +1131,6 @@ describe('PubMatic adapter', function () { expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); - expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.app.content).to.deep.equal(content); expect(data.site).to.not.exist; sandbox.restore(); @@ -1026,7 +1162,6 @@ describe('PubMatic adapter', function () { expect(data.app.bundle).to.equal('org.prebid.mobile.demoapp'); expect(data.app.domain).to.equal('prebid.org'); expect(data.app.publisher.id).to.equal(bidRequests[0].params.publisherId); - expect(data.app.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.app.content).to.deep.equal(appContent); expect(data.site).to.not.exist; sandbox.restore(); @@ -1043,8 +1178,6 @@ describe('PubMatic adapter', function () { expect(data.site.domain).to.be.a('string'); // domain should be set expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id - expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter - expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude @@ -1063,6 +1196,7 @@ describe('PubMatic adapter', function () { expect(data.imp[0].banner.w).to.equal(728); // width expect(data.imp[0].banner.h).to.equal(90); // height expect(data.imp[0].banner.format).to.deep.equal([{w: 160, h: 600}]); + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); }); @@ -2167,7 +2301,6 @@ describe('PubMatic adapter', function () { let data = JSON.parse(request.data); expect(data.imp[0].video).to.exist; expect(data.imp[0].tagid).to.equal('Div1'); - expect(data.imp[0].video.ext['video_skippable']).to.equal(videoBidRequests[0].params.video.skippable ? 1 : 0); expect(data.imp[0]['video']['mimes']).to.exist.and.to.be.an('array'); expect(data.imp[0]['video']['mimes'][0]).to.equal(videoBidRequests[0].params.video['mimes'][0]); expect(data.imp[0]['video']['mimes'][1]).to.equal(videoBidRequests[0].params.video['mimes'][1]); @@ -2237,7 +2370,6 @@ describe('PubMatic adapter', function () { // video imp object check expect(data.imp[1].video).to.exist; expect(data.imp[1].tagid).to.equal('Div1'); - expect(data.imp[1].video.ext['video_skippable']).to.equal(multipleMediaRequests[1].params.video.skippable ? 1 : 0); expect(data.imp[1]['video']['mimes']).to.exist.and.to.be.an('array'); expect(data.imp[1]['video']['mimes'][0]).to.equal(multipleMediaRequests[1].params.video['mimes'][0]); expect(data.imp[1]['video']['mimes'][1]).to.equal(multipleMediaRequests[1].params.video['mimes'][1]); @@ -2556,6 +2688,93 @@ describe('PubMatic adapter', function () { expect(data.video).to.exist; expect(data.native).to.not.exist; }); + + it('should build video impression if video params are present in adunit.mediaTypes instead of bid.params', function() { + let videoReq = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890', + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': 'adc36682-887c-41e9-9848-8b72c08332c0', + 'sizes': [ + [640, 480] + ], + 'bidId': '21b59b1353ba82', + 'bidderRequestId': '1a08245305e6dd', + 'auctionId': 'bad3a743-7491-4d19-9a96-b0a69dd24a67', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }] + let request = spec.buildRequests(videoReq, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.video).to.exist; + }); + + it('should build video impression with overwriting video params present in adunit.mediaTypes with bid.params', function() { + let videoReq = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5890', + 'video': { + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 5], + 'linearity': 1 + } + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': 'adc36682-887c-41e9-9848-8b72c08332c0', + 'sizes': [ + [640, 480] + ], + 'bidId': '21b59b1353ba82', + 'bidderRequestId': '1a08245305e6dd', + 'auctionId': 'bad3a743-7491-4d19-9a96-b0a69dd24a67', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }] + let request = spec.buildRequests(videoReq, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.video).to.exist; + expect(data.video.linearity).to.equal(1); + }); }); it('Request params dctr check', function () { @@ -2565,17 +2784,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', - kadfloor: '1.2', - pmzoneid: 'aabc, ddef', - kadpageurl: 'www.publisher.com', - yob: '1986', - gender: 'M', - lat: '12.3', - lon: '23.7', - wiid: '1234567890', - profId: '100', - verId: '200', - currency: 'AUD', dctr: 'key1=val1|key2=val2,!val3' }, placementCode: '/19968336/header-bid-tag-1', @@ -2621,11 +2829,11 @@ describe('PubMatic adapter', function () { dctr is found in adunit[0] */ - expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter - expect(data.site.ext.key_val).to.exist.and.to.equal(multipleBidRequests[0].params.dctr); + expect(data.imp[0].ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.imp[0].ext.key_val).to.exist.and.to.equal(multipleBidRequests[0].params.dctr); /* case 2 - - dctr not present in adunit[0] + dctr not present in adunit[0] but present in adunit[1] */ delete multipleBidRequests[0].params.dctr; request = spec.buildRequests(multipleBidRequests, { @@ -2633,7 +2841,9 @@ describe('PubMatic adapter', function () { }); data = JSON.parse(request.data); - expect(data.site.ext).to.not.exist; + expect(data.imp[0].ext).to.exist.and.to.deep.equal({}); + expect(data.imp[1].ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.imp[1].ext.key_val).to.exist.and.to.equal(multipleBidRequests[1].params.dctr); /* case 3 - dctr is present in adunit[0], but is not a string value @@ -2644,7 +2854,7 @@ describe('PubMatic adapter', function () { }); data = JSON.parse(request.data); - expect(data.site.ext).to.not.exist; + expect(data.imp[0].ext).to.exist.and.to.deep.equal({}); }); it('Request params deals check', function () { @@ -3028,6 +3238,155 @@ describe('PubMatic adapter', function () { let response = spec.interpretResponse(bidResponses, request); expect(response[0].renderer).to.not.exist; }); + + it('should assign mediaType by reading bid.ext.mediaType', function() { + let newvideoRequests = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5670', + 'video': { + 'mimes': ['video/mp4'], + 'skippable': true, + 'protocols': [1, 2, 5], + 'linearity': 1 + } + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }]; + let newvideoBidResponses = { + 'body': { + 'id': '1621441141473', + 'cur': 'USD', + 'customdata': 'openrtb1', + 'ext': { + 'buyid': 'myBuyId' + }, + 'seatbid': [{ + 'bid': [{ + 'id': '2c95df014cfe97', + 'impid': '2c95df014cfe97', + 'price': 4.2, + 'cid': 'test1', + 'crid': 'test2', + 'adm': "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1", + 'w': 0, + 'h': 0, + 'dealId': 'ASEA-MS-KLY-TTD-DESKTOP-ID-VID-6S-030420', + 'ext': { + 'BidType': 1 + } + }], + 'ext': { + 'buyid': 'myBuyId' + } + }] + }, + 'headers': {} + } + let newrequest = spec.buildRequests(newvideoRequests, { + auctionId: 'new-auction-id' + }); + let newresponse = spec.interpretResponse(newvideoBidResponses, newrequest); + expect(newresponse[0].mediaType).to.equal('video') + }) + + it('should assign mediaType even if bid.ext.mediaType does not exists', function() { + let newvideoRequests = [{ + 'bidder': 'pubmatic', + 'params': { + 'adSlot': 'SLOT_NHB1@728x90', + 'publisherId': '5670', + 'video': { + 'mimes': ['video/mp4'], + 'skippable': true, + 'protocols': [1, 2, 5], + 'linearity': 1 + } + }, + 'mediaTypes': { + 'video': { + 'playerSize': [ + [640, 480] + ], + 'protocols': [1, 2, 5], + 'context': 'instream', + 'mimes': ['video/flv'], + 'skippable': false, + 'skip': 1, + 'linearity': 2 + } + }, + 'adUnitCode': 'video1', + 'transactionId': '803e3750-0bbe-4ffe-a548-b6eca15087bf', + 'sizes': [ + [640, 480] + ], + 'bidId': '2c95df014cfe97', + 'bidderRequestId': '1fe59391566442', + 'auctionId': '3a4118ef-fb96-4416-b0b0-3cfc1cebc142', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }]; + let newvideoBidResponses = { + 'body': { + 'id': '1621441141473', + 'cur': 'USD', + 'customdata': 'openrtb1', + 'ext': { + 'buyid': 'myBuyId' + }, + 'seatbid': [{ + 'bid': [{ + 'id': '2c95df014cfe97', + 'impid': '2c95df014cfe97', + 'price': 4.2, + 'cid': 'test1', + 'crid': 'test2', + 'adm': "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1", + 'w': 0, + 'h': 0, + 'dealId': 'ASEA-MS-KLY-TTD-DESKTOP-ID-VID-6S-030420' + }], + 'ext': { + 'buyid': 'myBuyId' + } + }] + }, + 'headers': {} + } + let newrequest = spec.buildRequests(newvideoRequests, { + auctionId: 'new-auction-id' + }); + let newresponse = spec.interpretResponse(newvideoBidResponses, newrequest); + expect(newresponse[0].mediaType).to.equal('video') + }) }); describe('getUserSyncs', function() { diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index c3830d5cb46..6630cb0907c 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -254,7 +254,7 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ttl).to.equal(20); }); - it('Verify ttl/currency applied to bid', function () { + it('Verify ttl/currency/adomain applied to bid', function () { const request = spec.buildRequests(slotConfigs, bidderRequest); const ortbRequest = request.data; const ortbResponse = { @@ -264,7 +264,8 @@ describe('PulsePoint Adapter Tests', function () { price: 1.25, adm: 'This is an Ad#1', crid: 'Creative#123', - exp: 50 + exp: 50, + adomain: ['advertiser.com'] }, { impid: ortbRequest.imp[1].id, price: 1.25, @@ -282,11 +283,15 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ad).to.equal('This is an Ad#1'); expect(bid.ttl).to.equal(50); expect(bid.currency).to.equal('GBP'); + expect(bid.meta).to.not.be.null; + expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); const secondBid = bids[1]; expect(secondBid.cpm).to.equal(1.25); expect(secondBid.ad).to.equal('This is an Ad#2'); expect(secondBid.ttl).to.equal(20); expect(secondBid.currency).to.equal('GBP'); + expect(secondBid.meta).to.not.be.null; + expect(secondBid.meta.advertiserDomains).to.eql([]); }); it('Verify full passback', function () { @@ -778,4 +783,59 @@ describe('PulsePoint Adapter Tests', function () { const secondBid = bids[1]; expect(secondBid.vastXml).to.equal(''); }); + it('Verify bid floor', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].params.bidfloor = 1.05; + let request = spec.buildRequests(bidRequests, bidderRequest); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.05); + expect(ortbRequest.imp[1].bidfloor).to.be.undefined; + let floorArg = null; + // publisher uses the floor module + bidRequests[0].getFloor = (arg) => { + floorArg = arg; + return { floor: 1.25 }; + }; + bidRequests[1].getFloor = () => { + return { floor: 2.05 }; + }; + request = spec.buildRequests(bidRequests, bidderRequest); + ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.25); + expect(ortbRequest.imp[1].bidfloor).to.equal(2.05); + expect(floorArg).to.not.be.null; + expect(floorArg.mediaType).to.equal('banner'); + expect(floorArg.currency).to.equal('USD'); + expect(floorArg.size).to.equal('*'); + }); + it('Verify Video params on mediaTypes.video', function () { + const bidRequests = deepClone(videoSlotConfig); + bidRequests[0].mediaTypes = { + video: { + w: 600, + h: 400, + minduration: 15, + maxduration: 20, + startdelay: 10, + skip: 0, + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.null; + expect(ortbRequest.imp[0].banner).to.be.null; + expect(ortbRequest.imp[0].video.w).to.equal(600); + expect(ortbRequest.imp[0].video.h).to.equal(400); + expect(ortbRequest.imp[0].video.minduration).to.equal(15); + expect(ortbRequest.imp[0].video.maxduration).to.equal(20); + expect(ortbRequest.imp[0].video.startdelay).to.equal(10); + expect(ortbRequest.imp[0].video.skip).to.equal(0); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); }); diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 06d4af0756c..560206681ee 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -9,6 +9,15 @@ const REQUEST = { 'params': { zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 + }, + 'schain': { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'qwarry.com', + sid: '00001', + hp: 1 + }] } } @@ -24,7 +33,8 @@ const BIDDER_BANNER_RESPONSE = { 'creativeId': 1, 'netRevenue': true, 'winUrl': 'http://test.com', - 'format': 'banner' + 'format': 'banner', + 'adomain': ['test.com'] }] } @@ -40,7 +50,8 @@ const BIDDER_VIDEO_RESPONSE = { 'creativeId': 2, 'netRevenue': true, 'winUrl': 'http://test.com', - 'format': 'video' + 'format': 'video', + 'adomain': ['test.com'] }] } @@ -86,6 +97,7 @@ describe('qwarryBidAdapter', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') + expect(bidderRequest.data.schain).to.deep.contains({ver: '1.0', complete: 1, nodes: [{asi: 'qwarry.com', sid: '00001', hp: 1}]}) expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, sizes: [{ width: 100, height: 200 }, { width: 300, height: 400 }] }) expect(bidderRequest.data.gdprConsent).to.deep.contains({ consentRequired: true, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) @@ -109,6 +121,8 @@ describe('qwarryBidAdapter', function () { expect(result[0]).to.have.property('netRevenue').equal(true) expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('format').equal('banner') + expect(result[0].meta).to.exist.property('advertiserDomains') + expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) it('handles video request : should get correct bid response', function () { @@ -126,6 +140,8 @@ describe('qwarryBidAdapter', function () { expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('format').equal('video') expect(result[0]).to.have.property('vastXml').equal('vast') + expect(result[0].meta).to.exist.property('advertiserDomains') + expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) it('handles no bid response : should get empty array', function () { diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 91aa6b05e6e..c2082eb1e91 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/relaidoBidAdapter.js'; import * as utils from 'src/utils.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { getStorageManager } from '../../../src/storageManager.js'; const UUID_KEY = 'relaido_uuid'; @@ -59,7 +60,8 @@ describe('RelaidoAdapter', function () { uuid: relaido_uuid, vast: '', playerUrl: 'https://relaido/player.js', - syncUrl: 'https://relaido/sync.html' + syncUrl: 'https://relaido/sync.html', + adomain: ['relaido.co.jp', 'www.cmertv.co.jp'] } }; serverRequest = { @@ -276,6 +278,8 @@ describe('RelaidoAdapter', function () { expect(response.currency).to.equal(serverResponse.body.currency); expect(response.creativeId).to.equal(serverResponse.body.creativeId); expect(response.vastXml).to.equal(serverResponse.body.vast); + expect(response.meta.advertiserDomains).to.equal(serverResponse.body.adomain); + expect(response.meta.mediaType).to.equal(VIDEO); expect(response.ad).to.be.undefined; }); diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 72410b71fb2..00ae55823b0 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -149,8 +149,8 @@ describe('Richaudience adapter tests', function () { netRevenue: true, currency: 'USD', ttl: 300, - dealId: 'dealId' - + dealId: 'dealId', + adomain: 'richaudience.com' } }; @@ -165,7 +165,8 @@ describe('Richaudience adapter tests', function () { currency: 'USD', ttl: 300, vastXML: '', - dealId: 'dealId' + dealId: 'dealId', + adomain: 'richaudience.com' } }; @@ -600,6 +601,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'); }); it('no banner media response inestream', function () { @@ -628,6 +630,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'); }); it('no banner media response outstream', function () { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index c82c8300d2e..cb04ada59c7 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('Seedtag Adapter', function() { }) }) describe('when video slot has all mandatory params', function() { - it('should return true, when video mediatype object are correct.', function() { + it('should return true, when video context is instream', function () { const slotConfig = getSlotConfigs( { video: { @@ -72,6 +72,24 @@ describe('Seedtag Adapter', function() { const isBidRequestValid = spec.isBidRequestValid(slotConfig) expect(isBidRequestValid).to.equal(true) }) + + it('should return true, when video context is outstream', function () { + const slotConfig = getSlotConfigs( + { + video: { + context: 'outstream', + playerSize: [[600, 200]] + } + }, + { + publisherId: PUBLISHER_ID, + adUnitId: ADUNIT_ID, + placement: 'video' + } + ) + const isBidRequestValid = spec.isBidRequestValid(slotConfig) + expect(isBidRequestValid).to.equal(true) + }) }) }) describe('returns false', function() { @@ -137,7 +155,7 @@ describe('Seedtag Adapter', function() { ) expect(isBidRequestValid).to.equal(false) }) - it('is not instream ', function() { + it('is outstream ', function () { const isBidRequestValid = spec.isBidRequestValid( createVideoSlotConfig({ video: { @@ -146,7 +164,7 @@ describe('Seedtag Adapter', function() { } }) ) - expect(isBidRequestValid).to.equal(false) + expect(isBidRequestValid).to.equal(true) }) describe('order does not matter', function() { it('when video is not the first slot', function() { @@ -326,7 +344,8 @@ describe('Seedtag Adapter', function() { height: 90, mediaType: 'display', ttl: 360, - nurl: 'testurl.com/nurl' + nurl: 'testurl.com/nurl', + adomain: ['advertiserdomain.com'] } ], cookieSync: { url: '' } @@ -342,6 +361,7 @@ describe('Seedtag Adapter', function() { expect(bids[0].netRevenue).to.equal(true) expect(bids[0].ad).to.equal('content') expect(bids[0].nurl).to.equal('testurl.com/nurl') + expect(bids[0].meta.advertiserDomains).to.deep.equal(['advertiserdomain.com']) }) }) describe('the bid is a video', function() { @@ -374,6 +394,7 @@ describe('Seedtag Adapter', function() { expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) expect(bids[0].vastXml).to.equal('content') + expect(bids[0].meta.advertiserDomains).to.deep.equal([]) }) }) }) diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 5c8e01536dd..b8d91249ec3 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -436,7 +436,7 @@ describe('sharethrough adapter spec', function() { it('should include the bidfloor parameter if it is present in the bid request', function() { const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest['bidfloor'] = 0.50; + bidRequest['getFloor'] = () => ({ currency: 'USD', floor: 0.5 }); const builtBidRequest = spec.buildRequests([bidRequest])[0]; expect(builtBidRequest.data.bidfloor).to.eq(0.5); }); diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 1bc77fc9572..0abc8463d28 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1,200 +1,36 @@ import { spec } from 'modules/smaatoBidAdapter.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; -import {createEidsArray} from 'modules/userId/eids.js'; - -const imageAd = { - image: { - img: { - url: 'https://prebid/static/ad.jpg', - w: 320, - h: 50, - ctaurl: 'https://prebid/track/ctaurl' - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } -}; - -const richmediaAd = { - richmedia: { - mediadata: { - content: '

RICHMEDIA CONTENT

', - w: 800, - h: 600 - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } -}; +import { config } from 'src/config.js'; +import { createEidsArray } from 'modules/userId/eids.js'; const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; -const site = { - keywords: 'power tools,drills' -}; - -const user = { - keywords: 'a,b', - gender: 'M', - yob: 1984 -}; - -const openRtbBidResponse = (adType) => { - let adm = ''; - - switch (adType) { - case ADTYPE_IMG: - adm = JSON.stringify(imageAd); - break; - case ADTYPE_RICHMEDIA: - adm = JSON.stringify(richmediaAd); - break; - case ADTYPE_VIDEO: - adm = ''; - break; - default: - throw Error('Invalid AdType'); - } - - let resp = { - body: { - bidid: '04db8629-179d-4bcd-acce-e54722969006', - cur: 'USD', - ext: {}, - id: '5ebea288-f13a-4754-be6d-4ade66c68877', - seatbid: [ - { - bid: [ - { - 'adm': adm, - 'adomain': [ - 'smaato.com' - ], - 'bidderName': 'smaato', - 'cid': 'CM6523', - 'crid': 'CR69381', - 'dealid': '12345', - 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', - 'impid': '226416e6e6bf41', - 'iurl': 'https://prebid/iurl', - 'nurl': 'https://prebid/nurl', - 'price': 0.01, - 'w': 350, - 'h': 50 - } - ], - seat: 'CM6523' - } - ], - }, - headers: { - get: function (header) { - if (header === 'X-SMT-ADTYPE') { - return adType; - } - } - } - }; - return resp; -}; - const request = { method: 'POST', url: 'https://prebid.ad.smaato.net/oapi/prebid', data: '' }; -const interpretedBidsImg = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '
\"\"\"\"
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' - } - } -]; - -const interpretedBidsRichmedia = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '

RICHMEDIA CONTENT

\"\"\"\"
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' - } - } -]; - -const interpretedBidsVideo = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - vastXml: '', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'video' - } - } -]; +const REFERRER = 'http://example.com/page.html' +const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU' const defaultBidderRequest = { gdprConsent: { - consentString: 'HFIDUYFIUYIUYWIPOI87392DSU', + consentString: CONSENT_STRING, gdprApplies: true }, uspConsent: 'uspConsentString', refererInfo: { - referer: 'http://example.com/page.html', + referer: REFERRER, }, timeout: 1200 }; const minimalBidderRequest = { refererInfo: { - referer: 'http://example.com/page.html', + referer: REFERRER, } }; @@ -221,77 +57,6 @@ const singleBannerBidRequest = { bidderWinsCount: 0 }; -const singleVideoBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - -const combinedBannerAndVideoBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - }, - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - const inAppBidRequest = { bidder: 'smaato', params: { @@ -322,36 +87,10 @@ const inAppBidRequest = { bidderWinsCount: 0 }; -const userIdBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0, - userId: { - criteoId: '123456', - tdid: '89145' - }, - userIdAsEids: createEidsArray({ - criteoId: '123456', - tdid: '89145' - }) -}; +const extractPayloadOfFirstAndOnlyRequest = (reqs) => { + expect(reqs).to.have.length(1); + return JSON.parse(reqs[0].data); +} describe('smaatoBidAdapterTest', () => { describe('isBidRequestValid', () => { @@ -368,222 +107,557 @@ describe('smaatoBidAdapterTest', () => { }); describe('buildRequests', () => { - beforeEach(() => { - this.req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); - this.sandbox = sinon.sandbox.create(); - }); + describe('common', () => { + it('auction type is 1 (first price auction)', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - afterEach(() => { - this.sandbox.restore(); - }); + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.at).to.be.equal(1); + }) - it('can override endpoint', () => { - const overridenEndpoint = 'https://prebid/bidder'; - let bidRequest = utils.deepClone(singleBannerBidRequest); - utils.deepSetValue(bidRequest, 'params.endpoint', overridenEndpoint); - const actualEndpoint = spec.buildRequests([bidRequest], defaultBidderRequest).url; - expect(actualEndpoint).to.equal(overridenEndpoint); - }); + it('currency is US dollar', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - it('sends correct imps', () => { - expect(this.req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.cur).to.be.deep.equal(['USD']); + }) + + it('can override endpoint', () => { + const overridenEndpoint = 'https://prebid/bidder'; + const updatedBidRequest = utils.deepClone(singleBannerBidRequest); + utils.deepSetValue(updatedBidRequest, 'params.endpoint', overridenEndpoint); + + const reqs = spec.buildRequests([updatedBidRequest], defaultBidderRequest); + + expect(reqs).to.have.length(1); + expect(reqs[0].url).to.equal(overridenEndpoint); + }); + + it('sends correct imp', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + tagid: 'adspaceId' + } + ]); + }); + + it('sends correct site', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.id).to.exist.and.to.be.a('string'); + expect(req.site.domain).to.exist.and.to.be.a('string'); + expect(req.site.page).to.exist.and.to.be.a('string'); + expect(req.site.ref).to.equal(REFERRER); + expect(req.site.publisher.id).to.equal('publisherId'); + }) + + it('sends gdpr applies if exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.gdpr).to.equal(1); + expect(req.user.ext.consent).to.equal(CONSENT_STRING); + }); + + it('sends no gdpr applies if no gdpr exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.gdpr).to.not.exist; + expect(req.user.ext.consent).to.not.exist; + }); + + it('sends us_privacy if exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.us_privacy).to.equal('uspConsentString'); + }); + + it('sends tmax', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.tmax).to.equal(1200); + }); + + it('sends no us_privacy if no us_privacy exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.us_privacy).to.not.exist; + }); + + it('sends first party data', () => { + this.sandbox = sinon.sandbox.create() + this.sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + ortb2: { + site: { + keywords: 'power tools,drills' + }, + user: { + keywords: 'a,b', + gender: 'M', + yob: 1984 } - ] - }, - tagid: 'adspaceId' - } - ]) - }); + } + }; + return utils.deepAccess(config, key); + }); + + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.gender).to.equal('M'); + expect(req.user.yob).to.equal(1984); + expect(req.user.keywords).to.eql('a,b'); + expect(req.user.ext.consent).to.equal(CONSENT_STRING); + expect(req.site.keywords).to.eql('power tools,drills'); + expect(req.site.publisher.id).to.equal('publisherId'); + this.sandbox.restore(); + }); - it('sends correct site', () => { - expect(this.req.site.id).to.exist.and.to.be.a('string'); - expect(this.req.site.domain).to.exist.and.to.be.a('string'); - expect(this.req.site.page).to.exist.and.to.be.a('string'); - expect(this.req.site.ref).to.equal('http://example.com/page.html'); - expect(this.req.site.publisher.id).to.equal('publisherId'); - }) + it('has no user ids', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - it('sends gdpr applies if exists', () => { - expect(this.req.regs.ext.gdpr).to.equal(1); - expect(this.req.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.ext.eids).to.not.exist; + }); }); - it('sends no gdpr applies if no gdpr exists', () => { - let req_without_gdpr = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); - expect(req_without_gdpr.regs.ext.gdpr).to.not.exist; - expect(req_without_gdpr.user.ext.consent).to.not.exist; - }); + describe('multiple requests', () => { + it('build individual server request for each bid request', () => { + const bidRequest1 = utils.deepClone(singleBannerBidRequest); + const bidRequest1BidId = '1111'; + utils.deepSetValue(bidRequest1, 'bidId', bidRequest1BidId); + const bidRequest2 = utils.deepClone(singleBannerBidRequest); + const bidRequest2BidId = '2222'; + utils.deepSetValue(bidRequest2, 'bidId', bidRequest2BidId); - it('sends usp if exists', () => { - expect(this.req.regs.ext.us_privacy).to.equal('uspConsentString'); - }); + const reqs = spec.buildRequests([bidRequest1, bidRequest2], defaultBidderRequest); - it('sends tmax', () => { - expect(this.req.tmax).to.equal(1200); + expect(reqs).to.have.length(2); + expect(JSON.parse(reqs[0].data).imp[0].id).to.be.equal(bidRequest1BidId); + expect(JSON.parse(reqs[1].data).imp[0].id).to.be.equal(bidRequest2BidId); + }); }); - it('sends no usp if no usp exists', () => { - let req_without_usp = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); - expect(req_without_usp.regs.ext.us_privacy).to.not.exist; - }); + describe('buildRequests for video imps', () => { + it('sends correct video imps', () => { + const singleVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + }; - it('sends fp data', () => { - this.sandbox.stub(config, 'getConfig').callsFake(key => { - const config = { - ortb2: { - site, - user + const reqs = spec.buildRequests([singleVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + video: { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' } + ]); + }); + + it('allows combined banner and video imp in single bid request', () => { + const combinedBannerAndVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + }, + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 }; - return utils.deepAccess(config, key); + + const reqs = spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + video: { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' + } + ]); }); - let bidRequest = utils.deepClone(singleBannerBidRequest); - let req_fpd = JSON.parse(spec.buildRequests([bidRequest], defaultBidderRequest).data); - expect(req_fpd.user.gender).to.equal('M'); - expect(req_fpd.user.yob).to.equal(1984); - expect(req_fpd.user.keywords).to.eql('a,b'); - expect(req_fpd.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); - expect(req_fpd.site.keywords).to.eql('power tools,drills'); - expect(req_fpd.site.publisher.id).to.equal('publisherId'); }); - it('has no user ids', () => { - expect(this.req.user.ext.eids).to.not.exist; + describe('in-app requests', () => { + it('add geo and ifa info to device object', () => { + const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); + expect(req.device.ifa).to.equal('aDeviceId'); + }); + + it('when geo is missing, then add only ifa to device object', () => { + const inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); + delete inAppBidRequestWithoutGeo.params.app.geo + + const reqs = spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.equal('aDeviceId'); + }); + + it('add no specific device info if param does not exist', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.not.exist; + }); }); - }); - describe('buildRequests for video imps', () => { - it('sends correct video imps', () => { - let req = JSON.parse(spec.buildRequests([singleVideoBidRequest], defaultBidderRequest).data); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - video: { - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + describe('user ids in requests', () => { + it('user ids are added to user.ext.eids', () => { + const userIdBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' }, - tagid: 'adspaceId' - } - ]) - }); - it('allows combines banner and video imp in single bid request', () => { - let req = JSON.parse(spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest).data); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 - } - ] + mediaTypes: { + banner: { + sizes: [[300, 50]] + } }, - video: { - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + userId: { + criteoId: '123456', + tdid: '89145' }, - tagid: 'adspaceId' - } - ]) - }); - it('allows two imps in the same bid request', () => { - let req = JSON.parse(spec.buildRequests([singleBannerBidRequest, singleBannerBidRequest], defaultBidderRequest).data); - expect(req.imp).to.have.length(2); - }); - }); + userIdAsEids: createEidsArray({ + criteoId: '123456', + tdid: '89145' + }) + }; - describe('in-app requests', () => { - it('add geo and ifa info to device object', () => { - let req = JSON.parse(spec.buildRequests([inAppBidRequest], defaultBidderRequest).data); - expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); - expect(req.device.ifa).to.equal('aDeviceId'); - }); - it('add only ifa to device object', () => { - let inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); - delete inAppBidRequestWithoutGeo.params.app.geo - let req = JSON.parse(spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest).data); + const reqs = spec.buildRequests([userIdBidRequest], defaultBidderRequest); - expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.equal('aDeviceId'); - }); - it('add no specific device info if param does not exist', () => { - let req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); - expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.not.exist; + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.ext.eids).to.exist; + expect(req.user.ext.eids).to.have.length(2); + }); }); }); - describe('user ids in requests', () => { - it('user ids are added to user.ext.eids', () => { - let req = JSON.parse(spec.buildRequests([userIdBidRequest], defaultBidderRequest).data); - expect(req.user.ext.eids).to.exist; - expect(req.user.ext.eids).to.have.length(2); + describe('interpretResponse', () => { + const buildOpenRtbBidResponse = (adType) => { + let adm = ''; + + switch (adType) { + case ADTYPE_IMG: + adm = JSON.stringify({ + image: { + img: { + url: 'https://prebid/static/ad.jpg', + w: 320, + h: 50, + ctaurl: 'https://prebid/track/ctaurl' + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); + break; + case ADTYPE_RICHMEDIA: + adm = JSON.stringify({ + richmedia: { + mediadata: { + content: '

RICHMEDIA CONTENT

', + w: 800, + h: 600 + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); + break; + case ADTYPE_VIDEO: + adm = ''; + break; + default: + throw Error('Invalid AdType'); + } + + return { + body: { + bidid: '04db8629-179d-4bcd-acce-e54722969006', + cur: 'USD', + ext: {}, + id: '5ebea288-f13a-4754-be6d-4ade66c68877', + seatbid: [ + { + bid: [ + { + 'adm': adm, + 'adomain': [ + 'smaato.com' + ], + 'bidderName': 'smaato', + 'cid': 'CM6523', + 'crid': 'CR69381', + 'dealid': '12345', + 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', + 'impid': '226416e6e6bf41', + 'iurl': 'https://prebid/iurl', + 'nurl': 'https://prebid/nurl', + 'price': 0.01, + 'w': 350, + 'h': 50 + } + ], + seat: 'CM6523' + } + ], + }, + headers: { + get: function (header) { + if (header === 'X-SMT-ADTYPE') { + return adType; + } + } + } + }; + }; + + it('returns empty array on no bid responses', () => { + const response_with_empty_body = {body: {}} + + const bids = spec.interpretResponse(response_with_empty_body, request); + + expect(bids).to.be.empty }); - }); - describe('interpretResponse', () => { it('single image reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_IMG), request); - assert.deepStrictEqual(bids, interpretedBidsImg); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '
', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } + ]); }); + it('single richmedia reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_RICHMEDIA), request); - assert.deepStrictEqual(bids, interpretedBidsRichmedia); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '

RICHMEDIA CONTENT

', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } + ]); }); + it('single video reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_VIDEO), request); - assert.deepStrictEqual(bids, interpretedBidsVideo); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_VIDEO), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + vastXml: '', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'video' + } + } + ]); }); + it('ignores bid response with invalid ad type', () => { - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.headers.get = (header) => { if (header === 'X-SMT-ADTYPE') { return undefined; } } + const bids = spec.interpretResponse(resp, request); + expect(bids).to.be.empty }); + it('uses correct TTL when expire header exists', () => { const clock = sinon.useFakeTimers(); clock.tick(2000); - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.headers.get = (header) => { if (header === 'X-SMT-ADTYPE') { return ADTYPE_IMG; @@ -592,15 +666,27 @@ describe('smaatoBidAdapterTest', () => { return 2000 + (400 * 1000); } } + const bids = spec.interpretResponse(resp, request); + expect(bids[0].ttl).to.equal(400); + clock.restore(); }); + it('uses net revenue flag send from server', () => { - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.body.seatbid[0].bid[0].ext = {net: false}; + const bids = spec.interpretResponse(resp, request); + expect(bids[0].netRevenue).to.equal(false); }) }); + + describe('getUserSyncs', () => { + it('returns no pixels', () => { + expect(spec.getUserSyncs()).to.be.empty + }) + }) }); diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 3c871c6f88b..d0659865afa 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -108,14 +108,13 @@ describe('The smartx adapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should fail without bidfloor', function () { + it('should succeed with floor Module set', function () { delete bid.params.bidfloor; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should fail without bidfloorcur', function () { delete bid.params.bidfloorcur; - expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.floors = { + currency: 'EUR' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should fail with context outstream but no options set for outstream', function () { @@ -514,4 +513,101 @@ describe('The smartx adapter', function () { window.document.getElementById.restore(); }); }); + + describe('price floor module', function () { + var bid, + bidRequestObj; + + beforeEach(function () { + bid = getValidBidObject(); + bidRequestObj = { + refererInfo: { + referer: 'prebid.js' + } + }; + delete bid.params.bidfloor; + }); + + it('obtain floor from getFloor', function () { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 3.21 + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 3.21); + }); + + it('obtain floor from params', function() { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 3.21 + }; + }; + bid.params.bidfloor = 0.64; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0.64); + }); + + it('check currency USD', function() { + bid.getFloor = () => { + return { + currency: 'USD', + floor: 1.23 + }; + }; + bid.params.bidfloorcur = 'USD' + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloorcur', 'USD'); + expect(payload.data.imp).to.have.property('bidfloor', 1.23); + }); + + it('check defaut currency EUR', function() { + delete bid.params.bidfloorcur; + + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 4.56 + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloorcur', 'EUR'); + expect(payload.data.imp).to.have.property('bidfloor', 4.56); + }); + + it('bad floor value', function() { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 'bad' + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + + it('empty floor object', function() { + bid.getFloor = () => { + return {}; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + + it('undefined floor result', function() { + bid.getFloor = () => {}; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + }); }) diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 0314ffb71c1..79bfa86b216 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec, _getPlatform } from 'modules/sonobiBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import {userSync} from '../../../src/userSync.js'; import { config } from 'src/config.js'; +import * as utils from '../../../src/utils.js'; describe('SonobiBidAdapter', function () { const adapter = newBidder(spec) @@ -239,9 +240,12 @@ describe('SonobiBidAdapter', function () { describe('.buildRequests', function () { beforeEach(function() { sinon.stub(userSync, 'canBidderRegisterSync'); + sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') + .onFirstCall().returns({gptSlot: '/123123/gpt_publisher/adunit-code-3', divId: 'adunit-code-3-div-id'}) }); afterEach(function() { userSync.canBidderRegisterSync.restore(); + utils.getGptSlotInfoForAdUnitCode.restore(); }); let bidRequest = [{ 'schain': { @@ -271,6 +275,24 @@ describe('SonobiBidAdapter', function () { 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], 'bidId': '30b31c1838de1f', + ortb2Imp: { + ext: { + data: { + pbadslot: '/123123/gpt_publisher/adunit-code-1' + } + } + } + }, + { + 'bidder': 'sonobi', + 'params': { + 'placement_id': '1a2b3c4d5e6f1a2b3c4e', + 'sizes': [[300, 250], [300, 600]], + 'referrer': 'overrides_top_window_location' + }, + 'adUnitCode': 'adunit-code-3', + 'sizes': [[120, 600], [300, 600], [160, 600]], + 'bidId': '30b31c1838de1d', }, { 'bidder': 'sonobi', @@ -285,8 +307,9 @@ describe('SonobiBidAdapter', function () { }]; let keyMakerData = { - '30b31c1838de1f': '1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25', - '/7780971/sparks_prebid_LB|30b31c1838de1e': '300x250,300x600', + '30b31c1838de1f': '1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25|gpid=/123123/gpt_publisher/adunit-code-1', + '30b31c1838de1d': '1a2b3c4d5e6f1a2b3c4e|300x250,300x600|gpid=/123123/gpt_publisher/adunit-code-3', + '/7780971/sparks_prebid_LB|30b31c1838de1e': '300x250,300x600|gpid=/7780971/sparks_prebid_LB', }; let bidderRequests = { @@ -629,6 +652,7 @@ describe('SonobiBidAdapter', function () { 'sbi_crid': '1234abcd', 'sbi_aid': '30292e432662bd5f86d90774b944b039', 'sbi_mouse': 1.07, + 'sbi_adomain': 'sonobi.com' }, '30b31c1838de1e': { 'sbi_size': '300x250', @@ -636,7 +660,9 @@ describe('SonobiBidAdapter', function () { 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.25, 'sbi_dozer': 'dozerkey', - 'sbi_ct': 'video' + 'sbi_ct': 'video', + 'sbi_adomain': 'sonobi.com' + }, '/7780971/sparks_prebid_LB_OUTSTREAM|30b31c1838de1g': { 'sbi_size': '300x600', @@ -644,6 +670,8 @@ describe('SonobiBidAdapter', function () { 'sbi_crid': '1234abcd', 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.07, + 'sbi_adomain': 'sonobi.com' + }, '/7780971/sparks_prebid_LB|30b31c1838de1g': {}, '30b31c1838de1zzzz': { @@ -654,6 +682,7 @@ describe('SonobiBidAdapter', function () { sbi_mouse: 1.25, sbi_size: 'preroll', 'sbi_crid': 'somecrid', + 'sbi_adomain': 'sonobi.com' } @@ -680,7 +709,10 @@ describe('SonobiBidAdapter', function () { 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'aid': '30292e432662bd5f86d90774b944b039' + 'aid': '30292e432662bd5f86d90774b944b039', + meta: { + advertiserDomains: ['sonobi.com'] + } }, { 'requestId': '30b31c1838de1e', @@ -694,7 +726,10 @@ describe('SonobiBidAdapter', function () { 'currency': 'USD', 'dealId': 'dozerkey', 'aid': '30292e432662bd5f86d90774b944b038', - 'mediaType': 'video' + 'mediaType': 'video', + meta: { + advertiserDomains: ['sonobi.com'] + } }, { 'requestId': '30b31c1838de1g', @@ -706,7 +741,10 @@ describe('SonobiBidAdapter', function () { 'creativeId': '1234abcd', 'netRevenue': true, 'currency': 'USD', - 'aid': '30292e432662bd5f86d90774b944b038' + 'aid': '30292e432662bd5f86d90774b944b038', + meta: { + advertiserDomains: ['sonobi.com'] + } }, { 'requestId': '30b31c1838de1zzzz', @@ -721,7 +759,10 @@ describe('SonobiBidAdapter', function () { 'dealId': 'dozerkey', 'aid': 'force_1550072228_da1c5d030cb49150c5db8a2136175755', 'mediaType': 'video', - renderer: () => {} + renderer: () => {}, + meta: { + advertiserDomains: ['sonobi.com'] + } }, ]; diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index 2e1d65f0533..fab487cb65f 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -47,7 +47,7 @@ describe('Sublime Adapter', function() { }); describe('isBidRequestValid', function() { - let bid = { + const bid = { bidder: 'sublime', params: { zoneId: 24549, @@ -60,14 +60,14 @@ describe('Sublime Adapter', function() { }); it('should return false when required params are not passed', function() { - let bid = Object.assign({}, bid); + const bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); describe('buildRequests', function() { - let bidRequests = [ + const bidRequests = [ { bidder: 'sublime', adUnitCode: 'sublime_code', @@ -90,7 +90,7 @@ describe('Sublime Adapter', function() { } ]; - let bidderRequest = { + const bidderRequest = { gdprConsent: { consentString: 'EOHEIRCOUCOUIEHZIOEIU-TEST', gdprApplies: true @@ -101,7 +101,7 @@ describe('Sublime Adapter', function() { } }; - let request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest); it('should have a post method', function() { expect(request[0].method).to.equal('POST'); @@ -121,7 +121,7 @@ describe('Sublime Adapter', function() { }); describe('buildRequests: default arguments', function() { - let bidRequests = [{ + const bidRequests = [{ bidder: 'sublime', adUnitCode: 'sublime_code', bidId: 'abc1234', @@ -132,7 +132,7 @@ describe('Sublime Adapter', function() { } }]; - let request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); it('should have an url that match the default endpoint', function() { expect(request[0].url).to.equal('https://pbjs.sskzlabs.com/bid'); @@ -140,7 +140,7 @@ describe('Sublime Adapter', function() { }); describe('interpretResponse', function() { - let serverResponse = { + const serverResponse = { 'request_id': '3db3773286ee59', 'sspname': 'foo', 'cpm': 0.5, @@ -155,7 +155,7 @@ describe('Sublime Adapter', function() { } }; - let expectedResponse = [ + const expectedResponse = [ { requestId: '', cpm: 0.5, @@ -167,23 +167,23 @@ describe('Sublime Adapter', function() { sspname: 'foo', netRevenue: true, ttl: 600, - pbav: '0.7.1', + pbav: '0.7.2', ad: '', }, ]; - let result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({body: serverResponse}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('should get correct default size for 1x1', function() { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', 'cpm': 0.5, 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -197,9 +197,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({body: serverResponse}, bidRequest); + const result = spec.interpretResponse({body: serverResponse}, bidRequest); - let expectedResponse = { + const expectedResponse = { requestId: 'xyz654_2', cpm: 0.5, width: 1, @@ -210,7 +210,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.1', + pbav: '0.7.2', sspname: 'sublime' }; @@ -218,24 +218,24 @@ describe('Sublime Adapter', function() { }); it('should return bid empty response', function () { - let serverResponse = ''; - let bidRequest = {}; + const serverResponse = ''; + const bidRequest = {}; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = []; + const expectedResponse = []; expect(result).to.deep.equal(expectedResponse); }); it('should return bid with default value in response', function () { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -249,9 +249,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = { + const expectedResponse = { requestId: 'xyz654_2', cpm: 0, width: 1, @@ -263,20 +263,20 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.1', + pbav: '0.7.2', }; expect(result[0]).to.deep.equal(expectedResponse); }); it('should return empty bid response because of timeout', function () { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'timeout': true, 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -290,9 +290,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = []; + const expectedResponse = []; expect(result).to.deep.equal(expectedResponse); @@ -300,11 +300,28 @@ describe('Sublime Adapter', function() { spec.onTimeout(result); }); }); + + it('should add advertiserDomains', function() { + const responseWithAdvertiserDomains = utils.deepClone(serverResponse); + responseWithAdvertiserDomains.advertiserDomains = ['a_sublime_adomain']; + + const bidRequest = { + bidder: 'sublime', + params: { + zoneId: 456, + } + }; + + const result = spec.interpretResponse({ body: responseWithAdvertiserDomains }, bidRequest); + + expect(Object.keys(result[0].meta)).to.include.members(['advertiserDomains']); + expect(Object.keys(result[0].meta.advertiserDomains)).to.deep.equal([]); + }); }); describe('onBidWon', function() { let sandbox; - let bid = { foo: 'bar' }; + const bid = { foo: 'bar' }; beforeEach(function () { sandbox = sinon.sandbox.create(); diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 688f1c2c090..5f3633ec311 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -191,6 +191,62 @@ describe('synacormediaBidAdapter ', function () { uspConsent: '1YYY' }; + let validBidRequestWithUserIds = { + bidId: '9876abcd', + sizes: [[300, 250], [300, 600]], + params: { + seatId: 'prebid', + tagId: '1234', + bidfloor: '0.50' + }, + userIdAsEids: [ + { + source: 'pubcid.org', + uids: [{ + id: 'cid0032l2344jskdsl3', + atype: 1 + }] + }, + { + source: 'liveramp.com', + uids: [{ + id: 'lrv39010k42dl', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + }, + { + source: 'neustar.biz', + uids: [{ + id: 'neustar809-044-23njhwer3', + atype: 1 + }] + } + ] + }; + + let expectedEids = [ + { + source: 'pubcid.org', + uids: [{ + id: 'cid0032l2344jskdsl3', + atype: 1 + }] + }, + { + source: 'liveramp.com', + uids: [{ + id: 'lrv39010k42dl', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + } + ]; + let expectedDataImp1 = { banner: { format: [ @@ -631,7 +687,21 @@ describe('synacormediaBidAdapter ', function () { expect(req.data.id).to.equal('xyz123'); expect(req.data.regs.ext.us_privacy).to.equal('1YYY'); expect(req.data.imp).to.eql([expectedDataImp1]); - }) + }); + it('should contain user object when user ids are present in the bidder request', function () { + let req = spec.buildRequests([validBidRequestWithUserIds], bidderRequest); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.user).be.an('object'); + expect(req.data.user).to.have.property('ext'); + expect(req.data.user.ext).to.have.property('eids'); + expect(req.data.user.ext.eids).to.eql(expectedEids); + expect(req.data.imp).to.eql([expectedDataImp1]); + }); }); describe('Bid Requests with placementId should be backward compatible ', function () { diff --git a/test/spec/modules/trionBidAdapter_spec.js b/test/spec/modules/trionBidAdapter_spec.js index ae329b4a028..0fc03caa563 100644 --- a/test/spec/modules/trionBidAdapter_spec.js +++ b/test/spec/modules/trionBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import * as utils from 'src/utils.js'; import {spec, acceptPostMessage, getStorageData, setStorageData} from 'modules/trionBidAdapter.js'; +import {deepClone} from 'src/utils.js'; const CONSTANTS = require('src/constants.json'); const adloader = require('src/adloader'); @@ -316,6 +317,14 @@ describe('Trion adapter tests', function () { expect(response[0].cpm).to.equal(bidCpm); TRION_BID_RESPONSE.result.cpm = 100; }); + + it('advertiserDomains is included when sent by server', function () { + TRION_BID_RESPONSE.result.adomain = ['test_adomain']; + let response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + expect(Object.keys(response[0].meta)).to.include.members(['advertiserDomains']); + expect(response[0].meta.advertiserDomains).to.deep.equal(['test_adomain']); + delete TRION_BID_RESPONSE.result.adomain; + }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index c0155f87ab6..f53116f60a4 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -661,7 +661,7 @@ describe('TrustXAdapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300, 'adomain': ['somedomain.com']}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, @@ -699,6 +699,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, } ]; @@ -756,6 +759,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, }, { 'requestId': '4dff80cc4ee346', @@ -769,6 +775,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '5703af74d0472a', @@ -782,6 +791,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -909,6 +921,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '4e111f1b66e4', @@ -922,6 +937,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '26d6f897b516', @@ -935,6 +953,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '326bde7fbf69', @@ -948,6 +969,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1009,6 +1033,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '57b2ebe70e16', @@ -1022,6 +1049,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1082,6 +1112,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1161,6 +1194,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1178,6 +1214,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1195,6 +1234,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index bee420f40d4..5899554244b 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -25,8 +25,7 @@ const userId = { const validBannerBidReq = { bidder: BIDDER_CODE, params: { - adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D', - bidfloor: 1.0 + adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, sizes: [[300, 250]], bidId: '263be71e91dd9d', @@ -175,6 +174,41 @@ describe('ucfunnel Adapter', function () { expect(data.w).to.equal(width); expect(data.h).to.equal(height); }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.getFloor = function() { + return { + currency: 'USD', + floor: 2.02 + } + }; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.02); + }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.params.bidfloor = 2.01; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.01); + }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.getFloor = function() { + return { + currency: 'USD', + floor: 2.02 + } + }; + bid.params.bidfloor = 2.01; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.01); + }); }); describe('interpretResponse', function () { diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index aeb9f56c851..70d09513f27 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -276,6 +276,7 @@ describe('UnderdogMedia adapter', function () { mids: [ { ad_code_html: 'ad_code_html', + advertiser_domains: ['domain1'], cpm: 2.5, height: '600', mid: '32634', @@ -300,6 +301,7 @@ describe('UnderdogMedia adapter', function () { expect(bids).to.be.lengthOf(2); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['domain1']) expect(bids[0].bidderCode).to.equal('underdogmedia'); expect(bids[0].cpm).to.equal(2.5); expect(bids[0].width).to.equal('160'); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 95bba079fa2..420d2ddce91 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -234,9 +234,10 @@ describe('User ID', function () { }); }); }); - // Because the cookie exists already, there should be no setCookie call by default; the only setCookie call is - // to store consent data - expect(coreStorage.setCookie.callCount).to.equal(1); + // Because the consent cookie doesn't exist yet, we'll have two setCookie calls: + // 1) for the consent cookie + // 2) from the getId() call that results in a new call to store the results + expect(coreStorage.setCookie.callCount).to.equal(2); }); it('Extend cookie', function () { @@ -2407,7 +2408,7 @@ describe('User ID', function () { }); // check MockId data was copied to bid expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('123456778'); + expect(bid.userId.mid).to.equal('1234'); // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); @@ -2785,7 +2786,7 @@ describe('User ID', function () { sharedAfterFunction(); }); - it('does not call getId if no stored consent data and refresh is not needed', function () { + it('calls getId if no stored consent data and refresh is not needed', function () { coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); @@ -2796,9 +2797,9 @@ describe('User ID', function () { innerAdUnits = config.adUnits }, {adUnits}); - sinon.assert.notCalled(mockGetId); + sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); - sinon.assert.calledOnce(mockExtendId); + sinon.assert.notCalled(mockExtendId); }); it('calls getId if no stored consent data but refresh is needed', function () { diff --git a/test/spec/modules/viewdeosDXBidAdapter_spec.js b/test/spec/modules/viewdeosDXBidAdapter_spec.js index f9bee1b0efe..31df9244ada 100644 --- a/test/spec/modules/viewdeosDXBidAdapter_spec.js +++ b/test/spec/modules/viewdeosDXBidAdapter_spec.js @@ -39,9 +39,9 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 - } - ] + 'cpm': 0.9, + 'adomain': ['a.com'] + }] }; const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; @@ -123,7 +123,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 3600, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -136,7 +139,10 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 3600, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } }]; describe('viewdeosDXBidAdapter', function () { diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index c0e67ee1997..fa902480cd7 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -460,7 +460,7 @@ describe('VisxAdapter', function () { ]; const response = Object.assign({}, responses[0]); - Object.assign(response.bid[0], {'cur': 'PLN'}); + response.bid = [Object.assign({}, response.bid[0], {'cur': 'PLN'})]; const result = spec.interpretResponse({'body': {'seatbid': [response]}}, request); expect(result).to.deep.equal(expectedResponse); getConfigStub.restore(); @@ -756,32 +756,55 @@ describe('VisxAdapter', function () { const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); expect(result).to.deep.equal(expectedResponse); }); - }); - describe('check trackers', function () { - beforeEach(function () { - sinon.stub(utils, 'triggerPixel'); - }); - - afterEach(function () { - utils.triggerPixel.restore(); - }); - - it('onSetTargeting', function () { - const requestId = '111'; - spec.onSetTargeting({ requestId }); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/pending?requestId=' + requestId)).to.equal(true); - }); - - it('onBidWon', function () { - const requestId = '111'; - spec.onBidWon({ requestId }); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/win?requestId=' + requestId)).to.equal(true); - }); - it('onTimeout', function () { - const data = { timeout: 3000, bidId: '23423', params: { uid: 1 } }; - spec.onTimeout(data); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/bid_timeout?data=' + JSON.stringify(data))).to.equal(true); + it('should get right ext data in bid response', function () { + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const pendingUrl = 'https://t.visx.net/track/pending/123123123'; + const winUrl = 'https://t.visx.net/track/win/53245341'; + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + 'ext': { + 'events': { + 'pending': pendingUrl, + 'win': winUrl + } + } + } + ]; + const serverResponse = Object.assign({}, responses[0]); + serverResponse.bid = [Object.assign({}, {ext: { + prebid: { + events: { + 'pending': pendingUrl, + 'win': winUrl + } + } + }}, serverResponse.bid[0])]; + const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); + expect(result).to.deep.equal(expectedResponse); }); }); describe('check trackers', function () { @@ -794,15 +817,17 @@ describe('VisxAdapter', function () { }); it('onSetTargeting', function () { - const requestId = '111'; - spec.onSetTargeting({ requestId }); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/pending?requestId=' + requestId)).to.equal(true); + const trackUrl = 'https://t.visx.net/track/pending/123123123'; + const bid = { ext: { events: { pending: trackUrl } } }; + spec.onSetTargeting(bid); + expect(utils.triggerPixel.calledOnceWith(trackUrl)).to.equal(true); }); it('onBidWon', function () { - const requestId = '111'; - spec.onBidWon({ requestId }); - expect(utils.triggerPixel.calledOnceWith('https://t.visx.net/track/win?requestId=' + requestId)).to.equal(true); + const trackUrl = 'https://t.visx.net/track/win/123123123'; + const bid = { ext: { events: { win: trackUrl } } }; + spec.onBidWon(bid); + expect(utils.triggerPixel.calledOnceWith(trackUrl)).to.equal(true); }); it('onTimeout', function () { diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js index c6221cba9e5..6906c7dbba4 100644 --- a/test/spec/modules/voxBidAdapter_spec.js +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -243,7 +243,8 @@ describe('VOX Adapter', function() { content: 'html', width: 100, height: 100 - } + }, + advertiserDomains: ['voxexchange.io'] } ] } @@ -257,6 +258,7 @@ describe('VOX Adapter', function() { expect(bids[0].height).to.equal(100) expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].meta.advertiserDomains).to.deep.equal(['voxexchange.io']) expect(typeof bids[0].ad).to.equal('string') }) it('should return a In-Image bid', function() { diff --git a/test/spec/modules/vuukleBidAdapter_spec.js b/test/spec/modules/vuukleBidAdapter_spec.js index fdca4085c8e..17353a40b85 100644 --- a/test/spec/modules/vuukleBidAdapter_spec.js +++ b/test/spec/modules/vuukleBidAdapter_spec.js @@ -41,7 +41,8 @@ describe('vuukleBidAdapterTests', function() { 'width': 300, 'height': 250, 'creative_id': '12345', - 'ad': 'test ad' + 'ad': 'test ad', + 'adomain': ['example.com'] } }; @@ -55,5 +56,6 @@ describe('vuukleBidAdapterTests', function() { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.creativeId).to.equal('12345'); + expect(bid.meta.advertiserDomains).to.deep.equal(['example.com']); }); }); diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 3b317f88dc6..77542480c6c 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -331,13 +331,82 @@ describe('YieldmoAdapter', function () { }); describe('Instream video:', function () { + let videoBid; + const buildVideoBidAndGetVideoParam = () => build([videoBid])[0].data.imp[0].video; + + beforeEach(() => { + videoBid = mockVideoBid(); + }); + it('should attempt to send video bid requests to the endpoint via POST', function () { - const requests = build([mockVideoBid()]); + const requests = build([videoBid]); expect(requests.length).to.equal(1); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.be.equal(VIDEO_ENDPOINT); }); + it('should add mediaTypes.video prop to the imp.video prop', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 40; + expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); + }); + + it('should override mediaTypes.video prop if params.video prop is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 50; + utils.deepAccess(videoBid, 'params.video')['minduration'] = 40; + expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); + }); + + it('should add mediaTypes.video.mimes prop to the imp.video', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = ['video/mp4']; + expect(buildVideoBidAndGetVideoParam().minduration).to.deep.equal(['video/mp4']); + }); + + it('should override mediaTypes.video.mimes prop if params.video.mimes is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['mimes'] = ['video/mp4']; + utils.deepAccess(videoBid, 'params.video')['mimes'] = ['video/mkv']; + expect(buildVideoBidAndGetVideoParam().mimes).to.deep.equal(['video/mkv']); + }); + + describe('video.skip state check', () => { + it('should not set video.skip if neither *.video.skip nor *.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; + utils.deepAccess(videoBid, 'params.video')['skippable'] = false; + expect(buildVideoBidAndGetVideoParam().skip).to.undefined; + }); + + it('should set video.skip=1 if mediaTypes.video.skip is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skip'] = 1; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if params.video.skip is present', function () { + utils.deepAccess(videoBid, 'params.video')['skip'] = 1; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'params.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; + utils.deepAccess(videoBid, 'params.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should not set video.skip if params.video.skippable is false', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = true; + utils.deepAccess(videoBid, 'params.video')['skippable'] = false; + expect(buildVideoBidAndGetVideoParam().skip).to.undefined; + }); + }); + it('should process floors module if available', function () { const requests = build([ mockVideoBid({...mockGetFloor(3.99)}), diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index bdfc64c3234..4602e2d2b77 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -1,6 +1,29 @@ -import { spec } from '../../../modules/zetaSspBidAdapter.js' +import {spec} from '../../../modules/zetaSspBidAdapter.js' describe('Zeta Ssp Bid Adapter', function() { + const eids = [ + { + 'source': 'example.com', + 'uids': [ + { + 'id': 'someId1', + 'atype': 1 + }, + { + 'id': 'someId2', + 'atype': 1 + }, + { + 'id': 'someId3', + 'atype': 2 + } + ], + 'ext': { + 'foo': 'bar' + } + } + ]; + const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -23,10 +46,11 @@ describe('Zeta Ssp Bid Adapter', function() { sid: 'publisherId' }, test: 1 - } + }, + userIdAsEids: eids }]; - it('Test the bid validation function', function() { + it('Test the bid validation function', function () { const validBid = spec.isBidRequestValid(bannerRequest[0]); const invalidBid = spec.isBidRequestValid(null); @@ -34,6 +58,12 @@ describe('Zeta Ssp Bid Adapter', function() { expect(invalidBid).to.be.false; }); + it('Test provide eids', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.user.ext.eids).to.eql(eids); + }); + it('Test the request processing function', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); expect(request).to.not.be.empty; @@ -82,4 +112,28 @@ describe('Zeta Ssp Bid Adapter', function() { expect(bid.requestId).to.equal(receivedBid.impid); expect(bid.meta.advertiserDomains).to.equal(receivedBid.adomain); }); + + it('Different cases for user syncs', function () { + const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; + const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; + + const sync1 = spec.getUserSyncs({iframeEnabled: true})[0]; + expect(sync1.type).to.equal('iframe'); + expect(sync1.url).to.include(USER_SYNC_URL_IFRAME); + + const sync2 = spec.getUserSyncs({iframeEnabled: false})[0]; + expect(sync2.type).to.equal('image'); + expect(sync2.url).to.include(USER_SYNC_URL_IMAGE); + + const sync3 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true})[0]; + expect(sync3.type).to.equal('iframe'); + expect(sync3.url).to.include(USER_SYNC_URL_IFRAME); + expect(sync3.url).to.include('&gdpr='); + + const sync4 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true}, 'test')[0]; + expect(sync4.type).to.equal('iframe'); + expect(sync4.url).to.include(USER_SYNC_URL_IFRAME); + expect(sync4.url).to.include('&gdpr='); + expect(sync4.url).to.include('&us_privacy='); + }); }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 805eae9e3bc..0bd3380f737 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -199,6 +199,10 @@ describe('Unit: Prebid Module', function () { configObj.setConfig({ useBidCache: false }); }); + after(function() { + auctionManager.clearAllAuctions(); + }); + describe('getAdserverTargetingForAdUnitCodeStr', function () { beforeEach(function () { resetAuction(); @@ -1062,6 +1066,8 @@ describe('Unit: Prebid Module', function () { var adResponse = {}; var spyLogError = null; var spyLogMessage = null; + var spyLogWarn = null; + var spyAddWinningBid; var inIframe = true; var triggerPixelStub; @@ -1100,6 +1106,8 @@ describe('Unit: Prebid Module', function () { spyLogError = sinon.spy(utils, 'logError'); spyLogMessage = sinon.spy(utils, 'logMessage'); + spyLogWarn = sinon.spy(utils, 'logWarn'); + spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); inIframe = true; sinon.stub(utils, 'inIframe').callsFake(() => inIframe); @@ -1110,8 +1118,10 @@ describe('Unit: Prebid Module', function () { auction.getBidsReceived = getBidResponses; utils.logError.restore(); utils.logMessage.restore(); + utils.logWarn.restore(); utils.inIframe.restore(); triggerPixelStub.restore(); + spyAddWinningBid.restore(); }); it('should require doc and id params', function () { @@ -1218,6 +1228,117 @@ describe('Unit: Prebid Module', function () { sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, burl); }); + + it('should call addWinningBid', function () { + pushBidResponseToAuction({ + ad: "" + }); + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + var message = 'Calling renderAd with adId :' + bidId; + sinon.assert.calledWith(spyLogMessage, message); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + }); + + it('should warn stale rendering', function () { + var message = 'Calling renderAd with adId :' + bidId; + var warning = `Ad id ${bidId} has been rendered before`; + var onWonEvent = sinon.stub(); + var onStaleEvent = sinon.stub(); + + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + + pushBidResponseToAuction({ + ad: "" + }); + + // First render should pass with no warning and added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.neverCalledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.notCalled(onStaleEvent); + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + // Reset call history for spies and stubs + spyLogMessage.resetHistory(); + spyLogWarn.resetHistory(); + spyAddWinningBid.resetHistory(); + onWonEvent.resetHistory(); + onStaleEvent.resetHistory(); + + // Second render should have a warning but still added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.calledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.calledWith(onStaleEvent, adResponse); + + // Clean up + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + }); + + it('should stop stale rendering', function () { + var message = 'Calling renderAd with adId :' + bidId; + var warning = `Ad id ${bidId} has been rendered before`; + var onWonEvent = sinon.stub(); + var onStaleEvent = sinon.stub(); + + // Setting suppressStaleRender to true explicitly + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + + pushBidResponseToAuction({ + ad: "" + }); + + // First render should pass with no warning and added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.neverCalledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.notCalled(onStaleEvent); + + // Reset call history for spies and stubs + spyLogMessage.resetHistory(); + spyLogWarn.resetHistory(); + spyAddWinningBid.resetHistory(); + onWonEvent.resetHistory(); + onStaleEvent.resetHistory(); + + // Second render should have a warning and do not proceed further + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.calledWith(spyLogWarn, warning); + + sinon.assert.notCalled(spyAddWinningBid); + + sinon.assert.notCalled(onWonEvent); + sinon.assert.calledWith(onStaleEvent, adResponse); + + // Clean up + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + configObj.setConfig({'auctionOptions': {}}); + }); }); describe('requestBids', function () { diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 566154f0003..eca00e8c8fa 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,8 +1,18 @@ import { - _sendAdToCreative -} from '../../../src/secureCreatives.js'; -import { expect } from 'chai'; + _sendAdToCreative, receiveMessage +} from 'src/secureCreatives.js'; import * as utils from 'src/utils.js'; +import {getAdUnits, getBidRequests, getBidResponses} from 'test/fixtures/fixtures.js'; +import {auctionManager} from 'src/auctionManager.js'; +import * as auctionModule from 'src/auction.js'; +import * as native from 'src/native.js'; +import {fireNativeTrackers, getAllAssetsMessage} from 'src/native.js'; +import events from 'src/events.js'; +import { config as configObj } from 'src/config.js'; + +import { expect } from 'chai'; + +var CONSTANTS = require('src/constants.json'); describe('secureCreatives', () => { describe('_sendAdToCreative', () => { @@ -42,4 +52,319 @@ describe('secureCreatives', () => { window.apntag = oldapntag; }); }); + + describe('receiveMessage', function() { + const bidId = 1; + const warning = `Ad id ${bidId} has been rendered before`; + let auction; + let adResponse = {}; + let spyAddWinningBid; + let spyLogWarn; + let stubFireNativeTrackers; + let stubGetAllAssetsMessage; + let stubEmit; + + function pushBidResponseToAuction(obj) { + adResponse = Object.assign({ + auctionId: 1, + adId: bidId, + width: 300, + height: 250, + renderer: null + }, obj); + auction.getBidsReceived = function() { + let bidsReceived = getBidResponses(); + bidsReceived.push(adResponse); + return bidsReceived; + } + auction.getAuctionId = () => 1; + } + + function resetAuction() { + $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: false }); + auction.getBidRequests = getBidRequests; + auction.getBidsReceived = getBidResponses; + auction.getAdUnits = getAdUnits; + auction.getAuctionStatus = function() { return auctionModule.AUCTION_COMPLETED } + } + + function resetHistories(...others) { + [ + spyAddWinningBid, + spyLogWarn, + stubFireNativeTrackers, + stubGetAllAssetsMessage, + stubEmit + ].forEach(s => s.resetHistory()); + + if (others && others.length > 0) { others.forEach(s => s.resetHistory()); } + } + + before(function() { + const adUnits = getAdUnits(); + const adUnitCodes = getAdUnits().map(unit => unit.code); + const bidsBackHandler = function() {}; + const timeout = 2000; + auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout: timeout}); + resetAuction(); + }); + + after(function() { + auctionManager.clearAllAuctions(); + }); + + beforeEach(function() { + spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); + spyLogWarn = sinon.spy(utils, 'logWarn'); + stubFireNativeTrackers = sinon.stub(native, 'fireNativeTrackers'); + stubGetAllAssetsMessage = sinon.stub(native, 'getAllAssetsMessage'); + stubEmit = sinon.stub(events, 'emit'); + }); + + afterEach(function() { + spyAddWinningBid.restore(); + spyLogWarn.restore(); + stubFireNativeTrackers.restore(); + stubGetAllAssetsMessage.restore(); + stubEmit.restore(); + resetAuction(); + }); + + describe('Prebid Request', function() { + it('should render', function () { + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + }); + + it('should allow stale rendering without config', function () { + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(adResponse.renderer.render); + + receiveMessage(ev); + + sinon.assert.calledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER, adResponse); + }); + + it('should stop stale rendering with config', function () { + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(adResponse.renderer.render); + + receiveMessage(ev); + + sinon.assert.calledWith(spyLogWarn, warning); + sinon.assert.notCalled(spyAddWinningBid); + sinon.assert.notCalled(adResponse.renderer.render); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER, adResponse); + + configObj.setConfig({'auctionOptions': {}}); + }); + }); + + describe('Prebid Native', function() { + it('Prebid native should render', function () { + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + }); + + it('Prebid native should allow stale rendering without config', function () { + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(ev.source.postMessage); + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + }); + + it('Prebid native should allow stale rendering with config', function () { + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(ev.source.postMessage); + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + configObj.setConfig({'auctionOptions': {}}); + }); + }); + }); });