From a381fbb77e0d50e04803d9c3ef641d14b4949bf9 Mon Sep 17 00:00:00 2001 From: Nate Guisinger Date: Wed, 17 Aug 2016 09:55:50 -0700 Subject: [PATCH] Queue bid requests (#477) * JSCS fixes * queue bid requests * close over auction instance data when queuing a request for bids * review notes * mark bid requests and responses complete when the ad for a given auction and placement renders * call markComplete on renderAd and removeComplete on requestBids * test presence before accessing properties, add tests, ignore selenium files --- .gitignore | 4 + src/prebid.js | 73 +++++++++---- test/fixtures/fixtures.js | 130 ++++++++++++++++++++++- test/spec/unit/pbjs_api_spec.js | 177 ++++++++++++++++++-------------- 4 files changed, 285 insertions(+), 99 deletions(-) diff --git a/.gitignore b/.gitignore index 80bdbb8a771..f7243ecc54c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,10 @@ test/app gpt.html gpt-each-bidder3.html +# Selenium files +bin +selenium*.log + # Dev File integrationExamples/gpt/gpt.html diff --git a/src/prebid.js b/src/prebid.js index aa016a28db3..4a2e0226e98 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -24,6 +24,7 @@ var BID_WON = CONSTANTS.EVENTS.BID_WON; var AUCTION_END = CONSTANTS.EVENTS.AUCTION_END; var auctionRunning = false; +var bidRequestQueue = []; var presetTargeting = []; var pbTargetingKeys = []; @@ -246,6 +247,31 @@ function getAllTargeting() { return targeting; } +function markComplete(adObject) { + $$PREBID_GLOBAL$$._bidsRequested.filter(request => request.requestId === adObject.requestId) + .forEach(request => request.bids.filter(bid => bid.placementCode === adObject.adUnitCode) + .forEach(bid => bid.complete = true)); + + $$PREBID_GLOBAL$$._bidsReceived.filter(bid => { + return bid.requestId === adObject.requestId && bid.adUnitCode === adObject.adUnitCode; + }).forEach(bid => bid.complete = true); +} + +function removeComplete() { + let requests = $$PREBID_GLOBAL$$._bidsRequested; + let responses = $$PREBID_GLOBAL$$._bidsReceived; + + requests.map(request => request.bids + .filter(bid => bid.complete)) + .forEach(request => requests.splice(requests.indexOf(request), 1)); + + responses.filter(bid => bid.complete).forEach(bid => responses.splice(responses.indexOf(bid), 1)); + + // also remove bids that have an empty or error status so known as not pending for render + responses.filter(bid => bid.getStatusCode && bid.getStatusCode() === 2) + .forEach(bid => responses.slice(responses.indexOf(bid), 1)); +} + ////////////////////////////////// // // // Start Public APIs // @@ -330,10 +356,15 @@ $$PREBID_GLOBAL$$.getAdserverTargeting = function () { $$PREBID_GLOBAL$$.getBidResponses = function () { utils.logInfo('Invoking $$PREBID_GLOBAL$$.getBidResponses', arguments); + const responses = $$PREBID_GLOBAL$$._bidsReceived; + + // find the last requested id to get responses for most recent auction only + const currentRequestId = responses && responses.length && responses[responses.length - 1].requestId; - return $$PREBID_GLOBAL$$._bidsReceived.map(bid => bid.adUnitCode) - .filter(uniques).map(adUnitCode => $$PREBID_GLOBAL$$._bidsReceived - .filter(bid => bid.adUnitCode === adUnitCode)) + return responses.map(bid => bid.adUnitCode) + .filter(uniques).map(adUnitCode => responses + .filter(bid => bid.requestId === currentRequestId && bid.adUnitCode === adUnitCode)) + .filter(bids => bids && bids[0] && bids[0].adUnitCode) .map(bids => { return { [bids[0].adUnitCode]: { bids: bids } @@ -402,6 +433,9 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { $$PREBID_GLOBAL$$._winningBids.push(adObject); //emit 'bid won' event here events.emit(BID_WON, adObject); + + // mark bid requests and responses for this placement in this auction as "complete" + markComplete(adObject); var height = adObject.height; var width = adObject.width; var url = adObject.adUrl; @@ -414,11 +448,7 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id) { doc.defaultView.frameElement.width = width; doc.defaultView.frameElement.height = height; } - } - - //doc.body.style.width = width; - //doc.body.style.height = height; - else if (url) { + } else if (url) { doc.write(''); doc.close(); @@ -464,6 +494,9 @@ $$PREBID_GLOBAL$$.clearAuction = function() { auctionRunning = false; utils.logMessage('Prebid auction cleared'); events.emit(AUCTION_END); + if (bidRequestQueue.length) { + bidRequestQueue.shift()(); + } }; /** @@ -474,19 +507,7 @@ $$PREBID_GLOBAL$$.clearAuction = function() { * @param adUnitCodes */ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, adUnitCodes }) { - if (auctionRunning) { - utils.logError('Prebid Error: `$$PREBID_GLOBAL$$.requestBids` was called while a previous auction was' + - ' still running. Resubmit this request.'); - return; - } else { - auctionRunning = true; - $$PREBID_GLOBAL$$._bidsRequested = []; - $$PREBID_GLOBAL$$._bidsReceived = []; - } - const cbTimeout = timeout || $$PREBID_GLOBAL$$.bidderTimeout; - - // use adUnits provided or from $$PREBID_GLOBAL$$ global adUnits = adUnits || $$PREBID_GLOBAL$$.adUnits; // if specific adUnitCodes filter adUnits for those codes @@ -494,6 +515,16 @@ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, a adUnits = adUnits.filter(adUnit => adUnitCodes.includes(adUnit.code)); } + if (auctionRunning) { + bidRequestQueue.push(() => { + $$PREBID_GLOBAL$$.requestBids({ bidsBackHandler, cbTimeout, adUnits }); + }); + return; + } else { + auctionRunning = true; + removeComplete(); + } + if (typeof bidsBackHandler === objectType_function) { bidmanager.addOneTimeCallback(bidsBackHandler); } @@ -509,7 +540,7 @@ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, a //set timeout for all bids const timedOut = true; const timeoutCallback = bidmanager.executeCallback.bind(bidmanager, timedOut); - setTimeout(timeoutCallback, cbTimeout); + setTimeout(timeoutCallback, timeout); adaptermanager.callBids({ adUnits, adUnitCodes, cbTimeout }); }; diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js index e49875cbdd4..363e6ac8a95 100644 --- a/test/fixtures/fixtures.js +++ b/test/fixtures/fixtures.js @@ -301,6 +301,7 @@ export function getBidResponses() { "pbHg": "0.11", "pbAg": "0.10", "size": "0x0", + "requestId": 123456, "adserverTargeting": { "hb_bidder": "triplelift", "hb_adid": "222bb26f9e8bd", @@ -329,6 +330,7 @@ export function getBidResponses() { "pbAg": "10.00", "size": "300x250", "alwaysUseBid": true, + "requestId": 123456, "adserverTargeting": { "hb_bidder": "appnexus", "hb_adid": "233bcbee889d46d", @@ -357,6 +359,7 @@ export function getBidResponses() { "pbAg": "10.00", "size": "728x90", "alwaysUseBid": true, + "requestId": 123456, "adserverTargeting": { "hb_bidder": "appnexus", "hb_adid": "24bd938435ec3fc", @@ -384,6 +387,7 @@ export function getBidResponses() { "pbHg": "0.50", "pbAg": "0.50", "size": "300x250", + "requestId": 123456, "adserverTargeting": { "hb_bidder": "pagescience", "hb_adid": "25bedd4813632d7", @@ -410,6 +414,7 @@ export function getBidResponses() { "pbHg": "0.17", "pbAg": "0.15", "size": "300x250", + "requestId": 654321, "adserverTargeting": { "hb_bidder": "brightcom", "hb_adid": "26e0795ab963896", @@ -437,6 +442,7 @@ export function getBidResponses() { "pbHg": "0.50", "pbAg": "0.50", "size": "300x250", + "requestId": 654321, "adserverTargeting": { "hb_bidder": "brealtime", "hb_adid": "275bd666f5a5a5d", @@ -465,6 +471,7 @@ export function getBidResponses() { "pbHg": "5.93", "pbAg": "5.90", "size": "300x250", + "requestId": 654321, "adserverTargeting": { "hb_bidder": "pubmatic", "hb_adid": "28f4039c636b6a7", @@ -491,6 +498,7 @@ export function getBidResponses() { "pbHg": "2.74", "pbAg": "2.70", "size": "300x600", + "requestId": 654321, "adserverTargeting": { "hb_bidder": "rubicon", "hb_adid": "29019e2ab586a5a", @@ -1050,9 +1058,129 @@ export function getAdUnits() { } ] } - ] + ]; }; +export function getBidResponsesFromAPI() { + return { + "/19968336/header-bid-tag-0": { + "bids": [ + { + "bidderCode": "brightcom", + "width": 300, + "height": 250, + "statusMessage": "Bid available", + "adId": "26e0795ab963896", + "cpm": 0.17, + "ad": "", + "responseTimestamp": 1462919239420, + "requestTimestamp": 1462919238937, + "bidder": "brightcom", + "adUnitCode": "/19968336/header-bid-tag-0", + "timeToRespond": 483, + "pbLg": "0.00", + "pbMg": "0.10", + "pbHg": "0.17", + "pbAg": "0.15", + "size": "300x250", + "requestId": 654321, + "adserverTargeting": { + "hb_bidder": "brightcom", + "hb_adid": "26e0795ab963896", + "hb_pb": "10.00", + "hb_size": "300x250", + "foobar": "300x250" + } + }, + { + "bidderCode": "brealtime", + "width": 300, + "height": 250, + "statusMessage": "Bid available", + "adId": "275bd666f5a5a5d", + "creative_id": 29681110, + "cpm": 0.5, + "adUrl": "http://lax1-ib.adnxs.com/ab?e=wqT_3QLzBKhzAgAAAwDWAAUBCMjAybkFEIPr4YfMvKLoQBjL84KE1tzG-kkgASotCQAAAQII4D8RAQcQAADgPxkJCQjwPyEJCQjgPykRCaAwuvekAji-B0C-B0gCUNbLkw5YweAnYABokUB4mo8EgAEBigEDVVNEkgUG8FKYAawCoAH6AagBAbABALgBAcABA8gBANABANgBAOABAPABAIoCOnVmKCdhJywgNDk0NDcyLCAxNDYyOTE5MjQwKTt1ZigncicsIDI5NjgxMTEwLDIeAPBvkgLNASFsU2NQWlFpNjBJY0VFTmJMa3c0WUFDREI0Q2N3QURnQVFBUkl2Z2RRdXZla0FsZ0FZSk1IYUFCdzNBMTRDb0FCcGh5SUFRcVFBUUdZQVFHZ0FRR29BUU93QVFDNUFRQUFBQUFBQU9BX3dRRQkMSEFEZ1A4a0JHZmNvazFBejFUX1oVKCRQQV80QUVBOVFFBSw8bUFLS2dOU0NEYUFDQUxVQwUVBEwwCQh0T0FDQU9nQ0FQZ0NBSUFEQVEuLpoCJSFDUWxfYXdpMtAA8KZ3ZUFuSUFRb2lvRFVnZzAu2ALoB-ACx9MB6gIfaHR0cDovL3ByZWJpZC5vcmc6OTk5OS9ncHQuaHRtbIADAIgDAZADAJgDBaADAaoDALADALgDAMADrALIAwDYAwDgAwDoAwD4AwOABACSBAQvanB0mAQAogQKMTAuMS4xMy4zN6gEi-wJsgQICAAQABgAIAC4BADABADIBADSBAsxMC4wLjg1LjIwOA..&s=975cfe6518f064683541240f0d780d93a5f973da&referrer=http%3A%2F%2Fprebid.org%3A9999%2Fgpt.html", + "responseTimestamp": 1462919239486, + "requestTimestamp": 1462919238941, + "bidder": "brealtime", + "adUnitCode": "/19968336/header-bid-tag-0", + "timeToRespond": 545, + "pbLg": "0.50", + "pbMg": "0.50", + "pbHg": "0.50", + "pbAg": "0.50", + "size": "300x250", + "requestId": 654321, + "adserverTargeting": { + "hb_bidder": "brealtime", + "hb_adid": "275bd666f5a5a5d", + "hb_pb": "10.00", + "hb_size": "300x250", + "foobar": "300x250" + } + }, + { + "bidderCode": "pubmatic", + "width": "300", + "height": "250", + "statusMessage": "Bid available", + "adId": "28f4039c636b6a7", + "adSlot": "39620189@300x250", + "cpm": 5.9396, + "ad": "\r
", + "dealId": "", + "responseTimestamp": 1462919239544, + "requestTimestamp": 1462919238922, + "bidder": "pubmatic", + "adUnitCode": "/19968336/header-bid-tag-0", + "timeToRespond": 622, + "pbLg": "5.00", + "pbMg": "5.90", + "pbHg": "5.93", + "pbAg": "5.90", + "size": "300x250", + "requestId": 654321, + "adserverTargeting": { + "hb_bidder": "pubmatic", + "hb_adid": "28f4039c636b6a7", + "hb_pb": "10.00", + "hb_size": "300x250", + "foobar": "300x250" + } + }, + { + "bidderCode": "rubicon", + "width": 300, + "height": 600, + "statusMessage": "Bid available", + "adId": "29019e2ab586a5a", + "cpm": 2.74, + "ad": "", + "responseTimestamp": 1462919239860, + "requestTimestamp": 1462919238934, + "bidder": "rubicon", + "adUnitCode": "/19968336/header-bid-tag-0", + "timeToRespond": 926, + "pbLg": "2.50", + "pbMg": "2.70", + "pbHg": "2.74", + "pbAg": "2.70", + "size": "300x600", + "requestId": 654321, + "adserverTargeting": { + "hb_bidder": "rubicon", + "hb_adid": "29019e2ab586a5a", + "hb_pb": "10.00", + "hb_size": "300x600", + "foobar": "300x600" + } + } + ] + } + }; +} + // Ad server targeting when `$$PREBID_GLOBAL$$.enableSendAllBids()` is called. export function getAdServerTargeting() { return { diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 1fbcbf4480f..71a81b7847b 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1,9 +1,10 @@ import { - getAdServerTargeting, - getBidRequests, - getBidResponses, - getTargetingKeys, - getTargetingKeysBidLandscape, + getAdServerTargeting, + getBidRequests, + getBidResponses, + getBidResponsesFromAPI, + getTargetingKeys, + getTargetingKeysBidLandscape, } from 'test/fixtures/fixtures'; var assert = require('chai').assert; @@ -32,7 +33,7 @@ function resetAuction() { var Slot = function Slot(elementId, pathId) { var slot = { - targeting : [], + targeting: [], getSlotElementId: function getSlotElementId() { return elementId; }, @@ -73,14 +74,14 @@ var createSlotArray = function createSlotArray() { var createSlotArrayScenario2 = function createSlotArrayScenario2() { var slot1 = new Slot(config.adUnitElementIDs[0], config.adUnitCodes[0]); - slot1.setTargeting('pos1','750x350'); + slot1.setTargeting('pos1', '750x350'); var slot2 = new Slot(config.adUnitElementIDs[1], config.adUnitCodes[0]); - slot2.setTargeting('gender',['male','female']); + slot2.setTargeting('gender', ['male', 'female']); return [ slot1, slot2 ]; -} +}; window.googletag = { _slots: [], @@ -149,19 +150,19 @@ describe('Unit: Prebid Module', function () { it('should return correct targeting with default settings', () => { var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); var expected = { - "/19968336/header-bid-tag-0": { - "foobar": "300x250", - "hb_size": "300x250", - "hb_pb": "10.00", - "hb_adid": "233bcbee889d46d", - "hb_bidder": "appnexus" + '/19968336/header-bid-tag-0': { + foobar: '300x250', + hb_size: '300x250', + hb_pb: '10.00', + hb_adid: '233bcbee889d46d', + hb_bidder: 'appnexus' }, - "/19968336/header-bid-tag1": { - "foobar": "728x90", - "hb_size": "728x90", - "hb_pb": "10.00", - "hb_adid": "24bd938435ec3fc", - "hb_bidder": "appnexus" + '/19968336/header-bid-tag1': { + foobar: '728x90', + hb_size: '728x90', + hb_pb: '10.00', + hb_adid: '24bd938435ec3fc', + hb_bidder: 'appnexus' } }; assert.deepEqual(targeting, expected); @@ -183,7 +184,7 @@ describe('Unit: Prebid Module', function () { // Modify the losing bid to have `alwaysUseBid=true` and a custom `adserverTargeting` key. $$PREBID_GLOBAL$$._bidsReceived[0]['alwaysUseBid'] = true; $$PREBID_GLOBAL$$._bidsReceived[0]['adserverTargeting'] = { - 'always_use_me': 'abc', + always_use_me: 'abc', }; var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(); @@ -195,20 +196,20 @@ describe('Unit: Prebid Module', function () { ); var expected = { - "/19968336/header-bid-tag-0": { - "foobar": "300x250", - "hb_size": "300x250", - "hb_pb": "10.00", - "hb_adid": "233bcbee889d46d", - "hb_bidder": "appnexus", - "always_use_me": "abc" + '/19968336/header-bid-tag-0': { + foobar: '300x250', + hb_size: '300x250', + hb_pb: '10.00', + hb_adid: '233bcbee889d46d', + hb_bidder: 'appnexus', + always_use_me: 'abc' }, - "/19968336/header-bid-tag1": { - "foobar": "728x90", - "hb_size": "728x90", - "hb_pb": "10.00", - "hb_adid": "24bd938435ec3fc", - "hb_bidder": "appnexus" + '/19968336/header-bid-tag1': { + foobar: '728x90', + hb_size: '728x90', + hb_pb: '10.00', + hb_adid: '24bd938435ec3fc', + hb_bidder: 'appnexus' } }; @@ -217,20 +218,17 @@ describe('Unit: Prebid Module', function () { }); describe('getBidResponses', function () { - it('should return expected bid responses when not passed an adunitCode', function () { - var result = $$PREBID_GLOBAL$$.getBidResponses(); - var compare = getBidResponses().map(bid => bid.adUnitCode) - .filter((v, i, a) => a.indexOf(v) === i).map(adUnitCode => $$PREBID_GLOBAL$$._bidsReceived - .filter(bid => bid.adUnitCode === adUnitCode)) - .map(bids => { - return { - [bids[0].adUnitCode]: { bids: bids } - }; - }) - .reduce((a, b) => Object.assign(a, b), {}); + var result = $$PREBID_GLOBAL$$.getBidResponses(); + var compare = getBidResponsesFromAPI(); + it('should return expected bid responses when not passed an adunitCode', function () { assert.deepEqual(result, compare, 'expected bid responses are returned'); }); + + it('should return bid responses for most recent requestId only', () => { + const responses = $$PREBID_GLOBAL$$.getBidResponses(); + assert.equal(responses[Object.keys(responses)[0]].bids.length, 4); + }); }); describe('getBidResponsesForAdUnitCode', function () { @@ -238,7 +236,7 @@ describe('Unit: Prebid Module', function () { const adUnitCode = '/19968336/header-bid-tag-0'; const result = $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode(adUnitCode); const bids = getBidResponses().filter(bid => bid.adUnitCode === adUnitCode); - const compare = { bids: bids}; + const compare = { bids: bids }; assert.deepEqual(result, compare, 'expected id responses for ad unit code are returned'); }); }); @@ -256,13 +254,13 @@ describe('Unit: Prebid Module', function () { resetAuction(); }); - it('should set googletag targeting keys after calling setTargetingForGPTAsync function', function() { + it('should set googletag targeting keys after calling setTargetingForGPTAsync function', function () { var slots = createSlotArrayScenario2(); window.googletag.pubads().setSlots(slots); $$PREBID_GLOBAL$$.setTargetingForGPTAsync(config.adUnitCodes); var targeting = []; - slots[1].getTargeting().map(function(value) { + slots[1].getTargeting().map(function (value) { var temp = []; temp.push(Object.keys(value).toString()); temp.push(value[Object.keys(value)]); @@ -284,7 +282,7 @@ describe('Unit: Prebid Module', function () { var slots = createSlotArray(); window.googletag.pubads().setSlots(slots); - $$PREBID_GLOBAL$$.setTargetingForGPTAsync(); + $$PREBID_GLOBAL$$.setTargetingForGPTAsync(); var expected = getTargetingKeys(); expect(slots[0].spySetTargeting.args).to.deep.contain.members(expected); @@ -295,8 +293,8 @@ describe('Unit: Prebid Module', function () { var slots = createSlotArray(); window.googletag.pubads().setSlots(slots); - $$PREBID_GLOBAL$$.enableSendAllBids(); - $$PREBID_GLOBAL$$.setTargetingForGPTAsync(); + $$PREBID_GLOBAL$$.enableSendAllBids(); + $$PREBID_GLOBAL$$.setTargetingForGPTAsync(); var expected = getTargetingKeysBidLandscape(); expect(slots[0].spySetTargeting.args).to.deep.contain.members(expected); @@ -309,9 +307,9 @@ describe('Unit: Prebid Module', function () { assert.equal($$PREBID_GLOBAL$$._bidsReceived[0]['cpm'], 0.112256); // Modify the losing bid to have `alwaysUseBid=true` and a custom `adserverTargeting` key. - $$PREBID_GLOBAL$$._bidsReceived[0]['alwaysUseBid'] = true; - $$PREBID_GLOBAL$$._bidsReceived[0]['adserverTargeting'] = { - 'always_use_me': 'abc', + $$PREBID_GLOBAL$$._bidsReceived[0]['alwaysUseBid'] = true; + $$PREBID_GLOBAL$$._bidsReceived[0]['adserverTargeting'] = { + always_use_me: 'abc', }; var slots = createSlotArray(); @@ -321,32 +319,32 @@ describe('Unit: Prebid Module', function () { var expected = [ [ - "hb_bidder", - "appnexus" + 'hb_bidder', + 'appnexus' ], [ - "hb_adid", - "233bcbee889d46d" + 'hb_adid', + '233bcbee889d46d' ], [ - "hb_pb", - "10.00" + 'hb_pb', + '10.00' ], [ - "hb_size", - "300x250" + 'hb_size', + '300x250' ], [ - "foobar", - "300x250" + 'foobar', + '300x250' ], [ - "always_use_me", - "abc" + 'always_use_me', + 'abc' ], [ - "foobar", - "300x250" + 'foobar', + '300x250' ] ]; @@ -394,9 +392,9 @@ describe('Unit: Prebid Module', function () { }; adResponse = { - "adId": bidId, - "width": 300, - "height": 250, + adId: bidId, + width: 300, + height: 250, }; $$PREBID_GLOBAL$$._bidsReceived.push(adResponse); @@ -431,9 +429,9 @@ describe('Unit: Prebid Module', function () { }); it('should place the url inside an iframe on the doc', function () { - adResponse.adUrl = "http://server.example.com/ad/ad.js"; + adResponse.adUrl = 'http://server.example.com/ad/ad.js'; $$PREBID_GLOBAL$$.renderAd(doc, bidId); - var iframe = '' + var iframe = ''; assert.ok(doc.write.calledWith(iframe), 'url was written to iframe in doc'); }); @@ -471,7 +469,8 @@ describe('Unit: Prebid Module', function () { it('should add bidsBackHandler callback to bidmanager', () => { var spyAddOneTimeCallBack = sinon.spy(bidmanager, 'addOneTimeCallback'); var requestObj = { - bidsBackHandler: function bidsBackHandlerCallback() {} + bidsBackHandler: function bidsBackHandlerCallback() { + } }; $$PREBID_GLOBAL$$.requestBids(requestObj); assert.ok(spyAddOneTimeCallBack.calledWith(requestObj.bidsBackHandler), @@ -497,7 +496,9 @@ describe('Unit: Prebid Module', function () { var spyExecuteCallback = sinon.spy(bidmanager, 'executeCallback'); var clock = sinon.useFakeTimers(); var requestObj = { - bidsBackHandler: function bidsBackHandlerCallback() {}, + bidsBackHandler: function bidsBackHandlerCallback() { + }, + timeout: 2000 }; @@ -536,6 +537,28 @@ describe('Unit: Prebid Module', function () { adaptermanager.callBids.restore(); resetAuction(); }); + + it('should queue bid requests when a previous bid request is in process', () => { + var spyCallBids = sinon.spy(adaptermanager, 'callBids'); + var clock = sinon.useFakeTimers(); + var requestObj = { + bidsBackHandler: function bidsBackHandlerCallback() { + }, + + timeout: 2000 + }; + + $$PREBID_GLOBAL$$.requestBids(requestObj); + $$PREBID_GLOBAL$$.requestBids(requestObj); + clock.tick(requestObj.timeout - 1); + assert.ok(spyCallBids.calledOnce, 'When two requests or bids are made only one should' + + ' callBids immediately'); + clock.tick(1); + assert.ok(spyCallBids.calledTwice, 'The second queued request should callBids when the' + + ' first request has completed'); + resetAuction(); + adaptermanager.callBids.restore(); + }); }); describe('onEvent', () => { @@ -625,7 +648,7 @@ describe('Unit: Prebid Module', function () { it('should catch thrown errors', () => { var spyLogError = sinon.spy(utils, 'logError'); - var errorObject = {message: 'bidderAdaptor error'}; + var errorObject = { message: 'bidderAdaptor error' }; var bidderAdaptor = sinon.stub().throws(errorObject); $$PREBID_GLOBAL$$.registerBidAdapter(bidderAdaptor, 'biddercode');