diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index dc0751621417..87754bcba030 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -4,7 +4,7 @@ import * as utils from '../src/utils'; const BIDDER_CODE = '33across'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; -const SYNC_ENDPOINT = 'https://de.tynt.com/deb/v2?m=xch&rt=html'; +const SYNC_ENDPOINT = 'https://ssc-cms.33across.com/ps/?m=xch&rt=html&ru=deb'; const adapterState = {}; @@ -144,7 +144,7 @@ function _createServerRequest(bidRequest, gdprConsent = {}) { } // Sync object will always be of type iframe for TTX -function _createSync({siteId, gdprConsent = {}}) { +function _createSync({siteId = 'zzz000000000003zzz', gdprConsent = {}}) { const ttxSettings = config.getConfig('ttxSettings'); const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; diff --git a/modules/7xbidBidAdapter.js b/modules/7xbidBidAdapter.js new file mode 100644 index 000000000000..5464f87ee994 --- /dev/null +++ b/modules/7xbidBidAdapter.js @@ -0,0 +1,155 @@ +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; + +const BIDDER_CODE = '7xbid'; +const BIDDER_ALIAS = '7xb'; +const ENDPOINT_BANNER = '//bidder.7xbid.com/api/v1/prebid/banner'; +const ENDPOINT_NATIVE = '//bidder.7xbid.com/api/v1/prebid/native'; +const COOKIE_SYNC_URL = '//bidder.7xbid.com/api/v1/cookie/gen'; +const SUPPORTED_MEDIA_TYPES = ['banner', 'native']; +const SUPPORTED_CURRENCIES = ['USD', 'JPY']; +const DEFAULT_CURRENCY = 'JPY'; +const NET_REVENUE = true; + +const _encodeURIComponent = function(a) { + let b = window.encodeURIComponent(a); + b = b.replace(/'/g, '%27'); + return b; +} + +export const _getUrlVars = function(url) { + var hash; + var myJson = {}; + var hashes = url.slice(url.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + myJson[hash[0]] = hash[1]; + } + return myJson; +} + +export const spec = { + code: BIDDER_CODE, + aliases: [BIDDER_ALIAS], // short code + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + isBidRequestValid: function(bid) { + if (!(bid.params.placementId)) { + return false; + } + + if (bid.params.hasOwnProperty('currency') && + SUPPORTED_CURRENCIES.indexOf(bid.params.currency) === -1) { + utils.logInfo('Invalid currency type, we support only JPY and USD!') + return false; + } + + return true; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + let serverRequests = []; + var refererInfo; + if (bidderRequest && bidderRequest.refererInfo) { + refererInfo = bidderRequest.refererInfo; + } + validBidRequests.forEach((bid, i) => { + let endpoint = ENDPOINT_BANNER + let data = { + 'placementid': bid.params.placementId, + 'cur': bid.params.hasOwnProperty('currency') ? bid.params.currency : DEFAULT_CURRENCY, + 'ua': navigator.userAgent, + 'loc': utils.getTopWindowUrl(), + 'topframe': (window.parent === window.self) ? 1 : 0, + 'sw': screen && screen.width, + 'sh': screen && screen.height, + 'cb': Math.floor(Math.random() * 99999999999), + 'tpaf': 1, + 'cks': 1, + 'requestid': bid.bidId + }; + + if (bid.hasOwnProperty('nativeParams')) { + endpoint = ENDPOINT_NATIVE + data.tkf = 1 // return url tracker + data.ad_track = '1' + data.apiv = '1.1.0' + } + + if (refererInfo && refererInfo.referer) { + data.referer = refererInfo.referer; + } else { + data.referer = ''; + } + + serverRequests.push({ + method: 'GET', + url: endpoint, + data: utils.parseQueryStringParameters(data) + }) + }) + + return serverRequests; + }, + interpretResponse: function(serverResponse, request) { + const data = _getUrlVars(request.data) + const successBid = serverResponse.body || {}; + let bidResponses = []; + if (successBid.hasOwnProperty(data.placementid)) { + let bid = successBid[data.placementid] + let bidResponse = { + requestId: bid.requestid, + cpm: bid.price, + creativeId: bid.creativeId, + currency: bid.cur, + netRevenue: NET_REVENUE, + ttl: 700 + }; + + if (bid.hasOwnProperty('title')) { // it is native ad response + bidResponse.mediaType = 'native' + bidResponse.native = { + title: bid.title, + body: bid.description, + cta: bid.cta, + sponsoredBy: bid.advertiser, + clickUrl: _encodeURIComponent(bid.landingURL), + impressionTrackers: bid.trackings, + } + if (bid.screenshots) { + bidResponse.native.image = { + url: bid.screenshots.url, + height: bid.screenshots.height, + width: bid.screenshots.width, + } + } + if (bid.icon) { + bidResponse.native.icon = { + url: bid.icon.url, + height: bid.icon.height, + width: bid.icon.width, + } + } + } else { + bidResponse.ad = bid.adm + bidResponse.width = bid.width + bidResponse.height = bid.height + } + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + return [{ + type: 'image', + url: COOKIE_SYNC_URL + }]; + } +} +registerBidder(spec); diff --git a/modules/7xbidBidAdapter.md b/modules/7xbidBidAdapter.md new file mode 100644 index 000000000000..13a448da9927 --- /dev/null +++ b/modules/7xbidBidAdapter.md @@ -0,0 +1,60 @@ +# Overview + +Module Name: 7xbid Bid Adapter + +Maintainer: 7xbid.com@gmail.com + +# Description + +Module that connects to 7xbid's demand sources + +# Test Parameters +```javascript + var adUnits = [ + { + code: 'test', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, + bids: [ + { + bidder: '7xbid', + params: { + placementId: 1425292, + currency: 'USD' + + } + } + ] + }, + { + code: 'test', + mediaTypes: { + native: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + } + } + }, + bids: [ + { + bidder: '7xbid', + params: { + placementId: 1429695, + currency: 'USD' + } + }, + ], + } + ]; +``` \ No newline at end of file diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js new file mode 100644 index 000000000000..33642a1ac9ee --- /dev/null +++ b/modules/ablidaBidAdapter.js @@ -0,0 +1,93 @@ +import * as utils from '../src/utils'; +import {config} from '../src/config'; +import {registerBidder} from '../src/adapters/bidderFactory'; + +const BIDDER_CODE = 'ablida'; +const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; + +export const spec = { + code: BIDDER_CODE, + + /** + * 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) { + return !!(bid.params.placementId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @return Array Info describing the request to the server. + * @param validBidRequests + * @param bidderRequest + */ + buildRequests: function (validBidRequests, bidderRequest) { + if (validBidRequests.length === 0) { + return []; + } + return validBidRequests.map(bidRequest => { + const sizes = utils.parseSizesInput(bidRequest.sizes)[0]; + const size = sizes.split('x'); + const jaySupported = 'atob' in window && 'currentScript' in document; + const device = getDevice(); + const payload = { + placementId: bidRequest.params.placementId, + width: size[0], + height: size[1], + bidId: bidRequest.bidId, + categories: bidRequest.params.categories, + referer: bidderRequest.refererInfo.referer, + jaySupported: jaySupported, + device: device + }; + return { + method: 'POST', + url: ENDPOINT_URL, + data: payload + }; + }); + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} 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) { + const bidResponses = []; + const response = serverResponse.body; + + response.forEach(function(bid) { + bid.ttl = config.getConfig('_bidderTimeout'); + bidResponses.push(bid); + }); + return bidResponses; + }, +}; + +function getDevice() { + const ua = navigator.userAgent; + const topWindow = window.top; + if ((/(ipad|xoom|sch-i800|playbook|silk|tablet|kindle)|(android(?!.*mobi))/i).test(ua)) { + return 'tablet'; + } + if ((/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(ua)) { + return 'connectedtv'; + } + if ((/Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Windows\sCE|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/i).test(ua)) { + return 'smartphone'; + } + const width = topWindow.innerWidth || topWindow.document.documentElement.clientWidth || topWindow.document.body.clientWidth; + if (width > 320) { + return 'desktop'; + } + return 'other'; +} + +registerBidder(spec); diff --git a/modules/ablidaBidAdapter.md b/modules/ablidaBidAdapter.md new file mode 100644 index 000000000000..70e6576cd309 --- /dev/null +++ b/modules/ablidaBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +**Module Name**: Ablida Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: d.kuster@ablida.de + +# Description + +Module that connects to Ablida's bidder for bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'ad-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'ablida', + params: { + placementId: 'mediumrectangle-demo', + categories: ['automotive', 'news-and-politics'] // optional: categories of page + } + } + ] + } + ]; +``` diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index 1cdbec829d91..32b9f0d1b0c0 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -4,17 +4,53 @@ import adapter from '../src/AnalyticsAdapter'; import adapterManager from '../src/adapterManager'; +import CONSTANTS from '../src/constants.json'; +import * as utils from '../src/utils'; -// This config makes Prebid.js call this function on each event: -// `window['AdagioPrebidAnalytics']('on', eventType, args)` -// If it is missing, then Prebid.js will immediately log an error, -// instead of queueing the events until the function appears. -var adagioAdapter = adapter({ - global: 'AdagioPrebidAnalytics', - handler: 'on', - analyticsType: 'bundle' +const emptyUrl = ''; +const analyticsType = 'endpoint'; +const events = Object.keys(CONSTANTS.EVENTS).map(key => CONSTANTS.EVENTS[key]); +const VERSION = '2.0.0'; + +const adagioEnqueue = function adagioEnqueue(action, data) { + utils.getWindowTop().ADAGIO.queue.push({ action, data, ts: Date.now() }); +} + +function canAccessTopWindow() { + try { + if (utils.getWindowTop().location.href) { + return true; + } + } catch (error) { + return false; + } +} + +let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), { + track: function({ eventType, args }) { + if (typeof args !== 'undefined' && events.indexOf(eventType) !== -1) { + adagioEnqueue('pb-analytics-event', { eventName: eventType, args }); + } + } }); +adagioAdapter.originEnableAnalytics = adagioAdapter.enableAnalytics; + +adagioAdapter.enableAnalytics = config => { + if (!canAccessTopWindow()) { + return; + } + + const w = utils.getWindowTop(); + + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.queue = w.ADAGIO.queue || []; + w.ADAGIO.versions = w.ADAGIO.versions || {}; + w.ADAGIO.versions.adagioAnalyticsAdapter = VERSION; + + adagioAdapter.originEnableAnalytics(config); +} + adapterManager.registerAnalyticsAdapter({ adapter: adagioAdapter, code: 'adagio' diff --git a/modules/adagioAnalyticsAdapter.md b/modules/adagioAnalyticsAdapter.md index 5734bc85b2a0..312a26ea8da0 100644 --- a/modules/adagioAnalyticsAdapter.md +++ b/modules/adagioAnalyticsAdapter.md @@ -1,7 +1,7 @@ # Overview Module Name: Adagio Analytics Adapter -Module Type: Adagio Adapter +Module Type: Analytics Adapter Maintainer: dev@adagio.io # Description diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 8f6e59b06334..76614e52bc52 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1,36 +1,232 @@ import find from 'core-js/library/fn/array/find'; import * as utils from '../src/utils'; -import { registerBidder } from '../src/adapters/bidderFactory'; +import {registerBidder} from '../src/adapters/bidderFactory'; +import { loadExternalScript } from '../src/adloader' +import JSEncrypt from 'jsencrypt/bin/jsencrypt'; +import sha256 from 'crypto-js/sha256'; const BIDDER_CODE = 'adagio'; -const VERSION = '1.0.0'; +const VERSION = '2.0.0'; +const FEATURES_VERSION = '1'; const ENDPOINT = 'https://mp.4dex.io/prebid'; const SUPPORTED_MEDIA_TYPES = ['banner']; +const ADAGIO_TAG_URL = '//script.4dex.io/localstore.js'; +const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; -/** - * Based on https://github.com/ua-parser/uap-cpp/blob/master/UaParser.cpp#L331, with the following updates: - * - replaced `mobile` by `mobi` in the table regexp, so Opera Mobile on phones is not detected as a tablet. - */ -function _getDeviceType() { - let ua = navigator.userAgent; +export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0 +jFSzIbGJJyg3cKqvtE6A0iaz9PkIdJIvSSSNrmJv+lRGKPEyRA/VnzJIieL39Ngl +t0b0lsHN+W4n9kitS/DZ/xnxWK/9vxhv0ZtL1LL/rwR5Mup7rmJbNtDoNBw4TIGj +pV6EP3MTLosuUEpLaQIDAQAB +-----END PUBLIC KEY-----`; + +export function getAdagioScript() { + try { + const w = utils.getWindowTop(); + const ls = w.localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY); + + if (!ls) { + utils.logWarn('Adagio Script not found'); + return; + } + + const hashRgx = /^(\/\/ hash: (.+)\n)(.+\n)$/; - // Tablets must be checked before phones. - if ((/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i).test(ua)) { - return 5; // "tablet" + if (!hashRgx.test(ls)) { + utils.logWarn('No hash found in Adagio script'); + w.localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY); + } else { + const r = ls.match(hashRgx); + const hash = r[2]; + const content = r[3]; + + var jsEncrypt = new JSEncrypt(); + jsEncrypt.setPublicKey(ADAGIO_PUBKEY); + + if (jsEncrypt.verify(content, hash, sha256)) { + utils.logInfo('Start Adagio script'); + Function(ls)(); // eslint-disable-line no-new-func + } else { + utils.logWarn('Invalid Adagio script found'); + w.localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY); + } + } + } catch (err) { + // } - if ((/Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/).test(ua)) { - return 4; // "phone" +} + +function canAccessTopWindow() { + try { + if (utils.getWindowTop().location.href) { + return true; + } + } catch (error) { + return false; } - // Consider that all other devices are personal computers - return 2; +} + +function initAdagio() { + const w = utils.getWindowTop(); + + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.queue = w.ADAGIO.queue || []; + w.ADAGIO.versions = w.ADAGIO.versions || {}; + w.ADAGIO.versions.adagioBidderAdapter = VERSION; + + getAdagioScript(); + + loadExternalScript(ADAGIO_TAG_URL, BIDDER_CODE) +} + +if (canAccessTopWindow()) { + initAdagio(); +} + +const _features = { + getPrintNumber: function(adUnitCode) { + const adagioAdUnit = _getOrAddAdagioAdUnit(adUnitCode); + return adagioAdUnit.printNumber || 1; + }, + + getPageDimensions: function() { + const viewportDims = _features.getViewPortDimensions().split('x'); + const w = utils.getWindowTop(); + const body = w.document.body; + const html = w.document.documentElement; + const pageHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); + + return viewportDims[0] + 'x' + pageHeight; + }, + + getViewPortDimensions: function() { + let viewPortWidth; + let viewPortHeight; + const w = utils.getWindowTop(); + const d = w.document; + + if (w.innerWidth) { + viewPortWidth = w.innerWidth; + viewPortHeight = w.innerHeight; + } else { + viewPortWidth = d.getElementsByTagName('body')[0].clientWidth; + viewPortHeight = d.getElementsByTagName('body')[0].clientHeight; + } + + return viewPortWidth + 'x' + viewPortHeight; + }, + + isDomLoading: function() { + const w = utils.getWindowTop(); + let performance = w.performance || w.msPerformance || w.webkitPerformance || w.mozPerformance; + let domLoading = -1; + + if (performance && performance.timing && performance.timing.navigationStart > 0) { + const val = performance.timing.domLoading - performance.timing.navigationStart; + if (val > 0) domLoading = val; + } + return domLoading; + }, + + getSlotPosition: function(element) { + const w = utils.getWindowTop(); + const d = w.document; + const el = element; + + let box = el.getBoundingClientRect(); + const docEl = d.documentElement; + const body = d.body; + const clientTop = d.clientTop || body.clientTop || 0; + const clientLeft = d.clientLeft || body.clientLeft || 0; + const scrollTop = w.pageYOffset || docEl.scrollTop || body.scrollTop; + const scrollLeft = w.pageXOffset || docEl.scrollLeft || body.scrollLeft; + + const elComputedStyle = w.getComputedStyle(el, null); + const elComputedDisplay = elComputedStyle.display || 'block'; + const mustDisplayElement = elComputedDisplay === 'none'; + + if (mustDisplayElement) { + el.style = el.style || {}; + el.style.display = 'block'; + box = el.getBoundingClientRect(); + el.style.display = elComputedDisplay; + } + + const position = { + x: Math.round(box.left + scrollLeft - clientLeft), + y: Math.round(box.top + scrollTop - clientTop) + }; + + return position.x + 'x' + position.y; + }, + + getTimestamp: function() { + return Math.floor(new Date().getTime() / 1000) - new Date().getTimezoneOffset() * 60; + }, + + getDevice: function() { + if (!canAccessTopWindow()) return false; + const w = utils.getWindowTop(); + const ua = w.navigator.userAgent; + + if ((/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i).test(ua)) { + return 5; // "tablet" + } + if ((/Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/).test(ua)) { + return 4; // "phone" + } + return 2; // personal computers + }, + + getBrowser: function() { + const w = utils.getWindowTop(); + const ua = w.navigator.userAgent; + const uaLowerCase = ua.toLowerCase(); + return /Edge\/\d./i.test(ua) ? 'edge' : uaLowerCase.indexOf('chrome') > 0 ? 'chrome' : uaLowerCase.indexOf('firefox') > 0 ? 'firefox' : uaLowerCase.indexOf('safari') > 0 ? 'safari' : uaLowerCase.indexOf('opera') > 0 ? 'opera' : uaLowerCase.indexOf('msie') > 0 || w.MSStream ? 'ie' : 'unknow'; + }, + + getOS: function() { + const w = window.top; + const ua = w.navigator.userAgent; + const uaLowerCase = ua.toLowerCase(); + return uaLowerCase.indexOf('linux') > 0 ? 'linux' : uaLowerCase.indexOf('mac') > 0 ? 'mac' : uaLowerCase.indexOf('win') > 0 ? 'windows' : ''; + } +} + +function _pushInAdagioQueue(ob) { + if (!canAccessTopWindow()) return; + const w = utils.getWindowTop(); + w.ADAGIO.queue.push(ob); }; +function _getOrAddAdagioAdUnit(adUnitCode) { + const w = utils.getWindowTop(); + if (w.ADAGIO.adUnits[adUnitCode]) { + return w.ADAGIO.adUnits[adUnitCode] + } + return w.ADAGIO.adUnits[adUnitCode] = {}; +} + +function _computePrintNumber(adUnitCode) { + let printNumber = 1; + const w = utils.getWindowTop(); + if ( + w.ADAGIO && + w.ADAGIO.adUnits && w.ADAGIO.adUnits[adUnitCode] && + w.ADAGIO.adUnits[adUnitCode].pageviewId === _getPageviewId() && + w.ADAGIO.adUnits[adUnitCode].printNumber + ) { + printNumber = parseInt(w.ADAGIO.adUnits[adUnitCode].printNumber, 10) + 1; + } + return printNumber; +} + function _getDevice() { const language = navigator.language ? 'language' : 'userLanguage'; return { userAgent: navigator.userAgent, language: navigator[language], - deviceType: _getDeviceType(), + deviceType: _features.getDevice(), dnt: utils.getDNT() ? 1 : 0, geo: {}, js: 1 @@ -38,36 +234,91 @@ function _getDevice() { }; function _getSite() { - const topLocation = utils.getTopWindowLocation(); + const w = utils.getWindowTop(); return { - domain: topLocation.hostname, - page: topLocation.href, - referrer: utils.getTopWindowReferrer() + domain: w.location.hostname, + page: w.location.href, + referrer: w.document.referrer || '' }; }; function _getPageviewId() { - return (!window.top.ADAGIO || !window.top.ADAGIO.pageviewId) ? '_' : window.top.ADAGIO.pageviewId; + if (!canAccessTopWindow()) return false; + const w = utils.getWindowTop(); + w.ADAGIO.pageviewId = w.ADAGIO.pageviewId || utils.generateUUID(); + return w.ADAGIO.pageviewId; }; +function _getElementFromTopWindow(element, currentWindow) { + if (utils.getWindowTop() === currentWindow) { + if (!element.getAttribute('id')) { + element.setAttribute('id', `adg-${utils.getUniqueIdentifierStr()}`); + } + return element; + } else { + const frame = currentWindow.frameElement; + return _getElementFromTopWindow(frame, currentWindow.parent); + } +} + +/** + * Returns all features for a specific adUnit element + * + * @param {Object} bidRequest + * @returns {Object} features for an element (see specs) + */ function _getFeatures(bidRequest) { - if (!window.top._ADAGIO || !window.top._ADAGIO.features) { - return {}; + if (!canAccessTopWindow()) return; + const w = utils.getWindowTop(); + const adUnitElementId = bidRequest.params.adUnitElementId; + const adUnitCode = bidRequest.adUnitCode; + + let element = window.document.getElementById(adUnitElementId); + + if (bidRequest.params.postBid === true) { + element = _getElementFromTopWindow(element, window); + w.ADAGIO.pbjsAdUnits.map((adUnit) => { + if (adUnit.code === adUnitCode) { + const outerElementId = element.getAttribute('id'); + adUnit.outerAdUnitElementId = outerElementId; + bidRequest.params.outerAdUnitElementId = outerElementId; + } + }); + } else { + element = w.document.getElementById(adUnitElementId); } - const rawFeatures = window.top._ADAGIO.features.getFeatures( - document.getElementById(bidRequest.adUnitCode), - function(features) { - return { - site_id: bidRequest.params.siteId, - placement: bidRequest.params.placementId, - pagetype: bidRequest.params.pagetypeId, - categories: bidRequest.params.categories - }; - } - ); - return rawFeatures; -} + let features = {}; + if (element) { + features = Object.assign({}, { + print_number: _features.getPrintNumber(bidRequest.adUnitCode).toString(), + page_dimensions: _features.getPageDimensions().toString(), + viewport_dimensions: _features.getViewPortDimensions().toString(), + dom_loading: _features.isDomLoading().toString(), + // layout: features.getLayout().toString(), + adunit_position: _features.getSlotPosition(element).toString(), + user_timestamp: _features.getTimestamp().toString(), + device: _features.getDevice().toString(), + url: w.location.origin + w.location.pathname, + browser: _features.getBrowser(), + os: _features.getOS() + }) + } + + const adUnitFeature = {}; + adUnitFeature[adUnitElementId] = { + features: features, + version: FEATURES_VERSION + }; + + _pushInAdagioQueue({ + action: 'features', + ts: Date.now(), + data: adUnitFeature + }); + + return features; +}; function _getGdprConsent(bidderRequest) { const consent = {}; @@ -91,45 +342,77 @@ export const spec = { supportedMediaType: SUPPORTED_MEDIA_TYPES, isBidRequestValid: function(bid) { - return !!(bid.params.siteId && bid.params.placementId); + const { adUnitCode, auctionId, sizes, bidder, params, mediaTypes } = bid; + const { organizationId, site, placement, adUnitElementId } = bid.params; + let isValid = false; + + if (canAccessTopWindow()) { + const w = utils.getWindowTop(); + w.ADAGIO = w.ADAGIO || {}; + w.ADAGIO.adUnits = w.ADAGIO.adUnits || {}; + w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits || []; + isValid = !!(organizationId && site && placement && adUnitElementId && document.getElementById(adUnitElementId) !== null); + const tempAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode); + tempAdUnits.push({ + code: adUnitCode, + sizes: (mediaTypes && mediaTypes.banner && Array.isArray(mediaTypes.banner.sizes)) ? mediaTypes.banner.sizes : sizes, + bids: [{ + bidder, + params + }] + }); + w.ADAGIO.pbjsAdUnits = tempAdUnits; + + if (isValid === true) { + let printNumber = _computePrintNumber(adUnitCode); + w.ADAGIO.adUnits[adUnitCode] = { + auctionId: auctionId, + pageviewId: _getPageviewId(), + printNumber + }; + } + } + + return isValid; }, buildRequests: function(validBidRequests, bidderRequest) { + // AdagioBidAdapter works when window.top can be reached only + if (!bidderRequest.refererInfo.reachedTop) return []; + const secure = (location.protocol === 'https:') ? 1 : 0; const device = _getDevice(); const site = _getSite(); const pageviewId = _getPageviewId(); const gdprConsent = _getGdprConsent(bidderRequest); const adUnits = utils._map(validBidRequests, (bidRequest) => { - bidRequest.params.features = _getFeatures(bidRequest); - const categories = bidRequest.params.categories; - if (typeof categories !== 'undefined' && !Array.isArray(categories)) { - bidRequest.params.categories = [categories]; - } + bidRequest.features = _getFeatures(bidRequest); return bidRequest; }); // Regroug ad units by siteId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - (groupedAdUnits[adUnit.params.siteId] = groupedAdUnits[adUnit.params.siteId] || []).push(adUnit); + (groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []).push(adUnit); return groupedAdUnits; }, {}); // Build one request per siteId - const requests = utils._map(Object.keys(groupedAdUnits), (siteId) => { + const requests = utils._map(Object.keys(groupedAdUnits), (organizationId) => { return { method: 'POST', url: ENDPOINT, data: { id: utils.generateUUID(), + organizationId: organizationId, secure: secure, device: device, site: site, - siteId: siteId, pageviewId: pageviewId, - adUnits: groupedAdUnits[siteId], + adUnits: groupedAdUnits[organizationId], gdpr: gdprConsent, - adapterVersion: VERSION + prebidVersion: '$prebid.version$', + adapterVersion: VERSION, + featuresVersion: FEATURES_VERSION }, options: { contentType: 'application/json' @@ -145,15 +428,27 @@ export const spec = { try { const response = serverResponse.body; if (response) { - response.bids.forEach(bidObj => { - const bidReq = (find(bidRequest.data.adUnits, bid => bid.bidId === bidObj.requestId)); - if (bidReq) { - bidObj.placementId = bidReq.params.placementId; - bidObj.pagetypeId = bidReq.params.pagetypeId; - bidObj.categories = (bidReq.params.features && bidReq.params.features.categories) ? bidReq.params.features.categories : []; - } - bidResponses.push(bidObj); - }); + if (response.data) { + _pushInAdagioQueue({ + action: 'ssp-data', + ts: Date.now(), + data: response.data + }); + } + if (response.bids) { + response.bids.forEach(bidObj => { + const bidReq = (find(bidRequest.data.adUnits, bid => bid.bidId === bidObj.requestId)); + if (bidReq) { + bidObj.site = bidReq.params.site; + bidObj.placement = bidReq.params.placement; + bidObj.pagetype = bidReq.params.pagetype; + bidObj.category = bidReq.params.category; + bidObj.subcategory = bidReq.params.subcategory; + bidObj.environment = bidReq.params.environment; + } + bidResponses.push(bidObj); + }); + } } } catch (err) { utils.logError(err); diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index ff33b035e5fb..5dc8aa3d80c3 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -12,21 +12,33 @@ Connects to Adagio demand source to fetch bids. ```javascript var adUnits = [ + { + code: 'dfp_banniere_atf', + sizes: [[300, 250], [300, 600]], + bids: [ { - code: 'ad-unit_code', - sizes: [[300, 250], [300, 600]], - bids: [ - { - bidder: 'adagio', // Required - params: { - siteId: '0', // Required - Site ID from Adagio. - placementId: '4', // Required - Placement ID from Adagio. Refers to the placement of an ad unit in a page. - pagetypeId: '343', // Required - Page type ID from Adagio. - categories: ['IAB12', 'IAB12-2'], // IAB categories of the page. - } - } - ] - } + bidder: 'adagio', // Required + params: { + organizationId: '0', // Required - Organization ID provided by Adagio. + site: 'news-of-the-day', // Required - Site Name provided by Adagio. + adUnitElementId: 'dfp_banniere_atf', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above. + + // The following params are limited to 30 characters, + // and can only contain the following characters: + // - alphanumeric (A-Z+a-z+0-9, case-insensitive) + // - dashes `-` + // - underscores `_` + // Also, each param can have at most 50 unique active values (case-insensitive). + environment: 'mobile', // Required. Environment where the page is displayed. + placement: 'ban_atf', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'. + pagetype: 'article', // Required. The pagetype describes what kind of content will be present in the page. + category: 'sport', // Recommended. Category of the content displayed in the page. + subcategory: 'handball', // Optional. Subcategory of the content displayed in the page. + postBid: false // Optional. Use it in case of Post-bid integration only. + } + } + ] + } ]; pbjs.addAdUnits(adUnits); @@ -35,22 +47,40 @@ Connects to Adagio demand source to fetch bids. adagio: { alwaysUseBid: true, adserverTargeting: [ + { + key: "site", + val: function (bidResponse) { + return bidResponse.site; + } + }, + { + key: "environment", + val: function (bidResponse) { + return bidResponse.environment; + } + }, { key: "placement", val: function (bidResponse) { - return bidResponse.placementId; + return bidResponse.placement; } }, { key: "pagetype", val: function (bidResponse) { - return bidResponse.pagetypeId; + return bidResponse.pagetype; + } + }, + { + key: "category", + val: function (bidResponse) { + return bidResponse.category; } }, { - key: "categories", + key: "subcategory", val: function (bidResponse) { - return bidResponse.categories.join(","); + return bidResponse.subcategory; } } ] diff --git a/modules/adformBidAdapter.js b/modules/adformBidAdapter.js index a3aef10e41e7..0ac083e7e7c7 100644 --- a/modules/adformBidAdapter.js +++ b/modules/adformBidAdapter.js @@ -1,8 +1,12 @@ 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory'; +import { registerBidder } from '../src/adapters/bidderFactory'; import { config } from '../src/config'; import { BANNER, VIDEO } from '../src/mediaTypes'; +import { Renderer } from '../src/Renderer'; +import * as utils from '../src/utils'; + +const OUTSTREAM_RENDERER_URL = 'https://s2.adform.net/banners/scripts/video/outstream/render.js'; const BIDDER_CODE = 'adform'; export const spec = { @@ -18,7 +22,7 @@ export const spec = { var request = []; var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'fd', 1 ], [ 'url', null ], [ 'tid', null ] ]; var bids = JSON.parse(JSON.stringify(validBidRequests)); - var bidder = (bids[0] && bids[0].bidder) || BIDDER_CODE + var bidder = (bids[0] && bids[0].bidder) || BIDDER_CODE; for (i = 0, l = bids.length; i < l; i++) { bid = bids[i]; if ((bid.params.priceType === 'net') || (bid.params.pt === 'net')) { @@ -112,6 +116,12 @@ export const spec = { vastXml: response.vast_content, mediaType: type }; + + if (!bid.renderer && utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + bidObject.renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL}); + bidObject.renderer.setRender(renderer); + } + if (bidRequest.gdpr) { bidObject.gdpr = bidRequest.gdpr.gdpr; bidObject.gdpr_consent = bidRequest.gdpr.gdpr_consent; @@ -121,6 +131,12 @@ export const spec = { } return bidRespones; + function renderer(bid) { + bid.renderer.push(() => { + window.Adform.renderOutstream(bid); + }); + } + function verifySize(adItem, validSizes) { for (var j = 0, k = validSizes.length; j < k; j++) { if (adItem.width == validSizes[j][0] && diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index a5f8f65634de..c406604b20e2 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -23,7 +23,7 @@ const VERSION = '1.3'; export const spec = { code: 'adkernel', - aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia'], + aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak'], supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bidRequest) { return 'params' in bidRequest && diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 0143f01835d5..49b6b0c4edb8 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -7,13 +7,13 @@ import { VIDEO, BANNER } from '../src/mediaTypes'; import find from 'core-js/library/fn/array/find'; import includes from 'core-js/library/fn/array/includes'; -const ADAPTER_VERSION = '1.7'; +const ADAPTER_VERSION = '1.8'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; export const VIDEO_ENDPOINT = 'https://reachms.bfmio.com/bid.json?exchange_id='; export const BANNER_ENDPOINT = 'https://display.bfmio.com/prebid_display'; -export const OUTSTREAM_SRC = '//player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; +export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement']; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; @@ -68,10 +68,11 @@ export const spec = { requestId: bidRequest.bidId, bidderCode: spec.code, vastUrl: response.url, + vastXml: response.vast, cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, - creativeId: response.cmpId, + creativeId: response.crid || response.cmpId, renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null, mediaType: VIDEO, currency: 'USD', @@ -151,7 +152,8 @@ function createRenderer(bidRequest) { height: bid.height, expandInView: getPlayerBidParam(bidRequest, 'expandInView', false), collapseOnComplete: getPlayerBidParam(bidRequest, 'collapseOnComplete', true), - progressColor: getPlayerBidParam(bidRequest, 'progressColor') + progressColor: getPlayerBidParam(bidRequest, 'progressColor'), + adPosterColor: getPlayerBidParam(bidRequest, 'adPosterColor') }); }); }); @@ -284,7 +286,9 @@ function createVideoRequestData(bid, bidderRequest) { mimes: DEFAULT_MIMES }, video), bidfloor: bidfloor, - secure: topLocation.protocol === 'https:' ? 1 : 0 + secure: topLocation.protocol === 'https:' ? 1 : 0, + displaymanager: ADAPTER_NAME, + displaymanagerver: ADAPTER_VERSION }], site: { page: topLocation.href, @@ -325,6 +329,11 @@ function createVideoRequestData(bid, bidderRequest) { }]; } + let connection = navigator.connection || navigator.webkitConnection; + if (connection && connection.effectiveType) { + payload.device.connectiontype = connection.effectiveType; + } + return payload; } diff --git a/modules/beachfrontBidAdapter.md b/modules/beachfrontBidAdapter.md index 3f827fe9241a..0a6b8b73da46 100644 --- a/modules/beachfrontBidAdapter.md +++ b/modules/beachfrontBidAdapter.md @@ -109,6 +109,7 @@ Module that connects to Beachfront's demand sources }, player: { progressColor: '#50A8FA', + adPosterColor: '#FFF', expandInView: false, collapseOnComplete: true } diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 00ca6a7bbd68..a3479a9d1d17 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -3,7 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory'; import { BANNER, VIDEO } from '../src/mediaTypes'; const BIDDER_CODE = 'conversant'; -const URL = '//web.hb.ad.cpe.dotomi.com/s2s/header/24'; +const URL = 'https://web.hb.ad.cpe.dotomi.com/s2s/header/24'; export const spec = { code: BIDDER_CODE, @@ -44,26 +44,24 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {BidRequest[]} validBidRequests - an array of bids + * @param bidderRequest * @return {ServerRequest} Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const loc = utils.getTopWindowLocation(); - const page = loc.href; - const isPageSecure = (loc.protocol === 'https:') ? 1 : 0; + const page = (bidderRequest && bidderRequest.refererInfo) ? bidderRequest.refererInfo.referer : ''; let siteId = ''; let requestId = ''; let pubcid = null; const conversantImps = validBidRequests.map(function(bid) { const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); - const secure = isPageSecure || (utils.getBidIdParameter('secure', bid.params) ? 1 : 0); siteId = utils.getBidIdParameter('site_id', bid.params); requestId = bid.auctionId; const imp = { id: bid.bidId, - secure: secure, + secure: 1, bidfloor: bidfloor || 0, displaymanager: 'Prebid.js', displaymanagerver: '$prebid.version$' @@ -154,6 +152,7 @@ export const spec = { * 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) { @@ -249,7 +248,7 @@ function getDevice() { * * [[300, 250], [300, 600]] => [{w: 300, h: 250}, {w: 300, h: 600}] * - * @param {number[2][]|number[2]} bidSizes - arrays of widths and heights + * @param {Array.>} bidSizes - arrays of widths and heights * @returns {object[]} Array of objects with w and h */ function convertSizes(bidSizes) { diff --git a/modules/conversantBidAdapter.md b/modules/conversantBidAdapter.md index 3ce83d8c893e..5aba56530439 100644 --- a/modules/conversantBidAdapter.md +++ b/modules/conversantBidAdapter.md @@ -13,7 +13,11 @@ Module that connects to Conversant's demand sources. Supports banners and video var adUnits = [ { code: 'banner-test-div', - sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250],[300,600]] + } + }, bids: [{ bidder: "conversant", params: { diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 86f289421ce4..7f292c76e209 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -8,7 +8,7 @@ import find from 'core-js/library/fn/array/find'; import JSEncrypt from 'jsencrypt/bin/jsencrypt'; import sha256 from 'crypto-js/sha256'; -export const ADAPTER_VERSION = 21; +export const ADAPTER_VERSION = 23; const BIDDER_CODE = 'criteo'; const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; const CRITEO_VENDOR_ID = 91; @@ -79,7 +79,7 @@ export const spec = { url = adapter.buildCdbUrl(); data = adapter.buildCdbRequest(); } else { - const context = buildContext(bidRequests); + const context = buildContext(bidRequests, bidderRequest); url = buildCdbUrl(context); data = buildCdbRequest(context, bidRequests, bidderRequest); } @@ -177,14 +177,18 @@ function publisherTagAvailable() { /** * @param {BidRequest[]} bidRequests + * @param bidderRequest * @return {CriteoContext} */ -function buildContext(bidRequests) { - const url = utils.getTopWindowUrl(); - const queryString = parse(url).search; +function buildContext(bidRequests, bidderRequest) { + let referrer = ''; + if (bidderRequest && bidderRequest.refererInfo) { + referrer = bidderRequest.refererInfo.referer; + } + const queryString = parse(referrer).search; const context = { - url: url, + url: referrer, debug: queryString['pbt_debug'] === '1', noLog: queryString['pbt_nolog'] === '1', amp: false, diff --git a/modules/districtmDMXBidAdapter.js b/modules/districtmDMXBidAdapter.js index 12b7ac16c0c2..5942e0ea6833 100644 --- a/modules/districtmDMXBidAdapter.js +++ b/modules/districtmDMXBidAdapter.js @@ -60,6 +60,7 @@ export const spec = { }, buildRequests(bidRequest, bidderRequest) { let timeout = config.getConfig('bidderTimeout'); + let schain = null; let dmxRequest = { id: utils.generateUUID(), cur: ['USD'], @@ -80,11 +81,17 @@ export const spec = { dmxRequest.user.ext = {}; dmxRequest.user.ext.consent = bidderRequest.gdprConsent.consentString; } + try { + schain = bidRequest[0].schain; + dmxRequest.source = {}; + dmxRequest.source.ext = {}; + dmxRequest.source.ext.schain = schain || {} + } catch (e) {} let tosendtags = bidRequest.map(dmx => { var obj = {}; obj.id = dmx.bidId; obj.tagid = String(dmx.params.dmxid); - obj.secure = window.location.protocol === 'https:' ? 1 : 0; + obj.secure = 1; obj.banner = { topframe: 1, w: dmx.sizes[0][0] || 0, @@ -96,14 +103,11 @@ export const spec = { return obj; }); dmxRequest.imp = tosendtags; + return { method: 'POST', url: DMXURI, data: JSON.stringify(dmxRequest), - options: { - contentType: 'application/json', - withCredentials: true - }, bidderRequest } }, diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index 8b763202b7c7..a109bc612dbc 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -20,7 +20,7 @@ export const spec = { const placementId = params.placement; const rnd = Math.floor(Math.random() * 99999999999); - const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); + const referrer = bidderRequest.refererInfo.referer; const bidId = bidRequest.bidId; const payload = { _f: 'html', diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 12f3482184ba..7167f9018aa5 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -7,7 +7,7 @@ import includes from 'core-js/library/fn/array/includes'; const BIDDER_CODE = 'emx_digital'; const ENDPOINT = 'hb.emxdgt.com'; const RENDERER_URL = '//js.brealtime.com/outstream/1.30.0/bundle.js'; -const ADAPTER_VERSION = '1.41.0'; +const ADAPTER_VERSION = '1.41.1'; const DEFAULT_CUR = 'USD'; export const emxAdapter = { @@ -116,7 +116,7 @@ export const emxAdapter = { }, parseResponse: (bidResponseAdm) => { try { - return decodeURIComponent(bidResponseAdm); + return decodeURIComponent(bidResponseAdm.replace(/%(?![0-9][0-9a-fA-F]+)/g, '%25')); } catch (err) { utils.logError('emx_digitalBidAdapter', 'error', err); } diff --git a/modules/googleAnalyticsAdapter.js b/modules/googleAnalyticsAdapter.js index c1c160194493..2e569cd7ba11 100644 --- a/modules/googleAnalyticsAdapter.js +++ b/modules/googleAnalyticsAdapter.js @@ -19,6 +19,7 @@ var _enableCheck = true; var _category = 'Prebid.js Bids'; var _eventCount = 0; var _enableDistribution = false; +var _cpmDistribution = null; var _trackerSend = null; var _sampled = true; @@ -42,6 +43,9 @@ adapter.enableAnalytics = function ({ provider, options }) { if (options && typeof options.enableDistribution !== 'undefined') { _enableDistribution = options.enableDistribution; } + if (options && typeof options.cpmDistribution === 'function') { + _cpmDistribution = options.cpmDistribution; + } var bid = null; @@ -166,6 +170,9 @@ function getLoadTimeDistribution(time) { } function getCpmDistribution(cpm) { + if (_cpmDistribution) { + return _cpmDistribution(cpm); + } var distribution; if (cpm >= 0 && cpm < 0.5) { distribution = '$0-0.5'; @@ -255,6 +262,11 @@ function sendBidWonToGa(bid) { checkAnalytics(); } +/** + * Exposed for testing purposes + */ +adapter.getCpmDistribution = getCpmDistribution; + adapterManager.registerAnalyticsAdapter({ adapter, code: 'ga' diff --git a/modules/googleAnalyticsAdapter.md b/modules/googleAnalyticsAdapter.md new file mode 100644 index 000000000000..08423a1d492f --- /dev/null +++ b/modules/googleAnalyticsAdapter.md @@ -0,0 +1,37 @@ +# Google Analytics Adapter + +The google analytics adapter pushes prebid events into google analytics. + +## Usage + +The simplest way to enable the analytics adapter is this + +```javascript +pbjs.enableAnalytics([{ + provider: 'ga' +}]); +``` + +Defaults will be used and you should see events being pushed to analytics. + +You can customize the adapter with various `options` like this + +```javascript +pbjs.enableAnalytics([{ + provider: 'ga', + options: { ... } +}]); + +Here is a full list of settings available + +- `global` (string) - name of the global analytics object. Default is `ga` +- `trackerName` (string) - use another tracker for prebid events. Default is the default tracker +- `sampling` (number) - choose a value from `0` to `1`, where `0` means 0% and `1` means 100% tracked +- `enableDistribution` (boolean) - enables additional events that track load time and cpm distribution + by creating buckets for load time and cpm +- `cpmDistribution` (cpm: number => string) - customize the cpm buckets for the cpm distribution + + +## Additional resources + +- [Prebid GA Analytics](http://prebid.org/overview/ga-analytics.html) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index edbf5ed08bdb..ac9a7d30429f 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -81,7 +81,9 @@ export const spec = { const payload = { auids: auids.join(','), sizes: utils.getKeys(sizeMap).join(','), - r: reqId + r: reqId, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' }; if (bidderRequest) { diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index a269799e92a7..ffbfe466035a 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -28,16 +28,19 @@ export const identityLinkSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function + * @param {ConsentData} [consentData] * @param {SubmoduleParams} [configParams] * @returns {IdResponse|undefined} */ - getId(configParams) { + getId(configParams, consentData) { if (!configParams || typeof configParams.pid !== 'string') { utils.logError('identityLink submodule requires partner id to be defined'); return; } + const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; + const gdprConsentString = hasGdpr ? consentData.consentString : ''; // use protocol relative urls for http or https - const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}`; + const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? '&ct=1&cv=' + gdprConsentString : ''}`; let resp; // if ats library is initialised, use it to retrieve envelope. If not use standard third party endpoint if (window.ats) { diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 8d76d8626550..85d3be2f0ec2 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -6,7 +6,6 @@ import { registerBidder } from '../src/adapters/bidderFactory'; const BIDDER_CODE = 'ix'; const BANNER_SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus'; -const BANNER_INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus'; const SUPPORTED_AD_TYPES = [BANNER]; const ENDPOINT_VERSION = 7.2; const CENT_TO_DOLLAR_FACTOR = 100; @@ -188,10 +187,7 @@ export const spec = { let validBidRequest = null; let bannerImp = null; - // Always start by assuming the protocol is HTTPS. This way, it will work - // whether the page protocol is HTTP or HTTPS. Then check if the page is - // actually HTTP.If we can guarantee it is, then, and only then, set protocol to - // HTTP. + // Always use secure HTTPS protocol. let baseUrl = BANNER_SECURE_BID_URL; for (let i = 0; i < validBidRequests.length; i++) { @@ -258,10 +254,6 @@ export const spec = { if (options.refererInfo) { r.site.page = options.refererInfo.referer; - - if (options.refererInfo.referer && options.refererInfo.referer.indexOf('https') !== 0) { - baseUrl = BANNER_INSECURE_BID_URL; - } } } diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 1ad18cb15eb6..5006a5134540 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -71,7 +71,8 @@ export const spec = { gdprConsent: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.consentString : undefined, cookieSupport: !utils.isSafariBrowser() && utils.cookiesAreEnabled(), rcv: getAdblockerRecovered(), - adRequests: [...adRequests] + adRequests: [...adRequests], + rtbData: HandleEids(bidRequests) }; const payloadString = JSON.stringify(payload); return { @@ -101,7 +102,8 @@ export const spec = { ttl: ad.ttl, creativeId: ad.creativeId, netRevenue: true, - currency: serverResponse.body.currency + currency: serverResponse.body.currency, + meta: ad.meta }; bidResponses.push(bidResponse); @@ -198,4 +200,36 @@ function getAdblockerRecovered() { } catch (e) {} } +function AddExternalUserId(eids, value, source, atype, rtiPartner) { + if (utils.isStr(value)) { + var eid = { + source, + uids: [{ + id: value, + atype + }] + }; + + if (rtiPartner) { + eid.uids[0] = {ext: {rtiPartner}}; + } + + eids.push(eid); + } +} + +function HandleEids(bidRequests) { + let eids = []; + const bidRequest = bidRequests[0]; + if (bidRequest && bidRequest.userId) { + AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcommon', 1); // Also add this to eids + AddExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id`), 'id5-sync.com', 1); + } + if (eids.length > 0) { + return {user: {ext: {eids}}}; + } + + return undefined; +} + registerBidder(spec); diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index 16883aedc863..80084a0d9ce8 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -148,13 +148,6 @@ function getRequestData(bid, consentData) { id: '1', secure: isSecure(), bidfloor: bid.params.bidfloor, - video: { - mimes: bid.params.video.mimes, - w: bid.params.video.playerWidth, - h: bid.params.video.playerHeight, - linearity: 1, - protocols: bid.params.video.protocols || [2, 5] - }, ext: { hb: 1, } @@ -169,50 +162,64 @@ function getRequestData(bid, consentData) { tmax: 200 }; - if (bid.params.video.maxbitrate) { - bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate - } - if (bid.params.video.maxduration) { - bidData.imp[0].video.maxduration = bid.params.video.maxduration - } - if (bid.params.video.minduration) { - bidData.imp[0].video.minduration = bid.params.video.minduration - } - if (bid.params.video.api) { - bidData.imp[0].video.api = bid.params.video.api - } - if (bid.params.video.delivery) { - bidData.imp[0].video.delivery = bid.params.video.delivery - } - if (bid.params.video.position) { - bidData.imp[0].video.pos = bid.params.video.position - } - if (bid.params.video.playbackmethod) { - bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod - } - if (bid.params.video.placement) { - bidData.imp[0].ext.placement = bid.params.video.placement - } - if (bid.params.video.rewarded) { - bidData.imp[0].ext.rewarded = bid.params.video.rewarded - } - if (bid.params.site && bid.params.site.id) { - bidData.site.id = bid.params.site.id - } - if (bid.params.video.sid) { - bidData.source = { - ext: { - schain: { - complete: 1, - nodes: [{ - sid: bid.params.video.sid, - rid: bidData.id, - }] + if (bid.params.video.display == undefined || bid.params.video.display != 1) { + bidData.imp[0].video = { + mimes: bid.params.video.mimes, + w: bid.params.video.playerWidth, + h: bid.params.video.playerHeight, + pos: bid.params.video.position, + }; + if (bid.params.video.maxbitrate) { + bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate + } + if (bid.params.video.maxduration) { + bidData.imp[0].video.maxduration = bid.params.video.maxduration + } + if (bid.params.video.minduration) { + bidData.imp[0].video.minduration = bid.params.video.minduration + } + if (bid.params.video.api) { + bidData.imp[0].video.api = bid.params.video.api + } + if (bid.params.video.delivery) { + bidData.imp[0].video.delivery = bid.params.video.delivery + } + if (bid.params.video.position) { + bidData.imp[0].video.pos = bid.params.video.position + } + if (bid.params.video.playbackmethod) { + bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod + } + if (bid.params.video.placement) { + bidData.imp[0].ext.placement = bid.params.video.placement + } + if (bid.params.video.rewarded) { + bidData.imp[0].ext.rewarded = bid.params.video.rewarded + } + if (bid.params.site && bid.params.site.id) { + bidData.site.id = bid.params.site.id + } + if (bid.params.video.sid) { + bidData.source = { + ext: { + schain: { + complete: 1, + nodes: [{ + sid: bid.params.video.sid, + rid: bidData.id, + }] + } } } } + } else if (bid.params.video.display == 1) { + bidData.imp[0].banner = { + mimes: bid.params.video.mimes, + w: bid.params.video.playerWidth, + h: bid.params.video.playerHeight, + pos: bid.params.video.position, + }; } - if (isConsentRequired(consentData)) { bidData.regs = { ext: { diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index c7f6af399e78..a626a30eb605 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -9,44 +9,76 @@ Connects to One Video demand source to fetch bids. -# Test Parameters +# Test Parameters for Video ``` var adUnits = [ - { - code: 'video1', - sizes: [640,480], - mediaTypes: { - video: { - context: "instream" - } - }, - bids: [ - { - bidder: 'oneVideo', - params: { - video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1,5], - placement: 123, - sid: , - rewarded: 1 + { + code: 'video1', + sizes: [640,480], + mediaTypes: { + video: { + context: "instream" + } }, - }, - site: { - id: 1, - page: 'http://abhi12345.com', - referrer: 'http://abhi12345.com' + bids: [ + { + bidder: 'oneVideo', + params: { + video: { + playerWidth: 480, + playerHeight: 640, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [1], + position: 1, + delivery: [2], + playbackmethod: [1,5], + sid: , + rewarded: 1 + }, + site: { + id: 1, + page: 'http://abhi12345.com', + referrer: 'http://abhi12345.com' + }, + pubId: 'brxd' + } + } + ] + } +] +``` +# Test Parameters for banner request +``` + var adUnits = [ + { + code: 'video1', + sizes: [640,480], + mediaTypes: { + video: { + context: "instream" + } }, - pubId: 'brxd' - } - } - ] - } - ]; + bids: [ + { + bidder: 'oneVideo', + params: { + video: { + playerWidth: 480, + playerHeight: 640, + mimes: ['video/mp4', 'application/javascript'], + position: 1, + display: 1 + }, + site: { + id: 1, + page: 'http://abhi12345.com', + referrer: 'http://abhi12345.com' + }, + pubId: 'OneMDisplay' + } + } + ] + } +] ``` diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index ce671772dadf..77c257fd7fb0 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -3,6 +3,7 @@ const { registerBidder } = require('../src/adapters/bidderFactory'); const ENDPOINT = 'https://onetag-sys.com/prebid-request'; +const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; const BIDDER_CODE = 'onetag'; const BANNER = 'banner'; @@ -120,8 +121,15 @@ function getPageInfo() { masked: m, wWidth: w.innerWidth, wHeight: w.innerHeight, + oWidth: w.outerWidth, + oHeight: w.outerHeight, sWidth: s.width, sHeight: s.height, + aWidth: s.availWidth, + aHeight: s.availHeight, + sLeft: 'screenLeft' in w ? w.screenLeft : w.screenX, + sTop: 'screenTop' in w ? w.screenTop : w.screenY, + hLength: history.length, date: t.toUTCString(), timeOffset: t.getTimezoneOffset() }; @@ -162,6 +170,27 @@ function requestsToBids(bid) { return toRet; } +function getUserSyncs(syncOptions, serverResponses, gdprConsent) { + const syncs = []; + if (syncOptions.iframeEnabled) { + const rnd = new Date().getTime(); + let params = '?cb=' + rnd; + + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + params += '&gdpr_consent=' + gdprConsent.consentString; + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + } + } + + syncs.push({ + type: 'iframe', + url: USER_SYNC_ENDPOINT + params + }); + } + return syncs; +} + export const spec = { code: BIDDER_CODE, @@ -170,6 +199,7 @@ export const spec = { isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, + getUserSyncs: getUserSyncs }; diff --git a/modules/outconBidAdapter.js b/modules/outconBidAdapter.js index d691e807059b..ba0c11642797 100644 --- a/modules/outconBidAdapter.js +++ b/modules/outconBidAdapter.js @@ -1,18 +1,20 @@ import {registerBidder} from '../src/adapters/bidderFactory'; -import {config} from '../src/config'; const BIDDER_CODE = 'outcon'; + export const spec = { code: BIDDER_CODE, + supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function(bid) { return !!((bid.params.pod || (bid.params.internalId && bid.params.publisher)) && bid.params.env); }, buildRequests: function(validBidRequests) { for (let i = 0; i < validBidRequests.length; i++) { - let par = ''; let url = ''; + let par = ''; if (validBidRequests[i].params.pod != undefined) par = 'get?pod=' + validBidRequests[i].params.pod + '&bidId=' + validBidRequests[i].bidId; else par = 'get?internalId=' + validBidRequests[i].params.internalId + '&publisher=' + validBidRequests[i].params.publisher + '&bidId=' + validBidRequests[i].bidId; + par = par + '&vast=true'; switch (validBidRequests[i].params.env) { case 'test': par = par + '&demo=true'; @@ -42,10 +44,17 @@ export const spec = { creativeId: serverResponse.body.creatives[0].id, currency: serverResponse.body.cur, netRevenue: true, - ttl: config.getConfig('_bidderTimeout'), + ttl: 300, ad: wrapDisplayUrl(serverResponse.body.creatives[0].url, serverResponse.body.type), - vastImpUrl: serverResponse.body.trackingURL + vastImpUrl: serverResponse.body.trackingURL, + mediaType: serverResponse.body.type }; + if (serverResponse.body.type == 'video') { + Object.assign(bidResponse, { + vastUrl: serverResponse.body.vastURL, + ttl: 3600 + }); + } bidResponses.push(bidResponse); return bidResponses; }, @@ -54,6 +63,7 @@ export const spec = { function wrapDisplayUrl(displayUrl, type) { if (type == 'video') return `
`; if (type == 'banner') return `
`; + return null; } registerBidder(spec); diff --git a/modules/playgroundxyzBidAdapter.js b/modules/playgroundxyzBidAdapter.js index 26483f1277aa..3699266ea460 100644 --- a/modules/playgroundxyzBidAdapter.js +++ b/modules/playgroundxyzBidAdapter.js @@ -166,7 +166,7 @@ function isMobile() { } function isConnectedTV() { - return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(global.navigator.userAgent); + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); } registerBidder(spec); diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 872e0bb81d91..93b8dc085f87 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -1,7 +1,6 @@ import Adapter from '../../src/adapter'; import { createBid } from '../../src/bidfactory'; import * as utils from '../../src/utils'; -import { ajax } from '../../src/ajax'; import { STATUS, S2S, EVENTS } from '../../src/constants'; import adapterManager from '../../src/adapterManager'; import { config } from '../../src/config'; @@ -11,6 +10,7 @@ import { isValid } from '../../src/adapters/bidderFactory'; import events from '../../src/events'; import includes from 'core-js/library/fn/array/includes'; import { S2S_VENDORS } from './config.js'; +import { ajax } from '../../src/ajax'; const getConfig = config.getConfig; @@ -148,7 +148,6 @@ function queueSync(bidderCodes, gdprConsent) { } } const jsonPayload = JSON.stringify(payload); - ajax(_s2sConfig.syncEndpoint, (response) => { try { @@ -960,7 +959,11 @@ export function PrebidServer() { if (_s2sConfig && _s2sConfig.syncEndpoint) { let consent = (Array.isArray(bidRequests) && bidRequests.length > 0) ? bidRequests[0].gdprConsent : undefined; - queueSync(_s2sConfig.bidders, consent); + let syncBidders = _s2sConfig.bidders + .map(bidder => adapterManager.aliasRegistry[bidder] || bidder) + .filter((bidder, index, array) => (array.indexOf(bidder) === index)); + + queueSync(syncBidders, consent); } const request = protocolAdapter().buildRequest(s2sBidRequest, bidRequests, validAdUnits); diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 58d8d773dae8..73e6d8cfb523 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -174,8 +174,8 @@ function _parseAdSlot(bid) { utils.logWarn(LOG_WARN_PREFIX + 'AdSlot Error: adSlot not in required format'); return; } - bid.params.width = parseInt(splits[0]); - bid.params.height = parseInt(splits[1]); + bid.params.width = parseInt(splits[0], 10); + bid.params.height = parseInt(splits[1], 10); } else if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(BANNER) && bid.mediaTypes.banner.hasOwnProperty('sizes')) { @@ -441,8 +441,8 @@ function _createBannerRequest(bid) { utils.logWarn(LOG_WARN_PREFIX + 'Error: mediaTypes.banner.size missing for adunit: ' + bid.params.adUnit + '. Ignoring the banner impression in the adunit.'); return bannerObj; } else { - bannerObj.w = parseInt(sizes[0][0]); - bannerObj.h = parseInt(sizes[0][1]); + bannerObj.w = parseInt(sizes[0][0], 10); + bannerObj.h = parseInt(sizes[0][1], 10); sizes = sizes.splice(1, sizes.length - 1); } } else { @@ -482,11 +482,11 @@ function _createVideoRequest(bid) { } // read playersize and assign to h and w. if (utils.isArray(bid.mediaTypes.video.playerSize[0])) { - videoObj.w = parseInt(bid.mediaTypes.video.playerSize[0][0]); - videoObj.h = parseInt(bid.mediaTypes.video.playerSize[0][1]); + videoObj.w = parseInt(bid.mediaTypes.video.playerSize[0][0], 10); + videoObj.h = parseInt(bid.mediaTypes.video.playerSize[0][1], 10); } else if (utils.isNumber(bid.mediaTypes.video.playerSize[0])) { - videoObj.w = parseInt(bid.mediaTypes.video.playerSize[0]); - videoObj.h = parseInt(bid.mediaTypes.video.playerSize[1]); + videoObj.w = parseInt(bid.mediaTypes.video.playerSize[0], 10); + videoObj.h = parseInt(bid.mediaTypes.video.playerSize[1], 10); } if (bid.params.video.hasOwnProperty('skippable')) { videoObj.ext = { @@ -651,6 +651,7 @@ function _handleEids(payload, validBidRequests) { _addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.criteortus.${BIDDER_CODE}.userid`), 'criteortus', 1); _addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.idl_env`), 'liveramp.com', 1); _addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.lipb.lipbid`), 'liveintent.com', 1); + _addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.parrableid`), 'parrable.com', 1); } if (eids.length > 0) { payload.user.eids = eids; @@ -919,6 +920,11 @@ export const spec = { }; } + // coppa compliance + if (config.getConfig('coppa') === true) { + utils.deepSetValue(payload, 'regs.coppa', 1); + } + _handleDealCustomTargetings(payload, dctrArr, validBidRequests); _handleEids(payload, validBidRequests); _blockedIabCategoriesValidation(payload, blockedIabCategories); @@ -1018,6 +1024,11 @@ export const spec = { syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); } + // coppa compliance + if (config.getConfig('coppa') === true) { + syncurl += '&coppa=1'; + } + if (syncOptions.iframeEnabled) { return [{ type: 'iframe', diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index fee247ba31f7..3cdbd5596144 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -114,9 +114,9 @@ function bidResponseAvailable(request, response) { creative_id: idToBidMap[id].crid, creativeId: idToBidMap[id].crid, adId: id, - ttl: DEFAULT_BID_TTL, + ttl: idToBidMap[id].exp || DEFAULT_BID_TTL, netRevenue: DEFAULT_NET_REVENUE, - currency: DEFAULT_CURRENCY + currency: idToBidMap[id].cur || DEFAULT_CURRENCY }; if (idToImpMap[id]['native']) { bid['native'] = nativeResponse(idToImpMap[id], idToBidMap[id]); @@ -135,21 +135,12 @@ function bidResponseAvailable(request, response) { bid.width = idToImpMap[id].banner.w; bid.height = idToImpMap[id].banner.h; } - applyExt(bid, idToBidMap[id]) bids.push(bid); } }); return bids; } -function applyExt(bid, ortbBid) { - if (ortbBid && ortbBid.ext) { - bid.ttl = ortbBid.ext.ttl || bid.ttl; - bid.currency = ortbBid.ext.currency || bid.currency; - bid.netRevenue = ortbBid.ext.netRevenue != null ? ortbBid.ext.netRevenue : bid.netRevenue; - } -} - /** * Produces an OpenRTBImpression from a slot config. */ diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index cc17e2596956..d07b6731cda7 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -5,13 +5,9 @@ import {BANNER, VIDEO} from '../src/mediaTypes'; const DEFAULT_INTEGRATION = 'pbjs_lite'; -function isSecure() { - return location.protocol === 'https:'; -} - -// use protocol relative urls for http or https -export const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.json'; -export const VIDEO_ENDPOINT = '//prebid-server.rubiconproject.com/openrtb2/auction'; +// always use https, regardless of whether or not current page is secure +export const FASTLANE_ENDPOINT = 'https://fastlane.rubiconproject.com/a/api/fastlane.json'; +export const VIDEO_ENDPOINT = 'https://prebid-server.rubiconproject.com/openrtb2/auction'; export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html'; const DIGITRUST_PROP_NAMES = { @@ -153,7 +149,7 @@ export const spec = { imp: [{ exp: 300, id: bidRequest.adUnitCode, - secure: isSecure() || bidRequest.params.secure ? 1 : 0, + secure: 1, ext: { rubicon: bidRequest.params }, @@ -397,7 +393,7 @@ export const spec = { 'size_id': parsedSizes[0], 'alt_size_ids': parsedSizes.slice(1).join(',') || undefined, 'rp_floor': (params.floor = parseFloat(params.floor)) > 0.01 ? params.floor : 0.01, - 'rp_secure': isSecure() ? '1' : '0', + 'rp_secure': '1', 'tk_flint': `${configIntType || DEFAULT_INTEGRATION}_v$prebid.version$`, 'x_source.tid': bidRequest.transactionId, 'p_screen_res': _getScreenResolution(), @@ -410,9 +406,8 @@ export const spec = { }; // add p_pos only if specified and valid - if (params.position === 'atf' || params.position === 'btf') { - data['p_pos'] = params.position; - } + // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value + data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : ''; if ((bidRequest.userId || {}).tdid) { data['tpid_tdid'] = bidRequest.userId.tdid; diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index a0390a981a50..3fb9a83d2597 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -20,8 +20,36 @@ export const spec = { * @param {BidRequest} bid - The bid params to validate. * @return {boolean} True if this is a valid bid, and false otherwise. */ - isBidRequestValid: bid => !!(bid.params && (bid.params.ad_unit || bid.params.placement_id) && (bid.params.sizes || bid.sizes)), + isBidRequestValid: (bid) => { + if (!bid.params) { + return false; + } + if (!bid.params.ad_unit && !bid.params.placement_id) { + return false; + } + + if (!deepAccess(bid, 'mediaTypes.banner') && !deepAccess(bid, 'mediaTypes.video')) { + return false; + } + if (deepAccess(bid, 'mediaTypes.banner')) { // Sonobi does not support multi type bids, favor banner over video + if (!deepAccess(bid, 'mediaTypes.banner.sizes') && !bid.params.sizes) { + // sizes at the banner or params level is required. + return false; + } + } else if (deepAccess(bid, 'mediaTypes.video')) { + if (deepAccess(bid, 'mediaTypes.video.context') === 'outstream' && !bid.params.sizes) { + // bids.params.sizes is required for outstream video adUnits + return false; + } + if (deepAccess(bid, 'mediaTypes.video.context') === 'instream' && !deepAccess(bid, 'mediaTypes.video.playerSize')) { + // playerSize is required for instream adUnits. + return false; + } + } + + return true; + }, /** * Make a server request from the list of BidRequests. * @@ -227,10 +255,21 @@ function _findBidderRequest(bidderRequests, bidId) { } function _validateSize (bid) { + if (deepAccess(bid, 'mediaTypes.video')) { + return ''; // Video bids arent allowed to override sizes via the trinity request + } + if (bid.params.sizes) { return parseSizesInput(bid.params.sizes).join(','); } - return parseSizesInput(bid.sizes).join(','); + if (deepAccess(bid, 'mediaTypes.banner.sizes')) { + return parseSizesInput(deepAccess(bid, 'mediaTypes.banner.sizes')).join(','); + } + + // Handle deprecated sizes definition + if (bid.sizes) { + return parseSizesInput(bid.sizes).join(','); + } } function _validateSlot (bid) { diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index 45a3c9252218..cfa308a8670e 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -227,7 +227,7 @@ export const spec = { // Add schain object if it is present if (bid && bid.schain) { - requestPayload['ext']['source'] = { + requestPayload['source'] = { ext: { schain: bid.schain } diff --git a/modules/stvBidAdapter.js b/modules/stvBidAdapter.js index ac655f2013da..b8fb58c0ab30 100644 --- a/modules/stvBidAdapter.js +++ b/modules/stvBidAdapter.js @@ -28,7 +28,7 @@ export const spec = { const placementId = params.placement; const rnd = Math.floor(Math.random() * 99999999999); - const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); + const referrer = bidderRequest.refererInfo.referer; const bidId = bidRequest.bidId; let endpoint = VADS_ENDPOINT_URL; diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 5ed88384b11a..d49e4cde4808 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -80,6 +80,7 @@ export const tripleliftAdapterSpec = { function _buildPostBody(bidRequests) { let data = {}; + let { schain } = bidRequests[0]; data.imp = bidRequests.map(function(bid, index) { return { id: index, @@ -102,6 +103,11 @@ function _buildPostBody(bidRequests) { }; } + if (schain) { + data.ext = { + schain + } + } return data; } diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 7974f053bbdb..3a7441e73749 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -158,6 +158,28 @@ function parseSizes(bid) { return transformSizes(bid.sizes); } +function getSupplyChain(schain) { + var supplyChain = ''; + if (schain != null && schain.nodes) { + supplyChain = schain.ver + ',' + schain.complete; + for (let i = 0; i < schain.nodes.length; i++) { + supplyChain += '!'; + supplyChain += (schain.nodes[i].asi) ? encodeURIComponent(schain.nodes[i].asi) : ''; + supplyChain += ','; + supplyChain += (schain.nodes[i].sid) ? encodeURIComponent(schain.nodes[i].sid) : ''; + supplyChain += ','; + supplyChain += (schain.nodes[i].hp) ? encodeURIComponent(schain.nodes[i].hp) : ''; + supplyChain += ','; + supplyChain += (schain.nodes[i].rid) ? encodeURIComponent(schain.nodes[i].rid) : ''; + supplyChain += ','; + supplyChain += (schain.nodes[i].name) ? encodeURIComponent(schain.nodes[i].name) : ''; + supplyChain += ','; + supplyChain += (schain.nodes[i].domain) ? encodeURIComponent(schain.nodes[i].domain) : ''; + } + } + return supplyChain; +} + function getRequestData(bid, bidderRequest) { const size = parseSizes(bid); const loc = utils.getTopWindowLocation(); @@ -169,6 +191,7 @@ function getRequestData(bid, bidderRequest) { const videoContext = utils.deepAccess(bid, 'mediaTypes.video.context'); const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video'); const userIdTdid = (bid.userId && bid.userId.tdid) ? bid.userId.tdid : ''; + const supplyChain = getSupplyChain(bid.schain); // general bid data let bidData = { ver: VER, @@ -182,7 +205,8 @@ function getRequestData(bid, bidderRequest) { adid: utils.getBidIdParameter('adid', bid.params), w: size[0], h: size[1], - tdid: userIdTdid + tdid: userIdTdid, + schain: supplyChain }; if (bid.mediaType === 'video' || videoMediaType) { diff --git a/modules/viewdeosDXBidAdapter.js b/modules/viewdeosDXBidAdapter.js new file mode 100644 index 000000000000..a591a28b18d3 --- /dev/null +++ b/modules/viewdeosDXBidAdapter.js @@ -0,0 +1,243 @@ +import * as utils from '../src/utils'; +import {registerBidder} from '../src/adapters/bidderFactory'; +import {VIDEO, BANNER} from '../src/mediaTypes'; +import {Renderer} from '../src/Renderer'; +import findIndex from 'core-js/library/fn/array/find-index'; + +const URL = '//hb.sync.viewdeos.com/auction/'; +const OUTSTREAM_SRC = '//player.sync.viewdeos.com/outstream-unit/2.01/outstream.min.js'; +const BIDDER_CODE = 'viewdeosDX'; +const OUTSTREAM = 'outstream'; +const DISPLAY = 'display'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['viewdeos'], + supportedMediaTypes: [VIDEO, BANNER], + isBidRequestValid: function (bid) { + return !!utils.deepAccess(bid, 'params.aid'); + }, + getUserSyncs: function (syncOptions, serverResponses) { + const syncs = []; + + function addSyncs(bid) { + const uris = bid.cookieURLs; + const types = bid.cookieURLSTypes || []; + + if (Array.isArray(uris)) { + uris.forEach((uri, i) => { + const type = types[i] || 'image'; + + if ((!syncOptions.pixelEnabled && type === 'image') || + (!syncOptions.iframeEnabled && type === 'iframe')) { + return; + } + + syncs.push({ + type: type, + url: uri + }) + }) + } + } + + if (syncOptions.pixelEnabled || syncOptions.iframeEnabled) { + utils.isArray(serverResponses) && serverResponses.forEach((response) => { + if (response.body) { + if (utils.isArray(response.body)) { + response.body.forEach(b => { + addSyncs(b); + }) + } else { + addSyncs(response.body) + } + } + }) + } + return syncs; + }, + /** + * Make a server request from the list of BidRequests + * @param bidRequests + * @param bidderRequest + */ + buildRequests: function (bidRequests, bidderRequest) { + return { + data: bidToTag(bidRequests, bidderRequest), + bidderRequest, + method: 'GET', + url: URL + }; + }, + + /** + * Unpack the response from the server into a list of bids + * @param serverResponse + * @param bidderRequest + * @return {Bid[]} An array of bids which were nested inside the server + */ + interpretResponse: function (serverResponse, {bidderRequest}) { + serverResponse = serverResponse.body; + let bids = []; + + if (!utils.isArray(serverResponse)) { + return parseRTBResponse(serverResponse, bidderRequest); + } + + serverResponse.forEach(serverBidResponse => { + bids = utils.flatten(bids, parseRTBResponse(serverBidResponse, bidderRequest)); + }); + + return bids; + } +}; + +function parseRTBResponse(serverResponse, bidderRequest) { + const isInvalidValidResp = !serverResponse || !utils.isArray(serverResponse.bids); + + const bids = []; + + if (isInvalidValidResp) { + const extMessage = serverResponse && serverResponse.ext && serverResponse.ext.message ? `: ${serverResponse.ext.message}` : ''; + const errorMessage = `in response for ${bidderRequest.bidderCode} adapter ${extMessage}`; + + utils.logError(errorMessage); + + return bids; + } + + serverResponse.bids.forEach(serverBid => { + const requestId = findIndex(bidderRequest.bids, (bidRequest) => { + return bidRequest.bidId === serverBid.requestId; + }); + + if (serverBid.cpm !== 0 && requestId !== -1) { + const bid = createBid(serverBid, getMediaType(bidderRequest.bids[requestId])); + + bids.push(bid); + } + }); + + return bids; +} + +function bidToTag(bidRequests, bidderRequest) { + const tag = { + domain: utils.deepAccess(bidderRequest, 'refererInfo.referer') + }; + + if (utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies')) { + tag.gdpr = 1; + tag.gdpr_consent = utils.deepAccess(bidderRequest, 'gdprConsent.consentString'); + } + + for (let i = 0, length = bidRequests.length; i < length; i++) { + Object.assign(tag, prepareRTBRequestParams(i, bidRequests[i])); + } + + return tag; +} + +/** + * Parse mediaType + * @param _index {number} + * @param bid {object} + * @returns {object} + */ +function prepareRTBRequestParams(_index, bid) { + const mediaType = utils.deepAccess(bid, 'mediaTypes.video') ? VIDEO : DISPLAY; + const index = !_index ? '' : `${_index + 1}`; + const sizes = bid.sizes ? bid.sizes : (mediaType === VIDEO ? utils.deepAccess(bid, 'mediaTypes.video.playerSize') : utils.deepAccess(bid, 'mediaTypes.banner.sizes')); + return { + ['callbackId' + index]: bid.bidId, + ['aid' + index]: bid.params.aid, + ['ad_type' + index]: mediaType, + ['sizes' + index]: utils.parseSizesInput(sizes).join() + }; +} + +/** + * Prepare all parameters for request + * @param bidderRequest {object} + * @returns {object} + */ +function getMediaType(bidderRequest) { + const videoMediaType = utils.deepAccess(bidderRequest, 'mediaTypes.video'); + const context = utils.deepAccess(bidderRequest, 'mediaTypes.video.context'); + + return !videoMediaType ? DISPLAY : context === OUTSTREAM ? OUTSTREAM : VIDEO; +} + +/** + * Configure new bid by response + * @param bidResponse {object} + * @param mediaType {Object} + * @returns {object} + */ +function createBid(bidResponse, mediaType) { + const bid = { + requestId: bidResponse.requestId, + creativeId: bidResponse.cmpId, + height: bidResponse.height, + currency: bidResponse.cur, + width: bidResponse.width, + cpm: bidResponse.cpm, + netRevenue: true, + mediaType, + ttl: 3600 + }; + + if (mediaType === DISPLAY) { + return Object.assign(bid, { + ad: bidResponse.ad + }); + } + + Object.assign(bid, { + vastUrl: bidResponse.vastUrl + }); + + if (mediaType === OUTSTREAM) { + Object.assign(bid, { + mediaType: 'video', + adResponse: bidResponse, + renderer: newRenderer(bidResponse.requestId) + }); + } + + return bid; +} + +/** + * Create renderer + * @param requestId + * @returns {*} + */ +function newRenderer(requestId) { + const renderer = Renderer.install({ + id: requestId, + url: OUTSTREAM_SRC, + loaded: false + }); + + renderer.setRender(outstreamRender); + + return renderer; +} + +/** + * Initialise outstream + * @param bid + */ +function outstreamRender(bid) { + bid.renderer.push(() => { + window.VOutstreamAPI.initOutstreams([{ + width: bid.width, + height: bid.height, + vastUrl: bid.vastUrl, + elId: bid.adUnitCode + }]); + }); +} + +registerBidder(spec); diff --git a/modules/viewdeosDXBidAdapter.md b/modules/viewdeosDXBidAdapter.md new file mode 100644 index 000000000000..d9ede8cc3123 --- /dev/null +++ b/modules/viewdeosDXBidAdapter.md @@ -0,0 +1,60 @@ +# Overview + +**Module Name**: Viewdeos DX Bidder Adapter +**Module Type**: Bidder Adapter + +# Description + +Get access to multiple demand partners across Viewdeos and maximize your yield with Viewdeos header bidding adapter. + +# Test Parameters +``` + var adUnits = [ + + // Video instream adUnit + { + code: 'div-test-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + context: 'instream' + } + }, + bids: [{ + bidder: 'viewdeosDX', + params: { + aid: 331133 + } + }] + }, + + // Video outstream adUnit + { + code: 'outstream-test-div', + sizes: [[640, 480]], + mediaTypes: { + video: { + context: 'outstream' + } + }, + bids: [{ + bidder: 'viewdeosDX', + params: { + aid: 331133 + } + }] + }, + + // Banner adUnit + { + code: 'div-test-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'viewdeosDX', + params: { + aid: 350975 + } + }] + } + ]; +``` diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 2f9ec73c569f..15334e46e31f 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -15,8 +15,11 @@ const LOG_ERROR_MESS = { emptySeatbid: 'Seatbid array from response has an empty item', emptyResponse: 'Response is empty', hasEmptySeatbidArray: 'Response has empty seatbid array', - hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' + hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ', + notAllowedCurrency: 'Currency is not supported - ', + currencyMismatch: 'Currency from the request is not match currency from the response - ' }; +const currencyWhiteList = ['EUR', 'USD', 'GBP', 'PLN']; export const spec = { code: BIDDER_CODE, isBidRequestValid: function(bid) { @@ -34,6 +37,11 @@ export const spec = { DEFAULT_CUR; let reqId; + if (currencyWhiteList.indexOf(currency) === -1) { + utils.logError(LOG_ERROR_MESS.notAllowedCurrency + currency); + return; + } + bids.forEach(bid => { reqId = bid.bidderRequestId; const {params: {uid}, adUnitCode} = bid; @@ -78,7 +86,7 @@ export const spec = { if (bidderRequest) { if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = encodeURIComponent(bidderRequest.refererInfo.referer); + payload.u = bidderRequest.refererInfo.referer; } if (bidderRequest.gdprConsent) { if (bidderRequest.gdprConsent.consentString) { @@ -158,43 +166,48 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses, bidsWithout if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { + const reqCurrency = currency || DEFAULT_CUR; const awaitingBids = bidsMap[serverBid.auid]; if (awaitingBids) { - const sizeId = bidsWithoutSizeMatching ? `${serverBid.w}x${serverBid.h}` : Object.keys(awaitingBids)[0]; - if (awaitingBids[sizeId]) { - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - bidResponses.push({ - requestId: bid.bidId, - bidderCode: spec.code, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, - currency: currency || DEFAULT_CUR, - netRevenue: true, - ttl: TIME_TO_LIVE, - ad: serverBid.adm, - dealId: serverBid.dealid - }); - - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } + if (serverBid.cur && serverBid.cur !== reqCurrency) { + errorMessage = LOG_ERROR_MESS.currencyMismatch + reqCurrency + ' - ' + serverBid.cur; + } else { + const sizeId = bidsWithoutSizeMatching ? `${serverBid.w}x${serverBid.h}` : Object.keys(awaitingBids)[0]; + if (awaitingBids[sizeId]) { + const slot = awaitingBids[sizeId][0]; + + const bid = slot.bids.shift(); + bidResponses.push({ + requestId: bid.bidId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, + currency: reqCurrency, + netRevenue: true, + ttl: TIME_TO_LIVE, + ad: serverBid.adm, + dealId: serverBid.dealid }); + + if (!slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); + } + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); + } + } else { + bidsWithoutSizeMatching && bidsWithoutSizeMatching.push(serverBid); } - } else { - bidsWithoutSizeMatching && bidsWithoutSizeMatching.push(serverBid); } } else { errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; diff --git a/modules/vrtcalBidAdapter.js b/modules/vrtcalBidAdapter.js new file mode 100644 index 000000000000..139f985ed67f --- /dev/null +++ b/modules/vrtcalBidAdapter.js @@ -0,0 +1,88 @@ +import {registerBidder} from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; +import {ajax} from '../src/ajax'; + +export const spec = { + code: 'vrtcal', + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + if (bid.bidId == '' || bid.auctionId == '') { return false; } else { return true; }// No extras params required + }, + buildRequests: function (bidRequests) { + const requests = bidRequests.map(function (bid) { + const params = { + + prebidJS: 1, + prebidAdUnitCode: bid.adUnitCode, + id: bid.bidId, + imp: [{ + id: '1', + banner: { + }, + bidfloor: 0.75 + }], + site: { + id: 'VRTCAL_FILLED', + name: 'VRTCAL_FILLED', + cat: ['VRTCAL_FILLED'], + domain: decodeURIComponent(window.location.href).replace('https://', '').replace('http://', '').split('/')[0] + + }, + device: { + ua: 'VRTCAL_FILLED', + ip: 'VRTCAL_FILLED' + } + }; + + if (typeof (bid.mediaTypes) !== 'undefined' && typeof (bid.mediaTypes.banner) !== 'undefined' && typeof (bid.mediaTypes.banner.sizes) !== 'undefined') { + params.imp[0].banner.w = bid.mediaTypes.banner.sizes[0][0]; + params.imp[0].banner.h = bid.mediaTypes.banner.sizes[0][1]; + } else { + params.imp[0].banner.w = bid.sizes[0][0]; + params.imp[0].banner.h = bid.sizes[0][1]; + } + + return {method: 'POST', url: 'https://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804', data: JSON.stringify(params), options: {withCredentials: false, crossOrigin: true}} + }); + + return requests; + }, + interpretResponse: function (serverResponse, bidRequest) { + if (!serverResponse || !serverResponse.body) { + return []; + } + + const bidResponses = []; + + var response = serverResponse.body; + + if (response) { + const bidResponse = { + requestId: response.id, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + creativeId: response.seatbid[0].bid[0].crid, + currency: 'USD', + netRevenue: true, + ttl: 900, + ad: response.seatbid[0].bid[0].adm, + nurl: response.seatbid[0].bid[0].nurl + }; + + bidResponses.push(bidResponse); + } + return bidResponses; + }, + onBidWon: function(bid) { + if (!bid.nurl) { return false; } + const winUrl = bid.nurl.replace( + /\$\{AUCTION_PRICE\}/, + bid.cpm + ); + ajax(winUrl, null); + return true; + } +}; + +registerBidder(spec); diff --git a/modules/vrtcalBidAdapter.md b/modules/vrtcalBidAdapter.md new file mode 100644 index 000000000000..8d2809f7d5de --- /dev/null +++ b/modules/vrtcalBidAdapter.md @@ -0,0 +1,30 @@ +# Overview + +Module Name: Vrtcal Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@vrtcal.com + +# Description + +You can use this adapter to get a bid from vrtcal.com. + + +# Test Parameters +``` + var adUnits = [ + { + code: "vrtcal-test-adunit", + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: "vrtcal" + } + ] + } + ]; +``` +#Vrtcal requires no extra params passed, thus no params struct included diff --git a/modules/vubleBidAdapter.js b/modules/vubleBidAdapter.js index 80d62d21842c..63f817358de9 100644 --- a/modules/vubleBidAdapter.js +++ b/modules/vubleBidAdapter.js @@ -23,7 +23,7 @@ const outstreamRender = bid => { showBigPlayButton: false, showProgressBar: 'bar', showVolume: false, - allowFullscreen: false, + allowFullscreen: true, skippable: false, } }); diff --git a/package.json b/package.json index 0200db4eace7..01aaa244e1ff 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.37.0-pre", + "version": "2.39.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { diff --git a/src/adloader.js b/src/adloader.js index b422c802393b..c45dacd8af08 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -4,6 +4,7 @@ import * as utils from './utils'; const _requestCache = {}; const _vendorWhitelist = [ 'criteo', + 'adagio' ] /** diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index e98b4600d906..5507b9c1cbc9 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -579,11 +579,11 @@ describe('33acrossBidAdapter:', function () { syncs = [ { type: 'iframe', - url: 'https://de.tynt.com/deb/v2?m=xch&rt=html&id=id1' + url: 'https://ssc-cms.33across.com/ps/?m=xch&rt=html&ru=deb&id=id1' }, { type: 'iframe', - url: 'https://de.tynt.com/deb/v2?m=xch&rt=html&id=id2' + url: 'https://ssc-cms.33across.com/ps/?m=xch&rt=html&ru=deb&id=id2' }, ]; bidRequests = [ diff --git a/test/spec/modules/7xbidBidAdapter_spec.js b/test/spec/modules/7xbidBidAdapter_spec.js new file mode 100644 index 000000000000..4f32e96ce93c --- /dev/null +++ b/test/spec/modules/7xbidBidAdapter_spec.js @@ -0,0 +1,161 @@ +import {expect} from 'chai'; +import {spec, _getUrlVars} from 'modules/7xbidBidAdapter'; +import * as utils from 'src/utils'; +import {config} from 'src/config'; + +const BASE_URI = '//bidder.7xbid.com/api/v1/prebid/banner' +const NATIVE_BASE_URI = '//bidder.7xbid.com/api/v1/prebid/native' + +describe('7xbid adapter', function() { + let bidRequests; + let nativeBidRequests; + + beforeEach(function() { + bidRequests = [ + { + bidder: '7xbid', + params: { + placementId: 1425292, + currency: 'USD' + } + } + ] + + nativeBidRequests = [ + { + bidder: '7xbid', + params: { + placementId: 1429695, + currency: 'USD' + }, + nativeParams: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + } + } + } + ] + }) + describe('isBidRequestValid', function () { + it('valid bid case', function () { + let validBid = { + bidder: '7xbid', + params: { + placementId: 1425292, + currency: 'USD' + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('invalid bid case: placementId is not passed', function() { + let validBid = { + bidder: '7xbid', + params: { + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }) + + it('invalid bid case: currency is not support', function() { + let validBid = { + bidder: '7xbid', + params: { + placementId: 1108295, + currency: 'AUD' + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }) + }) + + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(BASE_URI); + expect(request.method).to.equal('GET'); + }); + + it('sends native bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(nativeBidRequests)[0]; + expect(request.url).to.equal(NATIVE_BASE_URI); + expect(request.method).to.equal('GET'); + }); + + it('buildRequests function should not modify original bidRequests object', function () { + let originalBidRequests = utils.deepClone(bidRequests); + let request = spec.buildRequests(bidRequests); + expect(bidRequests).to.deep.equal(originalBidRequests); + }); + + it('buildRequests function should not modify original nativeBidRequests object', function () { + let originalBidRequests = utils.deepClone(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests); + expect(nativeBidRequests).to.deep.equal(originalBidRequests); + }); + + it('Request params check', function() { + let request = spec.buildRequests(bidRequests)[0]; + const data = _getUrlVars(request.data) + expect(parseInt(data.placementid)).to.exist.and.to.equal(bidRequests[0].params.placementId); + expect(data.cur).to.exist.and.to.equal(bidRequests[0].params.currency); + }) + + it('Native request params check', function() { + let request = spec.buildRequests(nativeBidRequests)[0]; + const data = _getUrlVars(request.data) + expect(parseInt(data.placementid)).to.exist.and.to.equal(nativeBidRequests[0].params.placementId); + expect(data.cur).to.exist.and.to.equal(nativeBidRequests[0].params.currency); + }) + }) + + describe('interpretResponse', function () { + let response = { + 1425292: + { + 'creativeId': '', + 'cur': 'USD', + 'price': 0.0920, + 'width': 300, + 'height': 250, + 'requestid': '2e42361a6172bf', + 'adm': '' + } + } + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '2e42361a6172bf', + 'cpm': 0.0920, + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': '', + 'ttl': 700, + 'ad': '' + } + ]; + let request = spec.buildRequests(bidRequests)[0]; + let result = spec.interpretResponse({body: response}, request); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].cpm).to.not.equal(null); + expect(result[0].creativeId).to.not.equal(null); + expect(result[0].ad).to.not.equal(null); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + }); + }) +}) diff --git a/test/spec/modules/ablidaBidAdapter_spec.js b/test/spec/modules/ablidaBidAdapter_spec.js new file mode 100644 index 000000000000..8e0424aee23a --- /dev/null +++ b/test/spec/modules/ablidaBidAdapter_spec.js @@ -0,0 +1,102 @@ +import {assert, expect} from 'chai'; +import {spec} from 'modules/ablidaBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; + +describe('ablidaBidAdapter', function () { + const adapter = newBidder(spec); + describe('isBidRequestValid', function () { + let bid = { + bidder: 'ablida', + params: { + placementId: 123 + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250] + ], + bidId: '1234asdf1234', + bidderRequestId: '1234asdf1234asdf', + auctionId: '69e8fef8-5105-4a99-b011-d5669f3bc7f0' + }; + it('should return true where required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + describe('buildRequests', function () { + let bidRequests = [ + { + bidder: 'ablida', + params: { + placementId: 123 + }, + sizes: [ + [300, 250] + ], + adUnitCode: 'adunit-code', + bidId: '23beaa6af6cdde', + bidderRequestId: '14d2939272a26a', + auctionId: '69e8fef8-5105-4a99-b011-d5669f3bc7f0', + } + ]; + + let bidderRequests = { + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'http://example.com', + stack: ['http://example.com'] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequests); + it('sends bid request via POST', function () { + expect(request[0].method).to.equal('POST'); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = { + method: 'POST', + url: ENDPOINT_URL, + data: { + placementId: 'testPlacementId', + width: 300, + height: 200, + bidId: '2b8c4de0116e54', + jaySupported: true, + device: 'desktop', + referer: 'www.example.com' + } + }; + let serverResponse = { + body: [{ + requestId: '2b8c4de0116e54', + cpm: 1.00, + width: 300, + height: 250, + creativeId: '2b8c4de0116e54', + currency: 'EUR', + netRevenue: true, + ttl: 3000, + ad: '' + }] + }; + it('should get the correct bid response', function () { + let expectedResponse = [{ + requestId: '2b8c4de0116e54', + cpm: 1.00, + width: 300, + height: 250, + creativeId: '2b8c4de0116e54', + currency: 'EUR', + netRevenue: true, + ttl: 3000, + ad: '' + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + }); +}); diff --git a/test/spec/modules/adagioAnalyticsAdapter_spec.js b/test/spec/modules/adagioAnalyticsAdapter_spec.js new file mode 100644 index 000000000000..48bb33619a02 --- /dev/null +++ b/test/spec/modules/adagioAnalyticsAdapter_spec.js @@ -0,0 +1,183 @@ +import adagioAnalyticsAdapter from 'modules/adagioAnalyticsAdapter'; +import { expect } from 'chai'; +import * as utils from 'src/utils'; + +let adapterManager = require('src/adapterManager').default; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe.skip('adagio analytics adapter', () => { + let xhr; + let requests; + let sandbox + + beforeEach(() => { + sandbox = sinon.createSandbox(); + + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + sandbox.stub(events, 'getEvents').returns([]); + + adapterManager.registerAnalyticsAdapter({ + code: 'adagio', + adapter: adagioAnalyticsAdapter + }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('track', () => { + beforeEach(() => { + adapterManager.enableAnalytics({ + provider: 'adagio' + }); + }); + + afterEach(() => { + adagioAnalyticsAdapter.disableAnalytics(); + }); + + it('builds and sends auction data', () => { + const w = utils.getWindowTop(); + + let bidRequest = { + bids: [{ + adUnitCode: 'div-1', + params: { + features: { + siteId: '2', + placement: 'pave_top', + pagetype: 'article', + category: 'IAB12,IAB12-2', + device: '2', + } + } + }, { + adUnitCode: 'div-2', + params: { + features: { + siteId: '2', + placement: 'ban_top', + pagetype: 'article', + category: 'IAB12,IAB12-2', + device: '2', + } + }, + }], + }; + let bidResponse = { + bidderCode: 'adagio', + width: 300, + height: 250, + statusMessage: 'Bid available', + cpm: 6.2189757658226075, + currency: '', + netRevenue: false, + adUnitCode: 'div-1', + timeToRespond: 132, + }; + + // Step 1: Send bid requested event + events.emit(constants.EVENTS.BID_REQUESTED, bidRequest); + + // Step 2: Send bid response event + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + + // Step 3: Send auction end event + events.emit(constants.EVENTS.AUCTION_END, {}); + + expect(w.ADAGIO.queue).length(3); + + let o = w.ADAGIO.queue.shift(); + expect(o).to.not.be.undefined; + expect(o.action).to.equal('pb-analytics-event'); + expect(o.ts).to.not.be.undefined; + expect(o.data).to.not.be.undefined; + expect(o.data).to.deep.equal({eventName: constants.EVENTS.BID_REQUESTED, args: bidRequest}); + + o = w.ADAGIO.queue.shift(); + expect(o).to.not.be.undefined; + expect(o.action).to.equal('pb-analytics-event'); + expect(o.ts).to.not.be.undefined; + expect(o.data).to.not.be.undefined; + expect(o.data).to.deep.equal({eventName: constants.EVENTS.BID_RESPONSE, args: bidResponse}); + + o = w.ADAGIO.queue.shift(); + expect(o).to.not.be.undefined; + expect(o.action).to.equal('pb-analytics-event'); + expect(o.ts).to.not.be.undefined; + expect(o.data).to.not.be.undefined; + expect(o.data).to.deep.equal({eventName: constants.EVENTS.AUCTION_END, args: {}}); + }); + }); + + describe('no track', () => { + beforeEach(() => { + sandbox.stub(utils, 'getWindowTop').throws(); + + adapterManager.enableAnalytics({ + provider: 'adagio' + }); + }); + + afterEach(() => { + adagioAnalyticsAdapter.disableAnalytics(); + sandbox.restore(); + }); + + it('builds and sends auction data', () => { + let bidRequest = { + bids: [{ + adUnitCode: 'div-1', + params: { + features: { + siteId: '2', + placement: 'pave_top', + pagetype: 'article', + category: 'IAB12,IAB12-2', + device: '2', + } + } + }, { + adUnitCode: 'div-2', + params: { + features: { + siteId: '2', + placement: 'ban_top', + pagetype: 'article', + category: 'IAB12,IAB12-2', + device: '2', + } + }, + }], + }; + let bidResponse = { + bidderCode: 'adagio', + width: 300, + height: 250, + statusMessage: 'Bid available', + cpm: 6.2189757658226075, + currency: '', + netRevenue: false, + adUnitCode: 'div-1', + timeToRespond: 132, + }; + + // Step 1: Send bid requested event + events.emit(constants.EVENTS.BID_REQUESTED, bidRequest); + + // Step 2: Send bid response event + events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); + + // Step 3: Send auction end event + events.emit(constants.EVENTS.AUCTION_END, {}); + + utils.getWindowTop.restore(); + + expect(utils.getWindowTop().ADAGIO.queue).length(0); + }); + }); +}); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 7437b45b6c10..6c804418d039 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1,10 +1,22 @@ import { expect } from 'chai'; +import { getAdagioScript, spec } from 'modules/adagioBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; -import { spec } from 'modules/adagioBidAdapter'; +import * as utils from 'src/utils'; describe('adagioAdapter', () => { + let utilsMock; const adapter = newBidder(spec); const ENDPOINT = 'https://mp.4dex.io/prebid'; + const VERSION = '2.0.0'; + + beforeEach(function() { + localStorage.removeItem('adagioScript'); + utilsMock = sinon.mock(utils); + }); + + afterEach(function() { + utilsMock.restore(); + }); describe('inherited functions', () => { it('exists and is a function', () => { @@ -13,12 +25,40 @@ describe('adagioAdapter', () => { }); describe('isBidRequestValid', () => { + let sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + let element = { + x: 0, + y: 0, + width: 200, + height: 300, + getBoundingClientRect: () => { + return { + width: element.width, + height: element.height, + left: element.x, + top: element.y, + right: element.x + element.width, + bottom: element.y + element.height + }; + } + }; + sandbox.stub(document, 'getElementById').withArgs('banner-atf').returns(element); + }); + + afterEach(function () { + sandbox.restore(); + }); + let bid = { 'bidder': 'adagio', 'params': { - siteId: '123', - placementId: 4, - categories: ['IAB12', 'IAB12-2'] + organizationId: '0', + placement: 'PAVE_ATF', + site: 'SITE-NAME', + pagetype: 'ARTICLE', + adUnitElementId: 'banner-atf' }, 'adUnitCode': 'adunit-code', 'sizes': [[300, 250], [300, 600]], @@ -27,33 +67,168 @@ describe('adagioAdapter', () => { 'auctionId': 'lel4fhp239i9km', }; + let bidWithMediaTypes = { + 'bidder': 'adagio', + 'params': { + organizationId: '0', + placement: 'PAVE_ATF', + site: 'SITE-NAME', + pagetype: 'ARTICLE', + adUnitElementId: 'banner-atf' + }, + 'adUnitCode': 'adunit-code-2', + 'mediaTypes': { + banner: { + sizes: [[300, 250]], + } + }, + sizes: [[300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + } + it('should return true when required params found', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(1); + }) + + it('should compute a printNumber for the new bid request on same adUnitCode and same pageviewId', () => { + spec.isBidRequestValid(bid); + expect(window.top.ADAGIO.adUnits).ok; + expect(window.top.ADAGIO.adUnits['adunit-code']).ok; + expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(2); + + spec.isBidRequestValid(bid); + expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(3); + + window.top.ADAGIO.pageviewId = 123; + spec.isBidRequestValid(bid); + expect(window.top.ADAGIO.adUnits['adunit-code'].printNumber).to.equal(1); + }); + + it('should return false when organization params is not passed', () => { + let bidTest = Object.assign({}, bid); + delete bidTest.params.organizationId; + expect(spec.isBidRequestValid(bidTest)).to.equal(false); }); it('should return false when site params is not passed', () => { let bidTest = Object.assign({}, bid); - delete bidTest.params.siteId; + delete bidTest.params.site; expect(spec.isBidRequestValid(bidTest)).to.equal(false); }); it('should return false when placement params is not passed', () => { let bidTest = Object.assign({}, bid); - delete bidTest.params.placementId; + delete bidTest.params.placement; expect(spec.isBidRequestValid(bidTest)).to.equal(false); }); + + it('should return false when adUnit element id params is not passed', () => { + let bidTest = Object.assign({}, bid); + delete bidTest.params.adUnitElementId; + expect(spec.isBidRequestValid(bidTest)).to.equal(false); + }); + + it('should return false if not in the window.top', () => { + sandbox.stub(utils, 'getWindowTop').throws(); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should expose ADAGIO.pbjsAdUnits in window', () => { + spec.isBidRequestValid(bidWithMediaTypes); + spec.isBidRequestValid(bid); + expect(window.top.ADAGIO.pbjsAdUnits).ok; + expect(window.top.ADAGIO.pbjsAdUnits).to.have.lengthOf(2); + const adUnitWithMediaTypeSizes = window.top.ADAGIO.pbjsAdUnits.filter((aU) => aU.code === 'adunit-code-2')[0]; + const adUnitWithSizes = window.top.ADAGIO.pbjsAdUnits.filter((aU) => aU.code === 'adunit-code')[0]; + expect(adUnitWithMediaTypeSizes.sizes).to.eql([[300, 250]]); + expect(adUnitWithSizes.sizes).to.eql([[300, 250], [300, 600]]); + }); }); describe('buildRequests', () => { + const sandbox = sinon.createSandbox(); + + const banner300x250 = { + x: 0, + y: 0, + width: 300, + height: 250, + getBoundingClientRect: () => { + return { + width: banner300x250.width, + height: banner300x250.height, + left: banner300x250.x, + top: banner300x250.y, + right: banner300x250.x + banner300x250.width, + bottom: banner300x250.y + banner300x250.height + }; + }, + }; + + const banner300x600 = { + x: 0, + y: 0, + width: 300, + height: 600, + getBoundingClientRect: () => { + return { + width: banner300x600.width, + height: banner300x600.height, + left: banner300x600.x, + top: banner300x600.y, + right: banner300x600.x + banner300x600.width, + bottom: banner300x600.y + banner300x600.height + }; + }, + }; + + const computedStyleBlock = { + display: 'block' + }; + + const computedStyleNone = { + display: 'none' + }; + + const stubs = { + topGetElementById: undefined, + topGetComputedStyle: undefined + } + + top.ADAGIO = top.ADAGIO || {}; + top.ADAGIO.adUnits = top.ADAGIO.adUnits || {}; + top.ADAGIO.pbjsAdUnits = top.ADAGIO.pbjsAdUnits || []; + + beforeEach(function () { + stubs.topGetElementById = sandbox.stub(top.document, 'getElementById'); + stubs.topGetComputedStyle = sandbox.stub(top, 'getComputedStyle'); + + stubs.topGetElementById.withArgs('banner-atf-123').returns(banner300x250); + stubs.topGetElementById.withArgs('banner-atf-456').returns(banner300x600); + stubs.topGetElementById.withArgs('does-not-exist').returns(null); + stubs.topGetComputedStyle.returns(computedStyleBlock); + }); + + afterEach(function () { + sandbox.restore(); + }); + + after(function() { + sandbox.reset(); + }) + let bidRequests = [ - // siteId 123 { 'bidder': 'adagio', 'params': { - siteId: '123', - placementId: 4, - pagetypeId: '232', - categories: ['IAB12'] + organizationId: '123', + site: 'ADAGIO-123', + placement: 'PAVE_ATF-123', + pagetype: 'ARTICLE', + adUnitElementId: 'banner-atf-123' }, 'adUnitCode': 'adunit-code1', 'sizes': [[300, 250], [300, 600]], @@ -64,10 +239,11 @@ describe('adagioAdapter', () => { { 'bidder': 'adagio', 'params': { - siteId: '123', - placementId: 3, - pagetypeId: '232', - categories: ['IAB12'] + organizationId: '123', + site: 'ADAGIO-123', + placement: 'PAVE_ATF-123', + pagetype: 'ARTICLE', + adUnitElementId: 'banner-atf-123' }, 'adUnitCode': 'adunit-code2', 'sizes': [[300, 250], [300, 600]], @@ -75,14 +251,38 @@ describe('adagioAdapter', () => { 'bidderRequestId': '8vfscuixrovn8i', 'auctionId': 'lel4fhp239i9km', }, - // siteId 456 { 'bidder': 'adagio', 'params': { - siteId: '456', - placementId: 4, - pagetypeId: '232', - categories: ['IAB12'] + organizationId: '456', + site: 'ADAGIO-456', + placement: 'PAVE_ATF-456', + pagetype: 'ARTICLE', + adUnitElementId: 'banner-atf-456' + }, + 'adUnitCode': 'adunit-code3', + 'mediaTypes': { + banner: { + sizes: [[300, 250]] + } + }, + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c180kg4267tyqz', + 'bidderRequestId': '8vfscuixrovn8i', + 'auctionId': 'lel4fhp239i9km', + } + ]; + + const bidRequestsWithPostBid = [ + { + 'bidder': 'adagio', + 'params': { + organizationId: '456', + site: 'ADAGIO-456', + placement: 'PAVE_ATF-456', + pagetype: 'ARTICLE', + adUnitElementId: 'banner-atf-456', + postBid: true }, 'adUnitCode': 'adunit-code3', 'sizes': [[300, 250], [300, 600]], @@ -102,36 +302,120 @@ describe('adagioAdapter', () => { consentString: consentString, gdprApplies: true, allowAuctionWithoutConsent: true + }, + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'http://test.io/index.html?pbjs_debug=true' } }; it('groups requests by siteId', () => { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests).to.have.lengthOf(2); - expect(requests[0].data.siteId).to.equal('123'); + expect(requests[0].data.organizationId).to.equal('123'); expect(requests[0].data.adUnits).to.have.lengthOf(2); - expect(requests[1].data.siteId).to.equal('456'); + expect(requests[1].data.organizationId).to.equal('456'); expect(requests[1].data.adUnits).to.have.lengthOf(1); }); it('sends bid request to ENDPOINT_PB via POST', () => { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests).to.have.lengthOf(2); const request = requests[0]; expect(request.method).to.equal('POST'); expect(request.url).to.equal(ENDPOINT); + expect(request.data.prebidVersion).to.equal('$prebid.version$'); }); - it('features params must be an empty object if featurejs is not loaded', () => { - const requests = spec.buildRequests(bidRequests); - expect(requests).to.have.lengthOf(2); + it('features params must be empty if param adUnitElementId is not found', () => { + const requests = spec.buildRequests([Object.assign({}, bidRequests[0], {params: {adUnitElementId: 'does-not-exist'}})], bidderRequest); const request = requests[0]; - const expected = {}; - expect(request.data.adUnits[0].params.features).to.deep.equal(expected); + const expected = {} + expect(request.data.adUnits[0].features).to.deep.equal(expected); + }); + + it('features params "adunit_position" should be computed even if DOM element is display:none', () => { + stubs.topGetComputedStyle.returns(computedStyleNone); + const requests = spec.buildRequests([Object.assign({}, bidRequests[0])], bidderRequest); + let request = requests[0]; + expect(request.data.adUnits[0].features).to.exist; + expect(request.data.adUnits[0].features.adunit_position).to.equal('0x0'); + }); + + it('features params "viewport" should be computed even if window.innerWidth is not supported', () => { + sandbox.stub(top, 'innerWidth').value(undefined); + const requests = spec.buildRequests([Object.assign({}, bidRequests[0])], bidderRequest); + let request = requests[0]; + expect(request.data.adUnits[0].features).to.exist; + expect(request.data.adUnits[0].features.viewport_dimensions).to.match(/^[\d]+x[\d]+$/); + }); + + it('AdUnit requested should have the correct sizes array depending on the config', () => { + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[1].data.adUnits[0]).to.have.property('mediaTypes'); + }); + + it('features params must be an object if featurejs is loaded', () => { + let requests = spec.buildRequests(bidRequests, bidderRequest); + let request = requests[0]; + expect(request.data.adUnits[0].features).to.exist; + }); + + it('outerAdUnitElementId must be added when PostBid param has been set', () => { + top.ADAGIO = top.ADAGIO || {}; + top.ADAGIO.pbjsAdUnits = []; + + top.ADAGIO.pbjsAdUnits.push({ + code: bidRequestsWithPostBid[0].adUnitCode, + sizes: bidRequestsWithPostBid[0].sizes, + bids: [{ + bidder: bidRequestsWithPostBid[0].bidder, + params: bidRequestsWithPostBid[0].params + }] + }); + let requests = spec.buildRequests(bidRequestsWithPostBid, bidderRequest); + let request = requests[0]; + expect(request.data.adUnits[0].features).to.exist; + expect(request.data.adUnits[0].params.outerAdUnitElementId).to.exist; + }); + + it('generates a pageviewId if missing', () => { + window.top.ADAGIO = window.top.ADAGIO || {}; + delete window.top.ADAGIO.pageviewId; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.have.lengthOf(2); + + expect(requests[0].data.pageviewId).to.exist.and.to.not.equal('_').and.to.not.equal(''); + expect(requests[0].data.pageviewId).to.equal(requests[1].data.pageviewId); + }); + + it('uses an existing pageviewId if present', () => { + window.top.ADAGIO = window.top.ADAGIO || {}; + window.top.ADAGIO.pageviewId = 'abc'; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests).to.have.lengthOf(2); + + expect(requests[0].data.pageviewId).to.equal('abc'); + expect(requests[1].data.pageviewId).to.equal('abc'); }); + it('should send the printNumber in features object', () => { + window.top.ADAGIO = window.top.ADAGIO || {}; + window.top.ADAGIO.pageviewId = 'abc'; + window.top.ADAGIO.adUnits['adunit-code1'] = { + pageviewId: 'abc', + printNumber: 2 + }; + const requests = spec.buildRequests([bidRequests[0]], bidderRequest); + const request = requests[0]; + expect(request.data.adUnits[0].features.print_number).to.equal('2'); + }) + it('GDPR consent is applied', () => { const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests).to.have.lengthOf(2); @@ -172,11 +456,30 @@ describe('adagioAdapter', () => { expect(request.data.gdpr).to.exist; expect(request.data.gdpr).to.be.empty; }); + + it('should expose version in window', () => { + expect(window.top.ADAGIO).ok; + expect(window.top.ADAGIO.versions).ok; + expect(window.top.ADAGIO.versions.adagioBidderAdapter).to.eq(VERSION); + }); + + it('should returns an empty array if the bidder cannot access to window top (based on refererInfo.reachedTop)', () => { + const requests = spec.buildRequests(bidRequests, { + ...bidderRequest, + refererInfo: { reachedTop: false } + }); + expect(requests).to.be.empty; + }); }); describe('interpretResponse', () => { + const sandbox = sinon.createSandbox(); + let serverResponse = { body: { + data: { + pred: 1 + }, bids: [ { ad: '
', @@ -193,27 +496,66 @@ describe('adagioAdapter', () => { } }; + let emptyBodyServerResponse = { + body: null + }; + + let withoutBidsArrayServerResponse = { + body: { + bids: [] + } + }; + + let serverResponseWhichThrowsException = { + body: { + data: { + pred: 1 + }, + bids: { + foo: 'bar' + } + } + }; + let bidRequest = { 'data': { 'adUnits': [ { 'bidder': 'adagio', 'params': { - siteId: '666', - placementId: 4, - pagetypeId: '232', - categories: ['IAB12'] + organizationId: '456', + site: 'ADAGIO-456', + placement: 'PAVE_ATF-456', + adUnitElementId: 'banner-atf-456', + pagetype: 'ARTICLE', + category: 'NEWS', + subcategory: 'SPORT', + environment: 'SITE-MOBILE' }, 'adUnitCode': 'adunit-code', 'sizes': [[300, 250], [300, 600]], 'bidId': 'c180kg4267tyqz', 'bidderRequestId': '8vfscuixrovn8i', 'auctionId': 'lel4fhp239i9km', + 'pageviewId': 'd8c4fl2k39i0wn', } ] } }; + afterEach(function() { + sandbox.restore(); + }); + + it('Should returns empty response if body is empty', () => { + expect(spec.interpretResponse(emptyBodyServerResponse, bidRequest)).to.be.an('array').length(0); + expect(spec.interpretResponse({body: {}}, bidRequest)).to.be.an('array').length(0); + }); + + it('Should returns empty response if bids array is empty', () => { + expect(spec.interpretResponse({withoutBidsArrayServerResponse}, bidRequest)).to.be.an('array').length(0); + }); + it('should get correct bid response', () => { let expectedResponse = [{ ad: '
', @@ -225,13 +567,35 @@ describe('adagioAdapter', () => { requestId: 'c180kg4267tyqz', ttl: 360, width: 300, - categories: [], - pagetypeId: '232', - placementId: 4, + placement: 'PAVE_ATF-456', + site: 'ADAGIO-456', + pagetype: 'ARTICLE', + category: 'NEWS', + subcategory: 'SPORT', + environment: 'SITE-MOBILE' }]; expect(spec.interpretResponse(serverResponse, bidRequest)).to.be.an('array'); expect(spec.interpretResponse(serverResponse, bidRequest)).to.deep.equal(expectedResponse); }); + + it('Should populate ADAGIO queue with ssp-data', () => { + spec.interpretResponse(serverResponse, bidRequest); + expect(window.top.ADAGIO).ok; + expect(window.top.ADAGIO.queue).to.be.an('array'); + }); + + it('Should not populate ADAGIO queue with ssp-data if not in top window', () => { + utils.getWindowTop().ADAGIO.queue = []; + sandbox.stub(utils, 'getWindowTop').throws(); + spec.interpretResponse(serverResponse, bidRequest); + expect(window.top.ADAGIO).ok; + expect(window.top.ADAGIO.queue).to.be.an('array'); + expect(window.top.ADAGIO.queue).empty; + }); + + it('should return an empty response even if an exception is ', () => { + expect(spec.interpretResponse(serverResponseWhichThrowsException, bidRequest)).to.be.an('array').length(0); + }); }); describe('getUserSyncs', () => { @@ -270,4 +634,64 @@ describe('adagioAdapter', () => { expect(emptyResult).to.equal(false); }); }); + + describe('getAdagioScript', () => { + const VALID_HASH = 'Lddcw3AADdQDrPtbRJkKxvA+o1CtScGDIMNRpHB3NnlC/FYmy/9RKXelKrYj/sjuWusl5YcOpo+lbGSkk655i8EKuDiOvK6ae/imxSrmdziIp+S/TA6hTFJXcB8k1Q9OIp4CMCT52jjXgHwX6G0rp+uYoCR25B1jHaHnpH26A6I='; + const INVALID_HASH = 'invalid'; + const VALID_SCRIPT_CONTENT = 'var _ADAGIO=function(){};(_ADAGIO)();\n'; + const INVALID_SCRIPT_CONTENT = 'var _ADAGIO=function(){//corrupted};(_ADAGIO)();\n'; + const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; + + it('should verify valid hash with valid script', function () { + localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + VALID_HASH + '\n' + VALID_SCRIPT_CONTENT); + + utilsMock.expects('logInfo').withExactArgs('Start Adagio script').once(); + utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').never(); + utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').never(); + + getAdagioScript(); + + expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.equals('// hash: ' + VALID_HASH + '\n' + VALID_SCRIPT_CONTENT); + utilsMock.verify(); + }); + + it('should verify valid hash with invalid script', function () { + localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + VALID_HASH + '\n' + INVALID_SCRIPT_CONTENT); + + utilsMock.expects('logInfo').withExactArgs('Start Adagio script').never(); + utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').never(); + utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').once(); + + getAdagioScript(); + + expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.be.null; + utilsMock.verify(); + }); + + it('should verify invalid hash with valid script', function () { + localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, '// hash: ' + INVALID_HASH + '\n' + VALID_SCRIPT_CONTENT); + + utilsMock.expects('logInfo').withExactArgs('Start Adagio script').never(); + utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').never(); + utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').once(); + + getAdagioScript(); + + expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.be.null; + utilsMock.verify(); + }); + + it('should verify missing hash', function () { + localStorage.setItem(ADAGIO_LOCALSTORAGE_KEY, VALID_SCRIPT_CONTENT); + + utilsMock.expects('logInfo').withExactArgs('Start Adagio script').never(); + utilsMock.expects('logWarn').withExactArgs('No hash found in Adagio script').once(); + utilsMock.expects('logWarn').withExactArgs('Invalid Adagio script found').never(); + + getAdagioScript(); + + expect(localStorage.getItem(ADAGIO_LOCALSTORAGE_KEY)).to.be.null; + utilsMock.verify(); + }); + }); }); diff --git a/test/spec/modules/adformBidAdapter_spec.js b/test/spec/modules/adformBidAdapter_spec.js index f50474ae5009..17eef06c603b 100644 --- a/test/spec/modules/adformBidAdapter_spec.js +++ b/test/spec/modules/adformBidAdapter_spec.js @@ -258,6 +258,13 @@ describe('Adform adapter', function () { }; }); + it('should set a renderer for an outstream context', function () { + serverResponse.body = [serverResponse.body[3]]; + bidRequest.bids = [bidRequest.bids[6]]; + let result = spec.interpretResponse(serverResponse, bidRequest); + assert.ok(result[0].renderer); + }); + describe('verifySizes', function () { it('should respond with empty response when sizes doesn\'t match', function () { serverResponse.body[0].response = 'banner'; @@ -309,6 +316,7 @@ describe('Adform adapter', function () { let sizes = [[250, 300], [300, 250], [300, 600]]; let placementCode = ['div-01', 'div-02', 'div-03', 'div-04', 'div-05']; + let mediaTypes = [{video: {context: 'outstream'}}]; let params = [{ mid: 1, url: 'some// there' }, {adxDomain: null, mid: 2, someVar: 'someValue', pt: 'gross'}, { adxDomain: null, mid: 3, pdom: 'home' }, {mid: 5, pt: 'net'}, {mid: 6, pt: 'gross'}]; bids = [ { @@ -388,6 +396,7 @@ describe('Adform adapter', function () { params: params[4], placementCode: placementCode[2], sizes: [], + mediaTypes: mediaTypes[0], transactionId: '5f33781f-9552-7ev3' } ]; diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 1f221cd956ed..ddb3f3dddf20 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -379,8 +379,8 @@ describe('Adkernel adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(4); - expect(spec.aliases).to.be.eql(['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia']); + expect(spec.aliases).to.have.lengthOf(5); + expect(spec.aliases).to.be.eql(['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak']); }); }); }); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 4598a4e00c76..155da4fb3091 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -496,15 +496,16 @@ describe('BeachfrontAdapter', function () { const serverResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' + crid: '123abc' }; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse).to.deep.equal({ requestId: bidRequest.bidId, bidderCode: spec.code, cpm: serverResponse.bidPrice, - creativeId: serverResponse.cmpId, + creativeId: serverResponse.crid, vastUrl: serverResponse.url, + vastXml: undefined, width: width, height: height, renderer: null, @@ -515,6 +516,48 @@ describe('BeachfrontAdapter', function () { }); }); + it('should default to the legacy "cmpId" value for the creative ID', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + cmpId: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse).to.deep.contain({ + creativeId: serverResponse.cmpId + }); + }); + + it('should return vast xml if found on the bid response', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + vast: '', + crid: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse).to.deep.contain({ + vastUrl: serverResponse.url, + vastXml: serverResponse.vast + }); + }); + it('should return a renderer for outstream video bids', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { @@ -525,7 +568,7 @@ describe('BeachfrontAdapter', function () { const serverResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' + crid: '123abc' }; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); expect(bidResponse.renderer).to.deep.contain({ @@ -547,7 +590,7 @@ describe('BeachfrontAdapter', function () { const serverResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' + crid: '123abc' }; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); window.Beachfront = { Player: sinon.spy() }; @@ -581,7 +624,7 @@ describe('BeachfrontAdapter', function () { const serverResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' + crid: '123abc' }; const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); window.Beachfront = { Player: sinon.spy() }; @@ -662,7 +705,7 @@ describe('BeachfrontAdapter', function () { bidResponse = { bidPrice: 5.00, url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' + crid: '123abc' }; }); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index aeadc3bf828c..6c9f0c8e6e58 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -2,8 +2,6 @@ import {expect} from 'chai'; import {spec} from 'modules/conversantBidAdapter'; import * as utils from 'src/utils'; -var Adapter = require('modules/conversantBidAdapter'); - describe('Conversant adapter tests', function() { const siteId = '108060'; const versionPattern = /^\d+\.\d+\.\d+(.)*$/; @@ -16,7 +14,6 @@ describe('Conversant adapter tests', function() { site_id: siteId, position: 1, tag_id: 'tagid-1', - secure: false, bidfloor: 0.5 }, placementCode: 'pcode000', @@ -30,8 +27,7 @@ describe('Conversant adapter tests', function() { { bidder: 'conversant', params: { - site_id: siteId, - secure: false + site_id: siteId }, mediaTypes: { banner: { @@ -50,8 +46,7 @@ describe('Conversant adapter tests', function() { params: { site_id: siteId, position: 2, - tag_id: '', - secure: false + tag_id: '' }, placementCode: 'pcode002', transactionId: 'tx002', @@ -198,9 +193,15 @@ describe('Conversant adapter tests', function() { }); it('Verify buildRequest', function() { - const request = spec.buildRequests(bidRequests); + const page = 'http://test.com?a=b&c=123'; + const bidderRequest = { + refererInfo: { + referer: page + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.method).to.equal('POST'); - expect(request.url).to.equal('//web.hb.ad.cpe.dotomi.com/s2s/header/24'); + expect(request.url).to.equal('https://web.hb.ad.cpe.dotomi.com/s2s/header/24'); const payload = request.data; expect(payload).to.have.property('id', 'req000'); @@ -209,7 +210,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp).to.be.an('array').with.lengthOf(6); expect(payload.imp[0]).to.have.property('id', 'bid000'); - expect(payload.imp[0]).to.have.property('secure', 0); + expect(payload.imp[0]).to.have.property('secure', 1); expect(payload.imp[0]).to.have.property('bidfloor', 0.5); expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js'); expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(versionPattern); @@ -221,7 +222,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[0]).to.not.have.property('video'); expect(payload.imp[1]).to.have.property('id', 'bid001'); - expect(payload.imp[1]).to.have.property('secure', 0); + expect(payload.imp[1]).to.have.property('secure', 1); expect(payload.imp[1]).to.have.property('bidfloor', 0); expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js'); expect(payload.imp[1]).to.have.property('displaymanagerver').that.matches(versionPattern); @@ -232,7 +233,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[1].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); expect(payload.imp[2]).to.have.property('id', 'bid002'); - expect(payload.imp[2]).to.have.property('secure', 0); + expect(payload.imp[2]).to.have.property('secure', 1); expect(payload.imp[2]).to.have.property('bidfloor', 0); expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js'); expect(payload.imp[2]).to.have.property('displaymanagerver').that.matches(versionPattern); @@ -242,7 +243,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]); expect(payload.imp[3]).to.have.property('id', 'bid003'); - expect(payload.imp[3]).to.have.property('secure', 0); + expect(payload.imp[3]).to.have.property('secure', 1); expect(payload.imp[3]).to.have.property('bidfloor', 0); expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js'); expect(payload.imp[3]).to.have.property('displaymanagerver').that.matches(versionPattern); @@ -261,7 +262,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[3]).to.not.have.property('banner'); expect(payload.imp[4]).to.have.property('id', 'bid004'); - expect(payload.imp[4]).to.have.property('secure', 0); + expect(payload.imp[4]).to.have.property('secure', 1); expect(payload.imp[4]).to.have.property('bidfloor', 0); expect(payload.imp[4]).to.have.property('displaymanager', 'Prebid.js'); expect(payload.imp[4]).to.have.property('displaymanagerver').that.matches(versionPattern); @@ -280,7 +281,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[4]).to.not.have.property('banner'); expect(payload.imp[5]).to.have.property('id', 'bid005'); - expect(payload.imp[5]).to.have.property('secure', 0); + expect(payload.imp[5]).to.have.property('secure', 1); expect(payload.imp[5]).to.have.property('bidfloor', 0); expect(payload.imp[5]).to.have.property('displaymanager', 'Prebid.js'); expect(payload.imp[5]).to.have.property('displaymanagerver').that.matches(versionPattern); @@ -299,8 +300,7 @@ describe('Conversant adapter tests', function() { expect(payload).to.have.property('site'); expect(payload.site).to.have.property('id', siteId); expect(payload.site).to.have.property('mobile').that.is.oneOf([0, 1]); - const loc = utils.getTopWindowLocation(); - const page = loc.href; + expect(payload.site).to.have.property('page', page); expect(payload).to.have.property('device'); @@ -365,7 +365,7 @@ describe('Conversant adapter tests', function() { it('Verify publisher commond id support', function() { // clone bidRequests - let requests = utils.deepClone(bidRequests) + let requests = utils.deepClone(bidRequests); // add pubcid to every entry requests.forEach((unit) => { @@ -378,7 +378,7 @@ describe('Conversant adapter tests', function() { it('Verify User ID publisher commond id support', function() { // clone bidRequests - let requests = utils.deepClone(bidRequests) + let requests = utils.deepClone(bidRequests); // add pubcid to every entry requests.forEach((unit) => { @@ -415,4 +415,4 @@ describe('Conversant adapter tests', function() { expect(payload).to.have.deep.nested.property('user.ext.consent', ''); expect(payload).to.not.have.deep.nested.property('regs.ext.gdpr'); }); -}) +}); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 03500d4add69..e5d789ff60d3 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -368,7 +368,11 @@ describe('The Criteo bidding adapter', function () { }); describe('buildRequests', function () { + const refererUrl = 'https://criteo.com?pbt_debug=1&pbt_nolog=1'; const bidderRequest = { + refererInfo: { + referer: refererUrl + }, timeout: 3000, gdprConsent: { gdprApplies: 1, @@ -385,10 +389,23 @@ describe('The Criteo bidding adapter', function () { config.resetConfig(); }); - it('should properly build a zoneId request', function () { - const publisherUrl = 'https://criteo.com?pbt_debug=1&pbt_nolog=1'; - utilsMock.expects('getTopWindowUrl').withExactArgs().once().returns(publisherUrl); + it('should properly build a request if refererInfo is not provided', function () { + const bidderRequest = {}; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + sizes: [[728, 90]], + params: {} + }, + ]; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest.publisher.url).to.equal(''); + }); + it('should properly build a zoneId request', function () { const bidRequests = [ { bidder: 'criteo', @@ -407,7 +424,7 @@ describe('The Criteo bidding adapter', function () { expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d+&im=1&debug=1&nolog=1/); expect(request.method).to.equal('POST'); const ortbRequest = request.data; - expect(ortbRequest.publisher.url).to.equal(publisherUrl); + expect(ortbRequest.publisher.url).to.equal(refererUrl); expect(ortbRequest.slots).to.have.lengthOf(1); expect(ortbRequest.slots[0].impid).to.equal('bid-123'); expect(ortbRequest.slots[0].transactionid).to.equal('transaction-123'); @@ -421,6 +438,9 @@ describe('The Criteo bidding adapter', function () { it('should properly build a networkId request', function () { const bidderRequest = { + refererInfo: { + referer: refererUrl + }, timeout: 3000, gdprConsent: { gdprApplies: 0, @@ -451,7 +471,7 @@ describe('The Criteo bidding adapter', function () { expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); expect(request.method).to.equal('POST'); const ortbRequest = request.data; - expect(ortbRequest.publisher.url).to.equal(utils.getTopWindowUrl()); + expect(ortbRequest.publisher.url).to.equal(refererUrl); expect(ortbRequest.publisher.networkid).to.equal(456); expect(ortbRequest.slots).to.have.lengthOf(1); expect(ortbRequest.slots[0].impid).to.equal('bid-123'); @@ -465,7 +485,12 @@ describe('The Criteo bidding adapter', function () { }); it('should properly build a mixed request', function () { - const bidderRequest = { timeout: 3000 }; + const bidderRequest = { + refererInfo: { + referer: refererUrl + }, + timeout: 3000 + }; const bidRequests = [ { bidder: 'criteo', @@ -490,7 +515,7 @@ describe('The Criteo bidding adapter', function () { expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); expect(request.method).to.equal('POST'); const ortbRequest = request.data; - expect(ortbRequest.publisher.url).to.equal(utils.getTopWindowUrl()); + expect(ortbRequest.publisher.url).to.equal(refererUrl); expect(ortbRequest.publisher.networkid).to.equal(456); expect(ortbRequest.slots).to.have.lengthOf(2); expect(ortbRequest.slots[0].impid).to.equal('bid-123'); @@ -517,9 +542,9 @@ describe('The Criteo bidding adapter', function () { }, }, ]; - const bidderRequest = { timeout: 3000, - gdprConsent: { - }, + const bidderRequest = { + timeout: 3000, + gdprConsent: {}, }; const ortbRequest = spec.buildRequests(bidRequests, bidderRequest).data; diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 16f17174f884..80fd12a237c6 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -527,6 +527,15 @@ describe('emx_digital Adapter', function () { }); expect(result.length).to.equal(0); }); + + it('should not throw an error when decoding an improperly encoded adm', function () { + serverResponse.seatbid[0].bid[0].adm = '\\<\\/script\\>'; + serverResponse.seatbid[1].bid[0].adm = '%3F%%3Demx%3C3prebid' + + assert.doesNotThrow(() => spec.interpretResponse({ + body: serverResponse + })); + }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/googleAnalyticsAdapter_spec.js b/test/spec/modules/googleAnalyticsAdapter_spec.js index 20517f4138da..6bc0d4e192d3 100644 --- a/test/spec/modules/googleAnalyticsAdapter_spec.js +++ b/test/spec/modules/googleAnalyticsAdapter_spec.js @@ -4,12 +4,24 @@ var assert = require('assert'); describe('Ga', function () { describe('enableAnalytics', function () { - it('should accept a tracker name option and output prefixed send string', function () { - var config = { options: { trackerName: 'foo' } }; - ga.enableAnalytics(config); + var cpmDistribution = function(cpm) { + return cpm <= 1 ? '<= 1$' : '> 1$'; + } + var config = { options: { trackerName: 'foo', enableDistribution: true, cpmDistribution: cpmDistribution } }; + + // enableAnalytics can only be called once + ga.enableAnalytics(config); + it('should accept a tracker name option and output prefixed send string', function () { var output = ga.getTrackerSend(); assert.equal(output, 'foo.send'); }); + + it('should use the custom cpm distribution', function() { + assert.equal(ga.getCpmDistribution(0.5), '<= 1$'); + assert.equal(ga.getCpmDistribution(1), '<= 1$'); + assert.equal(ga.getCpmDistribution(2), '> 1$'); + assert.equal(ga.getCpmDistribution(5.23), '> 1$'); + }); }); }); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index a191d2211f54..36aaddb8b427 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -93,9 +93,11 @@ describe('TheMediaGrid Adapter', function () { expect(payload).to.have.property('auids', '1'); expect(payload).to.have.property('sizes', '300x250,300x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); }); - it('auids must not be duplicated', function () { + it('sizes must not be duplicated', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 38e64e8d3387..120d02408d7d 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -5,7 +5,6 @@ import { newBidder } from 'src/adapters/bidderFactory'; import { spec } from 'modules/ixBidAdapter'; describe('IndexexchangeAdapter', function () { - const IX_INSECURE_ENDPOINT = 'http://as.casalemedia.com/cygnus'; const IX_SECURE_ENDPOINT = 'https://as-sec.casalemedia.com/cygnus'; const BIDDER_VERSION = 7.2; @@ -401,7 +400,7 @@ describe('IndexexchangeAdapter', function () { it('request should be made to IX endpoint with GET method', function () { expect(requestMethod).to.equal('GET'); - expect(requestUrl).to.equal(IX_INSECURE_ENDPOINT); + expect(requestUrl).to.equal(IX_SECURE_ENDPOINT); }); it('query object (version, siteID and request) should be correct', function () { diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 855eb2ee3f9b..6db8bff4c3dd 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -577,6 +577,44 @@ describe('Livewrapped adapter tests', function () { }); }); + it('should make use of Id5-Id if available', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + testbidRequest.bids[0].userId = {}; + testbidRequest.bids[0].userId.id5id = 'id5-user-id'; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(data.rtbData.user.ext.eids).to.deep.equal([{ + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'id5-user-id', + 'atype': 1 + }] + }]); + }); + + it('should make use of publisher common Id if available', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + testbidRequest.bids[0].userId = {}; + testbidRequest.bids[0].userId.pubcid = 'publisher-common-id'; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + expect(data.rtbData.user.ext.eids).to.deep.equal([{ + 'source': 'pubcommon', + 'uids': [{ + 'id': 'publisher-common-id', + 'atype': 1 + }] + }]); + }); + describe('interpretResponse', function () { it('should handle single success response', function() { let lwResponse = { @@ -591,7 +629,8 @@ describe('Livewrapped adapter tests', function () { bidId: '32e50fad901ae89', auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', - ttl: 120 + ttl: 120, + meta: undefined } ], currency: 'USD' @@ -607,7 +646,8 @@ describe('Livewrapped adapter tests', function () { ttl: 120, creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', netRevenue: true, - currency: 'USD' + currency: 'USD', + meta: undefined }]; let bids = spec.interpretResponse({body: lwResponse}); @@ -628,7 +668,8 @@ describe('Livewrapped adapter tests', function () { bidId: '32e50fad901ae89', auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', - ttl: 120 + ttl: 120, + meta: undefined }, { id: '38e5ddf4-3c01-11e8-86a7-0a44794250d4', @@ -640,7 +681,8 @@ describe('Livewrapped adapter tests', function () { bidId: '42e50fad901ae89', auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', creativeId: '62cbd598-2715-4c43-a06f-229fc170f945:427077', - ttl: 120 + ttl: 120, + meta: undefined } ], currency: 'USD' @@ -656,7 +698,8 @@ describe('Livewrapped adapter tests', function () { ttl: 120, creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', netRevenue: true, - currency: 'USD' + currency: 'USD', + meta: undefined }, { requestId: '42e50fad901ae89', bidderCode: 'livewrapped', @@ -667,7 +710,47 @@ describe('Livewrapped adapter tests', function () { ttl: 120, creativeId: '62cbd598-2715-4c43-a06f-229fc170f945:427077', netRevenue: true, + currency: 'USD', + meta: undefined + }]; + + let bids = spec.interpretResponse({body: lwResponse}); + + expect(bids).to.deep.equal(expectedResponse); + }) + + it('should return meta-data', function() { + let lwResponse = { + ads: [ + { + id: '28e5ddf4-3c01-11e8-86a7-0a44794250d4', + callerId: 'site_outsider_0', + tag: 'ad', + width: 300, + height: 250, + cpmBid: 2.565917, + bidId: '32e50fad901ae89', + auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + ttl: 120, + meta: {metadata: 'metadata'} + } + ], currency: 'USD' + }; + + let expectedResponse = [{ + requestId: '32e50fad901ae89', + bidderCode: 'livewrapped', + cpm: 2.565917, + width: 300, + height: 250, + ad: 'ad', + ttl: 120, + creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', + netRevenue: true, + currency: 'USD', + meta: {metadata: 'metadata'} }]; let bids = spec.interpretResponse({body: lwResponse}); diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 58b90b0a017a..6597a4d87c0b 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -212,4 +212,140 @@ describe('OneVideoBidAdapter', function () { expect(data.source.ext.schain.nodes[0].rid).to.equal(data.id); }); }); + describe('should send banner object', function () { + it('should send banner object when display is 1', function () { + bidRequest = { + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, + bidder: 'oneVideo', + sizes: [640, 480], + bidId: '30b3efwfwe1e', + adUnitCode: 'video1', + params: { + video: { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [2], + position: 1, + delivery: [2], + playbackmethod: [1, 5], + placement: 123, + sid: 134, + display: 1 + }, + site: { + id: 1, + page: 'https://www.yahoo.com/', + referrer: 'http://www.yahoo.com' + }, + pubId: 'OneMDisplay' + } + }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + const width = bidRequest.params.video.playerWidth; + const height = bidRequest.params.video.playerHeight; + const position = bidRequest.params.video.position; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + expect(data.imp[0].banner.w).to.equal(width); + expect(data.imp[0].banner.h).to.equal(height); + expect(data.imp[0].banner.pos).to.equal(position); + expect(data.imp[0].banner.mimes).to.equal(bidRequest.params.video.mimes); + }); + it('should send video object when display is other than 1', function () { + bidRequest = { + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, + bidder: 'oneVideo', + sizes: [640, 480], + bidId: '30b3efwfwe1e', + adUnitCode: 'video1', + params: { + video: { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [2], + position: 1, + delivery: [2], + playbackmethod: [1, 5], + placement: 123, + sid: 134, + display: 12 + }, + site: { + id: 1, + page: 'https://www.yahoo.com/', + referrer: 'http://www.yahoo.com' + }, + pubId: 'OneMDisplay' + } + }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + const width = bidRequest.params.video.playerWidth; + const height = bidRequest.params.video.playerHeight; + const position = bidRequest.params.video.position; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + expect(data.imp[0].video.w).to.equal(width); + expect(data.imp[0].video.h).to.equal(height); + expect(data.imp[0].video.pos).to.equal(position); + expect(data.imp[0].video.mimes).to.equal(bidRequest.params.video.mimes); + }); + it('should send video object when display is not passed', function () { + bidRequest = { + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480] + } + }, + bidder: 'oneVideo', + sizes: [640, 480], + bidId: '30b3efwfwe1e', + adUnitCode: 'video1', + params: { + video: { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [2], + position: 1, + delivery: [2], + playbackmethod: [1, 5], + placement: 123, + sid: 134 + }, + site: { + id: 1, + page: 'https://www.yahoo.com/', + referrer: 'http://www.yahoo.com' + }, + pubId: 'OneMDisplay' + } + }; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + const width = bidRequest.params.video.playerWidth; + const height = bidRequest.params.video.playerHeight; + const position = bidRequest.params.video.position; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + expect(data.imp[0].video.w).to.equal(width); + expect(data.imp[0].video.h).to.equal(height); + expect(data.imp[0].video.pos).to.equal(position); + expect(data.imp[0].video.mimes).to.equal(bidRequest.params.video.mimes); + }); + }); }); diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index d56ad9e6dc5d..932845660695 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -53,7 +53,7 @@ describe('onetag', function () { const data = JSON.parse(d); it('Should contains all keys', function () { expect(data).to.be.an('object'); - expect(data).to.have.all.keys('location', 'masked', 'referrer', 'sHeight', 'sWidth', 'timeOffset', 'date', 'wHeight', 'wWidth', 'bids'); + expect(data).to.have.all.keys('location', 'masked', 'referrer', 'sHeight', 'sWidth', 'timeOffset', 'date', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids'); expect(data.location).to.be.a('string'); expect(data.masked).to.be.a('number'); expect(data.referrer).to.be.a('string'); @@ -61,6 +61,13 @@ describe('onetag', function () { expect(data.sWidth).to.be.a('number'); expect(data.wWidth).to.be.a('number'); expect(data.wHeight).to.be.a('number'); + expect(data.oHeight).to.be.a('number'); + expect(data.oWidth).to.be.a('number'); + expect(data.aWidth).to.be.a('number'); + expect(data.aHeight).to.be.a('number'); + expect(data.sLeft).to.be.a('number'); + expect(data.sTop).to.be.a('number'); + expect(data.hLength).to.be.a('number'); expect(data.timeOffset).to.be.a('number'); expect(data.date).to.be.a('string'); expect(data.bids).to.be.an('array'); @@ -146,4 +153,56 @@ describe('onetag', function () { }); }); }); + describe('getUserSyncs', function () { + const sync_endpoint = 'https://onetag-sys.com/usync/'; + it('Returns an iframe if iframeEnabled is true', function () { + const syncs = spec.getUserSyncs({iframeEnabled: true}); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(sync_endpoint); + }); + it('Returns an empty array if iframeEnabled is false', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: false }); + expect(syncs).to.be.an('array').that.is.empty; + }); + it('Must pass gdpr params when gdprApplies is true', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { + gdprApplies: true, consentString: 'foo' + }); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(sync_endpoint); + expect(syncs[0].url).to.match(/(?:[?&](?:gdpr_consent=foo([^&]*)|gdpr=1([^&]*)|[^&]*))+$/); + }); + it('Must pass gdpr params when gdprApplies is false', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { + gdprApplies: false, consentString: 'foo' + }); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(sync_endpoint); + expect(syncs[0].url).to.match(/(?:[?&](?:gdpr_consent=foo([^&]*)|gdpr=0([^&]*)))+$/); + }); + it('Must pass gdpr consent string param when gdprApplies is undefined', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { + consentString: 'foo' + }); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(sync_endpoint); + expect(syncs[0].url).to.match(/(?:[?&](?:gdpr_consent=foo([^&]*)))+$/); + }); + it('Must pass no gdpr params when consentString is null', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { + consentString: null + }); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(sync_endpoint); + expect(syncs[0].url).to.not.match(/(?:[?&](?:gdpr_consent=([^&]*)|gdpr=([^&]*)))+$/); + }); + it('Must pass no gdpr param when gdprConsent is empty', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, {}); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(sync_endpoint); + expect(syncs[0].url).to.not.match(/(?:[?&](?:gdpr_consent=([^&]*)|gdpr=([^&]*)))+$/); + }); + }); }); diff --git a/test/spec/modules/outconBidAdapter_spec.js b/test/spec/modules/outconBidAdapter_spec.js index dc9c96ed3a34..a263bc9dbf9a 100644 --- a/test/spec/modules/outconBidAdapter_spec.js +++ b/test/spec/modules/outconBidAdapter_spec.js @@ -23,6 +23,7 @@ describe('outconBidAdapter', function () { })).to.equal(true); }); }); + describe('buildRequests', function () { it('Build requests with pod param', function () { expect(spec.buildRequests([{ @@ -52,7 +53,8 @@ describe('outconBidAdapter', function () { url: 'http://test.outcondigital.com:8048/ad/', data: { pod: '5d603538eba7192ae14e39a4', - env: 'test' + env: 'test', + vast: 'true' } }; const bidResponse = { @@ -69,10 +71,11 @@ describe('outconBidAdapter', function () { codec: 'video/mp4' } ], - id: '5d6e6aef22063e392bf7f564', + ad: '5d6e6aef22063e392bf7f564', type: 'video', campaign: '5d42e44b306ea469593c76a2', - trackingURL: 'http://test.outcondigital.com:8048/ad/track?track=5d6e6aef22063e392bf7f564' + trackingURL: 'http://test.outcondigital.com:8048/ad/track?track=5d6e6aef22063e392bf7f564', + vastURL: 'http://test.outcondigital.com:8048/outcon.xml?impression=5d6e6aef22063e392bf7f564&demo=true' }, }; it('check all the keys that are needed to interpret the response', function () { @@ -87,7 +90,9 @@ describe('outconBidAdapter', function () { 'netRevenue', 'ttl', 'ad', - 'vastImpUrl' + 'vastImpUrl', + 'mediaType', + 'vastUrl' ]; let resultKeys = Object.keys(result[0]); resultKeys.forEach(function(key) { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 658f130d144a..7945be682826 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2,10 +2,8 @@ import { expect } from 'chai'; import { PrebidServer as Adapter, resetSyncedStatus } from 'modules/prebidServerBidAdapter/index.js'; import adapterManager from 'src/adapterManager'; import * as utils from 'src/utils'; -import { userSync } from 'src/userSync'; import { ajax } from 'src/ajax'; import { config } from 'src/config'; -import { requestBidsHook } from 'modules/consentManagement'; import events from 'src/events'; import CONSTANTS from 'src/constants'; @@ -32,7 +30,7 @@ const REQUEST = { 'sizes': [[300, 250], [300, 600]], 'mediaTypes': { 'banner': { - 'sizes': [[ 300, 250 ], [ 300, 300 ]] + 'sizes': [[300, 250], [300, 300]] }, 'native': { 'title': { @@ -77,7 +75,7 @@ const VIDEO_REQUEST = { 'sizes': [640, 480], 'mediaTypes': { 'video': { - 'playerSize': [[ 640, 480 ]], + 'playerSize': [[640, 480]], 'mimes': ['video/mp4'] } }, @@ -107,7 +105,7 @@ const OUTSTREAM_VIDEO_REQUEST = { 'sizes': [640, 480], 'mediaTypes': { 'video': { - playerSize: [[ 640, 480 ]], + playerSize: [[640, 480]], context: 'outstream', mimes: ['video/mp4'] }, @@ -325,7 +323,7 @@ const RESPONSE_OPENRTB = { 'price': 0.5, 'adm': '', 'adid': '29681110', - 'adomain': [ 'appnexus.com' ], + 'adomain': ['appnexus.com'], 'iurl': 'http://lax1-ib.adnxs.com/cr?id=2968111', 'cid': '958', 'crid': '2968111', @@ -517,6 +515,11 @@ describe('S2S Adapter', function () { }, 'bid_id': '123', 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c', 'sizes': [300, 250], 'bidId': '123', @@ -553,11 +556,11 @@ describe('S2S Adapter', function () { xhr.restore(); }); - it('should not add outstrean without renderer', function() { + it('should not add outstrean without renderer', function () { let ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' - config.setConfig({s2sConfig: ortb2Config}); + config.setConfig({ s2sConfig: ortb2Config }); adapter.callBids(OUTSTREAM_VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); @@ -570,7 +573,7 @@ describe('S2S Adapter', function () { }); it('exists converts types', function () { - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); expect(requestBid).to.have.property('cache_markup', 2); @@ -604,7 +607,7 @@ describe('S2S Adapter', function () { expect(requestBid.user.ext.consent).is.equal('abc123'); config.resetConfig(); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(requests[1].requestBody); @@ -681,7 +684,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { cacheMarkup: 999 }); - config.setConfig({s2sConfig: s2sConfig}); + config.setConfig({ s2sConfig: s2sConfig }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); expect(requestBid).to.have.property('cache_markup', 0); @@ -722,7 +725,8 @@ describe('S2S Adapter', function () { }); it('adds device and app objects to request', function () { - const _config = { s2sConfig: CONFIG, + const _config = { + s2sConfig: CONFIG, device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, app: { bundle: 'com.test.app' }, }; @@ -737,7 +741,7 @@ describe('S2S Adapter', function () { }); expect(requestBid.app).to.deep.equal({ bundle: 'com.test.app', - publisher: {'id': '1'} + publisher: { 'id': '1' } }); }); @@ -762,7 +766,7 @@ describe('S2S Adapter', function () { }); expect(requestBid.app).to.deep.equal({ bundle: 'com.test.app', - publisher: {'id': '1'} + publisher: { 'id': '1' } }); }); @@ -785,7 +789,7 @@ describe('S2S Adapter', function () { }); expect(requestBid.app).to.deep.equal({ bundle: 'com.test.app', - publisher: {'id': '1'} + publisher: { 'id': '1' } }); }); @@ -858,7 +862,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig: s2sConfig}); + config.setConfig({ s2sConfig: s2sConfig }); const aliasBidder = { bidder: 'brealtime', @@ -889,7 +893,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig: s2sConfig}); + config.setConfig({ s2sConfig: s2sConfig }); const alias = 'foobar'; const aliasBidder = { @@ -923,7 +927,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig: s2sConfig}); + config.setConfig({ s2sConfig: s2sConfig }); const myRequest = utils.deepClone(REQUEST); myRequest.ad_units[0].bids[0].params.usePaymentRule = true; @@ -949,7 +953,7 @@ describe('S2S Adapter', function () { config.resetConfig(); const oldS2sConfig = Object.assign({}, CONFIG); - config.setConfig({s2sConfig: oldS2sConfig}); + config.setConfig({ s2sConfig: oldS2sConfig }); const myRequest2 = utils.deepClone(REQUEST); myRequest2.ad_units[0].bids[0].params.keywords = { @@ -1068,7 +1072,7 @@ describe('S2S Adapter', function () { let s2sConfig = utils.deepClone(CONFIG); s2sConfig.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; config.setConfig({ - currency: {adServerCurrency: ['USD', 'GB', 'UK', 'AU']}, + currency: { adServerCurrency: ['USD', 'GB', 'UK', 'AU'] }, s2sConfig: s2sConfig }); @@ -1083,7 +1087,7 @@ describe('S2S Adapter', function () { let s2sConfig = utils.deepClone(CONFIG); s2sConfig.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; config.setConfig({ - currency: {adServerCurrency: 'NZ'}, + currency: { adServerCurrency: 'NZ' }, s2sConfig: s2sConfig }); @@ -1097,7 +1101,7 @@ describe('S2S Adapter', function () { it('when config \'currency.adServerCurrency\' is unset: ORTB should not define a \'cur\' property', function () { let s2sConfig = utils.deepClone(CONFIG); s2sConfig.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; - config.setConfig({s2sConfig: s2sConfig}); + config.setConfig({ s2sConfig: s2sConfig }); const bidRequests = utils.deepClone(BID_REQUESTS); adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); @@ -1246,7 +1250,7 @@ describe('S2S Adapter', function () { }); }); - it('passes schain object in request', function() { + it('passes schain object in request', function () { const bidRequests = utils.deepClone(BID_REQUESTS); const schainObject = { 'ver': '1.0', @@ -1298,7 +1302,7 @@ describe('S2S Adapter', function () { it('registers bids and calls BIDDER_DONE', function () { server.respondWith(JSON.stringify(RESPONSE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); sinon.assert.calledOnce(addBidResponse); @@ -1321,7 +1325,7 @@ describe('S2S Adapter', function () { it('registers video bids', function () { server.respondWith(JSON.stringify(VIDEO_RESPONSE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); sinon.assert.calledOnce(addBidResponse); @@ -1339,7 +1343,7 @@ describe('S2S Adapter', function () { it('does not call addBidResponse and calls done when ad unit not set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); @@ -1350,7 +1354,7 @@ describe('S2S Adapter', function () { it('does not call addBidResponse and calls done when server requests cookie sync', function () { server.respondWith(JSON.stringify(RESPONSE_NO_COOKIE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); @@ -1361,7 +1365,7 @@ describe('S2S Adapter', function () { it('does not call addBidResponse and calls done when ad unit is set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_UNIT_SET)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); @@ -1372,7 +1376,7 @@ describe('S2S Adapter', function () { it('registers successful bids and calls done when there are less bids than requests', function () { server.respondWith(JSON.stringify(RESPONSE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); @@ -1390,7 +1394,7 @@ describe('S2S Adapter', function () { it('should have dealId in bidObject', function () { server.respondWith(JSON.stringify(RESPONSE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); const response = addBidResponse.firstCall.args[1]; @@ -1400,11 +1404,11 @@ describe('S2S Adapter', function () { it('should pass through default adserverTargeting if present in bidObject', function () { server.respondWith(JSON.stringify(RESPONSE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); const response = addBidResponse.firstCall.args[1]; - expect(response).to.have.property('adserverTargeting').that.deep.equals({'foo': 'bar'}); + expect(response).to.have.property('adserverTargeting').that.deep.equals({ 'foo': 'bar' }); }); it('registers client user syncs when client bid adapter is present', function () { @@ -1415,7 +1419,7 @@ describe('S2S Adapter', function () { server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); @@ -1433,7 +1437,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); server.respondWith(JSON.stringify(RESPONSE_OPENRTB)); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -1447,7 +1451,7 @@ describe('S2S Adapter', function () { it('registers bid responses when server requests cookie sync', function () { server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); sinon.assert.calledOnce(addBidResponse); @@ -1467,7 +1471,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); server.respondWith(JSON.stringify(RESPONSE_OPENRTB)); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -1490,7 +1494,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); server.respondWith(JSON.stringify(RESPONSE_OPENRTB_VIDEO)); adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -1510,7 +1514,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); cacheResponse.seatbid.forEach(item => { item.bid[0].ext.prebid.cache = { @@ -1536,7 +1540,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); const targetingTestData = { hb_cache_path: '/cache', @@ -1564,7 +1568,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); cacheResponse.seatbid.forEach(item => { item.bid[0].ext.prebid.targeting = { @@ -1594,7 +1598,7 @@ describe('S2S Adapter', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); server.respondWith(JSON.stringify(RESPONSE_OPENRTB_NATIVE)); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -1624,7 +1628,7 @@ describe('S2S Adapter', function () { } config.setConfig(_config); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); server.respond(); @@ -1633,13 +1637,20 @@ describe('S2S Adapter', function () { }); describe('s2sConfig', function () { + let xhr; + let requests; let logErrorSpy; beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); logErrorSpy = sinon.spy(utils, 'logError'); + resetSyncedStatus(); }); afterEach(function () { + xhr.restore(); utils.logError.restore(); }); @@ -1792,11 +1803,85 @@ describe('S2S Adapter', function () { config.setConfig({ s2sConfig: { syncUrlModifier: { - appnexus: () => {} + appnexus: () => { } } } }); expect(typeof config.getConfig('s2sConfig').syncUrlModifier.appnexus).to.equal('function') }); + + it('should set correct bidder names to bidders property when using an alias for that bidder', function () { + const s2sConfig = utils.deepClone(CONFIG); + + // Add syncEndpoint so that the request goes to the User Sync endpoint + // Modify the bidders property to include an alias for Rubicon adapter + s2sConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; + s2sConfig.bidders = ['appnexus', 'rubicon-alias']; + + const request = utils.deepClone(REQUEST); + + // Add another bidder, `rubicon-alias` + request.ad_units[0].bids.push({ + bidder: 'rubicon-alias', + params: { + accoundId: 14062, + siteId: 70608, + zoneId: 498816 + } + }); + + // create an alias for the Rubicon Bid Adapter + adapterManager.aliasBidAdapter('rubicon', 'rubicon-alias'); + + config.setConfig({ s2sConfig }); + + const bidRequest = utils.deepClone(BID_REQUESTS); + bidRequest.push({ + 'bidderCode': 'rubicon-alias', + 'auctionId': '4146ab2b-9422-4040-9b1c-966fffbfe2d4', + 'bidderRequestId': '4b1a4f9c3e4546', + 'tid': 'd7fa8342-ae22-4ca1-b237-331169350f84', + 'bids': [ + { + 'bidder': 'rubicon-alias', + 'params': { + 'accountId': 14062, + 'siteId': 70608, + 'zoneId': 498816 + }, + 'bid_id': '2a9523915411c3', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '78ddc106-b7d8-45d1-bd29-86993098e53d', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '2a9523915411c3', + 'bidderRequestId': '4b1a4f9c3e4546', + 'auctionId': '4146ab2b-9422-4040-9b1c-966fffbfe2d4' + } + ], + 'auctionStart': 1569234122602, + 'timeout': 1000, + 'src': 's2s' + }); + + adapter.callBids(request, bidRequest, addBidResponse, done, ajax); + + const requestBid = JSON.parse(requests[0].requestBody); + expect(requestBid.bidders).to.deep.equal(['appnexus', 'rubicon']); + }); }); }); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 22c0f840026a..feb64dfe4acb 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1171,6 +1171,46 @@ describe('PubMatic adapter', function () { // should have called DigiTrust.getUser() once expect(window.DigiTrust.getUser.calledOnce).to.equal(true); }); + + it('should NOT include coppa flag in bid request if coppa config is not present', () => { + const request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + if (data.regs) { + // in case GDPR is set then data.regs will exist + expect(data.regs.coppa).to.equal(undefined); + } else { + expect(data.regs).to.equal(undefined); + } + }); + + it('should include coppa flag in bid request if coppa is set to true', () => { + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + 'coppa': true + }; + return config[key]; + }); + const request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.regs.coppa).to.equal(1); + }); + + it('should NOT include coppa flag in bid request if coppa is set to false', () => { + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + 'coppa': false + }; + return config[key]; + }); + const request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + if (data.regs) { + // in case GDPR is set then data.regs will exist + expect(data.regs.coppa).to.equal(undefined); + } else { + expect(data.regs).to.equal(undefined); + } + }); }); describe('AdsrvrOrgId from config', function() { @@ -1696,6 +1736,42 @@ describe('PubMatic adapter', function () { expect(data.user.eids).to.equal(undefined); }); }); + + describe('Parrable Id', function() { + it('send the Parrable id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.parrableid = 'parrable-user-id'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'parrable.com', + 'uids': [{ + 'id': 'parrable-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.parrableid = 1; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.parrableid = []; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.parrableid = null; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.parrableid = {}; + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); }); it('Request params check for video ad', function () { diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 9ed6d3631f54..6d5400de2162 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -200,7 +200,7 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ttl).to.equal(20); }); - it('Verify use ttl in ext', function () { + it('Verify ttl/currency applied to bid', function () { const request = spec.buildRequests(slotConfigs, bidderRequest); const ortbRequest = request.data; const ortbResponse = { @@ -208,22 +208,31 @@ describe('PulsePoint Adapter Tests', function () { bid: [{ impid: ortbRequest.imp[0].id, price: 1.25, - adm: 'This is an Ad', - ext: { - ttl: 30, - netRevenue: false, - currency: 'INR' - } + adm: 'This is an Ad#1', + crid: 'Creative#123', + exp: 50, + cur: 'GBP' + }, { + impid: ortbRequest.imp[1].id, + price: 1.25, + adm: 'This is an Ad#2', + crid: 'Creative#123' }] }] }; const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(1); + expect(bids).to.have.lengthOf(2); // verify first bid const bid = bids[0]; - expect(bid.ttl).to.equal(30); - expect(bid.netRevenue).to.equal(false); - expect(bid.currency).to.equal('INR'); + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('This is an Ad#1'); + expect(bid.ttl).to.equal(50); + expect(bid.currency).to.equal('GBP'); + const secondBid = bids[1]; + expect(secondBid.cpm).to.equal(1.25); + expect(secondBid.ad).to.equal('This is an Ad#2'); + expect(secondBid.ttl).to.equal(20); + expect(secondBid.currency).to.equal('USD'); }); it('Verify full passback', function () { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 4688f0f5d32e..a3e53c682945 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -316,7 +316,7 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); - expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); + expect(request.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); let expectedQuery = { 'account_id': '14062', @@ -375,6 +375,42 @@ describe('the rubicon adapter', function () { expect(data['p_pos']).to.equal(undefined); }); + it('should correctly send p_pos in sra fashion', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'rubicon.singleRequest': true + }; + return config[key]; + }); + // first one is atf + var sraPosRequest = utils.deepClone(bidderRequest); + + // second is not present + const bidCopy = utils.deepClone(sraPosRequest.bids[0]); + delete bidCopy.params.position; + sraPosRequest.bids.push(bidCopy); + + // third is btf + const bidCopy1 = utils.deepClone(sraPosRequest.bids[0]); + bidCopy1.params.position = 'btf'; + sraPosRequest.bids.push(bidCopy1); + + // fourth is invalid (aka not atf or btf) + const bidCopy2 = utils.deepClone(sraPosRequest.bids[0]); + bidCopy2.params.position = 'unknown'; + sraPosRequest.bids.push(bidCopy2); + + // fifth is not present + const bidCopy3 = utils.deepClone(sraPosRequest.bids[0]); + delete bidCopy3.params.position; + sraPosRequest.bids.push(bidCopy3); + + let [request] = spec.buildRequests(sraPosRequest.bids, sraPosRequest); + let data = parseQuery(request.data); + + expect(data['p_pos']).to.equal('atf;;btf;;'); + }); + it('ad engine query params should be ordered correctly', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -418,7 +454,7 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); - expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); + expect(request.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); // test that all values above are both present and correct Object.keys(expectedQuery).forEach(key => { @@ -434,7 +470,7 @@ describe('the rubicon adapter', function () { [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); data = parseQuery(request.data); - expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); + expect(request.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); // test that all values above are both present and correct Object.keys(expectedQuery).forEach(key => { @@ -931,7 +967,7 @@ describe('the rubicon adapter', function () { expect(item).to.have.property('bidRequest'); expect(item.method).to.equal('GET'); - expect(item.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); + expect(item.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); expect(item.data).to.be.a('string'); // 'bidRequest' type must be 'array' if SRA enabled diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index dc536846ae2a..a6bf88cfc74b 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -19,87 +19,222 @@ describe('SonobiBidAdapter', function () { }) describe('.isBidRequestValid', function () { - let bid = { - 'bidder': 'sonobi', - 'params': { - 'ad_unit': '/7780971/sparks_prebid_MR', - 'sizes': [[300, 250], [300, 600]], - 'floor': '1' - }, - '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 true when bid.params.placement_id and bid.params.sizes are found', function () { - let bid = Object.assign({}, bid) - delete bid.params - delete bid.sizes - bid.params = { - 'placement_id': '1a2b3c4d5e6f1a2b3c4d', - 'sizes': [[300, 250], [300, 600]], - } + it('should return false if there are no params', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) + it('should return false if there is no placement_id param and no ad_unit param', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placementId: '1a2b3c4d5e6f1a2b3c4d', + }, + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - it('should return true when bid.params.placement_id and bid.sizes are found', function () { - let bid = Object.assign({}, bid) - delete bid.params - bid.sizes = [[300, 250], [300, 600]] - bid.params = { - 'placement_id': '1a2b3c4d5e6f1a2b3c4d', - } + it('should return false if there is no mediaTypes', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) + it('should return true if the bid is valid', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); - it('should return true when bid.params.ad_unit and bid.params.sizes are found', function () { - let bid = Object.assign({}, bid) - delete bid.params - delete bid.sizes - bid.params = { - 'ad_unit': '/7780971/sparks_prebid_MR', - 'sizes': [[300, 250], [300, 600]], - } + describe('banner', () => { + it('should return false if there are no banner sizes and no param sizes', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + banner: { - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - it('should return true when bid.params.ad_unit and bid.sizes are found', function () { - let bid = Object.assign({}, bid) - delete bid.params - bid.sizes = [[300, 250], [300, 600]] - bid.params = { - 'ad_unit': '/7780971/sparks_prebid_MR', - } + it('should return true if there is banner sizes and no param sizes', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d' + }, + 'mediaTypes': { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); - expect(spec.isBidRequestValid(bid)).to.equal(true) - }) + it('should return true if there is param sizes and no banner sizes', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d', + sizes: [[300, 250], [300, 600]] + }, + 'mediaTypes': { + banner: { + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); - it('should return false when no params are found', function () { - let bid = Object.assign({}, bid) - delete bid.params - expect(spec.isBidRequestValid(bid)).to.equal(false) - }) + describe('video', () => { + describe('instream', () => { + it('should return false if there is no playerSize defined in the video mediaType', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d', + sizes: [[300, 250], [300, 600]] + }, + 'mediaTypes': { + video: { + context: 'instream' + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true if there is playerSize defined on the video mediaType', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d', + }, + 'mediaTypes': { + video: { + context: 'instream', + playerSize: [300, 250] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); - it('should return false when bid.params.placement_id and bid.params.ad_unit are not found', function () { - let bid = Object.assign({}, bid) - delete bid.params - bid.params = { - 'placement_id': 0, - 'ad_unit': 0, - 'sizes': [[300, 250], [300, 600]], - } - expect(spec.isBidRequestValid(bid)).to.equal(false) - }) - }) + describe('outstream', () => { + it('should return false if there is no param sizes', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d', + }, + 'mediaTypes': { + video: { + context: 'outstream', + playerSize: [300, 250] + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true if there is param sizes', () => { + const bid = { + 'bidder': 'sonobi', + 'adUnitCode': 'adunit-code', + params: { + placement_id: '1a2b3c4d5e6f1a2b3c4d', + sizes: [300, 250] + + }, + 'mediaTypes': { + video: { + context: 'outstream' + } + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + }); + }); describe('.buildRequests', function () { beforeEach(function() { diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index 6c6a551b7beb..d1662e162aa5 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -183,21 +183,7 @@ describe('the spotx adapter', function () { expect(request.data.imp.bidfloor).to.equal(123); expect(request.data.ext).to.deep.equal({ number_of_ads: 2, - wrap_response: 1, - source: { - ext: { - schain: { - complete: 1, - nodes: [ - { - asi: 'indirectseller.com', - sid: '00001', - hp: 1 - } - ] - } - } - } + wrap_response: 1 }); expect(request.data.user.ext).to.deep.equal({ consented_providers_settings: GOOGLE_CONSENT, @@ -209,6 +195,21 @@ describe('the spotx adapter', function () { }], fpc: 'pubcid_1' }) + + expect(request.data.source).to.deep.equal({ + ext: { + schain: { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + } + } + }) }); it('should process premarket bids', function() { diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 190f463f7a55..12a2f66dde28 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -56,6 +56,20 @@ describe('triplelift adapter', function () { describe('buildRequests', function () { let bidRequests; let bidderRequest; + const schain = { + validation: 'strict', + config: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1, + } + ] + } + }; this.beforeEach(() => { bidRequests = [ @@ -71,6 +85,7 @@ describe('triplelift adapter', function () { bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', userId: {}, + schain, } ]; @@ -220,6 +235,17 @@ describe('triplelift adapter', function () { expect(url).to.match(new RegExp('(?:' + prebid.version + ')')) expect(url).to.match(/(?:referrer)/); }); + it('should return schain when present', function() { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const { data: payload } = request; + expect(payload.ext.schain).to.deep.equal(schain); + }); + it('should not create root level ext when schain is not present', function() { + bidRequests[0].schain = undefined; + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const { data: payload } = request; + expect(payload.ext).to.deep.equal(undefined); + }); }); describe('interpretResponse', function () { @@ -280,7 +306,7 @@ describe('triplelift adapter', function () { expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); - it('should return multile responses to support SRA', function () { + it('should return multiple responses to support SRA', function () { let response = { body: { bids: [ diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 32daf5ecb963..9618aa8df696 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -12,6 +12,20 @@ const validBannerBidReq = { sizes: [[300, 250]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + } }; const invalidBannerBidReq = { @@ -114,6 +128,7 @@ describe('ucfunnel Adapter', function () { const [ width, height ] = validBannerBidReq.sizes[0]; expect(data.w).to.equal(width); expect(data.h).to.equal(height); + expect(data.schain).to.equal('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com'); }); it('must parse bid size from a nested array', function () { diff --git a/test/spec/modules/viewdeosDXBidAdapter_spec.js b/test/spec/modules/viewdeosDXBidAdapter_spec.js new file mode 100644 index 000000000000..80082347687f --- /dev/null +++ b/test/spec/modules/viewdeosDXBidAdapter_spec.js @@ -0,0 +1,308 @@ +import {expect} from 'chai'; +import {spec} from 'modules/viewdeosDXBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//hb.sync.viewdeos.com/auction/'; + +const DISPLAY_REQUEST = { + 'bidder': 'viewdeos', + 'params': { + 'aid': 12345 + }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d', + 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}} +}; + +const VIDEO_REQUEST = { + 'bidder': 'viewdeos', + 'params': { + 'aid': 12345 + }, + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d', + 'mediaTypes': {'video': {'playerSize': [480, 360], 'context': 'instream'}} +}; + +const SERVER_VIDEO_RESPONSE = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'vastUrl': 'http://rtb.sync.viewdeos.com/vast/?adid=44F2AEB9BFC881B3', + 'requestId': '2e41f65424c87c', + 'url': '44F2AEB9BFC881B3', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 480, + 'cur': 'USD', + 'width': 640, + 'cpm': 0.9 + } + ] +}; + +const SERVER_DISPLAY_RESPONSE = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'ad': '', + 'requestId': '2e41f65424c87c', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 250, + 'cur': 'USD', + 'width': 300, + 'cpm': 0.9 + }], + 'cookieURLs': ['link1', 'link2'] +}; +const SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'ad': '', + 'requestId': '2e41f65424c87c', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 250, + 'cur': 'USD', + 'width': 300, + 'cpm': 0.9 + }], + 'cookieURLs': ['link3', 'link4'], + 'cookieURLSTypes': ['image', 'iframe'] +}; + +const videoBidderRequest = { + bidderCode: 'bidderCode', + bids: [{mediaTypes: {video: {}}, bidId: '2e41f65424c87c'}] +}; + +const displayBidderRequest = { + bidderCode: 'bidderCode', + bids: [{bidId: '2e41f65424c87c'}] +}; + +const displayBidderRequestWithGdpr = { + bidderCode: 'bidderCode', + bids: [{bidId: '2e41f65424c87c'}], + gdprConsent: { + gdprApplies: true, + consentString: 'test' + } +}; + +const videoEqResponse = [{ + vastUrl: 'http://rtb.sync.viewdeos.com/vast/?adid=44F2AEB9BFC881B3', + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'video', + netRevenue: true, + currency: 'USD', + height: 480, + width: 640, + ttl: 3600, + cpm: 0.9 +}]; + +const displayEqResponse = [{ + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'display', + netRevenue: true, + currency: 'USD', + ad: '', + height: 250, + width: 300, + ttl: 3600, + cpm: 0.9 +}]; + +describe('viewdeosDXBidAdapter', function () { // todo remove only + const adapter = newBidder(spec); + + describe('user syncs as image', function () { + it('should be returned if pixel enabled', function () { + const syncs = spec.getUserSyncs({pixelEnabled: true}, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs.map(s => s.url)).to.deep.equal([SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs[0]]); + expect(syncs.map(s => s.type)).to.deep.equal(['image']); + }) + }) + + describe('user syncs as iframe', function () { + it('should be returned if iframe enabled', function () { + const syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs.map(s => s.url)).to.deep.equal([SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs[1]]); + expect(syncs.map(s => s.type)).to.deep.equal(['iframe']); + }) + }) + + describe('user syncs with both types', function () { + it('should be returned if pixel and iframe enabled', function () { + const syncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs.map(s => s.url)).to.deep.equal(SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLs); + expect(syncs.map(s => s.type)).to.deep.equal(SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS.cookieURLSTypes); + }) + }) + + describe('user syncs', function () { + it('should not be returned if pixel not set', function () { + const syncs = spec.getUserSyncs({}, [{body: SERVER_DISPLAY_RESPONSE_WITH_MIXED_SYNCS}]); + + expect(syncs).to.be.empty; + }) + }) + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(VIDEO_REQUEST)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, VIDEO_REQUEST); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let videoBidRequests = [VIDEO_REQUEST]; + let displayBidRequests = [DISPLAY_REQUEST]; + let videoAndDisplayBidRequests = [DISPLAY_REQUEST, VIDEO_REQUEST]; + + const displayRequest = spec.buildRequests(displayBidRequests, {}); + const videoRequest = spec.buildRequests(videoBidRequests, {}); + const videoAndDisplayRequests = spec.buildRequests(videoAndDisplayBidRequests, {}); + + it('sends bid request to ENDPOINT via GET', function () { + expect(videoRequest.method).to.equal('GET'); + expect(displayRequest.method).to.equal('GET'); + expect(videoAndDisplayRequests.method).to.equal('GET'); + }); + + it('sends bid request to correct ENDPOINT', function () { + expect(videoRequest.url).to.equal(ENDPOINT); + expect(displayRequest.url).to.equal(ENDPOINT); + expect(videoAndDisplayRequests.url).to.equal(ENDPOINT); + }); + + it('sends correct video bid parameters', function () { + const bid = Object.assign({}, videoRequest.data); + delete bid.domain; + + const eq = { + callbackId: '84ab500420319d', + ad_type: 'video', + aid: 12345, + sizes: '480x360' + }; + + expect(bid).to.deep.equal(eq); + }); + + it('sends correct display bid parameters', function () { + const bid = Object.assign({}, displayRequest.data); + delete bid.domain; + + const eq = { + callbackId: '84ab500420319d', + ad_type: 'display', + aid: 12345, + sizes: '300x250,300x600' + }; + + expect(bid).to.deep.equal(eq); + }); + + it('sends correct video and display bid parameters', function () { + const bid = Object.assign({}, videoAndDisplayRequests.data); + delete bid.domain; + + const eq = { + callbackId: '84ab500420319d', + ad_type: 'display', + aid: 12345, + sizes: '300x250,300x600', + callbackId2: '84ab500420319d', + ad_type2: 'video', + aid2: 12345, + sizes2: '480x360' + }; + + expect(bid).to.deep.equal(eq); + }); + }); + + describe('interpretResponse', function () { + let serverResponse; + let bidderRequest; + let eqResponse; + + afterEach(function () { + serverResponse = null; + bidderRequest = null; + eqResponse = null; + }); + + it('should get correct video bid response', function () { + serverResponse = SERVER_VIDEO_RESPONSE; + bidderRequest = videoBidderRequest; + eqResponse = videoEqResponse; + + bidServerResponseCheck(); + }); + + it('should get correct display bid response', function () { + serverResponse = SERVER_DISPLAY_RESPONSE; + bidderRequest = displayBidderRequest; + eqResponse = displayEqResponse; + + bidServerResponseCheck(); + }); + + it('should set gdpr data correctly', function () { + const builtRequestData = spec.buildRequests([DISPLAY_REQUEST], displayBidderRequestWithGdpr); + + expect(builtRequestData.data.gdpr).to.be.equal(1); + expect(builtRequestData.data.gdpr_consent).to.be.equal(displayBidderRequestWithGdpr.gdprConsent.consentString); + }); + + function bidServerResponseCheck() { + const result = spec.interpretResponse({body: serverResponse}, {bidderRequest}); + + expect(result).to.deep.equal(eqResponse); + } + + function nobidServerResponseCheck() { + const noBidServerResponse = {bids: []}; + const noBidResult = spec.interpretResponse({body: noBidServerResponse}, {bidderRequest}); + + expect(noBidResult.length).to.equal(0); + } + + it('handles video nobid responses', function () { + bidderRequest = videoBidderRequest; + + nobidServerResponseCheck(); + }); + + it('handles display nobid responses', function () { + bidderRequest = displayBidderRequest; + + nobidServerResponseCheck(); + }); + }); +}); diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 09ce92479f97..167d54e37a60 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -45,7 +45,7 @@ describe('VisxAdapter', function () { referer: 'http://example.com' } }; - const encodedReferrer = encodeURIComponent(bidderRequest.refererInfo.referer); + const referrer = bidderRequest.refererInfo.referer; let bidRequests = [ { 'bidder': 'visx', @@ -86,7 +86,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests([bidRequests[0]], bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535'); expect(payload).to.have.property('sizes', '300x250,300x600'); @@ -98,7 +98,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -111,7 +111,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -124,7 +124,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -138,7 +138,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -149,16 +149,16 @@ describe('VisxAdapter', function () { it('should add currency from currency.bidderCurrencyDefault', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'currency.bidderCurrencyDefault.visx' ? 'JPY' : 'USD'); + arg => arg === 'currency.bidderCurrencyDefault.visx' ? 'GBP' : 'USD'); const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('cur', 'JPY'); + expect(payload).to.have.property('cur', 'GBP'); getConfigStub.restore(); }); @@ -168,7 +168,7 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.be.an('object'); - expect(payload).to.have.property('u', encodedReferrer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535,903535,903536'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -204,11 +204,11 @@ describe('VisxAdapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 903537, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 903537, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, {'seat': '1'}, @@ -346,7 +346,7 @@ describe('VisxAdapter', function () { 'auctionId': '1cbd2feafe5e8b', } ]; - const getConfigStub = sinon.stub(config, 'getConfig').returns('JPY'); + const getConfigStub = sinon.stub(config, 'getConfig').returns('PLN'); const request = spec.buildRequests(bidRequests); const expectedResponse = [ { @@ -358,13 +358,15 @@ describe('VisxAdapter', function () { 'height': 250, 'ad': '
test content 1
', 'bidderCode': 'visx', - 'currency': 'JPY', + 'currency': 'PLN', 'netRevenue': true, 'ttl': 360, } ]; - const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); + const response = Object.assign({}, responses[0]); + Object.assign(response.bid[0], {'cur': 'PLN'}); + const result = spec.interpretResponse({'body': {'seatbid': [response]}}, request); expect(result).to.deep.equal(expectedResponse); getConfigStub.restore(); }); @@ -412,11 +414,11 @@ describe('VisxAdapter', function () { it('complicated case', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 903535, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 903536, 'h': 600, 'w': 350}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903536, 'h': 600, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 903535, 'h': 90, 'w': 728, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 903535, 'h': 600, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 903536, 'h': 600, 'w': 350, 'cur': 'EUR'}], 'seat': '1'}, ]; const bidRequests = [ { @@ -550,8 +552,8 @@ describe('VisxAdapter', function () { it('dublicate uids and sizes in one slot', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 903535, 'h': 250, 'w': 300, 'cur': 'EUR'}], 'seat': '1'}, ]; const bidRequests = [ { diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js new file mode 100644 index 000000000000..7b37e393575c --- /dev/null +++ b/test/spec/modules/vrtcalBidAdapter_spec.js @@ -0,0 +1,135 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/vrtcalBidAdapter'; + +describe('Vrtcal Adapter', function () { + let bid = { + bidId: 'bidID0001', + bidder: 'vrtcal', + bidderRequestId: 'brID0001', + auctionId: 'auID0001', + sizes: [[300, 250]], + transactionId: 'tid0001', + adUnitCode: 'vrtcal-test-adunit' + }; + + describe('isBidRequestValid', function () { + it('Should return true when base params as set', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false when bid.bidId is blank', function () { + bid.bidId = ''; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + it('Should return false when bid.auctionId is blank', function () { + bid.auctionId = ''; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequests = spec.buildRequests([bid]); + + let serverRequest = serverRequests[0]; + + 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('https://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804'); + }); + + it('Returns valid data if array of bids is valid', function () { + let data = JSON.parse(serverRequest.data); + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('prebidJS', 'prebidAdUnitCode', 'id', 'imp', 'site', 'device'); + expect(data.prebidJS).to.not.equal(''); + expect(data.prebidAdUnitCode).to.not.equal(''); + }); + + it('Sets width and height based on existence of bid.mediaTypes.banner', function () { + let data = JSON.parse(serverRequest.data); + if (typeof (bid.mediaTypes) !== 'undefined' && typeof (bid.mediaTypes.banner) !== 'undefined' && typeof (bid.mediaTypes.banner.sizes) !== 'undefined') { + expect(data.imp[0].banner.w).to.equal(bid.mediaTypes.banner.sizes[0][0]); + expect(data.imp[0].banner.h).to.equal(bid.mediaTypes.banner.sizes[0][1]); + } else { + expect(data.imp[0].banner.w).to.equal(bid.sizes[0][0]); + expect(data.imp[0].banner.h).to.equal(bid.sizes[0][1]); + } + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequests = spec.buildRequests([]); + expect(serverRequests).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + let bid = { + bidId: 'bidID0001', + bidder: 'vrtcal', + bidderRequestId: 'brID0001', + auctionId: 'auID0001', + sizes: [[300, 250]], + transactionId: 'tid0001', + adUnitCode: 'vrtcal-test-adunit' + }; + + let serverRequests = spec.buildRequests([bid]); + + let resObject = {body: {id: 'vrtcal-test-id', width: 300, height: 250, seatbid: [{bid: [{price: 3.0, w: 300, h: 250, crid: 'testcrid', adm: 'testad', nurl: 'https://vrtcal.com/faketracker'}]}], currency: 'USD', netRevenue: true, ttl: 900}}; + + let serverResponses = spec.interpretResponse(resObject, serverRequests); + + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'nurl'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.nurl).to.be.a('string'); + } + + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + + describe('onBidWon', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'vrtcal', + bidderRequestId: '145e1d6a7837c9', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', + adUnitCode: 'vrtcal-test-adunit' + }; + + let serverRequests = spec.buildRequests([bid]); + let resObject = {body: {id: 'vrtcal-test-id', width: 300, height: 250, seatbid: [{bid: [{price: 3.0, w: 300, h: 250, crid: 'testcrid', adm: 'testad', nurl: 'https://vrtcal.com/faketracker'}]}], currency: 'USD', netRevenue: true, ttl: 900}}; + let serverResponses = spec.interpretResponse(resObject, serverRequests); + let wonbid = serverResponses[0]; + + it('Returns true is nurl is good/not blank', function () { + expect(wonbid.nurl).to.not.equal(''); + expect(spec.onBidWon(wonbid)).to.be.true; + }); + }); +}); diff --git a/test/spec/modules/vubleBidAdapter_spec.js b/test/spec/modules/vubleBidAdapter_spec.js index 8996c1b49575..b38ad8f85840 100644 --- a/test/spec/modules/vubleBidAdapter_spec.js +++ b/test/spec/modules/vubleBidAdapter_spec.js @@ -280,4 +280,56 @@ describe('VubleAdapter', function () { expect(adapter.getUserSyncs(syncOptions, [response])).to.deep.equal([result]); }) }); + + describe('Check outstream scenario with renderer', function () { + // bid Request + let bid = { + data: { + context: 'outstream', + env: 'net', + width: '640', + height: '360', + pub_id: '3', + zone_id: '12345', + bid_id: 'abdc', + floor_price: 5.50, // optional + adUnitCode: 'code' + }, + method: 'POST', + url: '//player.mediabong.net/prebid/request' + }; + // Server's response + let response = { + body: { + status: 'ok', + cpm: 5.00, + creativeId: '2468', + url: 'https//player.mediabong.net/prebid/ad/a1b2c3d4', + dealId: 'MDB-TEST-1357', + renderer_id: 0, + renderer_url: 'vuble_renderer.js', + content: 'test' + } + }; + + let adResponse = { + ad: { + video: { + content: 'test' + } + } + }; + let adUnitCode = 'code'; + let rendererUrl = 'vuble_renderer.js'; + let rendererId = 0; + + let formattedResponses = adapter.interpretResponse(response, bid); + it('should equal to the expected format result', function () { + expect(formattedResponses[0].adResponse).to.deep.equal(adResponse); + expect(formattedResponses[0].adUnitCode).to.deep.equal(adUnitCode); + expect(formattedResponses[0].renderer.url).to.equal(rendererUrl); + expect(formattedResponses[0].renderer.id).to.equal(rendererId); + expect(formattedResponses[0].renderer.render).to.exist.and.to.be.a('function'); + }); + }); });