diff --git a/integrationExamples/gpt/fledge_example.html b/integrationExamples/gpt/fledge_example.html new file mode 100644 index 00000000000..5059e03daef --- /dev/null +++ b/integrationExamples/gpt/fledge_example.html @@ -0,0 +1,103 @@ + + + + + + + + + +

Prebid.js FLEDGE+GPT Example

+ +
Div-1
+
+ +
+ + diff --git a/integrationExamples/gpt/prebidServer_fledge_example.html b/integrationExamples/gpt/prebidServer_fledge_example.html new file mode 100644 index 00000000000..8523c0f2920 --- /dev/null +++ b/integrationExamples/gpt/prebidServer_fledge_example.html @@ -0,0 +1,111 @@ + + + + + + + + + +

Prebid.js FLEDGE+GPT Example

+ +
Div-1
+
+ +
+ + diff --git a/libraries/ortbConverter/processors/video.js b/libraries/ortbConverter/processors/video.js index 5fe411e6bcf..c38231d9002 100644 --- a/libraries/ortbConverter/processors/video.js +++ b/libraries/ortbConverter/processors/video.js @@ -6,6 +6,7 @@ import {sizesToFormat} from '../lib/sizes.js'; const ORTB_VIDEO_PARAMS = new Set([ 'pos', 'placement', + 'plcmt', 'api', 'mimes', 'protocols', diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index c3cd1963248..e59d12eadad 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -6,6 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; +const ENDPOINT_URL_EUROPE = 'https://europe.delivery.adnuntius.com/i'; const GVLID = 855; const DEFAULT_VAST_VERSION = 'vast4' // const DEFAULT_NATIVE = 'native' @@ -130,10 +131,11 @@ export const spec = { const network = networkKeys[j]; const networkRequest = [...request] if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) } + const requestURL = gdprApplies ? ENDPOINT_URL_EUROPE : ENDPOINT_URL // if (network.indexOf('_native') > -1) { networkRequest.push('tt=' + DEFAULT_NATIVE) } requests.push({ method: 'POST', - url: ENDPOINT_URL + '?' + networkRequest.join('&'), + url: requestURL + '?' + networkRequest.join('&'), data: JSON.stringify(networks[network]), bid: bidRequests[network] }); diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 8f499e1e31e..cf3763be9c8 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -333,14 +333,16 @@ export const spec = { payload.referrer_detection = refererinfo; } - const hasAdPodBid = find(bidRequests, hasAdPod); - if (hasAdPodBid) { - bidRequests.filter(hasAdPod).forEach(adPodBid => { - const adPodTags = createAdPodRequest(tags, adPodBid); - // don't need the original adpod placement because it's in adPodTags - const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId); - payload.tags = [...nonPodTags, ...adPodTags]; - }); + if (FEATURES.VIDEO) { + const hasAdPodBid = find(bidRequests, hasAdPod); + if (hasAdPodBid) { + bidRequests.filter(hasAdPod).forEach(adPodBid => { + const adPodTags = createAdPodRequest(tags, adPodBid); + // don't need the original adpod placement because it's in adPodTags + const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId); + payload.tags = [...nonPodTags, ...adPodTags]; + }); + } } if (bidRequests[0].userId) { @@ -653,7 +655,7 @@ function newBid(serverBid, rtbBid, bidderRequest) { bid.meta = Object.assign({}, bid.meta, { brandId: rtbBid.brand_id }); } - if (rtbBid.rtb.video) { + if (FEATURES.VIDEO && rtbBid.rtb.video) { // shared video properties used for all 3 contexts Object.assign(bid, { width: rtbBid.rtb.video.player_width, @@ -865,107 +867,111 @@ function bidToTag(bid) { } } - const videoMediaType = deepAccess(bid, `mediaTypes.${VIDEO}`); - const context = deepAccess(bid, 'mediaTypes.video.context'); - - if (videoMediaType && context === 'adpod') { - tag.hb_source = 7; - } else { - tag.hb_source = 1; - } - if (bid.mediaType === VIDEO || videoMediaType) { - tag.ad_types.push(VIDEO); - } - - // instream gets vastUrl, outstream gets vastXml - if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { - tag.require_asset_url = true; - } + if (FEATURES.VIDEO) { + const videoMediaType = deepAccess(bid, `mediaTypes.${VIDEO}`); + const context = deepAccess(bid, 'mediaTypes.video.context'); - if (bid.params.video) { - tag.video = {}; - // place any valid video params on the tag - Object.keys(bid.params.video) - .filter(param => includes(VIDEO_TARGETING, param)) - .forEach(param => { - switch (param) { - case 'context': - case 'playback_method': - let type = bid.params.video[param]; - type = (isArray(type)) ? type[0] : type; - tag.video[param] = VIDEO_MAPPING[param][type]; - break; - // Deprecating tags[].video.frameworks in favor of tags[].video_frameworks - case 'frameworks': - break; - default: - tag.video[param] = bid.params.video[param]; - } - }); + if (videoMediaType && context === 'adpod') { + tag.hb_source = 7; + } else { + tag.hb_source = 1; + } + if (bid.mediaType === VIDEO || videoMediaType) { + tag.ad_types.push(VIDEO); + } - if (bid.params.video.frameworks && isArray(bid.params.video.frameworks)) { - tag['video_frameworks'] = bid.params.video.frameworks; + // instream gets vastUrl, outstream gets vastXml + if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { + tag.require_asset_url = true; } - } - // use IAB ORTB values if the corresponding values weren't already set by bid.params.video - if (videoMediaType) { - tag.video = tag.video || {}; - Object.keys(videoMediaType) - .filter(param => includes(VIDEO_RTB_TARGETING, param)) - .forEach(param => { - switch (param) { - case 'minduration': - case 'maxduration': - if (typeof tag.video[param] !== 'number') tag.video[param] = videoMediaType[param]; - break; - case 'skip': - if (typeof tag.video['skippable'] !== 'boolean') tag.video['skippable'] = (videoMediaType[param] === 1); - break; - case 'skipafter': - if (typeof tag.video['skipoffset'] !== 'number') tag.video['skippoffset'] = videoMediaType[param]; - break; - case 'playbackmethod': - if (typeof tag.video['playback_method'] !== 'number') { - let type = videoMediaType[param]; + if (bid.params.video) { + tag.video = {}; + // place any valid video params on the tag + Object.keys(bid.params.video) + .filter(param => includes(VIDEO_TARGETING, param)) + .forEach(param => { + switch (param) { + case 'context': + case 'playback_method': + let type = bid.params.video[param]; type = (isArray(type)) ? type[0] : type; + tag.video[param] = VIDEO_MAPPING[param][type]; + break; + // Deprecating tags[].video.frameworks in favor of tags[].video_frameworks + case 'frameworks': + break; + default: + tag.video[param] = bid.params.video[param]; + } + }); - // we only support iab's options 1-4 at this time. - if (type >= 1 && type <= 4) { - tag.video['playback_method'] = type; - } - } - break; - case 'api': - if (!tag['video_frameworks'] && isArray(videoMediaType[param])) { - // need to read thru array; remove 6 (we don't support it), swap 4 <> 5 if found (to match our adserver mapping for these specific values) - let apiTmp = videoMediaType[param].map(val => { - let v = (val === 4) ? 5 : (val === 5) ? 4 : val; - - if (v >= 1 && v <= 5) { - return v; + if (bid.params.video.frameworks && isArray(bid.params.video.frameworks)) { + tag['video_frameworks'] = bid.params.video.frameworks; + } + } + + // use IAB ORTB values if the corresponding values weren't already set by bid.params.video + if (videoMediaType) { + tag.video = tag.video || {}; + Object.keys(videoMediaType) + .filter(param => includes(VIDEO_RTB_TARGETING, param)) + .forEach(param => { + switch (param) { + case 'minduration': + case 'maxduration': + if (typeof tag.video[param] !== 'number') tag.video[param] = videoMediaType[param]; + break; + case 'skip': + if (typeof tag.video['skippable'] !== 'boolean') tag.video['skippable'] = (videoMediaType[param] === 1); + break; + case 'skipafter': + if (typeof tag.video['skipoffset'] !== 'number') tag.video['skippoffset'] = videoMediaType[param]; + break; + case 'playbackmethod': + if (typeof tag.video['playback_method'] !== 'number') { + let type = videoMediaType[param]; + type = (isArray(type)) ? type[0] : type; + + // we only support iab's options 1-4 at this time. + if (type >= 1 && type <= 4) { + tag.video['playback_method'] = type; } - }).filter(v => v); - tag['video_frameworks'] = apiTmp; - } - break; - - case 'startdelay': - case 'placement': - const contextKey = 'context'; - if (typeof tag.video[contextKey] !== 'number') { - const placement = videoMediaType['placement']; - const startdelay = videoMediaType['startdelay']; - const context = getContextFromPlacement(placement) || getContextFromStartDelay(startdelay); - tag.video[contextKey] = VIDEO_MAPPING[contextKey][context]; - } - break; - } - }); - } + } + break; + case 'api': + if (!tag['video_frameworks'] && isArray(videoMediaType[param])) { + // need to read thru array; remove 6 (we don't support it), swap 4 <> 5 if found (to match our adserver mapping for these specific values) + let apiTmp = videoMediaType[param].map(val => { + let v = (val === 4) ? 5 : (val === 5) ? 4 : val; + + if (v >= 1 && v <= 5) { + return v; + } + }).filter(v => v); + tag['video_frameworks'] = apiTmp; + } + break; + + case 'startdelay': + case 'placement': + const contextKey = 'context'; + if (typeof tag.video[contextKey] !== 'number') { + const placement = videoMediaType['placement']; + const startdelay = videoMediaType['startdelay']; + const context = getContextFromPlacement(placement) || getContextFromStartDelay(startdelay); + tag.video[contextKey] = VIDEO_MAPPING[contextKey][context]; + } + break; + } + }); + } - if (bid.renderer) { - tag.video = Object.assign({}, tag.video, { custom_renderer_present: true }); + if (bid.renderer) { + tag.video = Object.assign({}, tag.video, { custom_renderer_present: true }); + } + } else { + tag.hb_source = 1; } if (bid.params.frameworks && isArray(bid.params.frameworks)) { diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 0908b02de2e..b6cfa54170c 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -2,6 +2,7 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; +import { getRefererInfo } from '../src/refererDetection.js'; const analyticsType = 'endpoint'; const url = 'URL_TO_SERVER_ENDPOINT'; @@ -145,6 +146,7 @@ function handleBidWon(args) { }); } args['cpmIncrement'] = increment; + args['referer'] = encodeURIComponent(getRefererInfo().page || getRefererInfo().topmostLocation); if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); } diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index c2ffba8bb48..e17a9fe4021 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -124,6 +124,9 @@ export const spec = { const payload = { id: requestId, imp: conversantImps, + source: { + tid: requestId + }, site: { id: siteId, mobile: document.querySelector('meta[name="viewport"][content*="width=device-width"]') !== null ? 1 : 0, diff --git a/modules/emtvBidAdapter.js b/modules/emtvBidAdapter.js new file mode 100644 index 00000000000..7a2fdae8adf --- /dev/null +++ b/modules/emtvBidAdapter.js @@ -0,0 +1,211 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'emtv'; +const AD_URL = 'https://us-east-ep.engagemedia.tv/pbjs'; +const SYNC_URL = 'https://cs.engagemedia.tv'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId, endpointId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + if (placementId) { + placement.placementId = placementId; + placement.type = 'publisher'; + } else if (endpointId) { + placement.endpointId = endpointId; + placement.type = 'network'; + } + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && (params.placementId || params.endpointId)); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } else { + valid = false; + } + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/emtvBidAdapter.md b/modules/emtvBidAdapter.md new file mode 100644 index 00000000000..ed58ca43294 --- /dev/null +++ b/modules/emtvBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: EMTV Bidder Adapter +Module Type: EMTV Bidder Adapter +Maintainer: support@engagemedia.tv +``` + +# Description + +Connects to EMTV exchange for bids. +EMTV bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'emtv', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'emtv', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'emtv', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/modules/eskimiBidAdapter.js b/modules/eskimiBidAdapter.js new file mode 100644 index 00000000000..4a00b97614b --- /dev/null +++ b/modules/eskimiBidAdapter.js @@ -0,0 +1,72 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import {ortbConverter} from '../libraries/ortbConverter/converter.js' + +const BIDDER_CODE = 'eskimi'; +// const ENDPOINT = 'https://hb.eskimi.com/bids' +const ENDPOINT = 'https://sspback.eskimi.com/bid-request' + +const DEFAULT_BID_TTL = 30; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_NET_REVENUE = true; +const GVLID = 814; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bid) { + return !!bid.params.placementId; + }, + + buildRequests(bidRequests, bidderRequest) { + const data = converter.toORTB({bidRequests, bidderRequest}) + + let bid = bidRequests.find((b) => b.params.placementId) + if (!data.site) data.site = {} + data.site.ext = {placementId: bid.params.placementId} + + if (bidderRequest.gdprConsent) { + if (!data.user) data.user = {}; + if (!data.user.ext) data.user.ext = {}; + if (!data.regs) data.regs = {}; + if (!data.regs.ext) data.regs.ext = {}; + data.user.ext.consent = bidderRequest.gdprConsent.consentString; + data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + + return [{ + method: 'POST', + url: ENDPOINT, + data, + options: {contentType: 'application/json;charset=UTF-8', withCredentials: false} + }] + }, + + interpretResponse(response, request) { + return converter.fromORTB({response: response.body, request: request.data}).bids; + }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} bid The bid that won the auction + */ + onBidWon: function (bid) { + if (bid.burl) { + utils.triggerPixel(bid.burl); + } + } +} + +const converter = ortbConverter({ + context: { + netRevenue: DEFAULT_NET_REVENUE, + ttl: DEFAULT_BID_TTL, + currency: DEFAULT_CURRENCY, + mediaType: BANNER // TODO: support more types, we should set mtype on the winning bid + } +}); + +registerBidder(spec); diff --git a/modules/eskimiBidAdapter.md b/modules/eskimiBidAdapter.md new file mode 100644 index 00000000000..83ae87fd01b --- /dev/null +++ b/modules/eskimiBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +Module Name: ESKIMI Bidder Adapter +Module Type: Bidder Adapter +Maintainer: tech@eskimi.com + +# Description + +An adapter to get a bid from Eskimi DSP. + +# Test Parameters +```javascript + var adUnits = [{ + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + + bids: [{ + bidder: 'eskimi', + params: { + placementId: 612 + } + }] + + }]; +``` + +Where: + +* placementId - Placement ID of the ad unit (required) + diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 792dc3b15b0..a043483d9b0 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -6,7 +6,10 @@ import { generateUUID, mergeDeep, logWarn, - parseUrl, isArray, isNumber + parseUrl, + isArray, + isNumber, + isStr } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; @@ -52,7 +55,12 @@ const ALIAS_CONFIG = { bidResponseExternal: { netRevenue: false } - } + }, + 'gridNM': { + defaultParams: { + multiRequest: true + } + }, }; let hasSynced = false; @@ -60,7 +68,7 @@ let hasSynced = false; export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['playwire', 'adlivetech', { code: 'trustx', skipPbsAliasing: true }], + aliases: ['playwire', 'adlivetech', 'gridNM', { code: 'trustx', skipPbsAliasing: true }], supportedMediaTypes: [ BANNER, VIDEO ], /** * Determines whether or not the given bid request is valid. @@ -126,8 +134,9 @@ export const spec = { if (!endpoint) { endpoint = ALIAS_CONFIG[bid.bidder] && ALIAS_CONFIG[bid.bidder].endpoint; } - const { params: { uid, keywords, forceBidder, multiRequest }, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp } = bid; - const { secid, pubid, source, content: bidParamsContent } = bid.params; + const { params, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp } = bid; + const { defaultParams } = ALIAS_CONFIG[bid.bidder] || {}; + const { secid, pubid, source, uid, keywords, forceBidder, multiRequest, content: bidParamsContent, video: videoParams } = { ...defaultParams, ...params }; const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting && !content && jwTargeting.content) { @@ -178,7 +187,7 @@ export const spec = { } } if (mediaTypes && mediaTypes[VIDEO]) { - const video = createVideoRequest(bid, mediaTypes[VIDEO]); + const video = createVideoRequest(videoParams, mediaTypes[VIDEO], bid.sizes); if (video) { impObj.video = video; } @@ -588,27 +597,41 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bid } } -function createVideoRequest(bid, mediaType) { - const { playerSize, mimes, durationRangeSec, protocols } = mediaType; - const size = (playerSize || bid.sizes || [])[0]; - if (!size) return; +function createVideoRequest(videoParams, mediaType, bidSizes) { + const { mind, maxd, size, playerSize, protocols, durationRangeSec = [], ...videoData } = { ...mediaType, ...videoParams }; + if (size && isStr(size)) { + const sizeArray = size.split('x'); + if (sizeArray.length === 2 && parseInt(sizeArray[0]) && parseInt(sizeArray[1])) { + videoData.w = parseInt(sizeArray[0]); + videoData.h = parseInt(sizeArray[1]); + } + } + if (!videoData.w || !videoData.h) { + const pSizesString = (playerSize || bidSizes || []).toString(); + const pSizeString = (pSizesString.match(/^\d+,\d+/) || [])[0]; + const pSize = pSizeString && pSizeString.split(',').map((d) => parseInt(d)); + if (pSize && pSize.length === 2) { + Object.assign(videoData, parseGPTSingleSizeArrayToRtbSize(pSize)); + } + } - let result = parseGPTSingleSizeArrayToRtbSize(size); + if (!videoData.w || !videoData.h) return; - if (mimes) { - result.mimes = mimes; - } + const minDur = mind || durationRangeSec[0] || videoData.minduration; + const maxDur = maxd || durationRangeSec[1] || videoData.maxduration; - if (durationRangeSec && durationRangeSec.length === 2) { - result.minduration = durationRangeSec[0]; - result.maxduration = durationRangeSec[1]; + if (minDur) { + videoData.minduration = minDur; + } + if (maxDur) { + videoData.maxduration = maxDur; } if (protocols && protocols.length) { - result.protocols = protocols; + videoData.protocols = protocols; } - return result; + return videoData; } function createBannerRequest(bid, mediaType) { diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js deleted file mode 100644 index 36038d521bd..00000000000 --- a/modules/gridNMBidAdapter.js +++ /dev/null @@ -1,422 +0,0 @@ -import { - isStr, - deepAccess, - isArray, - isNumber, - logError, - logWarn, - parseGPTSingleSizeArrayToRtbSize, - mergeDeep -} from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { Renderer } from '../src/Renderer.js'; -import { VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'gridNM'; -const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; -const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; -const TIME_TO_LIVE = 360; -const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; - -let hasSynced = false; - -const LOG_ERROR_MESS = { - noAdm: 'Bid from response has no adm parameter - ', - noPrice: 'Bid from response has no price parameter - ', - wrongContentType: 'Bid from response has wrong content_type parameter - ', - noBid: 'Array of bid objects is empty', - noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', - emptyUids: 'Uids should be not empty', - emptySeatbid: 'Seatbid array from response has empty item', - emptyResponse: 'Response is empty', - hasEmptySeatbidArray: 'Response has empty seatbid array', - hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' -}; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [ 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. - */ - isBidRequestValid: function(bid) { - let invalid = - !bid.params.source || !isStr(bid.params.source) || - !bid.params.secid || !isStr(bid.params.secid) || - !bid.params.pubid || !isStr(bid.params.pubid); - - const video = deepAccess(bid, 'mediaTypes.video') || {}; - const { protocols = video.protocols, mimes = video.mimes } = deepAccess(bid, 'params.video') || {}; - if (!invalid) { - invalid = !protocols || !mimes; - } - if (!invalid) { - invalid = !isArray(mimes) || !mimes.length || mimes.filter((it) => !(it && isStr(it))).length; - if (!invalid) { - invalid = !isArray(protocols) || !protocols.length || protocols.filter((it) => !(isNumber(it) && it > 0 && !(it % 1))).length; - } - } - return !invalid; - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests - an array of bids - * @param {bidderRequest} bidderRequest bidder request object - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests, bidderRequest) { - const bids = validBidRequests || []; - const requests = []; - let { bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo } = bidderRequest || {}; - - const referer = refererInfo ? encodeURIComponent(refererInfo.page) : ''; - - bids.forEach(bid => { - let user; - let userExt; - - const schain = bid.schain; - const userIdAsEids = bid.userIdAsEids; - - if (!bidderRequestId) { - bidderRequestId = bid.bidderRequestId; - } - if (!auctionId) { - auctionId = bid.auctionId; - } - const { - params: { floorcpm, source, secid, pubid, content, video }, - mediaTypes, bidId, adUnitCode, rtd, ortb2Imp, sizes - } = bid; - - const bidFloor = _getFloor(mediaTypes || {}, bid, isNumber(floorcpm) && floorcpm); - const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; - - const siteContent = content || (jwTargeting && jwTargeting.content); - - const impObj = { - id: bidId.toString(), - tagid: secid.toString(), - video: createVideoForImp(mergeDeep({}, video, mediaTypes && mediaTypes.video), sizes), - ext: { - divid: adUnitCode.toString() - } - }; - - if (ortb2Imp && ortb2Imp.ext && ortb2Imp.ext.data) { - impObj.ext.data = ortb2Imp.ext.data; - if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { - impObj.ext.gpid = impObj.ext.data.adserver.adslot.toString(); - } else { - impObj.ext.gpid = ortb2Imp.ext.data.pbadslot && ortb2Imp.ext.data.pbadslot.toString(); - } - } - - if (bidFloor) { - impObj.bidfloor = bidFloor; - } - - const imp = [impObj]; - - const reqSource = { - tid: auctionId && auctionId.toString(), - ext: { - wrapper: 'Prebid_js', - wrapper_version: '$prebid.version$' - } - }; - - if (schain) { - reqSource.ext.schain = schain; - } - - const bidderTimeout = timeout; - const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; - - const request = { - id: bidderRequestId && bidderRequestId.toString(), - site: { - page: referer, - publisher: { - id: pubid, - }, - }, - source: reqSource, - tmax, - imp, - }; - - if (siteContent) { - request.site.content = siteContent; - } - - if (gdprConsent && gdprConsent.consentString) { - userExt = { consent: gdprConsent.consentString }; - } - - if (userIdAsEids && userIdAsEids.length) { - userExt = userExt || {}; - userExt.eids = [...userIdAsEids]; - } - - if (userExt && Object.keys(userExt).length) { - user = user || {}; - user.ext = userExt; - } - - const ortb2UserData = deepAccess(bidderRequest, 'ortb2.user.data'); - if (ortb2UserData && ortb2UserData.length) { - if (!user) { - user = { data: [] }; - } - user = mergeDeep(user, { - data: [...ortb2UserData] - }); - } - - if (user) { - request.user = user; - } - const site = deepAccess(bidderRequest, 'ortb2.site'); - if (site) { - const data = deepAccess(site, 'content.data'); - if (data && data.length) { - const siteContent = request.site.content || {}; - request.site.content = mergeDeep(siteContent, { data }); - } - } - - if (gdprConsent && gdprConsent.gdprApplies) { - request.regs = { - ext: { - gdpr: gdprConsent.gdprApplies ? 1 : 0 - } - }; - } - - if (uspConsent) { - if (!request.regs) { - request.regs = { ext: {} }; - } - request.regs.ext.us_privacy = uspConsent; - } - - if (config.getConfig('coppa') === true) { - if (!request.regs) { - request.regs = {}; - } - request.regs.coppa = 1; - } - - requests.push({ - method: 'POST', - url: ENDPOINT_URL + '?no_mapping=1&sp=' + source, - bid: bid, - data: request - }); - }); - - return requests; - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param {*} bidRequest - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest) { - serverResponse = serverResponse && serverResponse.body; - const bidResponses = []; - - let errorMessage; - - if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; - else if (serverResponse.seatbid && !serverResponse.seatbid.length) { - errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; - } - - if (!errorMessage && serverResponse.seatbid) { - const serverBid = _getBidFromResponse(serverResponse.seatbid[0]); - if (serverBid) { - if (!serverBid.adm && !serverBid.nurl) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); - else if (!serverBid.price) errorMessage = LOG_ERROR_MESS.noPrice + JSON.stringify(serverBid); - else if (serverBid.content_type !== 'video') errorMessage = LOG_ERROR_MESS.wrongContentType + serverBid.content_type; - if (!errorMessage) { - const bid = bidRequest.bid; - const bidResponse = { - requestId: bid.bidId, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid || bid.bidderRequestId, - currency: 'USD', - netRevenue: true, - ttl: TIME_TO_LIVE, - dealId: serverBid.dealid, - mediaType: VIDEO, - meta: { - advertiserDomains: serverBid.adomain ? serverBid.adomain : [] - } - }; - - if (serverBid.adm) { - bidResponse.vastXml = serverBid.adm; - bidResponse.adResponse = { - content: bidResponse.vastXml - }; - } else if (serverBid.nurl) { - bidResponse.vastUrl = serverBid.nurl; - } - - if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { - bidResponse.renderer = createRenderer(bidResponse, { - id: bid.bidId, - url: RENDERER_URL - }); - } - bidResponses.push(bidResponse); - } - } - } - if (errorMessage) logError(errorMessage); - return bidResponses; - }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { - if (!hasSynced && syncOptions.pixelEnabled) { - let params = ''; - - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent) { - params += `&us_privacy=${uspConsent}`; - } - - hasSynced = true; - return { - type: 'image', - url: SYNC_URL + params - }; - } - } -}; - -/** - * Gets bidfloor - * @param {Object} mediaTypes - * @param {Object} bid - * @param {Number} floor - * @returns {Number} floor - */ -function _getFloor (mediaTypes, bid, floor) { - const curMediaType = mediaTypes.video ? 'video' : 'banner'; - - if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({ - currency: 'USD', - mediaType: curMediaType, - size: bid.sizes.map(([w, h]) => ({w, h})) - }); - - if (typeof floorInfo === 'object' && - floorInfo.currency === 'USD' && - !isNaN(parseFloat(floorInfo.floor))) { - floor = Math.max(floor, parseFloat(floorInfo.floor)); - } - } - - return floor; -} - -function _getBidFromResponse(respItem) { - if (!respItem) { - logError(LOG_ERROR_MESS.emptySeatbid); - } else if (!respItem.bid) { - logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(respItem)); - } else if (!respItem.bid[0]) { - logError(LOG_ERROR_MESS.noBid); - } - return respItem && respItem.bid && respItem.bid[0]; -} - -function outstreamRender (bid) { - bid.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - targetId: bid.adUnitCode, - adResponse: bid.adResponse - }); - }); -} - -function createRenderer (bid, rendererParams) { - const renderer = Renderer.install({ - id: rendererParams.id, - url: rendererParams.url, - loaded: false - }); - - try { - renderer.setRender(outstreamRender); - } catch (err) { - logWarn('Prebid Error calling setRender on renderer', err); - } - - return renderer; -} - -function createVideoForImp({ mind, maxd, size, ...paramsVideo }, bidSizes) { - if (size && isStr(size)) { - const sizeArray = size.split('x'); - if (sizeArray.length === 2 && parseInt(sizeArray[0]) && parseInt(sizeArray[1])) { - paramsVideo.w = parseInt(sizeArray[0]); - paramsVideo.h = parseInt(sizeArray[1]); - } - } - - if (!paramsVideo.w || !paramsVideo.h) { - const playerSizes = paramsVideo.playerSize && paramsVideo.playerSize.length === 2 ? paramsVideo.playerSize : bidSizes; - if (playerSizes) { - const playerSize = playerSizes[0]; - if (playerSize) { - Object.assign(paramsVideo, parseGPTSingleSizeArrayToRtbSize(playerSize)); - } - } - } - - if (paramsVideo.playerSize) { - delete paramsVideo.playerSize; - } - - const durationRangeSec = paramsVideo.durationRangeSec || []; - const minDur = mind || durationRangeSec[0] || paramsVideo.minduration; - const maxDur = maxd || durationRangeSec[1] || paramsVideo.maxduration; - - if (minDur) { - paramsVideo.minduration = minDur; - } - if (maxDur) { - paramsVideo.maxduration = maxDur; - } - - return paramsVideo; -} - -export function resetUserSync() { - hasSynced = false; -} - -export function getSyncUrl() { - return SYNC_URL; -} - -registerBidder(spec); diff --git a/modules/gridNMBidAdapter.md b/modules/gridNMBidAdapter.md deleted file mode 100644 index 6decdde7f4c..00000000000 --- a/modules/gridNMBidAdapter.md +++ /dev/null @@ -1,39 +0,0 @@ -# Overview - -Module Name: The Grid Media Bidder Adapter -Module Type: Bidder Adapter -Maintainer: grid-tech@themediagrid.com - -# Description - -Module that connects to Grid demand source to fetch bids. -Grid bid adapter supports Banner and Video (instream and outstream). - -# Test Parameters -``` - var adUnits = [ - { - code: 'test-div', - mediaTypes: { - video: { - playerSize: [728, 90], - context: 'outstream' - } - }, - bids: [ - { - bidder: "gridNM", - params: { - source: 'jwp', - secid: '11', - pubid: '22', - video: { - mimes: ['video/mp4', 'video/x-ms-wmv'], - protocols: [1,2,3,4,5,6] - } - } - } - ] - } - ]; -``` diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index e791d933352..06f96edca30 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -195,6 +195,9 @@ function bidToVideoImp(bid) { // populate imp level transactionId imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); + // AdUnit-Specific First Party Data + addAdUnitFPD(imp, bid) + // copy all video properties to imp object for (const adUnitProperty in videoAdUnitRef) { if (VIDEO_PARAMS_ALLOW_LIST.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) { @@ -268,6 +271,9 @@ function bidToNativeImp(bid) { // populate imp level transactionId imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); + // AdUnit-Specific First Party Data + addAdUnitFPD(imp, bid) + _applyFloor(bid, imp, NATIVE); return imp; @@ -956,6 +962,11 @@ function addImpressions(impressions, transactionIds, r, adUnitIndex) { _bannerImpression.bidfloorcur = impressionObjects[0].bidfloorcur; } + const adUnitFPD = impressions[transactionIds[adUnitIndex]].adUnitFPD + if (adUnitFPD) { + _bannerImpression.ext.data = adUnitFPD; + } + r.imp.push(_bannerImpression); } else { // set imp.ext.gpid to resolved gpid for each imp @@ -1011,6 +1022,19 @@ function addFPD(bidderRequest, r, fpd, site, user) { return r; } +/** + * Adds First-Party Data (FPD) from the bid object to the imp object. + * + * @param {Object} imp - The imp object, representing an impression in the OpenRTB format. + * @param {Object} bid - The bid object, containing information about the bid request. + */ +function addAdUnitFPD(imp, bid) { + const adUnitFPD = deepAccess(bid, 'ortb2Imp.ext.data'); + if (adUnitFPD) { + deepSetValue(imp, 'ext.data', adUnitFPD) + } +} + /** * addIdentifiersInfo adds indentifier info to ixDaig. * @@ -1196,6 +1220,12 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { bannerImps[validBidRequest.transactionId].tagId = deepAccess(validBidRequest, 'params.tagId'); bannerImps[validBidRequest.transactionId].pos = deepAccess(validBidRequest, 'mediaTypes.banner.pos'); + // AdUnit-Specific First Party Data + const adUnitFPD = deepAccess(validBidRequest, 'ortb2Imp.ext.data'); + if (adUnitFPD) { + bannerImps[validBidRequest.transactionId].adUnitFPD = adUnitFPD; + } + const sid = deepAccess(validBidRequest, 'params.id'); if (sid && (typeof sid === 'string' || typeof sid === 'number')) { bannerImps[validBidRequest.transactionId].sid = String(sid); diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index ecc40b26aa2..b612c88bb12 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -47,7 +47,7 @@ const SUA_ATTRIBUTES = [ const CERBERUS = Object.freeze({ KEY: 'krg_crb', - SYNC_URL: 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}&gdpr={GDPR}&gdpr_consent={GDPR_CONSENT}&us_privacy={US_PRIVACY}', + SYNC_URL: 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}&gdpr={GDPR}&gdpr_consent={GDPR_CONSENT}&us_privacy={US_PRIVACY}&gpp={GPP_STRING}&gpp_sid={GPP_SID}', SYNC_COUNT: 5, PAGE_VIEW_ID: 'pageViewId', PAGE_VIEW_TIMESTAMP: 'pageViewTimestamp', @@ -94,7 +94,7 @@ function buildRequests(validBidRequests, bidderRequest) { ] }, imp: impressions, - user: getUserIds(tdidAdapter, bidderRequest.uspConsent, bidderRequest.gdprConsent, firstBidRequest.userIdAsEids), + user: getUserIds(tdidAdapter, bidderRequest.uspConsent, bidderRequest.gdprConsent, firstBidRequest.userIdAsEids, bidderRequest.gppConsent), }); const reqCount = getRequestCount() @@ -229,7 +229,7 @@ function interpretResponse(response, bidRequest) { return bidResponses; } -function getUserSyncs(syncOptions, responses, gdprConsent, usPrivacy) { +function getUserSyncs(syncOptions, _, gdprConsent, usPrivacy, gppConsent) { const syncs = []; const seed = _generateRandomUUID(); const clientId = getClientId(); @@ -237,6 +237,9 @@ function getUserSyncs(syncOptions, responses, gdprConsent, usPrivacy) { var gdpr = (gdprConsent && gdprConsent.gdprApplies) ? 1 : 0; var gdprConsentString = (gdprConsent && gdprConsent.consentString) ? gdprConsent.consentString : ''; + var gppString = (gppConsent && gppConsent.consentString) ? gppConsent.consentString : ''; + var gppApplicableSections = (gppConsent && gppConsent.applicableSections && Array.isArray(gppConsent.applicableSections)) ? gppConsent.applicableSections.join(',') : ''; + // don't sync if opted out via usPrivacy if (typeof usPrivacy == 'string' && usPrivacy.length == 4 && usPrivacy[0] == 1 && usPrivacy[2] == 'Y') { return syncs; @@ -251,6 +254,8 @@ function getUserSyncs(syncOptions, responses, gdprConsent, usPrivacy) { .replace('{GDPR}', gdpr) .replace('{GDPR_CONSENT}', gdprConsentString) .replace('{US_PRIVACY}', usPrivacy || '') + .replace('{GPP_STRING}', gppString) + .replace('{GPP_SID}', gppApplicableSections) }); } } @@ -329,7 +334,7 @@ function getLocalStorageSafely(key) { } } -function getUserIds(tdidAdapter, usp, gdpr, eids) { +function getUserIds(tdidAdapter, usp, gdpr, eids, gpp) { const crb = spec._getCrb(); const userIds = { crbIDs: crb.syncIds || {} @@ -375,6 +380,19 @@ function getUserIds(tdidAdapter, usp, gdpr, eids) { userIds.sharedIDEids = eids; } + if (gpp) { + const parsedGPP = {} + if (gpp && gpp.consentString) { + parsedGPP.gppString = gpp.consentString + } + if (gpp && gpp.applicableSections) { + parsedGPP.applicableSections = gpp.applicableSections + } + if (!isEmpty(parsedGPP)) { + userIds.gpp = parsedGPP + } + } + return userIds; } diff --git a/modules/kueezRtbBidAdapter.js b/modules/kueezRtbBidAdapter.js index 3bd468a581f..ef53ed9baf4 100644 --- a/modules/kueezRtbBidAdapter.js +++ b/modules/kueezRtbBidAdapter.js @@ -77,6 +77,8 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { const pId = extractPID(params); const subDomain = extractSubDomain(params); + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); + if (isFn(bid.getFloor)) { const floorInfo = bid.getFloor({ currency: 'USD', @@ -105,6 +107,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { res: `${screen.width}x${screen.height}`, schain: schain, mediaTypes: mediaTypes, + gpid: gpid, auctionId: auctionId, transactionId: transactionId, bidderRequestId: bidderRequestId, @@ -204,7 +207,7 @@ function interpretResponse(serverResponse, request) { try { results.forEach(result => { - const {creativeId, ad, price, exp, width, height, currency, advertiserDomains, mediaType = BANNER} = result; + const {creativeId, ad, price, exp, width, height, currency, metaData, advertiserDomains, mediaType = BANNER} = result; if (!ad || !price) { return; } @@ -218,11 +221,20 @@ function interpretResponse(serverResponse, request) { currency: currency || CURRENCY, netRevenue: true, ttl: exp || TTL_SECONDS, - meta: { - advertiserDomains: advertiserDomains || [] - } }; + if (metaData) { + Object.assign(response, { + meta: metaData + }) + } else { + Object.assign(response, { + meta: { + advertiserDomains: advertiserDomains || [] + } + }) + } + if (mediaType === BANNER) { Object.assign(response, { ad: ad, diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 819ff280e35..4076cd6927a 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -4,6 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {Renderer} from '../src/Renderer.js'; +import { getRefererInfo } from '../src/refererDetection.js'; const BIDDER_CODE = 'mediasquare'; const BIDDER_URL_PROD = 'https://pbs-front.mediasquare.fr/' @@ -170,7 +171,7 @@ export const spec = { */ onBidWon: function(bid) { // fires a pixel to confirm a winning bid - let params = {'pbjs': '$prebid.version$'}; + let params = { pbjs: '$prebid.version$', referer: encodeURIComponent(getRefererInfo().page || getRefererInfo().topmostLocation) }; let endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'requestId', 'auctionId', 'originalCpm', 'originalCurrency']; if (bid.hasOwnProperty('mediasquare')) { diff --git a/modules/minutemediaplusBidAdapter.js b/modules/minutemediaplusBidAdapter.js index c07f227bcfa..bd7874c405b 100644 --- a/modules/minutemediaplusBidAdapter.js +++ b/modules/minutemediaplusBidAdapter.js @@ -77,6 +77,8 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { const pId = extractPID(params); const subDomain = extractSubDomain(params); + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); + if (isFn(bid.getFloor)) { const floorInfo = bid.getFloor({ currency: 'USD', @@ -105,6 +107,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { res: `${screen.width}x${screen.height}`, schain: schain, mediaTypes: mediaTypes, + gpid: gpid, auctionId: auctionId, transactionId: transactionId, bidderRequestId: bidderRequestId, @@ -204,7 +207,7 @@ function interpretResponse(serverResponse, request) { try { results.forEach(result => { - const {creativeId, ad, price, exp, width, height, currency, advertiserDomains, mediaType = BANNER} = result; + const {creativeId, ad, price, exp, width, height, currency, metaData, advertiserDomains, mediaType = BANNER} = result; if (!ad || !price) { return; } @@ -218,11 +221,20 @@ function interpretResponse(serverResponse, request) { currency: currency || CURRENCY, netRevenue: true, ttl: exp || TTL_SECONDS, - meta: { - advertiserDomains: advertiserDomains || [] - } }; + if (metaData) { + Object.assign(response, { + meta: metaData + }) + } else { + Object.assign(response, { + meta: { + advertiserDomains: advertiserDomains || [] + } + }) + } + if (mediaType === BANNER) { Object.assign(response, { ad: ad, diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index f461440c5a1..33fa6857e85 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -8,7 +8,7 @@ const EVENTS_DOMAIN = 'events.missena.io'; const EVENTS_DOMAIN_DEV = 'events.staging.missena.xyz'; export const spec = { - aliases: [BIDDER_CODE], + aliases: ['msna'], code: BIDDER_CODE, gvlid: 687, supportedMediaTypes: [BANNER], diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index a92168492d0..c62a74e6d6c 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -1,6 +1,7 @@ import { deepAccess, isEmpty } from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' +import { getGlobal } from '../src/prebidGlobal.js' // import { config } from 'src/config' const BIDDER_CODE = 'nativo' @@ -14,6 +15,8 @@ const SUPPORTED_AD_TYPES = [BANNER] const FLOOR_PRICE_CURRENCY = 'USD' const PRICE_FLOOR_WILDCARD = '*' +const localPbjsRef = getGlobal() + /** * Keep track of bid data by keys * @returns {Object} - Map of bid data that can be referenced by multiple keys @@ -133,6 +136,9 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + const requestData = new RequestData() + requestData.addBidRequestDataSource(new UserEIDs()) + // Parse values from bid requests const placementIds = new Set() const bidDataMap = BidDataMap() @@ -166,6 +172,8 @@ export const spec = { if (bidRequestFloorPriceData) { floorPriceData[bidRequest.adUnitCode] = bidRequestFloorPriceData } + + requestData.processBidRequestData(bidRequest, bidderRequest) }) bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap @@ -174,6 +182,10 @@ export const spec = { // Build basic required QS Params let params = [ + // Prebid version + { + key: 'ntv_pbv', value: localPbjsRef.version + }, // Prebid request id { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, // Ad unit data @@ -255,9 +267,12 @@ export const spec = { params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent }) } + const qsParamStrings = [requestData.getRequestDataQueryString(), arrayToQS(params)] + const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings) + let serverRequest = { method: 'GET', - url: BIDDER_ENDPOINT + arrayToQS(params), + url: requestUrl } return serverRequest @@ -404,13 +419,6 @@ export const spec = { return syncs }, - /** - * Will be called when an adpater timed out for an auction. - * Adapter can fire a ajax or pixel call to register a timeout at thier end. - * @param {Object} timeoutData - Timeout specific data - */ - onTimeout: function (timeoutData) {}, - /** * Will be called when a bid from the adapter won the auction. * @param {Object} bid - The bid that won the auction @@ -425,12 +433,6 @@ export const spec = { appendFilterData(campaignsToFilter, ext.campaignsToFilter) }, - /** - * Will be called when the adserver targeting has been set for a bid from the adapter. - * @param {Object} bidder - The bid of which the targeting has been set - */ - onSetTargeting: function (bid) {}, - /** * Maps Prebid's bidId to Nativo's placementId values per unique bidderRequestId * @param {String} bidderRequestId - The unique ID value associated with the bidderRequest @@ -451,6 +453,78 @@ export const spec = { registerBidder(spec) // Utils +export class RequestData { + constructor() { + this.bidRequestDataSources = [] + } + + addBidRequestDataSource(bidRequestDataSource) { + if (!(bidRequestDataSource instanceof BidRequestDataSource)) return + + this.bidRequestDataSources.push(bidRequestDataSource) + } + + processBidRequestData(bidRequest, bidderRequest) { + for (let bidRequestDataSource of this.bidRequestDataSources) { + bidRequestDataSource.processBidRequestData(bidRequest, bidderRequest) + } + } + + getRequestDataQueryString() { + if (this.bidRequestDataSources.length == 0) return + + const queryParams = this.bidRequestDataSources.map(dataSource => dataSource.getRequestQueryString()).filter(queryString => queryString !== '') + return queryParams.join('&') + } +} + +export class BidRequestDataSource { + constructor() { + this.type = 'BidRequestDataSource' + } + processBidRequestData(bidRequest, bidderRequest) { } + getRequestQueryString() { return '' } +} + +export class UserEIDs extends BidRequestDataSource { + constructor() { + super() + this.type = 'UserEIDs' + this.qsParam = new QueryStringParam('ntv_pb_eid') + this.eids = [] + } + + processBidRequestData(bidRequest, bidderRequest) { + if (bidRequest.userIdAsEids === undefined || this.eids.length > 0) return + this.eids = bidRequest.userIdAsEids + } + + getRequestQueryString() { + if (this.eids.length === 0) return '' + + const encodedValueArray = encodeToBase64(this.eids) + this.qsParam.value = encodedValueArray + return this.qsParam.toString() + } +} + +export class QueryStringParam { + constructor(key, value) { + this.key = key + this.value = value + } +} + +QueryStringParam.prototype.toString = function () { + return `${this.key}=${this.value}` +} + +export function encodeToBase64(value) { + try { + return btoa(JSON.stringify(value)) + } catch (err) { } +} + export function parseFloorPriceData(bidRequest) { if (typeof bidRequest.getFloor !== 'function') return @@ -589,12 +663,9 @@ function appendQSParamString(str, key, value) { * @returns */ function arrayToQS(arr) { - return ( - '?' + - arr.reduce((value, obj) => { - return appendQSParamString(value, obj.key, obj.value) - }, '') - ) + return arr.reduce((value, obj) => { + return appendQSParamString(value, obj.key, obj.value) + }, '') } /** @@ -615,6 +686,24 @@ function getLargestSize(sizes, method = area) { }) } +/** + * Build the final request url + */ +export function buildRequestUrl(baseUrl, qsParamStringArray = []) { + if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) return baseUrl + + const nonEmptyQSParamStrings = qsParamStringArray.filter(qsParamString => qsParamString.trim() !== '') + + if (nonEmptyQSParamStrings.length === 0) return baseUrl + + let requestUrl = `${baseUrl}?${nonEmptyQSParamStrings[0]}` + for (let i = 1; i < nonEmptyQSParamStrings.length; i++) { + requestUrl += `&${nonEmptyQSParamStrings[i]}` + } + + return requestUrl +} + /** * Calculate the area * @param {Array} size - [width, height] @@ -645,7 +734,7 @@ export function getPageUrlFromBidRequest(bidRequest) { try { const url = new URL(paramPageUrl) return url.href - } catch (err) {} + } catch (err) { } } export function hasProtocol(url) { diff --git a/modules/pairIdSystem.js b/modules/pairIdSystem.js new file mode 100644 index 00000000000..4d73d4efb7e --- /dev/null +++ b/modules/pairIdSystem.js @@ -0,0 +1,79 @@ +/** + * This module adds PAIR Id to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/pairIdSystem + * @requires module:modules/userId + */ + +import { submodule } from '../src/hook.js'; +import {getStorageManager} from '../src/storageManager.js' +import { logError } from '../src/utils.js'; +import {MODULE_TYPE_UID} from '../src/activities/modules.js'; + +const MODULE_NAME = 'pairId'; +const PAIR_ID_KEY = 'pairId'; +const DEFAULT_LIVERAMP_PAIR_ID_KEY = '_lr_pairId'; + +export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); + +function pairIdFromLocalStorage(key) { + return storage.localStorageIsEnabled ? storage.getDataFromLocalStorage(key) : null; +} + +function pairIdFromCookie(key) { + return storage.cookiesAreEnabled ? storage.getCookie(key) : null; +} + +/** @type {Submodule} */ +export const pairIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + /** + * decode the stored id value for passing to bid requests + * @function + * @param { string | undefined } value + * @returns {{pairId:string} | undefined } + */ + decode(value) { + return value && Array.isArray(value) ? {'pairId': value} : undefined + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @returns {id: string | undefined } + */ + getId(config) { + const pairIdsString = pairIdFromLocalStorage(PAIR_ID_KEY) || pairIdFromCookie(PAIR_ID_KEY) + let ids = [] + if (pairIdsString && typeof pairIdsString == 'string') { + try { + ids = ids.concat(JSON.parse(atob(pairIdsString))) + } catch (error) { + logError(error) + } + } + + const configParams = (config && config.params) || {}; + if (configParams && configParams.liveramp) { + let LRStorageLocation = configParams.liveramp.storageKey || DEFAULT_LIVERAMP_PAIR_ID_KEY + const liverampValue = pairIdFromLocalStorage(LRStorageLocation) || pairIdFromCookie(LRStorageLocation) + try { + const obj = JSON.parse(atob(liverampValue)); + ids = ids.concat(obj.envelope); + } catch (error) { + logError(error) + } + } + + if (ids.length == 0) { + logError('PairId not found.') + return undefined; + } + return {'id': ids}; + } +}; + +submodule('userId', pairIdSubmodule); diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 43a688f20e9..edfee7a0b6d 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -534,7 +534,7 @@ function _createBannerRequest(bid) { export function checkVideoPlacement(videoData, adUnitCode) { // Check for video.placement property. If property is missing display log message. - if (!deepAccess(videoData, 'placement')) { + if (FEATURES.VIDEO && !deepAccess(videoData, 'placement')) { logWarn(MSG_VIDEO_PLACEMENT_MISSING + ' for ' + adUnitCode); }; } @@ -543,7 +543,7 @@ function _createVideoRequest(bid) { var videoData = mergeDeep(deepAccess(bid.mediaTypes, 'video'), bid.params.video); var videoObj; - if (videoData !== UNDEFINED) { + if (FEATURES.VIDEO && videoData !== UNDEFINED) { videoObj = {}; checkVideoPlacement(videoData, bid.adUnitCode); for (var key in VIDEO_CUSTOM_PARAMS) { @@ -673,7 +673,7 @@ function _createImpressionObject(bid) { isInvalidNativeRequest = false; } break; - case VIDEO: + case FEATURES.VIDEO && VIDEO: videoObj = _createVideoRequest(bid); if (videoObj !== UNDEFINED) { impObj.video = videoObj; @@ -709,7 +709,7 @@ function _createImpressionObject(bid) { return impObj.hasOwnProperty(BANNER) || impObj.hasOwnProperty(NATIVE) || - impObj.hasOwnProperty(VIDEO) ? impObj : UNDEFINED; + (FEATURES.VIDEO && impObj.hasOwnProperty(VIDEO)) ? impObj : UNDEFINED; } function _addImpressionFPD(imp, bid) { @@ -810,7 +810,7 @@ function _checkMediaType(bid, newBid) { var videoRegex = new RegExp(/VAST\s+version/); if (adm.indexOf('span class="PubAPIAd"') >= 0) { newBid.mediaType = BANNER; - } else if (videoRegex.test(adm)) { + } else if (FEATURES.VIDEO && videoRegex.test(adm)) { newBid.mediaType = VIDEO; } else { try { @@ -896,7 +896,10 @@ function _assignRenderer(newBid, request) { for (let bidderRequestBidsIndex = 0; bidderRequestBidsIndex < request.bidderRequest.bids.length; bidderRequestBidsIndex++) { if (request.bidderRequest.bids[bidderRequestBidsIndex].bidId === newBid.requestId) { bidParams = request.bidderRequest.bids[bidderRequestBidsIndex].params; - context = request.bidderRequest.bids[bidderRequestBidsIndex].mediaTypes[VIDEO].context; + + if (FEATURES.VIDEO) { + context = request.bidderRequest.bids[bidderRequestBidsIndex].mediaTypes[VIDEO].context; + } adUnitCode = request.bidderRequest.bids[bidderRequestBidsIndex].adUnitCode; } } @@ -916,7 +919,7 @@ function _assignRenderer(newBid, request) { * @returns */ export function assignDealTier(newBid, bid, request) { - if (!bid?.ext?.prebiddealpriority) return; + if (!bid?.ext?.prebiddealpriority || !FEATURES.VIDEO) return; const bidRequest = getBidRequest(newBid.requestId, [request.bidderRequest]); const videoObj = deepAccess(bidRequest, 'mediaTypes.video'); if (videoObj?.context != ADPOD) return; @@ -999,7 +1002,7 @@ export const spec = { return false; } // video ad validation - if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (FEATURES.VIDEO && 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 = deepAccess(bid.mediaTypes, 'video.mimes'); let paramsVideoMimes = deepAccess(bid, 'params.video.mimes'); @@ -1284,7 +1287,7 @@ export const spec = { switch (newBid.mediaType) { case BANNER: break; - case VIDEO: + case FEATURES.VIDEO && VIDEO: newBid.width = bid.hasOwnProperty('w') ? bid.w : req.video.w; newBid.height = bid.hasOwnProperty('h') ? bid.h : req.video.h; newBid.vastXml = bid.adm; diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 5c3d430aadf..c4264d2920d 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -479,7 +479,7 @@ function interpretNativeBid(serverBid) { function interpretNativeAd(adm) { const native = JSON.parse(adm).native; const result = { - clickUrl: encodeURIComponent(native.link.url), + clickUrl: encodeURI(native.link.url), impressionTrackers: native.imptrackers }; native.assets.forEach(asset => { @@ -489,14 +489,14 @@ function interpretNativeAd(adm) { break; case OPENRTB.NATIVE.ASSET_ID.IMAGE: result.image = { - url: encodeURIComponent(asset.img.url), + url: encodeURI(asset.img.url), width: asset.img.w, height: asset.img.h }; break; case OPENRTB.NATIVE.ASSET_ID.ICON: result.icon = { - url: encodeURIComponent(asset.img.url), + url: encodeURI(asset.img.url), width: asset.img.w, height: asset.img.h }; diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 2f2d1ca7514..fa8e6f7647c 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -45,6 +45,12 @@ export const USER_IDS_CONFIG = { atype: 1 }, + // pairId + 'pairId': { + source: 'google.com', + atype: 571187 + }, + // justId 'justId': { source: 'justtag.com', diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js index 29128998fd1..c9ac9fae0f4 100644 --- a/modules/vidoomyBidAdapter.js +++ b/modules/vidoomyBidAdapter.js @@ -1,20 +1,21 @@ -import { logError, deepAccess, parseSizesInput } from '../src/utils.js'; +import {deepAccess, logError, parseSizesInput} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; -import { Renderer } from '../src/Renderer.js'; -import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import {Renderer} from '../src/Renderer.js'; +import {INSTREAM, OUTSTREAM} from '../src/video.js'; const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`; const BIDDER_CODE = 'vidoomy'; const GVLID = 380; const COOKIE_SYNC_FALLBACK_URLS = [ - 'https://x.bidswitch.net/sync?ssp=vidoomy', - 'https://ib.adnxs.com/getuid?https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D%24UID', + 'https://x.bidswitch.net/sync?ssp=vidoomy&gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&us_privacy=', 'https://pixel-sync.sitescout.com/dmp/pixelSync?nid=120&gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&redir=https%3A%2F%2Fa.vidoomy.com%2Fapi%2Frtbserver%2Fcookie%3Fi%3DCEN%26uid%3D%7BuserId%7D', 'https://cm.adform.net/cookie?redirect_url=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dadf%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D%24UID', - 'https://ups.analytics.yahoo.com/ups/58531/occ?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}' + 'https://pixel.rubiconproject.com/exchange/sync.php?p=pbs-vidoomy&gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&us_privacy=', + 'https://rtb.openx.net/sync/prebid?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&r=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dopenx%26uid%3D$%7BUID%7D', + 'https://ads.pubmatic.com/AdServer/js/user_sync.html?gdpr={{GDPR}}&gdpr_consent={{GDPR_CONSENT}}&us_privacy=&predirect=https%3A%2F%2Fa-prebid.vidoomy.com%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{GDPR}}%26gdpr_consent%3D{{GDPR_CONSENT}}%26uid%3D' ]; const isBidRequestValid = bid => { @@ -128,6 +129,20 @@ const buildRequests = (validBidRequests, bidderRequest) => { const bidfloor = deepAccess(bid, `params.bidfloor`, 0); const floor = getBidFloor(bid, adType, sizes, bidfloor); + const ortb2 = bidderRequest.ortb2 || { + bcat: [], + badv: [], + bapp: [], + btype: [], + battr: [] + }; + + let eids; + const userEids = deepAccess(bid, 'userIdAsEids'); + if (Array.isArray(userEids) && userEids.length > 0) { + eids = JSON.stringify(userEids) || ''; + } + const queryParams = { id: bid.params.id, adtype: adType, @@ -141,13 +156,19 @@ const buildRequests = (validBidRequests, bidderRequest) => { pid: bid.params.pid, requestId: bid.bidId, schain: serializeSupplyChainObj(bid.schain) || '', + eids: eids || '', bidfloor: floor, d: getDomainWithoutSubdomain(hostname), // 'vidoomy.com', // TODO: does the fallback make sense here? sp: encodeURIComponent(bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation), usp: bidderRequest.uspConsent || '', coppa: !!config.getConfig('coppa'), - videoContext: videoContext || '' + videoContext: videoContext || '', + bcat: ortb2.bcat || bid.params.bcat || [], + badv: ortb2.badv || bid.params.badv || [], + bapp: ortb2.bapp || bid.params.bapp || [], + btype: ortb2.btype || bid.params.btype || [], + battr: ortb2.battr || bid.params.battr || [] }; if (bidderRequest.gdprConsent) { @@ -253,7 +274,7 @@ const interpretResponse = (serverResponse, bidRequest) => { } }; -function getUserSyncs (syncOptions, responses, gdprConsent, uspConsent) { +function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent) { if (syncOptions.iframeEnabled || syncOptions.pixelEnabled) { const pixelType = syncOptions.pixelEnabled ? 'image' : 'iframe'; const urls = deepAccess(responses, '0.body.pixels') || COOKIE_SYNC_FALLBACK_URLS; @@ -280,7 +301,7 @@ export const spec = { registerBidder(spec); -function getDomainWithoutSubdomain (hostname) { +function getDomainWithoutSubdomain(hostname) { const parts = hostname.split('.'); const newParts = []; for (let i = parts.length - 1; i >= 0; i--) { diff --git a/modules/vidoomyBidAdapter.md b/modules/vidoomyBidAdapter.md index d91ace5a7b9..b4095606d9b 100644 --- a/modules/vidoomyBidAdapter.md +++ b/modules/vidoomyBidAdapter.md @@ -27,7 +27,12 @@ var adUnits = [ params: { id: '123123', pid: '123123', - bidfloor: 0.5 // This is optional + bidfloor: 0.5, // This is optional + bcat: ['IAB1-1'], // Optional - default is [] + badv: ['example.com'], // Optional - default is [] + bapp: ['app.com'], // Optional - default is [] + btype: [1, 2, 3], // Optional - default is [] + battr: [1, 2, 3] // Optional - default is [] } } ] @@ -52,7 +57,12 @@ var adUnits = [ params: { id: '123123', pid: '123123', - bidfloor: 0.5 // This is optional + bidfloor: 0.5, // This is optional + bcat: ['IAB1-1'], // Optional - default is [] + badv: ['example.com'], // Optional - default is [] + bapp: ['app.com'], // Optional - default is [] + btype: [1, 2, 3], // Optional - default is [] + battr: [1, 2, 3] // Optional - default is [] } } ] diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 526e1911a06..bef453cb4ad 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -68,6 +68,7 @@ export const spec = { const videoBidRequests = bidRequests.filter(request => hasVideoMediaType(request)); let serverRequests = []; const eids = getEids(bidRequests[0]) || []; + if (bannerBidRequests.length > 0) { let serverRequest = { pbav: '$prebid.version$', @@ -80,7 +81,9 @@ export const spec = { userConsent: JSON.stringify({ // case of undefined, stringify will remove param gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', - cmp: deepAccess(bidderRequest, 'gdprConsent.consentString') || '' + cmp: deepAccess(bidderRequest, 'gdprConsent.consentString') || '', + gpp: deepAccess(bidderRequest, 'gppConsent.gppString') || '', + gpp_sid: deepAccess(bidderRequest, 'gppConsent.applicableSections') || [] }), us_privacy: deepAccess(bidderRequest, 'uspConsent') || '' }; @@ -514,12 +517,19 @@ function openRtbSite(bidRequest, bidderRequest) { */ function populateOpenRtbGdpr(openRtbRequest, bidderRequest) { const gdpr = bidderRequest.gdprConsent; - if (gdpr && 'gdprApplies' in gdpr) { - deepSetValue(openRtbRequest, 'regs.ext.gdpr', gdpr.gdprApplies ? 1 : 0); - deepSetValue(openRtbRequest, 'user.ext.consent', gdpr.consentString); + const gpp = deepAccess(bidderRequest, 'gppConsent.gppString'); + const gppsid = deepAccess(bidderRequest, 'gppConsent.applicableSections'); + if (gpp) { + deepSetValue(openRtbRequest, 'regs.ext.gpp', gpp); + } else { + deepSetValue(openRtbRequest, 'regs.ext.gdpr', gdpr && gdpr.gdprApplies ? 1 : 0); + deepSetValue(openRtbRequest, 'user.ext.consent', gdpr && gdpr.consentString ? gdpr.consentString : ''); + } + if (gppsid && gppsid.length > 0) { + deepSetValue(openRtbRequest, 'regs.ext.gpp_sid', gppsid); } const uspConsent = deepAccess(bidderRequest, 'uspConsent'); - if (uspConsent) { + if (!gpp && uspConsent) { deepSetValue(openRtbRequest, 'regs.ext.us_privacy', uspConsent); } } diff --git a/package-lock.json b/package-lock.json index 54442c348e0..7baf4ab3094 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.46.0-pre", + "version": "7.47.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 7ebcb655ed7..df9a5551c50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.46.0-pre", + "version": "7.47.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { diff --git a/src/config.js b/src/config.js index 0922a2a2bea..48909d677e9 100644 --- a/src/config.js +++ b/src/config.js @@ -66,158 +66,109 @@ export function newConfig() { function resetConfig() { defaults = {}; - let newConfig = { - // `debug` is equivalent to legacy `pbjs.logging` property - _debug: DEFAULT_DEBUG, - get debug() { - return this._debug; - }, - set debug(val) { - this._debug = val; - }, - // default timeout for all bids - _bidderTimeout: DEFAULT_BIDDER_TIMEOUT, - get bidderTimeout() { - return this._bidderTimeout; - }, - set bidderTimeout(val) { - this._bidderTimeout = val; - }, + function getProp(name) { + return props[name].val; + } - _publisherDomain: null, - get publisherDomain() { - return this._publisherDomain; - }, - set publisherDomain(val) { - logWarn('publisherDomain is deprecated and has no effect since v7 - use pageUrl instead') - this._publisherDomain = val; - }, + function setProp(name, val) { + props[name].val = val; + } - // calls existing function which may be moved after deprecation - _priceGranularity: GRANULARITY_OPTIONS.MEDIUM, - set priceGranularity(val) { - if (validatePriceGranularity(val)) { - if (typeof val === 'string') { - this._priceGranularity = (hasGranularity(val)) ? val : GRANULARITY_OPTIONS.MEDIUM; - } else if (isPlainObject(val)) { - this._customPriceBucket = val; - this._priceGranularity = GRANULARITY_OPTIONS.CUSTOM; - logMessage('Using custom price granularity'); + const props = { + publisherDomain: { + set(val) { + if (val != null) { + logWarn('publisherDomain is deprecated and has no effect since v7 - use pageUrl instead') } + setProp('publisherDomain', val); } }, - get priceGranularity() { - return this._priceGranularity; - }, - - _customPriceBucket: {}, - get customPriceBucket() { - return this._customPriceBucket; - }, - - /** - * mediaTypePriceGranularity - * @type {MediaTypePriceGranularity} - */ - _mediaTypePriceGranularity: {}, - - get mediaTypePriceGranularity() { - return this._mediaTypePriceGranularity; - }, - set mediaTypePriceGranularity(val) { - this._mediaTypePriceGranularity = Object.keys(val).reduce((aggregate, item) => { - if (validatePriceGranularity(val[item])) { + priceGranularity: { + val: GRANULARITY_OPTIONS.MEDIUM, + set(val) { + if (validatePriceGranularity(val)) { if (typeof val === 'string') { - aggregate[item] = (hasGranularity(val[item])) ? val[item] : this._priceGranularity; + setProp('priceGranularity', (hasGranularity(val)) ? val : GRANULARITY_OPTIONS.MEDIUM); } else if (isPlainObject(val)) { - aggregate[item] = val[item]; - logMessage(`Using custom price granularity for ${item}`); + setProp('customPriceBucket', val); + setProp('priceGranularity', GRANULARITY_OPTIONS.CUSTOM) + logMessage('Using custom price granularity'); } - } else { - logWarn(`Invalid price granularity for media type: ${item}`); } - return aggregate; - }, {}); - }, - - _sendAllBids: DEFAULT_ENABLE_SEND_ALL_BIDS, - get enableSendAllBids() { - return this._sendAllBids; + } }, - set enableSendAllBids(val) { - this._sendAllBids = val; + customPriceBucket: { + val: {}, + set() {} }, - - _useBidCache: DEFAULT_BID_CACHE, - get useBidCache() { - return this._useBidCache; + mediaTypePriceGranularity: { + val: {}, + set(val) { + val != null && setProp('mediaTypePriceGranularity', Object.keys(val).reduce((aggregate, item) => { + if (validatePriceGranularity(val[item])) { + if (typeof val === 'string') { + aggregate[item] = (hasGranularity(val[item])) ? val[item] : getProp('priceGranularity'); + } else if (isPlainObject(val)) { + aggregate[item] = val[item]; + logMessage(`Using custom price granularity for ${item}`); + } + } else { + logWarn(`Invalid price granularity for media type: ${item}`); + } + return aggregate; + }, {})); + } }, - set useBidCache(val) { - this._useBidCache = val; + bidderSequence: { + val: DEFAULT_BIDDER_SEQUENCE, + set(val) { + if (VALID_ORDERS[val]) { + setProp('bidderSequence', val); + } else { + logWarn(`Invalid order: ${val}. Bidder Sequence was not set.`); + } + } }, + auctionOptions: { + val: {}, + set(val) { + if (validateauctionOptions(val)) { + setProp('auctionOptions', val); + } + } + } + } + let newConfig = { + // `debug` is equivalent to legacy `pbjs.logging` property + debug: DEFAULT_DEBUG, + bidderTimeout: DEFAULT_BIDDER_TIMEOUT, + enableSendAllBids: DEFAULT_ENABLE_SEND_ALL_BIDS, + useBidCache: DEFAULT_BID_CACHE, /** * deviceAccess set to false will disable setCookie, getCookie, hasLocalStorage * @type {boolean} */ - _deviceAccess: DEFAULT_DEVICE_ACCESS, - get deviceAccess() { - return this._deviceAccess; - }, - set deviceAccess(val) { - this._deviceAccess = val; - }, - - _bidderSequence: DEFAULT_BIDDER_SEQUENCE, - get bidderSequence() { - return this._bidderSequence; - }, - set bidderSequence(val) { - if (VALID_ORDERS[val]) { - this._bidderSequence = val; - } else { - logWarn(`Invalid order: ${val}. Bidder Sequence was not set.`); - } - }, + deviceAccess: DEFAULT_DEVICE_ACCESS, // timeout buffer to adjust for bidder CDN latency - _timeoutBuffer: DEFAULT_TIMEOUTBUFFER, - get timeoutBuffer() { - return this._timeoutBuffer; - }, - set timeoutBuffer(val) { - this._timeoutBuffer = val; - }, - - _disableAjaxTimeout: DEFAULT_DISABLE_AJAX_TIMEOUT, - get disableAjaxTimeout() { - return this._disableAjaxTimeout; - }, - set disableAjaxTimeout(val) { - this._disableAjaxTimeout = val; - }, + timeoutBuffer: DEFAULT_TIMEOUTBUFFER, + disableAjaxTimeout: DEFAULT_DISABLE_AJAX_TIMEOUT, // default max nested iframes for referer detection - _maxNestedIframes: DEFAULT_MAX_NESTED_IFRAMES, - get maxNestedIframes() { - return this._maxNestedIframes; - }, - set maxNestedIframes(val) { - this._maxNestedIframes = val; - }, - - _auctionOptions: {}, - get auctionOptions() { - return this._auctionOptions; - }, - set auctionOptions(val) { - if (validateauctionOptions(val)) { - this._auctionOptions = val; - } - }, + maxNestedIframes: DEFAULT_MAX_NESTED_IFRAMES, }; + Object.defineProperties(newConfig, + Object.fromEntries(Object.entries(props) + .map(([k, def]) => [k, Object.assign({ + get: getProp.bind(null, k), + set: setProp.bind(null, k), + enumerable: true, + }, def)])) + ); + if (config) { callSubscribers( Object.keys(config).reduce((memo, topic) => { diff --git a/test/spec/modules/1plusXRtdProvider_spec.js b/test/spec/modules/1plusXRtdProvider_spec.js index 496c005f470..8657c37d7d8 100644 --- a/test/spec/modules/1plusXRtdProvider_spec.js +++ b/test/spec/modules/1plusXRtdProvider_spec.js @@ -332,15 +332,16 @@ describe('1plusXRtdProvider', () => { } it('correctly builds URLs if gdpr parameters are present', () => { - const url1 = getPapiUrl(customer) - const url2 = getPapiUrl(customer, extractConsent(consent)) - expect(['&consent_string=myConsent&gdpr_applies=1', '&gdpr_applies=1&consent_string=myConsent']).to.contain(url2.replace(url1, '')) + const url1 = getPapiUrl(customer); + const url2 = getPapiUrl(customer, extractConsent(consent)); + expect(['&consent_string=myConsent&gdpr_applies=1', '&gdpr_applies=1&consent_string=myConsent']).to.contain(url2.replace(url1, '')); }) - it('correctly builds URLs if fpid parameters are present') - const url1 = getPapiUrl(customer) - const url2 = getPapiUrl(customer, {}, 'my_first_party_id') - expect(url2.replace(url1, '')).to.equal('&fpid=my_first_party_id') + it('correctly builds URLs if fpid parameters are present', () => { + const url1 = getPapiUrl(customer); + const url2 = getPapiUrl(customer, {}, 'my_first_party_id'); + expect(url2.replace(url1, '')).to.equal('&fpid=my_first_party_id'); + }) }) describe('updateBidderConfig', () => { diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 759b16a81bb..adba79ddc96 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1481,31 +1481,6 @@ describe('Adagio bid adapter', () => { }); }); - describe.skip('optional params auto detection', function() { - it('should auto detect adUnitElementId when GPT is used', function() { - sandbox.stub(utils, 'getGptSlotInfoForAdUnitCode').withArgs('banner').returns({divId: 'gpt-banner'}); - expect(adagio.autoDetectAdUnitElementId('banner')).to.eq('gpt-banner'); - }); - }); - - describe.skip('print number handling', function() { - it('should return 1 if no adunit-code found. This means it is the first auction', function() { - sandbox.stub(adagio, 'getPageviewId').returns('abc-def'); - expect(adagio.computePrintNumber('adunit-code')).to.eql(1); - }); - - it('should increment the adunit print number when the adunit-code has already been used for an other auction', function() { - sandbox.stub(adagio, 'getPageviewId').returns('abc-def'); - - window.top.ADAGIO.adUnits['adunit-code'] = { - pageviewId: 'abc-def', - printNumber: 1, - }; - - expect(adagio.computePrintNumber('adunit-code')).to.eql(2); - }); - }); - describe('site information using refererDetection or window.top', function() { it('should returns domain, page and window.referrer in a window.top context', function() { const bidderRequest = new BidderRequestBuilder({ diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 150e013af98..153565eb3ca 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -8,6 +8,7 @@ import { getStorageManager } from 'src/storageManager.js'; describe('adnuntiusBidAdapter', function () { const URL = 'https://ads.adnuntius.delivery/i?tzo='; + const EURO_URL = 'https://europe.delivery.adnuntius.com/i?tzo='; const GVLID = 855; const usi = utils.generateUUID() const meta = [{ key: 'usi', value: usi }] @@ -35,7 +36,7 @@ describe('adnuntiusBidAdapter', function () { const ENDPOINT_URL_VIDEO = `${URL}${tzo}&format=json&userId=${usi}&tt=vast4`; const ENDPOINT_URL_NOCOOKIE = `${URL}${tzo}&format=json&userId=${usi}&noCookies=true`; const ENDPOINT_URL_SEGMENTS = `${URL}${tzo}&format=json&segments=segment1,segment2,segment3&userId=${usi}`; - const ENDPOINT_URL_CONSENT = `${URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; + const ENDPOINT_URL_CONSENT = `${EURO_URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; const adapter = newBidder(spec); const bidderRequests = [ diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 9e88e2875c7..1603c6e9397 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -257,11 +257,15 @@ describe('AppNexusAdapter', function () { transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' }]; - let types = ['banner', 'video']; + let types = ['banner']; if (FEATURES.NATIVE) { types.push('native'); } + if (FEATURES.VIDEO) { + types.push('video'); + } + types.forEach(type => { getAdUnitsStub.callsFake(function (...args) { return adUnits; @@ -290,122 +294,303 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].ad_types).to.not.exist; }); - it('should populate the ad_types array on outstream requests', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes.video = { context: 'outstream' }; + if (FEATURES.VIDEO) { + it('should populate the ad_types array on outstream requests', function () { + const bidRequest = Object.assign({}, bidRequests[0]); + bidRequest.mediaTypes = {}; + bidRequest.mediaTypes.video = { context: 'outstream' }; - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); - expect(payload.tags[0].ad_types).to.deep.equal(['video']); - expect(payload.tags[0].hb_source).to.deep.equal(1); - }); + expect(payload.tags[0].ad_types).to.deep.equal(['video']); + expect(payload.tags[0].hb_source).to.deep.equal(1); + }); - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); + it('should attach valid video params to the tag', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + video: { + id: 123, + minduration: 100, + foobar: 'invalid' + } + } + } + ); - it('should attach valid video params to the tag', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + id: 123, + minduration: 100 + }); + expect(payload.tags[0].hb_source).to.deep.equal(1); + }); + + it('should include ORTB video values when video params were not set', function () { + let bidRequest = deepClone(bidRequests[0]); + bidRequest.params = { + placementId: '1234235', + video: { + skippable: true, + playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'], + context: 'outstream' + } + }; + bidRequest.mediaTypes = { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'], + skip: 0, + minduration: 5, + api: [1, 5, 6], + playbackmethod: [2, 4] + } + }; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].video).to.deep.equal({ + minduration: 5, + playback_method: 2, + skippable: true, + context: 4 + }); + expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4]) + }); + + it('should add video property when adUnit includes a renderer', function () { + const videoData = { + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + }, params: { placementId: '10433394', video: { - id: 123, - minduration: 100, - foobar: 'invalid' + skippable: true, + playback_method: ['auto_play_sound_off'] } } - } - ); + }; - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - id: 123, - minduration: 100 - }); - expect(payload.tags[0].hb_source).to.deep.equal(1); - }); + let bidRequest1 = deepClone(bidRequests[0]); + bidRequest1 = Object.assign({}, bidRequest1, videoData, { + renderer: { + url: 'https://test.renderer.url', + render: function () { } + } + }); - it('should include ORTB video values when video params were not set', function () { - let bidRequest = deepClone(bidRequests[0]); - bidRequest.params = { - placementId: '1234235', - video: { + let bidRequest2 = deepClone(bidRequests[0]); + bidRequest2.adUnitCode = 'adUnit_code_2'; + bidRequest2 = Object.assign({}, bidRequest2, videoData); + + const request = spec.buildRequests([bidRequest1, bidRequest2]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ skippable: true, - playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'], - context: 'outstream' - } - }; - bidRequest.mediaTypes = { - video: { - playerSize: [640, 480], - context: 'outstream', - mimes: ['video/mp4'], - skip: 0, - minduration: 5, - api: [1, 5, 6], - playbackmethod: [2, 4] - } - }; + playback_method: 2, + custom_renderer_present: true + }); + expect(payload.tags[1].video).to.deep.equal({ + skippable: true, + playback_method: 2 + }); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + it('should duplicate adpod placements into batches and set correct maxduration', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 300, + durationRangeSec: [15, 30], + } + } + } + ); - expect(payload.tags[0].video).to.deep.equal({ - minduration: 5, - playback_method: 2, - skippable: true, - context: 4 + const request = spec.buildRequests([bidRequest]); + const payload1 = JSON.parse(request[0].data); + const payload2 = JSON.parse(request[1].data); + + // 300 / 15 = 20 total + expect(payload1.tags.length).to.equal(15); + expect(payload2.tags.length).to.equal(5); + + expect(payload1.tags[0]).to.deep.equal(payload1.tags[1]); + expect(payload1.tags[0].video.maxduration).to.equal(30); + + expect(payload2.tags[0]).to.deep.equal(payload1.tags[1]); + expect(payload2.tags[0].video.maxduration).to.equal(30); }); - expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4]) - }); - it('should add video property when adUnit includes a renderer', function () { - const videoData = { - mediaTypes: { - video: { - context: 'outstream', - mimes: ['video/mp4'] + it('should round down adpod placements when numbers are uneven', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 123, + durationRangeSec: [45], + } + } } - }, - params: { - placementId: '10433394', - video: { - skippable: true, - playback_method: ['auto_play_sound_off'] + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags.length).to.equal(2); + }); + + it('should duplicate adpod placements when requireExactDuration is set', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 300, + durationRangeSec: [15, 30], + requireExactDuration: true, + } + } } - } - }; + ); - let bidRequest1 = deepClone(bidRequests[0]); - bidRequest1 = Object.assign({}, bidRequest1, videoData, { - renderer: { - url: 'https://test.renderer.url', - render: function () { } - } + // 20 total placements with 15 max impressions = 2 requests + const request = spec.buildRequests([bidRequest]); + expect(request.length).to.equal(2); + + // 20 spread over 2 requests = 15 in first request, 5 in second + const payload1 = JSON.parse(request[0].data); + const payload2 = JSON.parse(request[1].data); + expect(payload1.tags.length).to.equal(15); + expect(payload2.tags.length).to.equal(5); + + // 10 placements should have max/min at 15 + // 10 placemenst should have max/min at 30 + const payload1tagsWith15 = payload1.tags.filter(tag => tag.video.maxduration === 15); + const payload1tagsWith30 = payload1.tags.filter(tag => tag.video.maxduration === 30); + expect(payload1tagsWith15.length).to.equal(10); + expect(payload1tagsWith30.length).to.equal(5); + + // 5 placemenst with min/max at 30 were in the first request + // so 5 remaining should be in the second + const payload2tagsWith30 = payload2.tags.filter(tag => tag.video.maxduration === 30); + expect(payload2tagsWith30.length).to.equal(5); }); - let bidRequest2 = deepClone(bidRequests[0]); - bidRequest2.adUnitCode = 'adUnit_code_2'; - bidRequest2 = Object.assign({}, bidRequest2, videoData); + it('should set durations for placements when requireExactDuration is set and numbers are uneven', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 105, + durationRangeSec: [15, 30, 60], + requireExactDuration: true, + } + } + } + ); - const request = spec.buildRequests([bidRequest1, bidRequest2]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - skippable: true, - playback_method: 2, - custom_renderer_present: true + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + expect(payload.tags.length).to.equal(7); + + const tagsWith15 = payload.tags.filter(tag => tag.video.maxduration === 15); + const tagsWith30 = payload.tags.filter(tag => tag.video.maxduration === 30); + const tagsWith60 = payload.tags.filter(tag => tag.video.maxduration === 60); + expect(tagsWith15.length).to.equal(3); + expect(tagsWith30.length).to.equal(3); + expect(tagsWith60.length).to.equal(1); }); - expect(payload.tags[1].video).to.deep.equal({ - skippable: true, - playback_method: 2 + + it('should break adpod request into batches', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 225, + durationRangeSec: [5], + } + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload1 = JSON.parse(request[0].data); + const payload2 = JSON.parse(request[1].data); + const payload3 = JSON.parse(request[2].data); + + expect(payload1.tags.length).to.equal(15); + expect(payload2.tags.length).to.equal(15); + expect(payload3.tags.length).to.equal(15); + }); + + it('should contain hb_source value for adpod', function () { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { placementId: '14542875' } + }, + { + mediaTypes: { + video: { + context: 'adpod', + playerSize: [640, 480], + adPodDurationSec: 300, + durationRangeSec: [15, 30], + } + } + } + ); + const request = spec.buildRequests([bidRequest])[0]; + const payload = JSON.parse(request.data); + expect(payload.tags[0].hb_source).to.deep.equal(7); }); + } // VIDEO + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); }); it('should attach valid user params to the tag', function () { @@ -486,185 +671,6 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].reserve).to.exist.and.to.equal(3); }); - it('should duplicate adpod placements into batches and set correct maxduration', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placement_id: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload1 = JSON.parse(request[0].data); - const payload2 = JSON.parse(request[1].data); - - // 300 / 15 = 20 total - expect(payload1.tags.length).to.equal(15); - expect(payload2.tags.length).to.equal(5); - - expect(payload1.tags[0]).to.deep.equal(payload1.tags[1]); - expect(payload1.tags[0].video.maxduration).to.equal(30); - - expect(payload2.tags[0]).to.deep.equal(payload1.tags[1]); - expect(payload2.tags[0].video.maxduration).to.equal(30); - }); - - it('should round down adpod placements when numbers are uneven', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placement_id: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 123, - durationRangeSec: [45], - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags.length).to.equal(2); - }); - - it('should duplicate adpod placements when requireExactDuration is set', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placement_id: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - requireExactDuration: true, - } - } - } - ); - - // 20 total placements with 15 max impressions = 2 requests - const request = spec.buildRequests([bidRequest]); - expect(request.length).to.equal(2); - - // 20 spread over 2 requests = 15 in first request, 5 in second - const payload1 = JSON.parse(request[0].data); - const payload2 = JSON.parse(request[1].data); - expect(payload1.tags.length).to.equal(15); - expect(payload2.tags.length).to.equal(5); - - // 10 placements should have max/min at 15 - // 10 placemenst should have max/min at 30 - const payload1tagsWith15 = payload1.tags.filter(tag => tag.video.maxduration === 15); - const payload1tagsWith30 = payload1.tags.filter(tag => tag.video.maxduration === 30); - expect(payload1tagsWith15.length).to.equal(10); - expect(payload1tagsWith30.length).to.equal(5); - - // 5 placemenst with min/max at 30 were in the first request - // so 5 remaining should be in the second - const payload2tagsWith30 = payload2.tags.filter(tag => tag.video.maxduration === 30); - expect(payload2tagsWith30.length).to.equal(5); - }); - - it('should set durations for placements when requireExactDuration is set and numbers are uneven', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placement_id: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 105, - durationRangeSec: [15, 30, 60], - requireExactDuration: true, - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags.length).to.equal(7); - - const tagsWith15 = payload.tags.filter(tag => tag.video.maxduration === 15); - const tagsWith30 = payload.tags.filter(tag => tag.video.maxduration === 30); - const tagsWith60 = payload.tags.filter(tag => tag.video.maxduration === 60); - expect(tagsWith15.length).to.equal(3); - expect(tagsWith30.length).to.equal(3); - expect(tagsWith60.length).to.equal(1); - }); - - it('should break adpod request into batches', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placement_id: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 225, - durationRangeSec: [5], - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload1 = JSON.parse(request[0].data); - const payload2 = JSON.parse(request[1].data); - const payload3 = JSON.parse(request[2].data); - - expect(payload1.tags.length).to.equal(15); - expect(payload2.tags.length).to.equal(15); - expect(payload3.tags.length).to.equal(15); - }); - - it('should contain hb_source value for adpod', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placement_id: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - } - } - } - ); - const request = spec.buildRequests([bidRequest])[0]; - const payload = JSON.parse(request.data); - expect(payload.tags[0].hb_source).to.deep.equal(7); - }); - it('should contain hb_source value for other media', function () { let bidRequest = Object.assign({}, bidRequests[0], @@ -1392,27 +1398,31 @@ describe('AppNexusAdapter', function () { }); it('should populate iab_support object at the root level if omid support is detected', function () { - // with bid.params.frameworks - let bidRequest_A = Object.assign({}, bidRequests[0], { - params: { - frameworks: [1, 2, 5, 6], - video: { - frameworks: [1, 2, 5, 6] + let request, payload; + + if (FEATURES.VIDEO) { + // with bid.params.frameworks + let bidRequest_A = Object.assign({}, bidRequests[0], { + params: { + frameworks: [1, 2, 5, 6], + video: { + frameworks: [1, 2, 5, 6] + } } - } - }); - let request = spec.buildRequests([bidRequest_A]); - let payload = JSON.parse(request.data); - expect(payload.iab_support).to.be.an('object'); - expect(payload.iab_support).to.deep.equal({ - omidpn: 'Appnexus', - omidpv: '$prebid.version$' - }); - expect(payload.tags[0].banner_frameworks).to.be.an('array'); - expect(payload.tags[0].banner_frameworks).to.deep.equal([1, 2, 5, 6]); - expect(payload.tags[0].video_frameworks).to.be.an('array'); - expect(payload.tags[0].video_frameworks).to.deep.equal([1, 2, 5, 6]); - expect(payload.tags[0].video.frameworks).to.not.exist; + }); + request = spec.buildRequests([bidRequest_A]); + payload = JSON.parse(request.data); + expect(payload.iab_support).to.be.an('object'); + expect(payload.iab_support).to.deep.equal({ + omidpn: 'Appnexus', + omidpv: '$prebid.version$' + }); + expect(payload.tags[0].banner_frameworks).to.be.an('array'); + expect(payload.tags[0].banner_frameworks).to.deep.equal([1, 2, 5, 6]); + expect(payload.tags[0].video_frameworks).to.be.an('array'); + expect(payload.tags[0].video_frameworks).to.deep.equal([1, 2, 5, 6]); + expect(payload.tags[0].video.frameworks).to.not.exist; + } // without bid.params.frameworks const bidRequest_B = Object.assign({}, bidRequests[0]); @@ -1422,19 +1432,21 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].banner_frameworks).to.not.exist; expect(payload.tags[0].video_frameworks).to.not.exist; - // with video.frameworks but it is not an array - const bidRequest_C = Object.assign({}, bidRequests[0], { - params: { - video: { - frameworks: "'1', '2', '3', '6'" + if (FEATURES.VIDEO) { + // with video.frameworks but it is not an array + const bidRequest_C = Object.assign({}, bidRequests[0], { + params: { + video: { + frameworks: "'1', '2', '3', '6'" + } } - } - }); - request = spec.buildRequests([bidRequest_C]); - payload = JSON.parse(request.data); - expect(payload.iab_support).to.not.exist; - expect(payload.tags[0].banner_frameworks).to.not.exist; - expect(payload.tags[0].video_frameworks).to.not.exist; + }); + request = spec.buildRequests([bidRequest_C]); + payload = JSON.parse(request.data); + expect(payload.iab_support).to.not.exist; + expect(payload.tags[0].banner_frameworks).to.not.exist; + expect(payload.tags[0].video_frameworks).to.not.exist; + } }); }) @@ -1591,116 +1603,118 @@ describe('AppNexusAdapter', function () { expect(result.length).to.equal(0); }); - it('handles outstream video responses', function () { - let response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'content': '' - } - }, - 'javascriptTrackers': '' + if (FEATURES.VIDEO) { + it('handles outstream video responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'content': '' + } + }, + 'javascriptTrackers': '' + }] }] - }] - }; - let bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'outstream' + }; + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'outstream' + } } - } - }] - } + }] + } - let result = spec.interpretResponse({ body: response }, { bidderRequest }); - expect(result[0]).to.have.property('vastXml'); - expect(result[0]).to.have.property('vastImpUrl'); - expect(result[0]).to.have.property('mediaType', 'video'); - }); + let result = spec.interpretResponse({ body: response }, { bidderRequest }); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('vastImpUrl'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); - it('handles instream video responses', function () { - let response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'asset_url': 'https://sample.vastURL.com/here/vid' - } - }, - 'javascriptTrackers': '' + it('handles instream video responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'asset_url': 'https://sample.vastURL.com/here/vid' + } + }, + 'javascriptTrackers': '' + }] }] - }] - }; - let bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'instream' + }; + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'instream' + } } - } - }] - } + }] + } - let result = spec.interpretResponse({ body: response }, { bidderRequest }); - expect(result[0]).to.have.property('vastUrl'); - expect(result[0]).to.have.property('vastImpUrl'); - expect(result[0]).to.have.property('mediaType', 'video'); - }); + let result = spec.interpretResponse({ body: response }, { bidderRequest }); + expect(result[0]).to.have.property('vastUrl'); + expect(result[0]).to.have.property('vastImpUrl'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); - it('handles adpod responses', function () { - let response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'brand_category_id': 10, - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'asset_url': 'https://sample.vastURL.com/here/adpod', - 'duration_ms': 30000, + it('handles adpod responses', function () { + let response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'brand_category_id': 10, + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'asset_url': 'https://sample.vastURL.com/here/adpod', + 'duration_ms': 30000, + } + }, + 'viewability': { + 'config': '' } - }, - 'viewability': { - 'config': '' - } + }] }] - }] - }; + }; - let bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'adpod' + let bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'adpod' + } } - } - }] - }; - bfStub.returns('1'); + }] + }; + bfStub.returns('1'); - let result = spec.interpretResponse({ body: response }, { bidderRequest }); - expect(result[0]).to.have.property('vastUrl'); - expect(result[0].video.context).to.equal('adpod'); - expect(result[0].video.durationSeconds).to.equal(30); - }); + let result = spec.interpretResponse({ body: response }, { bidderRequest }); + expect(result[0]).to.have.property('vastUrl'); + expect(result[0].video.context).to.equal('adpod'); + expect(result[0].video.durationSeconds).to.equal(30); + }); + } if (FEATURES.NATIVE) { it('handles native responses', function () { @@ -1758,59 +1772,61 @@ describe('AppNexusAdapter', function () { }); } - it('supports configuring outstream renderers', function () { - const outstreamResponse = deepClone(response); - outstreamResponse.tags[0].ads[0].rtb.video = {}; - outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js'; + if (FEATURES.VIDEO) { + it('supports configuring outstream renderers', function () { + const outstreamResponse = deepClone(response); + outstreamResponse.tags[0].ads[0].rtb.video = {}; + outstreamResponse.tags[0].ads[0].renderer_url = 'renderer.js'; - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - renderer: { - options: { - adText: 'configured' - } - }, - mediaTypes: { - video: { - context: 'outstream' + const bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + renderer: { + options: { + adText: 'configured' + } + }, + mediaTypes: { + video: { + context: 'outstream' + } } - } - }] - }; + }] + }; - const result = spec.interpretResponse({ body: outstreamResponse }, { bidderRequest }); - expect(result[0].renderer.config).to.deep.equal( - bidderRequest.bids[0].renderer.options - ); - }); + const result = spec.interpretResponse({ body: outstreamResponse }, { bidderRequest }); + expect(result[0].renderer.config).to.deep.equal( + bidderRequest.bids[0].renderer.options + ); + }); - it('should add deal_priority and deal_code', function () { - let responseWithDeal = deepClone(response); - responseWithDeal.tags[0].ads[0].ad_type = 'video'; - responseWithDeal.tags[0].ads[0].deal_priority = 5; - responseWithDeal.tags[0].ads[0].deal_code = '123'; - responseWithDeal.tags[0].ads[0].rtb.video = { - duration_ms: 1500, - player_width: 640, - player_height: 340, - }; + it('should add deal_priority and deal_code', function () { + let responseWithDeal = deepClone(response); + responseWithDeal.tags[0].ads[0].ad_type = 'video'; + responseWithDeal.tags[0].ads[0].deal_priority = 5; + responseWithDeal.tags[0].ads[0].deal_code = '123'; + responseWithDeal.tags[0].ads[0].rtb.video = { + duration_ms: 1500, + player_width: 640, + player_height: 340, + }; - let bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'adpod' + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'adpod' + } } - } - }] - } - let result = spec.interpretResponse({ body: responseWithDeal }, { bidderRequest }); - expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); - expect(result[0].video.dealTier).to.equal(5); - }); + }] + } + let result = spec.interpretResponse({ body: responseWithDeal }, { bidderRequest }); + expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); + expect(result[0].video.dealTier).to.equal(5); + }); + } it('should add advertiser id', function () { let responseAdvertiserId = deepClone(response); diff --git a/test/spec/modules/axonixBidAdapter_spec.js b/test/spec/modules/axonixBidAdapter_spec.js index 37f409e5769..c1cc6d46fb2 100644 --- a/test/spec/modules/axonixBidAdapter_spec.js +++ b/test/spec/modules/axonixBidAdapter_spec.js @@ -286,26 +286,6 @@ describe('AxonixBidAdapter', function () { }); }); - describe.skip('buildRequests: can handle native ad requests', function () { - it('creates ServerRequests pointing to the correct region and endpoint if it changes', function () { - // loop: - // set supply id - // set region/endpoint in ssp config - // call buildRequests, validate request (url, method, supply id) - expect.fail('Not implemented'); - }); - - it('creates ServerRequests pointing to default endpoint if missing', function () { - // no endpoint in config means default value openrtb.axonix.com - expect.fail('Not implemented'); - }); - - it('creates ServerRequests pointing to default region if missing', function () { - // no region in config means default value us-east-1 - expect.fail('Not implemented'); - }); - }); - describe('interpretResponse', function () { it('considers corner cases', function() { expect(spec.interpretResponse(null)).to.be.an('array').that.is.empty; @@ -331,13 +311,6 @@ describe('AxonixBidAdapter', function () { expect(response).to.be.an('array').that.is.not.empty; expect(response[0]).to.equal(VIDEO_RESPONSE.body[0]); }); - - it.skip('parses 1 native responses', function () { - // passing 1 valid native in a response generates an array with 1 correct prebid response - // examine mediaType:native, native element - // check nativeBidIsValid from {@link file://./../../../src/native.js} - expect.fail('Not implemented'); - }); }); describe('onBidWon', function () { diff --git a/test/spec/modules/big-richmediaBidAdapter_spec.js b/test/spec/modules/big-richmediaBidAdapter_spec.js index b2ee0725d49..f01e261ef9f 100644 --- a/test/spec/modules/big-richmediaBidAdapter_spec.js +++ b/test/spec/modules/big-richmediaBidAdapter_spec.js @@ -92,40 +92,42 @@ describe('bigRichMediaAdapterTests', function () { expect(payload.tags[0].sizes).to.have.lengthOf(3); }); - it('should build video bid request', function() { - const bidRequest = deepClone(bidRequests[0]); - bidRequest.params = { - placementId: '1234235', - video: { - skippable: true, - playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'], - context: 'outstream', - format: 'sticky-top' - } - }; - bidRequest.mediaTypes = { - video: { - playerSize: [640, 480], - context: 'outstream', - mimes: ['video/mp4'], - skip: 0, - minduration: 5, - api: [1, 5, 6], - playbackmethod: [2, 4] - } - }; + if (FEATURES.VIDEO) { + it('should build video bid request', function() { + const bidRequest = deepClone(bidRequests[0]); + bidRequest.params = { + placementId: '1234235', + video: { + skippable: true, + playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'], + context: 'outstream', + format: 'sticky-top' + } + }; + bidRequest.mediaTypes = { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'], + skip: 0, + minduration: 5, + api: [1, 5, 6], + playbackmethod: [2, 4] + } + }; - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - minduration: 5, - playback_method: 2, - skippable: true, - context: 4 + expect(payload.tags[0].video).to.deep.equal({ + minduration: 5, + playback_method: 2, + skippable: true, + context: 4 + }); + expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4]) }); - expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4]) - }); + } }); describe('interpretResponse', function () { @@ -227,42 +229,44 @@ describe('bigRichMediaAdapterTests', function () { expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('handles outstream video responses', function () { - const response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'content': '' - } - }, - 'javascriptTrackers': '' + if (FEATURES.VIDEO) { + it('handles outstream video responses', function () { + const response = { + 'tags': [{ + 'uuid': '84ab500420319d', + 'ads': [{ + 'ad_type': 'video', + 'cpm': 0.500000, + 'notify_url': 'imptracker.com', + 'rtb': { + 'video': { + 'content': '' + } + }, + 'javascriptTrackers': '' + }] }] - }] - }; - const bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'outstream' + }; + const bidderRequest = { + bids: [{ + bidId: '84ab500420319d', + adUnitCode: 'code', + mediaTypes: { + video: { + context: 'outstream' + } } - } - }] - } + }] + } - const result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result[0]).not.to.have.property('vastXml'); - expect(result[0]).not.to.have.property('vastUrl'); - expect(result[0]).to.have.property('width', 1); - expect(result[0]).to.have.property('height', 1); - expect(result[0]).to.have.property('mediaType', 'banner'); - }); + const result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).not.to.have.property('vastXml'); + expect(result[0]).not.to.have.property('vastUrl'); + expect(result[0]).to.have.property('width', 1); + expect(result[0]).to.have.property('height', 1); + expect(result[0]).to.have.property('mediaType', 'banner'); + }); + } }); describe('getUserSyncs', function() { diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 261414f336d..bda5d8daca7 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -263,6 +263,7 @@ describe('Conversant adapter tests', function() { const payload = request.data; expect(payload).to.have.property('id', 'req000'); + expect(payload.source).to.have.property('tid', 'req000'); expect(payload).to.have.property('at', 1); expect(payload).to.have.property('imp'); expect(payload.imp).to.be.an('array').with.lengthOf(8); diff --git a/test/spec/modules/emtvBidAdapter_spec.js b/test/spec/modules/emtvBidAdapter_spec.js new file mode 100644 index 00000000000..4f95a0cc094 --- /dev/null +++ b/test/spec/modules/emtvBidAdapter_spec.js @@ -0,0 +1,400 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/emtvBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'emtv' +const adUrl = 'https://us-east-ep.engagemedia.tv/pbjs'; +const syncUrl = 'https://cs.engagemedia.tv'; + +describe('EMTVBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal(adUrl); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal(`${syncUrl}/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0`) + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal(`${syncUrl}/image?pbjs=1&ccpa_consent=1---&coppa=0`) + }); + }); +}); diff --git a/test/spec/modules/eskimiBidAdapter_spec.js b/test/spec/modules/eskimiBidAdapter_spec.js new file mode 100644 index 00000000000..4622b374de5 --- /dev/null +++ b/test/spec/modules/eskimiBidAdapter_spec.js @@ -0,0 +1,155 @@ +import {expect} from 'chai'; +import {spec} from 'modules/eskimiBidAdapter.js'; + +const REQUEST = { + 'bidderCode': 'eskimi', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708', + 'bidderRequestId': 'requestId', + 'bidRequest': [{ + 'bidder': 'eskimi', + 'params': { + 'placementId': 1003000, + 'accId': 123 + }, + 'sizes': [ + [300, 250] + ], + 'bidId': 'bidId1', + 'adUnitCode': 'adUnitCode1', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' + }, + { + 'bidder': 'eskimi', + 'params': { + 'placementId': 1003001, + 'accId': 123 + }, + 'sizes': [ + [300, 250] + ], + 'bidId': 'bidId2', + 'adUnitCode': 'adUnitCode2', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708' + }], + 'start': 1487883186070, + 'auctionStart': 1487883186069, + 'timeout': 3000 +}; + +const RESPONSE = { + 'headers': null, + 'body': { + 'id': 'requestId', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'bidId1', + 'impid': 'bidId1', + 'price': 0.18, + 'adm': '', + 'adid': '144762342', + 'adomain': [ + 'https://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'eskimi': { + 'brand_id': 334553, + 'auction_id': 514667951122925701, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'seat' + } + ] + } +}; + +describe('Eskimi bid adapter', function () { + describe('isBidRequestValid', function () { + it('should accept request if placementId is passed', function () { + let bid = { + bidder: 'eskimi', + params: { + placementId: 123 + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('reject requests without params', function () { + let bid = { + bidder: 'eskimi', + params: {} + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('creates request data', function () { + let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0]; + expect(request).to.exist.and.to.be.a('object'); + const payload = request.data; + expect(payload.imp[0]).to.have.property('id', REQUEST.bidRequest[0].bidId); + expect(payload.imp[1]).to.have.property('id', REQUEST.bidRequest[1].bidId); + }); + + it('has gdpr data if applicable', function () { + const req = Object.assign({}, REQUEST, { + gdprConsent: { + consentString: 'consentString', + gdprApplies: true, + } + }); + let request = spec.buildRequests(REQUEST.bidRequest, req)[0]; + + const payload = request.data; + expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); + expect(payload.regs.ext).to.have.property('gdpr', 1); + }); + }); + + describe('interpretResponse', function () { + it('has bids', function () { + let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0]; + let bids = spec.interpretResponse(RESPONSE, request); + expect(bids).to.be.an('array').that.is.not.empty; + validateBidOnIndex(0); + + function validateBidOnIndex(index) { + expect(bids[index]).to.have.property('currency', 'USD'); + expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].id); + expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); + expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); + expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); + expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); + expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); + expect(bids[index]).to.have.property('ttl', 30); + expect(bids[index]).to.have.property('netRevenue', true); + } + }); + + it('handles empty response', function () { + let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0]; + const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); + const bids = spec.interpretResponse(EMPTY_RESP, request); + expect(bids).to.be.empty; + }); + }); +}); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 1cafdf6134f..2ef604dd097 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -480,6 +480,14 @@ describe('TheMediaGrid Adapter', function () { 'h': 600, 'protocols': [1, 2, 3], 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'], + 'context': 'instream', + 'maxduration': 30, + 'minduration': 0, + 'api': [1, 2], + 'skip': 1, + 'placement': 1, + 'playbackmethod': 1, + 'startdelay': 0 } }, { 'id': bidRequests[3].bidId, diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js deleted file mode 100644 index e4f06a451d2..00000000000 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ /dev/null @@ -1,636 +0,0 @@ -import { expect } from 'chai'; -import { spec, resetUserSync, getSyncUrl } from 'modules/gridNMBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('TheMediaGridNM Adapter', function () { - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6] - } - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', 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'], - } - }, - { - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6] - } - }, - { - 'source': 'jwp', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6] - } - }, - { - 'source': 'jwp', - 'secid': '11', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6] - } - } - ]; - paramsList.forEach((params) => { - const invalidBid = Object.assign({}, bid); - delete invalidBid.params; - invalidBid.params = params; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - it('should return false when required params has invalid values', function () { - const paramsList = [ - { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': '1,2,3,4,5' - } - }, - { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': [1, 2], - 'protocols': [1, 2, 3, 4, 5] - } - }, - { - 'source': 'jwp', - 'secid': 11, - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5] - } - }, - { - 'source': 111, - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5] - } - } - ]; - - paramsList.forEach((params) => { - const invalidBid = Object.assign({}, bid); - delete invalidBid.params; - invalidBid.params = params; - 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 () { - function parseRequestUrl(url) { - const res = {}; - url.replace(/^[^\?]+\?/, '').split('&').forEach((it) => { - const couple = it.split('='); - res[couple[0]] = decodeURIComponent(couple[1]); - }); - return res; - } - const bidderRequest = { - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - timeout: 3000, - refererInfo: { page: 'https://example.com' } - }; - const referrer = encodeURIComponent(bidderRequest.refererInfo.page); - let bidRequests = [ - { - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'floorcpm': 2, - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6] - } - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3], - 'skip': 1 - } - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'bidId': '3150ccb55da321', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('if content and segment is present in jwTargeting, payload must have right params', function () { - const jsContent = {id: 'test_jw_content_id'}; - const jsSegments = ['test_seg_1', 'test_seg_2']; - const bidRequestsWithJwTargeting = bidRequests.map((bid) => { - return Object.assign({ - rtd: { - jwplayer: { - targeting: { - segments: jsSegments, - content: jsContent - } - } - } - }, bid); - }); - const requests = spec.buildRequests(bidRequestsWithJwTargeting, bidderRequest); - requests.forEach((req, i) => { - const payload = req.data; - expect(req).to.have.property('data'); - expect(payload).to.have.property('site'); - expect(payload.site.content).to.deep.equal(jsContent); - }); - }); - - it('should attach valid params to the tag', function () { - const requests = spec.buildRequests(bidRequests, bidderRequest); - const requestsSizes = ['300x250,300x600', '728x90']; - requests.forEach((req, i) => { - expect(req.url).to.be.an('string'); - const payload = parseRequestUrl(req.url); - expect(payload).to.have.property('no_mapping', '1'); - expect(payload).to.have.property('sp', 'jwp'); - - const sizes = { w: bidRequests[i].sizes[0][0], h: bidRequests[i].sizes[0][1] }; - const impObj = { - 'id': bidRequests[i].bidId, - 'tagid': bidRequests[i].params.secid, - 'ext': {'divid': bidRequests[i].adUnitCode}, - 'video': Object.assign(sizes, bidRequests[i].params.video) - }; - - if (bidRequests[i].params.floorcpm) { - impObj.bidfloor = bidRequests[i].params.floorcpm; - } - - expect(req.data).to.deep.equal({ - 'id': bidderRequest.bidderRequestId, - 'site': { - 'page': referrer, - 'publisher': { - 'id': bidRequests[i].params.pubid - } - }, - 'tmax': bidderRequest.timeout, - 'source': { - 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} - }, - 'imp': [impObj] - }); - }); - }); - - 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, - 'minduration': 10, - 'maxduration': 100, - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6], - 'w': 300, - 'h': 250 - }; - - expect(req.url).to.be.an('string'); - const payload = parseRequestUrl(req.url); - expect(payload).to.have.property('no_mapping', '1'); - expect(payload).to.have.property('sp', 'jwp'); - expect(req.data).to.deep.equal({ - 'id': bidderRequest.bidderRequestId, - 'site': { - 'page': referrer, - 'publisher': { - 'id': bidRequest.params.pubid - } - }, - 'tmax': bidderRequest.timeout, - 'source': { - 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} - }, - 'imp': [{ - 'id': bidRequest.bidId, - 'bidfloor': bidRequest.params.floorcpm, - 'tagid': bidRequest.params.secid, - 'ext': {'divid': bidRequest.adUnitCode}, - 'video': expectedVideo - }] - }); - }); - - it('if gdprConsent is present payload must have gdpr params', function () { - const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); - const request = spec.buildRequests([bidRequests[0]], gdprBidderRequest)[0]; - const payload = request.data; - expect(request).to.have.property('data'); - expect(payload).to.have.property('user'); - expect(payload.user).to.have.property('ext'); - expect(payload.user.ext).to.have.property('consent', 'AAA'); - expect(payload).to.have.property('regs'); - expect(payload.regs).to.have.property('ext'); - expect(payload.regs.ext).to.have.property('gdpr', 1); - }); - - it('if usPrivacy is present payload must have us_privacy param', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const request = spec.buildRequests([bidRequests[0]], bidderRequestWithUSP)[0]; - const payload = request.data; - expect(payload).to.have.property('regs'); - expect(payload.regs).to.have.property('ext'); - expect(payload.regs.ext).to.have.property('us_privacy', '1YNN'); - }); - }); - - 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, adomain: ['my_domain.ru']}], 'seat': '2'}, - {'bid': [{'price': 2.00, 'nurl': 'https://some_test_vast_url.com', 'content_type': 'video', 'adomain': ['example.com'], 'w': 300, 'h': 600}], '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, - {'bid': [], 'seat': '2'}, - {'seat': '2'}, - ]; - - it('should get correct video bid response', function () { - const bidRequests = [ - { - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': [1, 2, 3, 4, 5, 6] - } - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '659423fff799cb', - 'bidderRequestId': '5f2009617a7c0a', - 'auctionId': '1cbd2feafe5e8b', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }, - { - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5], - 'skip': 1 - } - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2bc598e42b6a', - 'bidderRequestId': '1e8b5a465f404', - 'auctionId': '1cbd2feafe5e8b', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }, - { - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3], - } - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '127f4b12a432c', - 'bidderRequestId': 'a75bc868f32', - 'auctionId': '1cbd2feafe5e8b', - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - } - ]; - const requests = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '659423fff799cb', - 'cpm': 1.15, - 'creativeId': '5f2009617a7c0a', - 'dealId': 11, - 'width': 300, - 'height': 250, - 'currency': 'USD', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'meta': { - 'advertiserDomains': [] - }, - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - } - }, - { - 'requestId': '2bc598e42b6a', - 'cpm': 0.5, - 'creativeId': '1e8b5a465f404', - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'currency': 'USD', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'vastXml': '\n<\/Ad>\n<\/VAST>', - 'meta': { - 'advertiserDomains': ['my_domain.ru'] - }, - 'adResponse': { - 'content': '\n<\/Ad>\n<\/VAST>' - } - }, - { - 'requestId': '127f4b12a432c', - 'cpm': 2.00, - 'creativeId': 'a75bc868f32', - 'dealId': undefined, - 'width': 300, - 'height': 600, - 'currency': 'USD', - 'mediaType': 'video', - 'netRevenue': true, - 'ttl': 360, - 'meta': { - advertiserDomains: ['example.com'] - }, - 'vastUrl': 'https://some_test_vast_url.com', - } - ]; - - requests.forEach((req, i) => { - const result = spec.interpretResponse({'body': {'seatbid': [responses[i]]}}, req); - expect(result[0]).to.deep.equal(expectedResponse[i]); - }); - }); - - it('handles wrong and nobid responses', function () { - responses.slice(3).forEach((resp) => { - const request = spec.buildRequests([{ - 'bidder': 'gridNM', - 'params': { - 'source': 'jwp', - 'secid': '11', - 'pubid': '22', - 'video': { - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5], - 'skip': 1 - } - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '2bc598e42b6a', - 'bidderRequestId': '39d74f5b71464', - 'auctionId': '1cbd2feafe5e8b', - 'meta': { - 'advertiserDomains': [] - }, - 'mediaTypes': { - 'video': { - 'context': 'instream' - } - } - }]); - const result = spec.interpretResponse({'body': {'seatbid': [resp]}}, request[0]); - expect(result.length).to.equal(0); - }); - }); - }); - - describe('user sync', function () { - const syncUrl = getSyncUrl(); - - beforeEach(function () { - resetUserSync(); - }); - - it('should register the Emily iframe', function () { - let syncs = spec.getUserSyncs({ - pixelEnabled: true - }); - - expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); - }); - - it('should not register the Emily iframe more than once', function () { - let syncs = spec.getUserSyncs({ - pixelEnabled: true - }); - expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); - - // when called again, should still have only been called once - syncs = spec.getUserSyncs(); - expect(syncs).to.equal(undefined); - }); - - it('should pass gdpr params if consent is true', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - gdprApplies: true, consentString: 'foo' - })).to.deep.equal({ - type: 'image', url: `${syncUrl}&gdpr=1&gdpr_consent=foo` - }); - }); - - it('should pass gdpr params if consent is false', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - gdprApplies: false, consentString: 'foo' - })).to.deep.equal({ - type: 'image', url: `${syncUrl}&gdpr=0&gdpr_consent=foo` - }); - }); - - it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: 'foo' - })).to.deep.equal({ - type: 'image', url: `${syncUrl}&gdpr_consent=foo` - }); - }); - - it('should pass no params if gdpr consentString is not defined', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {})).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr consentString is a number', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: 0 - })).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr consentString is null', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: null - })).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr consentString is a object', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { - consentString: {} - })).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass no params if gdpr is not defined', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined)).to.deep.equal({ - type: 'image', url: syncUrl - }); - }); - - it('should pass usPrivacy param if it is available', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {}, '1YNN')).to.deep.equal({ - type: 'image', url: `${syncUrl}&us_privacy=1YNN` - }); - }); - }); -}); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 8ea3b9dc522..149c4c44c21 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1703,6 +1703,63 @@ describe('IndexexchangeAdapter', function () { expect(r.regs.gpp_sid).to.be.an('array'); expect(r.regs.gpp_sid).to.include(1); }); + + it('should add adunit specific data to imp ext for banner', 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 banner', + adslot: AD_UNIT_CODE + } + } + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const imp = extractPayload(requests[0]).imp[0]; + expect(deepAccess(imp, 'ext.data.adserver.name')).to.equal('gam banner'); + expect(deepAccess(imp, 'ext.data.adserver.adslot')).to.equal(AD_UNIT_CODE); + }); + + it('should add adunit specific data to imp ext for native', function () { + const AD_UNIT_CODE = '/19968336/some-adunit-path'; + const validBids = utils.deepClone(DEFAULT_NATIVE_VALID_BID); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam native', + adslot: AD_UNIT_CODE + } + } + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const imp = extractPayload(requests[0]).imp[0]; + expect(deepAccess(imp, 'ext.data.adserver.name')).to.equal('gam native'); + expect(deepAccess(imp, 'ext.data.adserver.adslot')).to.equal(AD_UNIT_CODE); + }); + + it('should add adunit specific data to imp ext for video', function () { + const AD_UNIT_CODE = '/19968336/some-adunit-path'; + const validBids = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam video', + adslot: AD_UNIT_CODE + } + } + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const imp = extractPayload(requests[0]).imp[0]; + expect(deepAccess(imp, 'ext.data.adserver.name')).to.equal('gam video'); + expect(deepAccess(imp, 'ext.data.adserver.adslot')).to.equal(AD_UNIT_CODE); + }); }); describe('buildRequests', function () { diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 260ac6c2132..d692cc67e26 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -524,9 +524,7 @@ describe('kargo adapter tests', function () { } const reqCount = requestCount++; - if (reqCount > 0) { - base.requestCount = reqCount - } + base.requestCount = reqCount if (expectedCRB != null) { if (expectedCRB.rawCRB != null) { @@ -894,8 +892,8 @@ describe('kargo adapter tests', function () { }); }); - function getUserSyncsWhenAllowed(gdprConsent, usPrivacy) { - return spec.getUserSyncs({iframeEnabled: true}, null, gdprConsent, usPrivacy); + function getUserSyncsWhenAllowed(gdprConsent, usPrivacy, gppConsent) { + return spec.getUserSyncs({iframeEnabled: true}, null, gdprConsent, usPrivacy, gppConsent); } function getUserSyncsWhenForbidden() { @@ -910,17 +908,17 @@ describe('kargo adapter tests', function () { shouldSimulateOutdatedBrowser = true; } - function getSyncUrl(index, gdprApplies, gdprConsentString, usPrivacy) { + function getSyncUrl(index, gdprApplies, gdprConsentString, usPrivacy, gpp, gppSid) { return { type: 'iframe', - url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}&gdpr=${gdprApplies}&gdpr_consent=${gdprConsentString}&us_privacy=${usPrivacy}` + url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}&gdpr=${gdprApplies}&gdpr_consent=${gdprConsentString}&us_privacy=${usPrivacy}&gpp=${gpp}&gpp_sid=${gppSid}` }; } - function getSyncUrls(gdprApplies, gdprConsentString, usPrivacy) { + function getSyncUrls(gdprApplies, gdprConsentString, usPrivacy, gpp, gppSid) { var syncs = []; for (var i = 0; i < 5; i++) { - syncs[i] = getSyncUrl(i, gdprApplies || 0, gdprConsentString || '', usPrivacy || ''); + syncs[i] = getSyncUrl(i, gdprApplies || 0, gdprConsentString || '', usPrivacy || '', gpp || '', gppSid || ''); } return syncs; } @@ -957,6 +955,11 @@ describe('kargo adapter tests', function () { safelyRun(() => expect(getUserSyncsWhenAllowed({ gdprApplies: true, consentString: 'consentstring' })).to.deep.equal(getSyncUrls(1, 'consentstring', ''))); }); + it('pass through gpp consent', function () { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed(null, null, { consentString: 'gppString', applicableSections: [-1] })).to.deep.equal(getSyncUrls('', '', '', 'gppString', '-1'))); + }); + it('no user syncs when there is outdated browser', function() { turnOnClientId(); simulateOutdatedBrowser(); diff --git a/test/spec/modules/kueezRtbBidAdapter_spec.js b/test/spec/modules/kueezRtbBidAdapter_spec.js index 2f48fd6df8c..fc7219d2ee7 100644 --- a/test/spec/modules/kueezRtbBidAdapter_spec.js +++ b/test/spec/modules/kueezRtbBidAdapter_spec.js @@ -43,7 +43,12 @@ const BID = { 'bidderWinsCount': 1, 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', - 'mediaTypes': [BANNER] + 'mediaTypes': [BANNER], + 'ortb2Imp': { + 'ext': { + 'gpid': '0123456789' + } + } }; const VIDEO_BID = { @@ -315,7 +320,8 @@ describe('KueezRtbBidAdapter', function () { protocols: [2, 3, 5, 6], startdelay: 0 } - } + }, + gpid: '' } }); }); @@ -373,6 +379,7 @@ describe('KueezRtbBidAdapter', function () { schain: BID.schain, res: `${window.top.screen.width}x${window.top.screen.height}`, mediaTypes: [BANNER], + gpid: '0123456789', uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', @@ -448,6 +455,19 @@ describe('KueezRtbBidAdapter', function () { }); }); + it('should get meta from response metaData', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].metaData = { + advertiserDomains: ['kueezrtb.com'], + agencyName: 'Agency Name', + }; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses[0].meta).to.deep.equal({ + advertiserDomains: ['kueezrtb.com'], + agencyName: 'Agency Name' + }); + }); + it('should return an array of interpreted video responses', function () { const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); expect(responses).to.have.length(1); diff --git a/test/spec/modules/minutemediaplusBidAdapter_spec.js b/test/spec/modules/minutemediaplusBidAdapter_spec.js index 000ddb2778d..e19323c794f 100644 --- a/test/spec/modules/minutemediaplusBidAdapter_spec.js +++ b/test/spec/modules/minutemediaplusBidAdapter_spec.js @@ -43,7 +43,12 @@ const BID = { 'bidderWinsCount': 1, 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', - 'mediaTypes': [BANNER] + 'mediaTypes': [BANNER], + 'ortb2Imp': { + 'ext': { + 'gpid': '1234567890' + } + } }; const VIDEO_BID = { @@ -315,7 +320,8 @@ describe('MinuteMediaPlus Bid Adapter', function () { protocols: [2, 3, 5, 6], startdelay: 0 } - } + }, + gpid: '' } }); }); @@ -373,6 +379,7 @@ describe('MinuteMediaPlus Bid Adapter', function () { schain: BID.schain, res: `${window.top.screen.width}x${window.top.screen.height}`, mediaTypes: [BANNER], + gpid: '1234567890', uqs: getTopWindowQueryParams(), 'ext.param1': 'loremipsum', 'ext.param2': 'dolorsitamet', @@ -448,6 +455,19 @@ describe('MinuteMediaPlus Bid Adapter', function () { }); }); + it('should get meta from response metaData', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].metaData = { + advertiserDomains: ['minutemedia-prebid.com'], + agencyName: 'Agency Name', + }; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses[0].meta).to.deep.equal({ + advertiserDomains: ['minutemedia-prebid.com'], + agencyName: 'Agency Name' + }); + }); + it('should return an array of interpreted video responses', function () { const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); expect(responses).to.have.length(1); diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 4d70e6f7071..51e78d1f6d6 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -8,6 +8,10 @@ import { getPageUrlFromBidRequest, hasProtocol, addProtocol, + BidRequestDataSource, + RequestData, + UserEIDs, + buildRequestUrl, } from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { @@ -120,6 +124,7 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.be.a('string') expect(request.url).to.include('?') + expect(request.url).to.include('ntv_pbv') expect(request.url).to.include('ntv_ptd') expect(request.url).to.include('ntv_pb_rid') expect(request.url).to.include('ntv_ppc') @@ -731,3 +736,99 @@ describe('getPageUrlFromBidRequest', () => { expect(url).not.to.be.undefined }) }) + +describe('RequestData', () => { + describe('addBidRequestDataSource', () => { + it('Adds a BidRequestDataSource', () => { + const requestData = new RequestData() + const testBidRequestDataSource = new BidRequestDataSource() + + requestData.addBidRequestDataSource(testBidRequestDataSource) + + expect(requestData.bidRequestDataSources.length == 1) + }) + + it("Doeasn't add a non BidRequestDataSource", () => { + const requestData = new RequestData() + + requestData.addBidRequestDataSource({}) + requestData.addBidRequestDataSource('test') + requestData.addBidRequestDataSource(1) + requestData.addBidRequestDataSource(true) + + expect(requestData.bidRequestDataSources.length == 0) + }) + }) + + describe('getRequestDataString', () => { + it("Doesn't append empty query strings", () => { + const requestData = new RequestData() + const testBidRequestDataSource = new BidRequestDataSource() + + requestData.addBidRequestDataSource(testBidRequestDataSource) + + let qs = requestData.getRequestDataQueryString() + expect(qs).to.be.empty + + testBidRequestDataSource.getRequestQueryString = () => { + return 'ntv_test=true' + } + qs = requestData.getRequestDataQueryString() + expect(qs).to.be.equal('ntv_test=true') + }) + }) +}) + +describe('UserEIDs', () => { + const userEids = new UserEIDs() + const eids = [{ 'testId': 1111 }] + + describe('processBidRequestData', () => { + it('Processes bid request without eids', () => { + userEids.processBidRequestData({}) + + expect(userEids.eids).to.be.empty + }) + + it('Processed bid request with eids', () => { + userEids.processBidRequestData({ userIdAsEids: eids }) + + expect(userEids.eids).to.not.be.empty + }) + }) + + describe('getRequestQueryString', () => { + it('Correctly prints out QS param string', () => { + const qs = userEids.getRequestQueryString() + const value = qs.slice(11) + + expect(qs).to.include('ntv_pb_eid=') + try { + expect(JSON.parse(value)).to.be.equal(eids) + } catch (err) { } + }) + }) +}) + +describe('buildRequestUrl', () => { + const baseUrl = 'https://www.testExchange.com' + it('Returns baseUrl if no QS strings passed', () => { + const url = buildRequestUrl(baseUrl) + expect(url).to.be.equal(baseUrl) + }) + + it('Returns baseUrl if empty QS strings passed', () => { + const url = buildRequestUrl(baseUrl, ['', '', '']) + expect(url).to.be.equal(baseUrl) + }) + + it('Returns baseUrl + QS params if QS strings passed', () => { + const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', 'ntv_foo=bar']) + expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) + }) + + it('Returns baseUrl + QS params if mixed QS strings passed', () => { + const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', '', '', 'ntv_foo=bar']) + expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`) + }) +}) diff --git a/test/spec/modules/ooloAnalyticsAdapter_spec.js b/test/spec/modules/ooloAnalyticsAdapter_spec.js index a6030e972ce..2515c713b14 100644 --- a/test/spec/modules/ooloAnalyticsAdapter_spec.js +++ b/test/spec/modules/ooloAnalyticsAdapter_spec.js @@ -194,7 +194,7 @@ describe('oolo Prebid Analytic', () => { }) const conf = {} - const pbjsConfig = config.getConfig() + const pbjsConfig = JSON.parse(JSON.stringify(config.getConfig())) Object.keys(pbjsConfig).forEach(key => { if (key[0] !== '_') { diff --git a/test/spec/modules/pairIdSystem_spec.js b/test/spec/modules/pairIdSystem_spec.js new file mode 100644 index 00000000000..4f75666affe --- /dev/null +++ b/test/spec/modules/pairIdSystem_spec.js @@ -0,0 +1,68 @@ +import { storage, pairIdSubmodule } from 'modules/pairIdSystem.js'; +import * as utils from 'src/utils.js'; + +describe('pairId', function () { + let sandbox; + let logErrorStub; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + logErrorStub = sandbox.stub(utils, 'logError'); + }); + afterEach(() => { + sandbox.restore(); + }); + + it('should log an error if no ID is found when getId', function() { + pairIdSubmodule.getId({ params: {} }); + expect(logErrorStub.calledOnce).to.be.true; + }); + + it('should read pairId from local storage if exists', function() { + let pairIds = ['test-pair-id1', 'test-pair-id2', 'test-pair-id3']; + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('pairId').returns(btoa(JSON.stringify(pairIds))); + + let id = pairIdSubmodule.getId({ params: {} }); + expect(id).to.be.deep.equal({id: pairIds}); + }); + + it('should read pairId from cookie if exists', function() { + let pairIds = ['test-pair-id4', 'test-pair-id5', 'test-pair-id6']; + sandbox.stub(storage, 'getCookie').withArgs('pairId').returns(btoa(JSON.stringify(pairIds))); + + let id = pairIdSubmodule.getId({ params: {} }); + expect(id).to.be.deep.equal({id: pairIds}); + }); + + it('should read pairId from default liveramp envelope local storage key if configured', function() { + let pairIds = ['test-pair-id1', 'test-pair-id2', 'test-pair-id3']; + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': pairIds}))); + let id = pairIdSubmodule.getId({ + params: { + liveramp: {} + }}) + expect(id).to.be.deep.equal({id: pairIds}) + }) + + it('should read pairId from default liveramp envelope cookie entry if configured', function() { + let pairIds = ['test-pair-id4', 'test-pair-id5', 'test-pair-id6']; + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': pairIds}))); + let id = pairIdSubmodule.getId({ + params: { + liveramp: {} + }}) + expect(id).to.be.deep.equal({id: pairIds}) + }) + + it('should read pairId from specified liveramp envelope cookie entry if configured with storageKey', function() { + let pairIds = ['test-pair-id7', 'test-pair-id8', 'test-pair-id9']; + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('lr_pairId_custom').returns(btoa(JSON.stringify({'envelope': pairIds}))); + let id = pairIdSubmodule.getId({ + params: { + liveramp: { + storageKey: 'lr_pairId_custom' + } + }}) + expect(id).to.be.deep.equal({id: pairIds}) + }) +}); diff --git a/test/spec/modules/pubCommonId_spec.js b/test/spec/modules/pubCommonId_spec.js index a46ff26c4b8..7c539014cc5 100644 --- a/test/spec/modules/pubCommonId_spec.js +++ b/test/spec/modules/pubCommonId_spec.js @@ -233,23 +233,6 @@ describe('Publisher Common ID', function () { }); }); }); - - it.skip('disable auto create', function() { - setConfig({ - create: false - }); - - const config = getPubcidConfig(); - expect(config.create).to.be.false; - expect(config.typeEnabled).to.equal('html5'); - - let adUnits = getAdUnits(); - let innerAdUnits; - requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - - const pubcid = localStorage.getItem(ID_NAME); - expect(pubcid).to.be.null; - }); }); describe('Invoking requestBid', function () { diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 66bcf42bb0f..3198fe406e7 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -850,12 +850,86 @@ describe('PubMatic adapter', function () { expect(isValid).to.equal(true); }); - it('should check for context if video is present', function() { - let bid = { + if (FEATURES.VIDEO) { + 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' + 'publisherId': '5890', + 'video': {} }, 'mediaTypes': { 'video': { @@ -864,42 +938,6 @@ describe('PubMatic adapter', function () { ], '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 @@ -917,160 +955,124 @@ describe('PubMatic adapter', function () { '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 + 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 + 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.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 + 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 + 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; // 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 + 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); - }); + delete bid.mediaTypes.video.mimes; // Undefined + delete bid.params.video.mimes; // Undefined + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - it('checks on bid.params.outstreamAU & bid.renderer & bid.mediaTypes.video.renderer', function() { - const getThebid = function() { - let bid = utils.deepClone(validOutstreamBidRequest.bids[0]); - bid.params.outstreamAU = 'pubmatic-test'; - bid.renderer = ' '; // we are only checking if this key is set or not - bid.mediaTypes.video.renderer = ' '; // we are only checking if this key is set or not - return bid; - } + it('checks on bid.params.outstreamAU & bid.renderer & bid.mediaTypes.video.renderer', function() { + const getThebid = function() { + let bid = utils.deepClone(validOutstreamBidRequest.bids[0]); + bid.params.outstreamAU = 'pubmatic-test'; + bid.renderer = ' '; // we are only checking if this key is set or not + bid.mediaTypes.video.renderer = ' '; // we are only checking if this key is set or not + return bid; + } - // true: when all are present - // mdiatype: outstream - // bid.params.outstreamAU : Y - // bid.renderer : Y - // bid.mediaTypes.video.renderer : Y - let bid = getThebid(); - expect(spec.isBidRequestValid(bid)).to.equal(true); - - // true: atleast one is present; 3 cases - // mdiatype: outstream - // bid.params.outstreamAU : Y - // bid.renderer : N - // bid.mediaTypes.video.renderer : N - bid = getThebid(); - delete bid.renderer; - delete bid.mediaTypes.video.renderer; - expect(spec.isBidRequestValid(bid)).to.equal(true); - - // true: atleast one is present; 3 cases - // mdiatype: outstream - // bid.params.outstreamAU : N - // bid.renderer : Y - // bid.mediaTypes.video.renderer : N - bid = getThebid(); - delete bid.params.outstreamAU; - delete bid.mediaTypes.video.renderer; - expect(spec.isBidRequestValid(bid)).to.equal(true); - - // true: atleast one is present; 3 cases - // mdiatype: outstream - // bid.params.outstreamAU : N - // bid.renderer : N - // bid.mediaTypes.video.renderer : Y - bid = getThebid(); - delete bid.params.outstreamAU; - delete bid.renderer; - expect(spec.isBidRequestValid(bid)).to.equal(true); - - // false: none present; only outstream - // mdiatype: outstream - // bid.params.outstreamAU : N - // bid.renderer : N - // bid.mediaTypes.video.renderer : N - bid = getThebid(); - delete bid.params.outstreamAU; - delete bid.renderer; - delete bid.mediaTypes.video.renderer; - expect(spec.isBidRequestValid(bid)).to.equal(false); - - // true: none present; outstream + Banner - // mdiatype: outstream, banner - // bid.params.outstreamAU : N - // bid.renderer : N - // bid.mediaTypes.video.renderer : N - bid = getThebid(); - delete bid.params.outstreamAU; - delete bid.renderer; - delete bid.mediaTypes.video.renderer; - bid.mediaTypes.banner = {sizes: [ [300, 250], [300, 600] ]}; - expect(spec.isBidRequestValid(bid)).to.equal(true); - - // true: none present; outstream + Native - // mdiatype: outstream, native - // bid.params.outstreamAU : N - // bid.renderer : N - // bid.mediaTypes.video.renderer : N - bid = getThebid(); - delete bid.params.outstreamAU; - delete bid.renderer; - delete bid.mediaTypes.video.renderer; - bid.mediaTypes.native = {} - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); + // true: when all are present + // mdiatype: outstream + // bid.params.outstreamAU : Y + // bid.renderer : Y + // bid.mediaTypes.video.renderer : Y + let bid = getThebid(); + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: atleast one is present; 3 cases + // mdiatype: outstream + // bid.params.outstreamAU : Y + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: atleast one is present; 3 cases + // mdiatype: outstream + // bid.params.outstreamAU : N + // bid.renderer : Y + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.mediaTypes.video.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: atleast one is present; 3 cases + // mdiatype: outstream + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : Y + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // false: none present; only outstream + // mdiatype: outstream + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + // true: none present; outstream + Banner + // mdiatype: outstream, banner + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + bid.mediaTypes.banner = {sizes: [ [300, 250], [300, 600] ]}; + expect(spec.isBidRequestValid(bid)).to.equal(true); + + // true: none present; outstream + Native + // mdiatype: outstream, native + // bid.params.outstreamAU : N + // bid.renderer : N + // bid.mediaTypes.video.renderer : N + bid = getThebid(); + delete bid.params.outstreamAU; + delete bid.renderer; + delete bid.mediaTypes.video.renderer; + bid.mediaTypes.native = {} + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + } }); describe('Request formation', function () { @@ -1116,18 +1118,6 @@ describe('PubMatic adapter', function () { expect(data.test).to.equal(undefined); }); - // disabled this test case as it refreshes the whole suite when in karma watch mode - // todo: needs a fix - xit('test flag set to 1 when pubmaticTest=true is present in page url', function() { - window.location.href += '#pubmaticTest=true'; - // now all the test cases below will have window.location.href with #pubmaticTest=true - let request = spec.buildRequests(bidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - expect(data.test).to.equal(1); - }); - it('Request params check', function () { let request = spec.buildRequests(bidRequests, { auctionId: 'new-auction-id' @@ -2055,29 +2045,31 @@ describe('PubMatic adapter', function () { expect(data.bidfloor).to.equal(undefined); }); - it('ignore floormodule o/p if floor is not number', function() { - floorModuleTestData.banner['300x250'].floor = 'Not-a-Number'; - floorModuleTestData.banner['300x600'].floor = 'Not-a-Number'; - newRequest[0].params.kadfloor = undefined; - let request = spec.buildRequests(newRequest, { - auctionId: 'new-auction-id' + if (FEATURES.VIDEO) { + it('ignore floormodule o/p if floor is not number', function() { + floorModuleTestData.banner['300x250'].floor = 'Not-a-Number'; + floorModuleTestData.banner['300x600'].floor = 'Not-a-Number'; + newRequest[0].params.kadfloor = undefined; + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(2.5); // video will be lowest now }); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(2.5); // video will be lowest now - }); - it('ignore floormodule o/p if currency is not matched', function() { - floorModuleTestData.banner['300x250'].currency = 'INR'; - floorModuleTestData.banner['300x600'].currency = 'INR'; - newRequest[0].params.kadfloor = undefined; - let request = spec.buildRequests(newRequest, { - auctionId: 'new-auction-id' + it('ignore floormodule o/p if currency is not matched', function() { + floorModuleTestData.banner['300x250'].currency = 'INR'; + floorModuleTestData.banner['300x600'].currency = 'INR'; + newRequest[0].params.kadfloor = undefined; + let request = spec.buildRequests(newRequest, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(2.5); // video will be lowest now }); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.bidfloor).to.equal(2.5); // video will be lowest now - }); + } it('kadfloor is not passed, use minimum from floorModule', function() { newRequest[0].params.kadfloor = undefined; @@ -2556,114 +2548,6 @@ describe('PubMatic adapter', function () { }); }); - it('Request params check for video ad', function () { - let request = spec.buildRequests(videoBidRequests, { - auctionId: 'new-auction-id' - }); - 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']['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]); - expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); - expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); - expect(data.imp[0]['video']['startdelay']).to.equal(videoBidRequests[0].params.video['startdelay']); - - expect(data.imp[0]['video']['playbackmethod']).to.exist.and.to.be.an('array'); - expect(data.imp[0]['video']['playbackmethod'][0]).to.equal(videoBidRequests[0].params.video['playbackmethod'][0]); - expect(data.imp[0]['video']['playbackmethod'][1]).to.equal(videoBidRequests[0].params.video['playbackmethod'][1]); - - expect(data.imp[0]['video']['api']).to.exist.and.to.be.an('array'); - expect(data.imp[0]['video']['api'][0]).to.equal(videoBidRequests[0].params.video['api'][0]); - expect(data.imp[0]['video']['api'][1]).to.equal(videoBidRequests[0].params.video['api'][1]); - - expect(data.imp[0]['video']['protocols']).to.exist.and.to.be.an('array'); - expect(data.imp[0]['video']['protocols'][0]).to.equal(videoBidRequests[0].params.video['protocols'][0]); - expect(data.imp[0]['video']['protocols'][1]).to.equal(videoBidRequests[0].params.video['protocols'][1]); - - expect(data.imp[0]['video']['battr']).to.exist.and.to.be.an('array'); - expect(data.imp[0]['video']['battr'][0]).to.equal(videoBidRequests[0].params.video['battr'][0]); - expect(data.imp[0]['video']['battr'][1]).to.equal(videoBidRequests[0].params.video['battr'][1]); - - expect(data.imp[0]['video']['linearity']).to.equal(videoBidRequests[0].params.video['linearity']); - expect(data.imp[0]['video']['placement']).to.equal(videoBidRequests[0].params.video['placement']); - expect(data.imp[0]['video']['minbitrate']).to.equal(videoBidRequests[0].params.video['minbitrate']); - expect(data.imp[0]['video']['maxbitrate']).to.equal(videoBidRequests[0].params.video['maxbitrate']); - - expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); - expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); - }); - - it('Request params check for 1 banner and 1 video ad', function () { - let request = spec.buildRequests(multipleMediaRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - - expect(data.imp).to.be.an('array') - expect(data.imp).with.length.above(1); - - expect(data.at).to.equal(1); // auction type - expect(data.cur[0]).to.equal('USD'); // currency - expect(data.site.domain).to.be.a('string'); // domain should be set - expect(data.site.page).to.equal(multipleMediaRequests[0].params.kadpageurl); // forced pageURL - expect(data.site.publisher.id).to.equal(multipleMediaRequests[0].params.publisherId); // publisher Id - expect(data.user.yob).to.equal(parseInt(multipleMediaRequests[0].params.yob)); // YOB - expect(data.user.gender).to.equal(multipleMediaRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude - expect(data.device.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude - expect(data.user.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude - expect(data.user.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude - expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version - expect(data.ext.wrapper.transactionId).to.equal(multipleMediaRequests[0].transactionId); // Prebid TransactionId - expect(data.ext.wrapper.wiid).to.equal(multipleMediaRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID - expect(data.ext.wrapper.profile).to.equal(parseInt(multipleMediaRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID - expect(data.ext.wrapper.version).to.equal(parseInt(multipleMediaRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID - - // banner imp object check - expect(data.imp[0].id).to.equal(multipleMediaRequests[0].bidId); // Prebid bid id is passed as id - expect(data.imp[0].bidfloor).to.equal(parseFloat(multipleMediaRequests[0].params.kadfloor)); // kadfloor - expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid - 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(multipleMediaRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid - - // video imp object check - expect(data.imp[1].video).to.exist; - expect(data.imp[1].tagid).to.equal('Div1'); - 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]); - expect(data.imp[1]['video']['minduration']).to.equal(multipleMediaRequests[1].params.video['minduration']); - expect(data.imp[1]['video']['maxduration']).to.equal(multipleMediaRequests[1].params.video['maxduration']); - expect(data.imp[1]['video']['startdelay']).to.equal(multipleMediaRequests[1].params.video['startdelay']); - - expect(data.imp[1]['video']['playbackmethod']).to.exist.and.to.be.an('array'); - expect(data.imp[1]['video']['playbackmethod'][0]).to.equal(multipleMediaRequests[1].params.video['playbackmethod'][0]); - expect(data.imp[1]['video']['playbackmethod'][1]).to.equal(multipleMediaRequests[1].params.video['playbackmethod'][1]); - - expect(data.imp[1]['video']['api']).to.exist.and.to.be.an('array'); - expect(data.imp[1]['video']['api'][0]).to.equal(multipleMediaRequests[1].params.video['api'][0]); - expect(data.imp[1]['video']['api'][1]).to.equal(multipleMediaRequests[1].params.video['api'][1]); - - expect(data.imp[1]['video']['protocols']).to.exist.and.to.be.an('array'); - expect(data.imp[1]['video']['protocols'][0]).to.equal(multipleMediaRequests[1].params.video['protocols'][0]); - expect(data.imp[1]['video']['protocols'][1]).to.equal(multipleMediaRequests[1].params.video['protocols'][1]); - - expect(data.imp[1]['video']['battr']).to.exist.and.to.be.an('array'); - expect(data.imp[1]['video']['battr'][0]).to.equal(multipleMediaRequests[1].params.video['battr'][0]); - expect(data.imp[1]['video']['battr'][1]).to.equal(multipleMediaRequests[1].params.video['battr'][1]); - - expect(data.imp[1]['video']['linearity']).to.equal(multipleMediaRequests[1].params.video['linearity']); - expect(data.imp[1]['video']['placement']).to.equal(multipleMediaRequests[1].params.video['placement']); - expect(data.imp[1]['video']['minbitrate']).to.equal(multipleMediaRequests[1].params.video['minbitrate']); - expect(data.imp[1]['video']['maxbitrate']).to.equal(multipleMediaRequests[1].params.video['maxbitrate']); - - expect(data.imp[1]['video']['w']).to.equal(multipleMediaRequests[1].mediaTypes.video.playerSize[0]); - expect(data.imp[1]['video']['h']).to.equal(multipleMediaRequests[1].mediaTypes.video.playerSize[1]); - }); - it('should pass device.sua if present in bidderRequest fpd ortb2 object', function () { const suaObject = {'source': 2, 'platform': {'brand': 'macOS', 'version': ['12', '4', '0']}, 'browsers': [{'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']}], 'mobile': 0, 'model': '', 'bitness': '64', 'architecture': 'x86'}; let request = spec.buildRequests(multipleMediaRequests, { @@ -2723,127 +2607,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0]['native']['request']).to.exist.and.to.equal(validnativeBidImpressionWithAllParams.native.request); }); - it('Request params - should handle banner and video format in single adunit', function() { - let request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.banner).to.exist; - expect(data.banner.w).to.equal(300); - expect(data.banner.h).to.equal(250); - expect(data.banner.format).to.exist; - expect(data.banner.format.length).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes.length); - - // Case: when size is not present in adslo - bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; - request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.banner).to.exist; - expect(data.banner.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes[0][0]); - expect(data.banner.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes[0][1]); - expect(data.banner.format).to.exist; - expect(data.banner.format.length).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes.length - 1); - - expect(data.video).to.exist; - expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); - expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); - }); - - it('Request params - banner and video req in single adslot - should ignore banner imp if banner size is set to fluid and send video imp object', function () { - /* Adslot configured for banner and video. - banner size is set to [['fluid'], [300, 250]] - adslot specifies a size as 300x250 - => banner imp object should have primary w and h set to 300 and 250. fluid is ignored - */ - bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; - - let request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.banner).to.exist; - expect(data.banner.w).to.equal(300); - expect(data.banner.h).to.equal(250); - expect(data.banner.format).to.exist; - expect(data.banner.format[0].w).to.equal(160); - expect(data.banner.format[0].h).to.equal(600); - - /* Adslot configured for banner and video. - banner size is set to [['fluid'], [300, 250]] - adslot does not specify any size - => banner imp object should have primary w and h set to 300 and 250. fluid is ignored - */ - bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; - bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; - - request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.banner).to.exist; - expect(data.banner.w).to.equal(160); - expect(data.banner.h).to.equal(600); - expect(data.banner.format).to.not.exist; - - /* Adslot configured for banner and video. - banner size is set to [[728 90], ['fluid'], [300, 250]] - adslot does not specify any size - => banner imp object should have primary w and h set to 728 and 90. - banner.format should have 300, 250 set in it - fluid is ignore - */ - - bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [[728, 90], ['fluid'], [300, 250]]; - request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.banner).to.exist; - expect(data.banner.w).to.equal(728); - expect(data.banner.h).to.equal(90); - expect(data.banner.format).to.exist; - expect(data.banner.format[0].w).to.equal(300); - expect(data.banner.format[0].h).to.equal(250); - - /* Adslot configured for banner and video. - banner size is set to [['fluid']] - adslot does not specify any size - => banner object should not be sent in the request. only video should be sent. - */ - - bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid']]; - request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.banner).to.not.exist; - expect(data.video).to.exist; - }); - - it('Request params - should not contain banner imp if mediaTypes.banner is not present and sizes is specified in bid.sizes', function() { - delete bannerAndVideoBidRequests[0].mediaTypes.banner; - bannerAndVideoBidRequests[0].params.sizes = [300, 250]; - - let request = spec.buildRequests(bannerAndVideoBidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - data = data.imp[0]; - expect(data.banner).to.not.exist; - }); - it('Request params - should handle banner and native format in single adunit', function() { let request = spec.buildRequests(bannerAndNativeBidRequests, { auctionId: 'new-auction-id' @@ -2861,58 +2624,6 @@ describe('PubMatic adapter', function () { expect(data.native.request).to.exist; }); - it('Request params - should handle video and native format in single adunit', function() { - let request = spec.buildRequests(videoAndNativeBidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.video).to.exist; - expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); - expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); - - expect(data.native).to.exist; - expect(data.native.request).to.exist; - }); - - it('Request params - should handle banner, video and native format in single adunit', function() { - let request = spec.buildRequests(bannerVideoAndNativeBidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.banner).to.exist; - expect(data.banner.w).to.equal(300); - expect(data.banner.h).to.equal(250); - expect(data.banner.format).to.exist; - expect(data.banner.format.length).to.equal(bannerAndNativeBidRequests[0].mediaTypes.banner.sizes.length); - - expect(data.video).to.exist; - expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); - expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); - - expect(data.native).to.exist; - expect(data.native.request).to.exist; - }); - - it('Request params - should not add banner object if mediaTypes.banner is missing, but adunits.sizes is present', function() { - delete bannerAndNativeBidRequests[0].mediaTypes.banner; - bannerAndNativeBidRequests[0].sizes = [729, 90]; - - let request = spec.buildRequests(bannerAndNativeBidRequests, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - data = data.imp[0]; - - expect(data.banner).to.not.exist; - - expect(data.native).to.exist; - expect(data.native.request).to.exist; - }); - it('Request params - banner and native multiformat request - should have native object incase of invalid config present', function() { bannerAndNativeBidRequests[0].mediaTypes.native = { title: { required: true }, @@ -2936,115 +2647,399 @@ describe('PubMatic adapter', function () { expect(data.native).to.exist; }); - it('Request params - video and native multiformat request - should have native object incase of invalid config present', function() { - videoAndNativeBidRequests[0].mediaTypes.native = { - title: { required: true }, - image: { required: true }, - sponsoredBy: { required: true }, - clickUrl: { required: true } - }; - videoAndNativeBidRequests[0].nativeParams = { - title: { required: true }, - image: { required: true }, - sponsoredBy: { required: true }, - clickUrl: { required: true } - } - let request = spec.buildRequests(videoAndNativeBidRequests, { + it('Request params - should not add banner object if mediaTypes.banner is missing, but adunits.sizes is present', function() { + delete bannerAndNativeBidRequests[0].mediaTypes.banner; + bannerAndNativeBidRequests[0].sizes = [729, 90]; + + let request = spec.buildRequests(bannerAndNativeBidRequests, { auctionId: 'new-auction-id' }); let data = JSON.parse(request.data); data = data.imp[0]; - expect(data.video).to.exist; + expect(data.banner).to.not.exist; + expect(data.native).to.exist; + expect(data.native.request).to.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' + if (FEATURES.VIDEO) { + it('Request params - should not contain banner imp if mediaTypes.banner is not present and sizes is specified in bid.sizes', function() { + delete bannerAndVideoBidRequests[0].mediaTypes.banner; + bannerAndVideoBidRequests[0].params.sizes = [300, 250]; + + let request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.banner).to.not.exist; }); - 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' + it('Request params check for 1 banner and 1 video ad', function () { + let request = spec.buildRequests(multipleMediaRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + + expect(data.imp).to.be.an('array') + expect(data.imp).with.length.above(1); + + expect(data.at).to.equal(1); // auction type + expect(data.cur[0]).to.equal('USD'); // currency + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.page).to.equal(multipleMediaRequests[0].params.kadpageurl); // forced pageURL + expect(data.site.publisher.id).to.equal(multipleMediaRequests[0].params.publisherId); // publisher Id + expect(data.user.yob).to.equal(parseInt(multipleMediaRequests[0].params.yob)); // YOB + expect(data.user.gender).to.equal(multipleMediaRequests[0].params.gender); // Gender + expect(data.device.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version + expect(data.ext.wrapper.transactionId).to.equal(multipleMediaRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.wiid).to.equal(multipleMediaRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID + expect(data.ext.wrapper.profile).to.equal(parseInt(multipleMediaRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID + expect(data.ext.wrapper.version).to.equal(parseInt(multipleMediaRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID + + // banner imp object check + expect(data.imp[0].id).to.equal(multipleMediaRequests[0].bidId); // Prebid bid id is passed as id + expect(data.imp[0].bidfloor).to.equal(parseFloat(multipleMediaRequests[0].params.kadfloor)); // kadfloor + expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid + 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(multipleMediaRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + + // video imp object check + expect(data.imp[1].video).to.exist; + expect(data.imp[1].tagid).to.equal('Div1'); + 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]); + expect(data.imp[1]['video']['minduration']).to.equal(multipleMediaRequests[1].params.video['minduration']); + expect(data.imp[1]['video']['maxduration']).to.equal(multipleMediaRequests[1].params.video['maxduration']); + expect(data.imp[1]['video']['startdelay']).to.equal(multipleMediaRequests[1].params.video['startdelay']); + + expect(data.imp[1]['video']['playbackmethod']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['playbackmethod'][0]).to.equal(multipleMediaRequests[1].params.video['playbackmethod'][0]); + expect(data.imp[1]['video']['playbackmethod'][1]).to.equal(multipleMediaRequests[1].params.video['playbackmethod'][1]); + + expect(data.imp[1]['video']['api']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['api'][0]).to.equal(multipleMediaRequests[1].params.video['api'][0]); + expect(data.imp[1]['video']['api'][1]).to.equal(multipleMediaRequests[1].params.video['api'][1]); + + expect(data.imp[1]['video']['protocols']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['protocols'][0]).to.equal(multipleMediaRequests[1].params.video['protocols'][0]); + expect(data.imp[1]['video']['protocols'][1]).to.equal(multipleMediaRequests[1].params.video['protocols'][1]); + + expect(data.imp[1]['video']['battr']).to.exist.and.to.be.an('array'); + expect(data.imp[1]['video']['battr'][0]).to.equal(multipleMediaRequests[1].params.video['battr'][0]); + expect(data.imp[1]['video']['battr'][1]).to.equal(multipleMediaRequests[1].params.video['battr'][1]); + + expect(data.imp[1]['video']['linearity']).to.equal(multipleMediaRequests[1].params.video['linearity']); + expect(data.imp[1]['video']['placement']).to.equal(multipleMediaRequests[1].params.video['placement']); + expect(data.imp[1]['video']['minbitrate']).to.equal(multipleMediaRequests[1].params.video['minbitrate']); + expect(data.imp[1]['video']['maxbitrate']).to.equal(multipleMediaRequests[1].params.video['maxbitrate']); + + expect(data.imp[1]['video']['w']).to.equal(multipleMediaRequests[1].mediaTypes.video.playerSize[0]); + expect(data.imp[1]['video']['h']).to.equal(multipleMediaRequests[1].mediaTypes.video.playerSize[1]); }); - 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 - should handle banner and video format in single adunit', function() { + let request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes.length); + + // Case: when size is not present in adslo + bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes[0][0]); + expect(data.banner.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes[0][1]); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndVideoBidRequests[0].mediaTypes.banner.sizes.length - 1); + + expect(data.video).to.exist; + expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); + }); + + it('Request params - should handle banner, video and native format in single adunit', function() { + let request = spec.buildRequests(bannerVideoAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format.length).to.equal(bannerAndNativeBidRequests[0].mediaTypes.banner.sizes.length); + + expect(data.video).to.exist; + expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); + + expect(data.native).to.exist; + expect(data.native.request).to.exist; + }); + + it('Request params - should handle video and native format in single adunit', function() { + let request = spec.buildRequests(videoAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.video).to.exist; + expect(data.video.w).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.video.h).to.equal(bannerAndVideoBidRequests[0].mediaTypes.video.playerSize[1]); + + expect(data.native).to.exist; + expect(data.native.request).to.exist; + }); + + it('Request params - banner and video req in single adslot - should ignore banner imp if banner size is set to fluid and send video imp object', function () { + /* Adslot configured for banner and video. + banner size is set to [['fluid'], [300, 250]] + adslot specifies a size as 300x250 + => banner imp object should have primary w and h set to 300 and 250. fluid is ignored + */ + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; + + let request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(300); + expect(data.banner.h).to.equal(250); + expect(data.banner.format).to.exist; + expect(data.banner.format[0].w).to.equal(160); + expect(data.banner.format[0].h).to.equal(600); + + /* Adslot configured for banner and video. + banner size is set to [['fluid'], [300, 250]] + adslot does not specify any size + => banner imp object should have primary w and h set to 300 and 250. fluid is ignored + */ + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid'], [160, 600]]; + bannerAndVideoBidRequests[0].params.adSlot = '/15671365/DMDemo'; + + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(160); + expect(data.banner.h).to.equal(600); + expect(data.banner.format).to.not.exist; + + /* Adslot configured for banner and video. + banner size is set to [[728 90], ['fluid'], [300, 250]] + adslot does not specify any size + => banner imp object should have primary w and h set to 728 and 90. + banner.format should have 300, 250 set in it + fluid is ignore + */ + + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [[728, 90], ['fluid'], [300, 250]]; + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.exist; + expect(data.banner.w).to.equal(728); + expect(data.banner.h).to.equal(90); + expect(data.banner.format).to.exist; + expect(data.banner.format[0].w).to.equal(300); + expect(data.banner.format[0].h).to.equal(250); + + /* Adslot configured for banner and video. + banner size is set to [['fluid']] + adslot does not specify any size + => banner object should not be sent in the request. only video should be sent. + */ + + bannerAndVideoBidRequests[0].mediaTypes.banner.sizes = [['fluid']]; + request = spec.buildRequests(bannerAndVideoBidRequests, { + auctionId: 'new-auction-id' + }); + data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.banner).to.not.exist; + expect(data.video).to.exist; + }); + + it('Request params - video and native multiformat request - should have native object incase of invalid config present', function() { + videoAndNativeBidRequests[0].mediaTypes.native = { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + }; + videoAndNativeBidRequests[0].nativeParams = { + title: { required: true }, + image: { required: true }, + sponsoredBy: { required: true }, + clickUrl: { required: true } + } + let request = spec.buildRequests(videoAndNativeBidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + data = data.imp[0]; + + expect(data.video).to.exist; + expect(data.native).to.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 check for video ad', function () { + let request = spec.buildRequests(videoBidRequests, { + auctionId: 'new-auction-id' + }); + 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']['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]); + expect(data.imp[0]['video']['minduration']).to.equal(videoBidRequests[0].params.video['minduration']); + expect(data.imp[0]['video']['maxduration']).to.equal(videoBidRequests[0].params.video['maxduration']); + expect(data.imp[0]['video']['startdelay']).to.equal(videoBidRequests[0].params.video['startdelay']); + + expect(data.imp[0]['video']['playbackmethod']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['playbackmethod'][0]).to.equal(videoBidRequests[0].params.video['playbackmethod'][0]); + expect(data.imp[0]['video']['playbackmethod'][1]).to.equal(videoBidRequests[0].params.video['playbackmethod'][1]); + + expect(data.imp[0]['video']['api']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['api'][0]).to.equal(videoBidRequests[0].params.video['api'][0]); + expect(data.imp[0]['video']['api'][1]).to.equal(videoBidRequests[0].params.video['api'][1]); + + expect(data.imp[0]['video']['protocols']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['protocols'][0]).to.equal(videoBidRequests[0].params.video['protocols'][0]); + expect(data.imp[0]['video']['protocols'][1]).to.equal(videoBidRequests[0].params.video['protocols'][1]); + + expect(data.imp[0]['video']['battr']).to.exist.and.to.be.an('array'); + expect(data.imp[0]['video']['battr'][0]).to.equal(videoBidRequests[0].params.video['battr'][0]); + expect(data.imp[0]['video']['battr'][1]).to.equal(videoBidRequests[0].params.video['battr'][1]); + + expect(data.imp[0]['video']['linearity']).to.equal(videoBidRequests[0].params.video['linearity']); + expect(data.imp[0]['video']['placement']).to.equal(videoBidRequests[0].params.video['placement']); + expect(data.imp[0]['video']['minbitrate']).to.equal(videoBidRequests[0].params.video['minbitrate']); + expect(data.imp[0]['video']['maxbitrate']).to.equal(videoBidRequests[0].params.video['maxbitrate']); + + expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); + }); + } }); it('Request params dctr check', function () { @@ -3573,14 +3568,6 @@ describe('PubMatic adapter', function () { expect(response[0].mediaType).to.equal('banner'); }); - it('should check for valid video mediaType in case of multiformat request', function() { - let request = spec.buildRequests(videoBidRequests, { - auctionId: 'new-auction-id' - }); - let response = spec.interpretResponse(videoBidResponse, request); - expect(response[0].mediaType).to.equal('video'); - }); - it('should check for valid native mediaType in case of multiformat request', function() { let request = spec.buildRequests(nativeBidRequests, { auctionId: 'new-auction-id' @@ -3590,28 +3577,6 @@ describe('PubMatic adapter', function () { expect(response[0].mediaType).to.equal('native'); }); - it('should assign renderer if bid is video and request is for outstream', function() { - let request = spec.buildRequests(outstreamBidRequest, validOutstreamBidRequest); - let response = spec.interpretResponse(outstreamVideoBidResponse, request); - expect(response[0].renderer).to.exist; - }); - - it('should not assign renderer if bidderRequest is not present', function() { - let request = spec.buildRequests(outstreamBidRequest, { - auctionId: 'new-auction-id' - }); - let response = spec.interpretResponse(outstreamVideoBidResponse, request); - expect(response[0].renderer).to.not.exist; - }); - - it('should not assign renderer if bid is video and request is for instream', function() { - let request = spec.buildRequests(videoBidRequests, { - auctionId: 'new-auction-id' - }); - let response = spec.interpretResponse(videoBidResponse, request); - expect(response[0].renderer).to.not.exist; - }); - it('should not assign renderer if bid is native', function() { let request = spec.buildRequests(nativeBidRequests, { auctionId: 'new-auction-id' @@ -3628,154 +3593,186 @@ describe('PubMatic adapter', function () { 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' + if (FEATURES.VIDEO) { + it('should check for valid video mediaType in case of multiformat request', function() { + let request = spec.buildRequests(videoBidRequests, { + auctionId: 'new-auction-id' + }); + let response = spec.interpretResponse(videoBidResponse, request); + expect(response[0].mediaType).to.equal('video'); + }); + + it('should assign renderer if bid is video and request is for outstream', function() { + let request = spec.buildRequests(outstreamBidRequest, validOutstreamBidRequest); + let response = spec.interpretResponse(outstreamVideoBidResponse, request); + expect(response[0].renderer).to.exist; + }); + + it('should not assign renderer if bidderRequest is not present', function() { + let request = spec.buildRequests(outstreamBidRequest, { + auctionId: 'new-auction-id' + }); + let response = spec.interpretResponse(outstreamVideoBidResponse, request); + expect(response[0].renderer).to.not.exist; + }); + + it('should not assign renderer if bid is video and request is for instream', function() { + let request = spec.buildRequests(videoBidRequests, { + auctionId: 'new-auction-id' + }); + let response = spec.interpretResponse(videoBidResponse, 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 + } }, - '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 - } - }], + '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' - } - }] - }, - 'headers': {} - } - let newrequest = spec.buildRequests(newvideoRequests, { - auctionId: 'new-auction-id' - }); - let newresponse = spec.interpretResponse(newvideoBidResponses, newrequest); - expect(newresponse[0].mediaType).to.equal('video') - }) + }, + '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' + 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 + } }, - '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' - }], + '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' - } - }] - }, - 'headers': {} - } - let newrequest = spec.buildRequests(newvideoRequests, { - auctionId: 'new-auction-id' + }, + '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') }); - let newresponse = spec.interpretResponse(newvideoBidResponses, newrequest); - expect(newresponse[0].mediaType).to.equal('video') - }); + } }); describe('Preapare metadata', function () { @@ -3955,26 +3952,55 @@ describe('PubMatic adapter', function () { }); }); - describe('Checking for Video.Placement property', function() { - let sandbox, utilsMock; - const adUnit = 'Div1'; - const msg_placement_missing = 'Video.Placement param missing for Div1'; - let videoData = { - battr: [6, 7], - skipafter: 15, - maxduration: 50, - context: 'instream', - playerSize: [640, 480], - skip: 0, - connectiontype: [1, 2, 6], - skipmin: 10, - minduration: 10, - mimes: ['video/mp4', 'video/x-flv'], - } + if (FEATURES.VIDEO) { + describe('Checking for Video.Placement property', function() { + let sandbox, utilsMock; + const adUnit = 'Div1'; + const msg_placement_missing = 'Video.Placement param missing for Div1'; + let videoData = { + battr: [6, 7], + skipafter: 15, + maxduration: 50, + context: 'instream', + playerSize: [640, 480], + skip: 0, + connectiontype: [1, 2, 6], + skipmin: 10, + minduration: 10, + mimes: ['video/mp4', 'video/x-flv'], + } + beforeEach(() => { + utilsMock = sinon.mock(utils); + sandbox = sinon.sandbox.create(); + sandbox.spy(utils, 'logWarn'); + }); + + afterEach(() => { + utilsMock.restore(); + sandbox.restore(); + }) + + it('should log Video.Placement param missing', function() { + checkVideoPlacement(videoData, adUnit); + sinon.assert.calledWith(utils.logWarn, msg_placement_missing); + }) + it('shoud not log Video.Placement param missing', function() { + videoData['placement'] = 1; + checkVideoPlacement(videoData, adUnit); + sinon.assert.neverCalledWith(utils.logWarn, msg_placement_missing); + }) + }); + } + }); + + if (FEATURES.VIDEO) { + describe('Video request params', function() { + let sandbox, utilsMock, newVideoRequest; beforeEach(() => { utilsMock = sinon.mock(utils); sandbox = sinon.sandbox.create(); sandbox.spy(utils, 'logWarn'); + newVideoRequest = utils.deepClone(videoBidRequests) }); afterEach(() => { @@ -3982,112 +4008,87 @@ describe('PubMatic adapter', function () { sandbox.restore(); }) - it('should log Video.Placement param missing', function() { - checkVideoPlacement(videoData, adUnit); - sinon.assert.calledWith(utils.logWarn, msg_placement_missing); - }) - it('shoud not log Video.Placement param missing', function() { - videoData['placement'] = 1; - checkVideoPlacement(videoData, adUnit); - sinon.assert.neverCalledWith(utils.logWarn, msg_placement_missing); - }) - }); - }); + it('Should log warning if video params from mediaTypes and params obj of bid are not present', function () { + delete newVideoRequest[0].mediaTypes.video; + delete newVideoRequest[0].params.video; - describe('Video request params', function() { - let sandbox, utilsMock, newVideoRequest; - beforeEach(() => { - utilsMock = sinon.mock(utils); - sandbox = sinon.sandbox.create(); - sandbox.spy(utils, 'logWarn'); - newVideoRequest = utils.deepClone(videoBidRequests) - }); - - afterEach(() => { - utilsMock.restore(); - sandbox.restore(); - }) - - it('Should log warning if video params from mediaTypes and params obj of bid are not present', function () { - delete newVideoRequest[0].mediaTypes.video; - delete newVideoRequest[0].params.video; + let request = spec.buildRequests(newVideoRequest, { + auctionId: 'new-auction-id' + }); - let request = spec.buildRequests(newVideoRequest, { - auctionId: 'new-auction-id' + sinon.assert.calledOnce(utils.logWarn); + expect(request).to.equal(undefined); }); - sinon.assert.calledOnce(utils.logWarn); - expect(request).to.equal(undefined); - }); - - it('Should consider video params from mediaType object of bid', function () { - delete newVideoRequest[0].params.video; + it('Should consider video params from mediaType object of bid', function () { + delete newVideoRequest[0].params.video; - let request = spec.buildRequests(newVideoRequest, { - auctionId: 'new-auction-id' - }); - let data = JSON.parse(request.data); - expect(data.imp[0].video).to.exist; - expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); - expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); - expect(data.imp[0]['video']['battr']).to.equal(undefined); - }); - - describe('Assign Deal Tier (i.e. prebidDealPriority)', function () { - let videoSeatBid, request, newBid; - // let data = JSON.parse(request.data); - beforeEach(function () { - videoSeatBid = videoBidResponse.body.seatbid[0].bid[0]; - // const adpodValidOutstreamBidRequest = validOutstreamBidRequest.bids[0].mediaTypes.video.context = 'adpod'; - request = spec.buildRequests(bidRequests, validOutstreamBidRequest); - newBid = { - requestId: '47acc48ad47af5' - }; - videoSeatBid.ext = videoSeatBid.ext || {}; - videoSeatBid.ext.video = videoSeatBid.ext.video || {}; - // videoBidRequests[0].mediaTypes.video = videoBidRequests[0].mediaTypes.video || {}; - }); - - it('should not assign video object if deal priority is missing', function () { - assignDealTier(newBid, videoSeatBid, request); - expect(newBid.video).to.equal(undefined); - expect(newBid.video).to.not.exist; - }); - - it('should not assign video object if context is not a adpod', function () { - videoSeatBid.ext.prebiddealpriority = 5; - assignDealTier(newBid, videoSeatBid, request); - expect(newBid.video).to.equal(undefined); - expect(newBid.video).to.not.exist; + let request = spec.buildRequests(newVideoRequest, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist; + expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); + expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); + expect(data.imp[0]['video']['battr']).to.equal(undefined); }); - describe('when video deal tier object is present', function () { + describe('Assign Deal Tier (i.e. prebidDealPriority)', function () { + let videoSeatBid, request, newBid; + // let data = JSON.parse(request.data); beforeEach(function () { - videoSeatBid.ext.prebiddealpriority = 5; - request.bidderRequest.bids[0].mediaTypes.video = { - ...request.bidderRequest.bids[0].mediaTypes.video, - context: 'adpod', - maxduration: 50 + videoSeatBid = videoBidResponse.body.seatbid[0].bid[0]; + // const adpodValidOutstreamBidRequest = validOutstreamBidRequest.bids[0].mediaTypes.video.context = 'adpod'; + request = spec.buildRequests(bidRequests, validOutstreamBidRequest); + newBid = { + requestId: '47acc48ad47af5' }; + videoSeatBid.ext = videoSeatBid.ext || {}; + videoSeatBid.ext.video = videoSeatBid.ext.video || {}; + // videoBidRequests[0].mediaTypes.video = videoBidRequests[0].mediaTypes.video || {}; }); - it('should set video deal tier object, when maxduration is present in ext', function () { + it('should not assign video object if deal priority is missing', function () { assignDealTier(newBid, videoSeatBid, request); - expect(newBid.video.durationSeconds).to.equal(50); - expect(newBid.video.context).to.equal('adpod'); - expect(newBid.video.dealTier).to.equal(5); + expect(newBid.video).to.equal(undefined); + expect(newBid.video).to.not.exist; }); - it('should set video deal tier object, when duration is present in ext', function () { - videoSeatBid.ext.video.duration = 20; + it('should not assign video object if context is not a adpod', function () { + videoSeatBid.ext.prebiddealpriority = 5; assignDealTier(newBid, videoSeatBid, request); - expect(newBid.video.durationSeconds).to.equal(20); - expect(newBid.video.context).to.equal('adpod'); - expect(newBid.video.dealTier).to.equal(5); + expect(newBid.video).to.equal(undefined); + expect(newBid.video).to.not.exist; + }); + + describe('when video deal tier object is present', function () { + beforeEach(function () { + videoSeatBid.ext.prebiddealpriority = 5; + request.bidderRequest.bids[0].mediaTypes.video = { + ...request.bidderRequest.bids[0].mediaTypes.video, + context: 'adpod', + maxduration: 50 + }; + }); + + it('should set video deal tier object, when maxduration is present in ext', function () { + assignDealTier(newBid, videoSeatBid, request); + expect(newBid.video.durationSeconds).to.equal(50); + expect(newBid.video.context).to.equal('adpod'); + expect(newBid.video.dealTier).to.equal(5); + }); + + it('should set video deal tier object, when duration is present in ext', function () { + videoSeatBid.ext.video.duration = 20; + assignDealTier(newBid, videoSeatBid, request); + expect(newBid.video.durationSeconds).to.equal(20); + expect(newBid.video.context).to.equal('adpod'); + expect(newBid.video.dealTier).to.equal(5); + }); }); }); }); - }); + } describe('Marketplace params', function () { let sandbox, utilsMock, newBidRequests, newBidResponses; diff --git a/test/spec/modules/realvuAnalyticsAdapter_spec.js b/test/spec/modules/realvuAnalyticsAdapter_spec.js index e51a4e2e3a2..aa17a2c7c75 100644 --- a/test/spec/modules/realvuAnalyticsAdapter_spec.js +++ b/test/spec/modules/realvuAnalyticsAdapter_spec.js @@ -81,11 +81,6 @@ describe('RealVu', function() { result = realvuAnalyticsAdapter.checkIn(bid, ''); // test invalid partnerId '' }); - it.skip('isInView returns "yes"', () => { - let inview = realvuAnalyticsAdapter.isInView('ad1'); - expect(inview).to.equal('yes'); - }); - it('isInView return "NA"', function () { const adUnitCode = '1234'; let result = realvuAnalyticsAdapter.isInView(adUnitCode); diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index 792d3ab0a9e..70011cbf244 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -631,10 +631,10 @@ describe('RTBHouseAdapter', () => { expect(bids[0].meta.advertiserDomains).to.deep.equal(['rtbhouse.com']); expect(bids[0].native).to.deep.equal({ title: 'Title text', - clickUrl: encodeURIComponent('https://example.com'), + clickUrl: encodeURI('https://example.com'), impressionTrackers: ['https://example.com/imptracker'], image: { - url: encodeURIComponent('https://example.com/image.jpg'), + url: encodeURI('https://example.com/image.jpg'), width: 150, height: 50 }, diff --git a/test/spec/modules/vidoomyBidAdapter_spec.js b/test/spec/modules/vidoomyBidAdapter_spec.js index 607fdf4b5b8..38fa872e6b8 100644 --- a/test/spec/modules/vidoomyBidAdapter_spec.js +++ b/test/spec/modules/vidoomyBidAdapter_spec.js @@ -162,6 +162,29 @@ describe('vidoomyBidAdapter', function() { expect(request[0].data.schain).to.eq(serializedForm); }); + it('should return standard json formated eids', function () { + const eids = [{ + source: 'pubcid.org', + uids: [ + { + id: 'some-random-id-value-1', + atype: 1 + } + ] + }, + { + source: 'adserver.org', + uids: [{ + id: 'some-random-id-value-2', + atype: 1 + }] + }] + bidRequests[0].userIdAsEids = eids + const bidRequest = spec.buildRequests(bidRequests, bidderRequest); + expect(bidRequest[0].data).to.include.any.keys('eids'); + expect(JSON.parse(bidRequest[0].data.eids)).to.eql(eids); + }); + it('should set the bidfloor if getFloor module is undefined but static bidfloor is present', function () { const request = { ...bidRequests[0], params: { bidfloor: 2.5 } } const req = spec.buildRequests([request], bidderRequest)[0]; @@ -196,6 +219,55 @@ describe('vidoomyBidAdapter', function() { expect(bidRequest.data.bidfloor).to.equal(bidfloor); }); }); + + describe('badv, bcat, bapp, btype, battr', function () { + const bidderRequestNew = { + ...bidderRequest, + bcat: ['EX1', 'EX2', 'EX3'], + badv: ['site.com'], + bapp: ['app.com'], + btype: [1, 2, 3], + battr: [1, 2, 3] + } + const request = spec.buildRequests(bidRequests, bidderRequestNew); + it('should have badv, bcat, bapp, btype, battr in request', function () { + expect(request[0].data).to.include.any.keys('badv'); + expect(request[0].data).to.include.any.keys('bcat'); + expect(request[0].data).to.include.any.keys('bapp'); + expect(request[0].data).to.include.any.keys('btype'); + expect(request[0].data).to.include.any.keys('battr'); + }) + + it('should have equal badv, bcat, bapp, btype, battr in request', function () { + expect(request[0].badv).to.deep.equal(bidderRequest.refererInfo.badv); + expect(request[0].bcat).to.deep.equal(bidderRequest.refererInfo.bcat); + expect(request[0].bapp).to.deep.equal(bidderRequest.refererInfo.bapp); + expect(request[0].btype).to.deep.equal(bidderRequest.refererInfo.btype); + expect(request[0].battr).to.deep.equal(bidderRequest.refererInfo.battr); + }) + }) + + describe('first party data', function () { + const bidderRequest2 = { + ...bidderRequest, + ortb2: { + bcat: ['EX1', 'EX2', 'EX3'], + badv: ['site.com'], + bapp: ['app.com'], + btype: [1, 2, 3], + battr: [1, 2, 3] + } + } + const request = spec.buildRequests(bidRequests, bidderRequest2); + + it('should have badv, bcat, bapp, btype, battr in request and equal to bidderRequest.ortb2', function () { + expect(request[0].data.bcat).to.deep.equal(bidderRequest2.ortb2.bcat) + expect(request[0].data.badv).to.deep.equal(bidderRequest2.ortb2.badv) + expect(request[0].data.bapp).to.deep.equal(bidderRequest2.ortb2.bapp); + expect(request[0].data.btype).to.deep.equal(bidderRequest2.ortb2.btype); + expect(request[0].data.battr).to.deep.equal(bidderRequest2.ortb2.battr); + }); + }); }); describe('interpretResponse', function () { diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index d6bf15ff9d4..3706f770da8 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -212,7 +212,7 @@ describe('YieldmoAdapter', function () { expect(data.hasOwnProperty('h')).to.be.true; expect(data.hasOwnProperty('w')).to.be.true; expect(data.hasOwnProperty('pubcid')).to.be.true; - expect(data.userConsent).to.equal('{"gdprApplies":"","cmp":""}'); + expect(data.userConsent).to.equal('{"gdprApplies":"","cmp":"","gpp":"","gpp_sid":[]}'); expect(data.us_privacy).to.equal(''); }); @@ -262,6 +262,24 @@ describe('YieldmoAdapter', function () { JSON.stringify({ gdprApplies: true, cmp: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + gpp: '', + gpp_sid: [], + }) + ); + }); + + it('should add gpp information to request if available', () => { + const gppConsent = { + 'gppString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + 'applicableSections': [8] + }; + const data = buildAndGetData([mockBannerBid()], 0, mockBidderRequest({gppConsent})); + expect(data.userConsent).equal( + JSON.stringify({ + gdprApplies: '', + cmp: '', + gpp: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + gpp_sid: [8], }) ); }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index b069b13fc37..820d87ef49c 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -888,16 +888,32 @@ describe('Unit: Prebid Module', function () { it('should only apply price granularity if bid media type matches', function () { initTestConfig({ - adUnits: [ createAdUnit('div-gpt-ad-1460505748561-0', 'video') ], + adUnits: [createAdUnit('div-gpt-ad-1460505748561-0')], adUnitCodes: ['div-gpt-ad-1460505748561-0'] }); - response = videoResponse; + response = bannerResponse; response.tags[0].ads[0].cpm = 3.4288; auction.callBids(cbTimeout); let bidTargeting = targeting.getAllTargeting(); - expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.00'); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.25'); + + if (FEATURES.VIDEO) { + ajaxStub.restore(); + + initTestConfig({ + adUnits: [createAdUnit('div-gpt-ad-1460505748561-0', 'video')], + adUnitCodes: ['div-gpt-ad-1460505748561-0'] + }); + + response = videoResponse; + response.tags[0].ads[0].cpm = 3.4288; + + auction.callBids(cbTimeout); + let bidTargeting = targeting.getAllTargeting(); + expect(bidTargeting['div-gpt-ad-1460505748561-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]).to.equal('3.00'); + } }); }); diff --git a/test/test_index.js b/test/test_index.js index 04d1412860b..ce9b671be89 100644 --- a/test/test_index.js +++ b/test/test_index.js @@ -1,4 +1,3 @@ - [it, describe].forEach((ob) => { ob.only = function () { [ @@ -7,8 +6,19 @@ // eslint-disable-next-line no-console ].forEach(l => console.error(l)) throw new Error('do not use .only()') - } -}) + }; +}); + +[it, describe].forEach((ob) => { + ob.skip = function () { + [ + 'describe.skip and it.skip are disabled,', + 'because they pollute the pipeline test output', + // eslint-disable-next-line no-console + ].forEach(l => console.error(l)) + throw new Error('do not use .skip()') + }; +}); require('./test_deps.js');