diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 5adba83e9df..c76bd1ccfd6 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -1,341 +1,349 @@ -const utils = require('src/utils.js'); -const ajax = require('src/ajax.js').ajax; -const bidfactory = require('src/bidfactory.js'); -const bidmanager = require('src/bidmanager.js'); -const constants = require('src/constants.json'); -const adaptermanager = require('src/adaptermanager'); -const BaseAdapter = require('src/adapter').default; +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; +import constants from 'src/constants.json'; const AOL_BIDDERS_CODES = { - aol: 'aol', - onemobile: 'onemobile', - onedisplay: 'onedisplay' + AOL: 'aol', + ONEMOBILE: 'onemobile', + ONEDISPLAY: 'onedisplay' }; +const AOL_ENDPOINTS = { + DISPLAY: { + GET: 'display-get' + }, + MOBILE: { + GET: 'mobile-get', + POST: 'mobile-post' + } +}; + +const SYNC_TYPES = { + IFRAME: { + TAG: 'iframe', + TYPE: 'iframe' + }, + IMAGE: { + TAG: 'img', + TYPE: 'image' + } +}; + +const pubapiTemplate = template`//${'host'}/pubapi/3.0/${'network'}/${'placement'}/${'pageid'}/${'sizeid'}/ADTECH;v=2;cmd=bid;cors=yes;alias=${'alias'}${'bidfloor'}${'keyValues'};misc=${'misc'}`; +const nexageBaseApiTemplate = template`//${'host'}/bidRequest?`; +const nexageGetApiTemplate = template`dcn=${'dcn'}&pos=${'pos'}&cmd=bid${'ext'}`; +const MP_SERVER_MAP = { + us: 'adserver-us.adtech.advertising.com', + eu: 'adserver-eu.adtech.advertising.com', + as: 'adserver-as.adtech.advertising.com' +}; +const NEXAGE_SERVER = 'hb.nexage.com'; +const BID_RESPONSE_TTL = 300; + $$PREBID_GLOBAL$$.aolGlobals = { pixelsDropped: false }; -const AolAdapter = function AolAdapter() { - let showCpmAdjustmentWarning = true; - const pubapiTemplate = template`${'protocol'}://${'host'}/pubapi/3.0/${'network'}/${'placement'}/${'pageid'}/${'sizeid'}/ADTECH;v=2;cmd=bid;cors=yes;alias=${'alias'}${'bidfloor'};misc=${'misc'}`; - const nexageBaseApiTemplate = template`${'protocol'}://${'host'}/bidRequest?`; - const nexageGetApiTemplate = template`dcn=${'dcn'}&pos=${'pos'}&cmd=bid${'ext'}`; - const MP_SERVER_MAP = { - us: 'adserver-us.adtech.advertising.com', - eu: 'adserver-eu.adtech.advertising.com', - as: 'adserver-as.adtech.advertising.com' +let showCpmAdjustmentWarning = (function () { + let showCpmWarning = true; + + return function () { + let bidderSettings = $$PREBID_GLOBAL$$.bidderSettings; + if (showCpmWarning && bidderSettings && bidderSettings.aol && + typeof bidderSettings.aol.bidCpmAdjustment === 'function') { + utils.logWarn( + 'bidCpmAdjustment is active for the AOL adapter. ' + + 'As of Prebid 0.14, AOL can bid in net – please contact your accounts team to enable.' + ); + showCpmWarning = false; // warning is shown at most once + } }; - const NEXAGE_SERVER = 'hb.nexage.com'; - const SYNC_TYPES = { - iframe: 'IFRAME', - img: 'IMG' +})(); + +function template(strings, ...keys) { + return function(...values) { + let dict = values[values.length - 1] || {}; + let result = [strings[0]]; + keys.forEach(function(key, i) { + let value = Number.isInteger(key) ? values[key] : dict[key]; + result.push(value, strings[i + 1]); + }); + return result.join(''); }; - - let domReady = (() => { - let readyEventFired = false; - return fn => { - let idempotentFn = () => { - if (readyEventFired) { - return; +} + +function isSecureProtocol() { + return document.location.protocol === 'https:'; +} + +function parsePixelItems(pixels) { + let itemsRegExp = /(img|iframe)[\s\S]*?src\s*=\s*("|')(.*?)\2/gi; + let tagNameRegExp = /\w*(?=\s)/; + let srcRegExp = /src=("|')(.*?)\1/; + let pixelsItems = []; + + if (pixels) { + let matchedItems = pixels.match(itemsRegExp); + if (matchedItems) { + matchedItems.forEach(item => { + let tagName = item.match(tagNameRegExp)[0]; + let url = item.match(srcRegExp)[2]; + + if (tagName && tagName) { + pixelsItems.push({ + type: tagName === SYNC_TYPES.IMAGE.TAG ? SYNC_TYPES.IMAGE.TYPE : SYNC_TYPES.IFRAME.TYPE, + url: url + }); } - readyEventFired = true; - return fn(); - }; - - if (document.readyState === 'complete') { - return idempotentFn(); - } - - document.addEventListener('DOMContentLoaded', idempotentFn, false); - window.addEventListener('load', idempotentFn, false); - }; - })(); - - function dropSyncCookies(pixels) { - if (!$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped) { - let pixelElements = parsePixelItems(pixels); - renderPixelElements(pixelElements); - $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; + }); } } - function parsePixelItems(pixels) { - let itemsRegExp = /(img|iframe)[\s\S]*?src\s*=\s*("|')(.*?)\2/gi; - let tagNameRegExp = /\w*(?=\s)/; - let srcRegExp = /src=("|')(.*?)\1/; - let pixelsItems = []; - - if (pixels) { - let matchedItems = pixels.match(itemsRegExp); - if (matchedItems) { - matchedItems.forEach(item => { - let tagNameMatches = item.match(tagNameRegExp); - let sourcesPathMatches = item.match(srcRegExp); - if (tagNameMatches && sourcesPathMatches) { - pixelsItems.push({ - tagName: tagNameMatches[0].toUpperCase(), - src: sourcesPathMatches[2] - }); - } - }); - } - } + return pixelsItems; +} - return pixelsItems; - } +function _buildMarketplaceUrl(bid) { + const params = bid.params; + const serverParam = params.server; + let regionParam = params.region || 'us'; + let server; - function renderPixelElements(pixelsElements) { - pixelsElements.forEach((element) => { - switch (element.tagName) { - case SYNC_TYPES.img: - return renderPixelImage(element); - case SYNC_TYPES.iframe: - return renderPixelIframe(element); - } - }); + if (!MP_SERVER_MAP.hasOwnProperty(regionParam)) { + utils.logWarn(`Unknown region '${regionParam}' for AOL bidder.`); + regionParam = 'us'; // Default region. } - function renderPixelImage(pixelsItem) { - let image = new Image(); - image.src = pixelsItem.src; + if (serverParam) { + server = serverParam; + } else { + server = MP_SERVER_MAP[regionParam]; } - function renderPixelIframe(pixelsItem) { - let iframe = document.createElement('iframe'); - iframe.width = 1; - iframe.height = 1; - iframe.style.display = 'none'; - iframe.src = pixelsItem.src; - if (document.readyState === 'interactive' || - document.readyState === 'complete') { - document.body.appendChild(iframe); - } else { - domReady(() => { - document.body.appendChild(iframe); - }); - } - } + // Set region param, used by AOL analytics. + params.region = regionParam; + + return pubapiTemplate({ + host: server, + network: params.network, + placement: parseInt(params.placement), + pageid: params.pageId || 0, + sizeid: params.sizeId || 0, + alias: params.alias || utils.getUniqueIdentifierStr(), + bidfloor: formatMarketplaceBidFloor(params.bidFloor), + keyValues: formatMarketplaceKeyValues(params.keyValues), + misc: new Date().getTime() // cache busting + }); +} - function template(strings, ...keys) { - return function(...values) { - let dict = values[values.length - 1] || {}; - let result = [strings[0]]; - keys.forEach(function(key, i) { - let value = Number.isInteger(key) ? values[key] : dict[key]; - result.push(value, strings[i + 1]); - }); - return result.join(''); - }; - } +function formatMarketplaceBidFloor(bidFloor) { + return (typeof bidFloor !== 'undefined') ? `;bidfloor=${bidFloor.toString()}` : ''; +} - function _buildMarketplaceUrl(bid) { - const params = bid.params; - const serverParam = params.server; - let regionParam = params.region || 'us'; - let server; +function formatMarketplaceKeyValues(keyValues) { + let formattedKeyValues = ''; - if (!MP_SERVER_MAP.hasOwnProperty(regionParam)) { - utils.logWarn(`Unknown region '${regionParam}' for AOL bidder.`); - regionParam = 'us'; // Default region. - } + utils._each(keyValues, (value, key) => { + formattedKeyValues += `;kv${key}=${encodeURIComponent(value)}`; + }); - if (serverParam) { - server = serverParam; - } else { - server = MP_SERVER_MAP[regionParam]; - } + return formattedKeyValues; +} - // Set region param, used by AOL analytics. - params.region = regionParam; - - return pubapiTemplate({ - protocol: (document.location.protocol === 'https:') ? 'https' : 'http', - host: server, - network: params.network, - placement: parseInt(params.placement), - pageid: params.pageId || 0, - sizeid: params.sizeId || 0, - alias: params.alias || utils.getUniqueIdentifierStr(), - bidfloor: (typeof params.bidFloor !== 'undefined') - ? `;bidfloor=${params.bidFloor.toString()}` : '', - misc: new Date().getTime() // cache busting +function _buildOneMobileBaseUrl(bid) { + return nexageBaseApiTemplate({ + host: bid.params.host || NEXAGE_SERVER + }); +} + +function _buildOneMobileGetUrl(bid) { + let {dcn, pos} = bid.params; + let nexageApi = _buildOneMobileBaseUrl(bid); + if (dcn && pos) { + let ext = ''; + if (isSecureProtocol()) { + bid.params.ext = bid.params.ext || {}; + bid.params.ext.secure = 1; + } + utils._each(bid.params.ext, (value, key) => { + ext += `&${key}=${encodeURIComponent(value)}`; }); + nexageApi += nexageGetApiTemplate({dcn, pos, ext}); } + return nexageApi; +} - function _buildNexageApiUrl(bid) { - let {dcn, pos} = bid.params; - let isSecure = (document.location.protocol === 'https:'); - let nexageApi = nexageBaseApiTemplate({ - protocol: isSecure ? 'https' : 'http', - host: bid.params.host || NEXAGE_SERVER - }); - if (dcn && pos) { - let ext = ''; - if (isSecure) { - bid.params.ext = bid.params.ext || {}; - bid.params.ext.secure = 1; - } - utils._each(bid.params.ext, (value, key) => { - ext += `&${key}=${encodeURIComponent(value)}`; - }); - nexageApi += nexageGetApiTemplate({dcn, pos, ext}); - } - return nexageApi; - } +function _parseBidResponse(response, bidRequest) { + let bidData; - function _addErrorBidResponse(bid, response = {}) { - const bidResponse = bidfactory.createBid(2, bid); - bidResponse.bidderCode = bid.bidder; - bidResponse.reason = response.nbr; - bidResponse.raw = response; - bidmanager.addBidResponse(bid.placementCode, bidResponse); + try { + bidData = response.seatbid[0].bid[0]; + } catch (e) { + return; } - function _addBidResponse(bid, response) { - let bidData; + let cpm; - try { - bidData = response.seatbid[0].bid[0]; - } catch (e) { - _addErrorBidResponse(bid, response); - return; - } - - let cpm; + if (bidData.ext && bidData.ext.encp) { + cpm = bidData.ext.encp; + } else { + cpm = bidData.price; - if (bidData.ext && bidData.ext.encp) { - cpm = bidData.ext.encp; - } else { - cpm = bidData.price; - - if (cpm === null || isNaN(cpm)) { - utils.logError('Invalid price in bid response', AOL_BIDDERS_CODES.aol, bid); - _addErrorBidResponse(bid, response); - return; - } + if (cpm === null || isNaN(cpm)) { + utils.logError('Invalid price in bid response', AOL_BIDDERS_CODES.AOL, bid); + return; } + } - let ad = bidData.adm; - if (response.ext && response.ext.pixels) { - if (bid.params.userSyncOn === constants.EVENTS.BID_RESPONSE) { - dropSyncCookies(response.ext.pixels); - } else { - let formattedPixels = response.ext.pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, ''); - - ad += ''; - } - } + let ad = bidData.adm; + if (response.ext && response.ext.pixels) { + if (config.getConfig('aol.userSyncOn') !== constants.EVENTS.BID_RESPONSE) { + let formattedPixels = response.ext.pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, ''); - const bidResponse = bidfactory.createBid(1, bid); - bidResponse.bidderCode = bid.bidder; - bidResponse.ad = ad; - bidResponse.cpm = cpm; - bidResponse.width = bidData.w; - bidResponse.height = bidData.h; - bidResponse.creativeId = bidData.crid; - bidResponse.pubapiId = response.id; - bidResponse.currencyCode = response.cur; - if (bidData.dealid) { - bidResponse.dealId = bidData.dealid; + ad += ''; } - - bidmanager.addBidResponse(bid.placementCode, bidResponse); } - function _isMarketplaceBidder(bidder) { - return bidder === AOL_BIDDERS_CODES.aol || bidder === AOL_BIDDERS_CODES.onedisplay; - } + return { + bidderCode: bidRequest.bidderCode, + requestId: bidRequest.bidId, + ad: ad, + cpm: cpm, + width: bidData.w, + height: bidData.h, + creativeId: bidData.crid, + pubapiId: response.id, + currency: response.cur, + dealId: bidData.dealid, + netRevenue: true, + ttl: BID_RESPONSE_TTL + }; +} - function _isNexageBidder(bidder) { - return bidder === AOL_BIDDERS_CODES.aol || bidder === AOL_BIDDERS_CODES.onemobile; - } +function _isMarketplaceBidder(bidder) { + return bidder === AOL_BIDDERS_CODES.AOL || bidder === AOL_BIDDERS_CODES.ONEDISPLAY; +} + +function _isNexageBidder(bidder) { + return bidder === AOL_BIDDERS_CODES.AOL || bidder === AOL_BIDDERS_CODES.ONEMOBILE; +} - function _isNexageRequestPost(bid) { - if (_isNexageBidder(bid.bidder) && bid.params.id && bid.params.imp && bid.params.imp[0]) { - let imp = bid.params.imp[0]; - return imp.id && imp.tagid && - ((imp.banner && imp.banner.w && imp.banner.h) || +function _isNexageRequestPost(bid) { + if (_isNexageBidder(bid.bidder) && bid.params.id && bid.params.imp && bid.params.imp[0]) { + let imp = bid.params.imp[0]; + return imp.id && imp.tagid && + ((imp.banner && imp.banner.w && imp.banner.h) || (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration)); - } } - - function _isNexageRequestGet(bid) { - return _isNexageBidder(bid.bidder) && bid.params.dcn && bid.params.pos; +} + +function _isNexageRequestGet(bid) { + return _isNexageBidder(bid.bidder) && bid.params.dcn && bid.params.pos; +} + +function isMarketplaceBid(bid) { + return _isMarketplaceBidder(bid.bidder) && bid.params.placement && bid.params.network; +} + +function isMobileBid(bid) { + return _isNexageRequestGet(bid) || _isNexageRequestPost(bid); +} + +function resolveEndpointCode(bid) { + if (_isNexageRequestGet(bid)) { + return AOL_ENDPOINTS.MOBILE.GET; + } else if (_isNexageRequestPost(bid)) { + return AOL_ENDPOINTS.MOBILE.POST; + } else if (isMarketplaceBid(bid)) { + return AOL_ENDPOINTS.DISPLAY.GET; } +} - function _isMarketplaceRequest(bid) { - return _isMarketplaceBidder(bid.bidder) && bid.params.placement && bid.params.network; - } +function formatBidRequest(endpointCode, bid) { + let bidRequest; + + switch (endpointCode) { + case AOL_ENDPOINTS.DISPLAY.GET: + bidRequest = { + url: _buildMarketplaceUrl(bid), + method: 'GET' + }; + break; - function _callBids(params) { - utils._each(params.bids, bid => { - let apiUrl; - let data = null; - let options = { - withCredentials: true + case AOL_ENDPOINTS.MOBILE.GET: + bidRequest = { + url: _buildOneMobileGetUrl(bid), + method: 'GET' }; - let isNexageRequestPost = _isNexageRequestPost(bid); - let isNexageRequestGet = _isNexageRequestGet(bid); - let isMarketplaceRequest = _isMarketplaceRequest(bid); - - if (isNexageRequestGet || isNexageRequestPost) { - apiUrl = _buildNexageApiUrl(bid); - if (isNexageRequestPost) { - data = bid.params; - options.customHeaders = { + break; + + case AOL_ENDPOINTS.MOBILE.POST: + bidRequest = { + url: _buildOneMobileBaseUrl(bid), + method: 'POST', + data: bid.params, + options: { + contentType: 'application/json', + customHeaders: { 'x-openrtb-version': '2.2' - }; - options.method = 'POST'; - options.contentType = 'application/json'; + } } - } else if (isMarketplaceRequest) { - apiUrl = _buildMarketplaceUrl(bid); - } + }; + break; + } - if (apiUrl) { - ajax(apiUrl, response => { - // Needs to be here in case bidderSettings are defined after requestBids() is called - if (showCpmAdjustmentWarning && - $$PREBID_GLOBAL$$.bidderSettings && $$PREBID_GLOBAL$$.bidderSettings.aol && - typeof $$PREBID_GLOBAL$$.bidderSettings.aol.bidCpmAdjustment === 'function' - ) { - utils.logWarn( - 'bidCpmAdjustment is active for the AOL adapter. ' + - 'As of Prebid 0.14, AOL can bid in net – please contact your accounts team to enable.' - ); - } - showCpmAdjustmentWarning = false; // warning is shown at most once + bidRequest.bidderCode = bid.bidder; + bidRequest.bidId = bid.bidId; + bidRequest.userSyncOn = bid.params.userSyncOn; - if (!response && response.length <= 0) { - utils.logError('Empty bid response', AOL_BIDDERS_CODES.aol, bid); - _addErrorBidResponse(bid, response); - return; - } + return bidRequest; +} - try { - response = JSON.parse(response); - } catch (e) { - utils.logError('Invalid JSON in bid response', AOL_BIDDERS_CODES.aol, bid); - _addErrorBidResponse(bid, response); - return; - } +function interpretResponse({body}, bidRequest) { + showCpmAdjustmentWarning(); - _addBidResponse(bid, response); - }, data, options); + if (!body) { + utils.logError('Empty bid response', bidRequest.bidderCode, body); + } else { + let bid = _parseBidResponse(body, bidRequest); + + if (bid) { + return bid; + } + } +} + +export const spec = { + code: AOL_BIDDERS_CODES.AOL, + aliases: [AOL_BIDDERS_CODES.ONEMOBILE, AOL_BIDDERS_CODES.ONEDISPLAY], + isBidRequestValid: function(bid) { + return isMarketplaceBid(bid) || isMobileBid(bid); + }, + buildRequests: function (bids) { + return bids.map(bid => { + const endpointCode = resolveEndpointCode(bid); + + if (endpointCode) { + return formatBidRequest(endpointCode, bid); } }); - } + }, + interpretResponse: interpretResponse, + getUserSyncs: function(options, bidResponses) { + let bidResponse = bidResponses[0]; - return Object.assign(this, new BaseAdapter(AOL_BIDDERS_CODES.aol), { - callBids: _callBids - }); -}; + if (config.getConfig('aol.userSyncOn') === constants.EVENTS.BID_RESPONSE) { + if (!$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped && bidResponse.ext && bidResponse.ext.pixels) { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; -adaptermanager.registerBidAdapter(new AolAdapter(), AOL_BIDDERS_CODES.aol); -adaptermanager.aliasBidAdapter(AOL_BIDDERS_CODES.aol, AOL_BIDDERS_CODES.onedisplay); -adaptermanager.aliasBidAdapter(AOL_BIDDERS_CODES.aol, AOL_BIDDERS_CODES.onemobile); + return parsePixelItems(bidResponse.ext.pixels); + } + } + + return []; + } +}; -module.exports = AolAdapter; +registerBidder(spec); diff --git a/modules/aolBidAdapter.md b/modules/aolBidAdapter.md new file mode 100644 index 00000000000..a92e933bd36 --- /dev/null +++ b/modules/aolBidAdapter.md @@ -0,0 +1,47 @@ +# Overview + +Module Name: AOL Bid Adapter + +Module Type: AOL Adapter + +Maintainer: hb-fe-tech@oath.com + +# Description + +Module that connects to AOL's demand sources + +# Test Parameters +```javascript + var adUnits = [ + { + code: 'test-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: 'onedisplay', + params: { + placement: '3611253', + network: '9599.1', + bidFloor: '0.80', + keyValues: { + test: 'key' + } + } + } + ] + }, + { + code: 'test-mobile-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: 'onemobile', + params: { + dcn: '2c9d2b50015a5aa95b70a9b0b5b10012', + pos: 'header' + } + } + ] + } + ]; +``` diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 0b4b0d7cd0c..7535d6ad613 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -228,11 +228,19 @@ export function newBidder(spec) { const onResponse = delayExecution(afterAllResponses, requests.length) requests.forEach(processRequest); + function formatGetParameters(data) { + if (data) { + return `?${typeof data === 'object' ? parseQueryStringParameters(data) : data}`; + } + + return ''; + } + function processRequest(request) { switch (request.method) { case 'GET': ajax( - `${request.url}?${typeof request.data === 'object' ? parseQueryStringParameters(request.data) : request.data}`, + `${request.url}${formatGetParameters(request.data)}`, { success: onSuccess, error: onFailure diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 188c5375b89..efa595ecc64 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import * as utils from 'src/utils'; -import AolAdapter from 'modules/aolBidAdapter'; -import bidmanager from 'src/bidmanager'; +import {spec} from 'modules/aolBidAdapter'; +import {config} from 'src/config'; let getDefaultBidResponse = () => { return { @@ -13,9 +13,10 @@ let getDefaultBidResponse = () => { impid: '245730051428950632', price: 0.09, adm: '', - crid: '0', + crid: 'creative-id', h: 90, w: 728, + dealid: 'deal-id', ext: {sizeid: 225} }] }] @@ -67,15 +68,16 @@ let getDefaultBidRequest = () => { }; }; +let getPixels = () => { + return ''; +}; + describe('AolAdapter', () => { const MARKETPLACE_URL = 'adserver-us.adtech.advertising.com/pubapi/3.0/'; const NEXAGE_URL = 'hb.nexage.com/bidRequest?'; - let adapter; - - beforeEach(() => adapter = new AolAdapter()); - - function createBidderRequest({bids, params} = {}) { + function createCustomBidRequest({bids, params} = {}) { var bidderRequest = getDefaultBidRequest(); if (bids && Array.isArray(bids)) { bidderRequest.bids = bids; @@ -86,620 +88,437 @@ describe('AolAdapter', () => { return bidderRequest; } - describe('callBids()', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); + describe('interpretResponse()', () => { + let bidderSettingsBackup; + let bidResponse; + let bidRequest; + let logWarnSpy; + + beforeEach(() => { + bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; + bidRequest = { + bidderCode: 'test-bidder-code', + bidId: 'bid-id' + }; + bidResponse = { + body: getDefaultBidResponse() + }; + logWarnSpy = sinon.spy(utils, 'logWarn'); }); - describe('bid request', () => { - describe('Marketplace api', () => { - let xhr; - let requests; - - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); - }); - - afterEach(() => xhr.restore()); - - it('requires parameters to be made', () => { - adapter.callBids({}); - expect(requests).to.be.empty; - }); - - it('should hit the Marketplace api endpoint with the Marketplace config', () => { - adapter.callBids(getDefaultBidRequest()); - expect(requests[0].url).to.contain(MARKETPLACE_URL); - }); - - it('should hit the Marketplace via onedisplay bidder code', () => { - let bidRequest = createBidderRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: getMarketplaceBidParams() - }); - - adapter.callBids(bidRequest); - expect(requests[0].url).to.contain(MARKETPLACE_URL); - }); - - it('should hit the Marketplace via onedisplay bidder code when Marketplace and Nexage params are present', () => { - let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams()); - let bidRequest = createBidderRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: bidParams - }); - - adapter.callBids(bidRequest); - expect(requests[0].url).to.contain(MARKETPLACE_URL); - }); - - it('should hit the Marketplace via onedisplay bidder code when Nexage params are present', () => { - let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams(), getNexagePostBidParams()); - let bidRequest = createBidderRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: bidParams - }); - - adapter.callBids(bidRequest); - expect(requests[0].url).to.contain(MARKETPLACE_URL); - }); - - it('should not resolve endpoint for onedisplay bidder code when only Nexage params are present', () => { - let bidParams = Object.assign(getNexageGetBidParams(), getNexagePostBidParams()); - - adapter.callBids(createBidderRequest({ - bids: [{ - bidder: 'onedisplay' - }], - params: bidParams - })); - expect(requests.length).to.equal(0); - }); - - it('should hit endpoint based on the region config option', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - region: 'eu' - } - })); - expect(requests[0].url).to.contain('adserver-eu.adtech.advertising.com/pubapi/3.0/'); - }); - - it('should hit the default endpoint in case of unknown region config option', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - region: 'an' - } - })); - expect(requests[0].url).to.contain(MARKETPLACE_URL); - }); - - it('should hit endpoint based on the server config option', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - server: 'adserver-eu.adtech.advertising.com' - } - })); - expect(requests[0].url).to.contain('adserver-eu.adtech.advertising.com/pubapi/3.0/'); - }); - - it('should be the pubapi bid request', () => { - adapter.callBids(getDefaultBidRequest()); - expect(requests[0].url).to.contain('cmd=bid;'); - }); - - it('should be the version 2 of pubapi', () => { - adapter.callBids(getDefaultBidRequest()); - expect(requests[0].url).to.contain('v=2;'); - }); - - it('should contain cache busting', () => { - adapter.callBids(getDefaultBidRequest()); - expect(requests[0].url).to.match(/misc=\d+/); - }); - - it('should contain required params - placement & network', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1' - } - })); - expect(requests[0].url).to.contain('/pubapi/3.0/9599.1/1234567/'); - }); - - it('should contain pageId and sizeId of 0 if params are missing', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1' - } - })); - expect(requests[0].url).to.contain('/pubapi/3.0/9599.1/1234567/0/0/ADTECH;'); - }); - - it('should contain pageId optional param', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - pageId: 12345 - } - })); - expect(requests[0].url).to.contain('/pubapi/3.0/9599.1/1234567/12345/'); - }); - - it('should contain sizeId optional param', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - sizeId: 12345 - } - })); - expect(requests[0].url).to.contain('/12345/ADTECH;'); - }); - - it('should contain generated alias if alias param is missing', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1' - } - })); - expect(requests[0].url).to.match(/alias=\w+?;/); - }); - - it('should contain alias optional param', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - alias: 'desktop_articlepage_something_box_300_250' - } - })); - expect(requests[0].url).to.contain('alias=desktop_articlepage_something_box_300_250'); - }); - - it('should not contain bidfloor if bidFloor param is missing', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1' - } - })); - expect(requests[0].url).not.to.contain('bidfloor='); - }); + afterEach(() => { + $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; + logWarnSpy.restore(); + }); - it('should contain bidFloor optional param', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - bidFloor: 0.80 - } - })); - expect(requests[0].url).to.contain('bidfloor=0.8'); - }); + it('should return formatted bid response with required properties', () => { + let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); + expect(formattedBidResponse).to.deep.equal({ + bidderCode: bidRequest.bidderCode, + requestId: 'bid-id', + ad: '', + cpm: 0.09, + width: 728, + height: 90, + creativeId: 'creative-id', + pubapiId: '245730051428950632', + currency: 'USD', + dealId: 'deal-id', + netRevenue: true, + ttl: 300 }); + }); - describe('Nexage api', () => { - let xhr; - let requests; + it('should return formatted bid response including pixels', () => { + bidResponse.body.ext = { + pixels: '' + }; - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); - }); + let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - afterEach(() => xhr.restore()); + expect(formattedBidResponse.ad).to.equal( + '' + + '' + ); + }); - it('requires parameters to be made', () => { - adapter.callBids({}); - expect(requests).to.be.empty; - }); + it('should show warning in the console', function() { + $$PREBID_GLOBAL$$.bidderSettings = { + aol: { + bidCpmAdjustment: function() {} + } + }; + spec.interpretResponse(bidResponse, bidRequest); + expect(utils.logWarn.calledOnce).to.be.true; + }); + }); - it('should hit the nexage api endpoint with the nexage config', () => { - adapter.callBids(createBidderRequest({ - params: getNexageGetBidParams() - })); + describe('buildRequests()', () => { + it('method exists and is a function', () => { + expect(spec.buildRequests).to.exist.and.to.be.a('function'); + }); - expect(requests[0].url).to.contain(NEXAGE_URL); - }); + describe('Marketplace', () => { + it('should not return request when no bids are present', () => { + let [request] = spec.buildRequests([]); + expect(request).to.be.empty; + }); - it('should hit the nexage api custom endpoint if specified in the nexage config', () => { - let bidParams = Object.assign({ - host: 'qa-hb.nexage.com' - }, getNexageGetBidParams()); + it('should return request for Marketplace endpoint', () => { + let bidRequest = getDefaultBidRequest(); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(MARKETPLACE_URL); + }); - adapter.callBids(createBidderRequest({ - params: bidParams - })); - expect(requests[0].url).to.contain('qa-hb.nexage.com/bidRequest?'); + it('should return request for Marketplace via onedisplay bidder code', () => { + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onedisplay' + }], + params: getMarketplaceBidParams() }); - it('should hit nexage api when nexage and marketplace params are present', () => { - let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(MARKETPLACE_URL); + }); - adapter.callBids(createBidderRequest({ - params: bidParams - })); - expect(requests[0].url).to.contain(NEXAGE_URL); + it('should return Marketplace request via onedisplay bidder code when' + + 'Marketplace and One Mobile GET params are present', () => { + let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams()); + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onedisplay' + }], + params: bidParams }); - it('should hit nexage api via onemobile bidder code when nexage and marketplace params are present', () => { - let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(MARKETPLACE_URL); + }); - adapter.callBids(createBidderRequest({ - bids: [{ - bidder: 'onemobile' - }], - params: bidParams - })); - expect(requests[0].url).to.contain(NEXAGE_URL); + it('should return Marketplace request via onedisplay bidder code when' + + 'Marketplace and One Mobile GET + POST params are present', () => { + let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams(), getNexagePostBidParams()); + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onedisplay' + }], + params: bidParams }); - it('should not resolve endpoint for onemobile bidder code when only Marketplace params are present', () => { - adapter.callBids(createBidderRequest({ - bids: [{ - bidder: 'onemobile' - }], - params: getMarketplaceBidParams() - })); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(MARKETPLACE_URL); + }); - expect(requests.length).to.equal(0); + it('should not resolve endpoint for onedisplay bidder code ' + + 'when only One Mobile params are present', () => { + let bidParams = Object.assign(getNexageGetBidParams(), getNexagePostBidParams()); + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onedisplay' + }], + params: bidParams }); - it('should contain required params - dcn & pos', () => { - adapter.callBids(createBidderRequest({ - params: getNexageGetBidParams() - })); - - expect(requests[0].url).to.contain(NEXAGE_URL + 'dcn=2c9d2b50015c5ce9db6aeeed8b9500d6&pos=header'); - }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request).to.be.empty; + }); - it('should contain cmd=bid by default', () => { - adapter.callBids(createBidderRequest({ - params: { - dcn: '54321123', - pos: 'footer-2324' - } - })); - expect(requests[0].url).to.contain('hb.nexage.com/bidRequest?dcn=54321123&pos=footer-2324&cmd=bid'); + it('should return Marketplace URL for eu region', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + region: 'eu' + } }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('adserver-eu.adtech.advertising.com/pubapi/3.0/'); + }); - it('should contain optional parameters if they are set', () => { - adapter.callBids(createBidderRequest({ - params: { - dcn: '54321123', - pos: 'footer-2324', - ext: { - param1: 'val1', - param2: 'val2', - param3: 'val3', - param4: 'val4' - } - } - })); - expect(requests[0].url).to.contain('hb.nexage.com/bidRequest?dcn=54321123&pos=footer-2324&cmd=bid' + - '¶m1=val1¶m2=val2¶m3=val3¶m4=val4'); + it('should return Marketplace URL for eu region when server option is present', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + server: 'adserver-eu.adtech.advertising.com' + } }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('adserver-eu.adtech.advertising.com/pubapi/3.0/'); + }); - it('should hit the nexage api endpoint with post data with the openrtb config', () => { - let bidConfig = getNexagePostBidParams(); - - adapter.callBids(createBidderRequest({ - params: bidConfig - })); - expect(requests[0].url).to.contain(NEXAGE_URL); - expect(requests[0].requestBody).to.deep.equal(bidConfig); - expect(requests[0].requestHeaders).to.have.property('x-openrtb-version'); + it('should return default Marketplace URL in case of unknown region config option', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + region: 'an' + } }); - - it('should not hit the nexage api endpoint with post data with the openrtb config' + - ' if a required parameter is missing', () => { - let bidConfig = getNexagePostBidParams(); - - bidConfig.imp[0].id = null; - adapter.callBids(createBidderRequest({ - params: bidConfig - })); - expect(requests).to.be.empty; - }) - ; + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(MARKETPLACE_URL); }); - }); - - describe('bid response', () => { - let server; - beforeEach(() => { - server = sinon.fakeServer.create(); - sinon.stub(bidmanager, 'addBidResponse'); + it('should return url with pubapi bid option', () => { + let bidRequest = getDefaultBidRequest(); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('cmd=bid;'); }); - afterEach(() => { - server.restore(); - bidmanager.addBidResponse.restore(); + it('should return url with version 2 of pubapi', () => { + let bidRequest = getDefaultBidRequest(); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('v=2;'); }); - it('should be added to bidmanager if returned from pubapi', () => { - server.respondWith(JSON.stringify(getDefaultBidResponse())); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; + it('should return url with cache busting option', () => { + let bidRequest = getDefaultBidRequest(); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.match(/misc=\d+/); }); - it('should be added to bidmanager if returned from nexage GET bid request', () => { - server.respondWith(JSON.stringify(getDefaultBidResponse())); - adapter.callBids(createBidderRequest({ + it('should return url with default pageId and sizeId', () => { + let bidRequest = createCustomBidRequest({ params: { - dcn: '54321123', - pos: 'footer-2324' + placement: 1234567, + network: '9599.1' } - })); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('/pubapi/3.0/9599.1/1234567/0/0/ADTECH;'); }); - it('should be added to bidmanager if returned from nexage POST bid request', () => { - server.respondWith(JSON.stringify(getDefaultBidResponse())); - adapter.callBids(createBidderRequest({ + it('should return url with custom pageId and sizeId when options are present', () => { + let bidRequest = createCustomBidRequest({ params: { - id: 'id-1', - imp: [{ - id: 'id-2', - banner: { - w: '100', - h: '100' - }, - tagid: 'header1' - }] + placement: 1234567, + network: '9599.1', + pageId: 1111, + sizeId: 2222 } - })); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - var bidResponse = bidmanager.addBidResponse.firstCall.args[1]; + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('/pubapi/3.0/9599.1/1234567/1111/2222/ADTECH;'); }); - it('should be added to bidmanager with correct bidderCode', () => { - server.respondWith(JSON.stringify(getDefaultBidResponse())); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1]).to.have.property('bidderCode', 'aol'); + it('should return url with default alias if alias param is missing', () => { + let bidRequest = getDefaultBidRequest(); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.match(/alias=\w+?;/); }); - it('should have adId matching the bidId from related bid request', () => { - server.respondWith(JSON.stringify(getDefaultBidResponse())); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1]) - .to.have.property('adId', '84ab500420319d'); + it('should return url with custom alias if it is present', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + alias: 'desktop_articlepage_something_box_300_250' + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('alias=desktop_articlepage_something_box_300_250'); }); - it('should be added to bidmanager as invalid in case of empty response', () => { - server.respondWith(''); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); + it('should return url without bidfloor option if is is missing', () => { + let bidRequest = getDefaultBidRequest(); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).not.to.contain('bidfloor='); }); - it('should be added to bidmanager as invalid in case of invalid JSON response', () => { - server.respondWith('{foo:{bar:{baz:'); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); + it('should return url with bidFloor option if it is present', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + bidFloor: 0.80 + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('bidfloor=0.8'); }); - it('should be added to bidmanager as invalid in case of no bid data', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid = []; - server.respondWith(JSON.stringify(bidResponse)); - - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); + it('should return url with key values if keyValues param is present', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + keyValues: { + age: 25, + height: 3.42, + test: 'key' + } + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('kvage=25;kvheight=3.42;kvtest=key'); }); + }); - it('should have adId matching the bidId from bid request in case of no bid data', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid = []; - server.respondWith(JSON.stringify(bidResponse)); - - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1]) - .to.have.property('adId', '84ab500420319d'); + describe('One Mobile', () => { + it('should return One Mobile url when One Mobile get params are present', () => { + let bidRequest = createCustomBidRequest({ + params: getNexageGetBidParams() + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(NEXAGE_URL); }); - it('should be added to bidmanager as invalid in case of empty price', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid[0].bid[0].price = undefined; - - server.respondWith(JSON.stringify(bidResponse)); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); + it('should return One Mobile url with different host when host option is present', () => { + let bidParams = Object.assign({ + host: 'qa-hb.nexage.com' + }, getNexageGetBidParams()); + let bidRequest = createCustomBidRequest({ + params: bidParams + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('qa-hb.nexage.com/bidRequest?'); }); - it('should be added to bidmanager with attributes from pubapi response', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid[0].bid[0].crid = '12345'; - - server.respondWith(JSON.stringify(bidResponse)); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(addedBidResponse.ad).to.equal(''); - expect(addedBidResponse.cpm).to.equal(0.09); - expect(addedBidResponse.width).to.equal(728); - expect(addedBidResponse.height).to.equal(90); - expect(addedBidResponse.creativeId).to.equal('12345'); - expect(addedBidResponse.pubapiId).to.equal('245730051428950632'); + it('should return One Mobile url when One Mobile and Marketplace params are present', () => { + let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); + let bidRequest = createCustomBidRequest({ + params: bidParams + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(NEXAGE_URL); }); - it('should be added to bidmanager including pixels from pubapi response', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: '' - }; - - server.respondWith(JSON.stringify(bidResponse)); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(addedBidResponse.ad).to.equal( - '' + - '' - ); + it('should return One Mobile url for onemobile bidder code ' + + 'when One Mobile GET and Marketplace params are present', () => { + let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onemobile' + }], + params: bidParams + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(NEXAGE_URL); }); - it('should be added to bidmanager including dealid from pubapi response', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid[0].bid[0].dealid = '12345'; - - server.respondWith(JSON.stringify(bidResponse)); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(addedBidResponse.dealId).to.equal('12345'); + it('should not return any url for onemobile bidder code' + + 'when only Marketplace params are present', () => { + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onemobile' + }], + params: getMarketplaceBidParams() + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request).to.be.empty; }); - it('should be added to bidmanager including encrypted price from pubapi response', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid[0].bid[0].ext.encp = 'a9334987'; - server.respondWith(JSON.stringify(bidResponse)); - - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(addedBidResponse.cpm).to.equal('a9334987'); + it('should return One Mobile url with required params - dcn & pos', () => { + let bidRequest = createCustomBidRequest({ + params: getNexageGetBidParams() + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(NEXAGE_URL + 'dcn=2c9d2b50015c5ce9db6aeeed8b9500d6&pos=header'); }); - it('should not render pixels on pubapi response when no parameter is set', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: '' - }; - server.respondWith(JSON.stringify(bidResponse)); - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - expect(document.body.querySelectorAll('iframe[src="pixels.org"]').length).to.equal(0); + it('should return One Mobile url with cmd=bid option', () => { + let bidRequest = createCustomBidRequest({ + params: getNexageGetBidParams() + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('cmd=bid'); }); - it('should render pixels from pubapi response when param userSyncOn is set with \'bidResponse\'', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: '' - }; + it('should return One Mobile url with generic params if ext option is present', () => { + let bidRequest = createCustomBidRequest({ + params: { + dcn: '54321123', + pos: 'footer-2324', + ext: { + param1: 'val1', + param2: 'val2', + param3: 'val3', + param4: 'val4' + } + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain('hb.nexage.com/bidRequest?dcn=54321123&pos=footer-2324&cmd=bid' + + '¶m1=val1¶m2=val2¶m3=val3¶m4=val4'); + }); - server.respondWith(JSON.stringify(bidResponse)); - let bidRequest = getDefaultBidRequest(); - bidRequest.bids[0].params.userSyncOn = 'bidResponse'; - adapter.callBids(bidRequest); - server.respond(); + it('should return request object for One Mobile POST endpoint when POST configuration is present', () => { + let bidConfig = getNexagePostBidParams(); + let bidRequest = createCustomBidRequest({ + params: bidConfig + }); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(NEXAGE_URL); + expect(request.method).to.equal('POST'); + expect(request.data).to.deep.equal(bidConfig); + expect(request.options).to.deep.equal({ + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.2' + } + }); + }); - let assertPixelsItem = (pixelsItemSelector) => { - let pixelsItem = document.body.querySelectorAll(pixelsItemSelector)[0]; + it('should not return request object for One Mobile POST endpoint' + + 'if required parameterers are missed', () => { + let bidRequest = createCustomBidRequest({ + params: { + imp: [] + } + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request).to.be.empty; + }); + }); + }); - expect(pixelsItem.width).to.equal('1'); - expect(pixelsItem.height).to.equal('1'); - expect(pixelsItem.style.display).to.equal('none'); - }; + describe('getUserSyncs()', () => { + let bidResponse; + let bidRequest; - assertPixelsItem('iframe[src="pixels.org"]'); - assertPixelsItem('iframe[src="pixels1.org"]'); - expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; + beforeEach(() => { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = false; + config.setConfig({ + aol: { + userSyncOn: 'bidResponse' + }, }); + bidResponse = getDefaultBidResponse(); + bidResponse.ext = { + pixels: getPixels() + }; + }); - it('should not render pixels if it was rendered before', () => { - $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; - let bidResponse = getDefaultBidResponse(); - bidResponse.ext = { - pixels: '' - }; - server.respondWith(JSON.stringify(bidResponse)); - - let bidRequest = getDefaultBidRequest(); - bidRequest.bids[0].params.userSyncOn = 'bidResponse'; - adapter.callBids(bidRequest); - server.respond(); + it('should return user syncs only if userSyncOn equals to "bidResponse"', () => { + let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); - expect(bidmanager.addBidResponse.calledOnce).to.be.true; + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; + expect(userSyncs).to.deep.equal([ + {type: 'image', url: 'img.org'}, + {type: 'iframe', url: 'pixels1.org'} + ]); + }); - let assertPixelsItem = (pixelsItemSelector) => { - let pixelsItems = document.body.querySelectorAll(pixelsItemSelector); + it('should not return user syncs if it has already been returned', () => { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; - expect(pixelsItems.length).to.equal(0); - }; + let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); - assertPixelsItem('iframe[src="test.com"]'); - assertPixelsItem('iframe[src="test2.com"]'); - }); + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; + expect(userSyncs).to.deep.equal([]); }); - describe('when bidCpmAdjustment is set', () => { - let bidderSettingsBackup; - let server; + it('should not return user syncs if pixels are not present', () => { + bidResponse.ext.pixels = null; - beforeEach(() => { - bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; - server = sinon.fakeServer.create(); - }); + let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); - afterEach(() => { - $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; - server.restore(); - if (utils.logWarn.restore) { - utils.logWarn.restore(); - } - }); - - it('should show warning in the console', function() { - sinon.spy(utils, 'logWarn'); - server.respondWith(JSON.stringify(getDefaultBidResponse())); - $$PREBID_GLOBAL$$.bidderSettings = { - aol: { - bidCpmAdjustment: function() {} - } - }; - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(utils.logWarn.calledOnce).to.be.true; - }); + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.false; + expect(userSyncs).to.deep.equal([]); }); }); });