diff --git a/LICENSE b/LICENSE index c8daf0f8942..f5028c63317 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 2013 APPNEXUS INC + Copyright 2017 PREBID.ORG, INC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -199,4 +188,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/gulpfile.js b/gulpfile.js index f80cfe91d34..7b05b22ad06 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -173,10 +173,11 @@ gulp.task('webpack', ['clean'], function () { // 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) { @@ -186,8 +187,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/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html index 77c875b9787..1493634c1c7 100644 --- a/integrationExamples/gpt/pbjs_example_gpt.html +++ b/integrationExamples/gpt/pbjs_example_gpt.html @@ -268,6 +268,19 @@ params: { placement_id: 0 } + }, + { + bidder: 'pollux', + params: { + zone: '1806' // REQUIRED Zone Id (1806 is a test zone) + } + }, + { + bidder: 'adkernelAdn', + params: { + pubId: 50357, //REQUIRED + host: 'dsp-staging.adkernel.com' //OPTIONAL + } } ] }, { @@ -388,6 +401,12 @@ params: { placement_id: 0 } + }, + { + bidder: 'pollux', + params: { + zone: '276' // REQUIRED Zone Id (276 is a test zone) + } } ] } diff --git a/integrationExamples/gpt/pollux_zone_728x90.html b/integrationExamples/gpt/pollux_example.html similarity index 51% rename from integrationExamples/gpt/pollux_zone_728x90.html rename to integrationExamples/gpt/pollux_example.html index ecede9b5db2..56eedbf2a9c 100644 --- a/integrationExamples/gpt/pollux_zone_728x90.html +++ b/integrationExamples/gpt/pollux_example.html @@ -7,31 +7,40 @@ var PREBID_TIMEOUT = 3000; var adUnits = [{ - code: 'div-gpt-ad-1460505661639-0', - sizes: [[728, 90]], - bids: [ - { - bidder: 'pollux', - params: { - zone: '276' - } - }, - { - bidder: 'pollux', - params: { - zone: '1806' - } - } - ] - }]; - + code: 'div-gpt-ad-1460505661639-0', + sizes: [[728, 90], [300, 250]], + bids: [{ + bidder: 'pollux', + params: { + zone: '1806,276' + } + }, { + bidder: 'pollux', + params: { + zone: '276' + } + } + ] + }, + { + code: 'div-gpt-ad-1460505661631-0', + sizes: [[300, 250]], + bids: [{ + bidder: 'pollux', + params: { + zone: '1806,276,855' + } + } + ] + } + ]; var pbjs = pbjs || {}; pbjs.que = pbjs.que || []; - + @@ -79,7 +97,8 @@ + +
+
+ +
+ +
\ No newline at end of file diff --git a/karma.conf.maker.js b/karma.conf.maker.js index aad6a7d7c1d..d2b1d49e081 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -92,12 +92,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/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js new file mode 100644 index 00000000000..b496a66d081 --- /dev/null +++ b/modules/33acrossBidAdapter.js @@ -0,0 +1,140 @@ +const { registerBidder } = require('../src/adapters/bidderFactory'); +const utils = require('../src/utils'); + +const BIDDER_CODE = '33across'; +const END_POINT = 'https://ssc.33across.com/api/v1/hb'; +const SYNC_ENDPOINT = 'https://de.tynt.com/deb/v2?m=xch'; + +// All this assumes that only one bid is ever returned by ttx +function _createBidResponse(response) { + return { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ad: response.seatbid[0].bid[0].adm, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].ext.rp.advid, + currency: response.cur, + netRevenue: true + } +} + +// infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request +function _createServerRequest(bidRequest) { + const ttxRequest = {}; + const params = bidRequest.params; + + ttxRequest.imp = []; + ttxRequest.imp[0] = { + banner: { + format: bidRequest.sizes.map(_getFormatSize) + }, + ext: { + ttx: { + prod: params.productId + } + } + } + + // Allowing site to be a test configuration object or just the id (former required for testing, + // latter when used by publishers) + ttxRequest.site = params.site || { id: params.siteId }; + + // Go ahead send the bidId in request to 33exchange so it's kept track of in the bid response and + // therefore in ad targetting process + ttxRequest.id = bidRequest.bidId; + + const options = { + contentType: 'application/json', + withCredentials: false + }; + + if (bidRequest.params.customHeaders) { + options.customHeaders = bidRequest.params.customHeaders; + } + + return { + 'method': 'POST', + 'url': bidRequest.params.url || END_POINT, + 'data': JSON.stringify(ttxRequest), + 'options': options + } +} + +// Sync object will always be of type iframe for ttx +function _createSync(bid) { + const syncUrl = bid.params.syncUrl || SYNC_ENDPOINT; + + return { + type: 'iframe', + url: `${syncUrl}&id=${bid.params.siteId || bid.params.site.id}` + } +} + +function _getFormatSize(sizeArr) { + return { + w: sizeArr[0], + h: sizeArr[1], + ext: {} + } +} + +function isBidRequestValid(bid) { + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + + if ((typeof bid.params.site === 'undefined' || typeof bid.params.site.id === 'undefined') && + (typeof bid.params.siteId === 'undefined')) { + return false; + } + + if (typeof bid.params.productId === 'undefined') { + return false; + } + + return true; +} + +// NOTE: At this point, 33exchange only accepts request for a single impression +function buildRequests(bidRequests) { + return bidRequests.map(_createServerRequest); +} + +// NOTE: At this point, the response from 33exchange will only ever contain one bid i.e. the highest bid +function interpretResponse(serverResponse) { + const bidResponses = []; + + // If there are bids, look at the first bid of the first seatbid (see NOTE above for assumption about ttx) + if (serverResponse.body.seatbid.length > 0 && serverResponse.body.seatbid[0].bid.length > 0) { + bidResponses.push(_createBidResponse(serverResponse.body)); + } + + return bidResponses; +} + +// Register one sync per bid since each ad unit may potenitally be linked to a uniqe guid +function getUserSyncs(syncOptions) { + let syncs = []; + const ttxBidRequests = utils.getBidderRequestAllAdUnits(BIDDER_CODE).bids; + + if (syncOptions.iframeEnabled) { + syncs = ttxBidRequests.map(_createSync); + } + + return syncs; +} + +const spec = { + code: BIDDER_CODE, + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs +} + +registerBidder(spec); + +module.exports = spec; diff --git a/modules/33acrossBidAdapter.md b/modules/33acrossBidAdapter.md new file mode 100644 index 00000000000..c4eb319c157 --- /dev/null +++ b/modules/33acrossBidAdapter.md @@ -0,0 +1,95 @@ +# Overview + +``` +Module Name: 33Across Bid Adapter +Module Type: Bidder Adapter +Maintainer: aparna.hegde@33across.com +``` + +# Description + +Connects to 33Across's exchange for bids. + +33Across bid adapter supports only Banner at present and follows MRA + +# Sample Ad Unit: For Publishers +``` +var adUnits = [ +{ + code: '33across-hb-ad-123456-1', + sizes: [ + [300, 250], + [728, 90] + ], + bids: [{ + bidder: '33across', + params: { + siteId: 'pub1234', + productId: 'infeed' + } + }] +} +``` + +# Ad Unit and Setup: For Testing +In order to receive bids please map localhost to (any) test domain. + +``` +<--! Prebid Config section > + + `, ``); + iframeDoc.write(content); iframeDoc.close(); } - function _createRequestContent() { - var content = 'inDapIF=true;'; - content += ''; - content += ''; - content += '' + - 'window.pm_pub_id = "%%PM_PUB_ID%%";' + - 'window.pm_optimize_adslots = [%%PM_OPTIMIZE_ADSLOTS%%];' + - 'window.kaddctr = "%%PM_ADDCTR%%";' + - 'window.kadgender = "%%PM_GENDER%%";' + - 'window.kadage = "%%PM_AGE%%";' + - 'window.pm_async_callback_fn = "window.parent.$$PREBID_GLOBAL$$.handlePubmaticCallback";'; - - content += ''; - - var map = {}; - map.PM_PUB_ID = _pm_pub_id; - map.PM_ADDCTR = _pm_pub_kvs; - map.PM_GENDER = _pm_pub_gender; - map.PM_AGE = _pm_pub_age; - map.PM_OPTIMIZE_ADSLOTS = _pm_optimize_adslots.map(function (adSlot) { - return "'" + adSlot + "'"; - }).join(','); - - content += ''; - content += ''; - content += ''; - content += ''; - content = utils.replaceTokenInString(content, map, '%%'); - - return content; + function _generateLegacyCall(conf, slots) { + var request_url = 'gads.pubmatic.com/AdServer/AdCallAggregator'; + return _protocol + request_url + '?' + utils.parseQueryStringParameters(conf) + 'adslots=' + encodeURIComponent('[' + slots.join(',') + ']'); } - $$PREBID_GLOBAL$$.handlePubmaticCallback = function () { - let bidDetailsMap = {}; - let progKeyValueMap = {}; - try { - bidDetailsMap = iframe.contentWindow.bidDetailsMap; - progKeyValueMap = iframe.contentWindow.progKeyValueMap; - } catch (e) { - utils.logError(e, 'Error parsing Pubmatic response'); + function _initUserSync(pubId) { + // istanbul ignore else + if (!usersync) { + var iframe = utils.createInvisibleIframe(); + iframe.src = _protocol + 'ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p=' + pubId; + utils.insertElement(iframe, document); + usersync = true; + } + } + + function _callBids(params) { + var conf = _initConf(); + var slots = []; + + conf.pubId = 0; + bids = params.bids || []; + + for (var i = 0; i < bids.length; i++) { + var bid = bids[i]; + conf.pubId = conf.pubId || bid.params.publisherId; + conf = _handleCustomParams(bid.params, conf); + bid.params.adSlot = _cleanSlot(bid.params.adSlot); + bid.params.adSlot.length && slots.push(bid.params.adSlot); + } + + // istanbul ignore else + if (conf.pubId && slots.length > 0) { + _legacyExecution(conf, slots); } + _initUserSync(conf.pubId); + } + + $$PREBID_GLOBAL$$.handlePubmaticCallback = function(bidDetailsMap, progKeyValueMap) { var i; var adUnit; var adUnitInfo; var bid; - var bidResponseMap = bidDetailsMap || {}; - var bidInfoMap = progKeyValueMap || {}; - var dimensions; + var bidResponseMap = bidDetailsMap; + var bidInfoMap = progKeyValueMap; + + if (!bidResponseMap || !bidInfoMap) { + return; + } for (i = 0; i < bids.length; i++) { var adResponse; bid = bids[i].params; - adUnit = bidResponseMap[bid.adSlot] || {}; - // adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal= - // if using DFP GPT, the params string comes in the format: // "bidstatus;1;bid;5.0000;bidid;hb_test@468x60;wdeal;" // the code below detects and handles this. + // istanbul ignore else if (bidInfoMap[bid.adSlot] && bidInfoMap[bid.adSlot].indexOf('=') === -1) { bidInfoMap[bid.adSlot] = bidInfoMap[bid.adSlot].replace(/([a-z]+);(.[^;]*)/ig, '$1=$2'); } - adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function (result, pair) { + adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function(result, pair) { var parts = pair.split('='); result[parts[0]] = parts[1]; return result; }, {}); if (adUnitInfo.bidstatus === '1') { - dimensions = adUnitInfo.bidid.split('@')[1].split('x'); adResponse = bidfactory.createBid(1); adResponse.bidderCode = 'pubmatic'; adResponse.adSlot = bid.adSlot; adResponse.cpm = Number(adUnitInfo.bid); adResponse.ad = unescape(adUnit.creative_tag); adResponse.ad += utils.createTrackPixelIframeHtml(decodeURIComponent(adUnit.tracking_url)); - adResponse.width = dimensions[0]; - adResponse.height = dimensions[1]; + adResponse.width = adUnit.width; + adResponse.height = adUnit.height; adResponse.dealId = adUnitInfo.wdeal; + adResponse.dealChannel = dealChannelValues[adUnit.deal_channel] || null; bidmanager.addBidResponse(bids[i].placementCode, adResponse); } else { @@ -147,7 +226,7 @@ function PubmaticAdapter() { return { callBids: _callBids }; -} +}; adaptermanager.registerBidAdapter(new PubmaticAdapter(), 'pubmatic'); 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..dfed3d64370 --- /dev/null +++ b/modules/realvuBidAdapter.js @@ -0,0 +1,238 @@ +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; + 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..e3973b96d52 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:'; @@ -20,6 +18,7 @@ const TIMEOUT_BUFFER = 500; var sizeMap = { 1: '468x60', 2: '728x90', + 5: '120x90', 8: '120x600', 9: '160x600', 10: '300x600', @@ -58,6 +57,7 @@ var sizeMap = { 101: '480x320', 102: '768x1024', 103: '480x280', + 108: '320x240', 113: '1000x300', 117: '320x100', 125: '800x250', @@ -113,7 +113,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 +126,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 +187,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 +230,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 +240,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 +259,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 +281,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 +309,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/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index d53fb0d92db..ef0cf9619c1 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,128 +1,70 @@ -var utils = require('src/utils.js'); -var bidmanager = require('src/bidmanager.js'); -var bidfactory = require('src/bidfactory.js'); -var ajax = require('src/ajax.js').ajax; -var adaptermanager = require('src/adaptermanager'); - -const STR_BIDDER_CODE = 'sharethrough'; -const STR_VERSION = '1.2.0'; - -var SharethroughAdapter = function SharethroughAdapter() { - const str = {}; - str.STR_BTLR_HOST = document.location.protocol + '//btlr.sharethrough.com'; - str.STR_BEACON_HOST = document.location.protocol + '//b.sharethrough.com/butler?'; - str.placementCodeSet = {}; - str.ajax = ajax; - - function _callBids(params) { - const bids = params.bids; - - // cycle through bids - for (let i = 0; i < bids.length; i += 1) { - const bidRequest = bids[i]; - str.placementCodeSet[bidRequest.placementCode] = bidRequest; - const scriptUrl = _buildSharethroughCall(bidRequest); - str.ajax(scriptUrl, _createCallback(bidRequest), undefined, {withCredentials: true}); - } +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'sharethrough'; +const VERSION = '2.0.0'; +const STR_ENDPOINT = document.location.protocol + '//btlr.sharethrough.com/header-bid/v1'; + +export const sharethroughAdapterSpec = { + code: BIDDER_CODE, + isBidRequestValid: bid => !!bid.params.pkey && bid.bidder === BIDDER_CODE, + buildRequests: (bidRequests) => { + return bidRequests.map(bid => { + return { + method: 'GET', + url: STR_ENDPOINT, + data: { + bidId: bid.bidId, + placement_key: bid.params.pkey, + hbVersion: '$prebid.version$', + strVersion: VERSION, + hbSource: 'prebid' + } + }; + }) + }, + interpretResponse: ({ body }, req) => { + if (!Object.keys(body).length) return []; + + const creative = body.creatives[0]; + + return [{ + requestId: req.data.bidId, + width: 0, + height: 0, + cpm: creative.cpm, + creativeId: creative.creative.creative_key, + deal_id: creative.creative.deal_id, + currency: 'USD', + netRevenue: true, + ttl: 360, + ad: generateAd(body, req) + }]; } - - function _createCallback(bidRequest) { - return (bidResponse) => { - _strcallback(bidRequest, bidResponse); - }; - } - - function _buildSharethroughCall(bid) { - const pkey = utils.getBidIdParameter('pkey', bid.params); - - let host = str.STR_BTLR_HOST; - - let url = host + '/header-bid/v1?'; - url = utils.tryAppendQueryString(url, 'bidId', bid.bidId); - url = utils.tryAppendQueryString(url, 'placement_key', pkey); - url = appendEnvFields(url); - - return url; - } - - function _strcallback(bidObj, bidResponse) { - try { - bidResponse = JSON.parse(bidResponse); - } catch (e) { - _handleInvalidBid(bidObj); - return; - } - - if (bidResponse.creatives && bidResponse.creatives.length > 0) { - _handleBid(bidObj, bidResponse); - } else { - _handleInvalidBid(bidObj); - } - } - - function _handleBid(bidObj, bidResponse) { - try { - const bidId = bidResponse.bidId; - const bid = bidfactory.createBid(1, bidObj); - bid.bidderCode = STR_BIDDER_CODE; - bid.cpm = bidResponse.creatives[0].cpm; - const size = bidObj.sizes[0]; - bid.width = size[0]; - bid.height = size[1]; - bid.adserverRequestId = bidResponse.adserverRequestId; - str.placementCodeSet[bidObj.placementCode].adserverRequestId = bidResponse.adserverRequestId; - - bid.pkey = utils.getBidIdParameter('pkey', bidObj.params); - - const windowLocation = `str_response_${bidId}`; - const bidJsonString = JSON.stringify(bidResponse); - bid.ad = `
-
- - ` +} + +function generateAd(body, req) { + const strRespId = `str_response_${req.data.bidId}`; + + return ` +
+
+ + + ` - bid.ad += sfpScriptTag; + const sfp_js = document.createElement('script'); + sfp_js.src = "//native.sharethrough.com/assets/sfp.js"; + sfp_js.type = 'text/javascript'; + sfp_js.charset = 'utf-8'; + try { + window.top.document.getElementsByTagName('body')[0].appendChild(sfp_js); + } catch (e) { + console.log(e); + } } - bidmanager.addBidResponse(bidObj.placementCode, bid); - } catch (e) { - _handleInvalidBid(bidObj); - } - } - - function _handleInvalidBid(bidObj) { - const bid = bidfactory.createBid(2, bidObj); - bid.bidderCode = STR_BIDDER_CODE; - bidmanager.addBidResponse(bidObj.placementCode, bid); - } - - function appendEnvFields(url) { - url = utils.tryAppendQueryString(url, 'hbVersion', '$prebid.version$'); - url = utils.tryAppendQueryString(url, 'strVersion', STR_VERSION); - url = utils.tryAppendQueryString(url, 'hbSource', 'prebid'); - - return url; - } - - return { - callBids: _callBids, - str: str, - }; -}; - -adaptermanager.registerBidAdapter(new SharethroughAdapter(), 'sharethrough'); + })() + `; +} -module.exports = SharethroughAdapter; +registerBidder(sharethroughAdapterSpec); diff --git a/modules/sharethroughBidAdapter.md b/modules/sharethroughBidAdapter.md new file mode 100644 index 00000000000..8ab44f2a0f2 --- /dev/null +++ b/modules/sharethroughBidAdapter.md @@ -0,0 +1,40 @@ +# Overview + +``` +Module Name: Sharethrough Bidder Adapter +Module Type: Bidder Adapter +Maintainer: jchau@sharethrough.com && cpan@sharethrough.com +``` + +# Description + +Module that connects to Sharethrough's demand sources + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[1, 1]], // a display size + bids: [ + { + bidder: "sharethrough", + params: { + pkey: 'LuB3vxGGFrBZJa6tifXW4xgK' + } + } + ] + },{ + code: 'test-div', + sizes: [[1, 1]], // a mobile size + bids: [ + { + bidder: "sharethrough", + params: { + pkey: 'LuB3vxGGFrBZJa6tifXW4xgK' + } + } + ] + } + ]; +``` \ No newline at end of file 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/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 3c9e28e9658..0b2009d8133 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -1,112 +1,117 @@ +import * as utils from 'src/utils'; import { config } from 'src/config'; -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'); - -function UnderdogMediaAdapter() { - const UDM_ADAPTER_VERSION = '1.0.0'; - var getJsStaticUrl = window.location.protocol + '//udmserve.net/udm/img.fetch?tid=1;dt=9;callback=$$PREBID_GLOBAL$$.handleUnderdogMediaCB;'; - var bidParams = {}; - - function _callBids(params) { - bidParams = params; +import { registerBidder } from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'underdogmedia'; +const UDM_ADAPTER_VERSION = '1.0'; + +export const spec = { + code: BIDDER_CODE, + bidParams: [], + + isBidRequestValid: function (bid) { + return !!((bid.params && bid.params.siteId) && (bid.sizes && bid.sizes.length > 0)); + }, + + buildRequests: function (validBidRequests) { var sizes = []; var siteId = 0; - bidParams.bids.forEach(bidParam => { + validBidRequests.forEach(bidParam => { sizes = utils.flatten(sizes, utils.parseSizesInput(bidParam.sizes)); siteId = bidParam.params.siteId; }); - adloader.loadScript(getJsStaticUrl + 'sid=' + siteId + ';sizes=' + sizes.join(','), null, false); - } - function _callback(response) { - var mids = response.mids; - bidParams.bids.forEach(bidParam => { - var filled = false; - mids.forEach(mid => { + return { + method: 'GET', + url: `${window.location.protocol}//udmserve.net/udm/img.fetch`, + data: `tid=1;dt=10;sid=${siteId};sizes=${sizes.join(',')}`, + bidParams: validBidRequests + }; + }, + + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + bidRequest.bidParams.forEach(bidParam => { + serverResponse.body.mids.forEach(mid => { if (mid.useCount > 0) { return; } + if (!mid.useCount) { mid.useCount = 0; } + var size_not_found = true; utils.parseSizesInput(bidParam.sizes).forEach(size => { if (size === mid.width + 'x' + mid.height) { size_not_found = false; } }); + if (size_not_found) { return; } - var bid = bidfactory.createBid(1, bidParam); - bid.bidderCode = bidParam.bidder; - bid.width = mid.width; - bid.height = mid.height; + const bidResponse = { + requestId: bidParam.bidId, + bidderCode: spec.code, + cpm: parseFloat(mid.cpm), + width: mid.width, + height: mid.height, + ad: mid.ad_code_html, + creativeId: mid.mid, + currency: 'USD', + netRevenue: false, + ttl: config.getConfig('_bidderTimeout'), + }; - bid.cpm = parseFloat(mid.cpm); - if (bid.cpm <= 0) { + if (bidResponse.cpm <= 0) { return; } - mid.useCount++; - bid.ad = mid.ad_code_html; - bid.ad = _makeNotification(bid, mid, bidParam) + bid.ad; - if (!(bid.ad || bid.adUrl)) { + if (bidResponse.ad.length <= 0) { return; } - bidmanager.addBidResponse(bidParam.placementCode, bid); - filled = true; + + mid.useCount++; + + bidResponse.ad += makeNotification(bidResponse, mid, bidParam); + + bidResponses.push(bidResponse); }); - if (!filled) { - var nobid = bidfactory.createBid(2, bidParam); - nobid.bidderCode = bidParam.bidder; - bidmanager.addBidResponse(bidParam.placementCode, nobid); - } }); - } - $$PREBID_GLOBAL$$.handleUnderdogMediaCB = _callback; + return bidResponses; + }, +}; - function _makeNotification(bid, mid, bidParam) { - var url = mid.notification_url; +function makeNotification (bid, mid, bidParam) { + var url = mid.notification_url; - url += UDM_ADAPTER_VERSION; - url += ';cb=' + Math.random(); - url += ';qqq=' + (1 / bid.cpm); - url += ';hbt=' + config.getConfig('bidderTimeout'); - url += ';style=adapter'; - url += ';vis=' + encodeURIComponent(document.visibilityState); + url += UDM_ADAPTER_VERSION; + url += ';cb=' + Math.random(); + url += ';qqq=' + (1 / bid.cpm); + url += ';hbt=' + config.getConfig('_bidderTimeout'); + url += ';style=adapter'; + url += ';vis=' + encodeURIComponent(document.visibilityState); - url += ';traffic_info=' + encodeURIComponent(JSON.stringify(_getUrlVars())); - if (bidParam.params.subId) { - url += ';subid=' + encodeURIComponent(bidParam.params.subId); - } - return ''; + url += ';traffic_info=' + encodeURIComponent(JSON.stringify(getUrlVars())); + if (bidParam.params.subId) { + url += ';subid=' + encodeURIComponent(bidParam.params.subId); } + return ''; +} - function _getUrlVars() { - var vars = {}; - var hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split('='); - if (!hash[0].match(/^utm/)) { - continue; - } +function getUrlVars() { + var vars = {}; + var hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (hash[0].match(/^utm_/)) { vars[hash[0]] = hash[1].substr(0, 150); } - return vars; } - - return { - callBids: _callBids - }; + return vars; } -adaptermanager.registerBidAdapter(new UnderdogMediaAdapter(), 'underdogmedia'); - -module.exports = UnderdogMediaAdapter; +registerBidder(spec); diff --git a/modules/underdogmediaBidAdapter.md b/modules/underdogmediaBidAdapter.md new file mode 100644 index 00000000000..f652e2fcbbf --- /dev/null +++ b/modules/underdogmediaBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +**Module Name**: Underdog Media Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: jake@underdogmedia.com + +# Description + +Module that connects to Underdog Media's servers to fetch bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], // a display size + bids: [ + { + bidder: "underdogmedia", + params: { + siteId: '12143' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/vertamediaBidAdapter.js b/modules/vertamediaBidAdapter.js index d87ae56def1..81e2872d371 100644 --- a/modules/vertamediaBidAdapter.js +++ b/modules/vertamediaBidAdapter.js @@ -1,119 +1,121 @@ -import Adapter from 'src/adapter'; -import bidfactory from 'src/bidfactory'; -import bidmanager from 'src/bidmanager'; import * as utils from 'src/utils'; -import { ajax } from 'src/ajax'; -import { STATUS } from 'src/constants'; -import adaptermanager from 'src/adaptermanager'; - -const ENDPOINT = '//rtb.vertamedia.com/hb/'; - -function VertamediaAdapter() { - const baseAdapter = new Adapter('vertamedia'); - let bidRequest; - - baseAdapter.callBids = function (bidRequests) { - if (!bidRequests || !bidRequests.bids || bidRequests.bids.length === 0) { - return; - } - - var RTBDataParams = prepareAndSaveRTBRequestParams(bidRequests.bids[0]); - - if (!RTBDataParams) { - return; - } - - ajax(ENDPOINT, handleResponse, RTBDataParams, { - contentType: 'text/plain', - withCredentials: true, - method: 'GET' +import {registerBidder} from 'src/adapters/bidderFactory'; +import {VIDEO} from 'src/mediaTypes'; + +const URL = '//rtb.vertamedia.com/hb/'; +const BIDDER_CODE = 'vertamedia'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [VIDEO], + isBidRequestValid: function (bid) { + return Boolean(bid && bid.params && bid.params.aid); + }, + + /** + * Make a server request from the list of BidRequests + * @param bidRequests + * @param bidderRequest + */ + buildRequests: function (bidRequests, bidderRequest) { + return bidRequests.map((bid) => { + return { + data: prepareRTBRequestParams(bid), + bidderRequest, + method: 'GET', + url: URL + } }); - }; - - function prepareAndSaveRTBRequestParams(bid) { - if (!bid || !bid.params || !bid.params.aid || !bid.placementCode) { - return; + }, + + /** + * Unpack the response from the server into a list of bids + * @param serverResponse + * @param bidderRequest + * @return {Bid[]} An array of bids which were nested inside the server + */ + interpretResponse: function (serverResponse, {bidderRequest}) { + serverResponse = serverResponse.body; + const isInvalidValidResp = !serverResponse || !serverResponse.bids || !serverResponse.bids.length; + let bids = []; + + if (isInvalidValidResp) { + let extMessage = serverResponse && serverResponse.ext && serverResponse.ext.message ? `: ${serverResponse.ext.message}` : ''; + let errorMessage = `in response for ${bidderRequest.bidderCode} adapter ${extMessage}`; + + utils.logError(errorMessage); + + return bids; } - bidRequest = bid; - - let size = getSize(bid.sizes); - - bidRequest.width = size.width; - bidRequest.height = size.height; - - return { - aid: bid.params.aid, - w: size.width, - h: size.height, - domain: document.location.hostname - }; - } - - function getSize(requestSizes) { - const parsed = {}; - const size = utils.parseSizesInput(requestSizes)[0]; - - if (typeof size !== 'string') { - return parsed; - } - - let parsedSize = size.toUpperCase().split('X'); - - return { - width: parseInt(parsedSize[0], 10) || undefined, - height: parseInt(parsedSize[1], 10) || undefined - }; - } - - /* Notify Prebid of bid responses so bids can get in the auction */ - function handleResponse(response) { - var parsed; - - try { - parsed = JSON.parse(response); - } catch (error) { - utils.logError(error); - } - - if (!parsed || parsed.error || !parsed.bids || !parsed.bids.length) { - bidmanager.addBidResponse(bidRequest.placementCode, createBid(STATUS.NO_BID)); + serverResponse.bids.forEach(serverBid => { + if (serverBid.cpm !== 0) { + const bid = createBid(serverBid); + bids.push(bid); + } + }); - return; - } + return bids; + }, +}; + +/** + * Prepare all parameters for request + * @param bid {object} + * @returns {object} + */ +function prepareRTBRequestParams(bid) { + let size = getSize(bid.sizes); + + return { + domain: utils.getTopWindowLocation().hostname, + callbackId: bid.bidId, + aid: bid.params.aid, + h: size.height, + w: size.width + }; +} - bidmanager.addBidResponse(bidRequest.placementCode, createBid(STATUS.GOOD, parsed.bids[0])); +/** + * Prepare size for request + * @param requestSizes {array} + * @returns {object} bid The bid to validate + */ +function getSize(requestSizes) { + const size = utils.parseSizesInput(requestSizes)[0]; + const parsed = {}; + + if (typeof size !== 'string') { + return parsed; } - function createBid(status, tag) { - var bid = bidfactory.createBid(status, tag); + let parsedSize = size.toUpperCase().split('X'); - bid.code = baseAdapter.getBidderCode(); - bid.bidderCode = bidRequest.bidder; - - if (!tag || status !== STATUS.GOOD) { - return bid; - } - - bid.mediaType = 'video'; - bid.cpm = tag.cpm; - bid.creative_id = tag.cmpId; - bid.width = bidRequest.width; - bid.height = bidRequest.height; - bid.descriptionUrl = tag.url; - bid.vastUrl = tag.url; - - return bid; - } - - return Object.assign(this, { - callBids: baseAdapter.callBids, - setBidderCode: baseAdapter.setBidderCode - }); + return { + height: parseInt(parsedSize[1], 10) || undefined, + width: parseInt(parsedSize[0], 10) || undefined + }; } -adaptermanager.registerBidAdapter(new VertamediaAdapter(), 'vertamedia', { - supportedMediaTypes: ['video'] -}); +/** + * Configure new bid by response + * @param bidResponse {object} + * @returns {object} + */ +function createBid(bidResponse) { + return { + requestId: bidResponse.requestId, + descriptionUrl: bidResponse.url, + creativeId: bidResponse.cmpId, + vastUrl: bidResponse.vastUrl, + height: bidResponse.height, + currency: bidResponse.cur, + width: bidResponse.width, + cpm: bidResponse.cpm, + mediaType: 'video', + netRevenue: true, + ttl: 3600 + }; +} -module.exports = VertamediaAdapter; +registerBidder(spec); diff --git a/modules/vertamediaBidAdapter.md b/modules/vertamediaBidAdapter.md new file mode 100644 index 00000000000..b64e6b24214 --- /dev/null +++ b/modules/vertamediaBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +**Module Name**: VertaMedia Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: support@verta.media + +# Description + +Get access to multiple demand partners across VertaMedia AdExchange and maximize your yield with VertaMedia header bidding adapter. + +VertaMedia header bidding adapter connects with VertaMedia demand sources in order to fetch bids. +This adapter provides a solution for accessing Video demand + + +# Test Parameters +``` + var adUnits = [{ + code: 'div-test-div', + sizes: [[640, 480]], // ad size + bids: [{ + bidder: 'vertamedia', // adapter name + params: { + aid: 332842 + } + }] + }]; +``` 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 062e0943815..723c763ab15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "0.32.0-pre", + "version": "0.33.0-pre", "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 e490e17c60d..0b4b0d7cd0c 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; @@ -183,20 +196,7 @@ export function newBidder(spec) { const responses = []; function afterAllResponses() { fillNoBids(); - 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) - }); - } - } + 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); @@ -263,10 +267,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; @@ -288,14 +298,25 @@ export function newBidder(spec) { 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 @@ -308,6 +329,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)}`); @@ -316,6 +354,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 c12cc4828e6..abd8fea135d 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; @@ -82,7 +83,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(); @@ -250,7 +251,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 5e3168a829d..3d25eff6761 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'); @@ -434,7 +443,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); @@ -456,6 +465,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. @@ -486,6 +496,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); @@ -500,7 +511,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 @@ -523,7 +534,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 @@ -539,6 +550,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); @@ -552,6 +564,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); @@ -562,6 +575,9 @@ $$PREBID_GLOBAL$$.registerAnalyticsAdapter = function (options) { } }; +/** + * @alias module:pbjs.bidsAvailableForAdapter +*/ $$PREBID_GLOBAL$$.bidsAvailableForAdapter = function (bidderCode) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.bidsAvailableForAdapter', arguments); @@ -578,6 +594,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) { @@ -589,7 +606,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. @@ -604,6 +621,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); @@ -617,11 +635,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)) { @@ -632,6 +651,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) { @@ -644,6 +666,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) @@ -661,12 +684,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"`. @@ -713,7 +740,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. */ @@ -751,6 +778,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; @@ -759,6 +787,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) { @@ -777,6 +806,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')) { @@ -805,6 +835,7 @@ $$PREBID_GLOBAL$$.setS2SConfig = function(options) { /** * Get Prebid config options * @param {Object} options + * @alias module:pbjs.getConfig */ $$PREBID_GLOBAL$$.getConfig = config.getConfig; @@ -841,6 +872,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: { @@ -873,10 +905,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') { @@ -905,6 +937,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..6dc30a184d2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -178,6 +178,14 @@ exports.getTopWindowUrl = function () { return href; }; +exports.getTopWindowReferrer = function() { + try { + return window.top.document.referrer; + } catch (e) { + return document.referrer; + } +}; + exports.logWarn = function (msg) { if (debugTurnedOn() && console.warn) { console.warn('WARNING: ' + msg); @@ -339,7 +347,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); } @@ -730,6 +738,19 @@ export function deepAccess(obj, path) { return obj; } +/** + * Returns content for a friendly iframe to execute a URL in script tag + * @param {url} URL to be executed in a script tag in a friendly iframe + * and are macros left to be replaced if required + */ +export function createContentToExecuteExtScriptInFriendlyFrame(url) { + if (!url) { + return ''; + } + + return ``; +} + /** * Build an object consisting of only defined parameters to avoid creating an * object with defined keys and undefined values. 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/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js new file mode 100644 index 00000000000..edb9958ae66 --- /dev/null +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -0,0 +1,443 @@ +const { expect } = require('chai'); +const utils = require('../../../src/utils'); +const { isBidRequestValid, buildRequests, interpretResponse, getUserSyncs } = require('../../../modules/33acrossBidAdapter'); + +describe('33acrossBidAdapter:', function () { + const BIDDER_CODE = '33across'; + const SITE_ID = 'pub1234'; + const PRODUCT_ID = 'product1'; + const END_POINT = 'https://ssc.33across.com/api/v1/hb'; + + beforeEach(function() { + this.bidRequests = [ + { + bidId: 'b1', + bidder: '33across', + bidderRequestId: 'b1a', + params: { + siteId: SITE_ID, + productId: PRODUCT_ID + }, + adUnitCode: 'div-id', + requestId: 'r1', + sizes: [ + [300, 250], + [728, 90] + ], + transactionId: 't1' + } + ] + this.sandbox = sinon.sandbox.create(); + }); + + afterEach(function() { + this.sandbox.restore(); + }); + + describe('isBidRequestValid:', function () { + context('valid bid request:', function () { + it('returns true when bidder, params.siteId, params.productId are set', function() { + const validBid = { + bidder: BIDDER_CODE, + params: { + siteId: SITE_ID, + productId: PRODUCT_ID + } + } + + expect(isBidRequestValid(validBid)).to.be.true; + }) + }); + + context('valid test bid request:', function () { + it('returns true when bidder, params.site.id, params.productId are set', function() { + const validBid = { + bidder: BIDDER_CODE, + params: { + site: { + id: SITE_ID + }, + productId: PRODUCT_ID + } + } + + expect(isBidRequestValid(validBid)).to.be.true; + }); + }); + + context('invalid bid request:', function () { + it('returns false when bidder not set to "33across"', function () { + const invalidBid = { + bidder: 'foo', + params: { + siteId: SITE_ID, + productId: PRODUCT_ID + } + } + + expect(isBidRequestValid(invalidBid)).to.be.false; + }); + + it('returns false when params not set', function() { + const invalidBid = { + bidder: 'foo' + } + + expect(isBidRequestValid(invalidBid)).to.be.false; + }); + + it('returns false when params.siteId or params.site.id not set', function() { + const invalidBid = { + bidder: 'foo', + params: { + productId: PRODUCT_ID + } + } + + expect(isBidRequestValid(invalidBid)).to.be.false; + }); + + it('returns false when params.productId not set', function() { + const invalidBid = { + bidder: 'foo', + params: { + siteId: SITE_ID + } + } + + expect(isBidRequestValid(invalidBid)).to.be.false; + }); + }); + }); + + describe('buildRequests:', function() { + it('returns corresponding server requests for each valid bidRequest', function() { + const ttxRequest = { + imp: [{ + banner: { + format: [ + { + w: 300, + h: 250, + ext: {} + }, + { + w: 728, + h: 90, + ext: {} + } + ] + }, + ext: { + ttx: { + prod: PRODUCT_ID + } + } + }], + site: { + id: SITE_ID + }, + id: 'b1' + }; + const serverRequest = { + method: 'POST', + url: END_POINT, + data: JSON.stringify(ttxRequest), + options: { + contentType: 'application/json', + withCredentials: false + } + } + const builtServerRequests = buildRequests(this.bidRequests); + expect(builtServerRequests).to.deep.equal([serverRequest]); + expect(builtServerRequests.length).to.equal(1); + }); + + it('returns corresponding server requests for each valid test bidRequest', function() { + delete this.bidRequests[0].params.siteId; + this.bidRequests[0].params.site = { + id: SITE_ID, + page: 'http://test-url.com' + } + this.bidRequests[0].params.customHeaders = { + foo: 'bar' + }; + this.bidRequests[0].params.url = '//staging-ssc.33across.com/api/v1/hb'; + + const ttxRequest = { + imp: [{ + banner: { + format: [ + { + w: 300, + h: 250, + ext: {} + }, + { + w: 728, + h: 90, + ext: {} + } + ] + }, + ext: { + ttx: { + prod: PRODUCT_ID + } + } + }], + site: { + id: SITE_ID, + page: 'http://test-url.com' + }, + id: 'b1' + }; + const serverRequest = { + method: 'POST', + url: '//staging-ssc.33across.com/api/v1/hb', + data: JSON.stringify(ttxRequest), + options: { + contentType: 'application/json', + withCredentials: false, + customHeaders: { + foo: 'bar' + } + } + }; + + const builtServerRequests = buildRequests(this.bidRequests); + expect(builtServerRequests).to.deep.equal([serverRequest]); + expect(builtServerRequests.length).to.equal(1); + }); + + afterEach(function() { + delete this.bidRequests; + }) + }); + + describe('interpretResponse', function() { + context('when exactly one bid is returned', function() { + it('interprets and returns the single bid response', function() { + const serverResponse = { + cur: 'USD', + ext: {}, + id: 'b1', + seatbid: [ + { + bid: [{ + id: '1', + adm: '

I am an ad

', + ext: { + rp: { + advid: 1 + } + }, + h: 250, + w: 300, + price: 0.0938 + }] + } + ] + }; + + const bidResponse = { + requestId: 'b1', + bidderCode: BIDDER_CODE, + cpm: 0.0938, + width: 300, + height: 250, + ad: '

I am an ad

', + ttl: 60, + creativeId: 1, + currency: 'USD', + netRevenue: true + } + + expect(interpretResponse({body: serverResponse})).to.deep.equal([bidResponse]); + }); + }); + + context('when no bids are returned', function() { + it('interprets and returns empty array', function() { + const serverResponse = { + cur: 'USD', + ext: {}, + id: 'b1', + seatbid: [] + }; + + expect(interpretResponse({body: serverResponse})).to.deep.equal([]); + }); + }); + + context('when more than one bids are returned', function() { + it('interprets and returns the the first bid of the first seatbid', function() { + const serverResponse = { + cur: 'USD', + ext: {}, + id: 'b1', + seatbid: [ + { + bid: [{ + id: '1', + adm: '

I am an ad

', + ext: { + rp: { + advid: 1 + } + }, + h: 250, + w: 300, + price: 0.0940 + }, + { + id: '2', + adm: '

I am an ad

', + ext: { + rp: { + advid: 2 + } + }, + h: 250, + w: 300, + price: 0.0938 + } + ] + }, + { + bid: [{ + id: '3', + adm: '

I am an ad

', + ext: { + rp: { + advid: 3 + } + }, + h: 250, + w: 300, + price: 0.0938 + }] + } + ] + }; + + const bidResponse = { + requestId: 'b1', + bidderCode: BIDDER_CODE, + cpm: 0.0940, + width: 300, + height: 250, + ad: '

I am an ad

', + ttl: 60, + creativeId: 1, + currency: 'USD', + netRevenue: true + } + + expect(interpretResponse({body: serverResponse})).to.deep.equal([bidResponse]); + }); + }); + }); + + describe('getUserSyncs', function() { + beforeEach(function() { + this.ttxBids = [ + { + params: { + siteId: 'id1', + productId: 'p1' + } + }, + { + params: { + siteId: 'id2', + productId: 'p1' + } + } + ]; + + this.testTTXBids = [ + { + params: { + site: { id: 'id1' }, + productId: 'p1', + syncUrl: 'https://staging-de.tynt.com/deb/v2?m=xch' + } + }, + { + params: { + site: { id: 'id2' }, + productId: 'p1', + syncUrl: 'https://staging-de.tynt.com/deb/v2?m=xch' + } + } + ]; + + this.syncs = [ + { + type: 'iframe', + url: 'https://de.tynt.com/deb/v2?m=xch&id=id1' + }, + { + type: 'iframe', + url: 'https://de.tynt.com/deb/v2?m=xch&id=id2' + }, + ]; + + this.testSyncs = [ + { + type: 'iframe', + url: 'https://staging-de.tynt.com/deb/v2?m=xch&id=id1' + }, + { + type: 'iframe', + url: 'https://staging-de.tynt.com/deb/v2?m=xch&id=id2' + }, + ]; + }); + + context('when iframe is not enabled', function() { + it('returns empty sync array', function() { + this.sandbox.stub(utils, 'getBidderRequestAllAdUnits', () => ( + { + bids: this.ttxBids + } + )); + const syncOptions = {}; + expect(getUserSyncs(syncOptions)).to.deep.equal([]); + }); + }); + + context('when iframe is enabled', function() { + it('returns sync array equal to number of bids for ttx', function() { + this.sandbox.stub(utils, 'getBidderRequestAllAdUnits', () => ( + { + bids: this.ttxBids + } + )); + + const syncOptions = { + iframeEnabled: true + }; + const syncs = getUserSyncs(syncOptions); + expect(syncs.length).to.equal(this.ttxBids.length); + expect(syncs).to.deep.equal(this.syncs); + }); + + it('returns sync array equal to number of test bids for ttx', function() { + this.sandbox.stub(utils, 'getBidderRequestAllAdUnits', () => ( + { + bids: this.testTTXBids + } + )); + + const syncOptions = { + iframeEnabled: true + }; + const syncs = getUserSyncs(syncOptions); + expect(syncs.length).to.equal(this.testTTXBids.length); + expect(syncs).to.deep.equal(this.testSyncs); + }); + }); + }); +}); diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js new file mode 100644 index 00000000000..24249cc8c36 --- /dev/null +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -0,0 +1,128 @@ +import { expect} from 'chai'; +import { spec } from 'modules/a4gBidAdapter'; + +describe('a4gAdapterTests', () => { + describe('bidRequestValidity', () => { + it('bidRequest with zoneId and deliveryUrl params', () => { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + zoneId: 59304, + deliveryUrl: 'http://dev01.ad4game.com/v1/bid' + } + })).to.equal(true); + }); + + it('bidRequest with only zoneId', () => { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + zoneId: 59304 + } + })).to.equal(true); + }); + + it('bidRequest with only deliveryUrl', () => { + expect(spec.isBidRequestValid({ + bidder: 'a4g', + params: { + deliveryUrl: 'http://dev01.ad4game.com/v1/bid' + } + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'a4g', + 'bidId': '51ef8751f9aead', + 'params': { + 'zoneId': 59304, + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }, { + 'bidder': 'a4g', + 'bidId': '51ef8751f9aead', + 'params': { + 'zoneId': 59354, + 'deliveryUrl': '//dev01.ad4game.com/v1/bid' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + it('bidRequest method', () => { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('GET'); + }); + + it('bidRequest url', () => { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.match(new RegExp(`${bidRequests[1].params.deliveryUrl}`)); + }); + + it('bidRequest data', () => { + const request = spec.buildRequests(bidRequests); + expect(request.data).to.exists; + }); + + it('bidRequest zoneIds', () => { + const request = spec.buildRequests(bidRequests); + expect(request.data.zoneId).to.equal('59304;59354'); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = [{ + 'bidder': 'a4g', + 'bidId': '51ef8751f9aead', + 'params': { + 'zoneId': 59304, + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + const bidResponse = { + body: [{ + 'id': 'div-gpt-ad-1460505748561-0', + 'ad': 'test ad', + 'width': 320, + 'height': 250, + 'cpm': 5.2 + }], + headers: {} + }; + + it('required keys', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let requiredKeys = [ + 'requestId', + 'cpm', + 'width', + 'height', + 'ad', + 'ttl', + 'creativeId', + 'netRevenue', + 'currency' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function(key) { + expect(requiredKeys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); +}); 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/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js new file mode 100644 index 00000000000..3ba4012bd0b --- /dev/null +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -0,0 +1,254 @@ +import {expect} from 'chai'; +import {spec} from 'modules/adkernelAdnBidAdapter'; +import * as utils from 'src/utils'; + +describe('AdkernelAdn adapter', () => { + const bid1_pub1 = { + bidder: 'adkernelAdn', + transactionId: 'transact0', + bidderRequestId: 'req0', + bidId: 'bidid_1', + params: { + pubId: 1 + }, + placementCode: 'ad-unit-1', + sizes: [[300, 250], [300, 200]] + }, + bid2_pub1 = { + bidder: 'adkernelAdn', + transactionId: 'transact1', + bidderRequestId: 'req1', + bidId: 'bidid_2', + params: { + pubId: 1 + }, + placementCode: 'ad-unit-2', + sizes: [[300, 250]] + }, + bid1_pub2 = { + bidder: 'adkernelAdn', + transactionId: 'transact2', + bidderRequestId: 'req1', + bidId: 'bidid_3', + params: { + pubId: 7, + host: 'dps-test.com' + }, + placementCode: 'ad-unit-2', + sizes: [[728, 90]] + }, bid_video1 = { + bidder: 'adkernelAdn', + transactionId: 'transact3', + bidderRequestId: 'req1', + bidId: 'bidid_4', + mediaType: 'video', + sizes: [640, 300], + placementCode: 'video_wrapper', + params: { + pubId: 7, + video: { + mimes: ['video/mp4', 'video/webm', 'video/x-flv'], + api: [1, 2, 3, 4], + protocols: [1, 2, 3, 4, 5, 6] + } + } + }, bid_video2 = { + bidder: 'adkernelAdn', + transactionId: 'transact3', + bidderRequestId: 'req1', + bidId: 'bidid_5', + mediaTypes: {video: {context: 'instream'}}, + sizes: [640, 300], + placementCode: 'video_wrapper2', + params: { + pubId: 7, + video: { + mimes: ['video/mp4', 'video/webm', 'video/x-flv'], + api: [1, 2, 3, 4], + protocols: [1, 2, 3, 4, 5, 6] + } + } + }; + + const response = { + tags: [{ + id: 'ad-unit-1', + impid: '2c5e951baeeadd', + crid: '108_159810', + bid: 5.0, + tag: '', + w: 300, + h: 250 + }, { + id: 'ad-unit-2', + impid: '31d798477126c4', + crid: '108_21226', + bid: 2.5, + tag: '', + w: 300, + h: 250 + }, { + id: 'video_wrapper', + impid: '57d602ad1c9545', + crid: '108_158802', + bid: 10.0, + vast_url: 'http://vast.com/vast.xml' + }], + syncpages: ['https://dsp.adkernel.com/sync'] + }, usersyncOnlyResponse = { + syncpages: ['https://dsp.adkernel.com/sync'] + }; + + describe('input parameters validation', () => { + it('empty request shouldn\'t generate exception', () => { + expect(spec.isBidRequestValid({ + bidderCode: 'adkernelAdn' + })).to.be.equal(false); + }); + it('request without pubid should be ignored', () => { + expect(spec.isBidRequestValid({ + bidder: 'adkernelAdn', + params: {}, + placementCode: 'ad-unit-0', + sizes: [[300, 250]] + })).to.be.equal(false); + }); + it('request with invalid pubid should be ignored', () => { + expect(spec.isBidRequestValid({ + bidder: 'adkernelAdn', + params: { + pubId: 'invalid id' + }, + placementCode: 'ad-unit-0', + sizes: [[300, 250]] + })).to.be.equal(false); + }); + }); + + describe('banner request building', () => { + let pbRequest; + let tagRequest; + + before(() => { + let mock = sinon.stub(utils, 'getTopWindowLocation', () => { + return { + protocol: 'https:', + hostname: 'example.com', + host: 'example.com', + pathname: '/index.html', + href: 'https://example.com/index.html' + }; + }); + pbRequest = spec.buildRequests([bid1_pub1])[0]; + tagRequest = JSON.parse(pbRequest.data); + mock.restore(); + }); + + it('should have request id', () => { + expect(tagRequest).to.have.property('id'); + }); + it('should have transaction id', () => { + expect(tagRequest).to.have.property('tid'); + }); + it('should have sizes', () => { + expect(tagRequest.imp[0].banner).to.have.property('format'); + expect(tagRequest.imp[0].banner.format).to.be.eql(['300x250', '300x200']); + }); + it('should have impression id', () => { + expect(tagRequest.imp[0]).to.have.property('id', 'bidid_1'); + }); + it('should have tagid', () => { + expect(tagRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); + }); + it('should create proper site block', () => { + expect(tagRequest.site).to.have.property('page', 'https://example.com/index.html'); + expect(tagRequest.site).to.have.property('secure', 1); + }); + }); + + describe('video request building', () => { + let pbRequest = spec.buildRequests([bid_video1, bid_video2])[0]; + let tagRequest = JSON.parse(pbRequest.data); + + it('should have video object', () => { + expect(tagRequest.imp[0]).to.have.property('video'); + expect(tagRequest.imp[1]).to.have.property('video'); + }); + it('should have tagid', () => { + expect(tagRequest.imp[0]).to.have.property('tagid', 'video_wrapper'); + expect(tagRequest.imp[1]).to.have.property('tagid', 'video_wrapper2'); + }); + }); + + describe('requests routing', () => { + it('should issue a request for each publisher', () => { + let pbRequests = spec.buildRequests([bid1_pub1, bid_video1]); + expect(pbRequests).to.have.length(2); + expect(pbRequests[0].url).to.have.string(`account=${bid1_pub1.params.pubId}`); + expect(pbRequests[1].url).to.have.string(`account=${bid1_pub2.params.pubId}`); + let tagRequest1 = JSON.parse(pbRequests[0].data); + let tagRequest2 = JSON.parse(pbRequests[1].data); + expect(tagRequest1.imp).to.have.length(1); + expect(tagRequest2.imp).to.have.length(1); + }); + it('should issue a request for each host', () => { + let pbRequests = spec.buildRequests([bid1_pub1, bid1_pub2]); + expect(pbRequests).to.have.length(2); + expect(pbRequests[0].url).to.have.string('//tag.adkernel.com/tag'); + expect(pbRequests[1].url).to.have.string(`//${bid1_pub2.params.host}/tag`); + let tagRequest1 = JSON.parse(pbRequests[0].data); + let tagRequest2 = JSON.parse(pbRequests[1].data); + expect(tagRequest1.imp).to.have.length(1); + expect(tagRequest2.imp).to.have.length(1); + }); + }); + + describe('responses processing', () => { + let responses; + before(() => { + responses = spec.interpretResponse({body: response}); + }); + it('should parse all responses', () => { + expect(responses).to.have.length(3); + }); + it('should return fully-initialized bid-response', () => { + let resp = responses[0]; + expect(resp).to.have.property('bidderCode', 'adkernelAdn'); + expect(resp).to.have.property('requestId', '2c5e951baeeadd'); + expect(resp).to.have.property('cpm', 5.0); + expect(resp).to.have.property('width', 300); + expect(resp).to.have.property('height', 250); + expect(resp).to.have.property('creativeId', '108_159810'); + 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', () => { + let resp = responses[2]; + expect(resp).to.have.property('bidderCode', 'adkernelAdn'); + expect(resp).to.have.property('requestId', '57d602ad1c9545'); + expect(resp).to.have.property('cpm', 10.0); + expect(resp).to.have.property('creativeId', '108_158802'); + expect(resp).to.have.property('currency'); + expect(resp).to.have.property('ttl'); + expect(resp).to.have.property('mediaType', 'video'); + expect(resp).to.have.property('vastUrl', 'http://vast.com/vast.xml'); + expect(resp).to.not.have.property('ad'); + }); + it('should perform usersync', () => { + let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: response}]); + expect(syncs).to.have.length(0); + syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: response}]); + expect(syncs).to.have.length(1); + expect(syncs[0]).to.have.property('type', 'iframe'); + expect(syncs[0]).to.have.property('url', 'https://dsp.adkernel.com/sync'); + }); + it('should handle user-sync only response', () => { + let request = spec.buildRequests([bid1_pub1])[0]; + let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); + expect(resp).to.have.length(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..afb58361ba1 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('r20171102PB10'); + 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/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..331eea873dc 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -1,376 +1,283 @@ -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('w', 640); + expect(payload.imp[3].video).to.have.property('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'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + + // 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'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + + 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'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + }); - 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/komoonaBidAdapter_spec.js b/test/spec/modules/komoonaBidAdapter_spec.js index 2657c658ba2..82edba28d03 100644 --- a/test/spec/modules/komoonaBidAdapter_spec.js +++ b/test/spec/modules/komoonaBidAdapter_spec.js @@ -1,152 +1,164 @@ import { expect } from 'chai'; -import Adapter from 'modules/komoonaBidAdapter'; -import bidmanager from 'src/bidmanager'; +import { spec } from 'modules/komoonaBidAdapter'; -const ENDPOINT = '//bidder.komoona.com/v1/GetSBids'; - -const REQUEST = { - 'bidderCode': 'komoona', - 'requestId': '1f43cc36a6a7e', - 'bidderRequestId': '25392d757fad47', - 'bids': [ +describe('Komoona.com Adapter Tests', () => { + const bidsRequest = [ { - 'bidder': 'komoona', - 'params': { - 'hbid': 'abcd666dcba', - 'placementId': 'abcd123123dcba' + bidder: 'komoona', + params: { + placementId: '170577', + hbid: 'abc12345678', }, - 'placementCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [ + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', + sizes: [ [300, 250] ], - 'bidId': '30e5e911c00703', - 'bidderRequestId': '25392d757fad47', - 'requestId': '1f43cc36a6a7e' - } - ], - 'start': 1466493146527 -}; - -const RESPONSE = { - 'bids': [ + bidId: '2faedf1095f815', + bidderRequestId: '18065867f8ae39', + requestId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' + }, { - 'placementid': 'abcd123123dcba', - 'uuid': '30e5e911c00703', - 'width': 728, - 'height': 90, - 'cpm': 0.5, - 'creative': '' + bidder: 'komoona', + params: { + placementId: '281277', + hbid: 'abc12345678', + floorPrice: 0.5 + }, + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', + sizes: [ + [728, 90] + ], + bidId: '3c34e2367a3f59', + bidderRequestId: '18065867f8ae39', + requestId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' + }]; + + const bidsResponse = { + body: { + bids: [ + { + placementid: '170577', + uuid: '2faedf1095f815', + width: 300, + height: 250, + cpm: 0.51, + creative: '', + ttl: 360, + currency: 'USD', + netRevenue: true, + creativeId: 'd30b58c2ba' + } + ] } - ] -}; - -describe('komoonaAdapter', () => { - let adapter; - - beforeEach(() => adapter = new Adapter()); - - describe('request function', () => { - let xhr; - let requests; - let pbConfig; - - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); - pbConfig = REQUEST; - // just a single slot - pbConfig.bids = [pbConfig.bids[0]]; - }); - - afterEach(() => xhr.restore()); - - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - - it('requires paramters to make request', () => { - adapter.callBids({}); - expect(requests).to.be.empty; - }); - - it('requires placementid and hbid', () => { - let backup = pbConfig.bids[0].params; - pbConfig.bids[0].params = {placementid: 1234}; // no hbid - adapter.callBids(pbConfig); - expect(requests).to.be.empty; + }; - pbConfig.bids[0].params = {hbid: 1234}; // no placementid - adapter.callBids(pbConfig); - expect(requests).to.be.empty; - - pbConfig.bids[0].params = backup; - }); - - it('sends bid request to ENDPOINT via POST', () => { - adapter.callBids(pbConfig); - expect(requests[0].url).to.equal(ENDPOINT); - expect(requests[0].method).to.equal('POST'); - }); + it('Verifies komoonaAdapter bidder code', () => { + expect(spec.code).to.equal('komoona'); }); - describe('response handler', () => { - let server; - - beforeEach(() => { - server = sinon.fakeServer.create(); - sinon.stub(bidmanager, 'addBidResponse'); - }); - - afterEach(() => { - server.restore(); - bidmanager.addBidResponse.restore(); - }); - - it('registers bids', () => { - server.respondWith(JSON.stringify(RESPONSE)); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); - - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property('statusMessage', 'Bid available'); - expect(response).to.have.property('cpm', 0.5); - }); - - it('handles nobid responses', () => { - server.respondWith(JSON.stringify({ - 'bids': [{ - 'cpm': 0, - 'creative': '', - 'uuid': '30e5e911c00703' - }] - })); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); - - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property( - 'statusMessage', - 'Bid returned empty or error response' - ); - }); + it('Verifies komoonaAdapter bid request validation', () => { + expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true); + expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { hbid: 12345 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { placementid: 12345 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true); + }); - it('handles JSON.parse errors', () => { - server.respondWith(''); + it('Verify komoonaAdapter build request', () => { + var startTime = new Date().getTime(); + + const request = spec.buildRequests(bidsRequest); + expect(request.url).to.equal('//bidder.komoona.com/v1/GetSBids'); + expect(request.method).to.equal('POST'); + const requestData = JSON.parse(request.data); + + // bids object + let bids = requestData.bids; + expect(bids).to.have.lengthOf(2); + + // first bid request: no floor price + expect(bids[0].uuid).to.equal('2faedf1095f815'); + expect(bids[0].floorprice).to.be.undefined; + expect(bids[0].placementid).to.equal('170577'); + expect(bids[0].hbid).to.equal('abc12345678'); + expect(bids[0].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); + expect(bids[0].sizes).to.have.lengthOf(1); + expect(bids[0].sizes[0][0]).to.equal(300); + expect(bids[0].sizes[0][1]).to.equal(250); + + // second bid request: with floor price + expect(bids[1].uuid).to.equal('3c34e2367a3f59'); + expect(bids[1].floorprice).to.equal(0.5); + expect(bids[1].placementid).to.equal('281277'); + expect(bids[1].hbid).to.equal('abc12345678'); + expect(bids[1].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); + expect(bids[1]).to.have.property('sizes') + .that.is.an('array') + .of.length(1) + .that.deep.equals([[728, 90]]); + + // kbConf object + let kbConf = requestData.kbConf; + expect(kbConf.hdbdid).to.equal(bids[0].hbid); + expect(kbConf.hdbdid).to.equal(bids[1].hbid); + expect(kbConf.encode_bid).to.be.undefined; + // kbConf timezone and cb + expect(kbConf.cb).not.to.be.undefined; + expect(kbConf.ts_as).to.be.above(startTime - 1); + expect(kbConf.tz).to.equal(new Date().getTimezoneOffset()); + // kbConf bid ids + expect(kbConf.hb_placement_bidids) + .to.have.property(bids[0].placementid) + .that.equal(bids[0].uuid); + expect(kbConf.hb_placement_bidids) + .to.have.property(bids[1].placementid) + .that.equal(bids[1].uuid); + // kbConf floor price + expect(kbConf.hb_floors).not.to.have.property(bids[0].placementid) + expect(kbConf.hb_floors).to.have.property(bids[1].placementid).that.equal(bids[1].floorprice); + // kbConf placement ids + expect(kbConf.hb_placements).to.have.lengthOf(2); + expect(kbConf.hb_placements[0]).to.equal(bids[0].placementid); + expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid); + }); - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + it('Verify komoonaAdapter build response', () => { + const request = spec.buildRequests(bidsRequest); + const bids = spec.interpretResponse(bidsResponse, request); + + // 'server' return single bid + expect(bids).to.have.lengthOf(1); + + // verify bid object + const bid = bids[0]; + const responseBids = bidsResponse.body.bids; + + expect(bid.cpm).to.equal(responseBids[0].cpm); + expect(bid.ad).to.equal(responseBids[0].creative); + expect(bid.requestId).equal(responseBids[0].uuid); + expect(bid.uuid).equal(responseBids[0].uuid); + expect(bid.width).to.equal(responseBids[0].width); + expect(bid.height).to.equal(responseBids[0].height); + expect(bid.ttl).to.equal(responseBids[0].ttl); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.creativeId).to.equal(responseBids[0].creativeId); + }); - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property( - 'statusMessage', - 'Bid returned empty or error response' - ); - }); + it('Verifies komoonaAdapter sync options', () => { + // user sync disabled + expect(spec.getUserSyncs({})).to.be.undefined; + expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; + // user sync enabled + 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('//s.komoona.com/sync/usync.html'); }); }); diff --git a/test/spec/modules/mobfoxBidAdapter_spec.js b/test/spec/modules/mobfoxBidAdapter_spec.js index f2819009908..76bb95430fe 100644 --- a/test/spec/modules/mobfoxBidAdapter_spec.js +++ b/test/spec/modules/mobfoxBidAdapter_spec.js @@ -1,162 +1,142 @@ -describe('mobfox adapter tests', function () { +describe('mobfox adapter tests', () => { const expect = require('chai').expect; const utils = require('src/utils'); const adapter = require('modules/mobfoxBidAdapter'); - const bidmanager = require('src/bidmanager'); - const adloader = require('src/adloader'); - const CONSTANTS = require('src/constants.json'); - const ajax = require('src/ajax.js'); - let mockResponses = { - banner: { - 'request': { - 'type': 'textAd', - 'htmlString': '<\/title><style>body{margin:0;padding:0}#mobfoxCover{background:0 0;margin:0;padding:0;border:none;position:absolute;left:0;top:0;z-index:100}<\/style><\/head><body><div id="mobfoxCover"><\/div><script type="text\/javascript">function checkRedirect(e){return function(){if(state===REDIRECT){state=REDUNDANT;var t=window.document.querySelector("iframe").contentDocument.querySelector("html").innerHTML.toLowerCase();if(!(t.indexOf("<script")<0&&t.indexOf("<iframe")<0)){var o=new XMLHttpRequest,d={creativeId:creativeId,advertiserId:advertiserId,hParam:hParam,dspId:dspId,networkId:networkId,autoPilotInventoryConfId:autoPilotInventoryConfId,stackItemId:stackItemId,adSpaceId:adSpaceId,cId:cId,adomain:adomain,geo:geo,event:e,ua:window.navigator.userAgent,adId:adId,site:window.location.href,md5Hash:md5Hash,snapshot:btoa(unescape(encodeURIComponent(t)))};o.open("POST","http:\/\/my.mobfox.com\/fraud-integration",!1),o.setRequestHeader("Content-type","application\/json"),o.send(JSON.stringify(d))}}}}function init(){window.onbeforeunload=checkRedirect("onbeforeunload"),window.addEventListener("beforeunload",checkRedirect("beforeunload")),window.addEventListener("unload",checkRedirect("unload")),document.addEventListener("visibilitychange",function(){"hidden"===document.visibilityState&&checkRedirect("visibilityState")});var e=document.createElement("iframe");document.body.appendChild(e),e.width="320",e.height="50";var t=document.querySelector("#mobfoxCover");t.style.width=e.width+"px",t.style.height=e.height+"px",e.style.margin="0px",e.style.padding="0px",e.style.border="none",e.scrolling="no",e.style.overflow="hidden",e.sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox allow-top-navigation allow-same-origin";var o=atob(markupB64);setTimeout(function(){state=NORMAL},200),setTimeout(function(){var e=document.querySelector("#mobfoxCover");document.body.removeChild(e)},200);var d="srcdoc"in e,n=o;o.indexOf("<body>")<0&&(n="<html><body style="margin:0">"+o+"<\/body><\/html>"),d?e.srcdoc=n:(e.contentWindow.document.open(),e.contentWindow.document.write(n),e.contentWindow.document.close())}var markupB64="PGEgaHJlZj0iaHR0cDovL3Rva3lvLW15Lm1vYmZveC5jb20vZXhjaGFuZ2UuY2xpY2sucGhwP2g9ZGI1ZjZkOTJiMDk1OGI0ZDFlNjU4ZjZlNWRkNWY0MmUiIHRhcmdldD0iX2JsYW5rIj48aW1nIHNyYz0iaHR0cHM6Ly9jcmVhdGl2ZWNkbi5tb2Jmb3guY29tL2U4ZTkxNWYzMmJhOTVkM2JmMzY4YTM5N2EyMzQ4NzVmLmdpZiIgYm9yZGVyPSIwIi8+PC9hPjxicj48aW1nIHN0eWxlPSJwb3NpdGlvbjphYnNvbHV0ZTsgbGVmdDogLTEwMDAwcHg7IiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBzcmM9Imh0dHA6Ly90b2t5by1teS5tb2Jmb3guY29tL2V4Y2hhbmdlLnBpeGVsLnBocD9oPWRiNWY2ZDkyYjA5NThiNGQxZTY1OGY2ZTVkZDVmNDJlIi8+PHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPmRvY3VtZW50LndyaXRlKCc8aW1nIHN0eWxlPSJwb3NpdGlvbjphYnNvbHV0ZTsgbGVmdDogLTEwMDAwcHg7IiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBzcmM9Imh0dHA6Ly90b2t5by1teS5tb2Jmb3guY29tL2V4Y2hhbmdlLnBpeGVsLnBocD9oPWRiNWY2ZDkyYjA5NThiNGQxZTY1OGY2ZTVkZDVmNDJlJnRlc3Q9MSIvPicpOzwvc2NyaXB0Pg==",INITIAL=0,REDIRECT=1,REDUNDANT=2,NORMAL=3,state=INITIAL,creativeId="",advertiserId="",hParam="db5f6d92b0958b4d1e658f6e5dd5f42e",dspId="",networkId="",autoPilotInventoryConfId="",stackItemId="392746",serverHost="184.172.209.50",adSpaceId="",adId="",cId="",adomain="",geo="US",md5Hash="f3bd183c0b19faf12c76e75461cb8cac";document.addEventListener("DOMContentLoaded",function(e){state=REDIRECT}),setTimeout(init,1)<\/script><\/body><\/html>', - 'clicktype': 'safari', - 'clickurl': 'http://tokyo-my.mobfox.com/exchange.click.php?h=db5f6d92b0958b4d1e658f6e5dd5f42e', - 'urltype': 'link', - 'refresh': '30', - 'scale': 'no', - 'skippreflight': 'yes' - } - } - }; - - let mockRequestsParams = { - banner: { - rt: 'api', - r_type: 'banner', - i: '69.197.148.18', - s: 'fe96717d9875b9da4339ea5367eff1ec', - u: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4', - adspace_strict: 0, - - // o_iosadvid: '1976F519-26D0-4428-9891-3133253A453F', - // r_floor: '0.2', - // longitude: '12.12', - // latitude: '280.12', - // demo_gender: 'male', - // demo_age: '1982', - // demo_keywords: 'sports', - // adspace_width: 320, - // adspace_height: 50 - } - }; - before(() => sinon.stub(document.body, 'appendChild')); - after(() => document.body.appendChild.restore()); - - let xhrMock = { - getResponseHeader: getResponseHeaderMock - }; - function getResponseHeaderMock(header) { - switch (header) { - case 'Content-Type': - return 'application/json'; - case 'X-Pricing-CPM': - return '1'; - } - } - function createMobfoxErrorStub() { - return sinon.stub(ajax, 'ajax', (url, callbacks) => { - callbacks.success( - JSON.stringify({error: 'No Ad Available'}), - xhrMock - ); + const bidRequest = [{ + code: 'div-gpt-ad-1460505748561-0', + sizes: [[320, 480], [300, 250], [300, 600]], + // Replace this object to test a new Adapter! + bidder: 'mobfox', + bidId: '5t5t5t5', + params: { + s: '267d72ac3f77a3f447b32cf7ebf20673', // required - The hash of your inventory to identify which app is making the request, + imp_instl: 1 // optional - set to 1 if using interstitial otherwise delete or set to 0 + }, + placementCode: 'div-gpt-ad-1460505748561-0', + requestId: 'c241c810-18d9-4aa4-a62f-8c1980d8d36b', + transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' + }]; + + describe('validRequests', () => { + let bidRequestInvalid1 = [{ + code: 'div-gpt-ad-1460505748561-0', + sizes: [[320, 480], [300, 250], [300, 600]], + // Replace this object to test a new Adapter! + bidder: 'mobfox', + bidId: '5t5t5t5', + params: { + imp_instl: 1 // optional - set to 1 if using interstitial otherwise delete or set to 0 + }, + placementCode: 'div-gpt-ad-1460505748561-0', + requestId: 'c241c810-18d9-4aa4-a62f-8c1980d8d36b', + transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' + }]; + + let bidRequestInvalid2 = [{ + code: 'div-gpt-ad-1460505748561-0', + sizes: [[320, 480], [300, 250], [300, 600]], + // Replace this object to test a new Adapter! + bidder: 'mobfox', + bidId: '5t5t5t5', + params: { + s: '267d72ac3f77a3f447b32cf7ebf20673', // required - The hash of your inventory to identify which app is making the request, + imp_instl: 1 // optional - set to 1 if using interstitial otherwise delete or set to 0 + }, + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '31f42cba-5920-4e47-adad-69c79d0d4fb4' + }]; + + it('test valid MF request success', () => { + let isValid = adapter.spec.isBidRequestValid(bidRequest[0]); + expect(isValid).to.equal(true); }); - } - function createMobfoxSuccessStub() { - return sinon.stub(ajax, 'ajax', (url, callbacks) => { - callbacks.success( - JSON.stringify(mockResponses.banner) - , xhrMock - ); + it('test valid MF request failed1', () => { + let isValid = adapter.spec.isBidRequestValid(bidRequestInvalid1[0]); + expect(isValid).to.equal(false); }); - } - describe('test mobfox error response', function () { - let stubAddBidResponse, stubAjax; - before(function () { - stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - stubAjax = createMobfoxErrorStub() + it('test valid MF request failed2', () => { + let isValid = adapter.spec.isBidRequestValid(bidRequestInvalid2[0]); + expect(isValid).to.equal(false); }); - - after(function () { - stubAddBidResponse.restore(); - stubAjax.restore(); + }) + + describe('buildRequests', () => { + it('test build MF request', () => { + let request = adapter.spec.buildRequests(bidRequest); + let payload = request.data.split('&'); + expect(payload[0]).to.equal('rt=api-fetchip'); + expect(payload[1]).to.equal('r_type=banner'); + expect(payload[2]).to.equal('r_resp=json'); + expect(payload[3]).to.equal('s=267d72ac3f77a3f447b32cf7ebf20673'); + expect(payload[5]).to.equal('adspace_width=320'); + expect(payload[6]).to.equal('adspace_height=480'); + expect(payload[7]).to.equal('imp_instl=1'); }); - it('should add empty bid responses if no bids returned', function () { - let bidderRequest = { - bidderCode: 'mobfox', - bids: [ - { - bidId: 'bidId1', - bidder: 'mobfox', - params: {}, - sizes: [[300, 250]], - placementCode: 'test-gpt-div-1234' - } - ] - }; - - // empty ads in bidresponse - let requestParams = utils.cloneJson(mockRequestsParams.banner); - requestParams.adspace_width = 1231564; // should return an error - bidderRequest.bids[0].params = requestParams; - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - // adapter needs to be called, in order for the stub to register. - adapter().callBids(bidderRequest); - - let bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - let bidResponse1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidPlacementCode1).to.equal('test-gpt-div-1234'); - expect(bidResponse1.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); - expect(bidResponse1.bidderCode).to.equal('mobfox'); + it('test build MF request', () => { + let request = adapter.spec.buildRequests(bidRequest); + let payload = request.data.split('&'); + expect(payload[0]).to.equal('rt=api-fetchip'); + expect(payload[1]).to.equal('r_type=banner'); + expect(payload[2]).to.equal('r_resp=json'); + expect(payload[3]).to.equal('s=267d72ac3f77a3f447b32cf7ebf20673'); + expect(payload[5]).to.equal('adspace_width=320'); + expect(payload[6]).to.equal('adspace_height=480'); + expect(payload[7]).to.equal('imp_instl=1'); }); - }); - - describe('test mobfox success response', function () { - let stubAddBidResponse, stubAjax; - before(function () { - stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - stubAjax = createMobfoxSuccessStub() - }); - - after(function () { - stubAddBidResponse.restore(); - stubAjax.restore(); + }) + + describe('interceptResponse', () => { + let mockServerResponse = { + body: { + request: { + clicktype: 'safari', + clickurl: 'http://tokyo-my.mobfox.com/exchange.click.php?h=494ef76d5b0287a8b5ac8724855cb5e0', + cpmPrice: 50, + htmlString: 'test', + refresh: '30', + scale: 'no', + skippreflight: 'yes', + type: 'textAd', + urltype: 'link' + } + }, + headers: { + get: function (header) { + if (header === 'X-Pricing-CPM') { + return 50; + } + } + } + }; + it('test intercept response', () => { + let request = adapter.spec.buildRequests(bidRequest); + let bidResponses = adapter.spec.interpretResponse(mockServerResponse, request); + expect(bidResponses.length).to.equal(1); + expect(bidResponses[0].ad).to.equal('test'); + expect(bidResponses[0].cpm).to.equal(50); + expect(bidResponses[0].creativeId).to.equal('267d72ac3f77a3f447b32cf7ebf20673'); + expect(bidResponses[0].requestId).to.equal('5t5t5t5'); + expect(bidResponses[0].currency).to.equal('USD'); + expect(bidResponses[0].height).to.equal('480'); + expect(bidResponses[0].netRevenue).to.equal(true); + expect(bidResponses[0].referrer).to.equal('http://tokyo-my.mobfox.com/exchange.click.php?h=494ef76d5b0287a8b5ac8724855cb5e0'); + expect(bidResponses[0].ttl).to.equal(360); + expect(bidResponses[0].width).to.equal('320'); }); - it('should add a bid response', function () { - let bidderRequest = { - bidderCode: 'mobfox', - bids: [ - { - bidId: 'bidId1', - bidder: 'mobfox', - params: {}, - sizes: [[300, 250]], - placementCode: 'test-gpt-div-1234' - } - ] + it('test intercept response with empty server response', () => { + let request = adapter.spec.buildRequests(bidRequest); + let serverResponse = { + request: { + error: 'cannot get response' + } }; - - let requestParams = utils.cloneJson(mockRequestsParams.banner); - bidderRequest.bids[0].params = requestParams; - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - // adapter needs to be called, in order for the stub to register. - adapter().callBids(bidderRequest); - - let bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - let bidResponse1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidPlacementCode1).to.equal('test-gpt-div-1234'); - expect(bidResponse1.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidResponse1.bidderCode).to.equal('mobfox'); - - expect(bidResponse1.cpm).to.equal(1); - expect(bidResponse1.width).to.equal(bidderRequest.bids[0].sizes[0][0]); - expect(bidResponse1.height).to.equal(bidderRequest.bids[0].sizes[0][1]); - }); - }); + let bidResponses = adapter.spec.interpretResponse(serverResponse, request); + expect(bidResponses.length).to.equal(0); + }) + }) }); 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 = '<script type="text/javascript" src="https://trc.audiencemanager.de/ad/?pl=58c2829beb0a193456047a27&cb=${CACHEBUSTER}&tc=${CLICK_URL_ENC}"></script> <noscript> <a href="https://trc.audiencemanager.de/ad/?t=c&pl=58c2829beb0a193456047a27&cb=${CACHEBUSTER}&tc=${CLICK_URL_ENC}"> <img src="https://trc.audiencemanager.de/ad/?t=i&pl=58c2829beb0a193456047a27&cb=${CACHEBUSTER}" alt="Click Here" border="0"> </a> </noscript>'; + 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/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index c08e8c256e6..cee521b2921 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1,277 +1,220 @@ -const expect = require('chai').expect; -const assert = require('chai').assert; -const adapter = require('modules/openxBidAdapter')(); -const bidmanager = require('src/bidmanager'); -const adloader = require('src/adloader'); -const CONSTANTS = require('src/constants.json'); -const ajax = require('src/ajax'); +import { expect } from 'chai'; +import { spec } from 'modules/openxBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; -describe('openx adapter tests', function () { - describe('test openx callback response', function () { - let stubAjax; - let stubAddBidResponse; - this.response = null; - let responseHandlerCallback = (_url, callback, _data, _params) => { - return callback(this.response); +const URLBASE = '/w/1.0/arj'; + +describe('OpenxAdapter', () => { + 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', () => { + let bid = { + 'bidder': 'openx', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', }; - beforeEach(() => { - stubAjax = sinon.stub(ajax, 'ajax', responseHandlerCallback); - stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - sinon.stub(document.body, 'appendChild'); + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - afterEach(() => { - stubAjax.restore(); - stubAddBidResponse.restore(); - this.response = null; - document.body.appendChild.restore(); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {'unit': '12345678'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should add empty bid responses if no bids returned', () => { - // empty ads in bidresponse - this.response = JSON.stringify({ - 'ads': - { - 'version': 1, - 'count': 1, - 'pixels': 'http://testpixels.net', - 'ad': [] - } - }); + }); - let bidderRequest = { - bidderCode: 'openx', - bids: [ - { - bidId: 'bidId1', - bidder: 'openx', - params: { - delDomain: 'delDomain1', - unit: '1234' - }, - sizes: [[300, 250]], - placementCode: 'test-gpt-div-1234' - } - ] - }; + describe('buildRequests', () => { + let bidRequests = [{ + 'bidder': 'openx', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }]; + + it('should send bid request to openx url via GET', () => { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal('//' + bidRequests[0].params.delDomain + URLBASE); + expect(request.method).to.equal('GET'); + }); - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - adapter.callBids(bidderRequest); + it('should have the correct parameters', () => { + const request = spec.buildRequests(bidRequests); + const dataParams = request.data; - let bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - let bidResponse1 = stubAddBidResponse.getCall(0).args[1]; - expect(bidPlacementCode1).to.equal('test-gpt-div-1234'); - expect(bidResponse1.getStatusCode()).to.equal(CONSTANTS.STATUS.NO_BID); - expect(bidResponse1.bidderCode).to.equal('openx'); + expect(dataParams.auid).to.exist; + expect(dataParams.auid).to.equal('12345678'); + expect(dataParams.aus).to.exist; + expect(dataParams.aus).to.equal('300x250,300x600'); }); - it('should add bid responses if bids are returned', () => { - let bidderRequest = { - bidderCode: 'openx', - bids: [ - { - bidId: 'bidId1', - bidder: 'openx', - params: { - delDomain: 'delDomain1', - unit: '1234' - }, - sizes: [[300, 250]], - placementCode: 'test-gpt-div-1234' - } - ] - }; - - this.response = JSON.stringify({ - 'ads': + it('should send out custom params on bids that have customParams specified', () => { + let bidRequest = Object.assign({}, + bidRequests[0], { - 'version': 1, - 'count': 1, - 'pixels': 'http://testpixels.net', - 'ad': [ - { - 'adunitid': 1234, - 'adid': 5678, - 'type': 'html', - 'html': 'test_html', - 'framed': 1, - 'is_fallback': 0, - 'ts': 'ts', - 'cpipc': 1000, - 'pub_rev': '1000', - 'adv_id': 'adv_id', - 'brand_id': '', - 'creative': [ - { - 'width': '300', - 'height': '250', - 'target': '_blank', - 'mime': 'text/html', - 'media': 'test_media', - 'tracking': { - 'impression': 'test_impression', - 'inview': 'test_inview', - 'click': 'test_click' - } - } - ] - }] + params: { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'customParams': {'Test1': 'testval1+', 'test2': ['testval2/', 'testval3']} + } } - }); - - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - adapter.callBids(bidderRequest); + ); - let bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - let bidResponse1 = stubAddBidResponse.getCall(0).args[1]; - let bid1width = '300'; - let bid1height = '250'; - let cpm = 1; - expect(bidPlacementCode1).to.equal('test-gpt-div-1234'); - expect(bidResponse1.getStatusCode()).to.equal(CONSTANTS.STATUS.GOOD); - expect(bidResponse1.bidderCode).to.equal('openx'); - expect(bidResponse1.width).to.equal(bid1width); - expect(bidResponse1.height).to.equal(bid1height); - expect(bidResponse1.cpm).to.equal(cpm); - }); - }); - describe('test openx ad requests', () => { - let spyAjax; - let spyBtoa; - beforeEach(() => { - spyAjax = sinon.spy(ajax, 'ajax'); - spyBtoa = sinon.spy(window, 'btoa'); - sinon.stub(document.body, 'appendChild'); - }); - afterEach(() => { - spyAjax.restore(); - spyBtoa.restore(); - document.body.appendChild.restore(); - }); + const request = spec.buildRequests([bidRequest]); + const dataParams = request.data; - it('should not call ajax when inputting with empty params', () => { - adapter.callBids({}); - assert(!spyAjax.called); + expect(dataParams.tps).to.exist; + expect(dataParams.tps).to.equal(btoa('test1=testval1.&test2=testval2_,testval3')); }); - it('should call ajax with the correct bid url', () => { - let params = { - bids: [ - { - sizes: [[300, 250], [300, 600]], - params: { - delDomain: 'testdelDomain', - unit: 1234 - } + it('should send out custom floors on bids that have customFloors specified', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'customFloor': 1.5 } - ] - }; - adapter.callBids(params); - sinon.assert.calledOnce(spyAjax); + } + ); - let bidUrl = spyAjax.getCall(0).args[0]; - expect(bidUrl).to.include('testdelDomain'); - expect(bidUrl).to.include('1234'); - expect(bidUrl).to.include('300x250,300x600'); + const request = spec.buildRequests([bidRequest]); + const dataParams = request.data; + + expect(dataParams.aumfs).to.exist; + expect(dataParams.aumfs).to.equal('1500'); }); - it('should send out custom params on bids that have customParams specified', () => { - let params = { - bids: [ - { - sizes: [[300, 250], [300, 600]], - params: { - delDomain: 'testdelDomain', - unit: 1234, - customParams: {'test1': 'testval1+', 'test2': ['testval2/', 'testval3']} - } + it('should send out custom bc parameter, if override is present', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + params: { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'bc': 'hb_override' } - ] - }; - adapter.callBids(params); + } + ); - sinon.assert.calledOnce(spyAjax); - sinon.assert.calledWith(spyBtoa, 'test1=testval1.&test2=testval2_,testval3'); - let bidUrl = spyAjax.getCall(0).args[0]; - expect(bidUrl).to.include('testdelDomain'); - expect(bidUrl).to.include('1234'); - expect(bidUrl).to.include('300x250,300x600'); + const request = spec.buildRequests([bidRequest]); + const dataParams = request.data; + + expect(dataParams.bc).to.exist; + expect(dataParams.bc).to.equal('hb_override'); }); + }); - it('should send out custom floors on bids that have customFloors specified', () => { - let params = { - bids: [ - { - sizes: [[300, 250], [300, 600]], - params: { - delDomain: 'testdelDomain', - unit: 1234, - customFloor: 1 - } - }, - { - sizes: [[320, 50]], - params: { - delDomain: 'testdelDomain', - unit: 1234 - } - }, + describe('interpretResponse', () => { + let bids = [{ + 'bidder': 'openx', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }]; + let bidRequest = { + method: 'GET', + url: 'url', + data: {}, + payload: {'bids': bids, 'startTime': new Date()} + }; + let bidResponse = { + 'ads': + { + 'version': 1, + 'count': 1, + 'pixels': 'http://testpixels.net', + 'ad': [ { - sizes: [[728, 90]], - params: { - delDomain: 'testdelDomain', - unit: 1234, - customFloor: 1.5 - } - } - ] - }; - adapter.callBids(params); + 'adunitid': 12345678, + 'adid': 5678, + 'type': 'html', + 'html': 'test_html', + 'framed': 1, + 'is_fallback': 0, + 'ts': 'ts', + 'cpipc': 1000, + 'pub_rev': '1000', + 'adv_id': 'adv_id', + 'brand_id': '', + 'creative': [ + { + 'width': '300', + 'height': '250', + 'target': '_blank', + 'mime': 'text/html', + 'media': 'test_media', + 'tracking': { + 'impression': 'test_impression', + 'inview': 'test_inview', + 'click': 'test_click' + } + } + ] + }] + } + }; + it('should return correct bid response', () => { + let expectedResponse = [ + { + 'requestId': '30b31c1838de1e', + 'cpm': 1, + 'width': '300', + 'height': '250', + 'creativeId': 5678, + 'ad': 'test_html', + 'ttl': 300, + 'netRevenue': true, + 'currency': 'USD', + 'ts': 'ts' + } + ]; - sinon.assert.calledOnce(spyAjax); - let bidUrl = spyAjax.getCall(0).args[0]; - expect(bidUrl).to.include('testdelDomain'); - expect(bidUrl).to.include('1234'); - expect(bidUrl).to.include('300x250,300x600|320x50|728x90'); - expect(bidUrl).to.include('aumfs=1000%2C0%2C1500'); + let result = spec.interpretResponse({body: bidResponse}, bidRequest); + expect(Object.keys(result[0])).to.eql(Object.keys(expectedResponse[0])); }); - it('should change bc param if configureable bc is specified', () => { - let params = { - bids: [ - { - sizes: [[300, 250], [300, 600]], - params: { - delDomain: 'testdelDomain', - unit: 1234, - bc: 'hb_pb_test' - } - }, - { - sizes: [[320, 50]], - params: { - delDomain: 'testdelDomain', - unit: 1234, - bc: 'hb_pb_test' - } - }, - { - sizes: [[728, 90]], - params: { - delDomain: 'testdelDomain', - unit: 1234, - bc: 'hb_pb_test' - } - } - ] + it('handles nobid responses', () => { + bidResponse = { + 'ads': + { + 'version': 1, + 'count': 1, + 'pixels': 'http://testpixels.net', + 'ad': [] + } }; - adapter.callBids(params); - sinon.assert.calledOnce(spyAjax); - let bidUrl = spyAjax.getCall(0).args[0]; - expect(bidUrl).to.include('testdelDomain'); - expect(bidUrl).to.include('1234'); - expect(bidUrl).to.include('300x250,300x600|320x50|728x90'); - expect(bidUrl).to.include('bc=hb_pb_test'); + let result = spec.interpretResponse({body: bidResponse}, bidRequest); + expect(result.length).to.equal(0); }); }); }); 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: '<script>document.write("campaign banner");<\/script>' - }; - - adapter().callBids(params); - - var adUnits = []; - var unit = {}; - 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$$._doPlatformIOCallback(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('/19968336/header-bid-tag-0'); - expect(bidObject1.bidderCode).to.equal('platformio'); - expect(bidObject1.cpm).to.equal(1); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - expect(bidObject1.ad).to.have.length.above(1); - - stubAddBidResponse.restore(); - }); - - it('should return no 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: 0, - width: 300, - height: 250, - callback_uid: 'bid1111', - tag: '<script>document.write("default banner");<\/script>' - }; - - adapter().callBids(params); - - var adUnits = []; - var unit = {}; - 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$$._doPlatformIOCallback(response); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('/19968336/header-bid-tag-0'); - expect(bidObject1.bidderCode).to.equal('platformio'); - - stubAddBidResponse.restore(); - }); - }); -}); +import {expect} from 'chai'; +import {spec} from 'modules/platformioBidAdapter'; +import {getTopWindowLocation} from 'src/utils'; + +describe('Platformio Adapter Tests', () => { + const slotConfigs = [{ + placementCode: '/DfpAccount1/slot1', + sizes: [[300, 250]], + bidId: 'bid12345', + params: { + pubId: '28082', + siteId: '26047', + placementId: '123', + size: '300x250' + } + }, { + placementCode: '/DfpAccount2/slot2', + sizes: [[250, 250]], + bidId: 'bid23456', + params: { + pubId: '28082', + siteId: '26047', + placementId: '456', + size: '250x250' + } + }]; + it('Verify build request', () => { + const request = spec.buildRequests(slotConfigs); + expect(request.url).to.equal('//piohbdisp.hb.adx1.com/'); + expect(request.method).to.equal('POST'); + const ortbRequest = JSON.parse(request.data); + // site object + expect(ortbRequest.site).to.not.equal(null); + expect(ortbRequest.site.publisher).to.not.equal(null); + expect(ortbRequest.site.publisher.id).to.equal('28082'); + expect(ortbRequest.site.ref).to.equal(window.top.document.referrer); + expect(ortbRequest.site.page).to.equal(getTopWindowLocation().href); + expect(ortbRequest.imp).to.have.lengthOf(2); + // device object + expect(ortbRequest.device).to.not.equal(null); + expect(ortbRequest.device.ua).to.equal(navigator.userAgent); + // slot 1 + expect(ortbRequest.imp[0].tagid).to.equal('123'); + expect(ortbRequest.imp[0].banner).to.not.equal(null); + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + // slot 2 + expect(ortbRequest.imp[1].tagid).to.equal('456'); + expect(ortbRequest.imp[1].banner).to.not.equal(null); + expect(ortbRequest.imp[1].banner.w).to.equal(250); + expect(ortbRequest.imp[1].banner.h).to.equal(250); + }); + + it('Verify parse response', () => { + const request = spec.buildRequests(slotConfigs); + const ortbRequest = JSON.parse(request.data); + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad' + }] + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + expect(bids).to.have.lengthOf(1); + // verify first bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('This is an Ad'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.adId).to.equal('bid12345'); + expect(bid.creative_id).to.equal('bid12345'); + expect(bid.creativeId).to.equal('bid12345'); + }); + + it('Verify full passback', () => { + const request = spec.buildRequests(slotConfigs); + const bids = spec.interpretResponse({ body: null }, request) + expect(bids).to.have.lengthOf(0); + }); + + it('Verifies bidder code', () => { + expect(spec.code).to.equal('platformio'); + }); + + it('Verifies if bid request valid', () => { + expect(spec.isBidRequestValid(slotConfigs[0])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + }); +}); diff --git a/test/spec/modules/polluxBidAdapter_spec.js b/test/spec/modules/polluxBidAdapter_spec.js index 1bcfe28124d..ea550fecd71 100644 --- a/test/spec/modules/polluxBidAdapter_spec.js +++ b/test/spec/modules/polluxBidAdapter_spec.js @@ -1,172 +1,207 @@ -describe('Pollux Bid Adapter tests', function () { - var expect = require('chai').expect; - var urlParse = require('url-parse'); - var querystringify = require('querystringify'); - var Adapter = require('modules/polluxBidAdapter'); - var adLoader = require('src/adloader'); - var bidmanager = require('src/bidmanager'); - var utils = require('src/utils'); - - var stubLoadScript; - var stubAddBidResponse; - var polluxAdapter; - - // mock golbal _bidsRequested var - var bidsRequested = []; - utils.getBidRequest = function (id) { - return bidsRequested.map(bidSet => bidSet.bids.find(bid => bid.bidId === id)).find(bid => bid); - }; - - beforeEach(function () { - polluxAdapter = new Adapter(); - bidsRequested = []; - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); +import {expect} from 'chai'; +import {spec} from 'modules/polluxBidAdapter'; +import {utils} from 'src/utils'; +import {newBidder} from 'src/adapters/bidderFactory'; + +describe('POLLUX Bid Adapter tests', function () { + // ad units setup + const setup_single_bid = [{ + placementCode: 'div-gpt-ad-1460505661587-0', + bidId: '789s6354sfg856', + bidderUrl: '//adn.polluxnetwork.com/prebid/v1', + sizes: [[728, 90], [300, 250]], + params: {zone: '1806,276'} + }]; + const setup_multi_bid = [{ + placementCode: 'div-gpt-ad-1460505661639-0', + bidId: '21fe992ca48d55', + sizes: [[300, 250]], + params: {zone: '1806'} + }, { + placementCode: 'div-gpt-ad-1460505661812-0', + bidId: '23kljh54390534', + sizes: [[728, 90]], + params: {zone: '276'} + }]; + + it('TEST: verify buildRequests no valid bid requests', () => { + let request = spec.buildRequests(false); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); + request = spec.buildRequests([]); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); + request = spec.buildRequests({}); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); + request = spec.buildRequests(null); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); }); - afterEach(function () { - stubLoadScript.restore(); - stubAddBidResponse.restore(); + it('TEST: verify buildRequests single bid', () => { + const request = spec.buildRequests(setup_single_bid); + expect(request.method).to.equal('POST'); + const requested_bids = JSON.parse(request.data); + // bids request + expect(requested_bids).to.not.equal(null); + expect(requested_bids).to.have.lengthOf(1); + // bid objects + expect(requested_bids[0]).to.not.equal(null); + expect(requested_bids[0]).to.have.property('bidId'); + expect(requested_bids[0]).to.have.property('sizes'); + expect(requested_bids[0]).to.have.property('zones'); + // bid 0 + expect(requested_bids[0].bidId).to.equal('789s6354sfg856'); + expect(requested_bids[0].sizes).to.not.equal(null); + expect(requested_bids[0].sizes).to.have.lengthOf(2); + expect(requested_bids[0].sizes[0][0]).to.equal(728); + expect(requested_bids[0].sizes[0][1]).to.equal(90); + expect(requested_bids[0].sizes[1][0]).to.equal(300); + expect(requested_bids[0].sizes[1][1]).to.equal(250); + expect(requested_bids[0].zones).to.equal('1806,276'); }); - describe('creation of bid url', function () { - it('bid request for single placement', function () { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - bidId: '21fe992ca48d55', - bidder: 'pollux', - sizes: [[300, 250]], - params: { zone: '1806' } - }] - }; - - polluxAdapter.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('zone').and.to.equal('1806'); - expect(parsedBidUrlQueryString).to.have.property('domain').and.to.have.length.above(1); - }); + it('TEST: verify buildRequests multi bid', () => { + const request = spec.buildRequests(setup_multi_bid); + expect(request.method).to.equal('POST'); + const requested_bids = JSON.parse(request.data); + // bids request + expect(requested_bids).to.not.equal(null); + expect(requested_bids).to.have.lengthOf(2); + // bid objects + expect(requested_bids[0]).to.not.equal(null); + expect(requested_bids[0]).to.have.property('bidId'); + expect(requested_bids[0]).to.have.property('sizes'); + expect(requested_bids[0]).to.have.property('zones'); + expect(requested_bids[1]).to.not.equal(null); + expect(requested_bids[1]).to.have.property('bidId'); + expect(requested_bids[1]).to.have.property('sizes'); + expect(requested_bids[1]).to.have.property('zones'); + // bid 0 + expect(requested_bids[0].bidId).to.equal('21fe992ca48d55'); + expect(requested_bids[0].sizes).to.not.equal(null); + expect(requested_bids[0].sizes).to.have.lengthOf(1); + expect(requested_bids[0].sizes[0][0]).to.equal(300); + expect(requested_bids[0].sizes[0][1]).to.equal(250); + expect(requested_bids[0].zones).to.equal('1806'); + // bid 1 + expect(requested_bids[1].bidId).to.equal('23kljh54390534'); + expect(requested_bids[1].sizes).to.not.equal(null); + expect(requested_bids[1].sizes).to.have.lengthOf(1); + expect(requested_bids[1].sizes[0][0]).to.equal(728); + expect(requested_bids[1].sizes[0][1]).to.equal(90); + expect(requested_bids[1].zones).to.equal('276'); }); - describe('handling bid response', function () { - it('should return complete bid response adUrl', function() { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - sizes: [[300, 250]], - bidId: '21fe992ca48d55', - bidder: 'pollux', - params: { zone: '1806' } - }] - }; - - var response = { - cpm: 0.5, - width: 300, - height: 250, - callback_id: '21fe992ca48d55', - ad: 'some.ad.url', - ad_type: 'url', - zone: 1806 - }; - - polluxAdapter.callBids(params); - bidsRequested.push(params); - polluxAdapter.polluxHandler(response); - - sinon.assert.calledOnce(stubAddBidResponse); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('div-gpt-ad-1460505661639-0'); - expect(bidObject1.bidderCode).to.equal('pollux'); - expect(bidObject1.cpm).to.equal(0.5); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - expect(bidObject1.adUrl).to.have.length.above(1); - }); - - it('should return complete bid response ad (html)', function() { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - sizes: [[300, 250]], - bidId: '21fe992ca48d55', - bidder: 'pollux', - params: { zone: '1806' } - }] - }; - - var response = { - cpm: 0.5, - width: 300, - height: 250, - callback_id: '21fe992ca48d55', - ad: '<script src="some.ad.url"></script>', - ad_type: 'html', - zone: 1806 - }; - - polluxAdapter.callBids(params); - bidsRequested.push(params); - polluxAdapter.polluxHandler(response); - - sinon.assert.calledOnce(stubAddBidResponse); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('div-gpt-ad-1460505661639-0'); - expect(bidObject1.bidderCode).to.equal('pollux'); - expect(bidObject1.cpm).to.equal(0.5); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - expect(bidObject1.ad).to.have.length.above(1); - }); + it('TEST: verify interpretResponse empty', () => { + let bids = spec.interpretResponse(false, {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + bids = spec.interpretResponse([], {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + bids = spec.interpretResponse({}, {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + bids = spec.interpretResponse(null, {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + }); - it('should return no bid response', function() { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - sizes: [[300, 250]], - bidId: '21fe992ca48d55', - bidder: 'pollux', - params: { zone: '276' } - }] - }; + it('TEST: verify interpretResponse ad_type url', () => { + const serverResponse = { + body: [ + { + bidId: '789s6354sfg856', + cpm: '2.15', + width: '728', + height: '90', + ad: 'http://adn.polluxnetwork.com/zone/276?_plx_prebid=1&_plx_campaign=1125', + ad_type: 'url', + creativeId: '1125', + referrer: 'http://www.example.com' + } + ] + }; + const bids = spec.interpretResponse(serverResponse, {}); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('789s6354sfg856'); + expect(bids[0].cpm).to.equal(2.15); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + expect(bids[0].ttl).to.equal(3600); + expect(bids[0].creativeId).to.equal('1125'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].currency).to.equal('EUR'); + expect(bids[0].referrer).to.equal('http://www.example.com'); + expect(bids[0].adUrl).to.equal('http://adn.polluxnetwork.com/zone/276?_plx_prebid=1&_plx_campaign=1125'); + expect(bids[0]).to.not.have.property('ad'); + }); - var response = { - cpm: null, - width: null, - height: null, - callback_id: null, - ad: null, - zone: null - }; + it('TEST: verify interpretResponse ad_type html', () => { + const serverResponse = { + body: [ + { + bidId: '789s6354sfg856', + cpm: '2.15', + width: '728', + height: '90', + ad: '<html><h3>I am an ad</h3></html>', + ad_type: 'html', + creativeId: '1125' + } + ] + }; + const bids = spec.interpretResponse(serverResponse, {}); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('789s6354sfg856'); + expect(bids[0].cpm).to.equal(2.15); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + expect(bids[0].ttl).to.equal(3600); + expect(bids[0].creativeId).to.equal('1125'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].currency).to.equal('EUR'); + expect(bids[0]).to.not.have.property('referrer'); + expect(bids[0]).to.not.have.property('adUrl'); + expect(bids[0].ad).to.equal('<html><h3>I am an ad</h3></html>'); + }); - polluxAdapter.callBids(params); - bidsRequested.push(params); - polluxAdapter.polluxHandler(response); + it('TEST: verify url and query params', () => { + const URL = require('url-parse'); + const querystringify = require('querystringify'); + const request = spec.buildRequests(setup_single_bid); + const parsedUrl = new URL('https:' + request.url); + expect(parsedUrl.origin).to.equal('https://adn.polluxnetwork.com'); + expect(parsedUrl.pathname).to.equal('/prebid/v1'); + expect(parsedUrl).to.have.property('query'); + const parsedQuery = querystringify.parse(parsedUrl.query); + expect(parsedQuery).to.have.property('domain').and.to.have.length.above(1); + }); - sinon.assert.calledOnce(stubAddBidResponse); + it('TEST: verify isBidRequestValid', () => { + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({params: {}})).to.equal(false); + expect(spec.isBidRequestValid(setup_single_bid[0])).to.equal(true); + expect(spec.isBidRequestValid(setup_multi_bid[0])).to.equal(true); + expect(spec.isBidRequestValid(setup_multi_bid[1])).to.equal(true); + }); - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; + it('TEST: verify bidder code', () => { + expect(spec.code).to.equal('pollux'); + }); - expect(bidPlacementCode1).to.equal(''); - expect(bidObject1.bidderCode).to.equal('pollux'); - }); + it('TEST: verify bidder aliases', () => { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('plx'); }); }); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 21098a2859f..78eef4b016b 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import Adapter from 'modules/prebidServerBidAdapter'; +import adapterManager from 'src/adaptermanager'; import bidmanager from 'src/bidmanager'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils'; @@ -353,6 +354,23 @@ describe('S2S Adapter', () => { expect(response).to.have.property('adserverTargeting').that.deep.equals({'foo': 'bar'}); }); + it('registers client user syncs when client bid adapter is present', () => { + let rubiconAdapter = { + registerSyncs: sinon.spy() + }; + sinon.stub(adapterManager, 'getBidAdapter', () => rubiconAdapter); + + server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); + + adapter.setConfig(CONFIG); + adapter.callBids(REQUEST); + server.respond(); + + sinon.assert.calledOnce(rubiconAdapter.registerSyncs); + + adapterManager.getBidAdapter.restore(); + }); + it('registers bid responses when server requests cookie sync', () => { server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js new file mode 100644 index 00000000000..c7b8cd5cd8e --- /dev/null +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -0,0 +1,276 @@ +import { + expect +} from 'chai'; +import * as utils from 'src/utils'; +import PubMaticAdapter from 'modules/pubmaticBidAdapter'; +import bidmanager from 'src/bidmanager'; +import constants from 'src/constants.json'; + +let getDefaultBidRequest = () => { + return { + bidderCode: 'pubmatic', + requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + bidderRequestId: '7101db09af0db2', + start: new Date().getTime(), + bids: [{ + bidder: 'pubmatic', + bidId: '84ab500420319d', + bidderRequestId: '7101db09af0db2', + requestId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + placementCode: 'DIV_1', + params: { + placement: 1234567, + network: '9599.1' + } + }] + }; +}; + +describe('PubMaticAdapter', () => { + let adapter; + + function createBidderRequest({ + bids, + params + } = {}) { + var bidderRequest = getDefaultBidRequest(); + if (bids && Array.isArray(bids)) { + bidderRequest.bids = bids; + } + if (params) { + bidderRequest.bids.forEach(bid => bid.params = params); + } + return bidderRequest; + } + + beforeEach(() => adapter = new PubMaticAdapter()); + + describe('callBids()', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + + describe('user syncup', () => { + beforeEach(() => { + sinon.stub(utils, 'insertElement'); + }); + + afterEach(() => { + utils.insertElement.restore(); + }); + + it('usersync is initiated', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9999, + adSlot: 'abcd@728x90', + age: '20' + } + })); + utils.insertElement.calledOnce.should.be.true; + expect(utils.insertElement.getCall(0).args[0].src).to.equal('http://ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p=9999'); + }); + }); + + describe('bid request', () => { + beforeEach(() => { + sinon.stub(utils, 'createContentToExecuteExtScriptInFriendlyFrame', function() { + return ''; + }); + }); + + afterEach(() => { + utils.createContentToExecuteExtScriptInFriendlyFrame.restore(); + }); + + it('requires parameters to be made', () => { + adapter.callBids({}); + utils.createContentToExecuteExtScriptInFriendlyFrame.calledOnce.should.be.false; + }); + + it('for publisherId 9990 call is made to gads.pubmatic.com', () => { + var bidRequest = createBidderRequest({ + params: { + publisherId: 9990, + adSlot: ' abcd@728x90', + age: '20', + wiid: 'abcdefghijk', + profId: '1234', + verId: '12', + pmzoneid: 'abcd123, efg345', + dctr: 'key=1234,5678' + } + }); + adapter.callBids(bidRequest); + var callURL = utils.createContentToExecuteExtScriptInFriendlyFrame.getCall(0).args[0]; + expect(bidRequest.bids[0].params.adSlot).to.equal('abcd@728x90'); + expect(callURL).to.contain('gads.pubmatic.com/AdServer/AdCallAggregator?'); + expect(callURL).to.contain('SAVersion=1100'); + expect(callURL).to.contain('wp=PreBid'); + expect(callURL).to.contain('js=1'); + expect(callURL).to.contain('screenResolution='); + expect(callURL).to.contain('wv=' + constants.REPO_AND_VERSION); + expect(callURL).to.contain('ranreq='); + expect(callURL).to.contain('inIframe='); + expect(callURL).to.contain('pageURL='); + expect(callURL).to.contain('refurl='); + expect(callURL).to.contain('kltstamp='); + expect(callURL).to.contain('timezone='); + expect(callURL).to.contain('age=20'); + expect(callURL).to.contain('adslots=%5Babcd%40728x90%5D'); + expect(callURL).to.contain('kadpageurl='); + expect(callURL).to.contain('wiid=abcdefghijk'); + expect(callURL).to.contain('profId=1234'); + expect(callURL).to.contain('verId=12'); + expect(callURL).to.contain('pmZoneId=abcd123%2C%20efg345'); + expect(callURL).to.contain('dctr=key%3D1234%2C5678'); + }); + + it('for publisherId 9990 call is made to gads.pubmatic.com, age passed as int not being passed ahead', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9990, + adSlot: 'abcd@728x90', + age: 20, + wiid: 'abcdefghijk', + profId: '1234', + verId: '12', + pmzoneid: {}, + dctr: 1234 + } + })); + var callURL = utils.createContentToExecuteExtScriptInFriendlyFrame.getCall(0).args[0]; + expect(callURL).to.contain('gads.pubmatic.com/AdServer/AdCallAggregator?'); + expect(callURL).to.not.contain('age=20'); + expect(callURL).to.not.contain('dctr=1234'); + }); + + it('for publisherId 9990 call is made to gads.pubmatic.com, invalid data for pmzoneid', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9990, + adSlot: 'abcd@728x90', + age: '20', + wiid: 'abcdefghijk', + profId: '1234', + verId: '12', + pmzoneid: {}, + dctr: 1234 + } + })); + var callURL = utils.createContentToExecuteExtScriptInFriendlyFrame.getCall(0).args[0]; + expect(callURL).to.contain('gads.pubmatic.com/AdServer/AdCallAggregator?'); + expect(callURL).to.not.contain('pmZoneId='); + }); + }); + + describe('#handlePubmaticCallback: ', () => { + beforeEach(() => { + sinon.stub(utils, 'createContentToExecuteExtScriptInFriendlyFrame', function() { + return ''; + }); + sinon.stub(bidmanager, 'addBidResponse'); + }); + + afterEach(() => { + utils.createContentToExecuteExtScriptInFriendlyFrame.restore(); + bidmanager.addBidResponse.restore(); + }); + + it('exists and is a function', () => { + expect($$PREBID_GLOBAL$$.handlePubmaticCallback).to.exist.and.to.be.a('function'); + }); + + it('empty response, arguments not passed', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9999, + adSlot: 'abcd@728x90', + age: '20' + } + })); + $$PREBID_GLOBAL$$.handlePubmaticCallback(); + expect(bidmanager.addBidResponse.callCount).to.equal(0); + }); + + it('empty response', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9999, + adSlot: 'abcd@728x90', + age: '20' + } + })); + $$PREBID_GLOBAL$$.handlePubmaticCallback({}, {}); + sinon.assert.called(bidmanager.addBidResponse); + expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('DIV_1'); + var theBid = bidmanager.addBidResponse.firstCall.args[1]; + expect(theBid.bidderCode).to.equal('pubmatic'); + expect(theBid.getStatusCode()).to.equal(2); + }); + + it('not empty response', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9999, + adSlot: 'abcd@728x90:0', + age: '20' + } + })); + $$PREBID_GLOBAL$$.handlePubmaticCallback({ + 'abcd@728x90:0': { + 'ecpm': 10, + 'creative_tag': 'hello', + 'tracking_url': 'http%3a%2f%2fhaso.pubmatic.com%2fads%2f9999%2fGRPBID%2f2.gif%3ftrackid%3d12345', + 'width': 728, + 'height': 90, + 'deal_channel': 5 + } + }, { + 'abcd@728x90:0': 'bidstatus;1;bid;10.0000;bidid;abcd@728x90:0;wdeal;PMERW36842' + }); + sinon.assert.called(bidmanager.addBidResponse); + expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('DIV_1'); + var theBid = bidmanager.addBidResponse.firstCall.args[1]; + expect(theBid.bidderCode).to.equal('pubmatic'); + expect(theBid.adSlot).to.equal('abcd@728x90:0'); + expect(theBid.cpm).to.equal(10); + expect(theBid.width).to.equal(728); + expect(theBid.height).to.equal(90); + expect(theBid.dealId).to.equal('PMERW36842'); + expect(theBid.dealChannel).to.equal('PREF'); + }); + + it('not empty response, without dealChannel', () => { + adapter.callBids(createBidderRequest({ + params: { + publisherId: 9999, + adSlot: 'abcd@728x90', + age: '20' + } + })); + $$PREBID_GLOBAL$$.handlePubmaticCallback({ + 'abcd@728x90': { + 'ecpm': 10, + 'creative_tag': 'hello', + 'tracking_url': 'http%3a%2f%2fhaso.pubmatic.com%2fads%2f9999%2fGRPBID%2f2.gif%3ftrackid%3d12345', + 'width': 728, + 'height': 90 + } + }, { + 'abcd@728x90': 'bidstatus;1;bid;10.0000;bidid;abcd@728x90:0;wdeal;PMERW36842' + }); + sinon.assert.called(bidmanager.addBidResponse); + expect(bidmanager.addBidResponse.firstCall.args[0]).to.equal('DIV_1'); + var theBid = bidmanager.addBidResponse.firstCall.args[1]; + expect(theBid.bidderCode).to.equal('pubmatic'); + expect(theBid.adSlot).to.equal('abcd@728x90'); + expect(theBid.cpm).to.equal(10); + expect(theBid.width).to.equal(728); + expect(theBid.height).to.equal(90); + expect(theBid.dealId).to.equal('PMERW36842'); + expect(theBid.dealChannel).to.equal(null); + }); + }); + }); +}); diff --git a/test/spec/modules/pulsepointLiteBidAdapter_spec.js b/test/spec/modules/pulsepointLiteBidAdapter_spec.js index 96f5c7a8d1f..9731164cd50 100644 --- a/test/spec/modules/pulsepointLiteBidAdapter_spec.js +++ b/test/spec/modules/pulsepointLiteBidAdapter_spec.js @@ -89,7 +89,7 @@ describe('PulsePoint Lite Adapter Tests', () => { }] }] }; - const bids = spec.interpretResponse(ortbResponse, request); + const bids = spec.interpretResponse({ body: ortbResponse }, request); expect(bids).to.have.lengthOf(1); // verify first bid const bid = bids[0]; @@ -104,7 +104,7 @@ describe('PulsePoint Lite Adapter Tests', () => { it('Verify full passback', () => { const request = spec.buildRequests(slotConfigs); - const bids = spec.interpretResponse(null, request) + const bids = spec.interpretResponse({ body: null }, request) expect(bids).to.have.lengthOf(0); }); @@ -171,7 +171,7 @@ describe('PulsePoint Lite Adapter Tests', () => { }] }] }; - const bids = spec.interpretResponse(ortbResponse, request); + const bids = spec.interpretResponse({ body: ortbResponse }, request); // verify bid const bid = bids[0]; expect(bid.cpm).to.equal(1.25); diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index 05748d85845..14981f198b6 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -1,231 +1,216 @@ -import {expect} from 'chai'; -import Adapter from '../../../modules/quantcastBidAdapter'; -import * as ajax from 'src/ajax'; -import bidManager from '../../../src/bidmanager'; -import adLoader from '../../../src/adloader'; - -describe('quantcast adapter', () => { - let bidsRequestedOriginal; - let adapter; - let sandbox; - let ajaxStub; - - const bidderRequest = { - bidderCode: 'quantcast', - requestId: '595ffa73-d78a-46c9-b18e-f99548a5be6b', - bidderRequestId: '1cc026909c24c8', - bids: [ - { - bidId: '2f7b179d443f14', - bidder: 'quantcast', - placementCode: 'div-gpt-ad-1438287399331-0', - sizes: [[300, 250], [300, 600]], - params: { - publisherId: 'test-publisher', - battr: [1, 2], - } - } - ] - }; +import * as utils from 'src/utils'; +import { expect } from 'chai'; +import { + QUANTCAST_CALLBACK_URL_TEST, + QUANTCAST_CALLBACK_URL, + QUANTCAST_NET_REVENUE, + QUANTCAST_TTL, + spec as qcSpec +} from '../../../modules/quantcastBidAdapter'; +import { newBidder } from '../../../src/adapters/bidderFactory'; + +describe('Quantcast adapter', () => { + const quantcastAdapter = newBidder(qcSpec); + let bidRequest; beforeEach(() => { - bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested; - $$PREBID_GLOBAL$$._bidsRequested = []; - - adapter = new Adapter(); - sandbox = sinon.sandbox.create(); - ajaxStub = sandbox.stub(ajax, 'ajax'); - }); - - afterEach(() => { - sandbox.restore(); - - $$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal; - }); - - describe('sizes', () => { - let bidderRequest = { - bidderCode: 'quantcast', + bidRequest = { + bidder: 'quantcast', + bidId: '2f7b179d443f14', requestId: '595ffa73-d78a-46c9-b18e-f99548a5be6b', bidderRequestId: '1cc026909c24c8', - bids: [ - { - bidId: '2f7b179d443f14', - bidder: 'quantcast', - placementCode: 'div-gpt-ad-1438287399331-0', - sizes: [[300, 250], [300, 600]], - params: { - publisherId: 'test-publisher', - battr: [1, 2], - } - } - ] + placementCode: 'div-gpt-ad-1438287399331-0', + 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 + }, + sizes: [[300, 250]] }; + }); - it('should not call server when empty input is provided', () => { - adapter.callBids({}); - sinon.assert.notCalled(ajaxStub); + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(quantcastAdapter.callBids).to.exist.and.to.be.a('function'); }); + }); - it('should call server once even when multiple sizes are passed', () => { - adapter.callBids(bidderRequest); - sinon.assert.calledOnce(ajaxStub); - - expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL); - expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function'); - expect(ajaxStub.firstCall.args[2]).to.include('div-gpt-ad-1438287399331-0'); - expect(ajaxStub.firstCall.args[2]).to.include('test-publisher'); - expect(ajaxStub.firstCall.args[2]).to.include('2f7b179d443f14'); - expect(ajaxStub.firstCall.args[3]).to.eql({method: 'POST', withCredentials: true}); + describe('`isBidRequestValid`', () => { + it('should return `false` when bid is not passed', () => { + expect(qcSpec.isBidRequestValid()).to.equal(false); }); - it('should call server once when one size is passed', () => { - bidderRequest.bids[0].sizes = [728, 90]; - adapter.callBids(bidderRequest); - sinon.assert.calledOnce(ajaxStub); + it('should return `false` when bid `mediaType` is `video`', () => { + const bidRequest = { mediaType: 'video' }; - expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL); - expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function'); - expect(ajaxStub.firstCall.args[3]).to.eql({method: 'POST', withCredentials: true}); + expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('should call server once when size is passed as string', () => { - bidderRequest.bids[0].sizes = '728x90'; - adapter.callBids(bidderRequest); - sinon.assert.calledOnce(ajaxStub); + it('should return `true` when bid contains required params', () => { + const bidRequest = { mediaType: 'banner' }; - expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL); - expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function'); - expect(ajaxStub.firstCall.args[3]).to.eql({method: 'POST', withCredentials: true}); + expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(true); }); + }); - it('should call server once when sizes are passed as a comma-separated string', () => { - bidderRequest.bids[0].sizes = '728x90,360x240'; - adapter.callBids(bidderRequest); - sinon.assert.calledOnce(ajaxStub); + describe('`buildRequests`', () => { + it('sends bid requests to Quantcast Canary Endpoint if `publisherId` is `test-publisher`', () => { + const requests = qcSpec.buildRequests([bidRequest]); + + switch (window.location.protocol) { + case 'https:': + expect(requests[0]['url']).to.equal( + `https://${QUANTCAST_CALLBACK_URL_TEST}:8443/qchb` + ); + break; + default: + expect(requests[0]['url']).to.equal( + `http://${QUANTCAST_CALLBACK_URL_TEST}:8080/qchb` + ); + break; + } + }); - expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL); - expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function'); - expect(ajaxStub.firstCall.args[3]).to.eql({method: 'POST', withCredentials: true}); + it('sends bid requests to Quantcast Global Endpoint for regular `publisherId`', () => { + const bidRequest = { + bidder: 'quantcast', + bidId: '2f7b179d443f14', + requestId: '595ffa73-d78a-46c9-b18e-f99548a5be6b', + bidderRequestId: '1cc026909c24c8', + placementCode: 'div-gpt-ad-1438287399331-0', + params: { + publisherId: 'regular-publisher', // REQUIRED - Publisher ID provided by Quantcast + battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3 + }, + sizes: [[300, 250]] + }; + const requests = qcSpec.buildRequests([bidRequest]); + + switch (window.location.protocol) { + case 'https:': + expect(requests[0]['url']).to.equal( + `https://${QUANTCAST_CALLBACK_URL}:8443/qchb` + ); + break; + default: + expect(requests[0]['url']).to.equal( + `http://${QUANTCAST_CALLBACK_URL}:8080/qchb` + ); + break; + } }); - }); - describe('multiple requests', () => { - let bidderRequest = { - bidderCode: 'quantcast', - requestId: '595ffa73-d78a-46c9-b18e-f99548a5be6b', - bidderRequestId: '1cc026909c24c8', - bids: [ - { - bidId: '2f7b179d443f14', - bidder: 'quantcast', - placementCode: 'div-gpt-ad-1438287399331-0', - sizes: [[300, 250]], - params: { - publisherId: 'test-publisher', - battr: [1, 2], - } - }, { - bidId: '2f7b179d443f15', - bidder: 'quantcast', - placementCode: 'div-gpt-ad-1438287399331-1', - sizes: [[300, 600]], - params: { - publisherId: 'test-publisher', - battr: [1, 2], - } - } - ] - }; + it('sends bid requests to Quantcast Header Bidding Endpoints via POST', () => { + const requests = qcSpec.buildRequests([bidRequest]); - it('request is fired twice for two bids', () => { - adapter.callBids(bidderRequest); - sinon.assert.calledTwice(ajaxStub); + expect(requests[0].method).to.equal('POST'); + }); - let firstReq = JSON.parse(ajaxStub.firstCall.args[2]); - expect(firstReq.requestId).to.eql('2f7b179d443f14'); - expect(firstReq.imp[0].placementCode).to.eql('div-gpt-ad-1438287399331-0'); + it('sends bid requests contains all the required parameters', () => { + const referrer = utils.getTopWindowUrl(); + const loc = utils.getTopWindowLocation(); + const domain = loc.hostname; - let secondReq = JSON.parse(ajaxStub.secondCall.args[2]); - expect(secondReq.requestId).to.eql('2f7b179d443f15'); - expect(secondReq.imp[0].placementCode).to.eql('div-gpt-ad-1438287399331-1'); + const requests = qcSpec.buildRequests([bidRequest]); + const expectedBidRequest = { + publisherId: 'test-publisher', + requestId: '2f7b179d443f14', + imp: [ + { + banner: { + battr: [1, 2], + sizes: [{ width: 300, height: 250 }] + }, + placementCode: 'div-gpt-ad-1438287399331-0', + bidFloor: 1e-10 + } + ], + site: { + page: loc.href, + referrer, + domain + }, + bidId: '2f7b179d443f14' + }; + + expect(requests[0].data).to.equal(JSON.stringify(expectedBidRequest)); }); }); - describe('handleQuantcastCB add bids to the manager', () => { - let firstBid; - let addBidReponseStub; - let bidsRequestedOriginal; - // respond - let bidderReponse = { - 'bidderCode': 'quantcast', - 'requestId': bidderRequest.requestId, - 'bids': [ + describe('`interpretResponse`', () => { + // The sample response is from https://wiki.corp.qc/display/adinf/QCX + 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: [ { - 'statusCode': 1, - 'placementCode': bidderRequest.bids[0].bidId, - 'cpm': 4.5, - 'ad': '<!DOCTYPE html>\n\n\n<div style="height: 250; width: 300; display: table-cell; vertical-align: middle;">\n<div style="width: 300px; margin-left: auto; margin-right: auto;"> \n\n <script src="https://adserver.adtechus.com/addyn/3.0/5399.1/2394397/0/-1/QUANTCAST;size=300x250;target=_blank;alias=;kvp36=;sub1=;kvl=;kvc=;kvs=300x250;kvi=;kva=;sub2=;rdclick=http://exch.quantserve.com/r?a=;labels=_qc.clk,_click.adserver.rtb,_click.rand.;rtbip=;rtbdata2=;redirecturl2=" type="text/javascript"></script>\n\n<img src="https://exch.quantserve.com/pixel/p_12345.gif?media=ad&p=&r=&rand=&labels=_qc.imp,_imp.adserver.rtb&rtbip=&rtbdata2=" style="display: none;" border="0" height="1" width="1" alt="Quantcast"/>\n\n</div>\n</div>', - 'width': 300, - 'height': 250 + statusCode: 1, + placementCode: 'imp1', // Changing this to placementCode to be reflective + cpm: 4.5, + currency: 'USD', + ad: + '<!DOCTYPE html><div style="height: 250; width: 300; display: table-cell; vertical-align: middle;"><div style="width: 300px; margin-left: auto; margin-right: auto;"><script src="https://adserver.adtechus.com/addyn/3.0/5399.1/2394397/0/-1/QUANTCAST;size=300x250;target=_blank;alias=;kvp36=;sub1=;kvl=;kvc=;kvs=300x250;kvi=;kva=;sub2=;rdclick=http://exch.quantserve.com/r?a=;labels=_qc.clk,_click.adserver.rtb,_click.rand.;rtbip=;rtbdata2=;redirecturl2=" type="text/javascript"></script><img src="https://exch.quantserve.com/pixel/p_12345.gif?media=ad&p=&r=&rand=&labels=_qc.imp,_imp.adserver.rtb&rtbip=&rtbdata2=" style="display: none;" border="0" height="1" width="1" alt="Quantcast"/></div></div>', + 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: + '<!DOCTYPE html><div style="height: 250; width: 300; display: table-cell; vertical-align: middle;"><div style="width: 300px; margin-left: auto; margin-right: auto;"><script src="https://adserver.adtechus.com/addyn/3.0/5399.1/2394397/0/-1/QUANTCAST;size=300x250;target=_blank;alias=;kvp36=;sub1=;kvl=;kvc=;kvs=300x250;kvi=;kva=;sub2=;rdclick=http://exch.quantserve.com/r?a=;labels=_qc.clk,_click.adserver.rtb,_click.rand.;rtbip=;rtbdata2=;redirecturl2=" type="text/javascript"></script><img src="https://exch.quantserve.com/pixel/p_12345.gif?media=ad&p=&r=&rand=&labels=_qc.imp,_imp.adserver.rtb&rtbip=&rtbdata2=" style="display: none;" border="0" height="1" width="1" alt="Quantcast"/></div></div>', + 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(`<html>`) @@ -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(`<html>`) @@ -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/s2sTesting_spec.js b/test/spec/modules/s2sTesting_spec.js index f829087a967..4ddd7278f4e 100644 --- a/test/spec/modules/s2sTesting_spec.js +++ b/test/spec/modules/s2sTesting_spec.js @@ -312,15 +312,15 @@ describe('s2sTesting', function () { const AST = CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING; function checkTargeting(bidder) { - var targeting = window.pbjs.bidderSettings[bidder][AST]; + var targeting = window.$$PREBID_GLOBAL$$.bidderSettings[bidder][AST]; var srcTargeting = targeting[targeting.length - 1]; expect(srcTargeting.key).to.equal(`hb_source_${bidder}`); expect(srcTargeting.val).to.be.a('function'); - expect(window.pbjs.bidderSettings[bidder].alwaysUseBid).to.be.true; + expect(window.$$PREBID_GLOBAL$$.bidderSettings[bidder].alwaysUseBid).to.be.true; } function checkNoTargeting(bidder) { - var bs = window.pbjs.bidderSettings; + var bs = window.$$PREBID_GLOBAL$$.bidderSettings; var targeting = bs[bidder] && bs[bidder][AST]; if (!targeting) { expect(targeting).to.be.undefined; @@ -332,22 +332,22 @@ describe('s2sTesting', function () { } function checkTargetingVal(bidResponse, expectedVal) { - var targeting = window.pbjs.bidderSettings[bidResponse.bidderCode][AST]; + var targeting = window.$$PREBID_GLOBAL$$.bidderSettings[bidResponse.bidderCode][AST]; var targetingFunc = targeting[targeting.length - 1].val; expect(targetingFunc(bidResponse)).to.equal(expectedVal); } beforeEach(() => { // set bidderSettings - window.pbjs.bidderSettings = {}; + window.$$PREBID_GLOBAL$$.bidderSettings = {}; }); it('should not set hb_source_<bidder> unless testing is on and includeSourceKvp is set', () => { config.setConfig({s2sConfig: {bidders: ['rubicon', 'appnexus']}}); - expect(window.pbjs.bidderSettings).to.eql({}); + expect(window.$$PREBID_GLOBAL$$.bidderSettings).to.eql({}); config.setConfig({s2sConfig: {bidders: ['rubicon', 'appnexus'], testing: true}}); - expect(window.pbjs.bidderSettings).to.eql({}); + expect(window.$$PREBID_GLOBAL$$.bidderSettings).to.eql({}); config.setConfig({s2sConfig: { bidders: ['rubicon', 'appnexus'], @@ -357,7 +357,7 @@ describe('s2sTesting', function () { appnexus: {bidSource: {server: 1}} } }}); - expect(window.pbjs.bidderSettings).to.eql({}); + expect(window.$$PREBID_GLOBAL$$.bidderSettings).to.eql({}); config.setConfig({s2sConfig: { bidders: ['rubicon', 'appnexus'], @@ -367,7 +367,7 @@ describe('s2sTesting', function () { appnexus: {includeSourceKvp: true} } }}); - expect(window.pbjs.bidderSettings).to.eql({}); + expect(window.$$PREBID_GLOBAL$$.bidderSettings).to.eql({}); }); it('should set hb_source_<bidder> if includeSourceKvp is set', () => { diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index b92dfe4d493..fdac8a84d8d 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -1,239 +1,127 @@ import { expect } from 'chai'; -import Adapter from '../../../modules/sharethroughBidAdapter'; -import bidManager from '../../../src/bidmanager'; -import bidfactory from '../../../src/bidfactory'; - -describe('sharethrough adapter', () => { - let adapter; - let sandbox; - let bidsRequestedOriginal; +import { sharethroughAdapterSpec } from 'modules/sharethroughBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const spec = newBidder(sharethroughAdapterSpec).getSpec(); +const bidderRequest = [ + { + bidder: 'sharethrough', + bidId: 'bidId1', + sizes: [[600, 300]], + placementCode: 'foo', + params: { + pkey: 'aaaa1111' + } + }, + { + bidder: 'sharethrough', + bidId: 'bidId2', + sizes: [[700, 400]], + placementCode: 'bar', + params: { + pkey: 'bbbb2222' + } + }]; +const prebidRequest = [{ + method: 'GET', + url: document.location.protocol + '//btlr.sharethrough.com' + '/header-bid/v1', + data: { + bidId: 'bidId', + placement_key: 'pKey' + } +}]; +const bidderResponse = { + body: { + 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994', + 'bidId': 'bidId1', + 'version': 1, + 'creatives': [{ + 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051', + 'cpm': 12.34, + 'creative': { + 'deal_id': 'aDealId', + 'creative_key': 'aCreativeId' + } + }], + 'stxUserId': '' + }, + header: { get: (header) => header } +}; + +describe('sharethrough adapter spec', () => { + describe('.code', () => { + it('should return a bidder code of sharethrough', () => { + expect(spec.code).to.eql('sharethrough'); + }); + }) - const bidderRequest = { - bidderCode: 'sharethrough', - bids: [ - { - bidder: 'sharethrough', - bidId: 'bidId1', - sizes: [[600, 300]], - placementCode: 'foo', - params: { - pkey: 'aaaa1111' - } - }, - { + describe('.isBidRequestValid', () => { + it('should return false if req has no pkey', () => { + const invalidBidRequest = { bidder: 'sharethrough', - bidId: 'bidId2', - sizes: [[700, 400]], - placementCode: 'bar', params: { - pkey: 'bbbb2222' + notPKey: 'abc123' } - } - ] - }; - - beforeEach(() => { - adapter = new Adapter(); - sandbox = sinon.sandbox.create(); - bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested; - $$PREBID_GLOBAL$$._bidsRequested = []; - }); - - afterEach(() => { - sandbox.restore(); - - $$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal; - }); - - describe('callBids', () => { - let firstBidUrl; - let secondBidUrl; - - beforeEach(() => { - sandbox.spy(adapter.str, 'ajax'); - }); - - it('should call ajax to make a request for each bid', () => { - adapter.callBids(bidderRequest); - - firstBidUrl = adapter.str.ajax.firstCall.args[0]; - secondBidUrl = adapter.str.ajax.secondCall.args[0]; - - sinon.assert.calledTwice(adapter.str.ajax); - - expect(firstBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId1&placement_key=aaaa1111&hbVersion=%24prebid.version%24&strVersion=1.2.0&hbSource=prebid&'); - expect(secondBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId2&placement_key=bbbb2222&hbVersion=%24prebid.version%24&strVersion=1.2.0&hbSource=prebid&'); - }); - }); - - describe('bid requests', () => { - let firstBid; - let secondBid; - let server; - let stubAddBidResponse; - let stubCreateBid; - - beforeEach(() => { - stubAddBidResponse = sandbox.stub(bidManager, 'addBidResponse'); - server = sinon.fakeServer.create(); - - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - adapter.str.placementCodeSet['foo'] = {}; - adapter.str.placementCodeSet['bar'] = {}; - // respond - - let bidderResponse1 = { - 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994', - 'bidId': 'bidId1', - 'creatives': [ - { - 'cpm': 12.34, - 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051', - 'version': 1 - } - ], - 'stxUserId': '' - }; - - let bidderResponse2 = { - 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994', - 'bidId': 'bidId2', - 'creatives': [ - { - 'cpm': 12.35, - 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051', - 'version': 1 - } - ], - 'stxUserId': '' }; - - server.respondWith(/aaaa1111/, JSON.stringify(bidderResponse1)); - server.respondWith(/bbbb2222/, JSON.stringify(bidderResponse2)); - adapter.callBids(bidderRequest); - - server.respond(); - - firstBid = bidManager.addBidResponse.firstCall.args[1]; - secondBid = bidManager.addBidResponse.secondCall.args[1]; + expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); }); - afterEach(() => { - server.restore(); - }); - - it('should add a bid object for each bid', () => { - sinon.assert.calledTwice(bidManager.addBidResponse); - }); - - it('should pass the correct placement code as first param', () => { - let firstPlacementCode = bidManager.addBidResponse.firstCall.args[0]; - let secondPlacementCode = bidManager.addBidResponse.secondCall.args[0]; - - expect(firstPlacementCode).to.eql('foo'); - expect(secondPlacementCode).to.eql('bar'); - }); - - it('should include the bid request bidId as the adId', () => { - expect(firstBid).to.have.property('adId', 'bidId1'); - expect(secondBid).to.have.property('adId', 'bidId2'); - }); - - it('should have a good statusCode', () => { - expect(firstBid.getStatusCode()).to.eql(1); - expect(secondBid.getStatusCode()).to.eql(1); - }); - - it('should add the CPM to the bid object', () => { - expect(firstBid).to.have.property('cpm', 12.34); - expect(secondBid).to.have.property('cpm', 12.35); - }); - - it('should add the bidder code to the bid object', () => { - expect(firstBid).to.have.property('bidderCode', 'sharethrough'); - expect(secondBid).to.have.property('bidderCode', 'sharethrough'); + it('should return false if req has wrong bidder code', () => { + const invalidBidRequest = { + bidder: 'notSharethrough', + params: { + notPKey: 'abc123' + } + }; + expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false); }); - it('should include the ad on the bid object', () => { - expect(firstBid).to.have.property('ad'); - expect(secondBid).to.have.property('ad'); - }); + it('should return true if req is correct', () => { + expect(spec.isBidRequestValid(bidderRequest[0])).to.eq(true); + expect(spec.isBidRequestValid(bidderRequest[1])).to.eq(true); + }) + }); - it('should include the size on the bid object', () => { - expect(firstBid).to.have.property('width', 600); - expect(firstBid).to.have.property('height', 300); - expect(secondBid).to.have.property('width', 700); - expect(secondBid).to.have.property('height', 400); - }); + describe('.buildRequests', () => { + it('should return an array of requests', () => { + const bidRequests = spec.buildRequests(bidderRequest); - it('should include the pkey', () => { - expect(firstBid).to.have.property('pkey', 'aaaa1111'); - expect(secondBid).to.have.property('pkey', 'bbbb2222'); + expect(bidRequests[0].url).to.eq( + 'http://btlr.sharethrough.com/header-bid/v1'); + expect(bidRequests[1].url).to.eq( + 'http://btlr.sharethrough.com/header-bid/v1') + expect(bidRequests[0].method).to.eq('GET'); }); + }); - describe('when bidResponse string cannot be JSON parsed', () => { - beforeEach(() => { - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - adapter.str.placementCodeSet['foo'] = {}; - - server.respondWith(/aaaa1111/, 'non JSON string'); - adapter.callBids(bidderRequest); - - server.respond(); - }); - - afterEach(() => { - server.restore(); - stubAddBidResponse.reset(); - }); - - it('should add a bid response', () => { - sinon.assert.called(bidManager.addBidResponse); - }); - - it('should set bidder code on invalid bid response', () => { - let bidResponse = bidManager.addBidResponse.firstCall.args[1] - expect(bidResponse).to.have.property('bidderCode', 'sharethrough') - }); + describe('.interpretResponse', () => { + it('returns a correctly parsed out response', () => { + expect(spec.interpretResponse(bidderResponse, prebidRequest[0])[0]).to.include( + { + width: 0, + height: 0, + cpm: 12.34, + creativeId: 'aCreativeId', + deal_id: 'aDealId', + currency: 'USD', + netRevenue: true, + ttl: 360, + }); }); - describe('when no fill', () => { - beforeEach(() => { - $$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest); - adapter.str.placementCodeSet['foo'] = {}; - - let bidderResponse1 = { - 'adserverRequestId': '40b6afd5-6134-4fbb-850a-bb8972a46994', - 'bidId': 'bidId1', - 'creatives': [ - { - 'cpm': 12.34, - 'auctionWinId': 'b2882d5e-bf8b-44da-a91c-0c11287b8051', - 'version': 1 - } - ], - 'stxUserId': '' - }; - - server.respondWith(/aaaa1111/, JSON.stringify(bidderResponse1)); - adapter.callBids(bidderRequest); - - server.respond(); - }); - - afterEach(() => { - server.restore(); - stubAddBidResponse.reset(); - }); - - it('should add a bid response', () => { - sinon.assert.called(bidManager.addBidResponse); - }); - - it('should set bidder code on invalid bid response', () => { - let bidResponse = bidManager.addBidResponse.firstCall.args[1] - expect(bidResponse).to.have.property('bidderCode', 'sharethrough') - }); + it('correctly sends back a sfp script tag', () => { + const adMarkup = spec.interpretResponse(bidderResponse, prebidRequest[0])[0].ad; + const resp = btoa(JSON.stringify(bidderResponse)); + + expect(adMarkup).to.match( + /data-str-native-key="pKey" data-stx-response-name=\"str_response_bidId\"/); + expect(!!adMarkup.indexOf(resp)).to.eql(true); + expect(adMarkup).to.match( + /<script src="\/\/native.sharethrough.com\/assets\/sfp-set-targeting.js"><\/script>/); + expect(adMarkup).to.match( + /sfp_js.src = "\/\/native.sharethrough.com\/assets\/sfp.js";/); + expect(adMarkup).to.match( + /window.top.document.getElementsByTagName\('body'\)\[0\].appendChild\(sfp_js\);/) }); }); }); 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': '<div>test content 1</div>', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '<div>test content 2</div>', 'auid': 44, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '<div>test content 4</div>', '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': '<div>test content 1</div>', + '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': '<div>test content 1</div>', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '5703af74d0472a', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '<div>test content 1</div>', + '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': '<div>test content 2</div>', + '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: '<div>test content 1</div>', auid: 5, h: 90, w: 728}], seat: '1'}, - {bid: [{price: 0, auid: 6, h: 250, w: 300}], seat: '1'}, - {bid: [{price: 0, adm: '<div>test content 3</div>', h: 250, w: 300}], seat: '1'}, - undefined, - {bid: [], seat: '1'}, - {seat: '1'}, - {bid: [{price: 0, adm: '<div>test content 7</div>', 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</' + 'div>","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('<div>test content 1</div>'); - 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/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index 249111be6ea..1e7a80aaff8 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -1,122 +1,241 @@ -import Adapter from '../../../modules/underdogmediaBidAdapter'; -import bidManager from '../../../src/bidmanager'; -import adloader from '../../../src/adloader'; - -import { - expect -} from 'chai'; - -describe('underdogmedia adapter test', () => { - let adapter; - let server; - - // The third bid here is an invalid site id and should return a 'no-bid'. - - var bidderRequest = { - bidderCode: 'underdogmedia', - bids: [{ - bidder: 'underdogmedia', - adUnitCode: 'foo', - sizes: [ - [728, 90] - ], - params: { - siteId: '10272' - } - }, - { - bidder: 'underdogmedia', - adUnitCode: 'bar', - sizes: [ - [300, 250] - ], - params: { - siteId: '10272', - subId: 'TEST_SUBID' - } - }, - { - bidder: 'underdogmedia', - adUnitCode: 'nothing', - sizes: [160, 600], - params: { - siteId: '31337' - } - } - ] - }; - var response = { - 'mids': [{ - 'width': 728, - 'notification_url': '//udmserve.net/notification_url', - 'height': 90, - 'cpm': 2.5, - 'ad_code_html': 'Ad HTML for site ID 10272 size 728x90' - }, - { - 'width': 300, - 'notification_url': '//udmserve.net/notification_url', - 'height': 250, - 'cpm': 2.0, - 'ad_code_html': 'Ad HTML for site ID 10272 size 300x250' - } - ] - }; +import { expect } from 'chai'; +import { spec } from 'modules/underdogmediaBidAdapter'; + +describe('UnderdogMedia adapter', () => { + let bidRequests; beforeEach(() => { - adapter = new Adapter(); + bidRequests = [ + { + bidder: 'underdogmedia', + params: { + siteId: 12143 + }, + adUnitCode: '/19968336/header-bid-tag-1', + sizes: [[300, 250], [300, 600], [728, 90], [160, 600], [320, 50]], + bidId: '23acc48ad47af5', + requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; }); - afterEach(() => {}); + describe('implementation', () => { + describe('for requests', () => { + it('should accept valid bid', () => { + let validBid = { + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + sizes: [[300, 250], [300, 600]] + }; + const isValid = spec.isBidRequestValid(validBid); - describe('adding bids to the manager', () => { - let firstBid; - let secondBid; - let thirdBid; + expect(isValid).to.equal(true); + }); - beforeEach(() => { - sinon.stub(bidManager, 'addBidResponse'); - sinon.stub(adloader, 'loadScript'); + it('should reject invalid bid missing sizes', () => { + let invalidBid = { + bidder: 'underdogmedia', + params: { + siteId: '12143', + } + }; + const isValid = spec.isBidRequestValid(invalidBid); - adapter.callBids(bidderRequest); - $$PREBID_GLOBAL$$.handleUnderdogMediaCB(JSON.parse(JSON.stringify(response))); - firstBid = bidManager.addBidResponse.firstCall.args[1]; - secondBid = bidManager.addBidResponse.secondCall.args[1]; - thirdBid = bidManager.addBidResponse.thirdCall.args[1]; - }); + expect(isValid).to.equal(false); + }); - afterEach(() => { - bidManager.addBidResponse.restore(); - adloader.loadScript.restore(); - }); + it('should reject invalid bid missing siteId', () => { + let invalidBid = { + bidder: 'underdogmedia', + params: {}, + sizes: [[300, 250], [300, 600]] + }; + const isValid = spec.isBidRequestValid(invalidBid); - it('will add a bid object for each bid', () => { - sinon.assert.calledThrice(bidManager.addBidResponse); - }); + expect(isValid).to.equal(false); + }); - it('will add the ad html to the bid object', () => { - expect(firstBid).to.have.property('ad').includes('Ad HTML for site ID 10272 size 728x90'); - expect(secondBid).to.have.property('ad').includes('Ad HTML for site ID 10272 size 300x250').and.includes('TEST_SUBID'); - expect(thirdBid).to.not.have.property('ad'); - }); + it('request data should contain sid', () => { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250]], + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + requestId: '10b327aa396609', + adUnitCode: '/123456/header-bid-tag-1' + } + ]; + const request = spec.buildRequests(bidRequests); - it('will have the right size attached', () => { - expect(firstBid).to.have.property('width', 728); - expect(firstBid).to.have.property('height', 90); - expect(secondBid).to.have.property('width', 300); - expect(secondBid).to.have.property('height', 250); - }); + expect(request.data).to.have.string('sid=12143'); + }); + + it('request data should contain sizes', () => { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250], [728, 90]], + bidder: 'underdogmedia', + params: { + siteId: '12143' + }, + requestId: '10b327aa396609', + adUnitCode: '/123456/header-bid-tag-1' + } + ]; + const request = spec.buildRequests(bidRequests); - it('will add the CPM to the bid object', () => { - expect(firstBid).to.have.property('cpm', 2.5); - expect(secondBid).to.have.property('cpm', 2.0); - expect(thirdBid).to.not.have.property('cpm'); + expect(request.data).to.have.string('sizes=300x250,728x90'); + }); }); - it('will add the bidder code to the bid object', () => { - expect(firstBid).to.have.property('bidderCode', 'underdogmedia'); - expect(secondBid).to.have.property('bidderCode', 'underdogmedia'); - expect(thirdBid).to.have.property('bidderCode', 'underdogmedia'); + describe('bid responses', () => { + it('should return complete bid response', () => { + let serverResponse = { + body: { + mids: [ + { + ad_code_html: 'ad_code_html', + cpm: 2.5, + height: '600', + mid: '32634', + notification_url: 'notification_url', + tid: '4', + width: '160' + }, + { + ad_code_html: 'ad_code_html', + cpm: 2.5, + height: '250', + mid: '32633', + notification_url: 'notification_url', + tid: '2', + width: '300' + }, + ] + } + }; + const request = spec.buildRequests(bidRequests); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(2); + + expect(bids[0].bidderCode).to.equal('underdogmedia'); + expect(bids[0].cpm).to.equal(2.5); + expect(bids[0].width).to.equal('160'); + expect(bids[0].height).to.equal('600'); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].creativeId).to.equal('32634'); + expect(bids[0].currency).to.equal('USD'); + }); + + it('should return empty bid response if mids empty', () => { + let serverResponse = { + body: { + mids: [] + } + }; + const request = spec.buildRequests(bidRequests); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response on incorrect size', () => { + let serverResponse = { + body: { + mids: [ + { + ad_code_html: 'ad_code_html', + cpm: 2.5, + height: '123', + mid: '32634', + notification_url: 'notification_url', + tid: '4', + width: '160' + } + ] + } + }; + const request = spec.buildRequests(bidRequests); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response on 0 cpm', () => { + let serverResponse = { + body: { + mids: [ + { + ad_code_html: 'ad_code_html', + cpm: 0, + height: '600', + mid: '32634', + notification_url: 'notification_url', + tid: '4', + width: '160' + } + ] + } + }; + const request = spec.buildRequests(bidRequests); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response if no ad in response', () => { + let serverResponse = { + body: { + mids: [ + { + ad_code_html: '', + cpm: 2.5, + height: '600', + mid: '32634', + notification_url: 'notification_url', + tid: '4', + width: '160' + } + ] + } + }; + const request = spec.buildRequests(bidRequests); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(0); + }); + + it('ad html string should contain the notification urls', () => { + let serverResponse = { + body: { + mids: [ + { + ad_code_html: 'ad_cod_html', + cpm: 2.5, + height: '600', + mid: '32634', + notification_url: 'notification_url', + tid: '4', + width: '160' + } + ] + } + }; + const request = spec.buildRequests(bidRequests); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids[0].ad).to.have.string('notification_url'); + expect(bids[0].ad).to.have.string(';style=adapter'); + }); }); }); }); diff --git a/test/spec/modules/vertamediaBidAdapter_spec.js b/test/spec/modules/vertamediaBidAdapter_spec.js index 11c29dafad0..d505472dea1 100644 --- a/test/spec/modules/vertamediaBidAdapter_spec.js +++ b/test/spec/modules/vertamediaBidAdapter_spec.js @@ -1,141 +1,111 @@ -import { expect } from 'chai'; -import Adapter from 'modules/vertamediaBidAdapter'; -import bidmanager from 'src/bidmanager'; - -const ENDPOINT = 'http://rtb.vertamedia.com/hb/?aid=22489&w=640&h=480&domain=localhost'; +import {expect} from 'chai'; +import {spec} from 'modules/vertamediaBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; +const ENDPOINT = '//rtb.vertamedia.com/hb/'; const REQUEST = { - 'bidderCode': 'vertamedia', - 'requestId': 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + 'bidder': 'vertamedia', + 'params': { + 'aid': 12345 + }, 'bidderRequestId': '7101db09af0db2', - 'bids': [ - { - 'bidder': 'vertamedia', - 'params': { - aid: 22489, - placementId: '123456' - }, - 'placementCode': '/19968336/header-bid-tag1', - 'sizes': [640, 480], - 'bidId': '84ab500420319d', - 'bidderRequestId': '7101db09af0db2', - 'requestId': 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6' - } - ], - 'start': 1469479810130 + 'auctionId': '2e41f65424c87c', + 'adUnitCode': 'adunit-code', + 'bidId': '84ab500420319d', + 'sizes': [640, 480] }; -var RESPONSE = { - 'source': { - 'aid': 22489, - 'pubId': 18016, - 'sid': '0' - }, - 'bids': [ - { - 'cmpId': 9541, - 'cpm': 4.5, - 'url': 'http://rtb.vertamedia.com/vast?adid=BFDB9CC0038AD918', - 'cur': 'USD' - } + +const serverResponse = { + 'source': {'aid': 12345, 'pubId': 54321}, + 'bids': [{ + 'vastUrl': 'http://rtb.vertamedia.com/vast/?adid=44F2AEB9BFC881B3', + 'descriptionUrl': '44F2AEB9BFC881B3', + 'requestId': '2e41f65424c87c', + 'url': '44F2AEB9BFC881B3', + 'creative_id': 342516, + 'cmpId': 342516, + 'height': 480, + 'cur': 'USD', + 'width': 640, + 'cpm': 0.9 + } ] }; -describe('VertamediaAdater', () => { - let adapter; - - beforeEach(() => adapter = new Adapter()); - - describe('request function', () => { - let xhr; - let requests; - - beforeEach(() => { - xhr = sinon.useFakeXMLHttpRequest(); - requests = []; - xhr.onCreate = request => requests.push(request); - }); - - afterEach(() => xhr.restore()); +describe('vertamediaBidAdapter', () => { + const adapter = newBidder(spec); + describe('inherited functions', () => { it('exists and is a function', () => { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); + }); - it('requires paramters to make request', () => { - adapter.callBids({}); - expect(requests).to.be.empty; - }); - - it('requires member && invCode', () => { - let backup = REQUEST.bids[0].params; - REQUEST.bids[0].params = {member: 1234}; - adapter.callBids(REQUEST); - expect(requests).to.be.empty; - REQUEST.bids[0].params = backup; + describe('isBidRequestValid', () => { + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(REQUEST)).to.equal(true); }); - it('sends bid request to ENDPOINT via POST', () => { - adapter.callBids(REQUEST); - expect(requests[0].url).to.equal(ENDPOINT); - expect(requests[0].method).to.equal('GET'); + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, REQUEST); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('response handler', () => { - let server; + describe('buildRequests', () => { + let bidRequests = [REQUEST]; - beforeEach(() => { - server = sinon.fakeServer.create(); - sinon.stub(bidmanager, 'addBidResponse'); - }); + const request = spec.buildRequests(bidRequests, {}); - afterEach(() => { - server.restore(); - bidmanager.addBidResponse.restore(); + it('sends bid request to ENDPOINT via GET', () => { + expect(request[0].method).to.equal('GET'); + }); + it('sends bid request to correct ENDPOINT', () => { + expect(request[0].url).to.equal(ENDPOINT); }); - it('registers bids', () => { - server.respondWith(JSON.stringify(RESPONSE)); + it('sends correct bid parameters', () => { + const bid = Object.assign({}, request[0].data); + delete bid.domain; - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + const eq = { + callbackId: '84ab500420319d', + aid: 12345, + w: 640, + h: 480 + }; - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property('statusMessage', 'Bid available'); - expect(response).to.have.property('cpm', 4.5); + expect(bid).to.deep.equal(eq); }); + }); - it('handles nobid responses', () => { - server.respondWith(JSON.stringify({ - aid: 356465468, - w: 640, - h: 480, - domain: 'localhost' - })); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); - - const response = bidmanager.addBidResponse.firstCall.args[1]; - expect(response).to.have.property( - 'statusMessage', - 'Bid returned empty or error response' - ); + describe('interpretResponse', () => { + let bidderRequest = {bidderCode: 'bidderCode'}; + it('should get correct bid response', () => { + const result = spec.interpretResponse({body: serverResponse}, {bidderRequest}); + const eq = [{ + vastUrl: 'http://rtb.vertamedia.com/vast/?adid=44F2AEB9BFC881B3', + descriptionUrl: '44F2AEB9BFC881B3', + requestId: '2e41f65424c87c', + creativeId: 342516, + mediaType: 'video', + netRevenue: true, + currency: 'USD', + height: 480, + width: 640, + ttl: 3600, + cpm: 0.9 + }]; + + expect(result).to.deep.equal(eq); }); - it('handles JSON.parse errors', () => { - server.respondWith(''); - - adapter.callBids(REQUEST); - server.respond(); - sinon.assert.calledOnce(bidmanager.addBidResponse); + it('handles nobid responses', () => { + const nobidServerResponse = {bids: []}; + const nobidResult = spec.interpretResponse({body: nobidServerResponse}, {bidderRequest}); - expect(bidmanager.addBidResponse.firstCall.args[1]).to.have.property( - 'statusMessage', - 'Bid returned empty or error response' - ); + expect(nobidResult.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 e91ddcf39a4..e621f1fb329 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); @@ -262,20 +263,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); @@ -289,7 +295,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', @@ -320,16 +328,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({ @@ -348,6 +361,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', () => { @@ -364,7 +378,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', () => { @@ -384,6 +401,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..a08abaee847 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 () { @@ -653,6 +658,20 @@ describe('Utils', function () { }); }); + describe('createContentToExecuteExtScriptInFriendlyFrame', function () { + it('should return empty string if url is not passed', function () { + var output = utils.createContentToExecuteExtScriptInFriendlyFrame(); + assert.equal(output, ''); + }); + + it('should have URL in returned value if url is passed', function () { + var url = 'https://abcd.com/service?a=1&b=2&c=3'; + var output = utils.createContentToExecuteExtScriptInFriendlyFrame(url); + var expected = `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><base target="_top" /><script>inDapIF=true;</script></head><body><!--PRE_SCRIPT_TAG_MACRO--><script src="${url}"></script><!--POST_SCRIPT_TAG_MACRO--></body></html>`; + assert.equal(output, expected); + }); + }); + describe('getDefinedParams', () => { it('builds an object consisting of defined params', () => { const adUnit = { 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: '<xml>vast</xml>' }); + + expect(valid).to.be(false); + }); + it('validates valid outstream bids', () => { sinon.stub(utils, 'getBidRequest', () => ({ bidder: 'appnexusAst', diff --git a/webpack.conf.js b/webpack.conf.js index 68db3a389f2..38f4e5dadd7 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -19,7 +19,7 @@ module.exports = { ], }, output: { - jsonpFunction: 'pbjsChunk' + jsonpFunction: prebid.globalVarName+"Chunk" }, module: { rules: [ 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"