diff --git a/CHANGELOG b/CHANGELOG index cf89da26f0c..c228996b4c1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +AOL Prebid 1.32.0 +---------------- +Updated to Prebid 0.32.0 + + AOL Prebid 1.31.0 ---------------- Updated to Prebid 0.31.0 diff --git a/LICENSE b/LICENSE index 78acfd4f6f0..ea676949daf 100644 --- a/LICENSE +++ b/LICENSE @@ -176,18 +176,7 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016 AOL INC. + Copyright 2017 AOL INC. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. diff --git a/gulpfile.js b/gulpfile.js index 53e14213f62..bb7391f00a1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -188,10 +188,11 @@ gulp.task('build-aol-bundle', ['build-bundle-dev'], () => { // By default, this runs in headless chrome. // // If --watch is given, the task will re-run unit tests whenever the source code changes +// If --file "" is given, the task will only run tests in the specified file. // If --browserstack is given, it will run the full suite of currently supported browsers. // If --browsers is given, browsers can be chosen explicitly. e.g. --browsers=chrome,firefox,ie9 gulp.task('test', ['clean'], function (done) { - var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch); + var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); var browserOverride = helpers.parseBrowserArgs(argv).map(helpers.toCapitalCase); if (browserOverride.length > 0) { @@ -201,8 +202,9 @@ gulp.task('test', ['clean'], function (done) { new KarmaServer(karmaConf, newKarmaCallback(done)).start(); }); +// If --file "" is given, the task will only run tests in the specified file. gulp.task('test-coverage', ['clean'], function(done) { - new KarmaServer(karmaConfMaker(true, false), newKarmaCallback(done)).start(); + new KarmaServer(karmaConfMaker(true, false, false, argv.file), newKarmaCallback(done)).start(); }); // View the code coverage report in the browser. diff --git a/karma.conf.maker.js b/karma.conf.maker.js index e4643c4a5be..82d76674cc9 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -97,12 +97,12 @@ function setBrowsers(karmaConf, browserstack) { } } -module.exports = function(codeCoverage, browserstack, watchMode) { +module.exports = function(codeCoverage, browserstack, watchMode, file) { var webpackConfig = newWebpackConfig(codeCoverage); var plugins = newPluginsArray(browserstack); var files = [ 'test/helpers/prebidGlobal.js', - 'test/**/*_spec.js' + file ? file : 'test/**/*_spec.js' ]; // This file opens the /debug.html tab automatically. // It has no real value unless you're running --watch, and intend to do some debugging in the browser. diff --git a/modules/adbutlerBidAdapter.js b/modules/adbutlerBidAdapter.js index d6492a72e1c..44a2ef49f51 100644 --- a/modules/adbutlerBidAdapter.js +++ b/modules/adbutlerBidAdapter.js @@ -1,130 +1,123 @@ -/** - * @overview AdButler Prebid.js adapter. - * @author dkharton - */ - 'use strict'; -var utils = require('src/utils.js'); -var adloader = require('src/adloader.js'); -var bidmanager = require('src/bidmanager.js'); -var bidfactory = require('src/bidfactory.js'); -var adaptermanager = require('src/adaptermanager'); - -var AdButlerAdapter = function AdButlerAdapter() { - function _callBids(params) { - var bids = params.bids || []; - var callbackData = {}; - var zoneCount = {}; - var pageID = Math.floor(Math.random() * 10e6); - - // Build and send bid requests - for (var i = 0; i < bids.length; i++) { - var bid = bids[i]; - var zoneID = utils.getBidIdParameter('zoneID', bid.params); - var callbackID; - - if (!(zoneID in zoneCount)) { - zoneCount[zoneID] = 0; +import * as utils from 'src/utils'; +import {config} from 'src/config'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'adbutler'; + +export const spec = { + code: BIDDER_CODE, + pageID: Math.floor(Math.random() * 10e6), + + isBidRequestValid: function (bid) { + return !!(bid.params.accountID && bid.params.zoneID); + }, + + buildRequests: function (validBidRequests) { + var i; + var zoneID; + var bidRequest; + var accountID; + var keyword; + var domain; + var requestURI; + var serverRequests = []; + var zoneCounters = {}; + + for (i = 0; i < validBidRequests.length; i++) { + bidRequest = validBidRequests[i]; + zoneID = utils.getBidIdParameter('zoneID', bidRequest.params); + accountID = utils.getBidIdParameter('accountID', bidRequest.params); + keyword = utils.getBidIdParameter('keyword', bidRequest.params); + domain = utils.getBidIdParameter('domain', bidRequest.params); + + if (!(zoneID in zoneCounters)) { + zoneCounters[zoneID] = 0; } - // build callbackID to get placementCode later - callbackID = zoneID + '_' + zoneCount[zoneID]; + if (typeof domain === 'undefined' || domain.length === 0) { + domain = 'servedbyadbutler.com'; + } - callbackData[callbackID] = {}; - callbackData[callbackID].bidId = bid.bidId; + requestURI = location.protocol + '//' + domain + '/adserve/;type=hbr;'; + requestURI += 'ID=' + encodeURIComponent(accountID) + ';'; + requestURI += 'setID=' + encodeURIComponent(zoneID) + ';'; + requestURI += 'pid=' + encodeURIComponent(spec.pageID) + ';'; + requestURI += 'place=' + encodeURIComponent(zoneCounters[zoneID]) + ';'; - var adRequest = buildRequest(bid, zoneCount[zoneID], pageID); - zoneCount[zoneID]++; + // append the keyword for targeting if one was passed in + if (keyword !== '') { + requestURI += 'kw=' + encodeURIComponent(keyword) + ';'; + } - adloader.loadScript(adRequest); + zoneCounters[zoneID]++; + serverRequests.push({ + method: 'GET', + url: requestURI, + data: {}, + bidRequest: bidRequest + }); } + return serverRequests; + }, + + interpretResponse: function (serverResponse, bidRequest) { + var bidObj = bidRequest.bidRequest; + var bidResponses = []; + var bidResponse = {}; + var isCorrectSize = false; + var isCorrectCPM = true; + var CPM; + var minCPM; + var maxCPM; + var width; + var height; + + serverResponse = serverResponse.body; + if (serverResponse && serverResponse.status === 'SUCCESS' && bidObj) { + CPM = serverResponse.cpm; + minCPM = utils.getBidIdParameter('minCPM', bidObj.params); + maxCPM = utils.getBidIdParameter('maxCPM', bidObj.params); + width = parseInt(serverResponse.width); + height = parseInt(serverResponse.height); + + // Ensure response CPM is within the given bounds + if (minCPM !== '' && CPM < parseFloat(minCPM)) { + isCorrectCPM = false; + } + if (maxCPM !== '' && CPM > parseFloat(maxCPM)) { + isCorrectCPM = false; + } - // Define callback function for bid responses - $$PREBID_GLOBAL$$.adbutlerCB = function(aBResponseObject) { - var bidResponse = {}; - var callbackID = aBResponseObject.zone_id + '_' + aBResponseObject.place; - var width = parseInt(aBResponseObject.width); - var height = parseInt(aBResponseObject.height); - var isCorrectSize = false; - var isCorrectCPM = true; - var CPM; - var minCPM; - var maxCPM; - var bidObj = callbackData[callbackID] ? utils.getBidRequest(callbackData[callbackID].bidId) : null; - - if (bidObj) { - if (aBResponseObject.status === 'SUCCESS') { - CPM = aBResponseObject.cpm; - minCPM = utils.getBidIdParameter('minCPM', bidObj.params); - maxCPM = utils.getBidIdParameter('maxCPM', bidObj.params); - - // Ensure response CPM is within the given bounds - if (minCPM !== '' && CPM < parseFloat(minCPM)) { - isCorrectCPM = false; - } - if (maxCPM !== '' && CPM > parseFloat(maxCPM)) { - isCorrectCPM = false; - } - - // Ensure that response ad matches one of the placement sizes. - utils._each(bidObj.sizes, function(size) { - if (width === size[0] && height === size[1]) { - isCorrectSize = true; - } - }); - - if (isCorrectCPM && isCorrectSize) { - bidResponse = bidfactory.createBid(1, bidObj); - bidResponse.bidderCode = 'adbutler'; - bidResponse.cpm = CPM; - bidResponse.width = width; - bidResponse.height = height; - bidResponse.ad = aBResponseObject.ad_code; - bidResponse.ad += addTrackingPixels(aBResponseObject.tracking_pixels); - } else { - bidResponse = bidfactory.createBid(2, bidObj); - bidResponse.bidderCode = 'adbutler'; - } - } else { - bidResponse = bidfactory.createBid(2, bidObj); - bidResponse.bidderCode = 'adbutler'; + // Ensure that response ad matches one of the placement sizes. + utils._each(bidObj.sizes, function (size) { + if (width === size[0] && height === size[1]) { + isCorrectSize = true; } - - bidmanager.addBidResponse(bidObj.placementCode, bidResponse); + }); + if (isCorrectCPM && isCorrectSize) { + bidResponse.requestId = bidObj.bidId; + bidResponse.bidderCode = spec.code; + bidResponse.creativeId = serverResponse.placement_id; + bidResponse.cpm = CPM; + bidResponse.width = width; + bidResponse.height = height; + bidResponse.ad = serverResponse.ad_code; + bidResponse.ad += spec.addTrackingPixels(serverResponse.tracking_pixels); + bidResponse.currency = 'USD'; + bidResponse.netRevenue = true; + bidResponse.ttl = config.getConfig('_bidderTimeout'); + bidResponse.referrer = utils.getTopWindowUrl(); + bidResponses.push(bidResponse); } - }; - } - - function buildRequest(bid, adIndex, pageID) { - var accountID = utils.getBidIdParameter('accountID', bid.params); - var zoneID = utils.getBidIdParameter('zoneID', bid.params); - var keyword = utils.getBidIdParameter('keyword', bid.params); - var domain = utils.getBidIdParameter('domain', bid.params); - - if (typeof domain === 'undefined' || domain.length === 0) { - domain = 'servedbyadbutler.com'; } + return bidResponses; + }, - var requestURI = location.protocol + '//' + domain + '/adserve/;type=hbr;'; - requestURI += 'ID=' + encodeURIComponent(accountID) + ';'; - requestURI += 'setID=' + encodeURIComponent(zoneID) + ';'; - requestURI += 'pid=' + encodeURIComponent(pageID) + ';'; - requestURI += 'place=' + encodeURIComponent(adIndex) + ';'; - - // append the keyword for targeting if one was passed in - if (keyword !== '') { - requestURI += 'kw=' + encodeURIComponent(keyword) + ';'; - } - requestURI += 'jsonpfunc=$$PREBID_GLOBAL$$.adbutlerCB;'; - requestURI += 'click=CLICK_MACRO_PLACEHOLDER'; - - return requestURI; - } - - function addTrackingPixels(trackingPixels) { + addTrackingPixels: function (trackingPixels) { var trackingPixelMarkup = ''; - utils._each(trackingPixels, function(pixelURL) { + utils._each(trackingPixels, function (pixelURL) { var trackingPixel = ''; @@ -133,14 +126,5 @@ var AdButlerAdapter = function AdButlerAdapter() { }); return trackingPixelMarkup; } - - // Export the callBids function, so that prebid.js can execute this function - // when the page asks to send out bid requests. - return { - callBids: _callBids - }; }; - -adaptermanager.registerBidAdapter(new AdButlerAdapter(), 'adbutler'); - -module.exports = AdButlerAdapter; +registerBidder(spec); diff --git a/modules/adbutlerBidAdapter.md b/modules/adbutlerBidAdapter.md new file mode 100644 index 00000000000..5905074270a --- /dev/null +++ b/modules/adbutlerBidAdapter.md @@ -0,0 +1,31 @@ +# Overview + +**Module Name**: AdButler Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: dan@sparklit.com + +# Description + +Module that connects to an AdButler zone to fetch bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'display-div', + sizes: [[300, 250]], // a display size + bids: [ + { + bidder: "adbutler", + params: { + accountID: '167283', + zoneID: '210093', + keyword: 'red', //optional + minCPM: '1.00', //optional + maxCPM: '5.00' //optional + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 855014c2411..095a6cec4b9 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -1,310 +1,185 @@ -import bidmanager from 'src/bidmanager'; -import bidfactory from 'src/bidfactory'; import * as utils from 'src/utils'; -import {ajax} from 'src/ajax'; -import Adapter from 'src/adapter'; -import adaptermanager from 'src/adaptermanager'; +import { BANNER, VIDEO } from 'src/mediaTypes'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +const VIDEO_TARGETING = ['mimes', 'minduration', 'maxduration', 'protocols', + 'startdelay', 'linearity', 'boxingallowed', 'playbackmethod', 'delivery', + 'pos', 'api', 'ext']; +const VERSION = '1.0'; /** - * Adapter for requesting bids from AdKernel white-label platform - * @class + * Adapter for requesting bids from AdKernel white-label display platform */ -const AdKernelAdapter = function AdKernelAdapter() { - const AJAX_REQ_PARAMS = { - contentType: 'text/plain', - withCredentials: true, - method: 'GET' - }; - const EMPTY_BID_RESPONSE = {'seatbid': [{'bid': []}]}; - - const VIDEO_TARGETING = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'linearity', 'sequence', - 'boxingallowed', 'playbackmethod', 'delivery', 'pos', 'api', 'ext']; - - let baseAdapter = new Adapter('adkernel'); - - /** - * Helper object to build multiple bid requests in case of multiple zones/ad-networks - * @constructor - */ - function RtbRequestDispatcher() { - const _dispatch = {}; - const originalBids = {}; - const syncedHostZones = {}; - const site = createSite(); - - // translate adunit info into rtb impression dispatched by host/zone - this.addImp = function (bid) { - let host = bid.params.host; - let zone = bid.params.zoneId; - - if (!(host in _dispatch)) { - _dispatch[host] = {}; - } - /* istanbul ignore else */ - if (!(zone in _dispatch[host])) { - _dispatch[host][zone] = []; - } - let imp = buildImp(bid); - // save rtb impression for specified ad-network host and zone - _dispatch[host][zone].push(imp); - originalBids[bid.bidId] = bid; - // perform user-sync - if (!(host in syncedHostZones)) { - syncedHostZones[host] = []; - } - if (syncedHostZones[host].indexOf(zone) === -1) { - syncedHostZones[host].push(zone); - } - }; - - function buildImp(bid) { - const size = getBidSize(bid); - const imp = { - 'id': bid.bidId, - 'tagid': bid.placementCode - }; - - if (bid.mediaType === 'video') { - imp.video = {w: size[0], h: size[1]}; - if (bid.params.video) { - Object.keys(bid.params.video) - .filter(param => VIDEO_TARGETING.includes(param)) - .forEach(param => imp.video[param] = bid.params.video[param]); - } - } else { - imp.banner = {w: size[0], h: size[1]}; - } - if (utils.getTopWindowLocation().protocol === 'https:') { - imp.secure = 1; - } - return imp; - } - - function getBidSize(bid) { - if (bid.mediaType === 'video') { - return bid.sizes; - } - return bid.sizes[0]; - } - - /** - * Main function to get bid requests - */ - this.dispatch = function (callback) { - utils._each(_dispatch, (zones, host) => { - utils.logMessage(`processing network ${host}`); - utils._each(zones, (impressions, zone) => { - utils.logMessage(`processing zone ${zone}`); - dispatchRtbRequest(host, zone, impressions, callback); - }); - }); - }; - /** - * Build flat user-sync queue from host->zones mapping - */ - this.buildUserSyncQueue = function() { - return Object.keys(syncedHostZones) - .reduce((m, k) => { - syncedHostZones[k].forEach((v) => m.push([k, v])); - return m; - }, []); - }; - - function dispatchRtbRequest(host, zone, impressions, callback) { - let url = buildEndpointUrl(host); - let rtbRequest = buildRtbRequest(impressions); - let params = buildRequestParams(zone, rtbRequest); - ajax(url, (bidResp) => { - bidResp = bidResp === '' ? EMPTY_BID_RESPONSE : JSON.parse(bidResp); - utils._each(rtbRequest.imp, (imp) => { - let bidFound = false; - utils._each(bidResp.seatbid[0].bid, (bid) => { - /* istanbul ignore else */ - if (!bidFound && bid.impid === imp.id) { - bidFound = true; - callback(originalBids[imp.id], imp, bid); - } - }); - if (!bidFound) { - callback(originalBids[imp.id], imp); +export const spec = { + + code: 'adkernel', + aliases: ['headbidding'], + supportedMediaTypes: [VIDEO], + isBidRequestValid: function(bidRequest) { + return 'params' in bidRequest && typeof bidRequest.params.host !== 'undefined' && + 'zoneId' in bidRequest.params && !isNaN(Number(bidRequest.params.zoneId)); + }, + buildRequests: function(bidRequests) { + let auctionId; + let dispatch = bidRequests.map(buildImp) + .reduce((acc, curr, index) => { + let bidRequest = bidRequests[index]; + let zoneId = bidRequest.params.zoneId; + let host = bidRequest.params.host; + acc[host] = acc[host] || {}; + acc[host][zoneId] = acc[host][zoneId] || []; + acc[host][zoneId].push(curr); + auctionId = bidRequest.bidderRequestId; + return acc; + }, {}); + let requests = []; + Object.keys(dispatch).forEach(host => { + Object.keys(dispatch[host]).forEach(zoneId => { + const request = buildRtbRequest(dispatch[host][zoneId], auctionId); + requests.push({ + method: 'GET', + url: `${window.location.protocol}//${host}/rtbg`, + data: { + zone: Number(zoneId), + ad_type: 'rtb', + v: VERSION, + r: JSON.stringify(request) } }); - }, params, AJAX_REQ_PARAMS); - } - - /** - * Builds complete rtb bid request - * @param imps collection of impressions - */ - function buildRtbRequest(imps) { - return { - 'id': utils.getUniqueIdentifierStr(), - 'imp': imps, - 'site': site, - 'at': 1, - 'device': { - 'ip': 'caller', - 'ua': 'caller' - } - }; - } - - /** - * Build ad-network specific endpoint url - */ - function buildEndpointUrl(host) { - return `${window.location.protocol}//${host}/rtbg`; + }); + }); + return requests; + }, + interpretResponse: function(serverResponse, request) { + let response = serverResponse.body; + if (!response.seatbid) { + return []; } - function buildRequestParams(zone, rtbReq) { - return { - 'zone': encodeURIComponent(zone), - 'ad_type': 'rtb', - 'r': encodeURIComponent(JSON.stringify(rtbReq)) + let rtbRequest = JSON.parse(request.data.r); + let rtbImps = rtbRequest.imp; + let rtbBids = response.seatbid + .map(seatbid => seatbid.bid) + .reduce((a, b) => a.concat(b), []); + + return rtbBids.map(rtbBid => { + let imp = rtbImps.find(imp => imp.id === rtbBid.impid); + let prBid = { + requestId: rtbBid.impid, + cpm: rtbBid.price, + creativeId: rtbBid.crid, + currency: 'USD', + ttl: 360, + netRevenue: true }; - } - } - - /** - * Main module export function implementation - */ - baseAdapter.callBids = function (params) { - var bids = params.bids || []; - processBids(bids); - }; - - /** - * Process all bids grouped by network/zone - */ - function processBids(bids) { - const dispatcher = new RtbRequestDispatcher(); - // process individual bids - utils._each(bids, (bid) => { - if (!validateBidParams(bid.params)) { - utils.logError(`Incorrect configuration for adkernel bidder: ${bid.params}`); - bidmanager.addBidResponse(bid.placementCode, createEmptyBidObject(bid)); - } else { - dispatcher.addImp(bid); + if ('banner' in imp) { + prBid.mediaType = BANNER; + prBid.width = imp.banner.w; + prBid.height = imp.banner.h; + prBid.ad = formatAdMarkup(rtbBid); } - }); - // start async usersync - processUserSyncQueue(dispatcher.buildUserSyncQueue()); - - // process bids grouped into bid requests - dispatcher.dispatch((bid, imp, bidResp) => { - let adUnitId = bid.placementCode; - if (bidResp) { - utils.logMessage(`got response for ${adUnitId}`); - let dimensions = getCreativeSize(imp, bidResp); - bidmanager.addBidResponse(adUnitId, createBidObject(bidResp, bid, dimensions.w, dimensions.h)); - } else { - utils.logMessage(`got empty response for ${adUnitId}`); - bidmanager.addBidResponse(adUnitId, createEmptyBidObject(bid)); + if ('video' in imp) { + prBid.mediaType = VIDEO; + prBid.vastUrl = rtbBid.nurl; + prBid.width = imp.video.w; + prBid.height = imp.video.h; } + return prBid; }); - } - - /** - * Evaluate creative size from response or from request - */ - function getCreativeSize(imp, bid) { - let dimensions = (bid.h && bid.w) ? bid : (imp.banner || imp.video); - return { - w: dimensions.w, - h: dimensions.h - }; - } - - /** - * Create bid object for the bid manager - */ - function createBidObject(resp, bid, width, height) { - let bidObj = Object.assign(bidfactory.createBid(1, bid), { - bidderCode: bid.bidder, - width: width, - height: height, - cpm: parseFloat(resp.price) - }); - if (bid.mediaType === 'video') { - bidObj.vastUrl = resp.nurl; - bidObj.mediaType = 'video'; - } else { - bidObj.ad = formatAdMarkup(resp); + }, + getUserSyncs: function(syncOptions, serverResponses) { + if (!syncOptions.iframeEnabled || !serverResponses || serverResponses.length === 0) { + return []; } - return bidObj; + return serverResponses.filter(rsp => rsp.body && rsp.body.ext && rsp.body.ext.adk_usersync) + .map(rsp => rsp.body.ext.adk_usersync) + .reduce((a, b) => a.concat(b), []) + .map(sync_url => { + return { + type: 'iframe', + url: sync_url + } + }); } +}; - /** - * Create empty bid object for the bid manager - */ - function createEmptyBidObject(bid) { - return Object.assign(bidfactory.createBid(2, bid), { - bidderCode: bid.bidder - }); - } +registerBidder(spec); - /** - * Format creative with optional nurl call - */ - function formatAdMarkup(bid) { - var adm = bid.adm; - if ('nurl' in bid) { - adm += utils.createTrackPixelHtml(`${bid.nurl}&px=1`); - } - return adm; - } +/** + * Builds parameters object for single impression + */ +function buildImp(bid) { + const size = getAdUnitSize(bid); + const imp = { + 'id': bid.bidId, + 'tagid': bid.placementCode + }; - function validateBidParams(params) { - return typeof params.host !== 'undefined' && typeof params.zoneId !== 'undefined'; + if (bid.mediaType === 'video') { + imp.video = {w: size[0], h: size[1]}; + if (bid.params.video) { + Object.keys(bid.params.video) + .filter(param => VIDEO_TARGETING.includes(param)) + .forEach(param => imp.video[param] = bid.params.video[param]); + } + } else { + imp.banner = {w: size[0], h: size[1]}; } - - /** - * Creates site description object - */ - function createSite() { - var location = utils.getTopWindowLocation(); - return { - 'domain': location.hostname, - 'page': location.href.split('?')[0] - }; + if (utils.getTopWindowLocation().protocol === 'https:') { + imp.secure = 1; } + return imp; +} - /** - * Recursively process user-sync queue - */ - function processUserSyncQueue(queue) { - if (queue.length === 0) { - return; - } - let entry = queue.pop(); - insertUserSync(entry[0], entry[1], () => processUserSyncQueue(queue)); +/** + * Return ad unit single size + * @param bid adunit size definition + * @return {*} + */ +function getAdUnitSize(bid) { + if (bid.mediaType === 'video') { + return bid.sizes; } + return bid.sizes[0]; +} - /** - * Insert single iframe user-sync - */ - function insertUserSync(host, zone, callback) { - var iframe = utils.createInvisibleIframe(); - iframe.src = `//sync.adkernel.com/user-sync?zone=${zone}&r=%2F%2F${host}%2Fuser-synced%3Fuid%3D%7BUID%7D`; - utils.addEventHandler(iframe, 'load', callback); - try { - document.body.appendChild(iframe); - } catch (error) { - /* istanbul ignore next */ - utils.logError(error); +/** + * Builds complete rtb request + * @param imps collection of impressions + * @param auctionId + */ +function buildRtbRequest(imps, auctionId) { + return { + 'id': auctionId, + 'imp': imps, + 'site': createSite(), + 'at': 1, + 'device': { + 'ip': 'caller', + 'ua': 'caller' } - } - - return Object.assign(this, { - callBids: baseAdapter.callBids, - setBidderCode: baseAdapter.setBidderCode, - getBidderCode: baseAdapter.getBidderCode - }); -}; + }; +} -adaptermanager.registerBidAdapter(new AdKernelAdapter(), 'adkernel', { - supportedMediaTypes: ['video'] -}); -adaptermanager.aliasBidAdapter('adkernel', 'headbidding'); +/** + * Creates site description object + */ +function createSite() { + var location = utils.getTopWindowLocation(); + return { + 'domain': location.hostname, + 'page': location.href.split('?')[0] + }; +} -module.exports = AdKernelAdapter; +/** + * Format creative with optional nurl call + * @param bid rtb Bid object + */ +function formatAdMarkup(bid) { + var adm = bid.adm; + if ('nurl' in bid) { + adm += utils.createTrackPixelHtml(`${bid.nurl}&px=1`); + } + return `${adm}`; +} diff --git a/modules/adkernelBidAdapter.md b/modules/adkernelBidAdapter.md new file mode 100644 index 00000000000..bc30e6cc8e5 --- /dev/null +++ b/modules/adkernelBidAdapter.md @@ -0,0 +1,46 @@ +# Overview + +``` +Module Name: AdKernel Bidder Adapter +Module Type: Bidder Adapter +Maintainer: denis@adkernel.com +``` + +# Description + +Connects to AdKernel whitelabel platform. +Banner and video formats are supported. +The AdKernel adapter doesn't support multiple sizes per ad-unit and will use the first one if multiple sizes are defined. + + +# Test Parameters +``` + var adUnits = [ + { + code: 'banner-ad-div', + sizes: [[300, 250]], // banner size + bids: [ + { + bidder: 'adkernel', + params: { + zoneId: '30164', //required parameter + host: 'cpm.metaadserving.com' //required parameter + } + } + ] + }, { + code: 'video-ad-player', + sizes: [640, 480], // video player size + bids: [ + { + bidder: 'adkernel', + mediaType : 'video', + params: { + zoneId: '30164', //required parameter + host: 'cpm.metaadserving.com' //required parameter + } + } + ] + } + ]; +``` diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js new file mode 100644 index 00000000000..1949e0414a7 --- /dev/null +++ b/modules/adoceanBidAdapter.js @@ -0,0 +1,101 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'adocean'; + +function buildEndpointUrl(emiter, payload) { + let payloadString = ''; + utils._each(payload, function(v, k) { + if (payloadString.length) { + payloadString += '&'; + } + payloadString += k + '=' + encodeURIComponent(v); + }); + + return 'https://' + emiter + '/ad.json?' + payloadString; +} + +function buildRequest(masterBidRequests, masterId) { + const firstBid = masterBidRequests[0]; + const payload = { + id: masterId, + }; + + const bidIdMap = {}; + + utils._each(masterBidRequests, function(v) { + bidIdMap[v.params.slaveId] = v.bidId; + }); + + return { + method: 'GET', + url: buildEndpointUrl(firstBid.params.emiter, payload), + data: {}, + bidIdMap: bidIdMap + }; +} + +function assignToMaster(bidRequest, bidRequestsByMaster) { + const masterId = bidRequest.params.masterId; + bidRequestsByMaster[masterId] = bidRequestsByMaster[masterId] || []; + bidRequestsByMaster[masterId].push(bidRequest); +} + +function interpretResponse(placementResponse, bidRequest, bids) { + if (!placementResponse.error) { + let adCode = '`; - bid.bidderCode = IMPROVE_DIGITAL_BIDDER_CODE; - bid.cpm = parseFloat(bidObject.price); - bid.width = bidObject.w; - bid.height = bidObject.h; - - bidmanager.addBidResponse(bidRequest.placementCode, bid); - } - }); - }); - } - }; +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { userSync } from 'src/userSync'; + +const BIDDER_CODE = 'improvedigital'; + +export const spec = { + version: '4.0.0', + code: BIDDER_CODE, + aliases: ['id'], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + return !!(bid && bid.params && (bid.params.placementId || (bid.params.placementKey && bid.params.publisherId))); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (bidRequests) { + let normalizedBids = bidRequests.map((bidRequest) => { + return getNormalizedBidRequest(bidRequest); + }); - baseAdapter.callBids = function (params) { - // params will contain an array - let bidRequests = params.bids || []; - let loc = utils.getTopWindowLocation(); + let idClient = new ImproveDigitalAdServerJSClient('hb'); let requestParameters = { singleRequestMode: false, - httpRequestType: this.idClient.CONSTANTS.HTTP_REQUEST_TYPE.GET, - callback: CALLBACK_FUNCTION, - secure: (loc.protocol === 'https:') ? 1 : 0, - libVersion: this.LIB_VERSION + httpRequestType: idClient.CONSTANTS.HTTP_REQUEST_TYPE.GET, + returnObjType: idClient.CONSTANTS.RETURN_OBJ_TYPE.PREBID, + libVersion: this.version }; - let normalizedBids = bidRequests.map((bidRequest) => { - let normalizedBidRequest = this.getNormalizedBidRequest(bidRequest); - if (bidRequest.params && bidRequest.params.singleRequest) { - requestParameters.singleRequestMode = true; - } - return normalizedBidRequest; - }); - - let request = this.idClient.createRequest( + let requestObj = idClient.createRequest( normalizedBids, // requestObject requestParameters ); - if (request.errors && request.errors.length > 0) { + if (requestObj.errors && requestObj.errors.length > 0) { utils.logError('ID WARNING 0x01'); } - if (request && request.requests && request.requests[0]) { - utils._each(request.requests, function (requestElement) { - if (requestElement.url) { - adloader.loadScript(requestElement.url, null); - } - }); - } - } + return requestObj.requests; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, request) { + const bids = []; + utils._each(serverResponse.body.bid, function (bidObject) { + if (!bidObject.price || bidObject.price === null || + bidObject.hasOwnProperty('errorCode') || + typeof bidObject.adm !== 'string') { + return; + } - // Export the callBids function, so that prebid.js can execute this function - // when the page asks to send out bid requests. - return Object.assign(this, { - LIB_VERSION: LIB_VERSION, - idClient: baseAdapter.idClient, - getNormalizedBidRequest: baseAdapter.getNormalizedBidRequest, - callBids: baseAdapter.callBids - }); + let bid = {}; + let nurl = ''; + if (bidObject.nurl && bidObject.nurl.length > 0) { + nurl = ``; + } + bid.ad = `${nurl}`; + bid.adId = bidObject.id; + bid.cpm = parseFloat(bidObject.price); + bid.creativeId = bidObject.crid; + bid.currency = bidObject.currency ? bidObject.currency.toUpperCase() : 'USD'; + if (utils.isNumber(bidObject.lid)) { + bid.dealId = bidObject.lid; + } else if (typeof bidObject.lid === 'object' && bidObject.lid['1']) { + bid.dealId = bidObject.lid['1']; + } + bid.height = bidObject.h; + bid.netRevenue = bidObject.isNet ? bidObject.isNet : false; + bid.requestId = bidObject.id; + bid.ttl = 300; + bid.width = bidObject.w; + + bids.push(bid); + + // Register user sync URLs + if (utils.isArray(bidObject.sync)) { + utils._each(bidObject.sync, function (syncElement) { + userSync.registerSync('image', spec.code, syncElement); + }); + } + }); + return bids; + } }; -ImproveDigitalAdapter.createNew = function () { - return new ImproveDigitalAdapter(); -}; +function getNormalizedBidRequest(bid) { + let adUnitId = utils.getBidIdParameter('adUnitCode', bid) || null; + let placementId = utils.getBidIdParameter('placementId', bid.params) || null; + let publisherId = null; + let placementKey = null; + + if (placementId === null) { + publisherId = utils.getBidIdParameter('publisherId', bid.params) || null; + placementKey = utils.getBidIdParameter('placementKey', bid.params) || null; + } + let keyValues = utils.getBidIdParameter('keyValues', bid.params) || null; + let localSize = utils.getBidIdParameter('size', bid.params) || null; + let bidId = utils.getBidIdParameter('bidId', bid); + let transactionId = utils.getBidIdParameter('transactionId', bid); -adaptermanager.registerBidAdapter(new ImproveDigitalAdapter(), IMPROVE_DIGITAL_BIDDER_CODE); + let normalizedBidRequest = {}; + if (placementId) { + normalizedBidRequest.placementId = placementId; + } else { + if (publisherId) { + normalizedBidRequest.publisherId = publisherId; + } + if (placementKey) { + normalizedBidRequest.placementKey = placementKey; + } + } -module.exports = ImproveDigitalAdapter; + if (keyValues) { + normalizedBidRequest.keyValues = keyValues; + } + if (localSize && localSize.w && localSize.h) { + normalizedBidRequest.size = {}; + normalizedBidRequest.size.h = localSize.h; + normalizedBidRequest.size.w = localSize.w; + } + if (bidId) { + normalizedBidRequest.id = bidId; + } + if (adUnitId) { + normalizedBidRequest.adUnitId = adUnitId; + } + if (transactionId) { + normalizedBidRequest.transactionId = transactionId; + } + return normalizedBidRequest; +} +registerBidder(spec); function ImproveDigitalAdServerJSClient(endPoint) { this.CONSTANTS = { @@ -184,13 +159,17 @@ function ImproveDigitalAdServerJSClient(endPoint) { }, AD_SERVER_BASE_URL: 'ad.360yield.com', END_POINT: endPoint || 'hb', - AD_SERVER_URL_PARAM: '?jsonp=', - CLIENT_VERSION: 'JS-4.0.2', + AD_SERVER_URL_PARAM: 'jsonp=', + CLIENT_VERSION: 'JS-4.2.0', MAX_URL_LENGTH: 2083, ERROR_CODES: { BAD_HTTP_REQUEST_TYPE_PARAM: 1, MISSING_PLACEMENT_PARAMS: 2, LIB_VERSION_MISSING: 3 + }, + RETURN_OBJ_TYPE: { + DEFAULT: 0, + PREBID: 1 } }; @@ -210,6 +189,8 @@ function ImproveDigitalAdServerJSClient(endPoint) { return this.getErrorReturn(this.CONSTANTS.ERROR_CODES.LIB_VERSION_MISSING); } + requestParameters.returnObjType = requestParameters.returnObjType || this.CONSTANTS.RETURN_OBJ_TYPE.DEFAULT; + let impressionObjects = []; let impressionObject; let counter; @@ -223,12 +204,19 @@ function ImproveDigitalAdServerJSClient(endPoint) { impressionObjects.push(impressionObject); } + let returnIdMappings = true; + if (requestParameters.returnObjType === this.CONSTANTS.RETURN_OBJ_TYPE.PREBID) { + returnIdMappings = false; + } + let returnObject = {}; - returnObject.idMappings = []; returnObject.requests = []; + if (returnIdMappings) { + returnObject.idMappings = []; + } let errors = null; - let baseUrl = `${(requestParameters.secure === 1 ? 'https' : 'http')}://${this.CONSTANTS.AD_SERVER_BASE_URL}/${this.CONSTANTS.END_POINT}${this.CONSTANTS.AD_SERVER_URL_PARAM}`; + let baseUrl = `${(requestParameters.secure === 1 ? 'https' : 'http')}://${this.CONSTANTS.AD_SERVER_BASE_URL}/${this.CONSTANTS.END_POINT}?${this.CONSTANTS.AD_SERVER_URL_PARAM}`; let bidRequestObject = { bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters) @@ -243,49 +231,39 @@ function ImproveDigitalAdServerJSClient(endPoint) { adUnitId: impressionObject.adUnitId }); } else { - returnObject.idMappings.push({ - adUnitId: impressionObject.adUnitId, - id: impressionObject.impressionObject.id - }); - bidRequestObject.bid_request.imp = bidRequestObject.bid_request.imp || []; - - bidRequestObject.bid_request.imp.push(impressionObject.impressionObject); - let outputUri = encodeURIComponent(baseUrl + JSON.stringify(bidRequestObject)); - - if (!requestParameters.singleRequestMode) { - returnObject.requests.push({ - url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject)) + if (returnIdMappings) { + returnObject.idMappings.push({ + adUnitId: impressionObject.adUnitId, + id: impressionObject.impressionObject.id }); - bidRequestObject = { - bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters) - }; } + bidRequestObject.bid_request.imp = bidRequestObject.bid_request.imp || []; + bidRequestObject.bid_request.imp.push(impressionObject.impressionObject); + let writeLongRequest = false; + const outputUri = baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject)); if (outputUri.length > this.CONSTANTS.MAX_URL_LENGTH) { + writeLongRequest = true; if (bidRequestObject.bid_request.imp.length > 1) { + // Pop the current request and process it again in the next iteration bidRequestObject.bid_request.imp.pop(); - returnObject.requests.push({ - url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject)) - }); - bidRequestObject = { - bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters) - }; - bidRequestObject.bid_request.imp = []; - bidRequestObject.bid_request.imp.push(impressionObject.impressionObject); - } else { - // We have a problem. Single request is too long for a URI + if (returnIdMappings) { + returnObject.idMappings.pop(); + } + counter--; } } + + if (writeLongRequest || + !requestParameters.singleRequestMode || + counter === impressionObjects.length - 1) { + returnObject.requests.push(this.formatRequest(requestParameters, bidRequestObject)); + bidRequestObject = { + bid_request: this.createBasicBidRequestObject(requestParameters, extraRequestParameters) + }; + } } } - if (bidRequestObject.bid_request && - bidRequestObject.bid_request.imp && - bidRequestObject.bid_request.imp.length > 0) { - returnObject.requests = returnObject.requests || []; - returnObject.requests.push({ - url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject)) - }); - } if (errors) { returnObject.errors = errors; @@ -294,6 +272,24 @@ function ImproveDigitalAdServerJSClient(endPoint) { return returnObject; }; + this.formatRequest = function(requestParameters, bidRequestObject) { + switch (requestParameters.returnObjType) { + case this.CONSTANTS.RETURN_OBJ_TYPE.PREBID: + return { + method: 'GET', + url: `//${this.CONSTANTS.AD_SERVER_BASE_URL}/${this.CONSTANTS.END_POINT}`, + data: `${this.CONSTANTS.AD_SERVER_URL_PARAM}${JSON.stringify(bidRequestObject)}` + }; + default: + const baseUrl = `${(requestParameters.secure === 1 ? 'https' : 'http')}://` + + `${this.CONSTANTS.AD_SERVER_BASE_URL}/` + + `${this.CONSTANTS.END_POINT}?${this.CONSTANTS.AD_SERVER_URL_PARAM}`; + return { + url: baseUrl + encodeURIComponent(JSON.stringify(bidRequestObject)) + } + } + }; + this.createBasicBidRequestObject = function(requestParameters, extraRequestParameters) { let impressionBidRequestObject = {}; if (requestParameters.requestId) { diff --git a/modules/improvedigitalBidAdapter.md b/modules/improvedigitalBidAdapter.md new file mode 100644 index 00000000000..3d91d4f82f2 --- /dev/null +++ b/modules/improvedigitalBidAdapter.md @@ -0,0 +1,47 @@ +# Overview + +**Module Name**: Improve Digital Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: hb@improvedigital.com + +# Description + +Module that connects to Improve Digital's demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'div-gpt-ad-1499748733608-0', + bids: [ + { + bidder: 'improvedigital', + params: { + placementId:1053688 + } + } + ] + }, { + code: 'div-gpt-ad-1499748833901-0', + bids: [{ + bidder: 'improvedigital', + params: { + placementId:1053689, + keyValues: { + testKey: ["testValue"] + } + } + }] + }, { + code: 'div-gpt-ad-1499748913322-0', + bids: [{ + bidder: 'improvedigital', + params: { + placementId:1053687, + size: { + w:300, + h:300 + } + } + }] + }]; +``` \ No newline at end of file diff --git a/modules/jcmBidAdapter.js b/modules/jcmBidAdapter.js index 7666dae2f5b..4c9792a00f2 100644 --- a/modules/jcmBidAdapter.js +++ b/modules/jcmBidAdapter.js @@ -1,45 +1,23 @@ -var bidfactory = require('src/bidfactory.js'); -var bidmanager = require('src/bidmanager.js'); -var adloader = require('src/adloader.js'); -var utils = require('src/utils.js'); -var adaptermanager = require('src/adaptermanager'); +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'jcm'; +const URL = '//media.adfrontiers.com/pq' -var JCMAdapter = function JCMAdapter() { - window.pbjs = window.pbjs || {}; - window.pbjs.processJCMResponse = function(JCMResponse) { - if (JCMResponse) { - var JCMRespObj = JSON.parse(JCMResponse); - if (JCMRespObj) { - var bids = JCMRespObj.bids; - for (var i = 0; i < bids.length; i++) { - var bid = bids[i]; - var bidObject; - if (bid.cpm > 0) { - bidObject = bidfactory.createBid(1); - bidObject.bidderCode = 'jcm'; - bidObject.cpm = bid.cpm; - bidObject.ad = decodeURIComponent(bid.ad.replace(/\+/g, '%20')); - bidObject.width = bid.width; - bidObject.height = bid.height; - bidmanager.addBidResponse(utils.getBidRequest(bid.callbackId).placementCode, bidObject); - } else { - bidObject = bidfactory.createBid(2); - bidObject.bidderCode = 'jcm'; - bidmanager.addBidResponse(utils.getBidRequest(bid.callbackId).placementCode, bidObject); - } - } - } - } - }; +export const spec = { + code: BIDDER_CODE, + aliases: ['jcarter'], + isBidRequestValid: function(bid) { + return !!(bid.params && bid.params.siteId && bid.bidId); + }, - function _callBids(params) { - var BidRequest = { + buildRequests: function(validBidRequests) { + var BidRequestStr = { bids: [] }; - for (var i = 0; i < params.bids.length; i++) { + for (var i = 0; i < validBidRequests.length; i++) { var adSizes = ''; - var bid = params.bids[i]; + var bid = validBidRequests[i]; for (var x = 0; x < bid.sizes.length; x++) { adSizes += utils.parseGPTSingleSizeArray(bid.sizes[x]); if (x !== (bid.sizes.length - 1)) { @@ -47,23 +25,65 @@ var JCMAdapter = function JCMAdapter() { } } - BidRequest.bids.push({ + BidRequestStr.bids.push({ 'callbackId': bid.bidId, 'siteId': bid.params.siteId, - 'adSizes': adSizes + 'adSizes': adSizes, }); } - var JSONStr = JSON.stringify(BidRequest); - var reqURL = document.location.protocol + '//media.adfrontiers.com/pq?t=hb&bids=' + encodeURIComponent(JSONStr); - adloader.loadScript(reqURL); - } + var JSONStr = JSON.stringify(BidRequestStr); + var dataStr = 't=hb&ver=1.0&compact=true&bids=' + encodeURIComponent(JSONStr); + + return { + method: 'GET', + url: URL, + data: dataStr + } + }, - return { - callBids: _callBids - }; -}; + interpretResponse: function(serverResponse) { + const bidResponses = []; + serverResponse = serverResponse.body; + // loop through serverResponses + if (serverResponse) { + if (serverResponse.bids) { + var bids = serverResponse.bids; + for (var i = 0; i < bids.length; i++) { + var bid = bids[i]; + const bidResponse = { + requestId: bid.callbackId, + bidderCode: spec.code, + cpm: bid.cpm, + width: bid.width, + height: bid.height, + creativeId: bid.creativeId, + currency: 'USD', + netRevenue: bid.netRevenue, + ttl: bid.ttl, + ad: decodeURIComponent(bid.ad.replace(/\+/g, '%20')) + }; + bidResponses.push(bidResponse); + }; + }; + } + return bidResponses; + }, -adaptermanager.registerBidAdapter(new JCMAdapter(), 'jcm'); + getUserSyncs: function(syncOptions) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: '//media.adfrontiers.com/hb/jcm_usersync.html' + }]; + } + if (syncOptions.image) { + return [{ + type: 'image', + url: '//media.adfrontiers.com/hb/jcm_usersync.png' + }]; + } + } +} -module.exports = JCMAdapter; +registerBidder(spec); diff --git a/modules/jcmBidAdapter.md b/modules/jcmBidAdapter.md new file mode 100644 index 00000000000..53a2356df2f --- /dev/null +++ b/modules/jcmBidAdapter.md @@ -0,0 +1,40 @@ +#Overview + +``` +Module Name: JCM Bidder Adapter +Module Type: Bidder Adapter +Maintainer: george@jcartermarketing.com +``` + +# Description + +Module that connects to J Carter Marketing demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div1', + sizes: [[300, 250]], // display 300x250 + bids: [ + { + bidder: 'jcm', + params: { + siteId: '3608' + } + } + ] + },{ + code: 'test-div2', + sizes: [[728, 90]], // display 728x90 + bids: [ + { + bidder: 'jcm', + params: { + siteId: '3608' + } + } + ] + } + ]; + diff --git a/modules/justpremiumBidAdapter.js b/modules/justpremiumBidAdapter.js index b608461946c..d1ca62cd8e4 100644 --- a/modules/justpremiumBidAdapter.js +++ b/modules/justpremiumBidAdapter.js @@ -49,6 +49,12 @@ const JustpremiumAdapter = function JustpremiumAdapter() { return null; } + function isOldBrowser() { + const isPromisse = typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1; + const isWeakMap = typeof WeakMap !== 'undefined' && WeakMap.toString().indexOf('[native code]') !== -1; + return (!Array.prototype.find || !Array.prototype.sort || !Array.prototype.map || !Array.prototype.filter || !Array.prototype.keys || !isPromisse || !isWeakMap); + } + function setupVar() { d = top.document; jPAM = top.jPAM = top.jPAM || window.jPAM || {}; @@ -58,7 +64,7 @@ const JustpremiumAdapter = function JustpremiumAdapter() { server: null }; const libVer = readCookie('jpxhbjs') || null; - toLoad = dConfig.toLoad || [d.location.protocol + '//cdn-cf.justpremium.com/js/' + (libVer ? libVer + '/' : '') + 'jpx.js']; + toLoad = dConfig.toLoad || [d.location.protocol + '//cdn-cf.justpremium.com/js/' + (libVer ? libVer + '/' : '') + (isOldBrowser() ? 'jpxp.js' : 'jpx.js')]; server = dConfig.server || d.location.protocol + '//pre.ads.justpremium.com/v/1.4'; } @@ -114,7 +120,6 @@ const JustpremiumAdapter = function JustpremiumAdapter() { return rec.length ? rec.pop() : false; } } - return false; } diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 433cbb15433..154e4a7b3b7 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -1,60 +1,60 @@ -const bidfactory = require('src/bidfactory.js'); -const bidmanager = require('src/bidmanager.js'); -const adloader = require('src/adloader.js'); -const utils = require('src/utils.js'); -const adaptermanager = require('src/adaptermanager'); -const CONSTANTS = require('src/constants.json'); -const HOST = $$PREBID_GLOBAL$$.kargo_kraken_host || 'https://krk.kargo.com'; - -const KargoAdapter = function KargoAdapter() { - function _handleBid(bids) { - return function wrappedHandleBid(adUnits) { - utils._map(bids, bid => { - let adUnit = adUnits[bid.params.placementId]; - - if (adUnit) { - bidmanager.addBidResponse(bid.placementCode, _createBid(adUnit)); - - if (adUnit.receivedTracker) { - var el = document.createElement('img'); - el.src = adUnit.receivedTracker; - document.body.appendChild(el); - } - } - }); - }; - } - - function _createBid(adUnit) { - let bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD); - bidObject.bidderCode = 'kargo'; - bidObject.cpm = Number(adUnit.cpm); - bidObject.ad = adUnit.adm; - bidObject.width = adUnit.width; - bidObject.height = adUnit.height; - return bidObject; - } - - function _callBids(params) { +import * as utils from 'src/utils'; +import {config} from 'src/config'; +import {registerBidder} from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'kargo'; +const HOST = 'https://krk.kargo.com'; +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + if (!bid || !bid.params) { + return false; + } + return !!bid.params.placementId; + }, + buildRequests: function(validBidRequests, bidderRequest) { + const currency = config.getConfig('currency'); const transformedParams = Object.assign({}, { - timeout: params.timeout, - currency: 'USD', + timeout: bidderRequest.timeout, + currency: currency, cpmGranularity: 1, cpmRange: { floor: 0, ceil: 20 }, - adSlotIds: utils._map(params.bids, bid => bid.params.placementId) - }, _getAllMetadata()); + adSlotIds: utils._map(validBidRequests, bid => bid.params.placementId) + }, spec._getAllMetadata()); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); - const callbackName = `kargo_prebid_${params.requestId.replace(/-/g, '_')}`; - - window.$$PREBID_GLOBAL$$[callbackName] = _handleBid(params.bids); - - adloader.loadScript(`${HOST}/api/v1/bid?json=${encodedParams}&cb=window.$$PREBID_GLOBAL$$.${callbackName}`); - } + return Object.assign({}, bidderRequest, { + method: 'GET', + url: `${HOST}/api/v1/bid`, + data: `json=${encodedParams}`, + currency: currency + }); + }, + interpretResponse: function(response, bidRequest) { + let adUnits = response.body; + let bids = {}; + utils._each(bidRequest.bids, bid => bids[bid.params.placementId] = bid); + const bidResponses = []; + for (let adUnitId in adUnits) { + let adUnit = adUnits[adUnitId]; + bidResponses.push({ + requestId: bids[adUnitId].bidId, + cpm: Number(adUnit.cpm), + width: adUnit.width, + height: adUnit.height, + ad: adUnit.adm, + ttl: 300, + creativeId: adUnitId, + netRevenue: true, + currency: bidRequest.currency + }); + } + return bidResponses; + }, - function _readCookie(name) { + // PRIVATE + _readCookie(name) { let nameEquals = `${name}=`; let cookies = document.cookie.split(';'); @@ -70,15 +70,15 @@ const KargoAdapter = function KargoAdapter() { } return null; - } + }, - function _getCrbIds() { + _getCrbIds() { try { - const crb = JSON.parse(decodeURIComponent(_readCookie('krg_crb'))); - var syncIds = {}; + const crb = JSON.parse(decodeURIComponent(spec._readCookie('krg_crb'))); + let syncIds = {}; if (crb && crb.v) { - var vParsed = JSON.parse(atob(crb.v)); + let vParsed = JSON.parse(atob(crb.v)); if (vParsed && vParsed.syncIds) { syncIds = vParsed.syncIds; @@ -89,12 +89,12 @@ const KargoAdapter = function KargoAdapter() { } catch (e) { return {}; } - } + }, - function _getUid() { + _getUid() { try { - const uid = JSON.parse(decodeURIComponent(_readCookie('krg_uid'))); - var vData = {}; + const uid = JSON.parse(decodeURIComponent(spec._readCookie('krg_uid'))); + let vData = {}; if (uid && uid.v) { vData = uid.v; @@ -104,41 +104,41 @@ const KargoAdapter = function KargoAdapter() { } catch (e) { return {}; } - } + }, - function _getKruxUserId() { - return _getLocalStorageSafely('kxkar_user'); - } + _getKruxUserId() { + return spec._getLocalStorageSafely('kxkar_user'); + }, - function _getKruxSegments() { - return _getLocalStorageSafely('kxkar_segs'); - } + _getKruxSegments() { + return spec._getLocalStorageSafely('kxkar_segs'); + }, - function _getKrux() { - const segmentsStr = _getKruxSegments(); - var segments = []; + _getKrux() { + const segmentsStr = spec._getKruxSegments(); + let segments = []; if (segmentsStr) { segments = segmentsStr.split(','); } return { - userID: _getKruxUserId(), + userID: spec._getKruxUserId(), segments: segments }; - } + }, - function _getLocalStorageSafely(key) { + _getLocalStorageSafely(key) { try { return localStorage.getItem(key); } catch (e) { return null; } - } + }, - function _getUserIds() { - const uid = _getUid(); - const crbIds = _getCrbIds(); + _getUserIds() { + const uid = spec._getUid(); + const crbIds = spec._getCrbIds(); return { kargoID: uid.userId, @@ -146,23 +146,15 @@ const KargoAdapter = function KargoAdapter() { crbIDs: crbIds, optOut: uid.optOut }; - } + }, - function _getAllMetadata() { + _getAllMetadata() { return { - userIDs: _getUserIds(), - krux: _getKrux(), - pageURL: window.location.href + userIDs: spec._getUserIds(), + krux: spec._getKrux(), + pageURL: window.location.href, + rawCRB: spec._readCookie('krg_crb') }; } - - // Export the callBids function, so that prebid.js can execute - // this function when the page asks to send out bid requests. - return { - callBids: _callBids - }; }; - -adaptermanager.registerBidAdapter(new KargoAdapter(), 'kargo'); - -module.exports = KargoAdapter; +registerBidder(spec); diff --git a/modules/kargoBidAdapter.md b/modules/kargoBidAdapter.md new file mode 100644 index 00000000000..99fe82e7f54 --- /dev/null +++ b/modules/kargoBidAdapter.md @@ -0,0 +1,23 @@ +# Overview + +**Module Name**: Kargo Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: kraken@kargo.com + +# Description + +Please use `kargo` as the bidder code. Also, you *must* test on a mobile device, or emulate a mobile device by manipulating the user agent string sent to the server. + +# Test Parameters +``` + var adUnits = [{ + code: 'div-gpt-ad-1460505748561-1', + sizes: [[300,250],[1,1]], + bids: [{ + bidder: 'kargo', + params: { + placementId: '_m1Xt2E5dez' + } + }] + }]; +``` diff --git a/modules/nanointeractiveBidAdapter.js b/modules/nanointeractiveBidAdapter.js new file mode 100644 index 00000000000..9781e36edb6 --- /dev/null +++ b/modules/nanointeractiveBidAdapter.js @@ -0,0 +1,88 @@ +import * as utils from 'src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; + +export const BIDDER_CODE = 'nanointeractive'; +export const ENGINE_BASE_URL = 'http://tmp.audiencemanager.de/hb'; + +export const SECURITY = 'sec'; +export const DATA_PARTNER_ID = 'dpid'; +export const DATA_PARTNER_PIXEL_ID = 'pid'; +export const ALG = 'alg'; +export const NQ = 'nq'; +export const NQ_NAME = 'name'; +export const CATEGORY = 'category'; + +const DEFAULT_ALG = 'ihr'; + +export const spec = { + + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid(bid) { + const sec = bid.params[SECURITY]; + const dpid = bid.params[DATA_PARTNER_ID]; + const pid = bid.params[DATA_PARTNER_PIXEL_ID]; + return !!(sec && dpid && pid); + }, + buildRequests(bidRequests) { + let payload = []; + bidRequests.forEach(bid => payload.push(createSingleBidRequest(bid))); + return { + method: 'POST', + url: ENGINE_BASE_URL, + data: JSON.stringify(payload) + }; + }, + interpretResponse(serverResponse) { + const bids = []; + serverResponse.forEach(serverBid => { + if (isEngineResponseValid(serverBid)) { + bids.push(createSingleBidResponse(serverBid)); + } + }); + return bids; + } +}; + +function createSingleBidRequest(bid) { + return { + [SECURITY]: bid.params[SECURITY], + [DATA_PARTNER_ID]: bid.params[DATA_PARTNER_ID], + [DATA_PARTNER_PIXEL_ID]: bid.params[DATA_PARTNER_PIXEL_ID], + [ALG]: bid.params[ALG] || DEFAULT_ALG, + [NQ]: [createNqParam(bid), createCategoryParam(bid)], + sizes: bid.sizes.map(value => value[0] + 'x' + value[1]), + bidId: bid.bidId, + cors: location.origin + }; +} + +function createSingleBidResponse(serverBid) { + return { + requestId: serverBid.id, + cpm: serverBid.cpm, + width: serverBid.width, + height: serverBid.height, + ad: serverBid.ad, + ttl: serverBid.ttl, + creativeId: serverBid.creativeId, + netRevenue: serverBid.netRevenue || true, + currency: serverBid.currency, + }; +} + +function createNqParam(bid) { + return bid.params[NQ_NAME] ? utils.getParameterByName(bid.params[NQ_NAME]) : bid.params[NQ] || null; +} + +function createCategoryParam(bid) { + return bid.params[CATEGORY] || null; +} + +function isEngineResponseValid(response) { + return !!response.cpm && !!response.ad; +} + +registerBidder(spec); diff --git a/modules/nanointeractiveBidAdapter.md b/modules/nanointeractiveBidAdapter.md new file mode 100644 index 00000000000..0159353750a --- /dev/null +++ b/modules/nanointeractiveBidAdapter.md @@ -0,0 +1,69 @@ +# Overview + +``` +Module Name: NanoInteractive Bid Adapter +Module Type: Bidder Adapter +Maintainer: rade@nanointeractive.com +``` + +# Description + +Connects to NanoInteractive search retargeting Ad Server for bids. + +Besides standard params, please provide, if exist, user search params. + +Three examples calling the Ad Server. + +**First** is basic + +**Second** is with hardcoded nq (user search) params + +**Third** is with the search query param name of the current url + + +# Test Parameters +``` +var adUnits = [ + // Basic call + { + code: 'basic-div', + sizes: [[300, 250], [300,600]], + bids: [{ + bidder: 'nanointeractive', + params: { + sec: '04a0cb7fb9ac02840f7f33d68a883780', + dpid: '58bfec94eb0a1916fa380162', + pid: '58bfec94eb0a1916fa380163' + } + }] + }, + // Hardcoded user search + { + code: 'nq-div', + sizes: [[300, 250], [300,600]], + bids: [{ + bidder: 'nanointeractive', + params: { + sec: '04a0cb7fb9ac02840f7f33d68a883780', + dpid: '58bfec94eb0a1916fa380162', + pid: '58bfec94eb0a1916fa380163', + nq: 'user search' + } + }] + }, + // URL user search + { + code: 'url-div', + sizes: [[300, 250], [300,600]], + bids: [{ + bidder: 'nanointeractive', + params: { + sec: '04a0cb7fb9ac02840f7f33d68a883780', + dpid: '58bfec94eb0a1916fa380162', + pid: '58bfec94eb0a1916fa380163', + name: 'search' + } + }] + } +]; +``` diff --git a/modules/platformioBidAdapter.js b/modules/platformioBidAdapter.js index 87ebb2b61c4..2fb23ab92b3 100644 --- a/modules/platformioBidAdapter.js +++ b/modules/platformioBidAdapter.js @@ -1,63 +1,125 @@ -var bidfactory = require('src/bidfactory.js'); -var bidmanager = require('src/bidmanager.js'); -var adloader = require('src/adloader.js'); -var utils = require('src/utils.js'); -var CONSTANTS = require('src/constants.json'); -var adaptermanager = require('src/adaptermanager'); - -var PlatformIOAdapter = function PlatformIOAdapter() { - function _callBids(params) { - var bidURL; - var bids = params.bids || []; - var requestURL = window.location.protocol + '//js.adx1.com/pb_ortb.js?cb=' + new Date().getTime() + '&ver=1&'; - - for (var i = 0; i < bids.length; i++) { - var requestParams = {}; - var bid = bids[i]; - - requestParams.pub_id = bid.params.pubId; - requestParams.site_id = bid.params.siteId; - requestParams.placement_id = bid.placementCode; - - var parseSized = utils.parseSizesInput(bid.sizes); - var arrSize = parseSized[0].split('x'); - - requestParams.width = arrSize[0]; - requestParams.height = arrSize[1]; - requestParams.callback = '$$PREBID_GLOBAL$$._doPlatformIOCallback'; - requestParams.callback_uid = bid.bidId; - bidURL = requestURL + utils.parseQueryStringParameters(requestParams); - - utils.logMessage('PlatformIO.prebid, Bid ID: ' + bid.bidId + ', Pub ID: ' + bid.params.pubId); - adloader.loadScript(bidURL); - } - } - - $$PREBID_GLOBAL$$._doPlatformIOCallback = function (response) { - var bidObject; - var bidRequest; - var callbackID; - callbackID = response.callback_uid; - bidRequest = utils.getBidRequest(callbackID); - if (response.cpm > 0) { - bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bidRequest); - bidObject.bidderCode = 'platformio'; - bidObject.cpm = response.cpm; - bidObject.ad = response.tag; - bidObject.width = response.width; - bidObject.height = response.height; - } else { - bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bidRequest); - bidObject.bidderCode = 'platformio'; - utils.logMessage('No Bid response from Platformio request: ' + callbackID); - } - bidmanager.addBidResponse(bidRequest.placementCode, bidObject); - }; - - return { - callBids: _callBids - }; -}; -adaptermanager.registerBidAdapter(new PlatformIOAdapter(), 'platformio'); - -module.exports = PlatformIOAdapter; + +import {logError, getTopWindowLocation} from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +export const spec = { + + code: 'platformio', + + isBidRequestValid: bid => ( + !!(bid && bid.params && bid.params.pubId && bid.params.siteId) + ), + buildRequests: bidRequests => { + const request = { + id: bidRequests[0].bidderRequestId, + at: 2, + imp: bidRequests.map(slot => impression(slot)), + site: site(bidRequests), + device: device(), + }; + return { + method: 'POST', + url: '//piohbdisp.hb.adx1.com/', + data: JSON.stringify(request), + }; + }, + interpretResponse: (response, request) => ( + bidResponseAvailable(request, response.body) + ), +}; +function bidResponseAvailable(bidRequest, bidResponse) { + const idToImpMap = {}; + const idToBidMap = {}; + const ortbRequest = parse(bidRequest.data); + ortbRequest.imp.forEach(imp => { + idToImpMap[imp.id] = imp; + }); + if (bidResponse) { + bidResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { + idToBidMap[bid.impid] = bid; + })); + } + const bids = []; + Object.keys(idToImpMap).forEach(id => { + if (idToBidMap[id]) { + const bid = { + requestId: id, + cpm: idToBidMap[id].price, + creative_id: id, + creativeId: id, + adId: id, + }; + bid.ad = idToBidMap[id].adm; + bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_IMP_ID(%7D|\})/gi, idToBidMap[id].impid); + bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_AD_ID(%7D|\})/gi, idToBidMap[id].adid); + bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_PRICE(%7D|\})/gi, idToBidMap[id].price); + bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_CURRENCY(%7D|\})/gi, bidResponse.cur); + bid.ad = bid.ad.replace(/\$(%7B|\{)AUCTION_BID_ID(%7D|\})/gi, bidResponse.bidid); + bid.width = idToImpMap[id].banner.w; + bid.height = idToImpMap[id].banner.h; + bids.push(bid); + } + }); + return bids; +} +function impression(slot) { + return { + id: slot.bidId, + banner: banner(slot), + bidfloor: '0.000001', + tagid: slot.params.placementId.toString(), + }; +} +function banner(slot) { + const size = slot.params.size.toUpperCase().split('X'); + const width = parseInt(size[0]); + const height = parseInt(size[1]); + return { + w: width, + h: height, + }; +} +function site(bidderRequest) { + const pubId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.pubId : '0'; + const siteId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.siteId : '0'; + const appParams = bidderRequest[0].params.app; + if (!appParams) { + return { + publisher: { + id: pubId.toString(), + domain: getTopWindowLocation().hostname, + }, + id: siteId.toString(), + ref: referrer(), + page: getTopWindowLocation().href, + } + } + return null; +} +function referrer() { + try { + return window.top.document.referrer; + } catch (e) { + return document.referrer; + } +} +function device() { + return { + ua: navigator.userAgent, + language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), + w: (window.screen.width || window.innerWidth), + h: (window.screen.height || window.innerHeigh), + }; +} +function parse(rawResponse) { + try { + if (rawResponse) { + return JSON.parse(rawResponse); + } + } catch (ex) { + logError('platformio.parse', 'ERROR', ex); + } + return null; +} + +registerBidder(spec); diff --git a/modules/platformioBidAdapter.md b/modules/platformioBidAdapter.md new file mode 100644 index 00000000000..74f6adbf256 --- /dev/null +++ b/modules/platformioBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +**Module Name**: Platform.io Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: sk@ultralab.by + +# Description + +Connects to Platform.io demand source to fetch bids. +Please use ```platformio``` as the bidder code. + +# Test Parameters +``` + var adUnits = [{ + code: 'banner-ad-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'platformio', + params: { + pubId: '28082', + siteId: '26047', + placementId: '123', + size: '250X250' + } + }] + }]; +``` diff --git a/modules/prebidServerBidAdapter.js b/modules/prebidServerBidAdapter.js index 0906a1a0b3d..7120d67eb56 100644 --- a/modules/prebidServerBidAdapter.js +++ b/modules/prebidServerBidAdapter.js @@ -7,6 +7,7 @@ import { STATUS, S2S } from 'src/constants'; import { cookieSet } from 'src/cookie.js'; import adaptermanager from 'src/adaptermanager'; import { config } from 'src/config'; +import { VIDEO } from 'src/mediaTypes'; const getConfig = config.getConfig; @@ -111,7 +112,9 @@ function PrebidServer() { if (videoMediaType) { // pbs expects a ad_unit.video attribute if the imp is video adUnit.video = Object.assign({}, videoMediaType); - delete adUnit.mediaTypes.video; + delete adUnit.mediaTypes; + // default is assumed to be 'banner' so if there is a video type we assume video only until PBS can support multi format auction. + adUnit.media_types = [VIDEO]; } }) convertTypes(adUnits); @@ -180,6 +183,14 @@ function PrebidServer() { }); } + // do client-side syncs if available + requestedBidders.forEach(bidder => { + let clientAdapter = adaptermanager.getBidAdapter(bidder); + if (clientAdapter && clientAdapter.registerSyncs) { + clientAdapter.registerSyncs(); + } + }); + if (result.bids) { result.bids.forEach(bidObj => { let bidRequest = utils.getBidRequest(bidObj.bid_id); @@ -196,10 +207,26 @@ function PrebidServer() { bidObject.creative_id = bidObj.creative_id; bidObject.bidderCode = bidObj.bidder; bidObject.cpm = cpm; - bidObject.ad = bidObj.adm; - if (bidObj.nurl) { - bidObject.ad += utils.createTrackPixelHtml(decodeURIComponent(bidObj.nurl)); + // From ORTB see section 4.2.3: adm Optional means of conveying ad markup in case the bid wins; supersedes the win notice if markup is included in both. + if (bidObj.media_type === VIDEO) { + bidObject.mediaType = VIDEO; + if (bidObj.adm) { + bidObject.vastXml = bidObj.adm; + } + if (bidObj.nurl) { + bidObject.vastUrl = bidObj.nurl; + } + } else { + if (bidObj.adm && bidObj.nurl) { + bidObject.ad = bidObj.adm; + bidObject.ad += utils.createTrackPixelHtml(decodeURIComponent(bidObj.nurl)); + } else if (bidObj.adm) { + bidObject.ad = bidObj.adm; + } else if (bidObj.nurl) { + bidObject.adUrl = bidObj.nurl + } } + bidObject.width = bidObj.width; bidObject.height = bidObj.height; bidObject.adserverTargeting = bidObj.ad_server_targeting; @@ -223,7 +250,6 @@ function PrebidServer() { bidObject.source = TYPE; bidObject.adUnitCode = bidRequest.placementCode; bidObject.bidderCode = bidRequest.bidder; - bidmanager.addBidResponse(bidObject.adUnitCode, bidObject); }); }); diff --git a/modules/pulsepointLiteBidAdapter.js b/modules/pulsepointLiteBidAdapter.js index 00b5c014e98..99a83871dd8 100644 --- a/modules/pulsepointLiteBidAdapter.js +++ b/modules/pulsepointLiteBidAdapter.js @@ -69,6 +69,7 @@ export const spec = { function bidResponseAvailable(bidRequest, bidResponse) { const idToImpMap = {}; const idToBidMap = {}; + bidResponse = bidResponse.body // extract the request bids and the response bids, keyed by impr-id const ortbRequest = parse(bidRequest.data); ortbRequest.imp.forEach(imp => { diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index f199e9fad30..f10fd48502f 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -1,128 +1,165 @@ -const utils = require('src/utils.js'); -const bidfactory = require('src/bidfactory.js'); -const bidmanager = require('src/bidmanager.js'); -const ajax = require('src/ajax.js'); -const CONSTANTS = require('src/constants.json'); -const adaptermanager = require('src/adaptermanager'); -const QUANTCAST_CALLBACK_URL = 'http://global.qc.rtb.quantserve.com:8080/qchb'; - -var QuantcastAdapter = function QuantcastAdapter() { - const BIDDER_CODE = 'quantcast'; - - const DEFAULT_BID_FLOOR = 0.0000000001; - let bidRequests = {}; - - let returnEmptyBid = function(bidId) { - var bidRequested = utils.getBidRequest(bidId); - if (!utils.isEmpty(bidRequested)) { - let bid = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bidRequested); - bid.bidderCode = BIDDER_CODE; - bidmanager.addBidResponse(bidRequested.placementCode, bid); +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'quantcast'; +const DEFAULT_BID_FLOOR = 0.0000000001; + +export const QUANTCAST_CALLBACK_URL = 'global.qc.rtb.quantserve.com'; +export const QUANTCAST_CALLBACK_URL_TEST = 's2s-canary.quantserve.com'; +export const QUANTCAST_NET_REVENUE = true; +export const QUANTCAST_TEST_PUBLISHER = 'test-publisher'; +export const QUANTCAST_TTL = 4; + +/** + * The documentation for Prebid.js Adapter 1.0 can be found at link below, + * http://prebid.org/dev-docs/bidder-adapter-1.html + */ +export const spec = { + code: BIDDER_CODE, + + /** + * Verify the `AdUnits.bids` response with `true` for valid request and `false` + * for invalid request. + * + * @param {object} bid + * @return boolean `true` is this is a valid bid, and `false` otherwise + */ + isBidRequestValid(bid) { + if (!bid) { + return false; } - }; - // expose the callback to the global object: - $$PREBID_GLOBAL$$.handleQuantcastCB = function (responseText) { - if (utils.isEmpty(responseText)) { - return; - } - let response = null; - try { - response = JSON.parse(responseText); - } catch (e) { - // Malformed JSON - utils.logError("Malformed JSON received from server - can't do anything here"); - return; - } - - if (response === null || !response.hasOwnProperty('bids') || utils.isEmpty(response.bids)) { - utils.logError("Sub-optimal JSON received from server - can't do anything here"); - return; + if (bid.mediaType === 'video') { + return false; } - for (let i = 0; i < response.bids.length; i++) { - let seatbid = response.bids[i]; - let key = seatbid.placementCode; - var request = bidRequests[key]; - if (request === null || request === undefined) { - return returnEmptyBid(seatbid.placementCode); - } - // This line is required since this is the field - // that bidfactory.createBid looks for - request.bidId = request.imp[0].placementCode; - let responseBid = bidfactory.createBid(CONSTANTS.STATUS.GOOD, request); - - responseBid.cpm = seatbid.cpm; - responseBid.ad = seatbid.ad; - responseBid.height = seatbid.height; - responseBid.width = seatbid.width; - responseBid.bidderCode = response.bidderCode; - responseBid.requestId = request.requestId; - responseBid.bidderCode = BIDDER_CODE; - - bidmanager.addBidResponse(request.bidId, responseBid); + return true; + }, + + /** + * Make a server request when the page asks Prebid.js for bids from a list of + * `BidRequests`. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be send to Quantcast server + * @return ServerRequest information describing the request to the server. + */ + buildRequests(bidRequests) { + const bids = bidRequests || []; + + const referrer = utils.getTopWindowUrl(); + const loc = utils.getTopWindowLocation(); + const domain = loc.hostname; + + let publisherTagURL; + let publisherTagURLTest; + + // Switch the callback URL to Quantcast Canary Endpoint for testing purpose + // `//` is not used because we have different port setting at our end + switch (window.location.protocol) { + case 'https:': + publisherTagURL = `https://${QUANTCAST_CALLBACK_URL}:8443/qchb`; + publisherTagURLTest = `https://${QUANTCAST_CALLBACK_URL_TEST}:8443/qchb`; + break; + default: + publisherTagURL = `http://${QUANTCAST_CALLBACK_URL}:8080/qchb`; + publisherTagURLTest = `http://${QUANTCAST_CALLBACK_URL_TEST}:8080/qchb`; } - }; - function callBids(params) { - let bids = params.bids || []; - if (bids.length === 0) { - return; - } - - let referrer = utils.getTopWindowUrl(); - let loc = utils.getTopWindowLocation(); - let domain = loc.hostname; - let publisherId = 0; + const bidRequestsList = bids.map(bid => { + const bidSizes = []; - publisherId = '' + bids[0].params.publisherId; - utils._each(bids, function(bid) { - let key = bid.placementCode; - var bidSizes = []; - utils._each(bid.sizes, function (size) { + bid.sizes.forEach(size => { bidSizes.push({ - 'width': size[0], - 'height': size[1] + width: size[0], + height: size[1] }); }); - bidRequests[key] = bidRequests[key] || { - 'publisherId': publisherId, - 'requestId': bid.bidId, - 'bidId': bid.bidId, - 'site': { - 'page': loc.href, - 'referrer': referrer, - 'domain': domain, + // Request Data Format can be found at https://wiki.corp.qc/display/adinf/QCX + const requestData = { + publisherId: bid.params.publisherId, + requestId: bid.bidId, + imp: [ + { + banner: { + battr: bid.params.battr, + sizes: bidSizes + }, + placementCode: bid.placementCode, + bidFloor: bid.params.bidFloor || DEFAULT_BID_FLOOR + } + ], + site: { + page: loc.href, + referrer, + domain }, - 'imp': [{ - - 'banner': { - 'battr': bid.params.battr, - 'sizes': bidSizes, - }, - 'placementCode': bid.placementCode, - 'bidFloor': bid.params.bidFloor || DEFAULT_BID_FLOOR, - }] + bidId: bid.bidId }; - }); - utils._each(bidRequests, function (bidRequest) { - ajax.ajax(QUANTCAST_CALLBACK_URL, $$PREBID_GLOBAL$$.handleQuantcastCB, JSON.stringify(bidRequest), { + const data = JSON.stringify(requestData); + + const url = + bid.params.publisherId === QUANTCAST_TEST_PUBLISHER + ? publisherTagURLTest + : publisherTagURL; + + return { + data, method: 'POST', - withCredentials: true - }); + url + }; }); - } - // Export the `callBids` function, so that Prebid.js can execute - // this function when the page asks to send out bid requests. - return { - callBids: callBids, - QUANTCAST_CALLBACK_URL: QUANTCAST_CALLBACK_URL - }; -}; + return bidRequestsList; + }, + + /** + * Function get called when the browser has received the response from Quantcast server. + * The function parse the response and create a `bidResponse` object containing one/more bids. + * Returns an empty array if no valid bids + * + * Response Data Format can be found at https://wiki.corp.qc/display/adinf/QCX + * + * @param {*} serverResponse A successful response from Quantcast server. + * @return {Bid[]} An array of bids which were nested inside the server. + * + */ + interpretResponse(serverResponse) { + if (serverResponse === undefined) { + utils.logError('Server Response is undefined'); + return []; + } + + const response = serverResponse['body']; + + if ( + response === undefined || + !response.hasOwnProperty('bids') || + utils.isEmpty(response.bids) + ) { + utils.logError('Sub-optimal JSON received from Quantcast server'); + return []; + } -adaptermanager.registerBidAdapter(new QuantcastAdapter(), 'quantcast'); + const bidResponsesList = response.bids.map(bid => { + const { ad, cpm, width, height, creativeId, currency } = bid; + + return { + requestId: response.requestId, + cpm, + width, + height, + ad, + ttl: QUANTCAST_TTL, + creativeId, + netRevenue: QUANTCAST_NET_REVENUE, + currency + }; + }); + + return bidResponsesList; + } +}; -module.exports = QuantcastAdapter; +registerBidder(spec); diff --git a/modules/quantcastBidAdapter.md b/modules/quantcastBidAdapter.md new file mode 100644 index 00000000000..20cf25bffbf --- /dev/null +++ b/modules/quantcastBidAdapter.md @@ -0,0 +1,31 @@ +# Overview + +``` +Module Name: Quantcast Bidder Adapter +Module Type: Bidder Adapter +Maintainer: xli@quantcast.com +``` + +# Description + +Module that connects to Quantcast demand sources to fetch bids. + +# Test Parameters + +```js +const adUnits = [{ + code: 'banner', + sizes: [ + [300, 250] + ], + bids: [ + { + bidder: 'quantcast', + params: { + publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast + battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3 + } + } + ] +}]; +``` \ No newline at end of file diff --git a/modules/realvuBidAdapter.js b/modules/realvuBidAdapter.js new file mode 100644 index 00000000000..b3e0e4f043f --- /dev/null +++ b/modules/realvuBidAdapter.js @@ -0,0 +1,239 @@ +import { getBidRequest } from 'src/utils'; +import adaptermanager from 'src/adaptermanager'; + +const CONSTANTS = require('src/constants'); +const utils = require('src/utils.js'); +const adloader = require('src/adloader.js'); +const bidmanager = require('src/bidmanager.js'); +const bidfactory = require('src/bidfactory.js'); +const Adapter = require('src/adapter.js').default; + +var RealVuAdapter = function RealVuAdapter() { + var baseAdapter = new Adapter('realvu'); + baseAdapter.callBids = function (params) { + var pbids = params.bids; + var boost_back = function() { + var top1 = window; + realvu_frm = 0; + try { + var wnd = window; + while ((top1 != top) && (typeof (wnd.document) != 'undefined')) { + top1 = wnd; + wnd = wnd.parent; + } + } catch (e) { }; + for (var i = 0; i < pbids.length; i++) { + var bid_rq = pbids[i]; + var sizes = utils.parseSizesInput(bid_rq.sizes); + top1.realvu_boost.addUnitById({ + partner_id: bid_rq.params.partnerId, + unit_id: bid_rq.placementCode, + callback: baseAdapter.boostCall, + pbjs_bid: bid_rq, + size: sizes[0], + mode: 'kvp' + }); + } + }; + adloader.loadScript('//ac.realvu.net/realvu_boost.js', boost_back, 1); + }; + + baseAdapter.boostCall = function(rez) { + var bid_request = rez.pin.pbjs_bid; + var callbackId = bid_request.bidId; + if (rez.realvu === 'yes') { + var adap = new RvAppNexusAdapter(); + adloader.loadScript(adap.buildJPTCall(bid_request, callbackId)); + } else { // not in view - respond with no bid. + var adResponse = bidfactory.createBid(2); + adResponse.bidderCode = 'realvu'; + bidmanager.addBidResponse(bid_request.placementCode, adResponse); + } + }; + + // +copy/pasted appnexusBidAdapter, "handleAnCB" replaced with "handleRvAnCB" + var RvAppNexusAdapter = function RvAppNexusAdapter() { + var usersync = false; + + this.buildJPTCall = function (bid, callbackId) { + // determine tag params + var placementId = utils.getBidIdParameter('placementId', bid.params); + + // memberId will be deprecated, use member instead + var memberId = utils.getBidIdParameter('memberId', bid.params); + var member = utils.getBidIdParameter('member', bid.params); + var inventoryCode = utils.getBidIdParameter('invCode', bid.params); + var query = utils.getBidIdParameter('query', bid.params); + var referrer = utils.getBidIdParameter('referrer', bid.params); + var altReferrer = utils.getBidIdParameter('alt_referrer', bid.params); + var jptCall = '//ib.adnxs.com/jpt?'; + + jptCall = utils.tryAppendQueryString(jptCall, 'callback', '$$PREBID_GLOBAL$$.handleRvAnCB'); + jptCall = utils.tryAppendQueryString(jptCall, 'callback_uid', callbackId); + jptCall = utils.tryAppendQueryString(jptCall, 'psa', '0'); + jptCall = utils.tryAppendQueryString(jptCall, 'id', placementId); + if (member) { + jptCall = utils.tryAppendQueryString(jptCall, 'member', member); + } else if (memberId) { + jptCall = utils.tryAppendQueryString(jptCall, 'member', memberId); + utils.logMessage('appnexus.callBids: "memberId" will be deprecated soon. Please use "member" instead'); + } + + jptCall = utils.tryAppendQueryString(jptCall, 'code', inventoryCode); + jptCall = utils.tryAppendQueryString(jptCall, 'traffic_source_code', (utils.getBidIdParameter('trafficSourceCode', bid.params))); + + // sizes takes a bit more logic + var sizeQueryString = ''; + var parsedSizes = utils.parseSizesInput(bid.sizes); + + // combine string into proper querystring for impbus + var parsedSizesLength = parsedSizes.length; + if (parsedSizesLength > 0) { + // first value should be "size" + sizeQueryString = 'size=' + parsedSizes[0]; + if (parsedSizesLength > 1) { + // any subsequent values should be "promo_sizes" + sizeQueryString += '&promo_sizes='; + for (var j = 1; j < parsedSizesLength; j++) { + sizeQueryString += parsedSizes[j] += ','; + } + + // remove trailing comma + if (sizeQueryString && sizeQueryString.charAt(sizeQueryString.length - 1) === ',') { + sizeQueryString = sizeQueryString.slice(0, sizeQueryString.length - 1); + } + } + } + + if (sizeQueryString) { + jptCall += sizeQueryString + '&'; + } + + // this will be deprecated soon + var targetingParams = utils.parseQueryStringParameters(query); + + if (targetingParams) { + // don't append a & here, we have already done it in parseQueryStringParameters + jptCall += targetingParams; + } + + // append custom attributes: + var paramsCopy = Object.assign({}, bid.params); + + // delete attributes already used + delete paramsCopy.placementId; + delete paramsCopy.memberId; + delete paramsCopy.invCode; + delete paramsCopy.query; + delete paramsCopy.referrer; + delete paramsCopy.alt_referrer; + delete paramsCopy.member; + + // get the reminder + var queryParams = utils.parseQueryStringParameters(paramsCopy); + + // append + if (queryParams) { + jptCall += queryParams; + } + + // append referrer + if (referrer === '') { + referrer = utils.getTopWindowUrl(); + } + + jptCall = utils.tryAppendQueryString(jptCall, 'referrer', referrer); + jptCall = utils.tryAppendQueryString(jptCall, 'alt_referrer', altReferrer); + + // remove the trailing "&" + if (jptCall.lastIndexOf('&') === jptCall.length - 1) { + jptCall = jptCall.substring(0, jptCall.length - 1); + } + + // @if NODE_ENV='debug' + utils.logMessage('jpt request built: ' + jptCall); + // @endif + + // append a timer here to track latency + bid.startTime = new Date().getTime(); + + return jptCall; + } + + // expose the callback to the global object: + $$PREBID_GLOBAL$$.handleRvAnCB = function (jptResponseObj) { + var bidCode; + + if (jptResponseObj && jptResponseObj.callback_uid) { + var responseCPM; + var id = jptResponseObj.callback_uid; + var placementCode = ''; + var bidObj = getBidRequest(id); + if (bidObj) { + bidCode = bidObj.bidder; + + placementCode = bidObj.placementCode; + + // set the status + bidObj.status = CONSTANTS.STATUS.GOOD; + } + + // @if NODE_ENV='debug' + utils.logMessage('JSONP callback function called for ad ID: ' + id); + + // @endif + var bid = []; + if (jptResponseObj.result && jptResponseObj.result.cpm && jptResponseObj.result.cpm !== 0) { + responseCPM = parseInt(jptResponseObj.result.cpm, 10); + + // CPM response from /jpt is dollar/cent multiplied by 10000 + // in order to avoid using floats + // switch CPM to "dollar/cent" + responseCPM = responseCPM / 10000; + + // store bid response + // bid status is good (indicating 1) + var adId = jptResponseObj.result.creative_id; + bid = bidfactory.createBid(1, bidObj); + bid.creative_id = adId; + bid.bidderCode = bidCode; + bid.cpm = responseCPM; + bid.adUrl = jptResponseObj.result.ad; + bid.width = jptResponseObj.result.width; + bid.height = jptResponseObj.result.height; + bid.dealId = jptResponseObj.result.deal_id; + + bidmanager.addBidResponse(placementCode, bid); + } else { + // no bid + bid = bidfactory.createBid(2, bidObj); + bid.bidderCode = bidCode; + bidmanager.addBidResponse(placementCode, bid); + } + + if (!usersync) { + var iframe = utils.createInvisibleIframe(); + iframe.src = '//acdn.adnxs.com/ib/static/usersync/v3/async_usersync.html'; + try { + document.body.appendChild(iframe); + } catch (error) { + utils.logError(error); + } + usersync = true; + } + } else { + utils.logMessage('No prebid response for placement %%PLACEMENT%%'); + } + }; + }; + // -copy/pasted appnexusBidAdapter + return Object.assign(this, { + callBids: baseAdapter.callBids, + setBidderCode: baseAdapter.setBidderCode, + boostCall: baseAdapter.boostCall + }); +}; + +adaptermanager.registerBidAdapter(new RealVuAdapter(), 'realvu'); + +module.exports = RealVuAdapter; diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 69981ba2b56..2b7b0061430 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1,10 +1,8 @@ import * as utils from 'src/utils'; import { registerBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; -// use deferred function call since version isn't defined yet at this point -function getIntegration() { - return 'pbjs_lite_' + $$PREBID_GLOBAL$$.version; -} +const INTEGRATION = 'pbjs_lite_v$prebid.version$'; function isSecure() { return location.protocol === 'https:'; @@ -113,7 +111,7 @@ export const spec = { page_url: !params.referrer ? utils.getTopWindowUrl() : params.referrer, resolution: _getScreenResolution(), account_id: params.accountId, - integration: getIntegration(), + integration: INTEGRATION, timeout: bidderRequest.timeout - (Date.now() - bidderRequest.auctionStart + TIMEOUT_BUFFER), stash_creatives: true, ae_pass_through_parameters: params.video.aeParams, @@ -126,8 +124,8 @@ export const spec = { zone_id: params.zoneId, position: params.position || 'btf', floor: parseFloat(params.floor) > 0.01 ? params.floor : 0.01, - element_id: bidRequest.placementCode, - name: bidRequest.placementCode, + element_id: bidRequest.adUnitCode, + name: bidRequest.adUnitCode, language: params.video.language, width: size[0], height: size[1], @@ -187,7 +185,7 @@ export const spec = { 'p_pos', position, 'rp_floor', floor, 'rp_secure', isSecure() ? '1' : '0', - 'tk_flint', getIntegration(), + 'tk_flint', INTEGRATION, 'tid', bidRequest.transactionId, 'p_screen_res', _getScreenResolution(), 'kw', keywords, @@ -230,6 +228,7 @@ export const spec = { * @return {Bid[]} An array of bids which */ interpretResponse: function(responseObj, {bidRequest}) { + responseObj = responseObj.body let ads = responseObj.ads; // check overall response @@ -239,7 +238,7 @@ export const spec = { // video ads array is wrapped in an object if (typeof bidRequest === 'object' && bidRequest.mediaType === 'video' && typeof ads === 'object') { - ads = ads[bidRequest.placementCode]; + ads = ads[bidRequest.adUnitCode]; } // check the ad response @@ -258,10 +257,11 @@ export const spec = { let bid = { requestId: bidRequest.bidId, currency: 'USD', - creative_id: ad.creative_id, - bidderCode: spec.code, + creativeId: ad.creative_id, cpm: ad.cpm || 0, - dealId: ad.deal + dealId: ad.deal, + ttl: 300, // 5 minutes + netRevenue: config.getConfig('rubicon.netRevenue') || false }; if (bidRequest.mediaType === 'video') { bid.width = bidRequest.params.video.playerWidth; @@ -279,15 +279,15 @@ export const spec = { .reduce((memo, item) => { memo[item.key] = item.values[0]; return memo; - }, {'rpfl_elemid': bidRequest.placementCode}); + }, {'rpfl_elemid': bidRequest.adUnitCode}); bids.push(bid); return bids; }, []); }, - getUserSyncs: function() { - if (!hasSynced) { + getUserSyncs: function(syncOptions) { + if (!hasSynced && syncOptions.iframeEnabled) { hasSynced = true; return { type: 'iframe', @@ -307,7 +307,7 @@ function _getScreenResolution() { function _getDigiTrustQueryParams() { function getDigiTrustId() { - let digiTrustUser = window.DigiTrust && ($$PREBID_GLOBAL$$.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'})); + let digiTrustUser = window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'})); return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; } let digiTrustId = getDigiTrustId(); diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 81745427742..689de8635c9 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -12,7 +12,7 @@ var SonobiAdapter = function SonobiAdapter() { var trinity = 'https://apex.go.sonobi.com/trinity.js?key_maker='; var adSlots = request.bids || []; var bidderRequestId = request.bidderRequestId; - var ref = (window.frameElement) ? '&ref=' + encodeURI(top.location.host || document.referrer) : ''; + var ref = '&ref=' + encodeURI(utils.getTopWindowLocation().host); adloader.loadScript(trinity + JSON.stringify(_keymaker(adSlots)) + '&cv=' + _operator(bidderRequestId) + ref); } diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 13f893a841d..f16b8b96ec8 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -1,165 +1,148 @@ -const utils = require('src/utils.js'); -const bidfactory = require('src/bidfactory.js'); -const bidmanager = require('src/bidmanager.js'); -const adloader = require('src/adloader'); -const adaptermanager = require('src/adaptermanager'); -const CONSTANTS = require('src/constants.json'); - -var TrustxAdapter = function TrustxAdapter() { - const bidderCode = 'trustx'; - const reqHost = '//sofia.trustx.org'; - const reqPath = '/hb?'; - const LOG_ERROR_MESS = { - noAuid: 'Bid from response has no auid parameter - ', - noAdm: 'Bid from response has no adm parameter - ', - noBid: 'Array of bid objects is empty', - noPlacementCode: 'Can\'t find placementCode for bid with auid - ', - havePCodeFor: ', placementCode is available only for the following uids - ', - emptyUids: 'Uids should be not empty', - emptySeatbid: 'Seatbid array from response has empty item', - emptyResponse: 'Response is empty', - hasEmptySeatbidArray: 'Response has empty seatbid array', - hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' - }; +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'trustx'; +const ENDPOINT_URL = '//sofia.trustx.org/hb'; +const TIME_TO_LIVE = 360; +const ADAPTER_SYNC_URL = '//sofia.trustx.org/push_sync'; +const LOG_ERROR_MESS = { + noAuid: 'Bid from response has no auid parameter - ', + noAdm: 'Bid from response has no adm parameter - ', + noBid: 'Array of bid objects is empty', + noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', + emptyUids: 'Uids should be not empty', + emptySeatbid: 'Seatbid array from response has empty item', + emptyResponse: 'Response is empty', + hasEmptySeatbidArray: 'Response has empty seatbid array', + hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' +}; +export const spec = { + code: BIDDER_CODE, + /** + * 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.uid; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests) { + const auids = []; + const bidsMap = {}; + const bids = validBidRequests || []; + let priceType = 'net'; + + bids.forEach(bid => { + if (bid.params.priceType === 'gross') { + priceType = 'gross'; + } + if (!bidsMap[bid.params.uid]) { + bidsMap[bid.params.uid] = [bid]; + auids.push(bid.params.uid); + } else { + bidsMap[bid.params.uid].push(bid); + } + }); - function _makeHandler(auids, placementMap) { - var cbName = bidderCode + '_callback_wrapper_' + auids.join('_'); - $$PREBID_GLOBAL$$[cbName] = function(resp) { - delete $$PREBID_GLOBAL$$[cbName]; - _responseProcessing(resp, auids, placementMap); + const payload = { + u: utils.getTopWindowUrl(), + pt: priceType, + auids: auids.join(','), }; - return '$$PREBID_GLOBAL$$.' + cbName; - } - function _sendRequest(auids, placementMap) { - var query = []; - var path = reqPath; - query.push('u=' + encodeURIComponent(location.href)); - query.push('auids=' + encodeURIComponent(auids.join(','))); - query.push('cb=' + _makeHandler(auids, placementMap)); - query.push('pt=' + (window.globalPrebidTrustxPriceType === 'gross' ? 'gross' : 'net')); - - adloader.loadScript(reqHost + path + query.join('&')); - } - - function _callBids(params) { - var auids = []; - var placementMap = {}; - var hasBid; - var bid; - var bids = params.bids || []; - for (var i = 0; i < bids.length; i++) { - bid = bids[i]; - if (bid && bid.bidder === bidderCode && bid.placementCode) { - hasBid = true; - if (bid.params && bid.params.uid) { - if (!placementMap[bid.params.uid]) { - placementMap[bid.params.uid] = [bid.placementCode]; - auids.push(bid.params.uid); - } else { - placementMap[bid.params.uid].push(bid.placementCode); - } - } - } + return { + method: 'GET', + url: ENDPOINT_URL, + data: payload, + bidsMap: bidsMap, + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @param {*} bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + serverResponse = serverResponse && serverResponse.body + const bidResponses = []; + const bidsMap = bidRequest.bidsMap; + const priceType = bidRequest.data.pt; + + let errorMessage; + + if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; + else if (serverResponse.seatbid && !serverResponse.seatbid.length) { + errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; } - if (auids.length) { - _sendRequest(auids, placementMap); - } else if (hasBid) { - utils.logError(LOG_ERROR_MESS.emptyUids); + if (!errorMessage && serverResponse.seatbid) { + serverResponse.seatbid.forEach(respItem => { + _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses); + }); } - } - - function _getBidFromResponse(resp) { - if (!resp) { - utils.logError(LOG_ERROR_MESS.emptySeatbid); - } else if (!resp.bid) { - utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(resp)); - } else if (!resp.bid[0]) { - utils.logError(LOG_ERROR_MESS.noBid); + if (errorMessage) utils.logError(errorMessage); + return bidResponses; + }, + getUserSyncs: function(syncOptions) { + if (syncOptions.pixelEnabled) { + return [{ + type: 'image', + url: ADAPTER_SYNC_URL + }]; } - return resp && resp.bid && resp.bid[0]; } - - function _forEachPlacement(error, bid, placementCode) { - var bidObject; - if (error) { - bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid); - } else { - bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bid); - bidObject.cpm = bid.price; - bidObject.ad = bid.adm; - bidObject.width = bid.w; - bidObject.height = bid.h; - if (bid.dealid) { - bidObject.dealId = bid.dealid; - } - } - bidObject.bidderCode = bidderCode; - bidmanager.addBidResponse(placementCode, bidObject); +} + +function _getBidFromResponse(respItem) { + if (!respItem) { + utils.logError(LOG_ERROR_MESS.emptySeatbid); + } else if (!respItem.bid) { + utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(respItem)); + } else if (!respItem.bid[0]) { + utils.logError(LOG_ERROR_MESS.noBid); } - - function _addBidResponse(bid, auids, placementMap) { - if (!bid) return; - var errorMessage, placementCodes; - if (!bid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(bid); - else { - placementCodes = placementMap.hasOwnProperty(bid.auid) && placementMap[bid.auid]; - if (!placementCodes) { - errorMessage = LOG_ERROR_MESS.noPlacementCode + bid.auid + LOG_ERROR_MESS.havePCodeFor + auids.join(','); - } - } - - if (!errorMessage) { - if (!bid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(bid); - - var l = placementCodes.length; - while (l--) { - _forEachPlacement(errorMessage, bid, placementCodes[l]); - } - - delete placementMap[bid.auid]; - } - - if (errorMessage) { - utils.logError(errorMessage); + return respItem && respItem.bid && respItem.bid[0]; +} + +function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { + if (!serverBid) return; + let errorMessage; + if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); + if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); + else { + const awaitingBids = bidsMap[serverBid.auid]; + if (awaitingBids) { + awaitingBids.forEach(bid => { + const bidResponse = { + requestId: bid.bidId, // bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: priceType !== 'gross', + ttl: TIME_TO_LIVE, + ad: serverBid.adm, + dealId: serverBid.dealid + }; + bidResponses.push(bidResponse); + }); + } else { + errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; } } - - function _responseProcessing(resp, auids, placementMap) { - var errorMessage; - - if (!resp) errorMessage = LOG_ERROR_MESS.emptyResponse; - else if (resp.seatbid && !resp.seatbid.length) errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; - - if (!errorMessage) { - resp = resp.seatbid || []; - var l = resp.length; - while (l--) { - _addBidResponse(_getBidFromResponse(resp[l]), auids, placementMap); - } - } - - var n, bidObj; - for (var auid in placementMap) { - if (placementMap.hasOwnProperty(auid) && placementMap[auid]) { - n = placementMap[auid].length; - while (n--) { - bidObj = bidfactory.createBid(CONSTANTS.STATUS.NO_BID); - bidObj.bidderCode = bidderCode; - bidmanager.addBidResponse(placementMap[auid][n], bidObj); - } - } - } - - if (errorMessage) utils.logError(errorMessage); + if (errorMessage) { + utils.logError(errorMessage); } +} - return { - callBids: _callBids - }; -}; - -adaptermanager.registerBidAdapter(new TrustxAdapter(), 'trustx'); - -module.exports = TrustxAdapter; +registerBidder(spec); diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md new file mode 100755 index 00000000000..ca407b0c5e8 --- /dev/null +++ b/modules/trustxBidAdapter.md @@ -0,0 +1,40 @@ +# Overview + +Module Name: TrustX Bidder Adapter +Module Type: Bidder Adapter +Maintainer: paul@trustx.org + +# Description + +Module that connects to TrustX demand source to fetch bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "trustx", + params: { + uid: '44', + priceType: 'gross' // by default is 'net' + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: "trustx", + params: { + uid: 45, + priceType: 'gross' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index d311bb5722c..73f18794ea7 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -19,7 +19,7 @@ var YieldmoAdapter = function YieldmoAdapter() { function buildYieldmoCall(bids) { // build our base tag, based on if we are http or https - var ymURI = '//bid.yieldmo.com/exchange/prebid?'; + var ymURI = '//ads.yieldmo.com/exchange/prebid?'; var ymCall = document.location.protocol + ymURI; // Placement specific information diff --git a/package.json b/package.json index a5b573bcb41..6d14d33067c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "0.31.0", + "version": "0.32.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { diff --git a/src/adaptermanager.js b/src/adaptermanager.js index 2204e997084..34866365445 100644 --- a/src/adaptermanager.js +++ b/src/adaptermanager.js @@ -197,20 +197,18 @@ exports.callBids = ({adUnits, cbTimeout}) => { $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); _bidderRequests.push(bidderRequest); } + } else { + utils.logError(`Adapter trying to be called which does not exist: ${bidderCode} adaptermanager.callBids`); } }); _bidderRequests.forEach(bidRequest => { bidRequest.start = new Date().getTime(); const adapter = _bidderRegistry[bidRequest.bidderCode]; - if (adapter) { - if (bidRequest.bids && bidRequest.bids.length !== 0) { - utils.logMessage(`CALLING BIDDER ======= ${bidRequest.bidderCode}`); - events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest); - adapter.callBids(bidRequest); - } - } else { - utils.logError(`Adapter trying to be called which does not exist: ${bidRequest.bidderCode} adaptermanager.callBids`); + if (bidRequest.bids && bidRequest.bids.length !== 0) { + utils.logMessage(`CALLING BIDDER ======= ${bidRequest.bidderCode}`); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest); + adapter.callBids(bidRequest); } }) }; @@ -327,6 +325,10 @@ exports.setBidderSequence = function (order) { } }; +exports.getBidAdapter = function(bidder) { + return _bidderRegistry[bidder]; +}; + exports.setS2SConfig = function (config) { _s2sConfig = config; }; diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 06592736247..61446d8783e 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -44,10 +44,10 @@ import { logWarn, logError, parseQueryStringParameters, delayExecution } from 's * @property {function(BidRequest[], bidderRequest): ServerRequest|ServerRequest[]} buildRequests Build the request to the Server * which requests Bids for the given array of Requests. Each BidRequest in the argument array is guaranteed to have * passed the isBidRequestValid() test. - * @property {function(*, BidRequest): Bid[]} interpretResponse Given a successful response from the Server, + * @property {function(ServerResponse, BidRequest): Bid[]} interpretResponse Given a successful response from the Server, * interpret it and return the Bid objects. This function will be run inside a try/catch. * If it throws any errors, your bids will be discarded. - * @property {function(SyncOptions, Array): UserSync[]} [getUserSyncs] Given an array of all the responses + * @property {function(SyncOptions, ServerResponse[]): UserSync[]} [getUserSyncs] Given an array of all the responses * from the server, determine which user syncs should occur. The argument array will contain every element * which has been sent through to interpretResponse. The order of syncs in this array matters. The most * important ones should come first, since publishers may limit how many are dropped on their page. @@ -72,11 +72,20 @@ import { logWarn, logError, parseQueryStringParameters, delayExecution } from 's * JSON-serialized into the Request body. */ +/** + * @typedef {object} ServerResponse + * + * @property {*} body The response body. If this is legal JSON, then it will be parsed. Otherwise it'll be a + * string with the body's content. + * @property {get: function(string): string} headers The response headers. + * Call this like `ServerResponse.headers.get("Content-Type")` + */ + /** * @typedef {object} Bid * * @property {string} requestId The specific BidRequest which this bid is aimed at. - * This should correspond to one of the + * This should match the BidRequest.bidId which this Bid targets. * @property {string} ad A URL which can be used to load this ad, if it's chosen by the publisher. * @property {string} currency The currency code for the cpm value * @property {number} cpm The bid price, in US cents per thousand impressions. @@ -107,6 +116,9 @@ import { logWarn, logError, parseQueryStringParameters, delayExecution } from 's * @property {string} url The URL which makes the sync happen. */ +// common params for all mediaTypes +const COMMON_BID_RESPONSE_KEYS = ['requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency']; + /** * Register a bidder with prebid, using the given spec. * @@ -142,6 +154,7 @@ export function newBidder(spec) { getSpec: function() { return Object.freeze(spec); }, + registerSyncs, callBids: function(bidderRequest) { if (!Array.isArray(bidderRequest.bids)) { return; @@ -181,22 +194,9 @@ 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(request) { + function afterAllResponses() { fillNoBids(); - if (spec.getUserSyncs) { - let syncs = spec.getUserSyncs({ - iframeEnabled: config.getConfig('userSync.iframeEnabled'), - pixelEnabled: config.getConfig('userSync.pixelEnabled'), - }, responses, request); - if (syncs) { - if (!Array.isArray(syncs)) { - syncs = [syncs]; - } - syncs.forEach((sync) => { - userSync.registerSync(sync.type, spec.code, sync.url) - }); - } - } + registerSyncs(responses); } const validBidRequests = bidderRequest.bids.filter(filterAndWarn); @@ -207,6 +207,10 @@ export function newBidder(spec) { const bidRequestMap = {}; validBidRequests.forEach(bid => { bidRequestMap[bid.bidId] = bid; + // Delete this once we are 1.0 + if (!bid.adUnitCode) { + bid.adUnitCode = bid.placementCode + } }); let requests = spec.buildRequests(validBidRequests, bidderRequest); @@ -258,8 +262,6 @@ export function newBidder(spec) { typeof request.data === 'string' ? request.data : JSON.stringify(request.data), Object.assign({ method: 'POST', - contentType: request.contentType || 'text/plain', - customHeaders: request.customHeaders || {}, withCredentials: true }, request.options) ); @@ -272,10 +274,16 @@ export function newBidder(spec) { // If the server responds successfully, use the adapter code to unpack the Bids from it. // If the adapter code fails, no bids should be added. After all the bids have been added, make // sure to call the `onResponse` function so that we're one step closer to calling fillNoBids(). - function onSuccess(response) { + function onSuccess(response, responseObj) { try { response = JSON.parse(response); } catch (e) { /* response might not be JSON... that's ok. */ } + + // Make response headers available for #1742. These are lazy-loaded because most adapters won't need them. + response = { + body: response, + headers: headerParser(responseObj) + }; responses.push(response); let bids; @@ -294,17 +302,28 @@ export function newBidder(spec) { addBidUsingRequestMap(bids); } } - onResponse(request); + onResponse(); function addBidUsingRequestMap(bid) { - const bidRequest = bidRequestMap[bid.requestId]; - if (bidRequest) { - const prebidBid = Object.assign(bidfactory.createBid(STATUS.GOOD, bidRequest), bid); - addBidWithCode(bidRequest.placementCode, prebidBid); + // In Prebid 1.0 all the validation logic from bidmanager will move here, as of now we are only validating new params so that adapters dont miss adding them. + if (hasValidKeys(bid)) { + const bidRequest = bidRequestMap[bid.requestId]; + if (bidRequest) { + const prebidBid = Object.assign(bidfactory.createBid(STATUS.GOOD, bidRequest), bid); + addBidWithCode(bidRequest.placementCode, prebidBid); + } else { + logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`); + } } else { - logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`); + logError(`Bidder ${spec.code} is missing required params. Check http://prebid.org/dev-docs/bidder-adapter-1.html for list of params.`); } } + + function headerParser(xmlHttpResponse) { + return { + get: responseObj.getResponseHeader.bind(responseObj) + }; + } } // If the server responds with an error, there's not much we can do. Log it, and make sure to @@ -317,6 +336,23 @@ export function newBidder(spec) { } }); + function registerSyncs(responses) { + if (spec.getUserSyncs) { + let syncs = spec.getUserSyncs({ + iframeEnabled: config.getConfig('userSync.iframeEnabled'), + pixelEnabled: config.getConfig('userSync.pixelEnabled'), + }, responses); + if (syncs) { + if (!Array.isArray(syncs)) { + syncs = [syncs]; + } + syncs.forEach((sync) => { + userSync.registerSync(sync.type, spec.code, sync.url) + }); + } + } + } + function filterAndWarn(bid) { if (!spec.isBidRequestValid(bid)) { logWarn(`Invalid bid sent to bidder ${spec.code}: ${JSON.stringify(bid)}`); @@ -325,6 +361,11 @@ export function newBidder(spec) { return true; } + function hasValidKeys(bid) { + let bidKeys = Object.keys(bid); + return COMMON_BID_RESPONSE_KEYS.every(key => bidKeys.includes(key)); + } + function newEmptyBid() { const bid = bidfactory.createBid(STATUS.NO_BID); bid.code = spec.code; diff --git a/src/bidmanager.js b/src/bidmanager.js index 002a1150005..e425539f3f2 100644 --- a/src/bidmanager.js +++ b/src/bidmanager.js @@ -5,6 +5,7 @@ import { isValidVideoBid } from './video'; import { getCacheUrl, store } from './videoCache'; import { Renderer } from 'src/Renderer'; import { config } from 'src/config'; +import { createHook } from 'src/hook'; var CONSTANTS = require('./constants.json'); var AUCTION_END = CONSTANTS.EVENTS.AUCTION_END; @@ -84,7 +85,7 @@ exports.bidsBackAll = function () { /* * This function should be called to by the bidder adapter to register a bid response */ -exports.addBidResponse = function (adUnitCode, bid) { +exports.addBidResponse = createHook('asyncSeries', function (adUnitCode, bid) { if (isValid()) { prepareBidForAuction(); @@ -255,7 +256,7 @@ exports.addBidResponse = function (adUnitCode, bid) { doCallbacksIfNeeded(); } } -}; +}); function getKeyValueTargetingPairs(bidderCode, custBidObj) { var keyValues = {}; diff --git a/src/hook.js b/src/hook.js new file mode 100644 index 00000000000..5ba1d4b9bbf --- /dev/null +++ b/src/hook.js @@ -0,0 +1,78 @@ + +/** + * @typedef {function} HookedFunction + * @property {function(function(), [number])} addHook A method that takes a new function to attach as a hook + * to the HookedFunction + * @property {function(function())} removeHook A method to remove attached hooks + */ + +/** + * A map of global hook methods to allow easy extension of hooked functions that are intended to be extended globally + * @type {{}} + */ +export const hooks = {}; + +/** + * A utility function for allowing a regular function to be extensible with additional hook functions + * @param {string} type The method for applying all attached hooks when this hooked function is called + * @param {function()} fn The function to make hookable + * @param {string} hookName If provided this allows you to register a name for a global hook to have easy access to + * the addHook and removeHook methods for that hook (which are usually accessed as methods on the function itself) + * @returns {HookedFunction} A new function that implements the HookedFunction interface + */ +export function createHook(type, fn, hookName) { + let _hooks = [{fn, priority: 0}]; + + let types = { + sync: function(...args) { + _hooks.forEach(hook => { + hook.fn.apply(this, args); + }); + }, + asyncSeries: function(...args) { + let curr = 0; + + const asyncSeriesNext = (...args) => { + let hook = _hooks[++curr]; + if (typeof hook === 'object' && typeof hook.fn === 'function') { + return hook.fn.apply(this, args.concat(asyncSeriesNext)) + } + }; + + return _hooks[curr].fn.apply(this, args.concat(asyncSeriesNext)); + } + }; + + if (!types[type]) { + throw 'invalid hook type'; + } + + let methods = { + addHook: function(fn, priority = 10) { + if (typeof fn === 'function') { + _hooks.push({ + fn, + priority: priority + }); + + _hooks.sort((a, b) => b.priority - a.priority); + } + }, + removeHook: function(removeFn) { + _hooks = _hooks.filter(hook => hook.fn === fn || hook.fn !== removeFn); + } + }; + + if (typeof hookName === 'string') { + hooks[hookName] = methods; + } + + function hookedFn(...args) { + if (_hooks.length === 0) { + return fn.apply(this, args); + } + return types[type].apply(this, args); + } + + return Object.assign(hookedFn, methods); +} diff --git a/src/prebid.js b/src/prebid.js index d46f2c64227..12b588d54a1 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,4 +1,4 @@ -/** @module $$PREBID_GLOBAL$$ */ +/** @module pbjs */ import { getGlobal } from './prebidGlobal'; import { flatten, uniques, isGptPubadsDefined, adUnitsFilter } from './utils'; @@ -117,7 +117,7 @@ function setRenderSize(doc, width, height) { /** * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. * @param {string} [adunitCode] adUnitCode to get the bid responses for - * @alias module:$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr + * @alias module:pbjs.getAdserverTargetingForAdUnitCodeStr * @return {Array} returnObj return bids array */ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { @@ -135,6 +135,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { /** * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. * @param adUnitCode {string} adUnitCode to get the bid responses for + * @alias module:pbjs.getAdserverTargetingForAdUnitCode * @returns {Object} returnObj return bids */ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) { @@ -144,7 +145,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) { /** * returns all ad server targeting for all ad units * @return {Object} Map of adUnitCodes and targeting values [] - * @alias module:$$PREBID_GLOBAL$$.getAdserverTargeting + * @alias module:pbjs.getAdserverTargeting */ $$PREBID_GLOBAL$$.getAdserverTargeting = function (adUnitCode) { @@ -169,7 +170,7 @@ $$PREBID_GLOBAL$$.getAdserverTargeting = function (adUnitCode) { /** * This function returns the bid responses at the given moment. - * @alias module:$$PREBID_GLOBAL$$.getBidResponses + * @alias module:pbjs.getBidResponses * @return {Object} map | object that contains the bidResponses */ @@ -196,7 +197,7 @@ $$PREBID_GLOBAL$$.getBidResponses = function () { /** * Returns bidResponses for the specified adUnitCode * @param {string} adUnitCode adUnitCode - * @alias module:$$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode + * @alias module:pbjs.getBidResponsesForAdUnitCode * @return {Object} bidResponse object */ @@ -210,7 +211,7 @@ $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode = function (adUnitCode) { /** * Set query string targeting on one or more GPT ad units. * @param {(string|string[])} adUnit a single `adUnit.code` or multiple. - * @alias module:$$PREBID_GLOBAL$$.setTargetingForGPTAsync + * @alias module:pbjs.setTargetingForGPTAsync */ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function (adUnit) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.setTargetingForGPTAsync', arguments); @@ -232,6 +233,10 @@ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function (adUnit) { events.emit(SET_TARGETING); }; +/** + * Set query string targeting on all AST (AppNexus Seller Tag) ad units. Note that this function has to be called after all ad units on page are defined. For working example code, see [Using Prebid.js with AppNexus Publisher Ad Server](http://prebid.org/dev-docs/examples/use-prebid-with-appnexus-ad-server.html). + * @alias module:pbjs.setTargetingForAst + */ $$PREBID_GLOBAL$$.setTargetingForAst = function() { utils.logInfo('Invoking $$PREBID_GLOBAL$$.setTargetingForAn', arguments); if (!targeting.isApntagDefined()) { @@ -247,7 +252,7 @@ $$PREBID_GLOBAL$$.setTargetingForAst = function() { /** * Returns a bool if all the bids have returned or timed out - * @alias module:$$PREBID_GLOBAL$$.allBidsAvailable + * @alias module:pbjs.allBidsAvailable * @return {bool} all bids available * * @deprecated This function will be removed in Prebid 1.0 @@ -265,7 +270,7 @@ $$PREBID_GLOBAL$$.allBidsAvailable = function () { * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously * @param {HTMLDocument} doc document * @param {string} id bid id to locate the ad - * @alias module:$$PREBID_GLOBAL$$.renderAd + * @alias module:pbjs.renderAd */ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.renderAd', arguments); @@ -321,7 +326,7 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { /** * Remove adUnit from the $$PREBID_GLOBAL$$ configuration * @param {string} adUnitCode the adUnitCode to remove - * @alias module:$$PREBID_GLOBAL$$.removeAdUnit + * @alias module:pbjs.removeAdUnit */ $$PREBID_GLOBAL$$.removeAdUnit = function (adUnitCode) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.removeAdUnit', arguments); @@ -334,6 +339,9 @@ $$PREBID_GLOBAL$$.removeAdUnit = function (adUnitCode) { } }; +/** + * @alias module:pbjs.clearAuction + */ $$PREBID_GLOBAL$$.clearAuction = function() { auctionRunning = false; // Only automatically sync if the publisher has not chosen to "enableOverride" @@ -355,6 +363,7 @@ $$PREBID_GLOBAL$$.clearAuction = function() { * @param {number} requestOptions.timeout * @param {Array} requestOptions.adUnits * @param {Array} requestOptions.adUnitCodes + * @alias module:pbjs.requestBids */ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, adUnitCodes } = {}) { events.emit('requestBids'); @@ -449,7 +458,7 @@ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, a * * Add adunit(s) * @param {Array|Object} adUnitArr Array of adUnits or single adUnit Object. - * @alias module:$$PREBID_GLOBAL$$.addAdUnits + * @alias module:pbjs.addAdUnits */ $$PREBID_GLOBAL$$.addAdUnits = function (adUnitArr) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.addAdUnits', arguments); @@ -471,6 +480,7 @@ $$PREBID_GLOBAL$$.addAdUnits = function (adUnitArr) { * @param {string} event the name of the event * @param {Function} handler a callback to set on event * @param {string} id an identifier in the context of the event + * @alias module:pbjs.onEvent * * This API call allows you to register a callback to handle a Prebid.js event. * An optional `id` parameter provides more finely-grained event callback registration. @@ -501,6 +511,7 @@ $$PREBID_GLOBAL$$.onEvent = function (event, handler, id) { * @param {string} event the name of the event * @param {Function} handler a callback to remove from the event * @param {string} id an identifier in the context of the event (see `$$PREBID_GLOBAL$$.onEvent`) + * @alias module:pbjs.offEvent */ $$PREBID_GLOBAL$$.offEvent = function (event, handler, id) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.offEvent', arguments); @@ -515,7 +526,7 @@ $$PREBID_GLOBAL$$.offEvent = function (event, handler, id) { * Add a callback event * @param {string} eventStr event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack" * @param {Function} func function to execute. Parameters passed into the function: (bidResObj), [adUnitCode]); - * @alias module:$$PREBID_GLOBAL$$.addCallback + * @alias module:pbjs.addCallback * @returns {string} id for callback * * @deprecated This function will be removed in Prebid 1.0 @@ -538,7 +549,7 @@ $$PREBID_GLOBAL$$.addCallback = function (eventStr, func) { /** * Remove a callback event * //@param {string} cbId id of the callback to remove - * @alias module:$$PREBID_GLOBAL$$.removeCallback + * @alias module:pbjs.removeCallback * @returns {string} id for callback * * @deprecated This function will be removed in Prebid 1.0 @@ -554,6 +565,7 @@ $$PREBID_GLOBAL$$.removeCallback = function (/* cbId */) { * Wrapper to register bidderAdapter externally (adaptermanager.registerBidAdapter()) * @param {Function} bidderAdaptor [description] * @param {string} bidderCode [description] + * @alias module:pbjs.registerBidAdapter */ $$PREBID_GLOBAL$$.registerBidAdapter = function (bidderAdaptor, bidderCode) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.registerBidAdapter', arguments); @@ -567,6 +579,7 @@ $$PREBID_GLOBAL$$.registerBidAdapter = function (bidderAdaptor, bidderCode) { /** * Wrapper to register analyticsAdapter externally (adaptermanager.registerAnalyticsAdapter()) * @param {Object} options [description] + * @alias module:pbjs.registerAnalyticsAdapter */ $$PREBID_GLOBAL$$.registerAnalyticsAdapter = function (options) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.registerAnalyticsAdapter', arguments); @@ -577,6 +590,9 @@ $$PREBID_GLOBAL$$.registerAnalyticsAdapter = function (options) { } }; +/** + * @alias module:pbjs.bidsAvailableForAdapter +*/ $$PREBID_GLOBAL$$.bidsAvailableForAdapter = function (bidderCode) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.bidsAvailableForAdapter', arguments); @@ -593,6 +609,7 @@ $$PREBID_GLOBAL$$.bidsAvailableForAdapter = function (bidderCode) { /** * Wrapper to bidfactory.createBid() * @param {string} statusCode [description] + * @alias module:pbjs.createBid * @return {Object} bidResponse [description] */ $$PREBID_GLOBAL$$.createBid = function (statusCode) { @@ -604,7 +621,7 @@ $$PREBID_GLOBAL$$.createBid = function (statusCode) { * Wrapper to bidmanager.addBidResponse * @param {string} adUnitCode [description] * @param {Object} bid [description] - * + * @alias module:pbjs.addBidResponse * @deprecated This function will be removed in Prebid 1.0 * Each bidder will be passed a reference to addBidResponse function in callBids as an argument. * See https://github.com/prebid/Prebid.js/issues/1087 for more details. @@ -619,6 +636,7 @@ $$PREBID_GLOBAL$$.addBidResponse = function (adUnitCode, bid) { * Wrapper to adloader.loadScript * @param {string} tagSrc [description] * @param {Function} callback [description] + * @alias module:pbjs.loadScript */ $$PREBID_GLOBAL$$.loadScript = function (tagSrc, callback, useCache) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.loadScript', arguments); @@ -632,11 +650,12 @@ $$PREBID_GLOBAL$$.loadScript = function (tagSrc, callback, useCache) { * For usage, see [Integrate with the Prebid Analytics * API](http://prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html). * - * For a list of supported analytics adapters, see [Analytics for + * For a list of analytics adapters, see [Analytics for * Prebid](http://prebid.org/overview/analytics.html). * @param {Object} config * @param {string} config.provider The name of the provider, e.g., `"ga"` for Google Analytics. * @param {Object} config.options The options for this particular analytics adapter. This will likely vary between adapters. + * @alias module:pbjs.enableAnalytics */ $$PREBID_GLOBAL$$.enableAnalytics = function (config) { if (config && !utils.isEmpty(config)) { @@ -647,6 +666,9 @@ $$PREBID_GLOBAL$$.enableAnalytics = function (config) { } }; +/** + * @alias module:pbjs.aliasBidder + */ $$PREBID_GLOBAL$$.aliasBidder = function (bidderCode, alias) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.aliasBidder', arguments); if (bidderCode && alias) { @@ -659,6 +681,7 @@ $$PREBID_GLOBAL$$.aliasBidder = function (bidderCode, alias) { /** * Sets a default price granularity scheme. * @param {string|Object} granularity - the granularity scheme. + * @alias module:pbjs.setPriceGranularity * @deprecated - use pbjs.setConfig({ priceGranularity: }) * "low": $0.50 increments, capped at $5 CPM * "medium": $0.10 increments, capped at $20 CPM (the default) @@ -676,12 +699,16 @@ $$PREBID_GLOBAL$$.setPriceGranularity = function (granularity) { config.setConfig({ priceGranularity: granularity }); }; -/** @deprecated - use pbjs.setConfig({ enableSendAllBids: }) */ +/** + * @alias module:pbjs.enableSendAllBids + * @deprecated - use pbjs.setConfig({ enableSendAllBids: }) +*/ $$PREBID_GLOBAL$$.enableSendAllBids = function () { config.setConfig({ enableSendAllBids: true }); }; /** + * @alias module:pbjs.getAllWinningBids * The bid response object returned by an external bidder adapter during the auction. * @typedef {Object} AdapterBidResponse * @property {string} pbAg Auto granularity price bucket; CPM <= 5 ? increment = 0.05 : CPM > 5 && CPM <= 10 ? increment = 0.10 : CPM > 10 && CPM <= 20 ? increment = 0.50 : CPM > 20 ? priceCap = 20.00. Example: `"0.80"`. @@ -728,7 +755,7 @@ $$PREBID_GLOBAL$$.getAllWinningBids = function () { * Build master video tag from publishers adserver tag * @param {string} adserverTag default url * @param {Object} options options for video tag - * + * @alias module:pbjs.buildMasterVideoTagFromAdserverTag * @deprecated Include the dfpVideoSupport module in your build, and use the $$PREBID_GLOBAL$$.adservers.dfp.buildVideoAdUrl function instead. * This function will be removed in Prebid 1.0. */ @@ -766,6 +793,7 @@ $$PREBID_GLOBAL$$.buildMasterVideoTagFromAdserverTag = function (adserverTag, op * If never called, Prebid will use "random" as the default. * * @param {string} order One of the valid orders, described above. + * @alias module:pbjs.setBidderSequence * @deprecated - use pbjs.setConfig({ bidderSequence: }) */ $$PREBID_GLOBAL$$.setBidderSequence = adaptermanager.setBidderSequence; @@ -774,6 +802,7 @@ $$PREBID_GLOBAL$$.setBidderSequence = adaptermanager.setBidderSequence; * Get array of highest cpm bids for all adUnits, or highest cpm bid * object for the given adUnit * @param {string} adUnitCode - optional ad unit code + * @alias module:pbjs.getHighestCpmBids * @return {Array} array containing highest cpm bid object(s) */ $$PREBID_GLOBAL$$.getHighestCpmBids = function (adUnitCode) { @@ -792,6 +821,7 @@ $$PREBID_GLOBAL$$.getHighestCpmBids = function (adUnitCode) { * @property {string} [adapter] adapter code to use for S2S * @property {string} [syncEndpoint] endpoint URL for syncing cookies * @property {boolean} [cookieSet] enables cookieSet functionality + * @alias module:pbjs.setS2SConfig */ $$PREBID_GLOBAL$$.setS2SConfig = function(options) { if (!utils.contains(Object.keys(options), 'accountId')) { @@ -820,6 +850,7 @@ $$PREBID_GLOBAL$$.setS2SConfig = function(options) { /** * Get Prebid config options * @param {Object} options + * @alias module:pbjs.getConfig */ $$PREBID_GLOBAL$$.getConfig = config.getConfig; @@ -856,6 +887,7 @@ $$PREBID_GLOBAL$$.getConfig = config.getConfig; * @param {string} options.publisherDomain The publisher's domain where Prebid is running, for cross-domain iFrame communication. Example: `pbjs.setConfig({ publisherDomain: "https://www.theverge.com" })`. * @param {number} options.cookieSyncDelay A delay (in milliseconds) for requesting cookie sync to stay out of the critical path of page load. Example: `pbjs.setConfig({ cookieSyncDelay: 100 })`. * @param {Object} options.s2sConfig The configuration object for [server-to-server header bidding](http://prebid.org/dev-docs/get-started-with-prebid-server.html). Example: + * @alias module:pbjs.setConfig * ``` * pbjs.setConfig({ * s2sConfig: { @@ -888,10 +920,10 @@ $$PREBID_GLOBAL$$.que.push(() => listenMessagesFromCreative()); * by prebid once it's done loading. If it runs after prebid loads, then this monkey-patch causes their * function to execute immediately. * - * @memberof $$PREBID_GLOBAL$$ + * @memberof pbjs * @param {function} command A function which takes no arguments. This is guaranteed to run exactly once, and only after * the Prebid script has been fully loaded. - * @alias module:$$PREBID_GLOBAL$$.cmd.push + * @alias module:pbjs.cmd.push */ $$PREBID_GLOBAL$$.cmd.push = function(command) { if (typeof command === 'function') { @@ -920,6 +952,9 @@ function processQueue(queue) { }); } +/** + * @alias module:pbjs.processQueue + */ $$PREBID_GLOBAL$$.processQueue = function() { processQueue($$PREBID_GLOBAL$$.que); processQueue($$PREBID_GLOBAL$$.cmd); diff --git a/src/utils.js b/src/utils.js index 00a06fcb091..9efa4f53c57 100644 --- a/src/utils.js +++ b/src/utils.js @@ -339,7 +339,7 @@ exports.isNumber = function(object) { */ exports.isEmpty = function (object) { if (!object) return true; - if (this.isArray(object) || this.isStr(object)) { + if (exports.isArray(object) || exports.isStr(object)) { return !(object.length > 0); } diff --git a/src/video.js b/src/video.js index 386b6b692e9..f5203e4b198 100644 --- a/src/video.js +++ b/src/video.js @@ -1,5 +1,6 @@ import { videoAdapters } from './adaptermanager'; -import { getBidRequest, deepAccess } from './utils'; +import { getBidRequest, deepAccess, logError } from './utils'; +import { config } from '../src/config'; const VIDEO_MEDIA_TYPE = 'video'; const OUTSTREAM = 'outstream'; @@ -32,6 +33,15 @@ export function isValidVideoBid(bid) { // if context not defined assume default 'instream' for video bids // instream bids require a vast url or vast xml content if (!bidRequest || (videoMediaType && context !== OUTSTREAM)) { + // xml-only video bids require prebid-cache to be enabled + if (!config.getConfig('usePrebidCache') && bid.vastXml && !bid.vastUrl) { + logError(` + This bid contains only vastXml and will not work when prebid-cache is disabled. + Try enabling prebid-cache with pbjs.setConfig({ usePrebidCache: true }); + `); + return false; + } + return !!(bid.vastUrl || bid.vastXml); } diff --git a/test/spec/hook_spec.js b/test/spec/hook_spec.js new file mode 100644 index 00000000000..1fab4ecd1b7 --- /dev/null +++ b/test/spec/hook_spec.js @@ -0,0 +1,151 @@ + +import { expect } from 'chai'; +import { createHook, hooks } from 'src/hook'; + +describe('the hook module', () => { + let sandbox; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call all sync hooks attached to a function', () => { + let called = []; + let calledWith; + + let testFn = () => { + called.push(testFn); + }; + let testHook = (...args) => { + called.push(testHook); + calledWith = args; + }; + let testHook2 = () => { + called.push(testHook2); + }; + let testHook3 = () => { + called.push(testHook3); + }; + + let hookedTestFn = createHook('sync', testFn, 'testHook'); + + hookedTestFn.addHook(testHook, 50); + hookedTestFn.addHook(testHook2, 100); + + // make sure global test hooks work as well (with default priority) + hooks['testHook'].addHook(testHook3); + + hookedTestFn(1, 2, 3); + + expect(called).to.deep.equal([ + testHook2, + testHook, + testHook3, + testFn + ]); + + expect(calledWith).to.deep.equal([1, 2, 3]); + + called = []; + + hookedTestFn.removeHook(testHook); + hooks['testHook'].removeHook(testHook3); + + hookedTestFn(1, 2, 3); + + expect(called).to.deep.equal([ + testHook2, + testFn + ]); + }); + + it('should allow context to be passed to hooks, but keep bound contexts', () => { + let context; + let fn = function() { + context = this; + }; + + let boundContext = {}; + let calledBoundContext; + let hook = function() { + calledBoundContext = this; + }.bind(boundContext); + + let hookFn = createHook('sync', fn); + hookFn.addHook(hook); + + let newContext = {}; + hookFn.bind(newContext)(); + + expect(context).to.equal(newContext); + expect(calledBoundContext).to.equal(boundContext); + }); + + describe('asyncSeries', () => { + it('should call function as normal if no hooks attached', () => { + let fn = sandbox.spy(); + let hookFn = createHook('asyncSeries', fn); + + hookFn(1); + + expect(fn.calledOnce).to.equal(true); + expect(fn.firstCall.args[0]).to.equal(1); + }); + + it('should call hooks correctly applied in asyncSeries', () => { + let called = []; + + let testFn = (called) => { + called.push(testFn); + }; + let testHook = (called, next) => { + called.push(testHook); + next(called); + }; + let testHook2 = (called, next) => { + called.push(testHook2); + next(called); + }; + + let hookedTestFn = createHook('asyncSeries', testFn); + hookedTestFn.addHook(testHook); + hookedTestFn.addHook(testHook2); + + hookedTestFn(called); + + expect(called).to.deep.equal([ + testHook, + testHook2, + testFn + ]); + }); + + it('should allow context to be passed to hooks, but keep bound contexts', () => { + let context; + let fn = function() { + context = this; + }; + + let boundContext1 = {}; + let calledBoundContext1; + let hook1 = function(next) { + calledBoundContext1 = this; + next() + }.bind(boundContext1); + + let hookFn = createHook('asyncSeries', fn); + hookFn.addHook(hook1); + + let newContext = {}; + hookFn = hookFn.bind(newContext); + hookFn(); + + expect(context).to.equal(newContext); + expect(calledBoundContext1).to.equal(boundContext1); + }); + }); +}); diff --git a/test/spec/modules/adbutlerBidAdapter_spec.js b/test/spec/modules/adbutlerBidAdapter_spec.js index d026ac8de98..de40f72073b 100644 --- a/test/spec/modules/adbutlerBidAdapter_spec.js +++ b/test/spec/modules/adbutlerBidAdapter_spec.js @@ -1,516 +1,211 @@ -describe('adbutler adapter tests', function () { - var expect = require('chai').expect; - var adapter = require('modules/adbutlerBidAdapter'); - var adLoader = require('src/adloader'); - var bidmanager = require('src/bidmanager'); +import {expect} from 'chai'; +import {spec} from 'modules/adbutlerBidAdapter'; - describe('creation of bid url', function () { - var stubLoadScript; +describe('AdButler adapter', () => { + let bidRequests; - beforeEach(function () { - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - }); - - afterEach(function () { - stubLoadScript.restore(); - }); - - if (typeof ($$PREBID_GLOBAL$$._bidsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._bidsReceived = []; - } - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = []; - } - if (typeof ($$PREBID_GLOBAL$$._adsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._adsReceived = []; - } - - it('should be called', function () { - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093' - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - adapter().callBids(params); - - sinon.assert.called(stubLoadScript); - }); - - it('should populate the keyword', function() { - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093', - keyword: 'fish' - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - adapter().callBids(params); - - var requestURI = stubLoadScript.getCall(0).args[0]; - - expect(requestURI).to.have.string(';kw=fish;'); - }); - - it('should use custom domain string', function() { - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '107878', - zoneID: '86133', - domain: 'servedbyadbutler.com.dan.test' - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - ] - }; - - adapter().callBids(params); - - var requestURI = stubLoadScript.getCall(0).args[0]; - - expect(requestURI).to.have.string('.dan.test'); - }); - }); - describe('bid responses', function() { - it('should return complete bid response', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var params = { - bidderCode: 'adbutler', + beforeEach(() => { + bidRequests = [ + { bidder: 'adbutler', - bids: [ - { - bidId: '3c94018cdbf2f68-1', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093', - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 1.5, - width: 300, - height: 250, - place: 0 - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); + params: { + accountID: '167283', + zoneID: '210093', + keyword: 'red', + minCPM: '1.00', + maxCPM: '5.00' + }, + placementCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('/123456/header-bid-tag-1'); - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.bidderCode).to.equal('adbutler'); - expect(bidObject1.cpm).to.equal(1.5); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - - stubAddBidResponse.restore(); - }); - - it('should return empty bid response', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68-2', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210085', - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'NO_ELIGIBLE_ADS', - zone_id: 210085, - width: 728, - height: 90, - place: 0 - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('/123456/header-bid-tag-1'); - expect(bidObject1.getStatusCode()).to.equal(2); - expect(bidObject1.bidderCode).to.equal('adbutler'); - - stubAddBidResponse.restore(); - }); - - it('should return empty bid response on incorrect size', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68-3', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210085', - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210085, - cpm: 1.5, - width: 728, - height: 90, - place: 0 - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); - - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidObject1.getStatusCode()).to.equal(2); - - stubAddBidResponse.restore(); - }); - - it('should return empty bid response with CPM too low', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68-4', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093', - minCPM: '5.00' - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 1.5, - width: 300, - height: 250, - place: 0 - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); - - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidObject1.getStatusCode()).to.equal(2); - - stubAddBidResponse.restore(); - }); - - it('should return empty bid response with CPM too high', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68-5', - sizes: [[300, 250]], - bidder: 'adbutler', - params: { - accountID: '167283', - zoneID: '210093', - maxCPM: '1.00' - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 1.5, - width: 300, - height: 250, - place: 0 - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); - - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidObject1.getStatusCode()).to.equal(2); - - stubAddBidResponse.restore(); - }); + ]; }); - describe('ad code', function() { - it('should be populated', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68-6', - sizes: [[300, 250]], + describe('implementation', () => { + describe('for requests', () => { + it('should accept valid bid', () => { + let validBid = { bidder: 'adbutler', params: { accountID: '167283', zoneID: '210093' - }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 1.5, - width: 300, - height: 250, - place: 0, - ad_code: '' - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); + } + }, + isValid = spec.isBidRequestValid(validBid); - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.ad).to.have.length.above(1); - - stubAddBidResponse.restore(); - }); + expect(isValid).to.equal(true); + }); - it('should contain tracking pixels', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var params = { - bidderCode: 'adbutler', - bids: [ - { - bidId: '3c9408cdbf2f68-7', - sizes: [[300, 250]], + it('should reject invalid bid', () => { + let invalidBid = { bidder: 'adbutler', params: { accountID: '167283', - zoneID: '210093' + } + }, + isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should use custom domain string', () => { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250]], + bidder: 'adbutler', + params: { + accountID: '107878', + zoneID: '86133', + domain: 'servedbyadbutler.com.dan.test' + }, + requestId: '10b327aa396609', + placementCode: '/123456/header-bid-tag-1' + } + ], + requests = spec.buildRequests(bidRequests), + requestURL = requests[0].url; + + expect(requestURL).to.have.string('.dan.test'); + }); + + it('should set default domain', () => { + let requests = spec.buildRequests(bidRequests), + request = requests[0]; + + let [domain] = request.url.split('/adserve/'); + + expect(domain).to.equal('http://servedbyadbutler.com'); + }); + + it('should set the keyword parameter', () => { + let requests = spec.buildRequests(bidRequests), + requestURL = requests[0].url; + + expect(requestURL).to.have.string(';kw=red;'); + }); + + it('should increment the count for the same zone', () => { + let bidRequests = [ + { + sizes: [[300, 250]], + bidder: 'adbutler', + params: { + accountID: '107878', + zoneID: '86133', + domain: 'servedbyadbutler.com.dan.test' + } + }, { + sizes: [[300, 250]], + bidder: 'adbutler', + params: { + accountID: '107878', + zoneID: '86133', + domain: 'servedbyadbutler.com.dan.test' + } }, - requestId: '10b327aa396609', - placementCode: '/123456/header-bid-tag-1' - } - - ] - }; - - var response = { - status: 'SUCCESS', - account_id: 167283, - zone_id: 210093, - cpm: 1.5, - width: 300, - height: 250, - place: 0, - ad_code: '', - tracking_pixels: [ - 'http://tracking.pixel.com/params=info' - ] - }; - - adapter().callBids(params); - - var adUnits = new Array(); - var unit = new Object(); - unit.bids = params.bids; - unit.code = '/123456/header-bid-tag-1'; - unit.sizes = [[300, 250]]; - adUnits.push(unit); - - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - - $$PREBID_GLOBAL$$.adUnits = adUnits; - - $$PREBID_GLOBAL$$.adbutlerCB(response); - - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.ad).to.have.string('http://tracking.pixel.com/params=info'); + ], + requests = spec.buildRequests(bidRequests), + firstRequest = requests[0].url, + secondRequest = requests[1].url; + + expect(firstRequest).to.have.string(';place=0;'); + expect(secondRequest).to.have.string(';place=1;'); + }); + }); - stubAddBidResponse.restore(); + describe('bid responses', () => { + it('should return complete bid response', () => { + let serverResponse = { + body: { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210093, + cpm: 1.5, + width: 300, + height: 250, + place: 0, + ad_code: '', + tracking_pixels: [ + 'http://tracking.pixel.com/params=info' + ] + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(1); + + expect(bids[0].bidderCode).to.equal('adbutler'); + expect(bids[0].cpm).to.equal(1.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].ad).to.have.string('http://tracking.pixel.com/params=info'); + }); + + it('should return empty bid response', () => { + let serverResponse = { + status: 'NO_ELIGIBLE_ADS', + zone_id: 210083, + width: 300, + height: 250, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response on incorrect size', () => { + let serverResponse = { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210083, + cpm: 1.5, + width: 728, + height: 90, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with CPM too low', () => { + let serverResponse = { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210093, + cpm: 0.75, + width: 300, + height: 250, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with CPM too high', () => { + let serverResponse = { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210093, + cpm: 7.00, + width: 300, + height: 250, + place: 0 + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); }); }); }); diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 5fabbad7fbf..b958e96f656 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -1,9 +1,6 @@ import {expect} from 'chai'; -import Adapter from 'modules/adkernelBidAdapter'; -import * as ajax from 'src/ajax'; +import {spec} from 'modules/adkernelBidAdapter'; import * as utils from 'src/utils'; -import bidmanager from 'src/bidmanager'; -import CONSTANTS from 'src/constants.json'; describe('Adkernel adapter', () => { const bid1_zone1 = { @@ -36,6 +33,12 @@ describe('Adkernel adapter', () => { params: {zoneId: 1}, placementCode: 'ad-unit-1', sizes: [[728, 90]] + }, bid_with_wrong_zoneId = { + bidder: 'adkernel', + bidId: 'Bid_02', + params: {zoneId: 'wrong id', host: 'rtb.adkernel.com'}, + placementCode: 'ad-unit-2', + sizes: [[728, 90]] }, bid_video = { bidder: 'adkernel', bidId: 'Bid_Video', @@ -57,18 +60,23 @@ describe('Adkernel adapter', () => { bid: [{ id: '1', impid: 'Bid_01', + crid: '100_001', price: 3.01, nurl: 'https://rtb.com/win?i=ZjKoPYSFI3Y_0', adm: '' }] }], - cur: 'USD' + cur: 'USD', + ext: { + adk_usersync: ['http://adk.sync.com/sync'] + } }, bidResponse2 = { id: 'bid2', seatbid: [{ bid: [{ id: '2', impid: 'Bid_02', + crid: '100_002', price: 1.31, adm: '' }] @@ -80,85 +88,58 @@ describe('Adkernel adapter', () => { bid: [{ id: 'sZSYq5zYMxo_0', impid: 'Bid_Video', + crid: '100_003', price: 0.00145, adid: '158801', nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl', - cid: '16855', - crid: '158801', - w: 600, - h: 400 + cid: '16855' }] }], cur: 'USD' + }, usersyncOnlyResponse = { + id: 'nobid1', + ext: { + adk_usersync: ['http://adk.sync.com/sync'] + } }; - let adapter, - sandbox, - ajaxStub; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - adapter = new Adapter(); - ajaxStub = sandbox.stub(ajax, 'ajax'); - }); - - afterEach(() => { - sandbox.restore(); - }); - - function doRequest(bids) { - adapter.callBids({ - bidderCode: 'adkernel', - bids: bids - }); - } - describe('input parameters validation', () => { - let spy; - - beforeEach(() => { - spy = sandbox.spy(); - sandbox.stub(bidmanager, 'addBidResponse'); - }); - it('empty request shouldn\'t generate exception', () => { - expect(adapter.callBids({ + expect(spec.isBidRequestValid({ bidderCode: 'adkernel' - })).to.be.an('undefined'); + })).to.be.equal(false); }); it('request without zone shouldn\'t issue a request', () => { - doRequest([bid_without_zone]); - sinon.assert.notCalled(ajaxStub); - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); - expect(bidmanager.addBidResponse.firstCall.args[1].bidderCode).to.equal('adkernel'); + expect(spec.isBidRequestValid(bid_without_zone)).to.be.equal(false); }); it('request without host shouldn\'t issue a request', () => { - doRequest([bid_without_host]); - sinon.assert.notCalled(ajaxStub); - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); - expect(bidmanager.addBidResponse.firstCall.args[1].bidderCode).to.equal('adkernel'); + expect(spec.isBidRequestValid(bid_without_host)).to.be.equal(false); + }); + + it('empty request shouldn\'t generate exception', () => { + expect(spec.isBidRequestValid(bid_with_wrong_zoneId)).to.be.equal(false); }); }); describe('banner request building', () => { let bidRequest; + let mock; - beforeEach(() => { - sandbox.stub(utils, 'getTopWindowLocation', () => { + before(() => { + mock = sinon.stub(utils, 'getTopWindowLocation', () => { return { protocol: 'https:', hostname: 'example.com', host: 'example.com', pathname: '/index.html', - href: 'http://example.com/index.html' + href: 'https://example.com/index.html' }; }); - - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - doRequest([bid1_zone1]); - bidRequest = JSON.parse(decodeURIComponent(ajaxStub.getCall(0).args[2].r)); + let request = spec.buildRequests([bid1_zone1])[0]; + bidRequest = JSON.parse(request.data.r); + mock.restore(); }); it('should be a first-price auction', () => { @@ -184,7 +165,7 @@ describe('Adkernel adapter', () => { it('should create proper site block', () => { expect(bidRequest.site).to.have.property('domain', 'example.com'); - expect(bidRequest.site).to.have.property('page', 'http://example.com/index.html'); + expect(bidRequest.site).to.have.property('page', 'https://example.com/index.html'); }); it('should fill device with caller macro', () => { @@ -197,19 +178,9 @@ describe('Adkernel adapter', () => { describe('video request building', () => { let bidRequest; - beforeEach(() => { - sandbox.stub(utils, 'getTopWindowLocation', () => { - return { - protocol: 'https:', - hostname: 'example.com', - host: 'example.com', - pathname: '/index.html', - href: 'http://example.com/index.html' - }; - }); - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(videoBidResponse)); - doRequest([bid_video]); - bidRequest = JSON.parse(decodeURIComponent(ajaxStub.getCall(0).args[2].r)); + before(() => { + let request = spec.buildRequests([bid_video])[0]; + bidRequest = JSON.parse(request.data.r); }); it('should have video object', () => { @@ -227,123 +198,68 @@ describe('Adkernel adapter', () => { }); describe('requests routing', () => { - it('should issue a request for each network', () => { - ajaxStub.onFirstCall().callsArgWith(1, '') - .onSecondCall().callsArgWith(1, ''); - doRequest([bid1_zone1, bid3_host2]); - expect(ajaxStub.calledTwice); - expect(ajaxStub.firstCall.args[0]).to.include(bid1_zone1.params.host); - expect(ajaxStub.secondCall.args[0]).to.include(bid3_host2.params.host); + it('should issue a request for each host', () => { + let pbRequests = spec.buildRequests([bid1_zone1, bid3_host2]); + expect(pbRequests).to.have.length(2); + expect(pbRequests[0].url).to.have.string(`//${bid1_zone1.params.host}/`); + expect(pbRequests[1].url).to.have.string(`//${bid3_host2.params.host}/`); }); it('should issue a request for each zone', () => { - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - ajaxStub.onCall(1).callsArgWith(1, JSON.stringify(bidResponse2)); - doRequest([bid1_zone1, bid2_zone2]); - expect(ajaxStub.calledTwice); - }); - - it('should route calls to proper zones', () => { - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - ajaxStub.onCall(1).callsArgWith(1, JSON.stringify(bidResponse2)); - doRequest([bid1_zone1, bid2_zone2]); - expect(ajaxStub.firstCall.args[2].zone).to.equal('1'); - expect(ajaxStub.secondCall.args[2].zone).to.equal('2'); + let pbRequests = spec.buildRequests([bid1_zone1, bid2_zone2]); + expect(pbRequests).to.have.length(2); + expect(pbRequests[0].data.zone).to.be.equal(bid1_zone1.params.zoneId); + expect(pbRequests[1].data.zone).to.be.equal(bid2_zone2.params.zoneId); }); }); describe('responses processing', () => { - beforeEach(() => { - sandbox.stub(bidmanager, 'addBidResponse'); - }); - - it('should return fully-initialized bid-response', () => { - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - doRequest([bid1_zone1]); - let bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('ad-unit-1'); - expect(bidResponse.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidResponse.bidderCode).to.equal('adkernel'); - expect(bidResponse.cpm).to.equal(3.01); - expect(bidResponse.ad).to.include(''); - expect(bidResponse.width).to.equal(300); - expect(bidResponse.height).to.equal(250); + it('should return fully-initialized banner bid-response', () => { + let request = spec.buildRequests([bid1_zone1])[0]; + let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; + expect(resp).to.have.property('requestId', 'Bid_01'); + expect(resp).to.have.property('cpm', 3.01); + expect(resp).to.have.property('width', 300); + expect(resp).to.have.property('height', 250); + expect(resp).to.have.property('creativeId', '100_001'); + expect(resp).to.have.property('currency'); + expect(resp).to.have.property('ttl'); + expect(resp).to.have.property('mediaType', 'banner'); + expect(resp).to.have.property('ad'); + expect(resp.ad).to.have.string(''); }); it('should return fully-initialized video bid-response', () => { - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(videoBidResponse)); - doRequest([bid_video]); - let bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('ad-unit-1'); - expect(bidResponse.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidResponse.mediaType).to.equal('video'); - expect(bidResponse.bidderCode).to.equal('adkernel'); - expect(bidResponse.cpm).to.equal(0.00145); - expect(bidResponse.vastUrl).to.equal('https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl'); - expect(bidResponse.width).to.equal(600); - expect(bidResponse.height).to.equal(400); - }); - - it('should map responses to proper ad units', () => { - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - ajaxStub.onCall(1).callsArgWith(1, JSON.stringify(bidResponse2)); - doRequest([bid1_zone1, bid2_zone2]); - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidmanager.addBidResponse.firstCall.args[1].bidderCode).to.equal('adkernel'); - expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('ad-unit-1'); - expect(bidmanager.addBidResponse.secondCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidmanager.addBidResponse.secondCall.args[1].bidderCode).to.equal('adkernel'); - expect(bidmanager.addBidResponse.secondCall.args[0]).to.equal('ad-unit-2'); - }); - - it('should process empty responses', () => { - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - ajaxStub.onCall(1).callsArgWith(1, ''); - doRequest([bid1_zone1, bid2_zone2]); - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidmanager.addBidResponse.firstCall.args[1].bidderCode).to.equal('adkernel'); - expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('ad-unit-1'); - expect(bidmanager.addBidResponse.secondCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); - expect(bidmanager.addBidResponse.secondCall.args[1].bidderCode).to.equal('adkernel'); - expect(bidmanager.addBidResponse.secondCall.args[0]).to.equal('ad-unit-2'); + let request = spec.buildRequests([bid_video])[0]; + let resp = spec.interpretResponse({body: videoBidResponse}, request)[0]; + expect(resp).to.have.property('requestId', 'Bid_Video'); + expect(resp.mediaType).to.equal('video'); + expect(resp.cpm).to.equal(0.00145); + expect(resp.vastUrl).to.equal('https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl'); + expect(resp.width).to.equal(640); + expect(resp.height).to.equal(480); }); it('should add nurl as pixel for banner response', () => { - sandbox.spy(utils, 'createTrackPixelHtml'); - ajaxStub.onCall(0).callsArgWith(1, JSON.stringify(bidResponse1)); - doRequest([bid1_zone1]); - expect(utils.createTrackPixelHtml.calledOnce); - expect(bidmanager.addBidResponse.firstCall.args[1].getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); + let request = spec.buildRequests([bid1_zone1])[0]; + let resp = spec.interpretResponse({body: bidResponse1}, request)[0]; let expectedNurl = bidResponse1.seatbid[0].bid[0].nurl + '&px=1'; - expect(bidmanager.addBidResponse.firstCall.args[1].ad).to.include(expectedNurl); + expect(resp.ad).to.have.string(expectedNurl); }); - it('should perform usersync for each unique host/zone combination', () => { - ajaxStub.callsArgWith(1, ''); - const expectedSyncUrls = ['//sync.adkernel.com/user-sync?zone=1&r=%2F%2Frtb-private.adkernel.com%2Fuser-synced%3Fuid%3D%7BUID%7D', - '//sync.adkernel.com/user-sync?zone=2&r=%2F%2Frtb.adkernel.com%2Fuser-synced%3Fuid%3D%7BUID%7D', - '//sync.adkernel.com/user-sync?zone=1&r=%2F%2Frtb.adkernel.com%2Fuser-synced%3Fuid%3D%7BUID%7D']; - let userSyncUrls = []; - sandbox.stub(utils, 'createInvisibleIframe', () => { - return {}; - }); - sandbox.stub(utils, 'addEventHandler', (el, ev, cb) => { - userSyncUrls.push(el.src); - cb(); // instant callback - }); - doRequest([bid1_zone1, bid2_zone2, bid2_zone2, bid3_host2]); - expect(utils.createInvisibleIframe.calledThrice); - expect(userSyncUrls).to.be.eql(expectedSyncUrls); + it('should handle bidresponse with user-sync only', () => { + let request = spec.buildRequests([bid1_zone1])[0]; + let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); + expect(resp).to.have.length(0); }); - }); - - describe('adapter aliasing', () => { - const ALIAS_NAME = 'adkernelAlias'; - it('should allow bidder code changing', () => { - expect(adapter.getBidderCode()).to.equal('adkernel'); - adapter.setBidderCode(ALIAS_NAME); - expect(adapter.getBidderCode()).to.equal(ALIAS_NAME); + it('should perform usersync', () => { + let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: bidResponse1}]); + expect(syncs).to.have.length(0); + syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: bidResponse1}]); + expect(syncs).to.have.length(1); + expect(syncs[0]).to.have.property('type', 'iframe'); + expect(syncs[0]).to.have.property('url', 'http://adk.sync.com/sync'); }); }); }); diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js new file mode 100644 index 00000000000..149b9eb4d53 --- /dev/null +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -0,0 +1,158 @@ +import { expect } from 'chai'; +import { spec } from 'modules/adoceanBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('AdoceanAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + const bid = { + 'bidder': 'adocean', + 'params': { + 'masterId': 'tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7', + 'slaveId': 'adoceanmyaozpniqismex', + 'emiter': 'myao.adocean.pl' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + const bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'masterId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + const bidRequests = [ + { + 'bidder': 'adocean', + 'params': { + 'masterId': 'tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7', + 'slaveId': 'adoceanmyaozpniqismex', + 'emiter': 'myao.adocean.pl' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should add bidIdMap with slaveId => bidId mapping', () => { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.bidIdMap).to.exists; + const bidIdMap = request.bidIdMap; + expect(bidIdMap[bidRequests[0].params.slaveId]).to.equal(bidRequests[0].bidId); + }); + + it('sends bid request to url via GET', () => { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.method).to.equal('GET'); + expect(request.url).to.match(new RegExp(`^https://${bidRequests[0].params.emiter}/ad.json`)); + }); + + it('should attach id to url', () => { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.include('id=' + bidRequests[0].params.masterId); + }); + }) + + describe('interpretResponse', () => { + const response = { + 'body': [ + { + 'id': 'adoceanmyaozpniqismex', + 'price': '0.019000', + 'winurl': '', + 'statsUrl': '', + 'code': '%3C!--%20Creative%20--%3E', + 'currency': 'EUR', + 'minFloorPrice': '0.01', + 'width': '300', + 'height': '250', + 'crid': '0af345b42983cc4bc0', + 'ttl': '300' + } + ], + 'headers': { + 'get': function() {} + } + }; + + const bidRequest = { + 'bidder': 'adocean', + 'params': { + 'masterId': 'tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7', + 'slaveId': 'adoceanmyaozpniqismex', + 'emiter': 'myao.adocean.pl' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidIdMap': { + 'adoceanmyaozpniqismex': '30b31c1838de1e' + }, + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should get correct bid response', () => { + const expectedResponse = [ + { + 'requestId': '30b31c1838de1e', + 'cpm': 0.019000, + 'currency': 'EUR', + 'width': 300, + 'height': 250, + 'ad': '', + 'creativeId': '0af345b42983cc4bc0', + 'ttl': 300, + 'netRevenue': false + } + ]; + + const result = spec.interpretResponse(response, bidRequest); + expect(result).to.have.lengthOf(1); + let resultKeys = Object.keys(result[0]); + expect(resultKeys.sort()).to.deep.equal(Object.keys(expectedResponse[0]).sort()); + resultKeys.forEach(function(k) { + if (k === 'ad') { + expect(result[0][k]).to.match(/$/); + } else { + expect(result[0][k]).to.equal(expectedResponse[0][k]); + } + }); + }); + + it('handles nobid responses', () => { + response.body = [ + { + 'id': 'adoceanmyaolafpjwftbz', + 'error': 'true' + } + ]; + + const result = spec.interpretResponse(response, bidRequest); + expect(result).to.have.lengthOf(0); + }); + }); +}); diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index fa55bf92e2e..dbf7359e98d 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,116 +1,62 @@ -import { expect } from 'chai'; -import Adapter from 'modules/adxcgBidAdapter'; -import bidmanager from 'src/bidmanager'; +import {expect} from 'chai'; import * as url from 'src/url'; +import {spec} from 'modules/adxcgBidAdapter'; -const REQUEST = { - 'bidderCode': 'adxcg', - 'bids': [ - { +describe('AdxcgAdapter', () => { + describe('isBidRequestValid', () => { + let bid = { 'bidder': 'adxcg', 'params': { - 'adzoneid': '1', + 'adzoneid': '1' }, - 'sizes': [ - [300, 250], - [640, 360], - [1, 1] - ], + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], 'bidId': '84ab500420319d', - 'bidderRequestId': '7101db09af0db2' - } - ] -}; - -const RESPONSE = [{ - 'bidId': '84ab500420319d', - 'width': 300, - 'height': 250, - 'creativeId': '42', - 'cpm': 0.45, - 'ad': '' -}] - -const VIDEO_RESPONSE = [{ - 'bidId': '84ab500420319d', - 'width': 640, - 'height': 360, - 'creativeId': '42', - 'cpm': 0.45, - 'vastUrl': 'vastContentUrl' -}] - -const NATIVE_RESPONSE = [{ - 'bidId': '84ab500420319d', - 'width': 0, - 'height': 0, - 'creativeId': '42', - 'cpm': 0.45, - 'nativeResponse': { - 'assets': [{ - 'id': 1, - 'required': 0, - 'title': { - 'text': 'titleContent' - } - }, { - 'id': 2, - 'required': 0, - 'img': { - 'url': 'imageContent', - 'w': 600, - 'h': 600 - } - }, { - 'id': 3, - 'required': 0, - 'data': { - 'label': 'DESC', - 'value': 'descriptionContent' - } - }, { - 'id': 0, - 'required': 0, - 'data': { - 'label': 'SPONSORED', - 'value': 'sponsoredByContent' - } - }], - 'link': { - 'url': 'linkContent' - }, - 'imptrackers': ['impressionTracker1', 'impressionTracker2'] - } -}] + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }; -describe('AdxcgAdapter', () => { - let adapter; - - beforeEach(() => adapter = new Adapter()); - - describe('request function', () => { - let xhr; - let requests; + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); + }); - afterEach(() => xhr.restore()); + describe('request function http', () => { + let bid = { + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }; it('creates a valid adxcg request url', () => { - adapter.callBids(REQUEST); + let request = spec.buildRequests([bid]); + expect(request).to.exist; + // console.log('IS:' + JSON.stringify(request)); - let parsedRequestUrl = url.parse(requests[0].url); + expect(request.method).to.equal('GET'); + let parsedRequestUrl = url.parse(request.url); - expect(parsedRequestUrl.hostname).to.equal('ad-emea.adxcg.net'); + expect(parsedRequestUrl.hostname).to.equal('hbp.adxcg.net'); expect(parsedRequestUrl.pathname).to.equal('/get/adi'); let query = parsedRequestUrl.search; expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20141124'); + expect(query.ver).to.equal('r20171019PB10'); + expect(query.source).to.equal('pbjs10'); + expect(query.pbjs).to.equal('$prebid.version$'); expect(query.adzoneid).to.equal('1'); expect(query.format).to.equal('300x250|640x360|1x1'); expect(query.jsonp).to.be.empty; @@ -119,94 +65,202 @@ describe('AdxcgAdapter', () => { }); describe('response handler', () => { - let server; + let BIDDER_REQUEST = { + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }; - beforeEach(() => { - server = sinon.fakeServer.create(); - sinon.stub(bidmanager, 'addBidResponse'); - }); + let BANNER_RESPONSE = + { + body: [{ + 'bidId': '84ab500420319d', + 'bidderCode': 'adxcg', + 'width': 300, + 'height': 250, + 'creativeId': '42', + 'cpm': 0.45, + 'currency': 'USD', + 'netRevenue': true, + 'ad': '' + }], + header: {'someheader': 'fakedata'} + } - afterEach(() => { - server.restore() - bidmanager.addBidResponse.restore(); - }); + let BANNER_RESPONSE_WITHDEALID = + { + body: [{ + 'bidId': '84ab500420319d', + 'bidderCode': 'adxcg', + 'width': 300, + 'height': 250, + 'deal_id': '7722', + 'creativeId': '42', + 'cpm': 0.45, + 'currency': 'USD', + 'netRevenue': true, + 'ad': '' + }], + header: {'someheader': 'fakedata'} + } + + let VIDEO_RESPONSE = + { + body: [{ + 'bidId': '84ab500420319d', + 'bidderCode': 'adxcg', + 'width': 640, + 'height': 360, + 'creativeId': '42', + 'cpm': 0.45, + 'currency': 'USD', + 'netRevenue': true, + 'vastUrl': 'vastContentUrl' + }], + header: {'someheader': 'fakedata'} + } + + let NATIVE_RESPONSE = + { + body: [{ + 'bidId': '84ab500420319d', + 'bidderCode': 'adxcg', + 'width': 0, + 'height': 0, + 'creativeId': '42', + 'cpm': 0.45, + 'currency': 'USD', + 'netRevenue': true, + 'nativeResponse': { + 'assets': [{ + 'id': 1, + 'required': 0, + 'title': { + 'text': 'titleContent' + } + }, { + 'id': 2, + 'required': 0, + 'img': { + 'url': 'imageContent', + 'w': 600, + 'h': 600 + } + }, { + 'id': 3, + 'required': 0, + 'data': { + 'label': 'DESC', + 'value': 'descriptionContent' + } + }, { + 'id': 0, + 'required': 0, + 'data': { + 'label': 'SPONSORED', + 'value': 'sponsoredByContent' + } + }], + 'link': { + 'url': 'linkContent' + }, + 'imptrackers': ['impressionTracker1', 'impressionTracker2'] + } + }], + header: {'someheader': 'fakedata'} + } it('handles regular responses', () => { - server.respondWith(JSON.stringify(RESPONSE)); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); - - const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.bidderCode).to.equal('adxcg'); - expect(bidResponse.width).to.equal(300); - expect(bidResponse.height).to.equal(250); - expect(bidResponse.statusMessage).to.equal('Bid available'); - expect(bidResponse.adId).to.equal('84ab500420319d'); - expect(bidResponse.mediaType).to.equal('banner'); - expect(bidResponse.creative_id).to.equal('42'); - expect(bidResponse.code).to.equal('adxcg'); - expect(bidResponse.cpm).to.equal(0.45); - expect(bidResponse.ad).to.equal(''); + let result = spec.interpretResponse(BANNER_RESPONSE, BIDDER_REQUEST); + + expect(result).to.have.lengthOf(1); + + expect(result[0]).to.exist; + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal(42); + expect(result[0].cpm).to.equal(0.45); + expect(result[0].ad).to.equal(''); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); + expect(result[0].dealId).to.not.exist; + }); + + it('handles regular responses with dealid', () => { + let result = spec.interpretResponse(BANNER_RESPONSE_WITHDEALID, BIDDER_REQUEST); + + expect(result).to.have.lengthOf(1); + + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal(42); + expect(result[0].cpm).to.equal(0.45); + expect(result[0].ad).to.equal(''); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); }); it('handles video responses', () => { - server.respondWith(JSON.stringify(VIDEO_RESPONSE)); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); - - const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.bidderCode).to.equal('adxcg'); - expect(bidResponse.width).to.equal(640); - expect(bidResponse.height).to.equal(360); - expect(bidResponse.statusMessage).to.equal('Bid available'); - expect(bidResponse.adId).to.equal('84ab500420319d'); - expect(bidResponse.mediaType).to.equal('video'); - expect(bidResponse.creative_id).to.equal('42'); - expect(bidResponse.code).to.equal('adxcg'); - expect(bidResponse.cpm).to.equal(0.45); - expect(bidResponse.vastUrl).to.equal('vastContentUrl'); - expect(bidResponse.descriptionUrl).to.equal('vastContentUrl'); + let result = spec.interpretResponse(VIDEO_RESPONSE, BIDDER_REQUEST); + expect(result).to.have.lengthOf(1); + + expect(result[0].width).to.equal(640); + expect(result[0].height).to.equal(360); + expect(result[0].mediaType).to.equal('video'); + expect(result[0].creativeId).to.equal(42); + expect(result[0].cpm).to.equal(0.45); + expect(result[0].vastUrl).to.equal('vastContentUrl'); + expect(result[0].descriptionUrl).to.equal('vastContentUrl'); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); }); it('handles native responses', () => { - server.respondWith(JSON.stringify(NATIVE_RESPONSE)); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); - - const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.bidderCode).to.equal('adxcg'); - expect(bidResponse.width).to.equal(0); - expect(bidResponse.height).to.equal(0); - expect(bidResponse.statusMessage).to.equal('Bid available'); - expect(bidResponse.adId).to.equal('84ab500420319d'); - expect(bidResponse.mediaType).to.equal('native'); - expect(bidResponse.creative_id).to.equal('42'); - expect(bidResponse.code).to.equal('adxcg'); - expect(bidResponse.cpm).to.equal(0.45); - - expect(bidResponse.native.clickUrl).to.equal('linkContent'); - expect(bidResponse.native.impressionTrackers).to.deep.equal(['impressionTracker1', 'impressionTracker2']); - expect(bidResponse.native.title).to.equal('titleContent'); - expect(bidResponse.native.image).to.equal('imageContent'); - expect(bidResponse.native.body).to.equal('descriptionContent'); - expect(bidResponse.native.sponsoredBy).to.equal('sponsoredByContent'); + let result = spec.interpretResponse(NATIVE_RESPONSE, BIDDER_REQUEST); + + expect(result[0].width).to.equal(0); + expect(result[0].height).to.equal(0); + expect(result[0].mediaType).to.equal('native'); + expect(result[0].creativeId).to.equal(42); + expect(result[0].cpm).to.equal(0.45); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].ttl).to.equal(300); + + expect(result[0].native.clickUrl).to.equal('linkContent'); + expect(result[0].native.impressionTrackers).to.deep.equal(['impressionTracker1', 'impressionTracker2']); + expect(result[0].native.title).to.equal('titleContent'); + expect(result[0].native.image).to.equal('imageContent'); + expect(result[0].native.body).to.equal('descriptionContent'); + expect(result[0].native.sponsoredBy).to.equal('sponsoredByContent'); }); it('handles nobid responses', () => { - server.respondWith('[]'); + let response = []; + let bidderRequest = BIDDER_REQUEST; + + let result = spec.interpretResponse(response, bidderRequest); + expect(result.length).to.equal(0); + }); + }); - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + describe('getUserSyncs', () => { + let syncoptionsIframe = { + 'iframeEnabled': 'true' + }; - const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; - expect(bidResponse.statusMessage).to.equal('Bid returned empty or error response'); + it('should return iframe sync option', () => { + expect(spec.getUserSyncs(syncoptionsIframe)[0].type).to.equal('iframe'); + expect(spec.getUserSyncs(syncoptionsIframe)[0].url).to.equal('//cdn.adxcg.net/pb-sync.html'); }); }); }); diff --git a/test/spec/modules/aolBidAdapter_spec.js b/test/spec/modules/aolBidAdapter_spec.js index 5dd38a23ef6..977b1bbecfc 100644 --- a/test/spec/modules/aolBidAdapter_spec.js +++ b/test/spec/modules/aolBidAdapter_spec.js @@ -1,9 +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 {newBidder} from 'src/adapters/bidderFactory'; +import {config} from 'src/config'; let getDefaultBidResponse = () => { return { @@ -15,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} }] }] @@ -55,14 +54,14 @@ let getNexagePostBidParams = () => { let getDefaultBidRequest = () => { return { bidderCode: 'aol', - requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', bidderRequestId: '7101db09af0db2', start: new Date().getTime(), bids: [{ bidder: 'aol', bidId: '84ab500420319d', bidderRequestId: '7101db09af0db2', - requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', placementCode: 'foo', params: getMarketplaceBidParams() }] @@ -78,11 +77,7 @@ describe('AolAdapter', () => { const MARKETPLACE_URL = 'adserver-us.adtech.advertising.com/pubapi/3.0/'; const NEXAGE_URL = 'hb.nexage.com/bidRequest?'; - let adapter; - - beforeEach(() => adapter = newBidder(spec)); - - function createBidderRequest({bids, params} = {}) { + function createCustomBidRequest({bids, params} = {}) { var bidderRequest = getDefaultBidRequest(); if (bids && Array.isArray(bids)) { bidderRequest.bids = bids; @@ -93,573 +88,409 @@ describe('AolAdapter', () => { return bidderRequest; } - describe('callBids()', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - - 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='); - }); + describe('interpretResponse()', () => { + let bidderSettingsBackup; + let bidResponse; + let bidRequest; + let logWarnSpy; - 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'); - }); + beforeEach(() => { + bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; + bidRequest = { + bidderCode: 'test-bidder-code', + bidId: 'bid-id' + }; + bidResponse = { + body: getDefaultBidResponse() + }; + logWarnSpy = sinon.spy(utils, 'logWarn'); + }); - it('should contain key values if keyValues param is present', () => { - adapter.callBids(createBidderRequest({ - params: { - placement: 1234567, - network: '9599.1', - keyValues: { - age: 25, - height: 3.42, - test: 'key' - } - } - })); + afterEach(() => { + $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; + logWarnSpy.restore(); + }); - expect(requests[0].url).to.contain('kvage=25;kvheight=3.42;kvtest=key'); - }); + 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; - - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); - }); + it('should return formatted bid response including pixels', () => { + bidResponse.body.ext = { + pixels: '' + }; - afterEach(() => xhr.restore()); + let formattedBidResponse = spec.interpretResponse(bidResponse, bidRequest); - it('requires parameters to be made', () => { - adapter.callBids({}); - expect(requests).to.be.empty; - }); - - it('should hit the nexage api endpoint with the nexage config', () => { - adapter.callBids(createBidderRequest({ - params: getNexageGetBidParams() - })); + expect(formattedBidResponse.ad).to.equal( + '' + + '' + ); + }); - expect(requests[0].url).to.contain(NEXAGE_URL); - }); + 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 custom endpoint if specified in the nexage config', () => { - let bidParams = Object.assign({ - host: 'qa-hb.nexage.com' - }, getNexageGetBidParams()); + describe('buildRequests()', () => { + it('method exists and is a function', () => { + expect(spec.buildRequests).to.exist.and.to.be.a('function'); + }); - adapter.callBids(createBidderRequest({ - params: bidParams - })); - expect(requests[0].url).to.contain('qa-hb.nexage.com/bidRequest?'); - }); + describe('Marketplace', () => { + it('should not return request when no bids are present', () => { + let [request] = spec.buildRequests([]); + expect(request).to.be.empty; + }); - it('should hit nexage api when nexage and marketplace params are present', () => { - let bidParams = Object.assign(getNexageGetBidParams(), getMarketplaceBidParams()); + 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(NEXAGE_URL); + it('should return request for Marketplace via onedisplay bidder code', () => { + let bidRequest = createCustomBidRequest({ + bids: [{ + bidder: 'onedisplay' + }], + params: getMarketplaceBidParams() }); - 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 params are present', () => { + let bidParams = Object.assign(getMarketplaceBidParams(), getNexageGetBidParams()); + 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 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 contain required params - dcn & pos', () => { - adapter.callBids(createBidderRequest({ - params: getNexageGetBidParams() - })); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request.url).to.contain(MARKETPLACE_URL); + }); - expect(requests[0].url).to.contain(NEXAGE_URL + 'dcn=2c9d2b50015c5ce9db6aeeed8b9500d6&pos=header'); + 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 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'); - }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request).to.be.empty; + }); - 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', () => { + 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 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(JSON.stringify(bidConfig)); - expect(requests[0].requestHeaders).to.have.property('x-openrtb-version'); + 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 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; + it('should return default Marketplace URL in case of unknown region config option', () => { + let bidRequest = createCustomBidRequest({ + params: { + placement: 1234567, + network: '9599.1', + region: 'an' + } }); + 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 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); + 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 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 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 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 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 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 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 encrypted price from pubapi response', () => { - let bidResponse = getDefaultBidResponse(); - bidResponse.seatbid[0].bid[0].ext.encp = 'a9334987'; - server.respondWith(JSON.stringify(bidResponse)); + 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; + }); - 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'); }); - }); - describe('when bidCpmAdjustment is set', () => { - let bidderSettingsBackup; - let server; + 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'); + }); - beforeEach(() => { - bidderSettingsBackup = $$PREBID_GLOBAL$$.bidderSettings; - server = sinon.fakeServer.create(); + 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'); }); - afterEach(() => { - $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsBackup; - server.restore(); - if (utils.logWarn.restore) { - utils.logWarn.restore(); - } + it('should return request object for One Mobile POST endpoint when POST configuration is present', () => { + let bidConfig = getNexagePostBidParams(); + let bidRequest = createCustomBidRequest({ + params: bidConfig + }); + + 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.contentType).to.equal('application/json'); + expect(request.options).to.deep.equal({ + customHeaders: { + 'x-openrtb-version': '2.2' + } + }); }); - it('should show warning in the console', function() { - sinon.spy(utils, 'logWarn'); - server.respondWith(JSON.stringify(getDefaultBidResponse())); - $$PREBID_GLOBAL$$.bidderSettings = { - aol: { - bidCpmAdjustment: function() {} + it('should not return request object for One Mobile POST endpoint' + + 'if required parameterers are missed', () => { + let bidRequest = createCustomBidRequest({ + params: { + imp: [] } - }; - adapter.callBids(getDefaultBidRequest()); - server.respond(); - expect(utils.logWarn.calledOnce).to.be.true; + }); + let [request] = spec.buildRequests(bidRequest.bids); + expect(request).to.be.empty; }); }); }); - describe('getUserSyncs', () => { + describe('getUserSyncs()', () => { let bidResponse; let bidRequest; beforeEach(() => { + $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = false; + config.setConfig({ + aol: { + userSyncOn: 'bidResponse' + }, + }); 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"', () => { diff --git a/test/spec/modules/appnexusAstBidAdapter_spec.js b/test/spec/modules/appnexusAstBidAdapter_spec.js index d07ee6df543..3884b1c5863 100644 --- a/test/spec/modules/appnexusAstBidAdapter_spec.js +++ b/test/spec/modules/appnexusAstBidAdapter_spec.js @@ -244,6 +244,23 @@ describe('AppNexusAdapter', () => { 'value': ['123'] }]); }); + + it('should should add payment rules to the request', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + placementId: '10433394', + usePaymentRule: true + } + } + ); + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].use_pmt_rule).to.equal(true); + }); }) describe('interpretResponse', () => { @@ -296,18 +313,20 @@ describe('AppNexusAdapter', () => { { 'requestId': '3db3773286ee59', 'cpm': 0.5, - 'creative_id': 29681110, + 'creativeId': 29681110, 'dealId': undefined, 'width': 300, 'height': 250, 'ad': '', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true } ]; let bidderRequest; - - let result = spec.interpretResponse(response, {bidderRequest}); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('handles nobid responses', () => { @@ -322,7 +341,7 @@ describe('AppNexusAdapter', () => { }; let bidderRequest; - let result = spec.interpretResponse(response, {bidderRequest}); + let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(result.length).to.equal(0); }); @@ -343,7 +362,7 @@ describe('AppNexusAdapter', () => { }; let bidderRequest; - let result = spec.interpretResponse(response, {bidderRequest}); + let result = spec.interpretResponse({ body: response }, {bidderRequest}); expect(result[0]).to.have.property('vastUrl'); expect(result[0]).to.have.property('descriptionUrl'); expect(result[0]).to.have.property('mediaType', 'video'); @@ -376,7 +395,7 @@ describe('AppNexusAdapter', () => { }; let bidderRequest; - let result = spec.interpretResponse(response1, {bidderRequest}); + let result = spec.interpretResponse({ body: response1 }, {bidderRequest}); expect(result[0].native.title).to.equal('Native Creative'); expect(result[0].native.body).to.equal('Cool description great stuff'); expect(result[0].native.cta).to.equal('Do it'); diff --git a/test/spec/modules/audienceNetworkBidAdapter_spec.js b/test/spec/modules/audienceNetworkBidAdapter_spec.js index 3dcd4833871..604c3bf0e06 100644 --- a/test/spec/modules/audienceNetworkBidAdapter_spec.js +++ b/test/spec/modules/audienceNetworkBidAdapter_spec.js @@ -3,542 +3,346 @@ */ import { expect } from 'chai'; -import bidmanager from 'src/bidmanager'; -import { STATUS } from 'src/constants.json'; -import * as utils from 'src/utils'; +import { spec } from 'modules/audienceNetworkBidAdapter'; -import AudienceNetwork from 'modules/audienceNetworkBidAdapter'; +const { + code, + supportedMediaTypes, + isBidRequestValid, + buildRequests, + interpretResponse +} = spec; -const bidderCode = 'audienceNetwork'; +const bidder = 'audienceNetwork'; const placementId = 'test-placement-id'; -const placementCode = '/test/placement/code'; const playerwidth = 320; const playerheight = 180; - -/** - * Expect haystack string to contain needle n times. - * @param {String} haystack - * @param {String} needle - * @param {String} [n=1] - * @throws {Error} - */ -const expectToContain = (haystack, needle, n = 1) => - expect(haystack.split(needle)).to.have.lengthOf(n + 1, - `expected ${n} occurrence(s) of '${needle}' in '${haystack}'`); +const requestId = 'test-request-id'; describe('AudienceNetwork adapter', () => { describe('Public API', () => { - const adapter = new AudienceNetwork(); - it('getBidderCode', () => { - expect(adapter.getBidderCode).to.be.a('function'); - expect(adapter.getBidderCode()).to.equal(bidderCode); + it('code', () => { + expect(code).to.equal(bidder); }); - it('setBidderCode', () => { - expect(adapter.setBidderCode).to.be.a('function'); + it('supportedMediaTypes', () => { + expect(supportedMediaTypes).to.deep.equal(['video']); }); - it('callBids', () => { - expect(adapter.setBidderCode).to.be.a('function'); + it('isBidRequestValid', () => { + expect(isBidRequestValid).to.be.a('function'); }); - }); - - describe('callBids parameter parsing', () => { - let xhr; - let requests; - let addBidResponse; - let logError; - - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - xhr.onCreate = request => requests.push(request); - requests = []; - addBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - logError = sinon.stub(utils, 'logError'); + it('buildRequests', () => { + expect(buildRequests).to.be.a('function'); }); - - afterEach(() => { - xhr.restore(); - bidmanager.addBidResponse.restore(); - utils.logError.restore(); + it('interpretResponse', () => { + expect(interpretResponse).to.be.a('function'); }); + }); + describe('isBidRequestValid', () => { it('missing placementId parameter', () => { - // Invalid parameters - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - sizes: ['native'] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify no attempt to fetch response - expect(requests).to.have.lengthOf(0); - // Verify no attempt to add a response as no placement was provided - expect(addBidResponse.calledOnce).to.equal(false); - // Verify attempt to log error - expect(logError.calledOnce).to.equal(true); + expect(isBidRequestValid({ + bidder, + sizes: [[300, 250]] + })).to.equal(false); }); it('invalid sizes parameter', () => { - // Invalid parameters - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - params: { placementId }, - sizes: ['', undefined, null, '300x100', [300, 100], [300], {}] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify no attempt to fetch response - expect(requests).to.have.lengthOf(0); - // Verify attempt to log error - expect(logError.calledOnce).to.equal(true); + expect(isBidRequestValid({ + bidder, + sizes: ['', undefined, null, '300x100', [300, 100], [300], {}], + params: { placementId } + })).to.equal(false); }); - it('filter valid sizes', () => { - // Valid parameters - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - params: { placementId }, - sizes: [[1, 1], [300, 250]] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify attempt to fetch response - expect(requests).to.have.lengthOf(1); - expect(requests[0].method).to.equal('GET'); - expect(requests[0].url) - .to.contain('https://an.facebook.com/v2/placementbid.json?') - .and.to.contain('placementids[]=test-placement-id') - .and.to.contain('adformats[]=300x250') - .and.to.contain('pageurl=http%3A%2F%2F'); - // Verify no attempt to log error - expect(logError.called).to.equal(false); + it('valid when at least one valid size', () => { + expect(isBidRequestValid({ + bidder, + sizes: [[1, 1], [300, 250]], + params: { placementId } + })).to.equal(true); }); it('valid parameters', () => { - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - params: { placementId }, - sizes: [[300, 250], [320, 50]] - }, - { - bidder: bidderCode, - params: { placementId }, - sizes: [[320, 50], [300, 250]] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify attempt to fetch response - expect(requests).to.have.lengthOf(1); - expect(requests[0].method).to.equal('GET'); - expect(requests[0].url) - .to.contain('https://an.facebook.com/v2/placementbid.json?') - .and.to.contain('placementids[]=test-placement-id&placementids[]=test-placement-id') - .and.to.contain('adformats[]=320x50') - .and.to.contain('adformats[]=300x250') - .and.to.contain('pageurl=http%3A%2F%2F'); - // Verify no attempt to log error - expect(logError.called).to.equal(false); + expect(isBidRequestValid({ + bidder, + sizes: [[300, 250], [320, 50]], + params: { placementId } + })).to.equal(true); }); it('fullwidth', () => { - // Valid parameters - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - params: { - placementId, - format: 'fullwidth' - }, - sizes: [[300, 250]] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify attempt to fetch response - expect(requests).to.have.lengthOf(1); - expect(requests[0].method).to.equal('GET'); - expect(requests[0].url) - .to.contain('https://an.facebook.com/v2/placementbid.json?') - .and.to.contain('placementids[]=test-placement-id') - .and.to.contain('adformats[]=fullwidth') - .and.to.contain('pageurl=http%3A%2F%2F'); - // Verify no attempt to log error - expect(logError.called).to.equal(false); + expect(isBidRequestValid({ + bidder, + sizes: [[300, 250]], + params: { + placementId, + format: 'fullwidth' + } + })).to.equal(true); }); it('native', () => { - // Valid parameters - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - params: { - placementId, - format: 'native' - }, - sizes: [[300, 250]] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify attempt to fetch response - expect(requests).to.have.lengthOf(1); - expect(requests[0].method).to.equal('GET'); - expect(requests[0].url) - .to.contain('https://an.facebook.com/v2/placementbid.json?') - .and.to.contain('placementids[]=test-placement-id') - .and.to.contain('adformats[]=native') - .and.to.contain('pageurl=http%3A%2F%2F'); - // Verify no attempt to log error - expect(logError.called).to.equal(false); + expect(isBidRequestValid({ + bidder, + sizes: [[300, 250]], + params: { + placementId, + format: 'native' + } + })).to.equal(true); }); it('video', () => { - // Valid parameters - const params = { - bidderCode, - bids: [{ - bidder: bidderCode, - params: { - placementId, - format: 'video' - }, - sizes: [[playerwidth, playerheight]] - }] - }; - // Request bids - new AudienceNetwork().callBids(params); - // Verify attempt to fetch response - expect(requests).to.have.lengthOf(1); - expect(requests[0].method).to.equal('GET'); - expect(requests[0].url) - .to.contain('https://an.facebook.com/v2/placementbid.json?') - .and.to.contain('placementids[]=test-placement-id') - .and.to.contain('adformats[]=video') - .and.to.contain('sdk[]=') - .and.to.contain('pageurl=http%3A%2F%2F'); - // Verify no attempt to log error - expect(logError.called).to.equal(false); + expect(isBidRequestValid({ + bidder, + sizes: [[playerwidth, playerheight]], + params: { + placementId, + format: 'video' + } + })).to.equal(true); }); }); - describe('callBids response handling', () => { - let server; - let addBidResponse; - let logError; - - beforeEach(() => { - server = sinon.fakeServer.create(); - addBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - logError = sinon.stub(utils, 'logError'); + describe('buildRequests', () => { + it('can build URL for IAB unit', () => { + expect(buildRequests([{ + bidder, + bidId: requestId, + sizes: [[300, 250], [320, 50]], + params: { placementId } + }])).to.deep.equal([{ + adformats: ['300x250'], + method: 'GET', + requestIds: [requestId], + sizes: ['300x250'], + url: 'https://an.facebook.com/v2/placementbid.json', + data: 'placementids[]=test-placement-id&adformats[]=300x250&testmode=false&pageurl=&sdk[]=5.5.web' + }]); }); - afterEach(() => { - server.restore(); - bidmanager.addBidResponse.restore(); - utils.logError.restore(); + it('can build URL for video unit', () => { + expect(buildRequests([{ + bidder, + bidId: requestId, + sizes: [[640, 480]], + params: { + placementId, + format: 'video' + } + }])).to.deep.equal([{ + adformats: ['video'], + method: 'GET', + requestIds: [requestId], + sizes: ['640x480'], + url: 'https://an.facebook.com/v2/placementbid.json', + data: 'placementids[]=test-placement-id&adformats[]=video&testmode=false&pageurl=&sdk[]=&playerwidth=640&playerheight=480' + }]); }); + }); + describe('interpretResponse', () => { it('error in response', () => { - // Error response - const error = 'test-error-message'; - server.respondWith(JSON.stringify({ - errors: [error] - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - params: { placementId }, - sizes: [[300, 250]] - }] - }); - server.respond(); - // Verify attempt to call addBidResponse - expect(addBidResponse.calledOnce).to.equal(true); - expect(addBidResponse.args[0]).to.have.lengthOf(2); - expect(addBidResponse.args[0][1].getStatusCode()).to.equal(STATUS.NO_BID); - expect(addBidResponse.args[0][1].bidderCode).to.equal(bidderCode); - // Verify attempt to log error - expect(logError.calledOnce).to.equal(true); - expect(logError.calledWith(error)).to.equal(true); + expect(interpretResponse({ + body: { + errors: ['test-error-message'] + } + }, {})).to.deep.equal([]); }); it('valid native bid in response', () => { - // Valid response - server.respondWith(JSON.stringify({ - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] + const [bidResponse] = interpretResponse({ + body: { + errors: [], + bids: { + [placementId]: [{ + placement_id: placementId, + bid_id: 'test-bid-id', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } } - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - placementCode, - params: { - placementId, - format: 'native' - }, - sizes: [[300, 250]] - }] + }, { + adformats: ['native'], + requestIds: [requestId], + sizes: [[300, 250]] }); - server.respond(); - // Verify attempt to call addBidResponse - expect(addBidResponse.calledOnce).to.equal(true); - expect(addBidResponse.args[0]).to.have.lengthOf(2); - expect(addBidResponse.args[0][0]).to.equal(placementCode); - // Verify Prebid attributes in bid response - const bidResponse = addBidResponse.args[0][1]; - expect(bidResponse.getStatusCode()).to.equal(STATUS.GOOD); + expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.bidderCode).to.equal(bidderCode); + expect(bidResponse.requestId).to.equal(requestId); expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); expect(bidResponse.ad) .to.contain(`placementid:'${placementId}',format:'native',bidid:'test-bid-id'`, 'ad missing parameters') .and.to.contain('getElementsByTagName("style")', 'ad missing native styles') .and.to.contain('
', 'ad missing native container'); - // Verify Audience Network attributes in bid response + expect(bidResponse.creativeId).to.equal(placementId); + expect(bidResponse.netRevenue).to.equal(true); + expect(bidResponse.currency).to.equal('USD'); + expect(bidResponse.hb_bidder).to.equal('fan'); expect(bidResponse.fb_bidid).to.equal('test-bid-id'); expect(bidResponse.fb_format).to.equal('native'); expect(bidResponse.fb_placementid).to.equal(placementId); - // Verify no attempt to log error - expect(logError.called).to.equal(false, 'logError called'); }); it('valid IAB bid in response', () => { - // Valid response - server.respondWith(JSON.stringify({ - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] + const [bidResponse] = interpretResponse({ + body: { + errors: [], + bids: { + [placementId]: [{ + placement_id: placementId, + bid_id: 'test-bid-id', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } } - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - placementCode, - params: { placementId }, - sizes: [[300, 250]] - }] + }, { + adformats: ['300x250'], + requestIds: [requestId], + sizes: [[300, 250]] }); - server.respond(); - // Verify attempt to call addBidResponse - expect(addBidResponse.calledOnce).to.equal(true); - expect(addBidResponse.args[0]).to.have.lengthOf(2); - expect(addBidResponse.args[0][0]).to.equal(placementCode); - // Verify bidResponse Object - const bidResponse = addBidResponse.args[0][1]; - expect(bidResponse.getStatusCode()).to.equal(STATUS.GOOD); + expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.bidderCode).to.equal(bidderCode); + expect(bidResponse.requestId).to.equal(requestId); expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); expect(bidResponse.ad) .to.contain(`placementid:'${placementId}',format:'300x250',bidid:'test-bid-id'`, 'ad missing parameters') .and.not.to.contain('getElementsByTagName("style")', 'ad should not contain native styles') .and.not.to.contain('
', 'ad should not contain native container'); - // Verify no attempt to log error - expect(logError.called).to.equal(false, 'logError called'); + expect(bidResponse.creativeId).to.equal(placementId); + expect(bidResponse.netRevenue).to.equal(true); + expect(bidResponse.currency).to.equal('USD'); + expect(bidResponse.hb_bidder).to.equal('fan'); + expect(bidResponse.fb_bidid).to.equal('test-bid-id'); + expect(bidResponse.fb_format).to.equal('300x250'); + expect(bidResponse.fb_placementid).to.equal(placementId); }); it('filters invalid slot sizes', () => { - // Valid response - server.respondWith(JSON.stringify({ - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: 'test-bid-id', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] + const [bidResponse] = interpretResponse({ + body: { + errors: [], + bids: { + [placementId]: [{ + placement_id: placementId, + bid_id: 'test-bid-id', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } } - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - placementCode, - params: { placementId }, - sizes: ['350x200'] - }, { - bidder: bidderCode, - placementCode, - params: { placementId }, - sizes: [[300, 250]] - }] + }, { + adformats: ['300x250'], + requestIds: [requestId], + sizes: [[300, 250]] }); - server.respond(); - // Verify attempt to call addBidResponse - expect(addBidResponse.calledOnce).to.equal(true); - expect(addBidResponse.args[0]).to.have.lengthOf(2); - expect(addBidResponse.args[0][0]).to.equal(placementCode); - // Verify bidResponse Object - const bidResponse = addBidResponse.args[0][1]; - expect(bidResponse.getStatusCode()).to.equal(STATUS.GOOD); + expect(bidResponse.cpm).to.equal(1.23); - expect(bidResponse.bidderCode).to.equal(bidderCode); + expect(bidResponse.requestId).to.equal(requestId); expect(bidResponse.width).to.equal(300); expect(bidResponse.height).to.equal(250); - // Verify no attempt to log error - expect(logError.called).to.equal(false, 'logError called'); + expect(bidResponse.creativeId).to.equal(placementId); + expect(bidResponse.netRevenue).to.equal(true); + expect(bidResponse.currency).to.equal('USD'); + expect(bidResponse.hb_bidder).to.equal('fan'); + expect(bidResponse.fb_bidid).to.equal('test-bid-id'); + expect(bidResponse.fb_format).to.equal('300x250'); + expect(bidResponse.fb_placementid).to.equal(placementId); }); it('valid multiple bids in response', () => { const placementIdNative = 'test-placement-id-native'; const placementIdIab = 'test-placement-id-iab'; - const placementCodeNative = 'test-placement-code-native'; - const placementCodeIab = 'test-placement-code-iab'; - // Valid response - server.respondWith(JSON.stringify({ - errors: [], - bids: { - [placementIdNative]: [{ - placement_id: placementIdNative, - bid_id: 'test-bid-id-native', - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }], - [placementIdIab]: [{ - placement_id: placementIdIab, - bid_id: 'test-bid-id-iab', - bid_price_cents: 456, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] + + const [bidResponseNative, bidResponseIab] = interpretResponse({ + body: { + errors: [], + bids: { + [placementIdNative]: [{ + placement_id: placementIdNative, + bid_id: 'test-bid-id-native', + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }], + [placementIdIab]: [{ + placement_id: placementIdIab, + bid_id: 'test-bid-id-iab', + bid_price_cents: 456, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } } - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - placementCode: placementCodeNative, - params: { - placementId: placementIdNative, - format: 'native' - }, - sizes: [[300, 250]] - }, { - bidder: bidderCode, - placementCode: placementCodeIab, - params: { placementId: placementIdIab }, - sizes: [[300, 250]] - }] + }, { + adformats: ['native', '300x250'], + requestIds: [requestId, requestId], + sizes: [[300, 250], [300, 250]] }); - server.respond(); - // Verify multiple attempts to call addBidResponse - expect(addBidResponse.calledTwice).to.equal(true); - // Verify native - const addBidResponseNativeCall = addBidResponse.args[0]; - expect(addBidResponseNativeCall).to.have.lengthOf(2); - expect(addBidResponseNativeCall[0]).to.equal(placementCodeNative); - expect(addBidResponseNativeCall[1].getStatusCode()).to.equal(STATUS.GOOD); - expect(addBidResponseNativeCall[1].cpm).to.equal(1.23); - expect(addBidResponseNativeCall[1].bidderCode).to.equal(bidderCode); - expect(addBidResponseNativeCall[1].width).to.equal(300); - expect(addBidResponseNativeCall[1].height).to.equal(250); - expect(addBidResponseNativeCall[1].ad).to.contain(`placementid:'${placementIdNative}',format:'native',bidid:'test-bid-id-native'`, 'ad missing parameters'); - // Verify IAB - const addBidResponseIabCall = addBidResponse.args[1]; - expect(addBidResponseIabCall).to.have.lengthOf(2); - expect(addBidResponseIabCall[0]).to.equal(placementCodeIab); - expect(addBidResponseIabCall[1].getStatusCode()).to.equal(STATUS.GOOD); - expect(addBidResponseIabCall[1].cpm).to.equal(4.56); - expect(addBidResponseIabCall[1].bidderCode).to.equal(bidderCode); - expect(addBidResponseIabCall[1].width).to.equal(300); - expect(addBidResponseIabCall[1].height).to.equal(250); - expect(addBidResponseIabCall[1].ad).to.contain(`placementid:'${placementIdIab}',format:'300x250',bidid:'test-bid-id-iab'`, 'ad missing parameters'); - // Verify no attempt to log error - expect(logError.called).to.equal(false, 'logError called'); + + expect(bidResponseNative.cpm).to.equal(1.23); + expect(bidResponseNative.requestId).to.equal(requestId); + expect(bidResponseNative.width).to.equal(300); + expect(bidResponseNative.height).to.equal(250); + expect(bidResponseNative.ad).to.contain(`placementid:'${placementIdNative}',format:'native',bidid:'test-bid-id-native'`, 'ad missing parameters'); + expect(bidResponseNative.creativeId).to.equal(placementIdNative); + expect(bidResponseNative.netRevenue).to.equal(true); + expect(bidResponseNative.currency).to.equal('USD'); + expect(bidResponseNative.hb_bidder).to.equal('fan'); + expect(bidResponseNative.fb_bidid).to.equal('test-bid-id-native'); + expect(bidResponseNative.fb_format).to.equal('native'); + expect(bidResponseNative.fb_placementid).to.equal(placementIdNative); + + expect(bidResponseIab.cpm).to.equal(4.56); + expect(bidResponseIab.requestId).to.equal(requestId); + expect(bidResponseIab.width).to.equal(300); + expect(bidResponseIab.height).to.equal(250); + expect(bidResponseIab.ad).to.contain(`placementid:'${placementIdIab}',format:'300x250',bidid:'test-bid-id-iab'`, 'ad missing parameters'); + expect(bidResponseIab.creativeId).to.equal(placementIdIab); + expect(bidResponseIab.netRevenue).to.equal(true); + expect(bidResponseIab.currency).to.equal('USD'); + expect(bidResponseIab.hb_bidder).to.equal('fan'); + expect(bidResponseIab.fb_bidid).to.equal('test-bid-id-iab'); + expect(bidResponseIab.fb_format).to.equal('300x250'); + expect(bidResponseIab.fb_placementid).to.equal(placementIdIab); }); it('valid video bid in response', () => { const bidId = 'test-bid-id-video'; - // Valid response - server.respondWith(JSON.stringify({ - errors: [], - bids: { - [placementId]: [{ - placement_id: placementId, - bid_id: bidId, - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] + + const [bidResponse] = interpretResponse({ + body: { + errors: [], + bids: { + [placementId]: [{ + placement_id: placementId, + bid_id: bidId, + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } } - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - placementCode, - params: { - placementId, - format: 'video' - }, - sizes: [[playerwidth, playerheight]] - }] + }, { + adformats: ['video'], + requestIds: [requestId], + sizes: [[playerwidth, playerheight]] }); - server.respond(); - // Verify addBidResponse call - expect(addBidResponse.calledOnce).to.equal(true); - const addBidResponseArgs = addBidResponse.args[0]; - expect(addBidResponseArgs).to.have.lengthOf(2); - expect(addBidResponseArgs[0]).to.equal(placementCode); - expect(addBidResponseArgs[1].getStatusCode()).to.equal(STATUS.GOOD); - expect(addBidResponseArgs[1].cpm).to.equal(1.23); - expect(addBidResponseArgs[1].bidderCode).to.equal(bidderCode); - // Video-specific properties - expect(addBidResponseArgs[1].mediaType).to.equal('video'); - expect(addBidResponseArgs[1].vastUrl) - .to.equal(addBidResponseArgs[1].descriptionUrl) - .and.to.contain('https://an.facebook.com/v1/instream/vast.xml?') - .and.to.contain(`placementid=${placementId}`) - .and.to.contain('pageurl=http%3A%2F%2F') - .and.to.contain(`playerwidth=${playerwidth}`) - .and.to.contain(`playerheight=${playerheight}`) - .and.to.contain(`bidid=${bidId}`); - expect(addBidResponseArgs[1].width).to.equal(playerwidth); - expect(addBidResponseArgs[1].height).to.equal(playerheight); - // Verify no attempt to log error - expect(logError.called).to.equal(false, 'logError called'); + + expect(bidResponse.cpm).to.equal(1.23); + expect(bidResponse.requestId).to.equal(requestId); + expect(bidResponse.mediaType).to.equal('video'); + expect(bidResponse.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${placementId}&pageurl=&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${bidId}`); + expect(bidResponse.width).to.equal(playerwidth); + expect(bidResponse.height).to.equal(playerheight); }); it('mixed video and native bids', () => { @@ -546,81 +350,45 @@ describe('AudienceNetwork adapter', () => { const videoBidId = 'test-video-bid-id'; const nativePlacementId = 'test-native-placement-id'; const nativeBidId = 'test-native-bid-id'; - // Valid response - server.respondWith(JSON.stringify({ - errors: [], - bids: { - [videoPlacementId]: [{ - placement_id: videoPlacementId, - bid_id: videoBidId, - bid_price_cents: 123, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }], - [nativePlacementId]: [{ - placement_id: nativePlacementId, - bid_id: nativeBidId, - bid_price_cents: 456, - bid_price_currency: 'usd', - bid_price_model: 'cpm' - }] + + const [bidResponseVideo, bidResponseNative] = interpretResponse({ + body: { + errors: [], + bids: { + [videoPlacementId]: [{ + placement_id: videoPlacementId, + bid_id: videoBidId, + bid_price_cents: 123, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }], + [nativePlacementId]: [{ + placement_id: nativePlacementId, + bid_id: nativeBidId, + bid_price_cents: 456, + bid_price_currency: 'usd', + bid_price_model: 'cpm' + }] + } } - })); - // Request bids - new AudienceNetwork().callBids({ - bidderCode, - bids: [{ - bidder: bidderCode, - placementCode, - params: { - placementId: videoPlacementId, - format: 'video' - }, - sizes: [[playerwidth, playerheight]] - }, { - bidder: bidderCode, - placementCode, - params: { - placementId: nativePlacementId, - format: 'native' - }, - sizes: [[300, 250]] - }] + }, { + adformats: ['video', 'native'], + requestIds: [requestId, requestId], + sizes: [[playerwidth, playerheight], [300, 250]] }); - server.respond(); - // Verify multiple attempts to call addBidResponse - expect(addBidResponse.calledTwice).to.equal(true); - // Verify video - const addBidResponseVideoCall = addBidResponse.args[0]; - expect(addBidResponseVideoCall).to.have.lengthOf(2); - expect(addBidResponseVideoCall[0]).to.equal(placementCode); - expect(addBidResponseVideoCall[1].getStatusCode()).to.equal(STATUS.GOOD); - expect(addBidResponseVideoCall[1].cpm).to.equal(1.23); - expect(addBidResponseVideoCall[1].bidderCode).to.equal(bidderCode); - // Video-specific properties - expect(addBidResponseVideoCall[1].mediaType).to.equal('video'); - expect(addBidResponseVideoCall[1].vastUrl) - .to.equal(addBidResponseVideoCall[1].descriptionUrl) - .and.to.contain('https://an.facebook.com/v1/instream/vast.xml?') - .and.to.contain(`placementid=${videoPlacementId}`) - .and.to.contain('pageurl=http%3A%2F%2F') - .and.to.contain(`playerwidth=${playerwidth}`) - .and.to.contain(`playerheight=${playerheight}`) - .and.to.contain(`bidid=${videoBidId}`); - expect(addBidResponseVideoCall[1].width).to.equal(playerwidth); - expect(addBidResponseVideoCall[1].height).to.equal(playerheight); - // Verify native - const addBidResponseNativeCall = addBidResponse.args[1]; - expect(addBidResponseNativeCall).to.have.lengthOf(2); - expect(addBidResponseNativeCall[0]).to.equal(placementCode); - expect(addBidResponseNativeCall[1].getStatusCode()).to.equal(STATUS.GOOD); - expect(addBidResponseNativeCall[1].cpm).to.equal(4.56); - expect(addBidResponseNativeCall[1].bidderCode).to.equal(bidderCode); - expect(addBidResponseNativeCall[1].width).to.equal(300); - expect(addBidResponseNativeCall[1].height).to.equal(250); - expect(addBidResponseNativeCall[1].ad).to.contain(`placementid:'${nativePlacementId}',format:'native',bidid:'${nativeBidId}'`); - // Verify no attempt to log error - expect(logError.called).to.equal(false, 'logError called'); + + expect(bidResponseVideo.cpm).to.equal(1.23); + expect(bidResponseVideo.requestId).to.equal(requestId); + expect(bidResponseVideo.mediaType).to.equal('video'); + expect(bidResponseVideo.vastUrl).to.equal(`https://an.facebook.com/v1/instream/vast.xml?placementid=${videoPlacementId}&pageurl=&playerwidth=${playerwidth}&playerheight=${playerheight}&bidid=${videoBidId}`); + expect(bidResponseVideo.width).to.equal(playerwidth); + expect(bidResponseVideo.height).to.equal(playerheight); + + expect(bidResponseNative.cpm).to.equal(4.56); + expect(bidResponseNative.requestId).to.equal(requestId); + expect(bidResponseNative.width).to.equal(300); + expect(bidResponseNative.height).to.equal(250); + expect(bidResponseNative.ad).to.contain(`placementid:'${nativePlacementId}',format:'native',bidid:'${nativeBidId}'`); }); }); }); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 3c9b6d47e9c..92e16573972 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,131 +1,149 @@ import { expect } from 'chai'; -import BeachfrontAdapter from 'modules/beachfrontBidAdapter'; -import bidmanager from 'src/bidmanager'; - -const ENDPOINT = '//reachms.bfmio.com/bid.json?exchange_id=11bc5dd5-7421-4dd8-c926-40fa653bec76'; - -const REQUEST = { - 'width': 640, - 'height': 480, - 'bidId': '2a1444be20bb2c', - 'bidder': 'beachfront', - 'bidderRequestId': '7101db09af0db2', - 'params': { - 'appId': 'whatever', - 'video': {}, - 'placementCode': 'video', - 'sizes': [ - 640, 480 - ] - }, - 'bids': [ - { - 'bidFloor': 0.01, - 'bidder': 'beachfront', - 'params': { - 'appId': '11bc5dd5-7421-4dd8-c926-40fa653bec76', - 'bidfloor': 0.01, - 'dev': true - }, - 'placementCode': 'video', - 'sizes': [640, 480], - 'bidId': '2a1444be20bb2c', - 'bidderRequestId': '7101db09af0db2', - 'requestId': '979b659e-ecff-46b8-ae03-7251bae4b725' - } - ], - 'requestId': '979b659e-ecff-46b8-ae03-7251bae4b725', -}; -var RESPONSE = { - 'bidPrice': 5.00, - 'url': 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da:0a47f4ce-d91f-48d0-bd1c-64fa2c196f13:2.90&dsp=58bf26882aba5e6ad608beda,0.612&i_type=pre' -}; +import { spec, ENDPOINT } from 'modules/beachfrontBidAdapter'; +import * as utils from 'src/utils'; describe('BeachfrontAdapter', () => { - let adapter; - - beforeEach(() => adapter = new BeachfrontAdapter()); + let bidRequest; + + beforeEach(() => { + bidRequest = { + bidder: 'beachfront', + params: { + bidfloor: 5.00, + appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76' + }, + adUnitCode: 'adunit-code', + sizes: [ 640, 480 ], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }; + }); - describe('request function', () => { - let xhr; - let requests; - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); + describe('spec.isBidRequestValid', () => { + it('should return true when the required params are passed', () => { + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - afterEach(() => xhr.restore()); + it('should return false when the "bidfloor" param is missing', () => { + bidRequest.params = { + appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); + it('should return false when the "appId" param is missing', () => { + bidRequest.params = { + bidfloor: 5.00 + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('requires parameters to make request', () => { - adapter.callBids({}); - expect(requests).to.be.empty; + it('should return false when no bid params are passed', () => { + bidRequest.params = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('sends bid request to ENDPOINT via POST', () => { - adapter.callBids(REQUEST); - expect(requests[0].url).to.equal(ENDPOINT); - expect(requests[0].method).to.equal('POST'); + it('should return false when a bid request is not passed', () => { + expect(spec.isBidRequestValid()).to.equal(false); + expect(spec.isBidRequestValid({})).to.equal(false); }); }); - describe('response handler', () => { - let server; - - beforeEach(() => { - server = sinon.fakeServer.create(); - sinon.stub(bidmanager, 'addBidResponse'); + describe('spec.buildRequests', () => { + it('should create a POST request for every bid', () => { + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(ENDPOINT + bidRequest.params.appId); }); - afterEach(() => { - server.restore(); - bidmanager.addBidResponse.restore(); + it('should attach the bid request object', () => { + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].bidRequest).to.equal(bidRequest); }); - it('registers bids', () => { - server.respondWith(JSON.stringify(RESPONSE)); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + it('should attach request data', () => { + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + const [ width, height ] = bidRequest.sizes; + expect(data.isPrebid).to.equal(true); + expect(data.appId).to.equal(bidRequest.params.appId); + expect(data.domain).to.equal(document.location.hostname); + expect(data.imp[0].video).to.deep.equal({ w: width, h: height }); + expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); + expect(data.site).to.deep.equal({ page: utils.getTopWindowLocation().host }); + expect(data.device).to.deep.contain({ ua: navigator.userAgent }); + expect(data.cur).to.deep.equal(['USD']); + }); - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property('statusMessage', 'Bid available'); - expect(response).to.have.property('cpm', 5.00); + it('must parse bid size from a nested array', () => { + const width = 640; + const height = 480; + bidRequest.sizes = [[ width, height ]]; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.imp[0].video).to.deep.equal({ w: width, h: height }); }); - it('handles nobid responses', () => { - server.respondWith(JSON.stringify({ - 'bidPrice': 5.00 - })); + it('must parse bid size from a string', () => { + const width = 640; + const height = 480; + bidRequest.sizes = `${width}x${height}`; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.imp[0].video).to.deep.equal({ w: width, h: height }); + }); - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + it('must handle an empty bid size', () => { + bidRequest.sizes = []; + const requests = spec.buildRequests([ bidRequest ]); + const data = requests[0].data; + expect(data.imp[0].video).to.deep.equal({ w: undefined, h: undefined }); + }); + }); - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property( - 'statusMessage', - 'Bid returned empty or error response' - ); + describe('spec.interpretResponse', () => { + it('should return no bids if the response is not valid', () => { + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + expect(bidResponse.length).to.equal(0); }); - it('handles JSON.parse errors', () => { - server.respondWith(''); + it('should return no bids if the response "url" is missing', () => { + const serverResponse = { + bidPrice: 5.00 + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + it('should return no bids if the response "bidPrice" is missing', () => { + const serverResponse = { + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.length).to.equal(0); + }); - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property( - 'statusMessage', - 'Bid returned empty or error response' - ); + it('should return a valid bid response', () => { + 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.equal({ + requestId: bidRequest.bidId, + bidderCode: spec.code, + cpm: serverResponse.bidPrice, + creativeId: serverResponse.cmpId, + vastUrl: serverResponse.url, + width: 640, + height: 480, + mediaType: 'video', + currency: 'USD', + ttl: 300, + netRevenue: true + }); }); }); }); diff --git a/test/spec/modules/c1xBidAdapter_spec.js b/test/spec/modules/c1xBidAdapter_spec.js index 3e482dfaae9..e1a48a5b701 100644 --- a/test/spec/modules/c1xBidAdapter_spec.js +++ b/test/spec/modules/c1xBidAdapter_spec.js @@ -177,6 +177,23 @@ describe('c1x adapter tests: ', () => { }); it('should show error when bidder sends invalid bid responses', () => { let responses; + let adUnits = []; + let unit = {}; + let params = getDefaultBidRequest(); + + unit.bids = params.bids; + unit.code = '/123456/header-bid-tag-1'; + unit.sizes = [[300, 250]]; + adUnits.push(unit); + + if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { + $$PREBID_GLOBAL$$._bidsRequested = [params]; + } else { + $$PREBID_GLOBAL$$._bidsRequested.push(params); + } + + $$PREBID_GLOBAL$$.adUnits = adUnits; + pbjs._c1xResponse(responses); let bidObject = stubAddBidResponse.getCall(0).args[1]; expect(bidObject.statusMessage).to.equal('Bid returned empty or error response'); diff --git a/test/spec/modules/centroBidAdapter_spec.js b/test/spec/modules/centroBidAdapter_spec.js index 9f354e1ba56..a4bceb5de39 100644 --- a/test/spec/modules/centroBidAdapter_spec.js +++ b/test/spec/modules/centroBidAdapter_spec.js @@ -43,6 +43,7 @@ describe('centro adapter tests', function () { unit: 28136, page_url: 'http://test_url.ru' }, + bidId: '1234', placementCode: 'div-gpt-ad-12345-1' }, { @@ -51,12 +52,14 @@ describe('centro adapter tests', function () { params: { unit: 28137 }, + bidId: '5678', placementCode: 'div-gpt-ad-12345-2' }, { bidder: 'centro', sizes: [[728, 90]], params: {}, + bidId: '9101112', placementCode: 'div-gpt-ad-12345-3' } ] @@ -71,7 +74,7 @@ describe('centro adapter tests', function () { var parsedBidUrl = urlParse(bidUrl1); var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - var generatedCallback = 'window["adCentroHandler_28136300x250div-gpt-ad-12345-1"]'; + var generatedCallback = 'window["adCentroHandler_28136300x2501234"]'; expect(parsedBidUrl.hostname).to.equal('staging.brand-server.com'); expect(parsedBidUrl.pathname).to.equal('/hb'); @@ -85,7 +88,7 @@ describe('centro adapter tests', function () { parsedBidUrl = urlParse(bidUrl2); parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - generatedCallback = 'window["adCentroHandler_28137728x90div-gpt-ad-12345-2"]'; + generatedCallback = 'window["adCentroHandler_28137728x905678"]'; expect(parsedBidUrl.hostname).to.equal('t.brand-server.com'); expect(parsedBidUrl.pathname).to.equal('/hb'); @@ -117,6 +120,7 @@ describe('centro adapter tests', function () { params: { unit: 28136 }, + bidId: '12345', placementCode: '/19968336/header-bid-tag-0' }, { @@ -125,6 +129,7 @@ describe('centro adapter tests', function () { params: { unit: 111111 }, + bidId: '12346', placementCode: '/19968336/header-bid-tag-1' }, { @@ -133,6 +138,7 @@ describe('centro adapter tests', function () { params: { unit: 222222 }, + bidId: '12347', placementCode: '/19968336/header-bid-tag-2' }, { @@ -141,6 +147,7 @@ describe('centro adapter tests', function () { params: { unit: 333333 }, + bidId: '12348', placementCode: '/19968336/header-bid-tag-3' } ] @@ -149,9 +156,9 @@ describe('centro adapter tests', function () { it('callback function should exist', function () { adapter().callBids(params); - expect(window['adCentroHandler_28136300x250%2F19968336%2Fheader-bid-tag-0']) + expect(window['adCentroHandler_28136300x25012345']) .to.exist.and.to.be.a('function'); - expect(window['adCentroHandler_111111728x90%2F19968336%2Fheader-bid-tag-1']) + expect(window['adCentroHandler_111111728x9012346']) .to.exist.and.to.be.a('function'); }); @@ -180,10 +187,10 @@ describe('centro adapter tests', function () { var response3 = {'adTag': '', 'height': 0, 'value': 0, 'width': 0, 'sectionID': 222222}; var response4 = ''; - window['adCentroHandler_28136300x250%2F19968336%2Fheader-bid-tag-0'](response); - window['adCentroHandler_111111728x90%2F19968336%2Fheader-bid-tag-1'](response2); - window['adCentroHandler_222222728x90%2F19968336%2Fheader-bid-tag-2'](response3); - window['adCentroHandler_333333728x90%2F19968336%2Fheader-bid-tag-3'](response4); + window['adCentroHandler_28136300x25012345'](response); + window['adCentroHandler_111111728x9012346'](response2); + window['adCentroHandler_222222728x9012347'](response3); + window['adCentroHandler_333333728x9012348'](response4); var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; var bidObject1 = stubAddBidResponse.getCall(0).args[1]; diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 57cd9411e66..81da6867132 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -1,376 +1,277 @@ -var expect = require('chai').expect; +import {expect} from 'chai'; +import {spec} from 'modules/conversantBidAdapter'; +import * as utils from 'src/utils'; + var Adapter = require('modules/conversantBidAdapter'); var bidManager = require('src/bidmanager'); -describe('Conversant adapter tests', function () { - var addBidResponseSpy; - var adapter; - - var bidderRequest = { - bidderCode: 'conversant', - bids: [ - { - bidId: 'bidId1', - bidder: 'conversant', - placementCode: 'div1', - sizes: [[300, 600]], - params: { - site_id: '87293', - position: 1, - tag_id: 'tagid-1', - secure: false - } - }, { - bidId: 'bidId2', - bidder: 'conversant', - placementCode: 'div2', - sizes: [[300, 600]], - params: { - site_id: '87293', - secure: false - } - }, { - bidId: 'bidId3', - bidder: 'conversant', - placementCode: 'div3', - sizes: [[300, 600], [160, 600]], - params: { - site_id: '87293', - position: 1, - tag_id: '', - secure: false +describe('Conversant adapter tests', function() { + const siteId = '108060'; + + const bidRequests = [ + { + bidder: 'conversant', + params: { + site_id: siteId, + position: 1, + tag_id: 'tagid-1', + secure: false, + bidfloor: 0.5 + }, + placementCode: 'pcode000', + transactionId: 'tx000', + sizes: [[300, 250]], + bidId: 'bid000', + bidderRequestId: '117d765b87bed38', + requestId: 'req000' + }, { + bidder: 'conversant', + params: { + site_id: siteId, + secure: false + }, + placementCode: 'pcode001', + transactionId: 'tx001', + sizes: [[468, 60]], + bidId: 'bid001', + bidderRequestId: '117d765b87bed38', + requestId: 'req000' + }, { + bidder: 'conversant', + params: { + site_id: siteId, + position: 2, + tag_id: '', + secure: false + }, + placementCode: 'pcode002', + transactionId: 'tx002', + sizes: [[300, 600], [160, 600]], + bidId: 'bid002', + bidderRequestId: '117d765b87bed38', + requestId: 'req000' + }, { + bidder: 'conversant', + params: { + site_id: siteId, + api: [2], + protocols: [1, 2], + mimes: ['video/mp4', 'video/x-flv'], + maxduration: 30 + }, + mediaTypes: { + video: { + context: 'instream' } - }, { - bidId: 'bidId4', - bidder: 'conversant', - placementCode: 'div4', - mediaType: 'video', - sizes: [[480, 480]], - params: { - site_id: '89192', - pos: 1, - tagid: 'tagid-4', - secure: false - } - } - ] - }; - - it('The Conversant response should exist and be a function', function () { - expect($$PREBID_GLOBAL$$.conversantResponse).to.exist.and.to.be.a('function'); - }); - - describe('Should submit bid responses correctly', function () { - beforeEach(function () { - addBidResponseSpy = sinon.stub(bidManager, 'addBidResponse'); - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - adapter = new Adapter(); - }); - - afterEach(function () { - addBidResponseSpy.restore(); - }); - - it('Should correctly submit valid and empty bids to the bid manager', function () { - var bidResponse = { - id: 123, - seatbid: [{ - bid: [{ - id: 1111111, - impid: 'bidId1', - price: 0 - }, { - id: 2345, - impid: 'bidId2', - price: 0.22, - nurl: '', - adm: 'adm2', - h: 300, - w: 600 - }] - }] - }; - - $$PREBID_GLOBAL$$.conversantResponse(bidResponse); - - // in this case, the valid bid (div2) is submitted before the empty bids (div1, div3) - var firstBid = addBidResponseSpy.getCall(0).args[1]; - var secondBid = addBidResponseSpy.getCall(1).args[1]; - var thirdBid = addBidResponseSpy.getCall(2).args[1]; - var placementCode1 = addBidResponseSpy.getCall(0).args[0]; - var placementCode2 = addBidResponseSpy.getCall(1).args[0]; - var placementCode3 = addBidResponseSpy.getCall(2).args[0]; - - expect(firstBid.getStatusCode()).to.equal(1); - expect(firstBid.bidderCode).to.equal('conversant'); - expect(firstBid.cpm).to.equal(0.22); - expect(firstBid.ad).to.equal('adm2' + ''); - expect(placementCode1).to.equal('div2'); - - expect(secondBid.getStatusCode()).to.equal(2); - expect(secondBid.bidderCode).to.equal('conversant'); - expect(placementCode2).to.equal('div1'); - - expect(thirdBid.getStatusCode()).to.equal(2); - expect(thirdBid.bidderCode).to.equal('conversant'); - expect(placementCode3).to.equal('div3'); - - expect(addBidResponseSpy.getCalls().length).to.equal(4); - }); - - it('Should submit bids with statuses of 2 to the bid manager for empty bid responses', function () { - $$PREBID_GLOBAL$$.conversantResponse({id: 1, seatbid: []}); - - var placementCode1 = addBidResponseSpy.getCall(0).args[0]; - var firstBid = addBidResponseSpy.getCall(0).args[1]; - var placementCode2 = addBidResponseSpy.getCall(1).args[0]; - var secondBid = addBidResponseSpy.getCall(1).args[1]; - var placementCode3 = addBidResponseSpy.getCall(2).args[0]; - var thirdBid = addBidResponseSpy.getCall(2).args[1]; - - expect(placementCode1).to.equal('div1'); - expect(firstBid.getStatusCode()).to.equal(2); - expect(firstBid.bidderCode).to.equal('conversant'); - - expect(placementCode2).to.equal('div2'); - expect(secondBid.getStatusCode()).to.equal(2); - expect(secondBid.bidderCode).to.equal('conversant'); - - expect(placementCode3).to.equal('div3'); - expect(thirdBid.getStatusCode()).to.equal(2); - expect(thirdBid.bidderCode).to.equal('conversant'); - - expect(addBidResponseSpy.getCalls().length).to.equal(4); - }); - - it('Should submit valid bids to the bid manager', function () { - var bidResponse = { - id: 123, - seatbid: [{ - bid: [{ - id: 1111111, - impid: 'bidId1', - price: 0.11, - nurl: '', - adm: 'adm', - h: 250, - w: 300, - ext: {} - }, { - id: 2345, - impid: 'bidId2', - price: 0.22, - nurl: '', - adm: 'adm2', - h: 300, - w: 600 - }, { - id: 33333, - impid: 'bidId3', - price: 0.33, - nurl: '', - adm: 'adm3', - h: 160, - w: 600 - }] - }] - }; - - $$PREBID_GLOBAL$$.conversantResponse(bidResponse); - - var firstBid = addBidResponseSpy.getCall(0).args[1]; - var secondBid = addBidResponseSpy.getCall(1).args[1]; - var thirdBid = addBidResponseSpy.getCall(2).args[1]; - var placementCode1 = addBidResponseSpy.getCall(0).args[0]; - var placementCode2 = addBidResponseSpy.getCall(1).args[0]; - var placementCode3 = addBidResponseSpy.getCall(2).args[0]; - - expect(firstBid.getStatusCode()).to.equal(1); - expect(firstBid.bidderCode).to.equal('conversant'); - expect(firstBid.cpm).to.equal(0.11); - expect(firstBid.ad).to.equal('adm' + ''); - expect(placementCode1).to.equal('div1'); - - expect(secondBid.getStatusCode()).to.equal(1); - expect(secondBid.bidderCode).to.equal('conversant'); - expect(secondBid.cpm).to.equal(0.22); - expect(secondBid.ad).to.equal('adm2' + ''); - expect(placementCode2).to.equal('div2'); - - expect(thirdBid.getStatusCode()).to.equal(1); - expect(thirdBid.bidderCode).to.equal('conversant'); - expect(thirdBid.cpm).to.equal(0.33); - expect(thirdBid.ad).to.equal('adm3' + ''); - expect(placementCode3).to.equal('div3'); - - expect(addBidResponseSpy.getCalls().length).to.equal(4); - }); - - it('Should submit video bid responses correctly.', function () { - var bidResponse = { - id: 123, - seatbid: [{ - bid: [{ - id: 1111111, - impid: 'bidId4', - price: 0.11, - nurl: 'imp_tracker', - adm: 'vasturl' - }] - }] - }; - - $$PREBID_GLOBAL$$.conversantResponse(bidResponse); - - var videoBid = addBidResponseSpy.getCall(0).args[1]; - var placementCode = addBidResponseSpy.getCall(0).args[0]; - - expect(videoBid.getStatusCode()).to.equal(1); - expect(videoBid.bidderCode).to.equal('conversant'); - expect(videoBid.cpm).to.equal(0.11); - expect(videoBid.vastUrl).to.equal('vasturl'); - expect(placementCode).to.equal('div4'); - }) - }); - - describe('Should submit the correct headers in the xhr', function () { - var server, - adapter; - - var bidResponse = { - id: 123, + }, + placementCode: 'pcode003', + transactionId: 'tx003', + sizes: [640, 480], + bidId: 'bid003', + bidderRequestId: '117d765b87bed38', + requestId: 'req000' + }]; + + const bidResponses = { + body: { + id: 'req000', seatbid: [{ bid: [{ - id: 1111, - impid: 'bidId1', - price: 0.11, - nurl: '', - adm: 'adm', - h: 250, + nurl: 'notify000', + adm: 'markup000', + crid: '1000', + impid: 'bid000', + price: 0.99, w: 300, - ext: {} + h: 250, + adomain: ['https://example.com'], + id: 'bid000' }, { - id: 2222, - impid: 'bidId2', - price: 0.22, - nurl: '', - adm: 'adm2', - h: 300, - w: 600 + impid: 'bid001', + price: 0.00000, + id: 'bid001' }, { - id: 3333, - impid: 'bidId3', - price: 0.33, - nurl: '', - adm: 'adm3', - h: 160, - w: 600 - }] - }] - }; - - beforeEach(function () { - server = sinon.fakeServer.create(); - adapter = new Adapter(); - }); - - afterEach(function () { - server.restore(); - }); - - beforeEach(function () { - var resp = [200, {'Content-type': 'text/javascript'}, '$$PREBID_GLOBAL$$.conversantResponse(\'' + JSON.stringify(bidResponse) + '\')']; - server.respondWith('POST', new RegExp('media.msg.dotomi.com/s2s/header'), resp); - }); - - it('Should contain valid request header properties', function () { - adapter.callBids(bidderRequest); - server.respond(); - - var request = server.requests[0]; - expect(request.requestBody).to.not.be.empty; - }); - }); - describe('Should create valid bid requests.', function () { - var server, - adapter; - - var bidResponse = { - id: 123, - seatbid: [{ - bid: [{ - id: 1111, - impid: 'bidId1', - price: 0.11, - nurl: '', - adm: 'adm', - h: 250, + nurl: 'notify002', + adm: 'markup002', + crid: '1002', + impid: 'bid002', + price: 2.99, w: 300, - ext: {} - }, { - id: 2222, - impid: 'bidId2', - price: 0.22, - nurl: '', - adm: 'adm2', - h: 300, - w: 600 + h: 600, + adomain: ['https://example.com'], + id: 'bid002' }, { - id: 3333, - impid: 'bidId3', - price: 0.33, - nurl: '', - adm: 'adm3', - h: 160, - w: 600 + nurl: 'notify003', + adm: 'markup003', + crid: '1003', + impid: 'bid003', + price: 3.99, + adomain: ['https://example.com'], + id: 'bid003' }] }] - }; - - beforeEach(function () { - server = sinon.fakeServer.create(); - adapter = new Adapter(); - }); - - afterEach(function () { - server.restore(); - }); + }, + headers: {}}; + + it('Verify basic properties', function() { + expect(spec.code).to.equal('conversant'); + expect(spec.aliases).to.be.an('array').with.lengthOf(1); + expect(spec.aliases[0]).to.equal('cnvr'); + expect(spec.supportedMediaTypes).to.be.an('array').with.lengthOf(1); + expect(spec.supportedMediaTypes[0]).to.equal('video'); + }); - beforeEach(function () { - var resp = [200, {'Content-type': 'text/javascript'}, '$$PREBID_GLOBAL$$.conversantResponse(\'' + JSON.stringify(bidResponse) + '\')']; - server.respondWith('POST', new RegExp('media.msg.dotomi.com/s2s/header'), resp); - }); + it('Verify user syncs', function() { + expect(spec.getUserSyncs({})).to.be.undefined; + expect(spec.getUserSyncs({iframeEnabled: true})).to.be.undefined; + expect(spec.getUserSyncs({pixelEnabled: false})).to.be.undefined; - it('Should create valid bid requests.', function () { - adapter.callBids(bidderRequest); - server.respond(); - var request = JSON.parse(server.requests[0].requestBody); - expect(request.imp[0].banner.format[0].w).to.equal(300); - expect(request.imp[0].banner.format[0].h).to.equal(600); - expect(request.imp[0].tagid).to.equal('tagid-1'); - expect(request.imp[0].banner.pos).to.equal(1); - expect(request.imp[0].secure).to.equal(0); - expect(request.site.id).to.equal('89192'); - }); + const syncs = spec.getUserSyncs({pixelEnabled: true}); + expect(syncs).to.be.an('array').with.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.equal('//media.msg.dotomi.com/w/user.sync'); + }); - it('Should not pass empty or missing optional parameters on requests.', function () { - adapter.callBids(bidderRequest); - server.respond(); + it('Verify isBidRequestValid', function() { + expect(spec.isBidRequestValid({})).to.be.false; + expect(spec.isBidRequestValid({params: {}})).to.be.false; + expect(spec.isBidRequestValid({params: {site_id: '123'}})).to.be.true; + expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[1])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[2])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[3])).to.be.true; + + const simpleVideo = JSON.parse(JSON.stringify(bidRequests[3])); + simpleVideo.params.site_id = 123; + expect(spec.isBidRequestValid(simpleVideo)).to.be.false; + simpleVideo.params.site_id = siteId; + simpleVideo.params.mimes = [1, 2, 3]; + expect(spec.isBidRequestValid(simpleVideo)).to.be.false; + simpleVideo.params.mimes = 'bad type'; + expect(spec.isBidRequestValid(simpleVideo)).to.be.false; + delete simpleVideo.params.mimes; + expect(spec.isBidRequestValid(simpleVideo)).to.be.true; + }); - var request = JSON.parse(server.requests[0].requestBody); - expect(request.imp[1].tagid).to.equal(undefined); - expect(request.imp[2].tagid).to.equal(undefined); - expect(request.imp[1].pos).to.equal(undefined); - }); + it('Verify buildRequest', function() { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('//media.msg.dotomi.com/s2s/header/24'); + const payload = request.data; + + expect(payload).to.have.property('id', 'req000'); + expect(payload).to.have.property('at', 1); + expect(payload).to.have.property('imp'); + expect(payload.imp).to.be.an('array').with.lengthOf(4); + + 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('bidfloor', 0.5); + expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/); + expect(payload.imp[0]).to.have.property('tagid', 'tagid-1'); + expect(payload.imp[0]).to.have.property('banner'); + expect(payload.imp[0].banner).to.have.property('pos', 1); + expect(payload.imp[0].banner).to.have.property('format'); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); + 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('bidfloor', 0); + expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/); + expect(payload.imp[1]).to.not.have.property('tagid'); + expect(payload.imp[1]).to.have.property('banner'); + expect(payload.imp[1].banner).to.not.have.property('pos'); + expect(payload.imp[1].banner).to.have.property('format'); + expect(payload.imp[1].banner.format).to.deep.equal([{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('bidfloor', 0); + expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/); + expect(payload.imp[2]).to.have.property('banner'); + expect(payload.imp[2].banner).to.have.property('pos', 2); + expect(payload.imp[2].banner).to.have.property('format'); + 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('bidfloor', 0); + expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(/^\d+\.\d+\.\d+$/); + expect(payload.imp[3]).to.not.have.property('tagid'); + expect(payload.imp[3]).to.have.property('video'); + expect(payload.imp[3].video).to.not.have.property('pos'); + expect(payload.imp[3].video).to.have.property('format'); + expect(payload.imp[3].video.format).to.deep.equal([{w: 640, h: 480}]); + expect(payload.imp[3].video).to.have.property('mimes'); + expect(payload.imp[3].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[3].video).to.have.property('protocols'); + expect(payload.imp[3].video.protocols).to.deep.equal([1, 2]); + expect(payload.imp[3].video).to.have.property('api'); + expect(payload.imp[3].video.api).to.deep.equal([2]); + expect(payload.imp[3].video).to.have.property('maxduration', 30); + expect(payload.imp[3]).to.not.have.property('banner'); + + 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.pathname + loc.search + loc.hash; + expect(payload.site).to.have.property('page', page); + + expect(payload).to.have.property('device'); + expect(payload.device).to.have.property('w', screen.width); + expect(payload.device).to.have.property('h', screen.height); + expect(payload.device).to.have.property('dnt').that.is.oneOf([0, 1]); + expect(payload.device).to.have.property('ua', navigator.userAgent); + }); - it('Should create the format objects correctly.', function () { - adapter.callBids(bidderRequest); - server.respond(); + it('Verify interpretResponse', function() { + const request = spec.buildRequests(bidRequests); + const response = spec.interpretResponse(bidResponses, request); + expect(response).to.be.an('array').with.lengthOf(3); + + let bid = response[0]; + expect(bid).to.have.property('requestId', 'bid000'); + expect(bid).to.have.property('currency', 'USD'); + expect(bid).to.have.property('cpm', 0.99); + expect(bid).to.have.property('creativeId', '1000'); + expect(bid).to.have.property('width', 300); + expect(bid).to.have.property('height', 250); + expect(bid).to.have.property('ad', 'markup000'); + + // There is no bid001 because cpm is $0 + + bid = response[1]; + expect(bid).to.have.property('requestId', 'bid002'); + expect(bid).to.have.property('currency', 'USD'); + expect(bid).to.have.property('cpm', 2.99); + expect(bid).to.have.property('creativeId', '1002'); + expect(bid).to.have.property('width', 300); + expect(bid).to.have.property('height', 600); + expect(bid).to.have.property('ad', 'markup002'); + + bid = response[2]; + expect(bid).to.have.property('requestId', 'bid003'); + expect(bid).to.have.property('currency', 'USD'); + expect(bid).to.have.property('cpm', 3.99); + expect(bid).to.have.property('creativeId', '1003'); + expect(bid).to.have.property('width', 640); + expect(bid).to.have.property('height', 480); + expect(bid).to.have.property('vastUrl', 'markup003'); + expect(bid).to.have.property('mediaType', 'video'); + }); - var request = JSON.parse(server.requests[0].requestBody); - expect(request.imp[2].banner.format.length).to.equal(2); - expect(request.imp[2].banner.format[0].w).to.equal(300); - expect(request.imp[2].banner.format[1].w).to.equal(160); - }); + it('Verify handling of bad responses', function() { + let response = spec.interpretResponse({}, {}); + expect(response).to.be.an('array').with.lengthOf(0); + response = spec.interpretResponse({id: '123'}, {}); + expect(response).to.be.an('array').with.lengthOf(0); + response = spec.interpretResponse({id: '123', seatbid: []}, {}); + expect(response).to.be.an('array').with.lengthOf(0); }); -}); +}) diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index 937e6a084e4..06faa5665c9 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -5,7 +5,7 @@ import { import { setConfig, - addBidResponseDecorator, + addBidResponseHook, currencySupportEnabled, currencyRates @@ -46,10 +46,6 @@ describe('currency', function () { var bid = { cpm: 1, bidder: 'rubicon' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { - innerBid = bid; - }); - setConfig({ adServerCurrency: 'GBP', bidderCurrencyDefault: { @@ -57,7 +53,9 @@ describe('currency', function () { } }); - wrappedAddBidResponseFn('elementId', bid); + addBidResponseHook('elementId', bid, function(adCodeId, bid) { + innerBid = bid; + }); expect(innerBid.currency).to.equal('GBP') }); @@ -68,10 +66,6 @@ describe('currency', function () { var bid = { cpm: 1, currency: 'JPY', bidder: 'rubicon' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { - innerBid = bid; - }); - setConfig({ adServerCurrency: 'JPY', bidderCurrencyDefault: { @@ -79,7 +73,9 @@ describe('currency', function () { } }); - wrappedAddBidResponseFn('elementId', bid); + addBidResponseHook('elementId', bid, function(adCodeId, bid) { + innerBid = bid; + }); expect(innerBid.currency).to.equal('JPY') }); @@ -97,12 +93,10 @@ describe('currency', function () { var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); - expect(innerBid.cpm).to.equal('1.0000'); }); }); @@ -113,14 +107,15 @@ describe('currency', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); - var marker = false; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { - marker = true; - }); var bid = { 'cpm': 1, 'currency': 'USD' }; setConfig({ 'adServerCurrency': 'JPY' }); - wrappedAddBidResponseFn('elementId', bid); + + var marker = false; + addBidResponseHook('elementId', bid, function() { + marker = true; + }); + expect(marker).to.equal(false); fakeCurrencyFileServer.respond(); @@ -133,10 +128,9 @@ describe('currency', function () { setConfig({}); var bid = { 'cpm': 1, 'currency': 'USD' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.cpm).to.equal(1); }); @@ -144,10 +138,9 @@ describe('currency', function () { setConfig({}); var bid = { 'cpm': 1, 'currency': 'GBP' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.statusMessage).to.equal('Bid returned empty or error response'); }); @@ -157,10 +150,9 @@ describe('currency', function () { }); var bid = { 'cpm': 1, 'currency': 'USD' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(bid).to.equal(innerBid); }); @@ -170,10 +162,9 @@ describe('currency', function () { fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'ABC' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.statusMessage).to.equal('Bid returned empty or error response'); }); @@ -183,10 +174,9 @@ describe('currency', function () { fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'GBP' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.statusMessage).to.equal('Bid returned empty or error response'); }); @@ -196,10 +186,9 @@ describe('currency', function () { fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'JPY' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.cpm).to.equal(1); expect(innerBid.currency).to.equal('JPY'); }); @@ -210,10 +199,9 @@ describe('currency', function () { fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'USD' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.cpm).to.equal('0.7798'); expect(innerBid.currency).to.equal('GBP'); }); @@ -224,10 +212,9 @@ describe('currency', function () { fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'CNY' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.cpm).to.equal('0.1133'); expect(innerBid.currency).to.equal('GBP'); }); @@ -238,10 +225,9 @@ describe('currency', function () { fakeCurrencyFileServer.respond(); var bid = { 'cpm': 1, 'currency': 'JPY' }; var innerBid; - var wrappedAddBidResponseFn = addBidResponseDecorator(function(adCodeId, bid) { + addBidResponseHook('elementId', bid, function(adCodeId, bid) { innerBid = bid; }); - wrappedAddBidResponseFn('elementId', bid); expect(innerBid.cpm).to.equal('0.0623'); expect(innerBid.currency).to.equal('CNY'); }); diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index 81c83baa65c..07439be126c 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -4,6 +4,7 @@ import parse from 'url-parse'; import buildDfpVideoUrl from 'modules/dfpAdServerVideo'; import { parseQS } from 'src/url'; import adUnit from 'test/fixtures/video/adUnit'; +import { newConfig } from 'src/config'; const bid = { videoCacheKey: 'abc', @@ -36,6 +37,43 @@ describe('The DFP video support module', () => { expect(queryParams).to.have.property('url'); }); + it('can take an adserver url as a parameter', () => { + const bidCopy = Object.assign({ }, bid); + bidCopy.vastUrl = 'vastUrl.example'; + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + url: 'https://video.adserver.example/', + })); + + expect(url.host).to.equal('video.adserver.example'); + + const queryObject = parseQS(url.query); + expect(queryObject.description_url).to.equal('vastUrl.example'); + }); + + it('requires a params object or url', () => { + const url = buildDfpVideoUrl({ + adUnit: adUnit, + bid: bid, + }); + + expect(url).to.be.undefined; + }); + + it('overwrites url params when both url and params object are given', () => { + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bid, + url: 'https://video.adserver.example/ads?sz=640x480&iu=/123/aduniturl&impl=s', + params: { iu: 'my/adUnit' } + })); + + const queryObject = parseQS(url.query); + expect(queryObject.iu).to.equal('my/adUnit'); + }); + it('should override param defaults with user-provided ones', () => { const url = parse(buildDfpVideoUrl({ adUnit: adUnit, @@ -91,4 +129,33 @@ describe('The DFP video support module', () => { expect(customParams).to.have.property('hb_adid', 'ad_id'); expect(customParams).to.have.property('my_targeting', 'foo'); }); + + it('should not overwrite an existing description_url for object input and cache disabled', () => { + const config = newConfig(); + config.setConfig({ usePrebidCache: true }); + + const bidCopy = Object.assign({}, bid); + bidCopy.vastUrl = 'vastUrl.example'; + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + iu: 'my/adUnit', + description_url: 'descriptionurl.example' + } + })); + + const queryObject = parseQS(url.query); + expect(queryObject.description_url).to.equal('descriptionurl.example'); + }); + + it('should work with nobid responses', () => { + const url = buildDfpVideoUrl({ + adUnit: adUnit, + params: { 'iu': 'my/adUnit' } + }); + + expect(url).to.be.a('string'); + }); }); diff --git a/test/spec/modules/fidelityBidAdapter_spec.js b/test/spec/modules/fidelityBidAdapter_spec.js index 5777c6af8cd..036a34ee0b0 100644 --- a/test/spec/modules/fidelityBidAdapter_spec.js +++ b/test/spec/modules/fidelityBidAdapter_spec.js @@ -1,195 +1,160 @@ -describe('fidelity adapter tests', function() { - const expect = require('chai').expect; - const adapter = require('modules/fidelityBidAdapter'); - const adLoader = require('src/adloader'); - const bidmanager = require('src/bidmanager'); - const STATUS = require('src/constants').STATUS; - var urlParse = require('url-parse'); - var querystringify = require('querystringify'); - - describe('creation of bid url', function () { - it('should be called', function () { - var stubLoadScript; - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - - var bidderRequest = { - bidderCode: 'fidelity', - bids: [ - { - bidId: 'bidId-123456-1', - bidder: 'fidelity', - params: { - zoneid: '37' - }, - placementCode: 'div-gpt-ad-123456-1' - }, - ] - }; +import { expect } from 'chai'; +import { spec } from 'modules/fidelityBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; - adapter().callBids(bidderRequest); - sinon.assert.called(stubLoadScript); +describe('FidelityAdapter', () => { + const adapter = newBidder(spec); - stubLoadScript.restore(); + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); }); + }); - it('should populate required parameters', function () { - var stubLoadScript; - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - - var bidderRequest = { - bidderCode: 'fidelity', - bids: [ - { - bidId: 'bidId-123456-1', - bidder: 'fidelity', - params: { - zoneid: '37', - }, - placementCode: 'div-gpt-ad-123456-1' - }, - ] - }; - - adapter().callBids(bidderRequest); - - stubLoadScript.restore(); + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'fidelity', + 'params': { + 'zoneid': '37', + 'floor': '0.05', + 'server': 't.fidelity-media.com', + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should populate required and optional parameters', function () { - var stubLoadScript; - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - - var bidderRequest = { - bidderCode: 'fidelity', - bids: [ - { - bidId: 'bidId-123456-1', - bidder: 'fidelity', - params: { - zoneid: '37', - server: 't.fidelity-media.com', - loc: 'http://locurl', - click: 'http://clickurl', - subid: '000' - }, - placementCode: 'div-gpt-ad-123456-1' - }, - ] + it('should return true when required params found', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'zoneid': '37', }; - - adapter().callBids(bidderRequest); - - var requestURI = stubLoadScript.getCall(0).args[0]; - var parsedBidUrl = urlParse(requestURI); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - - expect(parsedBidUrl.hostname).to.equal('t.fidelity-media.com'); - - expect(parsedBidUrlQueryString).to.have.property('zoneid').and.to.equal('37'); - expect(parsedBidUrlQueryString).to.have.property('impid').and.to.equal('bidId-123456-1'); - expect(parsedBidUrlQueryString).to.have.property('callback').and.to.equal('window.$$PREBID_GLOBAL$$.fidelityResponse'); - expect(parsedBidUrlQueryString).to.have.property('loc').and.to.equal('http://locurl'); - expect(parsedBidUrlQueryString).to.have.property('ct0').and.to.equal('http://clickurl'); - expect(parsedBidUrlQueryString).to.have.property('subid').and.to.equal('000'); - - stubLoadScript.restore(); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - }); - describe('fidelityResponse', function () { - it('should exist and be a function', function () { - expect($$PREBID_GLOBAL$$.fidelityResponse).to.exist.and.to.be.a('function'); + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'zoneid': 0, + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); + }); - it('should add empty bid response if no bids returned', function () { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var bidderRequest = { - bidderCode: 'fidelity', - bids: [ - { - bidId: 'bidId-123456-1', - bidder: 'fidelity', - params: { - zoneid: '37' - }, - placementCode: 'div-gpt-ad-123456-1' + describe('buildRequests', () => { + let bidderRequest = { + bidderCode: 'fidelity', + requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + bidderRequestId: '178e34bad3658f', + bids: [ + { + bidder: 'fidelity', + params: { + zoneid: '37', + floor: '0.05', + server: 't.fidelity-media.com', }, - ] - }; - - // no bids returned in the response. - var response = { - 'id': '543210', - 'seatbid': [] - }; - - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - // adapter needs to be called, in order for the stub to register. - adapter() - - $$PREBID_GLOBAL$$.fidelityResponse(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('div-gpt-ad-123456-1'); - expect(bidObject1.getStatusCode()).to.equal(2); - expect(bidObject1.bidderCode).to.equal('fidelity'); - - stubAddBidResponse.restore(); + placementCode: '/19968336/header-bid-tag-0', + sizes: [[300, 250], [320, 50]], + bidId: '2ffb201a808da7', + bidderRequestId: '178e34bad3658f', + requestId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a', + transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' + } + ], + start: 1472239426002, + auctionStart: 1472239426000, + timeout: 5000 + }; + + it('should add source and verison to the tag', () => { + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const payload = request.data; + expect(payload.from).to.exist; + expect(payload.v).to.exist; + expect(payload.requestid).to.exist; + expect(payload.impid).to.exist; + expect(payload.zoneid).to.exist; + expect(payload.floor).to.exist; + expect(payload.charset).to.exist; + expect(payload.defloc).to.exist; + expect(payload.altloc).to.exist; + expect(payload.subid).to.exist; + expect(payload.flashver).to.exist; + expect(payload.tmax).to.exist; }); - it('should add a bid response for bid returned', function () { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var bidderRequest = { - bidderCode: 'fidelity', - bids: [ - { - bidId: 'bidId-123456-1', - bidder: 'fidelity', - params: { - zoneid: '37' - }, - placementCode: 'div-gpt-ad-123456-1' - }, - ] - }; + it('sends bid request to ENDPOINT via GET', () => { + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.url).to.equal('//t.fidelity-media.com/delivery/hb.php'); + expect(request.method).to.equal('GET'); + }); + }) + + describe('interpretResponse', () => { + let response = { + 'id': '543210', + 'seatbid': [ { + 'bid': [ { + 'id': '1111111', + 'impid': 'bidId-123456-1', + 'price': 0.09, + 'adm': '', + 'width': 728, + 'height': 90, + } ] + } ] + }; + + it('should get correct bid response', () => { + let expectedResponse = [ + { + requestId: 'bidId-123456-1', + creativeId: 'bidId-123456-1', + cpm: 0.09, + width: 728, + height: 90, + ad: '', + netRevenue: true, + currency: 'USD', + ttl: 360, + } + ]; + + let result = spec.interpretResponse({ body: response }); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); - // Returning a single bid in the response. - var response = { + it('handles nobid responses', () => { + let response = { 'id': '543210', - 'seatbid': [ { - 'bid': [ { - 'id': '1111111', - 'impid': 'bidId-123456-1', - 'price': 0.09, - 'adm': '<>', - 'height': 90, - 'width': 728 - } ] - } ] + 'seatbid': [ ] }; - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - // adapter needs to be called, in order for the stub to register. - adapter() - - $$PREBID_GLOBAL$$.fidelityResponse(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('div-gpt-ad-123456-1'); - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.bidderCode).to.equal('fidelity'); - expect(bidObject1.cpm).to.equal(0.09); - expect(bidObject1.height).to.equal(90); - expect(bidObject1.width).to.equal(728); - expect(bidObject1.ad).to.equal('<>'); + let result = spec.interpretResponse({ body: response }); + expect(result.length).to.equal(0); + }); + }); - stubAddBidResponse.restore(); + describe('user sync', () => { + const syncUrl = '//x.fidelity-media.com/delivery/matches.php?type=iframe'; + + it('should register the sync iframe', () => { + expect(spec.getUserSyncs({})).to.be.undefined; + expect(spec.getUserSyncs({iframeEnabled: false})).to.be.undefined; + const options = spec.getUserSyncs({iframeEnabled: true}); + expect(options).to.not.be.undefined; + expect(options).to.have.lengthOf(1); + expect(options[0].type).to.equal('iframe'); + expect(options[0].url).to.equal(syncUrl); }); }); }); diff --git a/test/spec/modules/getintentBidAdapter_spec.js b/test/spec/modules/getintentBidAdapter_spec.js index e66d2138eaf..1b76c4852b4 100644 --- a/test/spec/modules/getintentBidAdapter_spec.js +++ b/test/spec/modules/getintentBidAdapter_spec.js @@ -1,146 +1,131 @@ -import Adapter from '../../../modules/getintentBidAdapter'; -import bidManager from '../../../src/bidmanager'; -import {expect} from 'chai'; - -var assert = require('chai').assert; - -describe('getintent media adapter test', () => { - let adapter; - - window.gi_hb = { - makeBid: function(bidRequest, callback) { - var pid = bidRequest.pid; - var tid = bidRequest.tid; - - if (pid == 'p1' || pid == 'p2') { - callback({ - ad: `Ad Markup ${pid} ${tid}`, - cpm: 2.71, - size: `${bidRequest.size}` - }, bidRequest); - } else if (pid == 'p3') { - callback({ - no_bid: 1 - }, bidRequest); - } else if (pid == 'p4') { - callback({ - vast_url: `http://test.com?pid=${pid}&tid=${tid}`, - cpm: 2.88, - size: `${bidRequest.size}` - }, bidRequest); +import { expect } from 'chai' +import { spec } from 'modules/getintentBidAdapter' + +describe('GetIntent Adapter Tests:', () => { + const bidRequests = [{ + bidId: 'bid12345', + params: { + pid: 'p1000', + tid: 't1000' + }, + sizes: [[300, 250]] + }]; + const videoBidRequest = { + bidId: 'bid789', + params: { + pid: 'p1001', + tid: 't1001', + video: { + mimes: ['video/mp4', 'application/javascript'], + max_dur: 20, + api: [1, 2], + skippable: true } - } + }, + sizes: [300, 250], + mediaType: 'video' }; - function callOut() { - adapter.callBids({ - bidderCode: 'getintent', - bids: [ - { - bidder: 'getintent', - adUnitCode: 'test1', - sizes: [[320, 240]], - params: { - pid: 'p1', - tid: 't1', - cur: 'USD' - } - }, - { - bidder: 'getintent', - adUnitCode: 'test2', - sizes: [[720, 90]], - params: { - pid: 'p2', - tid: 't1', - cur: 'USD' - } - }, - { - bidder: 'getintent', - adUnitCode: 'test3', - sizes: [[400, 500]], - params: { - pid: 'p3', - tid: 't2', - cur: 'USD' - } - }, - { - bidder: 'getintent', - adUnitCode: 'test4', - mediaType: 'video', - sizes: [[480, 352]], - params: { - pid: 'p4', - tid: 't3', - cur: 'USD' - } - } - ] - }); - } - - beforeEach(() => { - adapter = new Adapter(); + it('Verify build request', () => { + const serverRequests = spec.buildRequests(bidRequests); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('//px.adhigh.net/rtb/direct_banner'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid12345'); + expect(serverRequest.data.pid).to.equal('p1000'); + expect(serverRequest.data.tid).to.equal('t1000'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(false); }); - afterEach(() => { + it('Verify build video request', () => { + const serverRequests = spec.buildRequests([videoBidRequest]); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('//px.adhigh.net/rtb/direct_vast'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid789'); + expect(serverRequest.data.pid).to.equal('p1001'); + expect(serverRequest.data.tid).to.equal('t1001'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(true); + expect(serverRequest.data.mimes).to.equal('video/mp4,application/javascript'); + expect(serverRequest.data.max_dur).to.equal(20); + expect(serverRequest.data.api).to.equal('1,2'); + expect(serverRequest.data.skippable).to.equal(true); }); - describe('adding bids to the manager', () => { - let firstBid; - let secondBid; - let thirdBid; - let videoBid; - - beforeEach(() => { - sinon.stub(bidManager, 'addBidResponse'); - callOut(); - firstBid = bidManager.addBidResponse.firstCall.args[1]; - secondBid = bidManager.addBidResponse.secondCall.args[1]; - thirdBid = bidManager.addBidResponse.thirdCall.args[1]; - videoBid = bidManager.addBidResponse.lastCall.args[1]; - }); - - afterEach(() => { - bidManager.addBidResponse.restore(); - }); - - it('was called four times', () => { - assert.strictEqual(bidManager.addBidResponse.callCount, 4); - }); + it('Verify parse response', () => { + const serverResponse = { + body: { + bid_id: 'bid12345', + cpm: 2.25, + currency: 'USD', + size: '300x250', + creative_id: '1000', + ad: 'Ad markup' + }, + headers: { + } + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(2.25); + expect(bid.currency).to.equal('USD'); + expect(bid.creativeId).to.equal('1000'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.mediaType).to.equal('banner'); + expect(bid.ad).to.equal('Ad markup'); + }); - it('will respond to the first bid', () => { - expect(firstBid).to.have.property('ad', 'Ad Markup p1 t1'); - expect(firstBid).to.have.property('cpm', 2.71); - expect(firstBid).to.have.property('width', '320'); - expect(firstBid).to.have.property('height', '240'); - }); + it('Verify parse video response', () => { + const serverResponse = { + body: { + bid_id: 'bid789', + cpm: 3.25, + currency: 'USD', + size: '300x250', + creative_id: '2000', + vast_url: '//vast.xml/url' + }, + headers: { + } + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3.25); + expect(bid.currency).to.equal('USD'); + expect(bid.creativeId).to.equal('2000'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.requestId).to.equal('bid789'); + expect(bid.mediaType).to.equal('video'); + expect(bid.vastUrl).to.equal('//vast.xml/url'); + }); - it('will respond to the second bid', () => { - expect(secondBid).to.have.property('ad', 'Ad Markup p2 t1'); - expect(secondBid).to.have.property('cpm', 2.71); - expect(secondBid).to.have.property('width', '720'); - expect(secondBid).to.have.property('height', '90'); - }); + it('Verify bidder code', () => { + expect(spec.code).to.equal('getintent'); + }); - it('wont respond to the third bid', () => { - expect(thirdBid).to.not.have.property('ad'); - expect(thirdBid).to.not.have.property('cpm'); - }); + it('Verify bidder aliases', () => { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('getintentAdapter'); + }); - it('will add the bidder code to the bid object', () => { - expect(firstBid).to.have.property('bidderCode', 'getintent'); - expect(secondBid).to.have.property('bidderCode', 'getintent'); - expect(thirdBid).to.have.property('bidderCode', 'getintent'); - }); + it('Verify supported media types', () => { + expect(spec.supportedMediaTypes).to.have.lengthOf(2); + expect(spec.supportedMediaTypes[0]).to.equal('video'); + expect(spec.supportedMediaTypes[1]).to.equal('banner'); + }); - it('will respond to the video bid', () => { - expect(videoBid).to.have.property('vastUrl', 'http://test.com?pid=p4&tid=t3'); - expect(videoBid).to.have.property('cpm', 2.88); - expect(videoBid).to.have.property('width', '480'); - expect(videoBid).to.have.property('height', '352'); - }); + it('Verify if bid request valid', () => { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { test: 123 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { pid: 111, tid: 222 } })).to.equal(true); }); }); diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 5b0a9d37d57..3f93a62e850 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -1,599 +1,315 @@ -describe('improvedigital adapter tests', function () { - const expect = require('chai').expect; - const Adapter = require('modules/improvedigitalBidAdapter'); - const bidmanager = require('src/bidmanager'); - const adloader = require('src/adloader'); - const constants = require('src/constants.json'); - var bidfactory = require('src/bidfactory'); - var utils = require('src/utils.js'); - - var improveDigitalAdapter, - sandbox, - bidsRequestedOriginal; +import { expect } from 'chai'; +import { ImproveDigitalAdServerJSClient, spec } from 'modules/improvedigitalBidAdapter'; +import { userSync } from 'src/userSync'; + +describe('Improve Digital Adapter Tests', function () { + let idClient = new ImproveDigitalAdServerJSClient('hb'); + + const METHOD = 'GET'; + const URL = '//ad.360yield.com/hb'; + const PARAM_PREFIX = 'jsonp='; const simpleBidRequest = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - placementId: 1012544 - } - } - ] + bidder: 'improvedigital', + params: { + placementId: 1053688 + }, + adUnitCode: 'div-gpt-ad-1499748733608-0', + transactionId: 'f183e871-fbed-45f0-a427-c8a63c4c01eb', + bidId: '33e9500b21129f', + bidderRequestId: '2772c1e566670b', + auctionId: '192721e36a0239' }; const simpleSmartTagBidRequest = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - publisherId: 1032, - placementKey: 'data_team_test_hb_smoke_test' - } - } - ] + bidder: 'improvedigital', + bidId: '1a2b3c', + placementCode: 'placement1', + params: { + publisherId: 1032, + placementKey: 'data_team_test_hb_smoke_test' + } }; - const keyValueBidRequest = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - placementId: 1012546, - keyValues: { - hbkv: ['01'] - } - } - } - ] - }; + describe('isBidRequestValid', () => { + it('should return false when no bid', () => { + expect(spec.isBidRequestValid()).to.equal(false); + }); - const sizeBidRequest = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - placementId: 1012545, - size: { - w: 800, - h: 600 - } - } - } - ] - }; + it('should return false when no bid.params', () => { + let bid = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - const twoAdSlots = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - placementId: 1012544, - } - }, - { - bidId: '4d5e6f', - placementCode: 'placement2', - params: { - placementId: 1012545, - size: { - w: 800, - h: 600 - } - } - } - ] - }; + it('should return false when both placementId and placementKey + publisherId are missing', () => { + let bid = { 'params': {} }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - const threeAdSlots = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', + it('should return false when only one of placementKey and publisherId is present', () => { + let bid = { params: { - placementId: 1012544, + publisherId: 1234 } - }, - { - bidId: '4d5e6f', - placementCode: 'placement2', + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + bid = { params: { - placementId: 1012545, - size: { - w: 800, - h: 600 - } + placementKey: 'xyz' } - }, - { - bidId: '7g8h9i', - placementCode: 'placement3', - params: { - placementId: 1012546, - keyValues: { - hbkv: ['01'] - } - } - } - ] - }; + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); - const badRequest1 = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - unknownId: 123456 - } - } - ] - }; + it('should return true when placementId is passed', () => { + let bid = { 'params': {} }; + expect(spec.isBidRequestValid(simpleBidRequest)).to.equal(true); + }); - const twoAdSlotsSingleRequest = { - bidderCode: 'improvedigital', - bids: [ - { - bidId: '1a2b3c', - placementCode: 'placement1', - params: { - singleRequest: true, - placementId: 1012544, - } - }, - { - bidId: '4d5e6f', - placementCode: 'placement2', - params: { - placementId: 1012545, - size: { - w: 800, - h: 600 - } + it('should return true when both placementKey and publisherId are passed', () => { + let bid = { 'params': {} }; + expect(spec.isBidRequestValid(simpleSmartTagBidRequest)).to.equal(true); + }); + }); + + describe('buildRequests', () => { + it('should make a well-formed request objects', () => { + const requests = spec.buildRequests([simpleBidRequest]); + expect(requests).to.be.an('array'); + expect(requests.length).to.equal(1); + + const request = requests[0]; + expect(request.method).to.equal(METHOD); + expect(request.url).to.equal(URL); + expect(request.data.substring(0, PARAM_PREFIX.length)).to.equal(PARAM_PREFIX); + + const params = JSON.parse(request.data.substring(PARAM_PREFIX.length)); + expect(params.bid_request).to.be.an('object'); + expect(params.bid_request.id).to.be.a('string'); + expect(params.bid_request.version).to.equal(`${spec.version}-${idClient.CONSTANTS.CLIENT_VERSION}`); + expect(params.bid_request.imp).to.deep.equal([ + { + id: '33e9500b21129f', + pid: 1053688, + tid: 'f183e871-fbed-45f0-a427-c8a63c4c01eb', + banner: {} } - } - ] - }; + ]); + }); - const simpleResponse = { - id: '701903620', - site_id: 191642, - bid: [ - { - price: 1.85185185185185, - lid: 268514, - advid: '5279', - id: '1a2b3c', - sync: [ - 'http://link', - 'http://link2', - 'http://link3' - ], - nurl: 'http://nurl', - h: 300, - pid: 1053687, - crid: '422030', - w: 300, - cid: '99005', - adm: 'document.writeln(\" { + const requests = spec.buildRequests([simpleSmartTagBidRequest]); + const params = JSON.parse(requests[0].data.substring(PARAM_PREFIX.length)); + expect(params.bid_request.imp[0].pubid).to.equal(1032); + expect(params.bid_request.imp[0].pkey).to.equal('data_team_test_hb_smoke_test'); + }); - const zeroPriceResponse = { - id: '701903620', - site_id: 191642, - bid: [ - { - price: 0, - lid: 268514, - advid: '5279', - id: '1a2b3c', - sync: [ - 'http://link', - 'http://link2', - 'http://link3' - ], - nurl: 'http://nurl', - h: 300, - pid: 1053687, - crid: '422030', - w: 300, - cid: '99005', - adm: 'document.writeln(\" { + let bidRequest = Object.assign({}, simpleBidRequest); + const keyValues = { + testKey: [ + 'testValue' + ] + }; + bidRequest.params.keyValues = keyValues; + const request = spec.buildRequests([bidRequest])[0]; + const params = JSON.parse(request.data.substring(PARAM_PREFIX.length)); + expect(params.bid_request.imp[0].kvw).to.deep.equal(keyValues); + }); - const multipleResponse = { - id: '701903620', - site_id: 191642, - bid: [ - { - price: 1.85185185185185, - lid: 268514, - advid: '5279', - id: '1a2b3c', - sync: [ - 'http://link', - 'http://link2', - 'http://link3' - ], - nurl: 'http://nurl', - h: 300, - pid: 1053687, - crid: '422030', - w: 300, - cid: '99005', - adm: 'document.writeln(\" { + let bidRequest = Object.assign({}, simpleBidRequest); + const size = { w: 800, - cid: '99005', - adm: 'document.writeln(\" { + const requests = spec.buildRequests([ + simpleBidRequest, + simpleSmartTagBidRequest + ]); + expect(requests).to.be.an('array'); + expect(requests.length).to.equal(2); + }); + }); + + describe('interpretResponse', () => { + const serverResponse = { + 'body': { + 'id': '687a06c541d8d1', + 'site_id': 191642, + 'bid': [ + { + 'isNet': false, + 'id': '33e9500b21129f', + 'advid': '5279', + 'price': 1.45888594164456, + 'nurl': 'http://ad.360yield.com/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', + 'h': 290, + 'pid': 1053688, + 'sync': [ + 'http://link1', + 'http://link2' + ], + 'crid': '422031', + 'w': 600, + 'cid': '99006', + 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + } ], - nurl: 'http://nurl2', - h: 600, - pid: 1053687, - crid: '422030', - w: 800, - cid: '99005', - adm: 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + } ], - nurl: 'http://nurl2', - h: 600, - pid: 1053687, - crid: '422030', - w: 800, - cid: '99005' + 'debug': '' } - ], - debug: '' - }; + }; - const simpleResponseNoSync = { - id: '701903620', - site_id: 191642, - bid: [ + let expectedBid = [ { - price: 1.85185185185185, - lid: 268514, - advid: '5279', - id: '1a2b3c', - sync: [], - nurl: 'http://nurl', - h: 300, - pid: 1053687, - crid: '422030', - w: 300, - cid: '99005', - adm: 'document.writeln(\"', + 'adId': '33e9500b21129f', + 'creativeId': '422031', + 'cpm': 1.45888594164456, + 'currency': 'USD', + 'height': 290, + 'netRevenue': false, + 'requestId': '33e9500b21129f', + 'ttl': 300, + 'width': 600 } - ] - }; + ]; - var randomNumber = 9876543210; - beforeEach(() => { - improveDigitalAdapter = new Adapter(); - sandbox = sinon.sandbox.create(); - sandbox.stub( - utils, - 'getUniqueIdentifierStr', - function() { - var retValue = randomNumber.toString(); - randomNumber++; - return retValue; + let expectedTwoBids = [ + expectedBid[0], + { + 'ad': '', + 'adId': '1234', + 'creativeId': '422033', + 'cpm': 1.23, + 'currency': 'USD', + 'height': 400, + 'netRevenue': true, + 'requestId': '1234', + 'ttl': 300, + 'width': 700 } - ); - bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested; - $$PREBID_GLOBAL$$._bidsRequested = []; - }); + ]; - afterEach(() => { - sandbox.restore(); - $$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal; - }); - - describe('callBids simpleBidRequest', () => { - beforeEach(() => { - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(simpleBidRequest); + it('should return a well-formed bid', () => { + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.deep.equal(expectedBid); }); - it('should call loadScript with correct parameters', () => { - sinon.assert.calledOnce(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null); - }); - }); - describe('callBids simpleSmartTagBidRequest', () => { - beforeEach(() => { - randomNumber = 9876543210; - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(simpleSmartTagBidRequest); + it('should return two bids', () => { + const bids = spec.interpretResponse(serverResponseTwoBids); + expect(bids).to.deep.equal(expectedTwoBids); }); - it('should call loadScript with correct parameters', () => { - sinon.assert.calledOnce(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pubid%22%3A1032%2C%22pkey%22%3A%22data_team_test_hb_smoke_test%22%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null); - }); - }); - describe('callBids keyValueBidRequest', () => { - beforeEach(() => { - randomNumber = 9876543210; - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(keyValueBidRequest); - }); - it('should call loadScript with correct parameters', () => { - sinon.assert.calledOnce(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012546%2C%22kvw%22%3A%7B%22hbkv%22%3A%5B%2201%22%5D%7D%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null); + it('should register user syncs', () => { + const registerSyncSpy = sinon.spy(userSync, 'registerSync'); + const bids = spec.interpretResponse(serverResponse); + expect(registerSyncSpy.withArgs('image', 'improvedigital', 'http://link1').calledOnce).to.equal(true); + expect(registerSyncSpy.withArgs('image', 'improvedigital', 'http://link2').calledOnce).to.equal(true); }); - }); - describe('callBids sizeBidRequest', () => { - beforeEach(() => { - randomNumber = 9876543210; - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(sizeBidRequest); - }); - it('should call loadScript with correct parameters', () => { - sinon.assert.calledOnce(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null); - }); - }); + it('should set dealId correctly', () => { + let response = JSON.parse(JSON.stringify(serverResponse)); + let bids; - describe('callBids twoAdSlots', () => { - beforeEach(() => { - randomNumber = 9876543210; - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(twoAdSlots); - }); - it('should call loadScript twice with correct parameters', () => { - sinon.assert.calledTwice(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543211%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%224d5e6f%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null); - }); - }); + response.body.bid[0].lid = 'xyz'; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.not.exist; - describe('callBids threeAdSlots', () => { - beforeEach(() => { - randomNumber = 9876543210; - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(threeAdSlots); - }); - it('should call loadScript thrice with correct parameters', () => { - sinon.assert.calledThrice(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543211%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%224d5e6f%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543212%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%227g8h9i%22%2C%22pid%22%3A1012546%2C%22kvw%22%3A%7B%22hbkv%22%3A%5B%2201%22%5D%7D%2C%22banner%22%3A%7B%7D%7D%5D%7D%7D', null); - }); - }); + response.body.bid[0].lid = 268515; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.equal(268515); - describe('callBids bad request 1', () => { - beforeEach(() => { - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(badRequest1); + response.body.bid[0].lid = { + 1: 268515 + }; + bids = spec.interpretResponse(response); + expect(bids[0].dealId).to.equal(268515); }); - it('should not call loadScript', () => { - sinon.assert.notCalled(adloader.loadScript); - }); - }); - describe('callBids twoAdSlotsSingleRequest', () => { - beforeEach(() => { - randomNumber = 9876543210; - sandbox.stub( - adloader, - 'loadScript' - ); - improveDigitalAdapter.callBids(twoAdSlotsSingleRequest); + it('should set currency', () => { + let response = JSON.parse(JSON.stringify(serverResponse)); + response.body.bid[0].currency = 'eur'; + const bids = spec.interpretResponse(response); + expect(bids[0].currency).to.equal('EUR'); }); - it('should call loadScript twice with correct parameters', () => { - sinon.assert.calledOnce(adloader.loadScript); - sinon.assert.calledWith(adloader.loadScript, 'http://ad.360yield.com/hb?jsonp=%7B%22bid_request%22%3A%7B%22id%22%3A%229876543210%22%2C%22callback%22%3A%22$$PREBID_GLOBAL$$.improveDigitalResponse%22%2C%22secure%22%3A0%2C%22version%22%3A%22' + improveDigitalAdapter.LIB_VERSION + '-' + improveDigitalAdapter.idClient.CONSTANTS.CLIENT_VERSION + '%22%2C%22imp%22%3A%5B%7B%22id%22%3A%221a2b3c%22%2C%22pid%22%3A1012544%2C%22banner%22%3A%7B%7D%7D%2C%7B%22id%22%3A%224d5e6f%22%2C%22pid%22%3A1012545%2C%22banner%22%3A%7B%22w%22%3A800%2C%22h%22%3A600%7D%7D%5D%7D%7D', null); - }); - }); - describe('improveDigitalResponse no response', () => { - beforeEach(() => { - sandbox.stub( - bidmanager, - 'addBidResponse' - ); - $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest); - improveDigitalAdapter.callBids(simpleBidRequest); - $$PREBID_GLOBAL$$.improveDigitalResponse([]); - }); - it('should not call bidmanager.addBidResponse', () => { - sinon.assert.notCalled(bidmanager.addBidResponse); - }); - }); - - describe('improveDigitalResponse simpleResponse', () => { - beforeEach(() => { - sandbox.stub( - bidmanager, - 'addBidResponse' - ); - $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest); - improveDigitalAdapter.callBids(simpleBidRequest); - $$PREBID_GLOBAL$$.improveDigitalResponse(simpleResponse); - }); - it('should call bidmanager.addBidResponse once with correct parameters', () => { - sinon.assert.calledOnce(bidmanager.addBidResponse); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 300, height: 300, statusMessage: 'Bid available', ad: '', cpm: 1.85185185185185, adId: '1a2b3c'})); - }); - }); + it('should return empty array for bad response or no price', () => { + let response = JSON.parse(JSON.stringify(serverResponse)); + let bids; - describe('improveDigitalResponse zero bid', () => { - beforeEach(() => { - randomNumber = 1111111111; - sandbox.stub( - bidmanager, - 'addBidResponse' - ); - $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest); - improveDigitalAdapter.callBids(simpleBidRequest); - $$PREBID_GLOBAL$$.improveDigitalResponse(zeroPriceResponse); - }); - it('should call bidmanager.addBidResponse once with correct parameters', () => { - sinon.assert.calledOnce(bidmanager.addBidResponse); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, statusMessage: 'Bid returned empty or error response', adId: '1a2b3c'})); - }); - }); + // Price missing or 0 + response.body.bid[0].price = 0; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); + delete response.body.bid[0].price; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); + response.body.bid[0].price = null; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); - describe('improveDigitalResponse multipleResponseWithOneNoBid', () => { - beforeEach(() => { - randomNumber = 1111111111; - sandbox.stub( - bidmanager, - 'addBidResponse' - ); - $$PREBID_GLOBAL$$._bidsRequested.push(twoAdSlots); - improveDigitalAdapter.callBids(twoAdSlots); - $$PREBID_GLOBAL$$.improveDigitalResponse(multipleResponseWithOneNoBid); - }); - it('should call bidmanager.addBidResponse once with correct parameters', () => { - sinon.assert.calledTwice(bidmanager.addBidResponse); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 300, height: 300, adId: '1a2b3c', statusMessage: 'Bid available', ad: '', cpm: 1.85185185185185})); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement2', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, adId: '4d5e6f', statusMessage: 'Bid returned empty or error response'})); - }); - }); + // errorCode present + response = JSON.parse(JSON.stringify(serverResponse)); + response.body.bid[0].errorCode = undefined; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); - describe('improveDigitalResponse multipleInvalidResponses', () => { - beforeEach(() => { - randomNumber = 1111111111; - sandbox.stub( - bidmanager, - 'addBidResponse' - ); - $$PREBID_GLOBAL$$._bidsRequested.push(twoAdSlots); - improveDigitalAdapter.callBids(twoAdSlots); - $$PREBID_GLOBAL$$.improveDigitalResponse(multipleInvalidResponses); - }); - it('should call bidmanager.addBidResponse twice both with invalid', () => { - sinon.assert.calledTwice(bidmanager.addBidResponse); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, adId: '1a2b3c', statusMessage: 'Bid returned empty or error response'})); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement2', sinon.match({bidderCode: 'improvedigital', width: 0, height: 0, adId: '4d5e6f', statusMessage: 'Bid returned empty or error response'})); + // Adm missing or bad + response = JSON.parse(JSON.stringify(serverResponse)); + delete response.body.bid[0].adm; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); + response.body.bid[0].adm = null; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); + response.body.bid[0].adm = 1234; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); + response.body.bid[0].adm = {}; + bids = spec.interpretResponse(response); + expect(bids).to.deep.equal([]); }); - }); - describe('improveDigitalResponse simpleResponseNoSync', () => { - beforeEach(() => { - sandbox.stub( - bidmanager, - 'addBidResponse' - ); - $$PREBID_GLOBAL$$._bidsRequested.push(simpleBidRequest); - improveDigitalAdapter.callBids(simpleBidRequest); - $$PREBID_GLOBAL$$.improveDigitalResponse(simpleResponseNoSync); - }); - it('should call bidmanager.addBidResponse once with correct parameters', () => { - sinon.assert.calledOnce(bidmanager.addBidResponse); - sinon.assert.calledWith(bidmanager.addBidResponse, 'placement1', sinon.match({bidderCode: 'improvedigital', width: 300, height: 300, statusMessage: 'Bid available', ad: '', cpm: 1.85185185185185, adId: '1a2b3c'})); + it('should set netRevenue', () => { + let response = JSON.parse(JSON.stringify(serverResponse)); + response.body.bid[0].isNet = true; + const bids = spec.interpretResponse(response); + expect(bids[0].netRevenue).to.equal(true); }); }); }); diff --git a/test/spec/modules/jcmBidAdapter_spec.js b/test/spec/modules/jcmBidAdapter_spec.js index b34141869d8..95356a9658e 100644 --- a/test/spec/modules/jcmBidAdapter_spec.js +++ b/test/spec/modules/jcmBidAdapter_spec.js @@ -1,244 +1,139 @@ -describe('jcm adapter tests', function () { - var expect = require('chai').expect; - var urlParse = require('url-parse'); +import { expect } from 'chai'; +import { spec } from 'modules/jcmBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; - // FYI: querystringify will perform encoding/decoding - var querystringify = require('querystringify'); +const ENDPOINT = '//media.adfrontiers.com/'; - var adapter = require('modules/jcmBidAdapter'); - var adLoader = require('src/adloader'); - var bidmanager = require('src/bidmanager'); +describe('jcmAdapter', () => { + const adapter = newBidder(spec); - let stubLoadScript; - - beforeEach(function () { - stubLoadScript = sinon.stub(adLoader, 'loadScript'); + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); }); - afterEach(function () { - stubLoadScript.restore(); - }); + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'jcm', + 'params': { + 'siteId': '3608' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; - describe('creation of bid url', function () { - if (typeof ($$PREBID_GLOBAL$$._bidsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._bidsReceived = []; - } - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = []; - } - if (typeof ($$PREBID_GLOBAL$$._adsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._adsReceived = []; - } - - it('should be called only once', function () { - var params = { - bidderCode: 'jcm', - bidder: 'jcm', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250], [300, 300]], - bidder: 'jcm', - params: { siteId: '3608', adSizes: '300x250' }, - requestId: '10b327aa396609', - placementCode: '/19968336/header-bid-tag-0' - } - - ] - }; - - adapter().callBids(params); - - sinon.assert.calledOnce(stubLoadScript); + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should fix parameter name', function () { - var params = { - bidderCode: 'jcm', - bidder: 'jcm', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'jcm', - params: { siteId: '3608', adSizes: '300x250' }, - requestId: '10b327aa396609', - placementCode: '/19968336/header-bid-tag-0' - } - - ] - }; - - adapter().callBids(params); - var bidUrl = stubLoadScript.getCall(0).args[0]; - - sinon.assert.calledWith(stubLoadScript, bidUrl); - - var parsedBidUrl = urlParse(bidUrl); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); - expect(parsedBidUrl.hostname).to.equal('media.adfrontiers.com'); - expect(parsedBidUrl.pathname).to.equal('/pq'); + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': 'jcm', + 'params': { + 'siteId': '3608' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } - expect(parsedBidUrlQueryString).to.have.property('t').and.to.equal('hb'); - expect(parsedBidUrlQueryString).to.have.property('bids'); + ]; - var bidObjArr = JSON.parse(parsedBidUrlQueryString.bids); - expect(bidObjArr).to.have.property('bids'); - var bidObj = bidObjArr.bids[0]; + const request = spec.buildRequests(bidRequests); - expect(bidObj).to.have.property('adSizes').and.to.equal('300x250'); - expect(bidObj).to.have.property('siteId').and.to.equal('3608'); - expect(bidObj).to.have.property('callbackId').and.to.equal('3c9408cdbf2f68'); + it('sends bid request to ENDPOINT via GET', () => { + expect(request.method).to.equal('GET'); }); - }); - describe('placement by size', function () { - if (typeof ($$PREBID_GLOBAL$$._bidsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._bidsReceived = []; - } - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = []; - } - if (typeof ($$PREBID_GLOBAL$$._adsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._adsReceived = []; - } - - it('should be called with specific parameters for two bids', function () { - var params = { - bidderCode: 'jcm', - bidder: 'jcm', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'jcm', - params: { siteId: '3608', adSizes: '300x250' }, - requestId: '10b327aa396609', - placementCode: '/19968336/header-bid-tag-0' - }, - { - bidId: '3c9408cdbf2f69', - sizes: [[728, 90]], - bidder: 'jcm', - params: { siteId: '3608', adSizes: '728x90' }, - requestId: '10b327aa396610', - placementCode: '/19968336/header-bid-tag-1' - } - - ] - }; - - adapter().callBids(params); - var bidUrl = stubLoadScript.getCall(0).args[0]; - - sinon.assert.calledWith(stubLoadScript, bidUrl); - - var parsedBidUrl = urlParse(bidUrl); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - - expect(parsedBidUrl.hostname).to.equal('media.adfrontiers.com'); - expect(parsedBidUrl.pathname).to.equal('/pq'); - - expect(parsedBidUrlQueryString).to.have.property('t').and.to.equal('hb'); - expect(parsedBidUrlQueryString).to.have.property('bids'); - - var bidObjArr = JSON.parse(parsedBidUrlQueryString.bids); - expect(bidObjArr).to.have.property('bids'); - var bidObj1 = bidObjArr.bids[0]; - - expect(bidObj1).to.have.property('adSizes').and.to.equal('300x250'); - expect(bidObj1).to.have.property('siteId').and.to.equal('3608'); - expect(bidObj1).to.have.property('callbackId').and.to.equal('3c9408cdbf2f68'); - - var bidObj2 = bidObjArr.bids[1]; - - expect(bidObj2).to.have.property('adSizes').and.to.equal('728x90'); - expect(bidObj2).to.have.property('siteId').and.to.equal('3608'); - expect(bidObj2).to.have.property('callbackId').and.to.equal('3c9408cdbf2f69'); + it('sends correct bid parameters', () => { + const payloadArr = request.data.split('&'); + expect(request.method).to.equal('GET'); + expect(payloadArr.length).to.equal(4); + expect(payloadArr[0]).to.equal('t=hb'); + expect(payloadArr[1]).to.equal('ver=1.0'); + expect(payloadArr[2]).to.equal('compact=true'); + const adReqStr = request.data.split('&bids=')[1]; + const adReq = JSON.parse(decodeURIComponent(adReqStr)); + const adReqBid = JSON.parse(decodeURIComponent(adReqStr)).bids[0]; + expect(adReqBid.siteId).to.equal('3608'); + expect(adReqBid.callbackId).to.equal('30b31c1838de1e'); + expect(adReqBid.adSizes).to.equal('300x250,300x600'); }); }); - describe('handling of the callback response', function () { - if (typeof ($$PREBID_GLOBAL$$._bidsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._bidsReceived = []; - } - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = []; - } - if (typeof ($$PREBID_GLOBAL$$._adsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._adsReceived = []; - } - - var params = { - bidderCode: 'jcm', - bidder: 'jcm', - bidderRequestId: '2068db3c904101', - bids: [ - { - bidId: '3c9408cdbf2f68', - sizes: [[300, 250]], - bidder: 'jcm', - params: { siteId: '3608', adSizes: '300x250' }, - requestId: '10b327aa396609', - placementCode: '/19968336/header-bid-tag-0' - }, + describe('interpretResponse', () => { + it('should get correct bid response', () => { + let serverResponse = {'bids': [{'width': 300, 'height': 250, 'creativeId': '29681110', 'ad': '', 'cpm': 0.5, 'callbackId': '30b31c1838de1e'}]}; + + let expectedResponse = [ { - bidId: '3c9408cdbf2f69', - sizes: [[728, 90]], - bidder: 'jcm', - params: { siteId: '3608', adSizes: '728x90' }, - requestId: '10b327aa396610', - placementCode: '/19968336/header-bid-tag-1' + 'requestId': '30b31c1838de1e', + 'bidderCode': 'jcm', + 'cpm': 0.5, + 'creativeId': '29681110', + 'width': 300, + 'height': 250, + 'ttl': 60, + 'currency': 'USA', + 'netRevenue': true, + 'ad': '', } - - ] - }; - - var response = '{"bids":[{"width":300,"cpm":3,"ad":"%3Cimg+src%3D%22http%3A%2F%2Fmedia.adfrontiers.com%2Fimgs%2Fpartnership_300x250.png%22%3E","callbackId":"3c9408cdbf2f68","height":250},{"width":728,"cpm":0,"ad":"%3Cimg+src%3D%22http%3A%2F%2Fmedia.adfrontiers.com%2Fimgs%2Fpartnership_728x90.png%22%3E","callbackId":"3c9408cdbf2f69","height":90}]}'; - - it('callback function should exist', function () { - expect(pbjs.processJCMResponse).to.exist.and.to.be.a('function'); + ]; + + let result = spec.interpretResponse({ body: serverResponse }); + expect(Object.keys(result[0]).length).to.equal(Object.keys(expectedResponse[0]).length); + expect(Object.keys(result[0]).requestId).to.equal(Object.keys(expectedResponse[0]).requestId); + expect(Object.keys(result[0]).bidderCode).to.equal(Object.keys(expectedResponse[0]).bidderCode); + expect(Object.keys(result[0]).cpm).to.equal(Object.keys(expectedResponse[0]).cpm); + expect(Object.keys(result[0]).creativeId).to.equal(Object.keys(expectedResponse[0]).creativeId); + expect(Object.keys(result[0]).width).to.equal(Object.keys(expectedResponse[0]).width); + expect(Object.keys(result[0]).height).to.equal(Object.keys(expectedResponse[0]).height); + expect(Object.keys(result[0]).ttl).to.equal(Object.keys(expectedResponse[0]).ttl); + expect(Object.keys(result[0]).currency).to.equal(Object.keys(expectedResponse[0]).currency); + expect(Object.keys(result[0]).netRevenue).to.equal(Object.keys(expectedResponse[0]).netRevenue); + + expect(Object.keys(result[0]).ad).to.equal(Object.keys(expectedResponse[0]).ad); }); - it('bidmanager.addBidResponse should be called twice with correct arguments', function () { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - adapter().callBids(params); + it('handles nobid responses', () => { + let serverResponse = {'bids': []}; - var adUnits = new Array(); - var unit = new Object(); - unit.bids = [params]; - unit.code = '/19968336/header-bid-tag'; - unit.sizes = [[300, 250], [728, 90]]; - adUnits.push(unit); + let result = spec.interpretResponse({ body: serverResponse }); + expect(result.length).to.equal(0); + }); + }); + describe('getUserSyncs', () => { + it('Verifies sync iframe option', () => { + expect(spec.getUserSyncs({})).to.be.undefined; + expect(spec.getUserSyncs({ iframeEnabled: false})).to.be.undefined; + const options = spec.getUserSyncs({ iframeEnabled: true}); + expect(options).to.not.be.undefined; + expect(options).to.have.lengthOf(1); + expect(options[0].type).to.equal('iframe'); + expect(options[0].url).to.equal('//media.adfrontiers.com/hb/jcm_usersync.html'); + }); - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = [params]; - } else { - $$PREBID_GLOBAL$$._bidsRequested.push(params); - } - $$PREBID_GLOBAL$$.adUnits = adUnits; - pbjs.processJCMResponse(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - var bidPlacementCode2 = stubAddBidResponse.getCall(1).args[0]; - var bidObject2 = stubAddBidResponse.getCall(1).args[1]; - - expect(bidPlacementCode1).to.equal('/19968336/header-bid-tag-0'); - expect(bidObject1.cpm).to.equal(3); - expect(bidObject1.ad).to.equal(''); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.bidderCode).to.equal('jcm'); - - expect(bidPlacementCode2).to.equal('/19968336/header-bid-tag-1'); - expect(bidObject2.getStatusCode()).to.equal(2); - - sinon.assert.calledTwice(stubAddBidResponse); - stubAddBidResponse.restore(); + it('Verifies sync image option', () => { + expect(spec.getUserSyncs({ image: false})).to.be.undefined; + const options = spec.getUserSyncs({ image: true}); + expect(options).to.not.be.undefined; + expect(options).to.have.lengthOf(1); + expect(options[0].type).to.equal('image'); + expect(options[0].url).to.equal('//media.adfrontiers.com/hb/jcm_usersync.png'); }); }); }); diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index f74457d9f9b..87c16dabfae 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -1,25 +1,49 @@ -describe('kargo adapter tests', function () { - const expect = require('chai').expect; - const assert = require('chai').assert; - const adapter = require('modules/kargoBidAdapter'); - const bidmanager = require('src/bidmanager'); - const bidfactory = require('src/bidfactory'); - const adloader = require('src/adloader'); - const CONSTANTS = require('src/constants.json'); +import {expect, assert} from 'chai'; +import {spec} from 'modules/kargoBidAdapter'; +import {registerBidder} from 'src/adapters/bidderFactory'; +import {config} from 'src/config'; - var sandbox, params, krakenParams, adUnits, bidFactorySpy, addBidResponseSpy, bodyAppendSpy, cookies = [], localStorageItems = []; +describe('kargo adapter tests', function () { + var sandbox; beforeEach(() => { sandbox = sinon.sandbox.create(); - addBidResponseSpy = sandbox.stub(bidmanager, 'addBidResponse'); - bodyAppendSpy = sandbox.stub(document.body, 'appendChild'); - simulateBidFactory(); - simulateAdLoader(); - - params = { - timeout: 200, - requestId: 'f4cf851b-665a-43d7-b22c-33c8fdebe577', - bids: [ + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('bid request validity', function() { + it('passes when the bid includes a placement ID', function() { + assert(spec.isBidRequestValid({params: {placementId: 'foo'}}) === true); + }); + + it('fails when the bid does not include a placement ID', function() { + assert(spec.isBidRequestValid({params: {}}) === false); + }); + + it('fails when bid is falsey', function() { + assert(spec.isBidRequestValid() === false); + }); + + it('fails when the bid has no params at all', function() { + assert(spec.isBidRequestValid({}) === false); + }); + }); + + describe('build request', function() { + var bids, cookies = [], localStorageItems = []; + + beforeEach(() => { + sandbox.stub(config, 'getConfig', function(key) { + if (key === 'currency') { + return 'USD'; + } + throw new Error(`Config stub incomplete! Missing key "${key}"`) + }); + + bids = [ { params: { placementId: 'foo' @@ -32,321 +56,301 @@ describe('kargo adapter tests', function () { }, placementCode: 2 } - ] - }; + ]; + }); - adUnits = { - foo: { - receivedTracker: 'fake-tracker-1', - cpm: 3, - adm: '
', - width: 320, - height: 50 - }, - bar: { - cpm: 2.5, - adm: '
', - width: 300, - height: 250 + afterEach(() => { + for (let key in cookies) { + let cookie = cookies[key]; + removeCookie(cookie); } + + for (let key in localStorageItems) { + let localStorageItem = localStorageItems[key]; + localStorage.removeItem(localStorageItem); + } + + cookies.length = 0; + localStorageItems.length = 0; + }); + + function setCookie(cname, cvalue, exdays = 1) { + _setCookie(cname, cvalue, exdays); + cookies.push(cname); } - }); - afterEach(() => { - sandbox.restore(); + function removeCookie(cname) { + _setCookie(cname, '', -1); + } - for (let key in cookies) { - let cookie = cookies[key]; - removeCookie(cookie); + function _setCookie(cname, cvalue, exdays = 1) { + var d = new Date(), + expires; + + d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); + expires = `expires=${d.toUTCString()}`; + document.cookie = `${cname}=${cvalue};${expires};path=/`; } - for (let key in localStorageItems) { - let localStorageItem = localStorageItems[key]; - localStorage.removeItem(localStorageItem); + function setLocalStorageItem(name, val) { + localStorage.setItem(name, val); + localStorageItems.push(name); } - cookies.length = 0; - localStorageItems.length = 0; - }); + function simulateNoLocalStorage() { + return sandbox.stub(localStorage, 'getItem').throws(); + } - function setCookie(cname, cvalue, exdays = 1) { - _setCookie(cname, cvalue, exdays); - cookies.push(cname); - } - - function removeCookie(cname) { - _setCookie(cname, '', -1); - } - - function _setCookie(cname, cvalue, exdays = 1) { - var d = new Date(), - expires; - - d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); - expires = `expires=${d.toUTCString()}`; - document.cookie = `${cname}=${cvalue};${expires};path=/`; - } - - function setLocalStorageItem(name, val) { - localStorage.setItem(name, val); - localStorageItems.push(name); - } - - function simulateAdLoader() { - sandbox.stub(adloader, 'loadScript', (url) => { - window.$$PREBID_GLOBAL$$.kargo_prebid_f4cf851b_665a_43d7_b22c_33c8fdebe577(adUnits); - krakenParams = JSON.parse(decodeURIComponent(url.match(/\?json=(.*)&cb=/)[1])); - }); - } + function initializeKruxUser() { + setLocalStorageItem('kxkar_user', 'rsgr9pnij'); + } - function simulateNoLocalStorage() { - return sandbox.stub(localStorage, 'getItem').throws(); - } + function initializeKruxSegments() { + setLocalStorageItem('kxkar_segs', 'qv9v984dy,rpx2gy365,qrd5u4axv,rnub9nmtd,reha00jnu'); + } - function simulateBidFactory() { - bidFactorySpy = sandbox.stub(bidfactory, 'createBid').withArgs(CONSTANTS.STATUS.GOOD); + function initializeKrgUid() { + setCookie('krg_uid', '%7B%22v%22%3A%7B%22userId%22%3A%225f108831-302d-11e7-bf6b-4595acd3bf6c%22%2C%22clientId%22%3A%222410d8f2-c111-4811-88a5-7b5e190e475f%22%2C%22optOut%22%3Afalse%7D%7D'); + } - bidFactorySpy.onCall(0).returns({ - statusMessage: 'Bid available', - adId: '12dd646671a959' - }); + function getKrgCrb() { + return '%7B%22v%22%3A%22eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwiZXhwaXJlVGltZSI6MTQ5NzQ0OTM4MjY2OCwibGFzdFN5bmNlZEF0IjoxNDk3MzYyOTc5MDEyfQ%3D%3D%22%7D'; + } - bidFactorySpy.onCall(1).returns({ - statusMessage: 'Bid available', - adId: '33f07659bdaf94' - }); - } - - function initializeKruxUser() { - setLocalStorageItem('kxkar_user', 'rsgr9pnij'); - } - - function initializeKruxSegments() { - setLocalStorageItem('kxkar_segs', 'qv9v984dy,rpx2gy365,qrd5u4axv,rnub9nmtd,reha00jnu'); - } - - function initializeKrgUid() { - setCookie('krg_uid', '%7B%22v%22%3A%7B%22userId%22%3A%225f108831-302d-11e7-bf6b-4595acd3bf6c%22%2C%22clientId%22%3A%222410d8f2-c111-4811-88a5-7b5e190e475f%22%2C%22optOut%22%3Afalse%7D%7D'); - } - - function initializeKrgCrb() { - setCookie('krg_crb', '%7B%22v%22%3A%22eyJzeW5jSWRzIjp7IjIiOiI4MmZhMjU1NS01OTY5LTQ2MTQtYjRjZS00ZGNmMTA4MGU5ZjkiLCIxNiI6IlZveElrOEFvSnowQUFFZENleUFBQUFDMiY1MDIiLCIyMyI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjI0IjoiVm94SWs4QW9KejBBQUVkQ2V5QUFBQUMyJjUwMiIsIjI1IjoiNWVlMjQxMzgtNWUwMy00YjlkLWE5NTMtMzhlODMzZjI4NDlmIiwiMl84MCI6ImQyYTg1NWE1LTFiMWMtNDMwMC05NDBlLWE3MDhmYTFmMWJkZSIsIjJfOTMiOiI1ZWUyNDEzOC01ZTAzLTRiOWQtYTk1My0zOGU4MzNmMjg0OWYifSwiZXhwaXJlVGltZSI6MTQ5NzQ0OTM4MjY2OCwibGFzdFN5bmNlZEF0IjoxNDk3MzYyOTc5MDEyfQ%3D%3D%22%7D'); - } - - function initializeInvalidKrgUid() { - setCookie('krg_uid', 'invalid-krg-uid'); - } - - function initializeInvalidKrgCrbType1() { - setCookie('krg_crb', 'invalid-krg-crb'); - } - - function initializeInvalidKrgCrbType2() { - setCookie('krg_crb', '%7B%22v%22%3A%22%26%26%26%26%26%26%22%7D'); - } - - function initializeInvalidKrgCrbType3() { - setCookie('krg_crb', '%7B%22v%22%3A%22Ly8v%22%7D'); - } - - function initializeEmptyKrgUid() { - setCookie('krg_uid', '%7B%7D'); - } - - function initializeEmptyKrgCrb() { - setCookie('krg_crb', '%7B%22v%22%3A%22eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9%22%7D'); - } - - function getExpectedKrakenParams(excludeUserIds, excludeKrux) { - var base = { - timeout: 200, - currency: 'USD', - cpmGranularity: 1, - cpmRange: { - floor: 0, - ceil: 20 - }, - adSlotIds: [ - 'foo', - 'bar' - ], - userIDs: { - kargoID: '5f108831-302d-11e7-bf6b-4595acd3bf6c', - clientID: '2410d8f2-c111-4811-88a5-7b5e190e475f', - crbIDs: { - 2: '82fa2555-5969-4614-b4ce-4dcf1080e9f9', - 16: 'VoxIk8AoJz0AAEdCeyAAAAC2&502', - 23: 'd2a855a5-1b1c-4300-940e-a708fa1f1bde', - 24: 'VoxIk8AoJz0AAEdCeyAAAAC2&502', - 25: '5ee24138-5e03-4b9d-a953-38e833f2849f', - '2_80': 'd2a855a5-1b1c-4300-940e-a708fa1f1bde', - '2_93': '5ee24138-5e03-4b9d-a953-38e833f2849f' - }, - optOut: false - }, - krux: { - userID: 'rsgr9pnij', - segments: [ - 'qv9v984dy', - 'rpx2gy365', - 'qrd5u4axv', - 'rnub9nmtd', - 'reha00jnu' - ] - }, - pageURL: window.location.href - }; - - if (excludeUserIds === true) { - base.userIDs = { - crbIDs: {} - }; - } else if (excludeUserIds) { - if (excludeUserIds.uid) { - delete base.userIDs.kargoID; - delete base.userIDs.clientID; - delete base.userIDs.optOut; - } + function initializeKrgCrb() { + setCookie('krg_crb', getKrgCrb()); + } - if (excludeUserIds.crb) { - base.userIDs.crbIDs = {}; - } + function initializeInvalidKrgUid() { + setCookie('krg_uid', 'invalid-krg-uid'); } - if (excludeKrux) { - base.krux = { - userID: null, - segments: [] - }; + function getInvalidKrgCrbType1() { + return 'invalid-krg-crb'; } - return base; - } - - function getExpectedFirstBid() { - return { - 'bidderCode': 'kargo', - 'width': 320, - 'height': 50, - 'statusMessage': 'Bid available', - 'adId': '12dd646671a959', - 'cpm': 3, - 'ad': '
' - }; - } - - function getExpectedSecondBid() { - return { - 'bidderCode': 'kargo', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '33f07659bdaf94', - 'cpm': 2.5, - 'ad': '
' - }; - } - - function generalAssertions() { - assert(bidFactorySpy.calledTwice); - - assert(addBidResponseSpy.getCall(0).calledWithExactly(1, sinon.match(getExpectedFirstBid()))); - assert(addBidResponseSpy.getCall(1).calledWithExactly(2, sinon.match(getExpectedSecondBid()))); - assert(addBidResponseSpy.calledTwice); - - var trackerEl = bodyAppendSpy.getCall(0).args[0]; - assert(trackerEl instanceof HTMLImageElement); - assert(trackerEl.src === `${window.location.origin}/fake-tracker-1`); - assert(bodyAppendSpy.calledOnce); - } - - it('works when all params and cookies are correctly set', function() { - initializeKruxUser(); - initializeKruxSegments(); - initializeKrgUid(); - initializeKrgCrb(); - - adapter().callBids(params); - - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams()); - }); + function initializeInvalidKrgCrbType1() { + setCookie('krg_crb', getInvalidKrgCrbType1()); + } - it('gracefully handles nothing being set', function() { - adapter().callBids(params); + function getInvalidKrgCrbType2() { + return '%7B%22v%22%3A%22%26%26%26%26%26%26%22%7D'; + } - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams(true, true)); - }); + function initializeInvalidKrgCrbType2() { + setCookie('krg_crb', getInvalidKrgCrbType2()); + } - it('gracefully handles browsers without localStorage', function() { - simulateNoLocalStorage(); - initializeKrgUid(); - initializeKrgCrb(); + function getInvalidKrgCrbType3() { + return '%7B%22v%22%3A%22Ly8v%22%7D'; + } - adapter().callBids(params); + function initializeInvalidKrgCrbType3() { + setCookie('krg_crb', getInvalidKrgCrbType3()); + } - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams(false, true)); - }); + function initializeEmptyKrgUid() { + setCookie('krg_uid', '%7B%7D'); + } - it('handles empty yet valid Kargo CRBs and UIDs', function() { - initializeKruxUser(); - initializeKruxSegments(); - initializeEmptyKrgUid(); - initializeEmptyKrgCrb(); + function getEmptyKrgCrb() { + return '%7B%22v%22%3A%22eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9%22%7D'; + } - adapter().callBids(params); + function initializeEmptyKrgCrb() { + setCookie('krg_crb', getEmptyKrgCrb()); + } - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams(true)); - }); + function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB) { + var base = { + timeout: 200, + currency: 'USD', + cpmGranularity: 1, + cpmRange: { + floor: 0, + ceil: 20 + }, + adSlotIds: [ + 'foo', + 'bar' + ], + userIDs: { + kargoID: '5f108831-302d-11e7-bf6b-4595acd3bf6c', + clientID: '2410d8f2-c111-4811-88a5-7b5e190e475f', + crbIDs: { + 2: '82fa2555-5969-4614-b4ce-4dcf1080e9f9', + 16: 'VoxIk8AoJz0AAEdCeyAAAAC2&502', + 23: 'd2a855a5-1b1c-4300-940e-a708fa1f1bde', + 24: 'VoxIk8AoJz0AAEdCeyAAAAC2&502', + 25: '5ee24138-5e03-4b9d-a953-38e833f2849f', + '2_80': 'd2a855a5-1b1c-4300-940e-a708fa1f1bde', + '2_93': '5ee24138-5e03-4b9d-a953-38e833f2849f' + }, + optOut: false + }, + krux: { + userID: 'rsgr9pnij', + segments: [ + 'qv9v984dy', + 'rpx2gy365', + 'qrd5u4axv', + 'rnub9nmtd', + 'reha00jnu' + ] + }, + pageURL: window.location.href, + rawCRB: expectedRawCRB + }; - it('handles broken Kargo UIDs', function() { - initializeKruxUser(); - initializeKruxSegments(); - initializeInvalidKrgUid(); - initializeKrgCrb(); + if (excludeUserIds === true) { + base.userIDs = { + crbIDs: {} + }; + } else if (excludeUserIds) { + if (excludeUserIds.uid) { + delete base.userIDs.kargoID; + delete base.userIDs.clientID; + delete base.userIDs.optOut; + } - adapter().callBids(params); + if (excludeUserIds.crb) { + base.userIDs.crbIDs = {}; + } + } - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams({uid: true})); - }); + if (excludeKrux) { + base.krux = { + userID: null, + segments: [] + }; + } - it('handles broken Kargo CRBs where top level JSON is invalid', function() { - initializeKruxUser(); - initializeKruxSegments(); - initializeKrgUid(); - initializeInvalidKrgCrbType1(); + return base; + } - adapter().callBids(params); + function testBuildRequests(expected) { + var request = spec.buildRequests(bids, {timeout: 200, foo: 'bar'}); + var krakenParams = JSON.parse(decodeURIComponent(request.data.slice(5))); + expect(request.data.slice(0, 5)).to.equal('json='); + expect(request.url).to.equal('https://krk.kargo.com/api/v1/bid'); + expect(request.method).to.equal('GET'); + expect(request.currency).to.equal('USD'); + expect(request.timeout).to.equal(200); + expect(request.foo).to.equal('bar'); + expect(krakenParams).to.deep.equal(expected); + } - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams({crb: true})); - }); + it('works when all params and cookies are correctly set', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgUid(); + initializeKrgCrb(); + testBuildRequests(getExpectedKrakenParams(undefined, undefined, getKrgCrb())); + }); - it('handles broken Kargo CRBs where inner base 64 is invalid', function() { - initializeKruxUser(); - initializeKruxSegments(); - initializeKrgUid(); - initializeInvalidKrgCrbType2(); + it('gracefully handles nothing being set', function() { + testBuildRequests(getExpectedKrakenParams(true, true, null)); + }); - adapter().callBids(params); + it('gracefully handles browsers without localStorage', function() { + simulateNoLocalStorage(); + initializeKrgUid(); + initializeKrgCrb(); + testBuildRequests(getExpectedKrakenParams(false, true, getKrgCrb())); + }); - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams({crb: true})); - }); + it('handles empty yet valid Kargo CRBs and UIDs', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeEmptyKrgUid(); + initializeEmptyKrgCrb(); + testBuildRequests(getExpectedKrakenParams(true, undefined, getEmptyKrgCrb())); + }); - it('handles broken Kargo CRBs where inner JSON is invalid', function() { - initializeKruxUser(); - initializeKruxSegments(); - initializeKrgUid(); - initializeInvalidKrgCrbType3(); + it('handles broken Kargo UIDs', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeInvalidKrgUid(); + initializeKrgCrb(); + testBuildRequests(getExpectedKrakenParams({uid: true}, undefined, getKrgCrb())); + }); + + it('handles broken Kargo CRBs where top level JSON is invalid', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgUid(); + initializeInvalidKrgCrbType1(); + testBuildRequests(getExpectedKrakenParams({crb: true}, undefined, getInvalidKrgCrbType1())); + }); - adapter().callBids(params); + it('handles broken Kargo CRBs where inner base 64 is invalid', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgUid(); + initializeInvalidKrgCrbType2(); + testBuildRequests(getExpectedKrakenParams({crb: true}, undefined, getInvalidKrgCrbType2())); + }); + + it('handles broken Kargo CRBs where inner JSON is invalid', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeKrgUid(); + initializeInvalidKrgCrbType3(); + testBuildRequests(getExpectedKrakenParams({crb: true}, undefined, getInvalidKrgCrbType3())); + }); + }); - generalAssertions(); - expect(krakenParams).to.deep.equal(getExpectedKrakenParams({crb: true})); + describe('response handler', function() { + it('handles bid responses', function() { + var resp = spec.interpretResponse({body: { + foo: { + cpm: 3, + adm: '
', + width: 320, + height: 50 + }, + bar: { + cpm: 2.5, + adm: '
', + width: 300, + height: 250 + } + }}, { + currency: 'USD', + bids: [{ + bidId: 'fake bid id 1', + params: { + placementId: 'foo' + } + }, { + bidId: 'fake bid id 2', + params: { + placementId: 'bar' + } + }] + }); + var expectation = [{ + requestId: 'fake bid id 1', + cpm: 3, + width: 320, + height: 50, + ad: '
', + ttl: 300, + creativeId: 'foo', + netRevenue: true, + currency: 'USD' + }, { + requestId: 'fake bid id 2', + cpm: 2.5, + width: 300, + height: 250, + ad: '
', + ttl: 300, + creativeId: 'bar', + netRevenue: true, + currency: 'USD' + }]; + expect(resp).to.deep.equal(expectation); + }); }); }); diff --git a/test/spec/modules/nanointeractiveBidAdapter_spec.js b/test/spec/modules/nanointeractiveBidAdapter_spec.js new file mode 100644 index 00000000000..b9b9207aca2 --- /dev/null +++ b/test/spec/modules/nanointeractiveBidAdapter_spec.js @@ -0,0 +1,109 @@ +import { expect } from 'chai'; +import { + ALG, + BIDDER_CODE, CATEGORY, DATA_PARTNER_ID, DATA_PARTNER_PIXEL_ID, ENGINE_BASE_URL, NQ, NQ_NAME, SECURITY, + spec +} from '../../../modules/nanointeractiveBidAdapter'; + +describe('nanointeractive adapter tests', function () { + const SEARCH_QUERY = 'rumpelstiltskin'; + const WIDTH = 300; + const HEIGHT = 250; + const SIZES = [[WIDTH, HEIGHT]]; + const AD = '
'; + const CPM = 1; + + function getBid(isValid) { + return { + bidder: BIDDER_CODE, + params: (function () { + return { + [SECURITY]: isValid === true ? 'sec1' : null, + [DATA_PARTNER_ID]: 'dpid1', + [DATA_PARTNER_PIXEL_ID]: 'pid1', + [ALG]: 'ihr', + [NQ]: SEARCH_QUERY, + [NQ_NAME]: null, + [CATEGORY]: null, + } + })(), + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: 'ee335735-ddd3-41f2-b6c6-e8aa99f81c0f', + sizes: SIZES, + bidId: '24a1c9ec270973', + bidderRequestId: '189135372acd55', + requestId: 'ac15bb68-4ef0-477f-93f4-de91c47f00a9' + } + } + + const SINGlE_BID_REQUEST = { + [SECURITY]: 'sec1', + [DATA_PARTNER_ID]: 'dpid1', + [DATA_PARTNER_PIXEL_ID]: 'pid1', + [ALG]: 'ihr', + [NQ]: [SEARCH_QUERY, null], + sizes: [WIDTH + 'x' + HEIGHT], + bidId: '24a1c9ec270973', + cors: 'http://localhost:9876' + }; + + function getSingleBidResponse(isValid) { + return { + id: '24a1c9ec270973', + cpm: isValid === true ? CPM : null, + width: WIDTH, + height: HEIGHT, + ad: AD, + ttl: 360, + creativeId: 'TEST_ID', + netRevenue: false, + currency: 'EUR', + } + } + + const VALID_BID = { + requestId: '24a1c9ec270973', + cpm: CPM, + width: WIDTH, + height: HEIGHT, + ad: AD, + ttl: 360, + creativeId: 'TEST_ID', + netRevenue: false, + currency: 'EUR', + }; + + describe('NanoAdapter', () => { + let nanoBidAdapter = spec; + + describe('Methods', () => { + it('Test isBidRequestValid() with valid param', function () { + expect(nanoBidAdapter.isBidRequestValid(getBid(true))).to.equal(true); + }); + it('Test isBidRequestValid() with invalid param', function () { + expect(nanoBidAdapter.isBidRequestValid(getBid(false))).to.equal(false); + }); + it('Test buildRequests()', function () { + let request = nanoBidAdapter.buildRequests([getBid(true)]); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(ENGINE_BASE_URL); + expect(request.data).to.equal(JSON.stringify([SINGlE_BID_REQUEST])); + }); + it('Test interpretResponse() length', function () { + let bids = nanoBidAdapter.interpretResponse([getSingleBidResponse(true), getSingleBidResponse(false)]); + expect(bids.length).to.equal(1); + }); + it('Test interpretResponse() bids', function () { + let bid = nanoBidAdapter.interpretResponse([getSingleBidResponse(true), getSingleBidResponse(false)])[0]; + expect(bid.requestId).to.equal(VALID_BID.requestId); + expect(bid.cpm).to.equal(VALID_BID.cpm); + expect(bid.width).to.equal(VALID_BID.width); + expect(bid.height).to.equal(VALID_BID.height); + expect(bid.ad).to.equal(VALID_BID.ad); + expect(bid.ttl).to.equal(VALID_BID.ttl); + expect(bid.creativeId).to.equal(VALID_BID.creativeId); + expect(bid.currency).to.equal(VALID_BID.currency); + }); + }); + }); +}); diff --git a/test/spec/modules/platformioBidAdapter_spec.js b/test/spec/modules/platformioBidAdapter_spec.js index 01a6176c58d..86bf52cac72 100644 --- a/test/spec/modules/platformioBidAdapter_spec.js +++ b/test/spec/modules/platformioBidAdapter_spec.js @@ -1,156 +1,94 @@ -describe('platformio adapter tests', function () { - var expect = require('chai').expect; - var urlParse = require('url-parse'); - var querystringify = require('querystringify'); - var adapter = require('modules/platformioBidAdapter'); - var adLoader = require('src/adloader'); - var bidmanager = require('src/bidmanager'); - - var stubLoadScript; - - beforeEach(function () { - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - }); - - afterEach(function () { - stubLoadScript.restore(); - }); - - describe('creation of bid url', function () { - if (typeof ($$PREBID_GLOBAL$$._bidsReceived) === 'undefined') { - $$PREBID_GLOBAL$$._bidsReceived = []; - } - if (typeof ($$PREBID_GLOBAL$$._bidsRequested) === 'undefined') { - $$PREBID_GLOBAL$$._bidsRequested = []; - } - - it('bid request for single placement', function () { - var params = { - bids: [{ - placementCode: '/19968336/header-bid-tag-0', - sizes: [[300, 250]], - bidId: 'bid1111', - bidder: 'platformio', - params: { pubId: '37054', siteId: '123' } - }] - }; - - adapter().callBids(params); - - var bidUrl = stubLoadScript.getCall(0).args[0]; - - sinon.assert.calledOnce(stubLoadScript); - - var parsedBidUrl = urlParse(bidUrl); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - expect(parsedBidUrlQueryString).to.have.property('pub_id').and.to.equal('37054'); - expect(parsedBidUrlQueryString).to.have.property('site_id').and.to.equal('123'); - expect(parsedBidUrlQueryString).to.have.property('width').and.to.equal('300'); - expect(parsedBidUrlQueryString).to.have.property('height').and.to.equal('250'); - }); - }); - - describe('handling bid response', function () { - it('should return complete bid response', function() { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - - var params = { - bids: [{ - placementCode: '/19968336/header-bid-tag-0', - sizes: [[300, 250]], - bidId: 'bid1111', - bidder: 'platformio', - params: { pubId: '37054', siteId: '123' } - }] - }; - - var response = { - cpm: 1, - width: 300, - height: 250, - callback_uid: 'bid1111', - tag: '\n\nQuantcast\n\n
\n
', - 'width': 300, - 'height': 250 + statusCode: 1, + placementCode: 'imp1', // Changing this to placementCode to be reflective + cpm: 4.5, + currency: 'USD', + ad: + '
Quantcast
', + creativeId: 1001, + width: 300, + height: 250 } ] }; - beforeEach(() => { - bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested; - addBidReponseStub = sandbox.stub(bidManager, 'addBidResponse'); - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - }); + const response = { + body, + headers: {} + }; - afterEach(() => { - sandbox.restore(); - $$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal; - }); + it('should return an empty array if `serverResponse` is `undefined`', () => { + const interpretedResponse = qcSpec.interpretResponse(); - it('should exist and be a function', () => { - expect($$PREBID_GLOBAL$$.handleQuantcastCB).to.exist.and.to.be.a('function'); + expect(interpretedResponse.length).to.equal(0); }); - it('should not add bid when empty text response comes', () => { - $$PREBID_GLOBAL$$.handleQuantcastCB(); - sinon.assert.notCalled(addBidReponseStub); - }); + it('should return an empty array if the parsed response does NOT include `bids`', () => { + const interpretedResponse = qcSpec.interpretResponse({}); - it('should not add bid when empty json response comes', () => { - $$PREBID_GLOBAL$$.handleQuantcastCB(JSON.stringify({})); - sinon.assert.notCalled(addBidReponseStub); + expect(interpretedResponse.length).to.equal(0); }); - it('should not add bid when malformed json response comes', () => { - $$PREBID_GLOBAL$$.handleQuantcastCB('non json text'); - sinon.assert.notCalled(addBidReponseStub); + it('should return an empty array if the parsed response has an empty `bids`', () => { + const interpretedResponse = qcSpec.interpretResponse({ bids: [] }); + + expect(interpretedResponse.length).to.equal(0); }); - it('should add a bid object for each bid', () => { - // You need the following call so that the in-memory storage of the bidRequest is carried out. Without this the callback won't work correctly. - adapter.callBids(bidderRequest); - $$PREBID_GLOBAL$$.handleQuantcastCB(JSON.stringify(bidderReponse)); - sinon.assert.calledOnce(addBidReponseStub); - expect(addBidReponseStub.firstCall.args[0]).to.eql('div-gpt-ad-1438287399331-0'); + it('should get correct bid response', () => { + const expectedResponse = { + requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', + cpm: 4.5, + width: 300, + height: 250, + ad: + '
Quantcast
', + ttl: QUANTCAST_TTL, + creativeId: 1001, + netRevenue: QUANTCAST_NET_REVENUE, + currency: 'USD' + }; + const interpretedResponse = qcSpec.interpretResponse(response); + + expect(interpretedResponse[0]).to.deep.equal(expectedResponse); }); - it('should return no bid even when requestId and sizes are missing', () => { - let bidderReponse = { - 'bidderCode': 'quantcast', - 'bids': [ - { - 'statusCode': 0, - 'placementCode': bidderRequest.bids[0].bidId, - } - ] + it('handles no bid response', () => { + const body = { + bidderCode: 'qcx', // Renaming it to use CamelCase since that is what is used in the Prebid.js variable name + requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', // Added this field. This is not used now but could be useful in troubleshooting later on. Specially for sites using iFrames + bids: [] + }; + const response = { + body, + headers: {} }; + const expectedResponse = []; + const interpretedResponse = qcSpec.interpretResponse(response); - // You need the following call so that the in-memory storage of the bidRequest is carried out. Without this the callback won't work correctly. - adapter.callBids(bidderRequest); - $$PREBID_GLOBAL$$.handleQuantcastCB(JSON.stringify(bidderReponse)); - // sinon.assert.calledOnce(addBidReponseStub); - // expect(addBidReponseStub.firstCall.args[0]).to.eql("div-gpt-ad-1438287399331-0"); + expect(interpretedResponse.length).to.equal(0); }); }); }); diff --git a/test/spec/modules/realvuBidAdapter_spec.js b/test/spec/modules/realvuBidAdapter_spec.js new file mode 100644 index 00000000000..36517fa723e --- /dev/null +++ b/test/spec/modules/realvuBidAdapter_spec.js @@ -0,0 +1,61 @@ +import {expect} from 'chai'; +import RealVuAdapter from '../../../modules/realvuBidAdapter'; +import bidmanager from '../../../src/bidmanager'; +import adloader from '../../../src/adloader'; + +describe('RealVu Adapter Test', () => { + let adapter; + + const REQUEST = { + bidderCode: 'realvu', + requestId: '0d67ddab-1502-4897-a7bf-e8078e983405', + bidderRequestId: '1b5e314fe79b1d', + bids: [ + { + bidId: '2d86a04312d95d', + bidder: 'realvu', + bidderRequestId: '1b5e314fe79b1d', + // mediaType:undefined, + params: { + partnerId: '1Y', + placementId: '9339508', + }, + placementCode: 'ad_container_1', + // renderer:undefined, + sizes: [[300, 250]], + transactionId: '0d67ddab-1502-4897-a7bf-e8078e983405' + } + ], + start: 1504628062271 + }; + + var bidResponseStub; + var adloaderStub; + + beforeEach(function() { + bidResponseStub = sinon.stub(bidmanager, 'addBidResponse'); + adloaderStub = sinon.stub(adloader, 'loadScript'); + }); + + afterEach(function() { + adloaderStub.restore(); + bidResponseStub.restore(); + }); + + adapter = new RealVuAdapter(); + + it('load boost', () => { + adapter.callBids(REQUEST); + expect(adloaderStub.getCall(0).args[0]).to.contain('realvu_boost.js'); + }); + + it('callBid "yes"', () => { + adapter.boostCall({realvu: 'yes', pin: {pbjs_bid: REQUEST.bids[0]}}); + expect(adloaderStub.getCall(0).args[0]).to.contain('id=9339508'); + }); + + it('callBid "no"', () => { + adapter.boostCall({realvu: 'no', pin: {pbjs_bid: REQUEST.bids[0]}}); + expect(bidResponseStub.getCall(0).args[1].getStatusCode()).to.equal(2); + }); +}); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 620fc56e516..552a86b5ac4 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -4,6 +4,7 @@ import { spec, masSizeOrdering, resetUserSync } from 'modules/rubiconBidAdapter' import { parse as parseQuery } from 'querystring'; import { newBidder } from 'src/adapters/bidderFactory'; import { userSync } from 'src/userSync'; +import { config } from 'src/config'; var CONSTANTS = require('src/constants.json'); @@ -78,7 +79,7 @@ describe('the rubicon adapter', () => { position: 'atf', referrer: 'localhost' }, - placementCode: '/19968336/header-bid-tag-0', + adUnitCode: '/19968336/header-bid-tag-0', sizes: [[300, 250], [320, 50]], bidId: '2ffb201a808da7', bidderRequestId: '178e34bad3658f', @@ -312,16 +313,14 @@ describe('the rubicon adapter', () => { window.DigiTrust = { getUser: sandbox.spy() }; - origGetConfig = window.$$PREBID_GLOBAL$$.getConfig; }); afterEach(() => { delete window.DigiTrust; - window.$$PREBID_GLOBAL$$.getConfig = origGetConfig; }); it('should send digiTrustId config params', () => { - sandbox.stub(window.$$PREBID_GLOBAL$$, 'getConfig', (key) => { + sandbox.stub(config, 'getConfig', (key) => { var config = { digiTrustId: { success: true, @@ -354,7 +353,7 @@ describe('the rubicon adapter', () => { }); it('should not send digiTrustId config params due to optout', () => { - sandbox.stub(window.$$PREBID_GLOBAL$$, 'getConfig', (key) => { + sandbox.stub(config, 'getConfig', (key) => { var config = { digiTrustId: { success: true, @@ -383,7 +382,7 @@ describe('the rubicon adapter', () => { }); it('should not send digiTrustId config params due to failure', () => { - sandbox.stub(window.$$PREBID_GLOBAL$$, 'getConfig', (key) => { + sandbox.stub(config, 'getConfig', (key) => { var config = { digiTrustId: { success: false, @@ -412,7 +411,7 @@ describe('the rubicon adapter', () => { }); it('should not send digiTrustId config params if they do not exist', () => { - sandbox.stub(window.$$PREBID_GLOBAL$$, 'getConfig', (key) => { + sandbox.stub(config, 'getConfig', (key) => { var config = {}; return config[key]; }); @@ -473,8 +472,8 @@ describe('the rubicon adapter', () => { expect(slot.zone_id).to.equal('335918'); expect(slot.position).to.equal('atf'); expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].placementCode); - expect(slot.name).to.equal(bidderRequest.bids[0].placementCode); + expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); expect(slot.language).to.equal('en'); expect(slot.width).to.equal(640); expect(slot.height).to.equal(320); @@ -604,17 +603,18 @@ describe('the rubicon adapter', () => { ] }; - let bids = spec.interpretResponse(response, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(2); - expect(bids[0].bidderCode).to.equal('rubicon'); expect(bids[0].width).to.equal(320); expect(bids[0].height).to.equal(50); expect(bids[0].cpm).to.equal(0.911); - expect(bids[0].creative_id).to.equal('crid-9'); + expect(bids[0].ttl).to.equal(300); + expect(bids[0].netRevenue).to.equal(false); + expect(bids[0].creativeId).to.equal('crid-9'); expect(bids[0].currency).to.equal('USD'); expect(bids[0].ad).to.contain(`alert('foo')`) .and.to.contain(``) @@ -622,11 +622,12 @@ describe('the rubicon adapter', () => { expect(bids[0].rubiconTargeting.rpfl_elemid).to.equal('/19968336/header-bid-tag-0'); expect(bids[0].rubiconTargeting.rpfl_14062).to.equal('43_tier_all_test'); - expect(bids[1].bidderCode).to.equal('rubicon'); expect(bids[1].width).to.equal(300); expect(bids[1].height).to.equal(250); expect(bids[1].cpm).to.equal(0.811); - expect(bids[1].creative_id).to.equal('crid-9'); + expect(bids[1].ttl).to.equal(300); + expect(bids[1].netRevenue).to.equal(false); + expect(bids[1].creativeId).to.equal('crid-9'); expect(bids[1].currency).to.equal('USD'); expect(bids[1].ad).to.contain(`alert('foo')`) .and.to.contain(``) @@ -654,7 +655,7 @@ describe('the rubicon adapter', () => { }] }; - let bids = spec.interpretResponse(response, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -677,7 +678,7 @@ describe('the rubicon adapter', () => { 'ads': [] }; - let bids = spec.interpretResponse(response, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -701,7 +702,7 @@ describe('the rubicon adapter', () => { }] }; - let bids = spec.interpretResponse(response, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -711,7 +712,7 @@ describe('the rubicon adapter', () => { it('should handle an error because of malformed json response', () => { let response = '{test{'; - let bids = spec.interpretResponse(response, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -752,15 +753,16 @@ describe('the rubicon adapter', () => { 'account_id': 7780 }; - let bids = spec.interpretResponse(response, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(1); - expect(bids[0].bidderCode).to.equal('rubicon'); - expect(bids[0].creative_id).to.equal('crid-999999'); + expect(bids[0].creativeId).to.equal('crid-999999'); expect(bids[0].cpm).to.equal(1); + expect(bids[0].ttl).to.equal(300); + expect(bids[0].netRevenue).to.equal(false); expect(bids[0].descriptionUrl).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); expect(bids[0].vastUrl).to.equal( 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml' @@ -779,13 +781,17 @@ describe('the rubicon adapter', () => { }); it('should register the Emily iframe', () => { - let syncs = spec.getUserSyncs(); + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); expect(syncs).to.deep.equal({type: 'iframe', url: emilyUrl}); }); it('should not register the Emily iframe more than once', () => { - let syncs = spec.getUserSyncs(); + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); expect(syncs).to.deep.equal({type: 'iframe', url: emilyUrl}); // when called again, should still have only been called once diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 7208ebef343..918e03674a9 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -1,234 +1,289 @@ -describe('trustx adapter tests', function () { - var expect = require('chai').expect; - var assert = require('chai').assert; - var urlParse = require('url-parse'); - var querystringify = require('querystringify'); - - var adapter = require('modules/trustxBidAdapter'); - var bidmanager = require('src/bidmanager'); - var adLoader = require('src/adloader'); - var utils = require('src/utils'); - window.$$PREBID_GLOBAL$$ = window.$$PREBID_GLOBAL$$ || {}; - - if (typeof (pbjs) === 'undefined') { - var pbjs = window.$$PREBID_GLOBAL$$; - } - let stubLoadScript; - beforeEach(function () { - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - }); - afterEach(function () { - stubLoadScript.restore(); - }); - var logErrorSpy; - beforeEach(function () { - logErrorSpy = sinon.spy(utils, 'logError'); - }); - afterEach(function () { - logErrorSpy.restore(); +import { expect } from 'chai'; +import { spec } from 'modules/trustxBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('TrustXAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); }); - describe('creation of request url', function () { - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = []; - } - it('should fix parameter name', function () { - var params = { - bidderCode: 'trustx', - bids: [ - { - bidder: 'trustx', - params: { - uid: 5 - }, - placementCode: 'div-1' - }, - { - bidder: 'trustx', - params: { - uid: 6 - }, - placementCode: 'div-1' - }, - { - bidder: 'trustx', - params: {}, - placementCode: 'div-2' - }, - { - bidder: 'trustx', - params: { - uid: 6, - test: true - }, - placementCode: 'div-3' - }, - { - bidder: 'trustx', - placementCode: 'div-4' - } - ] + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'trustx', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'uid': 0 }; - adapter().callBids(params); - var bidUrl = stubLoadScript.getCall(0).args[0]; - sinon.assert.calledWith(stubLoadScript, bidUrl); - var parsedBidUrl = urlParse(bidUrl); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - var generatedCallback = '$$PREBID_GLOBAL$$.trustx_callback_wrapper_5_6'; - expect(parsedBidUrl.hostname).to.equal('sofia.trustx.org'); - expect(parsedBidUrl.pathname).to.equal('/hb'); - expect(parsedBidUrlQueryString).to.have.property('auids').and.to.equal('5,6'); - expect(parsedBidUrlQueryString).to.have.property('u').and.to.equal(location.href); - expect(parsedBidUrlQueryString).to.have.property('cb').and.to.equal(generatedCallback); + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('validate incoming params', function () { - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = []; - } - it('has no correct item in config', function () { - var params = { - bidderCode: 'trustx', - bids: [ - { - bidder: 'trustx', - params: {}, - placementCode: 'div-1' - }, - { - bidder: 'trustx', - placementCode: 'div-1' - } - ] - }; - adapter().callBids(params); - sinon.assert.notCalled(stubLoadScript); - expect(logErrorSpy.getCall(0).args[0]).to.equal('Uids should be not empty'); + + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '45' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should attach valid params to the tag', () => { + const request = spec.buildRequests([bidRequests[0]]); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '43'); + }); + + it('auids must not be duplicated', () => { + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '43,45'); + }); + + it('pt parameter must be "gross" if params.priceType === "gross"', () => { + bidRequests[1].params.priceType = 'gross'; + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'gross'); + expect(payload).to.have.property('auids', '43,45'); + delete bidRequests[1].params.priceType; + }); + + it('pt parameter must be "net" or "gross"', () => { + bidRequests[1].params.priceType = 'some'; + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '43,45'); + delete bidRequests[1].params.priceType; }); }); - describe('handling of the callback response', function () { - if (typeof (pbjs._bidsReceived) === 'undefined') { - pbjs._bidsReceived = []; - } - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = []; - } - if (typeof (pbjs._adsReceived) === 'undefined') { - pbjs._adsReceived = []; - } - var params = { - bidderCode: 'trustx', - bids: [ + + describe('interpretResponse', () => { + const responses = [ + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
test content 4
', 'h': 250, 'w': 300}], 'seat': '1'}, + undefined, + {'bid': [], 'seat': '1'}, + {'seat': '1'}, + ]; + + it('should get correct bid response', () => { + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should get correct multi bid response', () => { + const bidRequests = [ { - bidder: 'trustx', - params: { - uid: 5 + 'bidder': 'trustx', + 'params': { + 'uid': '43' }, - placementCode: '/19968336/header-bid-tag-0' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71a5b', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', }, { - bidder: 'trustx', - params: { - uid: 6 + 'bidder': 'trustx', + 'params': { + 'uid': '44' }, - placementCode: '/19968336/header-bid-tag-1' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4dff80cc4ee346', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', }, { - bidder: 'trustx', - params: { - uid: 42 + 'bidder': 'trustx', + 'params': { + 'uid': '43' }, - placementCode: '/19968336/header-bid-tag-2' + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '5703af74d0472a', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '300bfeb0d71a5b', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '5703af74d0472a', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, }, { - bidder: 'trustx', - params: { - uid: 43 + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 44, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
test content 2
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('handles wrong and nobid responses', () => { + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '45' }, - placementCode: '/19968336/header-bid-tag-3' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d7190gf', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', }, { - bidder: 'trustx', - params: { - uid: 44 + 'bidder': 'trustx', + 'params': { + 'uid': '46' }, - placementCode: '/19968336/header-bid-tag-4' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71321', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', }, { - bidder: 'trustx', - params: { - uid: 45 + 'bidder': 'trustx', + 'params': { + 'uid': '50' }, - placementCode: '/19968336/header-bid-tag-5' + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '300bfeb0d7183bb', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', } - ] - }; - it('callback function should exist', function () { - adapter().callBids(params); - expect(pbjs['trustx_callback_wrapper_5_6_42_43_44_45']) - .to.exist.and.to.be.a('function'); - }); - it('bidmanager.addBidResponse should be called with correct arguments', function () { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - adapter().callBids(params); - var adUnits = []; - var unit = {}; - unit.bids = params.bids; - unit.code = '/19968336/header-bid-tag'; - adUnits.push(unit); - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = [params]; - } else { - pbjs._bidsRequested.push(params); - } - pbjs.adUnits = adUnits; - var response = { - seatbid: [ - {bid: [{price: 1.15, adm: '
test content 1
', auid: 5, h: 90, w: 728}], seat: '1'}, - {bid: [{price: 0, auid: 6, h: 250, w: 300}], seat: '1'}, - {bid: [{price: 0, adm: '
test content 3
', h: 250, w: 300}], seat: '1'}, - undefined, - {bid: [], seat: '1'}, - {seat: '1'}, - {bid: [{price: 0, adm: '
test content 7
', auid: 46, h: 250, w: 300}], seat: '1'} - ] - }; - pbjs['trustx_callback_wrapper_5_6_42_43_44_45'](response); - var bidPlacementCode1 = stubAddBidResponse.getCall(1).args[0]; - var bidObject1 = stubAddBidResponse.getCall(1).args[1]; - var bidPlacementCode2 = stubAddBidResponse.getCall(0).args[0]; - var bidObject2 = stubAddBidResponse.getCall(0).args[1]; - var bidPlacementCode3 = stubAddBidResponse.getCall(2).args[0]; - var bidObject3 = stubAddBidResponse.getCall(2).args[1]; - var bidPlacementCode4 = stubAddBidResponse.getCall(3).args[0]; - var bidObject4 = stubAddBidResponse.getCall(3).args[1]; - var bidPlacementCode5 = stubAddBidResponse.getCall(4).args[0]; - var bidObject5 = stubAddBidResponse.getCall(4).args[1]; - var bidPlacementCode6 = stubAddBidResponse.getCall(5).args[0]; - var bidObject6 = stubAddBidResponse.getCall(5).args[1]; - expect(logErrorSpy.getCall(5).args[0]).to.equal('Bid from response has no adm parameter - {"price":0,"auid":6,"h":250,"w":300}'); - expect(logErrorSpy.getCall(4).args[0]).to.equal('Bid from response has no auid parameter - {"price":0,"adm":"<' + 'div>test content 3","h":250,"w":300}'); - expect(logErrorSpy.getCall(3).args[0]).to.equal('Seatbid array from response has empty item'); - expect(logErrorSpy.getCall(2).args[0]).to.equal('Array of bid objects is empty'); - expect(logErrorSpy.getCall(1).args[0]).to.equal('Seatbid from response has no array of bid objects - {"seat":"1"}'); - expect(logErrorSpy.getCall(0).args[0]).to.equal('Can\'t find placementCode for bid with auid - 46, placementCode is available only for the following uids - 5,6,42,43,44,45'); - expect(bidPlacementCode1).to.equal('/19968336/header-bid-tag-0'); - expect(bidObject1.cpm).to.equal(1.15); - expect(bidObject1.ad).to.equal('
test content 1
'); - expect(bidObject1.width).to.equal(728); - expect(bidObject1.height).to.equal(90); - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.bidderCode).to.equal('trustx'); - expect(bidPlacementCode2).to.equal('/19968336/header-bid-tag-1'); - expect(bidObject2.getStatusCode()).to.equal(2); - expect(bidPlacementCode3).to.equal('/19968336/header-bid-tag-2'); - expect(bidObject3.getStatusCode()).to.equal(2); - expect(bidPlacementCode4).to.equal('/19968336/header-bid-tag-3'); - expect(bidObject4.getStatusCode()).to.equal(2); - expect(bidPlacementCode5).to.equal('/19968336/header-bid-tag-4'); - expect(bidObject5.getStatusCode()).to.equal(2); - expect(bidPlacementCode6).to.equal('/19968336/header-bid-tag-5'); - expect(bidObject6.getStatusCode()).to.equal(2); - stubAddBidResponse.restore(); + ]; + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + expect(result.length).to.equal(0); }); }); }); diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 370aeb15457..d2a533be17e 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -52,7 +52,7 @@ describe('Yieldmo adapter', () => { }); it('should load a script with passed bid params', () => { - let route = 'http://bid.yieldmo.com/exchange/prebid?'; + let route = 'http://ads.yieldmo.com/exchange/prebid?'; let requestParams = parseURL(bidRequestURL).search; let parsedPlacementParams = JSON.parse(decodeURIComponent(requestParams.p)); diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index 6da22ed8984..8476d53af7b 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -34,6 +34,30 @@ var appnexusAdapterMock = { }; describe('adapterManager tests', () => { + describe('callBids', () => { + beforeEach(() => { + sinon.stub(utils, 'logError'); + }); + + afterEach(() => { + utils.logError.restore(); + }); + + it('should log an error if a bidder is used that does not exist', () => { + const adUnits = [{ + code: 'adUnit-code', + bids: [ + {bidder: 'appnexus', params: {placementId: 'id'}}, + {bidder: 'fakeBidder', params: {placementId: 'id'}} + ] + }]; + + AdapterManager.callBids({adUnits}); + + sinon.assert.called(utils.logError); + }); + }); + describe('S2S tests', () => { beforeEach(() => { AdapterManager.setS2SConfig(CONFIG); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index c3fcacb6d70..adb0baab69a 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -5,6 +5,7 @@ import * as ajax from 'src/ajax'; import { expect } from 'chai'; import { STATUS } from 'src/constants'; import { userSync } from 'src/userSync' +import * as utils from 'src/utils'; const CODE = 'sampleBidder'; const MOCK_BIDS_REQUEST = { @@ -113,7 +114,7 @@ describe('bidders created by newBidder', () => { expect(spec.buildRequests.firstCall.args[0]).to.deep.equal([MOCK_BIDS_REQUEST.bids[0]]); }); - it("should make no server requests if the spec doesn't return any", () => { + it('should make no server requests if the spec doesn\'t return any', () => { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -124,7 +125,7 @@ describe('bidders created by newBidder', () => { expect(ajaxStub.called).to.equal(false); }); - it('should make the appropriate POST request with default settings ', () => { + it('should make the appropriate POST request', () => { const bidder = newBidder(spec); const url = 'test.url.com'; const data = { arg: 2 }; @@ -133,6 +134,7 @@ describe('bidders created by newBidder', () => { method: 'POST', url: url, data: data, + contentType: 'text/plain' }); bidder.callBids(MOCK_BIDS_REQUEST); @@ -143,59 +145,7 @@ describe('bidders created by newBidder', () => { expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'POST', contentType: 'text/plain', - 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' - } - }); - }); - - it('should make the appropriate POST request when options are passed', () => { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - const options = { contentType: 'application/json'}; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: url, - data: data, - options: options - }); - - bidder.callBids(MOCK_BIDS_REQUEST); - - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(url); - expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({ - method: 'POST', - contentType: 'application/json', - withCredentials: true, - customHeaders: {} + withCredentials: true }); }); @@ -221,30 +171,6 @@ describe('bidders created by newBidder', () => { }); }); - it('should make the appropriate GET request when options are passed', () => { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - const opt = { withCredentials: false } - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'GET', - url: url, - data: data, - options: opt - }); - - bidder.callBids(MOCK_BIDS_REQUEST); - - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2&`); - expect(ajaxStub.firstCall.args[2]).to.be.undefined; - expect(ajaxStub.firstCall.args[3]).to.deep.equal({ - method: 'GET', - withCredentials: false - }); - }); - it('should make multiple calls if the spec returns them', () => { const bidder = newBidder(spec); const url = 'test.url.com'; @@ -289,20 +215,25 @@ describe('bidders created by newBidder', () => { describe('when the ajax call succeeds', () => { let ajaxStub; let userSyncStub; + let logErrorSpy; beforeEach(() => { ajaxStub = sinon.stub(ajax, 'ajax', function(url, callbacks) { - callbacks.success('response body'); + const fakeResponse = sinon.stub(); + fakeResponse.returns('headerContent'); + callbacks.success('response body', { getResponseHeader: fakeResponse }); }); userSyncStub = sinon.stub(userSync, 'registerSync') + logErrorSpy = sinon.spy(utils, 'logError'); }); afterEach(() => { ajaxStub.restore(); userSyncStub.restore(); + utils.logError.restore(); }); - it('should call spec.interpretResponse() with the response body content', () => { + it('should call spec.interpretResponse() with the response content', () => { const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); @@ -316,7 +247,9 @@ describe('bidders created by newBidder', () => { bidder.callBids(MOCK_BIDS_REQUEST); expect(spec.interpretResponse.calledOnce).to.equal(true); - expect(spec.interpretResponse.firstCall.args[0]).to.equal('response body'); + const response = spec.interpretResponse.firstCall.args[0] + expect(response.body).to.equal('response body') + expect(response.headers.get('some-header')).to.equal('headerContent'); expect(spec.interpretResponse.firstCall.args[1]).to.deep.equal({ method: 'POST', url: 'test.url.com', @@ -347,16 +280,21 @@ describe('bidders created by newBidder', () => { expect(spec.interpretResponse.calledTwice).to.equal(true); }); - it("should add bids for each placement code into the bidmanager, even if the bidder doesn't bid on all of them", () => { + it('should add bids for each placement code into the bidmanager, even if the bidder doesn\'t bid on all of them', () => { const bidder = newBidder(spec); const bid = { + creativeId: 'creative-id', + bidderCode: 'code', requestId: 'some-id', ad: 'ad-url.com', cpm: 0.5, height: 200, width: 300, - placementCode: 'mock/placement' + placementCode: 'mock/placement', + currency: 'USD', + netRevenue: true, + ttl: 300 }; spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ @@ -375,6 +313,7 @@ describe('bidders created by newBidder', () => { [bidmanager.addBidResponse.firstCall.args[0], bidmanager.addBidResponse.secondCall.args[0]]; expect(placementsWithBids).to.contain('mock/placement'); expect(placementsWithBids).to.contain('mock/placement2'); + expect(logErrorSpy.callCount).to.equal(0); }); it('should call spec.getUserSyncs() with the response', () => { @@ -391,7 +330,10 @@ describe('bidders created by newBidder', () => { bidder.callBids(MOCK_BIDS_REQUEST); expect(spec.getUserSyncs.calledOnce).to.equal(true); - expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal(['response body']); + expect(spec.getUserSyncs.firstCall.args[1].length).to.equal(1); + expect(spec.getUserSyncs.firstCall.args[1][0].body).to.equal('response body'); + expect(spec.getUserSyncs.firstCall.args[1][0].headers).to.have.property('get'); + expect(spec.getUserSyncs.firstCall.args[1][0].headers.get).to.be.a('function'); }); it('should register usersync pixels', () => { @@ -411,6 +353,32 @@ describe('bidders created by newBidder', () => { expect(userSyncStub.firstCall.args[1]).to.equal(spec.code); expect(userSyncStub.firstCall.args[2]).to.equal('usersync.com'); }); + + it('should logError when required bid response params are missing', () => { + const bidder = newBidder(spec); + + const bid = { + requestId: 'some-id', + ad: 'ad-url.com', + cpm: 0.5, + height: 200, + width: 300, + placementCode: 'mock/placement' + }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + spec.getUserSyncs.returns([]); + + spec.interpretResponse.returns(bid); + + bidder.callBids(MOCK_BIDS_REQUEST); + + expect(logErrorSpy.calledOnce).to.equal(true); + }); }); describe('when the ajax call fails', () => { diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 4ffc6a9f15f..ad2645b2351 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -525,89 +525,94 @@ describe('Utils', function () { }); }); - describe('cookie support', function () { - // store original cookie getter and setter so we can reset later - var origCookieSetter = document.__lookupSetter__('cookie'); - var origCookieGetter = document.__lookupGetter__('cookie'); - - // store original cookieEnabled getter and setter so we can reset later - var origCookieEnabledSetter = window.navigator.__lookupSetter__('cookieEnabled'); - var origCookieEnabledGetter = window.navigator.__lookupGetter__('cookieEnabled'); - - // Replace the document cookie set function with the output of a custom function for testing - let setCookie = (v) => v; - - beforeEach(() => { - // Redefine window.navigator.cookieEnabled such that you can set otherwise "read-only" values - Object.defineProperty(window.navigator, 'cookieEnabled', (function (_value) { - return { - get: function _get() { - return _value; - }, - set: function _set(v) { - _value = v; - }, - configurable: true - }; - })(window.navigator.cookieEnabled)); - - // Reset the setCookie cookie function before each test - setCookie = (v) => v; - // Redefine the document.cookie object such that you can purposefully have it output nothing as if it is disabled - Object.defineProperty(window.document, 'cookie', (function (_value) { - return { - get: function _get() { - return _value; - }, - set: function _set(v) { - _value = setCookie(v); - }, - configurable: true - }; - })(window.navigator.cookieEnabled)); - }); - - afterEach(() => { - // redefine window.navigator.cookieEnabled to original getter and setter - Object.defineProperty(window.navigator, 'cookieEnabled', { - get: origCookieEnabledGetter, - set: origCookieEnabledSetter, - configurable: true - }); - // redefine document.cookie to original getter and setter - Object.defineProperty(document, 'cookie', { - get: origCookieGetter, - set: origCookieSetter, - configurable: true - }); - }); - - it('should be detected', function() { - assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should be enabled by default'); - }); - - it('should be not available', function() { - setCookie = () => ''; - window.navigator.cookieEnabled = false; - window.document.cookie = ''; - assert.equal(utils.cookiesAreEnabled(), false, 'Cookies should be disabled'); - }); - - it('should be available', function() { - window.navigator.cookieEnabled = false; - window.document.cookie = 'key=value'; - assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should already be set'); - window.navigator.cookieEnabled = false; - window.document.cookie = ''; - assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should settable'); - setCookie = () => ''; - window.navigator.cookieEnabled = true; - window.document.cookie = ''; - assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should be on via on window.navigator'); - // Reset the setCookie - setCookie = (v) => v; - }); - }); + /** + * tests fail in IE10 because __lookupSetter__ and __lookupGetter__ are + * not supported. See #1656. commenting out until they can be fixed. + * + * describe('cookie support', function () { + * // store original cookie getter and setter so we can reset later + * var origCookieSetter = document.__lookupSetter__('cookie'); + * var origCookieGetter = document.__lookupGetter__('cookie'); + * + * // store original cookieEnabled getter and setter so we can reset later + * var origCookieEnabledSetter = window.navigator.__lookupSetter__('cookieEnabled'); + * var origCookieEnabledGetter = window.navigator.__lookupGetter__('cookieEnabled'); + * + * // Replace the document cookie set function with the output of a custom function for testing + * let setCookie = (v) => v; + * + * beforeEach(() => { + * // Redefine window.navigator.cookieEnabled such that you can set otherwise "read-only" values + * Object.defineProperty(window.navigator, 'cookieEnabled', (function (_value) { + * return { + * get: function _get() { + * return _value; + * }, + * set: function _set(v) { + * _value = v; + * }, + * configurable: true + * }; + * })(window.navigator.cookieEnabled)); + * + * // Reset the setCookie cookie function before each test + * setCookie = (v) => v; + * // Redefine the document.cookie object such that you can purposefully have it output nothing as if it is disabled + * Object.defineProperty(window.document, 'cookie', (function (_value) { + * return { + * get: function _get() { + * return _value; + * }, + * set: function _set(v) { + * _value = setCookie(v); + * }, + * configurable: true + * }; + * })(window.navigator.cookieEnabled)); + * }); + * + * afterEach(() => { + * // redefine window.navigator.cookieEnabled to original getter and setter + * Object.defineProperty(window.navigator, 'cookieEnabled', { + * get: origCookieEnabledGetter, + * set: origCookieEnabledSetter, + * configurable: true + * }); + * // redefine document.cookie to original getter and setter + * Object.defineProperty(document, 'cookie', { + * get: origCookieGetter, + * set: origCookieSetter, + * configurable: true + * }); + * }); + * + * it('should be detected', function() { + * assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should be enabled by default'); + * }); + * + * it('should be not available', function() { + * setCookie = () => ''; + * window.navigator.cookieEnabled = false; + * window.document.cookie = ''; + * assert.equal(utils.cookiesAreEnabled(), false, 'Cookies should be disabled'); + * }); + * + * it('should be available', function() { + * window.navigator.cookieEnabled = false; + * window.document.cookie = 'key=value'; + * assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should already be set'); + * window.navigator.cookieEnabled = false; + * window.document.cookie = ''; + * assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should settable'); + * setCookie = () => ''; + * window.navigator.cookieEnabled = true; + * window.document.cookie = ''; + * assert.equal(utils.cookiesAreEnabled(), true, 'Cookies should be on via on window.navigator'); + * // Reset the setCookie + * setCookie = (v) => v; + * }); + * }); + **/ describe('delayExecution', function () { it('should execute the core function after the correct number of calls', function () { diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js index 57a7f7a127e..512b56c334f 100644 --- a/test/spec/video_spec.js +++ b/test/spec/video_spec.js @@ -1,5 +1,6 @@ import { isValidVideoBid } from 'src/video'; -const utils = require('src/utils'); +import { newConfig } from 'src/config'; +import * as utils from 'src/utils'; describe('video.js', () => { afterEach(() => { @@ -34,6 +35,20 @@ describe('video.js', () => { expect(valid).to.be(false); }); + it('catches invalid bids when prebid-cache is disabled', () => { + sinon.stub(utils, 'getBidRequest', () => ({ + bidder: 'vastOnlyVideoBidder', + mediaTypes: { video: {} }, + })); + + const config = newConfig(); + config.setConfig({ usePrebidCache: false }); + + const valid = isValidVideoBid({ vastXml: 'vast' }); + + expect(valid).to.be(false); + }); + it('validates valid outstream bids', () => { sinon.stub(utils, 'getBidRequest', () => ({ bidder: 'appnexusAst', diff --git a/yarn.lock b/yarn.lock index f6efe31c6a5..30e6b93b544 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4249,6 +4249,10 @@ ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" +ignore-loader@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ignore-loader/-/ignore-loader-0.1.2.tgz#d81f240376d0ba4f0d778972c3ad25874117a463" + ignore@^3.3.3: version "3.3.5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.5.tgz#c4e715455f6073a8d7e5dae72d2fc9d71663dba6"