diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 5adba83e9df..cc7a8f6818f 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -1,341 +1,341 @@ -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 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'; + $$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 _parseBid(response, bid) { + 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; + + if (bidData.ext && bidData.ext.encp) { + cpm = bidData.ext.encp; + } else { + cpm = bidData.price; - try { - bidData = response.seatbid[0].bid[0]; - } catch (e) { - _addErrorBidResponse(bid, response); + if (cpm === null || isNaN(cpm)) { + utils.logError('Invalid price in bid response', AOL_BIDDERS_CODES.AOL, bid); return; } + } - let cpm; - - if (bidData.ext && bidData.ext.encp) { - cpm = bidData.ext.encp; - } else { - cpm = bidData.price; + let ad = bidData.adm; + if (response.ext && response.ext.pixels) { + if (bid.userSyncOn !== constants.EVENTS.BID_RESPONSE) { + let formattedPixels = response.ext.pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, ''); - if (cpm === null || isNaN(cpm)) { - utils.logError('Invalid price in bid response', AOL_BIDDERS_CODES.aol, bid); - _addErrorBidResponse(bid, response); - return; - } + ad += ''; } + } - 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, ''); + return { + bidderCode: bid.bidderCode, + requestId: bid.bidId, + ad: ad, + cpm: cpm, + width: bidData.w, + height: bidData.h, + creativeId: bidData.crid, + pubapiId: response.id, + currencyCode: response.cur, + dealId: bidData.dealid + }; +} - ad += ''; - } - } +function _isMarketplaceBidder(bidder) { + return bidder === AOL_BIDDERS_CODES.AOL || bidder === AOL_BIDDERS_CODES.ONEDISPLAY; +} - 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; - } +function _isNexageBidder(bidder) { + return bidder === AOL_BIDDERS_CODES.AOL || bidder === AOL_BIDDERS_CODES.ONEMOBILE; +} - bidmanager.addBidResponse(bid.placementCode, bidResponse); +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 _isMarketplaceBidder(bidder) { - return bidder === AOL_BIDDERS_CODES.aol || bidder === AOL_BIDDERS_CODES.onedisplay; +} + +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 _isNexageBidder(bidder) { - return bidder === AOL_BIDDERS_CODES.aol || bidder === AOL_BIDDERS_CODES.onemobile; - } +function formatBidRequest(endpointCode, bid) { + let bidRequest; - 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)); - } - } + switch (endpointCode) { + case AOL_ENDPOINTS.DISPLAY.GET: + bidRequest = { + url: _buildMarketplaceUrl(bid), + method: 'GET' + }; + break; - function _isNexageRequestGet(bid) { - return _isNexageBidder(bid.bidder) && bid.params.dcn && bid.params.pos; + case AOL_ENDPOINTS.MOBILE.GET: + bidRequest = { + url: _buildOneMobileGetUrl(bid), + method: 'GET' + }; + break; + + case AOL_ENDPOINTS.MOBILE.POST: + bidRequest = { + url: _buildOneMobileBaseUrl(bid), + method: 'POST', + data: bid.params, + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.2' + } + }; + break; } - function _isMarketplaceRequest(bid) { - return _isMarketplaceBidder(bid.bidder) && bid.params.placement && bid.params.network; + bidRequest.bidderCode = bid.bidder; + bidRequest.bidId = bid.bidId; + bidRequest.userSyncOn = bid.params.userSyncOn; + + return bidRequest; +} + +function interpretResponse(bidResponse, bidRequest) { + showCpmAdjustmentWarning(); + + if (!bidResponse) { + utils.logError('Empty bid response', bidRequest.bidderCode, bidResponse); + } else { + let bid = _parseBid(bidResponse, 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); + + return formatBidRequest(endpointCode, bid) + }); + }, + interpretResponse: interpretResponse, + getUserSyncs: function(options, bidResponses, bidRequest) { + let bidResponse = bidResponses[0]; - function _callBids(params) { - utils._each(params.bids, bid => { - let apiUrl; - let data = null; - let options = { - withCredentials: true - }; - 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 = { - 'x-openrtb-version': '2.2' - }; - options.method = 'POST'; - options.contentType = 'application/json'; - } - } else if (isMarketplaceRequest) { - apiUrl = _buildMarketplaceUrl(bid); - } + if (bidResponse && bidRequest && bidRequest.userSyncOn === constants.EVENTS.BID_RESPONSE) { + if (!$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped && bidResponse.ext && bidResponse.ext.pixels) { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; - 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 - - if (!response && response.length <= 0) { - utils.logError('Empty bid response', AOL_BIDDERS_CODES.aol, bid); - _addErrorBidResponse(bid, response); - return; - } - - try { - response = JSON.parse(response); - } catch (e) { - utils.logError('Invalid JSON in bid response', AOL_BIDDERS_CODES.aol, bid); - _addErrorBidResponse(bid, response); - return; - } - - _addBidResponse(bid, response); - }, data, options); + return parsePixelItems(bidResponse.ext.pixels); } - }); - } + } - return Object.assign(this, new BaseAdapter(AOL_BIDDERS_CODES.aol), { - callBids: _callBids - }); + return []; + } }; -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); - -module.exports = AolAdapter; +registerBidder(spec); diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index becc4afc029..25a25c2d9c9 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -180,13 +180,13 @@ export function newBidder(spec) { // After all the responses have come back, fill up the "no bid" bids and // register any required usersync pixels. const responses = []; - function afterAllResponses() { + function afterAllResponses(request) { fillNoBids(); if (spec.getUserSyncs) { let syncs = spec.getUserSyncs({ iframeEnabled: config.getConfig('userSync.iframeEnabled'), pixelEnabled: config.getConfig('userSync.pixelEnabled'), - }, responses); + }, responses, request); if (syncs) { if (!Array.isArray(syncs)) { syncs = [syncs]; @@ -223,11 +223,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 @@ -249,7 +257,8 @@ export function newBidder(spec) { typeof request.data === 'string' ? request.data : JSON.stringify(request.data), { method: 'POST', - contentType: 'text/plain', + contentType: request.contentType || 'text/plain', + customHeaders: request.customHeaders || {}, withCredentials: true } ); @@ -284,7 +293,7 @@ export function newBidder(spec) { addBidUsingRequestMap(bids); } } - onResponse(); + onResponse(request); function addBidUsingRequestMap(bid) { const bidRequest = bidRequestMap[bid.requestId]; diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 188c5375b89..db968d820cc 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -2,6 +2,8 @@ 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 {newBidder} from 'src/adapters/bidderFactory'; let getDefaultBidResponse = () => { return { @@ -67,13 +69,18 @@ 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()); + beforeEach(() => adapter = newBidder(spec)); function createBidderRequest({bids, params} = {}) { var bidderRequest = getDefaultBidRequest(); @@ -407,7 +414,7 @@ describe('AolAdapter', () => { params: bidConfig })); expect(requests[0].url).to.contain(NEXAGE_URL); - expect(requests[0].requestBody).to.deep.equal(bidConfig); + expect(requests[0].requestBody).to.deep.equal(JSON.stringify(bidConfig)); expect(requests[0].requestHeaders).to.have.property('x-openrtb-version'); }); @@ -420,8 +427,7 @@ describe('AolAdapter', () => { params: bidConfig })); expect(requests).to.be.empty; - }) - ; + }); }); }); @@ -521,18 +527,6 @@ describe('AolAdapter', () => { expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(2); }); - 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'); - }); - it('should be added to bidmanager as invalid in case of empty price', () => { let bidResponse = getDefaultBidResponse(); bidResponse.seatbid[0].bid[0].price = undefined; @@ -603,72 +597,6 @@ describe('AolAdapter', () => { let addedBidResponse = bidmanager.addBidResponse.firstCall.args[1]; expect(addedBidResponse.cpm).to.equal('a9334987'); }); - - 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 render pixels from pubapi response when param userSyncOn is set with \'bidResponse\'', () => { - 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(); - - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - - let assertPixelsItem = (pixelsItemSelector) => { - let pixelsItem = document.body.querySelectorAll(pixelsItemSelector)[0]; - - expect(pixelsItem.width).to.equal('1'); - expect(pixelsItem.height).to.equal('1'); - expect(pixelsItem.style.display).to.equal('none'); - }; - - assertPixelsItem('iframe[src="pixels.org"]'); - assertPixelsItem('iframe[src="pixels1.org"]'); - expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; - }); - - 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(); - - expect(bidmanager.addBidResponse.calledOnce).to.be.true; - - let assertPixelsItem = (pixelsItemSelector) => { - let pixelsItems = document.body.querySelectorAll(pixelsItemSelector); - - expect(pixelsItems.length).to.equal(0); - }; - - assertPixelsItem('iframe[src="test.com"]'); - assertPixelsItem('iframe[src="test2.com"]'); - }); }); describe('when bidCpmAdjustment is set', () => { @@ -702,4 +630,48 @@ describe('AolAdapter', () => { }); }); }); + + describe('getUserSyncs', () => { + let bidResponse; + let bidRequest; + + beforeEach(() => { + bidResponse = getDefaultBidResponse(); + bidResponse.ext = { + pixels: getPixels() + }; + bidRequest = { + userSyncOn: 'bidResponse' + }; + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = false; + }); + + it('should return user syncs only if userSyncOn equals to "bidResponse"', () => { + let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; + expect(userSyncs).to.deep.equal([ + {type: 'image', url: 'img.org'}, + {type: 'iframe', url: 'pixels1.org'} + ]); + }); + + it('should not return user syncs if it has already been returned', () => { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true; + + let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.true; + expect(userSyncs).to.deep.equal([]); + }); + + it('should not return user syncs if pixels are not present', () => { + bidResponse.ext.pixels = null; + + let userSyncs = spec.getUserSyncs({}, [bidResponse], bidRequest); + + expect($$PREBID_GLOBAL$$.aolGlobals.pixelsDropped).to.be.false; + expect(userSyncs).to.deep.equal([]); + }); + }); }); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 04e4c051365..114123bb711 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -124,7 +124,7 @@ describe('bidders created by newBidder', () => { expect(ajaxStub.called).to.equal(false); }); - it('should make the appropriate POST request', () => { + it('should make the appropriate POST request with default settings ', () => { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -132,7 +132,7 @@ describe('bidders created by newBidder', () => { spec.buildRequests.returns({ method: 'POST', url: url, - data: data + data: data, }); bidder.callBids(MOCK_BIDS_REQUEST); @@ -143,7 +143,33 @@ describe('bidders created by newBidder', () => { expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'POST', contentType: 'text/plain', - withCredentials: true + withCredentials: true, + customHeaders: {} + }); + }); + + it('should make the appropriate POST request with custom contentType and headers', () => { + const bidder = newBidder(spec); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + contentType: 'application/json', + customHeaders: { + 'custom-header': 'header-value' + } + }); + + bidder.callBids(MOCK_BIDS_REQUEST); + + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ + method: 'POST', + contentType: 'application/json', + withCredentials: true, + customHeaders: { + 'custom-header': 'header-value' + } }); });