diff --git a/src/adapters/hiromedia.js b/src/adapters/hiromedia.js index 856ee629060..8bef4bc6cd3 100644 --- a/src/adapters/hiromedia.js +++ b/src/adapters/hiromedia.js @@ -1,24 +1,21 @@ -/* jslint white:true, browser:true, single: true */ -/* global $$PREBID_GLOBAL$$, require, module */ - -/** +/** * Adapter for HIRO Media * * @module HiroMediaAdapter * - * @requires src/adloader + * @requires src/ajax * @requires src/bidfactory * @requires src/bidmanager * @requires src/constants * @requires src/utils */ -var adloader = require('src/adloader'); +var Ajax = require('src/ajax'); var bidfactory = require('src/bidfactory'); var bidmanager = require('src/bidmanager'); var utils = require('src/utils'); var STATUS = require('src/constants').STATUS; -var HiroMediaAdapter = function HiroMediaAdapter() { +var HiroMediaAdapter = function HiroMediaAdapter() { 'use strict'; /** @@ -43,7 +40,7 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * Default bid param values * * @memberof module:HiroMediaAdapter~ - * @constant {module:HiroMediaAdapter~bidParams} + * @constant {array.} * @private */ var REQUIRED_BID_PARAMS = ['accountId']; @@ -60,16 +57,17 @@ var HiroMediaAdapter = function HiroMediaAdapter() { }; /** - * Storage for bid objects. - * - * Bids need to be stored between requests and response since the response - * is a global callback. + * Returns true if the given value is `undefined` * * @memberof module:HiroMediaAdapter~ - * @var {array.} * @private + * + * @param {*} value value to check + * @return {boolean} true if the given value is `undefined`, false otherwise */ - var _bidStorage = []; + function isUndefined(value) { + return typeof value === 'undefined'; + } /** * Call bidmanager.addBidResponse @@ -80,14 +78,14 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * @memberof module:HiroMediaAdapter~ * @private * - * @param {module:HiroMediaAdapter~bidInfo} bidInfo bid object wrapper to respond for + * @param {object} bid bid object connected to the response * @param {object|boolean} [bidResponse] response object for bid, if not * set the response will be an empty bid response. */ - function addBidResponse(bidInfo, bidResponse) { - var placementCode = bidInfo.bid.placementCode; + function addBidResponse(bid, bidResponse) { + var placementCode = bid.placementCode; var bidStatus = bidResponse ? STATUS.GOOD : STATUS.NO_BID; - var bidObject = bidfactory.createBid(bidStatus, bidInfo.bid); + var bidObject = bidfactory.createBid(bidStatus, bid); bidObject.bidderCode = BIDDER_CODE; @@ -99,7 +97,7 @@ var HiroMediaAdapter = function HiroMediaAdapter() { } utils.logMessage('hiromedia.callBids, addBidResponse for ' + placementCode + ' status: ' + bidStatus); - bidmanager.addBidResponse(placementCode, bidObject); + bidmanager.addBidResponse(placementCode, bidObject); } /** @@ -121,35 +119,19 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * @memberof module:HiroMediaAdapter~ * @private * - * @param {object} response [description] + * @param {object} response bid response object + * @param {object} bid bid object connected to response */ - function handleResponse(response) { - _bidStorage.filter(function (bidInfo) { - return bidInfo.batchKey === response.batchKey; - }).forEach(function (bidInfo) { - // Sample the bid responses according to `response.chance`, - // if `response.chance` is not provided, sample at 100%. - if (response.chance === undefined || checkChance(response.chance)) { - addBidResponse(bidInfo, response); - } else { - addBidResponse(bidInfo, false); - } - }); + function handleResponse(response, bid) { + // Sample the bid responses according to `response.chance`, + // if `response.chance` is not provided, sample at 100%. + if (isUndefined(response.chance) || checkChance(response.chance)) { + addBidResponse(bid, response); + } else { + addBidResponse(bid, false); + } } - /** - * Call {@linkcode module:HiroMediaAdapter~handleResponse} for valid responses - * - * @global - * - * @param {object} [response] the response from the server - */ - $$PREBID_GLOBAL$$.hiromedia_callback = function (response) { - if (response && response.batchKey) { - handleResponse(response); - } - }; - /** * Find browser name and version * @@ -161,7 +143,7 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * @return {module:HiroMediaAdapter~browserInfo} object containing name and version of browser * or empty strings. */ - function getBrowser() { + function getBrowser() { var ua = navigator.userAgent; var browsers = [{ name: 'Mobile', @@ -184,7 +166,7 @@ var HiroMediaAdapter = function HiroMediaAdapter() { var name = ''; var version = ''; - browsers.some(function (browser) { + browsers.some(function (browser) { var nameSearch = browser.stringSearch || browser.name; var defaultVersionSearch = nameSearch + '\\/(\\d+)'; var versionSearch = browser.versionSearch || defaultVersionSearch; @@ -197,13 +179,13 @@ var HiroMediaAdapter = function HiroMediaAdapter() { version = versionMatch && versionMatch[1]; } return true; - } + } }); return { name: name, version: version - }; + }; } /** @@ -214,10 +196,10 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * * @return {string} domain of top context url. */ - function getDomain() { + function getDomain() { var a = document.createElement('a'); a.href = utils.getTopWindowUrl(); - return a.hostname; + return a.hostname; } /** @@ -235,97 +217,49 @@ var HiroMediaAdapter = function HiroMediaAdapter() { } /** - * Calculate and return a batchKey key for a bid - * - * Bid of similar placement can have similar responses, - * we can calculate a key based on the variant properties - * of a bid which can share the same response + * Build a {@linkcode module:HiroMediaAdapter~bidInfo|bidInfo} object based on a + * bid sent to {@linkcode module:HiroMediaAdapter#callBids|callBids} * * @memberof module:HiroMediaAdapter~ * @private * - * @param {module:HiroMediaAdapter~bidInfo} bidInfo bid information - * @return {string} batch key for bid + * @param {object} bid bid from `Prebid.js` + * @return {module:HiroMediaAdapter~bidInfo} information for bid request */ - function getBatchKey(bidInfo) { - var bidParams = bidInfo.bidParams; - var batchParams = [ - bidParams.bidUrl, - bidParams.accountId, - bidInfo.selectedSize, - bidInfo.additionalSizes - ]; - - return batchParams.join('-'); + function processBid(bid) { + var sizes = utils.parseSizesInput(bid.sizes); + var bidParams = defaultParams(bid.params); + var hasValidBidRequest = utils.hasValidBidRequest(bidParams, REQUIRED_BID_PARAMS, BIDDER_CODE); + var shouldBid = hasValidBidRequest; + var bidInfo = { + bidParams: bidParams, + shouldBid: shouldBid, + selectedSize: sizes[0], + additionalSizes: sizes.slice(1).join(',') + }; + + return bidInfo; } /** - * Build a set of {@linkcode module:HiroMediaAdapter~bidInfo|bidInfo} objects based on the - * bids sent to {@linkcode module:HiroMediaAdapter#callBids|callBids} + * Wrapper around `JSON.parse()` that returns false on error * * @memberof module:HiroMediaAdapter~ * @private * - * @param {object} bids bids sent from `Prebid.js` - * @return {array.} wrapped bids + * @param {string} text potential JSON string to convert to object + * @return {object|boolean} object parsed from text or `false` in case of and error */ - function processBids(bids) { - var result = []; - - if (bids) { - utils.logMessage('hiromedia.processBids, processing ' + bids.length + ' bids'); - - bids.forEach(function (bid) { - var sizes = utils.parseSizesInput(bid.sizes); - var bidParams = defaultParams(bid.params); - var hasValidBidRequest = utils.hasValidBidRequest(bidParams, REQUIRED_BID_PARAMS, BIDDER_CODE); - var shouldBid = hasValidBidRequest; - var bidInfo = { - bid: bid, - bidParams: bidParams, - shouldBid: shouldBid, - selectedSize: sizes[0], - additionalSizes: sizes.slice(1).join(',') - }; - - if (shouldBid) { - bidInfo.batchKey = getBatchKey(bidInfo); - } + function tryJson(text) { + var object = false; - result.push(bidInfo); - }); + try { + object = JSON.parse(text); + } catch (ignore) { + // Ignored } - return result; - } - - /** - * Send a bid request to the bid server endpoint - * - * Calls `adLoader.loadScript` - * - * @memberof module:HiroMediaAdapter~ - * @private - * - * @param {string} url base url, can already contain query parameters - * @param {object} requestParams parameters to add to query - */ - function sendBidRequest(url, requestParams) { - if (requestParams) { - if (url.indexOf('?') !== -1) { - url = url + '&'; - } else { - url = url + '?'; - } - - Object.keys(requestParams).forEach(function (key) { - url = utils.tryAppendQueryString(url, key, requestParams[key]); - }); - } - - utils.logMessage('hiromedia.callBids, url:' + url); - - adloader.loadScript(url); + return object; } /** @@ -335,56 +269,65 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * * @param {object} params placement and bid data from `Prebid.js` */ - function _callBids(params) { + function _callBids(params) { var browser = getBrowser(); var domain = getDomain(); - var bidsRequested = {}; + var bids = params && params.bids; + var ajaxOptions = { + method: 'GET', + withCredentials: true + }; + + // Fixed data, shared by all requests + var fixedRequest = { + adapterVersion: ADAPTER_VERSION, + browser: browser.name, + browserVersion: browser.version, + domain: domain + }; utils.logMessage('hiromedia.callBids'); - if (params) { - // Processed bids are stored in the adapter scope - _bidStorage = processBids(params.bids); - } else { - // Ensure we don't run on stale data - _bidStorage = []; - } - - if (_bidStorage.length) { - // Loop over processed bids and send a request if a request for the bid - // batchKey has not been sent. - _bidStorage.forEach(function (bidInfo) { - var bid = bidInfo.bid; - var batchKey = bidInfo.batchKey; + if (bids && bids.length) { + bids.forEach(function (bid) { + var bidInfo = processBid(bid); var bidParams = bidInfo.bidParams; - utils.logMessage('hiromedia.callBids, bidInfo ' + bid.placementCode + ' ' + bidInfo.shouldBid); - if (bidInfo.shouldBid) { var url = bidParams.bidUrl; - - if (!bidsRequested[batchKey]) { - bidsRequested[batchKey] = true; - - sendBidRequest(url, { - adapterVersion: ADAPTER_VERSION, - callback: '$$PREBID_GLOBAL$$.hiromedia_callback', - batchKey: batchKey, - placementCode: bid.placementCode, - accountId: bidParams.accountId, - browser: browser.name, - browserVersion: browser.version, - domain: domain, - selectedSize: bidInfo.selectedSize, - additionalSizes: bidInfo.additionalSizes - }); - } - } else { + var requestParams = Object.assign({}, fixedRequest, bidInfo.bidParams, { + placementCode: bid.placementCode, + selectedSize: bidInfo.selectedSize, + additionalSizes: bidInfo.additionalSizes + }); + + Object.keys(requestParams).forEach(function (key) { + if (requestParams[key] === '' || isUndefined(requestParams[key])) { + delete requestParams[key]; + } + }); + + utils.logMessage('hiromedia.callBids, bid request ' + url + ' ' + JSON.stringify(bidInfo.bidRequest)); + + Ajax.ajax(url, { + + success: function(responseText) { + var response = tryJson(responseText); + handleResponse(response, bid); + }, + + error: function(err, xhr) { + utils.logError('hiromedia.callBids, bid request error', xhr.status, err); + addBidResponse(bid, false); + } + + }, requestParams, ajaxOptions); + } else { // No bid - addBidResponse(bidInfo, false); - } - }); - } + addBidResponse(bid, false); + } + }); + } } return { @@ -409,10 +352,8 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * @typedef {object} module:HiroMediaAdapter~bidInfo * @private * - * @property {object} bid original bid passed to #callBids * @property {string} selectedSize the first size in the the placement sizes array * @property {string} additionalSizes list of sizes in the placement sizes array besides the first - * @property {string} batchKey key used for batching requests which have the same basic properties * @property {module:HiroMediaAdapter~bidParams} bidParams original params passed for bid in #callBids * @property {boolean} shouldBid flag to determine if the bid is valid for bidding or not */ @@ -425,7 +366,7 @@ var HiroMediaAdapter = function HiroMediaAdapter() { * * @property {string} name browser name (e.g. `'Chrome'` or `'Firefox'`) * @property {string} version browser major version (e.g. `'53'`) - */ + */ }; module.exports = HiroMediaAdapter; diff --git a/test/spec/adapters/hiromedia_spec.js b/test/spec/adapters/hiromedia_spec.js index 4fe7a0ec1cd..643379b4ff2 100644 --- a/test/spec/adapters/hiromedia_spec.js +++ b/test/spec/adapters/hiromedia_spec.js @@ -1,36 +1,30 @@ -/* jslint white: true, es6: true, single: true */ -/* jshint esversion:6 */ - -import { expect } from 'chai'; -import querystringify from 'querystringify'; +import { expect } from 'chai'; import urlParse from 'url-parse'; -import adloader from 'src/adloader'; import Adapter from 'src/adapters/hiromedia'; import bidmanager from 'src/bidmanager'; import { STATUS } from 'src/constants'; import * as utils from 'src/utils'; -describe('hiromedia adapter', function () { +describe('hiromedia adapter', function () { const BIDDER_CODE = 'hiromedia'; - const DEFAULT_CALLBACK_NAME = 'hiromedia_callback'; const DEFAULT_ENDPOINT = 'https://hb-rtb.ktdpublishers.com/bid/get'; let adapter; let sandbox; - let loadScriptStub; + let xhr; let addBidResponseStub; let hasValidBidRequestSpy; let placementId = 0; window.$$PREBID_GLOBAL$$ = window.$$PREBID_GLOBAL$$ || {}; - beforeEach(() => { + beforeEach(() => { adapter = new Adapter(); sandbox = sinon.sandbox.create(); // Used to spy on bid requests - loadScriptStub = sandbox.stub(adloader, 'loadScript'); + xhr = sandbox.useFakeXMLHttpRequest(); // Used to spy on bid responses addBidResponseStub = sandbox.stub(bidmanager, 'addBidResponse'); @@ -38,7 +32,7 @@ describe('hiromedia adapter', function () { // Used to spy on bid validation hasValidBidRequestSpy = sandbox.spy(utils, 'hasValidBidRequest'); - placementId = 0; + placementId = 0; }); afterEach(() => { @@ -47,13 +41,13 @@ describe('hiromedia adapter', function () { // Helper function that asserts that no bidding activity (requests nor responses) // was made during a test. - const assertNoBids = () => { - sinon.assert.notCalled(loadScriptStub); - sinon.assert.notCalled(addBidResponseStub); + const assertNoBids = () => { + expect(xhr.requests.length).to.be.equal(0); + sinon.assert.notCalled(addBidResponseStub); }; // Helper function to generate a 'mock' bid object - const makePlacement = (size) => { + const makePlacement = (size) => { placementId += 1; return { @@ -63,7 +57,7 @@ describe('hiromedia adapter', function () { accountId: '1337' }, placementCode: 'div-gpt-ad-12345-' + placementId - }; + }; }; // 300x250 are in the allowed size by default @@ -72,7 +66,7 @@ describe('hiromedia adapter', function () { // anything else should have no bid by default const leaderPlacement = () => makePlacement([728, 90]); - describe('callbids', () => { + describe('callbids', () => { it('exists and is a function', () => { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); @@ -92,51 +86,50 @@ describe('hiromedia adapter', function () { assertNoBids(); }); - it('invokes a bid request per placement', () => { + it('invokes a bid request per placement', () => { const expectedRequests = [{ - batchKey: [DEFAULT_ENDPOINT, '1337', '300x250', ''].join('-'), placementCode: 'div-gpt-ad-12345-1', selectedSize: '300x250' }, { - batchKey: [DEFAULT_ENDPOINT, '1337', '728x90', ''].join('-'), placementCode: 'div-gpt-ad-12345-2', selectedSize: '728x90' + }, { + placementCode: 'div-gpt-ad-12345-3', + selectedSize: '300x250' }]; const params = { - bids: [tilePlacement(), leaderPlacement()] + bids: [tilePlacement(), leaderPlacement(), tilePlacement()] }; adapter.callBids(params); - sinon.assert.calledTwice(loadScriptStub); + expect(xhr.requests.length).to.equal(3); sinon.assert.notCalled(addBidResponseStub); - sinon.assert.calledTwice(hasValidBidRequestSpy); + sinon.assert.calledThrice(hasValidBidRequestSpy); - expectedRequests.forEach(function(request, index) { + expectedRequests.forEach(function(request, index) { expect(hasValidBidRequestSpy.returnValues[index]).to.be.equal(true); // validate request - const bidRequest = loadScriptStub.getCall(index).args[0]; + const bidRequest = xhr.requests[index].url; const defaultBidUrl = urlParse(DEFAULT_ENDPOINT); - const bidUrl = urlParse(bidRequest); - const query = querystringify.parse(bidUrl.query); + const bidUrl = urlParse(bidRequest, true); + const query = bidUrl.query; expect(bidUrl.hostname).to.equal(defaultBidUrl.hostname); expect(bidUrl.pathname).to.equal(defaultBidUrl.pathname); expect(query).to.have.property('adapterVersion').and.to.equal('3'); - expect(query).to.have.property('callback').and.to.equal('$$PREBID_GLOBAL$$.' + DEFAULT_CALLBACK_NAME); - expect(query).to.have.property('batchKey').and.to.equal(request.batchKey); expect(query).to.have.property('placementCode').and.to.equal(request.placementCode); expect(query).to.have.property('accountId').and.to.equal('1337'); expect(query).to.have.property('selectedSize').and.to.equal(request.selectedSize); expect(query).to.not.have.property('additionalSizes'); - expect(query).to.have.property('domain').and.to.equal(window.top.location.hostname); - }); + expect(query).to.have.property('domain').and.to.equal(window.top.location.hostname); + }); }); // Test additionalSizes parameter - it('attaches multiple sizes to additionalSizes', () => { + it('attaches multiple sizes to additionalSizes', () => { const placement = tilePlacement(); // Append additional @@ -148,18 +141,18 @@ describe('hiromedia adapter', function () { }; adapter.callBids(params); - sinon.assert.calledOnce(loadScriptStub); + expect(xhr.requests.length).to.be.equal(1); - const bidRequest = loadScriptStub.getCall(0).args[0]; - const bidUrl = urlParse(bidRequest); - const query = querystringify.parse(bidUrl.query); + const bidRequest = xhr.requests[0].url; + const bidUrl = urlParse(bidRequest, true); + const query = bidUrl.query; expect(query).to.have.property('selectedSize').and.to.equal('300x250'); - expect(query).to.have.property('additionalSizes').and.to.equal('300x600,300x900'); + expect(query).to.have.property('additionalSizes').and.to.equal('300x600,300x900'); }); // Test `params.accountId` validation - it('invalidates bids with no id', () => { + it('invalidates bids with no id', () => { const placement = tilePlacement(); delete placement.params; @@ -168,13 +161,20 @@ describe('hiromedia adapter', function () { }; adapter.callBids(params); - sinon.assert.notCalled(loadScriptStub); + expect(xhr.requests.length).to.be.equal(0); sinon.assert.calledOnce(hasValidBidRequestSpy); - expect(hasValidBidRequestSpy.returnValues[0]).to.be.equal(false); + sinon.assert.calledOnce(addBidResponseStub); + + expect(hasValidBidRequestSpy.returnValues[0]).to.be.equal(false); + const placementCode = addBidResponseStub.getCall(0).args[0]; + const bidObject = addBidResponseStub.getCall(0).args[1]; + + expect(placementCode).to.be.equal('div-gpt-ad-12345-1'); + expect(bidObject.getStatusCode()).to.be.equal(STATUS.NO_BID); }); // Test `params.bidUrl` - it('accepts a custom bid endpoint url', () => { + it('accepts a custom bid endpoint url', () => { const placement = tilePlacement(); placement.params.bidUrl = DEFAULT_ENDPOINT + '?someparam=value'; @@ -183,73 +183,76 @@ describe('hiromedia adapter', function () { }; adapter.callBids(params); - sinon.assert.calledOnce(loadScriptStub); + expect(xhr.requests.length).to.be.equal(1); - const bidRequest = loadScriptStub.getCall(0).args[0]; + const bidRequest = xhr.requests[0].url; const defaultBidUrl = urlParse(DEFAULT_ENDPOINT); - const bidUrl = urlParse(bidRequest); - const query = querystringify.parse(bidUrl.query); + const bidUrl = urlParse(bidRequest, true); + const query = bidUrl.query; expect(bidUrl.hostname).to.equal(defaultBidUrl.hostname); expect(bidUrl.pathname).to.equal(defaultBidUrl.pathname); - expect(query).to.have.property('someparam').and.to.equal('value'); - }); - - it('batches similar bid requests for similar sized placements', () => { - const params = { - bids: [tilePlacement(), tilePlacement()] - }; - - adapter.callBids(params); - sinon.assert.calledOnce(loadScriptStub); // and only once! + expect(query).to.have.property('someparam').and.to.equal('value'); }); - }); + }); + + describe('response handler', () => { + let server; + + beforeEach(() => { + server = sandbox.useFakeServer(); + }); + + const assertSingleFailedBidResponse = () => { + sinon.assert.calledOnce(addBidResponseStub); + const placementCode = addBidResponseStub.getCall(0).args[0]; + const bidObject = addBidResponseStub.getCall(0).args[1]; + + expect(placementCode).to.be.equal('div-gpt-ad-12345-1'); + expect(bidObject.getStatusCode()).to.be.equal(STATUS.NO_BID); + }; + + it('tolerates an empty response', () => { + server.respondWith(''); + adapter.callBids({bids: [tilePlacement()]}); + server.respond(); + + assertSingleFailedBidResponse(); + }); - describe('global response handler', () => { - const getPbjs = () => window.$$PREBID_GLOBAL$$; - const getResponseHandler = () => window.$$PREBID_GLOBAL$$[DEFAULT_CALLBACK_NAME]; + it('tolerates a response error', () => { + server.respondWith([500, {}, '']); + adapter.callBids({bids: [tilePlacement()]}); + server.respond(); - it('exists and is a function', () => { - expect(getResponseHandler()).to.exist.and.to.be.a('function'); + assertSingleFailedBidResponse(); }); - it('tolerates empty arguments', () => { - expect(getResponseHandler()).to.not.throw(Error); - }); + it('tolerates an invalid response', () => { + server.respondWith('not json'); + adapter.callBids({bids: [tilePlacement()]}); + server.respond(); - it('tolerates an empty response', () => { - expect(getResponseHandler().bind(getPbjs(), {})).to.not.throw(Error); + assertSingleFailedBidResponse(); }); - // This test is coupled with `callBids`, this is done - // to ensure that the response handler is able to - // add bid responses for each placement with the same - // batch key. - // To do this, we have to have the internal state of - // the adapter set up correctly. - it('adds a bid reponse for each pending bid', () => { - const expectedResponses = [{ - batchKey: [DEFAULT_ENDPOINT, '1337', '300x250', ''].join('-'), + it('adds a bid reponse for each pending bid', () => { + const responses = [{ width: '300', height: '250', cpm: 0.4, ad: '' }, { - batchKey: [DEFAULT_ENDPOINT, '1337', '728x90', ''].join('-'), width: '728', height: '90', cpm: 0.4, ad: '' }]; - // Instead of the dead stub defined in the top scope, we'll use - // one that mocks a response. - loadScriptStub.restore(); let id = 0; - const activeLoadScriptStub = sandbox.stub(adloader, 'loadScript', (url) => { - const handler = getResponseHandler(); - handler(expectedResponses[id]); + server.respondWith((request) => { + request.respond(200, {}, JSON.stringify(responses[id])); id += 1; }); @@ -258,11 +261,12 @@ describe('hiromedia adapter', function () { }; adapter.callBids(params); + server.respond(); - sinon.assert.calledTwice(activeLoadScriptStub); + expect(server.requests.length).to.be.equal(2); sinon.assert.calledTwice(addBidResponseStub); - expectedResponses.forEach((expectedResponse, i) => { + responses.forEach((expectedResponse, i) => { const placementCode = addBidResponseStub.getCall(i).args[0]; const bidObject = addBidResponseStub.getCall(i).args[1]; @@ -272,17 +276,16 @@ describe('hiromedia adapter', function () { expect(bidObject).to.have.property('cpm').and.to.equal(expectedResponse.cpm); expect(bidObject).to.have.property('ad').and.to.equal(expectedResponse.ad); expect(bidObject).to.have.property('width').and.to.equal(expectedResponse.width); - expect(bidObject).to.have.property('height').and.to.equal(expectedResponse.height); - }); + expect(bidObject).to.have.property('height').and.to.equal(expectedResponse.height); + }); }); // We want to check that responses are added according to a sampling value, // this is possible by stubbing `Math.random`, to ensure the effect is // limited to the area we check, we create a self destructing stub which // restores itself once called. - it('adds responses according to the sampling defined in the response', () => { + it('adds responses according to the sampling defined in the response', () => { const response = { - batchKey: [DEFAULT_ENDPOINT, '1337', '300x250', ''].join('-'), cpm: 0.4, chance: 0.25, ad: '' @@ -293,22 +296,19 @@ describe('hiromedia adapter', function () { const randomValues = [0.2, 0.3]; let randomIndex = 0; - loadScriptStub.restore(); - const activeLoadScriptStub = sandbox.stub(adloader, 'loadScript', (url) => { - const handler = getResponseHandler(); - - const mathRandomStub = sandbox.stub(Math, 'random', function () { + server.respondWith((request) => { + const mathRandomStub = sandbox.stub(Math, 'random', function () { const randomValue = randomValues[randomIndex]; randomIndex += 1; mathRandomStub.restore(); // self destruct on call - return randomValue; + return randomValue; }); - handler(response); + request.respond(200, {}, JSON.stringify(response)); - mathRandomStub.restore(); + mathRandomStub.restore(); }); const params = { @@ -317,6 +317,7 @@ describe('hiromedia adapter', function () { adapter.callBids(params); adapter.callBids(params); + server.respond(); sinon.assert.calledTwice(addBidResponseStub); @@ -324,7 +325,7 @@ describe('hiromedia adapter', function () { const secondBidObject = addBidResponseStub.getCall(1).args[1]; expect(firstBidObject.getStatusCode()).to.be.equal(STATUS.GOOD); - expect(secondBidObject.getStatusCode()).to.be.equal(STATUS.NO_BID); - }); - }); + expect(secondBidObject.getStatusCode()).to.be.equal(STATUS.NO_BID); + }); + }); });