diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 05be60b482d..11481f0703e 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -19,7 +19,7 @@ const SCHEMA_VERSION = '0.1'; const MAX_RETRIES = 2; const MAX_TIMEOUT = 10000; -const AUCTION_END_WAIT_TIME = 2000; +const AUCTION_END_WAIT_TIME = 1000; const DEFAULT_SLOT_LOAD_BUFFER_TIME = 100; const auctionInitConst = CONSTANTS.EVENTS.AUCTION_INIT; @@ -36,11 +36,11 @@ const SLOT_LOADED = 'slotOnload'; * @property {string} publisherPlatformId * @property {number} publisherAccountId * @property {number} sampling + * @property {boolean} enableV2 * @property {boolean} testPipeline - * @property {number} slotLoadWaitTime * @property {Object} utmTagData * @property {string} adIdKey - * @property {number} payloadSendDelayTime + * @property {number} payloadWaitTime * @property {Array}adUnits */ @@ -51,12 +51,13 @@ const DEFAULT_ANALYTICS_CONFIG = { publisherPlatformId: void (0), publisherAccountId: void (0), sampling: 0.05, // default sampling rate of 5% + testCode: 'default', + enableV2: false, testPipeline: false, adIdKey: 'hb_adid', utmTagData: {}, adUnits: [], - slotLoadWaitTime: DEFAULT_SLOT_LOAD_BUFFER_TIME, - payloadSendDelayTime: AUCTION_END_WAIT_TIME, + payloadWaitTime: AUCTION_END_WAIT_TIME, }; let googletag = window.googletag || {}; @@ -66,6 +67,7 @@ googletag.cmd = googletag.cmd || []; * @type {AnalyticsConfig} */ let analyticsConfig; + let eventStack = {}; let loadedAdSlots = {}; @@ -364,94 +366,93 @@ function onSlotLoaded({ slot }) { eventStack[auctionId] = null; } delete loadedAdSlots[auctionId]; - }, analyticsConfig.slotLoadWaitTime); + }, analyticsConfig.payloadWaitTime); } } -let openxAdapter = Object.assign(adapter({ urlParam, analyticsType }), { - track({ eventType, args }) { - if (!checkInitOptions()) { - send(eventType, {}, null); - return; - } - - let info = Object.assign({}, args); +let openxAdapter = Object.assign(adapter({ urlParam, analyticsType })); - if (info && info.ad) { - info.ad = ''; - } +openxAdapter.originEnableAnalytics = openxAdapter.enableAnalytics; - // on bid timeout events, the info is an array of bids - let auctionId = eventType === CONSTANTS.EVENTS.BID_TIMEOUT - ? info[0].auctionId - : info.auctionId; - - if (eventType === auctionInitConst) { - eventStack[auctionId] = { options: {}, events: [] }; - // utils.logInfo('OX: Event Stack updated after AuctionInit', eventStack); - } else if (eventType === bidWonConst) { - pushEvent(eventType, info, auctionId); - // utils.logInfo('OX: Bid won called for', auctionId); - } else if (eventType === auctionEndConst) { - pushEvent(eventType, removeads(info), auctionId); - // utils.logInfo('OX: Auction end called for', auctionId); - updateSessionId(); - buildEventStack(auctionId); - if (isValidEventStack(auctionId)) { - setTimeout(function() { - // utils.logInfo('OX: Sending data', eventStack); - if (eventStack[auctionId]) { - send( - eventType, - eventStack, - auctionId - ); - eventStack[auctionId] = null; - } - delete loadedAdSlots[auctionId]; - // utils.logInfo('OX: Deleted Auction Info for auctionId', auctionId); - }, analyticsConfig.payloadSendDelayTime); - } else { - setTimeout(function() { - eventStack[auctionId] = null; - // utils.logInfo('OX: Deleted Auction Info for auctionId', auctionId); - }, analyticsConfig.payloadSendDelayTime); - } - } else if (eventType === bidTimeoutConst) { - // utils.logInfo('SA: Bid Timedout for', auctionId); - pushEvent(eventType, info, auctionId); - } +openxAdapter.enableAnalytics = function(adapterConfig = {options:{}}) { + // Backwards compatibility for external documentation + if(adapterConfig.options.slotLoadWaitTime){ + adapterConfig.options.payloadWaitTime = adapterConfig.options.slotLoadWaitTime; } -}); - -openxAdapter.originEnableAnalytics = openxAdapter.enableAnalytics; -openxAdapter.enableAnalytics = function(adapterConfig) { - analyticsConfig = {...DEFAULT_ANALYTICS_CONFIG, ...adapterConfig.options}; - analyticsConfig.testCode = getTestCode(); - analyticsConfig.utmTagData = this.buildUtmTagData(); - utils.logInfo('OpenX Analytics enabled with config', analyticsConfig); + if(isValidConfig(adapterConfig)){ + analyticsConfig = {...DEFAULT_ANALYTICS_CONFIG, ...adapterConfig.options}; + analyticsConfig.utmTagData = this.buildUtmTagData(); + utils.logInfo('OpenX Analytics enabled with config', analyticsConfig); + + if (analyticsConfig.testPipeline) { + openxAdapter.track = (args) => { + prebidAnalyticsEventHandlerV1(args); + prebidAnalyticsEventHandlerV2(args); + }; + + googletag.cmd.push(function() { + googletag.pubads().addEventListener(SLOT_LOADED, args => { + utils.logInfo('OX: SlotOnLoad event triggered'); + onSlotLoaded(args); + onSlotLoadedV2(args); + }); + }); - if (analyticsConfig.testPipeline) { - // override track method with v2 handlers - openxAdapter.track = prebidAnalyticsEventHandler; + } else if (analyticsConfig.enableV2) { + // override track method with v2 handlers + openxAdapter.track = prebidAnalyticsEventHandlerV2; - googletag.cmd.push(function() { - googletag.pubads().addEventListener(SLOT_LOADED, args => { - openxAdapter.track({ eventType: SLOT_LOADED, args }); - utils.logInfo('OX: SlotOnLoad event triggered'); + googletag.cmd.push(function() { + googletag.pubads().addEventListener(SLOT_LOADED, args => { + openxAdapter.track({ eventType: SLOT_LOADED, args }); + utils.logInfo('OX: SlotOnLoad event triggered'); + }); }); - }); - } else { - googletag.cmd.push(function() { - googletag.pubads().addEventListener(SLOT_LOADED, function(args) { - utils.logInfo('OX: SlotOnLoad event triggered'); - onSlotLoaded(args); + } else { + openxAdapter.track = prebidAnalyticsEventHandlerV1; + googletag.cmd.push(function() { + googletag.pubads().addEventListener(SLOT_LOADED, function(args) { + utils.logInfo('OX: SlotOnLoad event triggered'); + onSlotLoaded(args); + }); }); - }); + } + + openxAdapter.originEnableAnalytics(adapterConfig); } - openxAdapter.originEnableAnalytics(adapterConfig); + function isValidConfig({options: analyticsOptions}){ + const fieldValidations = [ + // tuple of property, type, required + ['publisherPlatformId', 'string', true], + ['publisherAccountId', 'number', true], + ['sampling', 'number', false], + ['enableV2', 'boolean', false], + ['testPipeline', 'boolean', false], + ['adIdKey', 'string', false], + ['payloadWaitTime', 'number', false], + ]; + + let failedValidation = fieldValidations.find(([property, type, required]) => { + // if required, the property has to exist + // if property exists, type check value + return (required && !analyticsOptions.hasOwnProperty(property)) || + (analyticsOptions.hasOwnProperty(property) && typeof analyticsOptions[property] !== type); + }); + + if(failedValidation) { + let [property, type, required] = failedValidation; + + if(required){ + utils.logError(`OpenXAnalyticsAdapter: Expected '${property}' to exist and of type '${type}'`); + } else { + utils.logError(`OpenXAnalyticsAdapter: Expected '${property}' to be type '${type}'`); + } + } + + return !failedValidation; + } }; openxAdapter.buildUtmTagData = function() { @@ -743,11 +744,6 @@ function getAdPositionByElementId(elementId) { return adPosition; } -openxAdapter.reset = function() { - eventStack = {}; - loadedAdSlots = {}; -}; - openxAdapter.slotOnLoad = onSlotLoaded; adapterManager.registerAnalyticsAdapter({ @@ -755,6 +751,60 @@ adapterManager.registerAnalyticsAdapter({ code: 'openx' }); +function prebidAnalyticsEventHandlerV1({eventType, args}){ + if (!checkInitOptions()) { + send(eventType, {}, null); + return; + } + + let info = Object.assign({}, args); + + if (info && info.ad) { + info.ad = ''; + } + + // on bid timeout events, the info is an array of bids + let auctionId = eventType === CONSTANTS.EVENTS.BID_TIMEOUT + ? info[0].auctionId + : info.auctionId; + + if (eventType === auctionInitConst) { + eventStack[auctionId] = { options: {}, events: [] }; + // utils.logInfo('OX: Event Stack updated after AuctionInit', eventStack); + } else if (eventType === bidWonConst) { + pushEvent(eventType, info, auctionId); + // utils.logInfo('OX: Bid won called for', auctionId); + } else if (eventType === auctionEndConst) { + pushEvent(eventType, removeads(info), auctionId); + // utils.logInfo('OX: Auction end called for', auctionId); + updateSessionId(); + buildEventStack(auctionId); + if (isValidEventStack(auctionId)) { + setTimeout(function() { + // utils.logInfo('OX: Sending data', eventStack); + if (eventStack[auctionId]) { + send( + eventType, + eventStack, + auctionId + ); + eventStack[auctionId] = null; + } + delete loadedAdSlots[auctionId]; + // utils.logInfo('OX: Deleted Auction Info for auctionId', auctionId); + }, analyticsConfig.payloadWaitTime); + } else { + setTimeout(function() { + eventStack[auctionId] = null; + // utils.logInfo('OX: Deleted Auction Info for auctionId', auctionId); + }, analyticsConfig.payloadWaitTime); + } + } else if (eventType === bidTimeoutConst) { + // utils.logInfo('SA: Bid Timedout for', auctionId); + pushEvent(eventType, info, auctionId); + } +} + //* ******* V2 Code ******* const { EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, BID_WON } @@ -764,7 +814,7 @@ const ENDPOINT = 'https://prebid.openx.net/ox/analytics'; let auctionMap = {}; let auctionOrder = 1; // tracks the number of auctions ran on the page -function prebidAnalyticsEventHandler({eventType, args}) { +function prebidAnalyticsEventHandlerV2({eventType, args}) { utils.logMessage(eventType, Object.assign({}, args)); switch (eventType) { case AUCTION_INIT: @@ -802,7 +852,7 @@ noBids: [] bidsReceived: [] winningBids: [] timeout: 3000 -config: {publisherPlatformId: "a3aece0c-9e80-4316-8deb-faf804779bd1", publisherAccountId: 537143056, sampling: 1, testPipeline: true} +config: {publisherPlatformId: "a3aece0c-9e80-4316-8deb-faf804779bd1", publisherAccountId: 537143056, sampling: 1, enableV2: true} */ function onAuctionInit({auctionId, timestamp: startTime, timeout, adUnitCodes}) { auctionMap[auctionId] = { @@ -949,8 +999,8 @@ function onSlotLoadedV2({ slot }) { // prepare to send regardless if auction is complete or not as a failsafe in case not all events are tracked // add additional padding when not all slots are rendered const delayTime = auction.adunitCodesRenderedCount === auction.adUnitCodesCount - ? analyticsConfig.slotLoadWaitTime - : analyticsConfig.slotLoadWaitTime + 500; + ? analyticsConfig.payloadWaitTime + : analyticsConfig.payloadWaitTime + 500; auction.auctionSendDelayTimer = setTimeout(() => { let payload = JSON.stringify([buildAuctionPayload(auction)]); @@ -1031,7 +1081,7 @@ function buildAuctionPayload(auction) { } = bidderBidResponse; return { - microCPM: cpm * 1000, + microCpm: cpm * 1000, netRevenue, currency, mediaType, @@ -1093,6 +1143,11 @@ export default Object.assign({ // reset the cache for unit tests openxAdapter.reset = function() { + // V1 data + eventStack = {}; + loadedAdSlots = {}; + + // V2 data auctionMap = {}; auctionOrder = 1; }; @@ -1145,15 +1200,15 @@ openxAdapter.reset = function() { /** * @typedef {Object} BidResponseMeta - * @property {number} [networkId] Bidder-specific Network/DSP Id + * @property {string} [networkId] Bidder-specific Network/DSP Id * @property {string} [networkName] - Network/DSP Name. example: "NetworkN" - * @property {number} [agencyId] - Bidder-specific Agency ID. example: 2222 + * @property {string} [agencyId] - Bidder-specific Agency ID. example: 2222 * @property {string} [agencyName] - Agency Name. example: "Agency, Inc." - * @property {number} [advertiserId] - Bidder-specific Advertiser ID. example: 3333 + * @property {string} [advertiserId] - Bidder-specific Advertiser ID. example: 3333 * @property {string} [advertiserName] - Advertiser Name. example: "AdvertiserA" * @property {Array} [advertiserDomains] - Array of Advertiser Domains for the landing page(s). This is an array * to align with the OpenRTB ‘adomain’ field.. example: ["advertisera.com"] - * @property {number} [brandId] - Bidder-specific Brand ID (some advertisers may have many brands). example: 4444 + * @property {string} [brandId] - Bidder-specific Brand ID (some advertisers may have many brands). example: 4444 * @property {string} [brandName] - Brand Name. example: "BrandB" * @property {string} [primaryCatId] - Primary IAB category ID. example: "IAB-111" * @property {Array} [secondaryCatIds] - Array of secondary IAB category IDs. example: ["IAB-222","IAB-333"] diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js index f41d6a7c96b..e64248de53d 100644 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -15,17 +15,35 @@ const zlib = require('zlib'); const openxAdapter = openxAdapterParams.adapter; describe('openx analytics adapter', function() { - it.skip('should require publisher id', function() { - sinon.spy(utils, 'logError'); + describe('when validating the configuration', function () { + let spy; + beforeEach(function () { + spy = sinon.spy(utils, 'logError'); + }); + + afterEach(function(){ + utils.logError.restore(); + }); - openxAdapter.enableAnalytics(); - expect( - utils.logError.calledWith( - 'OpenX analytics adapter: publisherId is required.' - ) - ).to.be.true; + it('should require publisher id when no configuration is passed', function() { + openxAdapter.enableAnalytics(); + expect(spy.firstCall.args[0]).to.match(/publisherPlatformId/); + expect(spy.firstCall.args[0]).to.match(/to exist/); + }); + + it('should validate types', function() { + openxAdapter.enableAnalytics({ + provider: 'openx', + options: { + publisherPlatformId: 'test platformId', + publisherAccountId: 123, + sampling: 'invalid-float' + } + }); - utils.logError.restore(); + expect(spy.firstCall.args[0]).to.match(/sampling/); + expect(spy.firstCall.args[0]).to.match(/type 'number'/); + }); }); describe('sending analytics event', function() { @@ -45,12 +63,12 @@ describe('openx analytics adapter', function() { clock.restore(); }); - const openxAdUnitInfo = {'code': 'div-1', + const openxAdUnitInfo = [{'code': 'div-1', 'mediaTypes': {'banner': {'sizes': [[320, 50]]}}, 'bids': [{'bidder': 'openx', 'params': {'unit': '540249866', 'delDomain': 'sademo-d.openx.net'}}], 'sizes': [[320, 50]], - 'transactionId': 'ac66c3e6-3118-4213-a3ae-8cdbe4f72873'}; + 'transactionId': 'ac66c3e6-3118-4213-a3ae-8cdbe4f72873'}]; const bidRequestedOpenX = { auctionId: 'add5eb0f-587d-441d-86ec-bbb722c70f79', @@ -204,7 +222,7 @@ describe('openx analytics adapter', function() { publisherAccountId: 537143056, sampling: 1.0, testCode: 'test-code-1', - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); }); @@ -463,8 +481,8 @@ describe('openx analytics adapter', function() { [AUCTION_INIT, auctionInit], [BID_REQUESTED, bidRequestedOpenX], [BID_RESPONSE, bidResponseOpenX], + [AUCTION_END, auctionEnd], [BID_WON, bidWonOpenX], - [AUCTION_END, auctionEnd] ]); // Handle timeouts @@ -566,7 +584,7 @@ describe('openx analytics adapter', function() { }); }); - describe('when test pipeline is enabled', function () { + describe('when version 2 is enabled', function () { const AD_UNIT_CODE = 'test-div-1'; const auctionInit = { @@ -717,11 +735,11 @@ describe('openx analytics adapter', function() { beforeEach(function () { openxAdapter.enableAnalytics({ options: { - publisherAccountId: 'test-publisher-account-id', + publisherAccountId: 123, publisherPlatformId: 'test-platform-id', sample: 1.0, - testPipeline: true, - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + enableV2: true, + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); @@ -766,12 +784,12 @@ describe('openx analytics adapter', function() { beforeEach(function () { openxAdapter.enableAnalytics({ options: { - publisherAccountId: 'test-publisher-account-id', + publisherAccountId: 123, publisherPlatformId: 'test-platform-id', sample: 1.0, - testPipeline: true, + enableV2: true, testCode: 'test-code', - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); @@ -797,11 +815,11 @@ describe('openx analytics adapter', function() { beforeEach(function () { openxAdapter.enableAnalytics({ options: { - publisherAccountId: 'test-publisher-account-id', + publisherAccountId: 123, publisherPlatformId: 'test-platform-id', sample: 1.0, - testPipeline: true, - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + enableV2: true, + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); @@ -852,11 +870,11 @@ describe('openx analytics adapter', function() { beforeEach(function () { openxAdapter.enableAnalytics({ options: { - publisherAccountId: 'test-publisher-account-id', + publisherAccountId: 123, publisherPlatformId: 'test-platform-id', sample: 1.0, - testPipeline: true, - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + enableV2: true, + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); @@ -893,11 +911,11 @@ describe('openx analytics adapter', function() { beforeEach(function () { openxAdapter.enableAnalytics({ options: { - publisherAccountId: 'test-publisher-account-id', + publisherAccountId: 123, publisherPlatformId: 'test-platform-id', sample: 1.0, - testPipeline: true, - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + enableV2: true, + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); @@ -921,8 +939,8 @@ describe('openx analytics adapter', function() { }); it('should track the cpm in microCPM', function () { - expect(openxBidResponse.microCPM).to.equal(bidResponseOpenX.cpm * 1000); - expect(closexBidResponse.microCPM).to.equal(bidResponseCloseX.cpm * 1000); + expect(openxBidResponse.microCpm).to.equal(bidResponseOpenX.cpm * 1000); + expect(closexBidResponse.microCpm).to.equal(bidResponseCloseX.cpm * 1000); }); it('should track if the bid is in net revenue', function () { @@ -975,11 +993,11 @@ describe('openx analytics adapter', function() { beforeEach(function () { openxAdapter.enableAnalytics({ options: { - publisherAccountId: 'test-publisher-account-id', publisherPlatformId: 'test-platform-id', + publisherAccountId: 123, sample: 1.0, - testPipeline: true, - slotLoadWaitTime: SLOT_LOAD_WAIT_TIME + enableV2: true, + payloadWaitTime: SLOT_LOAD_WAIT_TIME } }); @@ -1026,4 +1044,256 @@ describe('openx analytics adapter', function() { }); }); }); + + describe('when we\'re testing the pipeline', function () { + const AD_UNIT_CODE = 'test-div-1'; + + const auctionInit = { + auctionId: 'test-auction-id', + timestamp: 1586000000000, + timeout: 3000, + adUnitCodes: [AD_UNIT_CODE], + }; + + const bidRequestedOpenX = { + auctionId: 'test-auction-id', + auctionStart: 1586000000000, + bids: [ + { + adUnitCode: AD_UNIT_CODE, + bidId: 'test-openx-request-id', + bidder: 'openx', + params: { unit: 'test-openx-ad-unit-id' }, + } + ], + start: 1586000000010 + }; + + const bidRequestedCloseX = { + auctionId: 'test-auction-id', + auctionStart: 1586000000000, + bids: [ + { + adUnitCode: AD_UNIT_CODE, + bidId: 'test-closex-request-id', + bidder: 'closex', + params: { unit: 'test-closex-ad-unit-id' }, + } + ], + start: 1586000000020 + }; + + const bidResponseOpenX = { + adUnitCode: AD_UNIT_CODE, + cpm: 0.5, + netRevenue: true, + requestId: 'test-openx-request-id', + mediaType: 'banner', + width: 300, + height: 250, + adId: 'test-openx-ad-id', + auctionId: 'test-auction-id', + creativeId: 'openx-crid', + currency: 'USD', + timeToRespond: 100, + responseTimestamp: 1586000000030, + ts: 'test-openx-ts' + }; + + const bidResponseCloseX = { + adUnitCode: AD_UNIT_CODE, + cpm: 0.3, + netRevenue: true, + requestId: 'test-closex-request-id', + mediaType: 'video', + width: 300, + height: 250, + adId: 'test-closex-ad-id', + auctionId: 'test-auction-id', + creativeId: 'closex-crid', + currency: 'USD', + timeToRespond: 200, + dealId: 'test-closex-deal-id', + responseTimestamp: 1586000000040, + ts: 'test-closex-ts' + }; + + const bidTimeoutOpenX = [{ + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id', + bidId: 'test-openx-request-id' + }]; + + const bidTimeoutCloseX = [{ + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id', + bidId: 'test-closex-request-id' + }]; + + const openxAdUnitInfo = [{'code': 'test-div-1', + 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, + 'bids': [{'bidder': 'openx', + 'params': {'unit': '540249866', 'delDomain': 'sademo-d.openx.net'}}, + {'bidder': 'closex', + 'params': {'unit': '540249866', 'delDomain': 'sademo-d.openx.net'}}], + 'sizes': [[300, 250]], + 'transactionId': 'test-transaction-id'}]; + + const auctionEnd = { + 'auctionId': 'test-auction-id', + 'timestamp': 1586000000000, + 'auctionEnd': 1586000000100, + 'auctionStatus': 'completed', + 'adUnits': openxAdUnitInfo, + 'adUnitCodes': [ + 'test-div-1' + ], + 'bidderRequests': [bidRequestedOpenX, bidRequestedCloseX], + 'noBids': [], + 'bidsReceived': [bidResponseOpenX, bidResponseCloseX], + 'winningBids': [bidResponseOpenX], + 'timeout': 300 + }; + + const bidWonOpenX = { + requestId: 'test-openx-request-id', + adId: 'test-openx-ad-id', + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id' + }; + + const bidWonCloseX = { + requestId: 'test-closex-request-id', + adId: 'test-closex-ad-id', + adUnitCode: AD_UNIT_CODE, + auctionId: 'test-auction-id' + }; + + let highestBid; + const onSlotLoadEvent = { + eventType: SLOT_LOADED, + args: { + slot: { + getAdUnitPath: () => { + return '/12345678/test_ad_unit'; + }, + getSlotElementId: () => { + return AD_UNIT_CODE; + }, + getTargeting: () => { + return highestBid ? [highestBid.adId] : [] + } + } + } + }; + function simulateAuction(events) { + events.forEach(event => { + const [eventType, args] = event; + openxAdapter.track({eventType, args}); + if (eventType === BID_RESPONSE) { + highestBid = highestBid || args; + if (highestBid.cpm < args.cpm) { + highestBid = args; + } + } + }); + openxAdapter.slotOnLoad(onSlotLoadEvent.args); + openxAdapter.track(onSlotLoadEvent); + } + + const SLOT_LOAD_WAIT_TIME = 10; + let clock; + + beforeEach(function() { + sinon.stub(events, 'getEvents').returns([]); + clock = sinon.useFakeTimers(); + }); + + afterEach(function() { + events.getEvents.restore(); + clock.restore(); + }); + + describe('when are bidder wins', function () { + const CURRENT_TIME = 1586000000000; + let v1Auction; + let v2Auction; + beforeEach(function () { + openxAdapter.enableAnalytics({ + options: { + publisherAccountId: 123123, + publisherPlatformId: 'test-platform-id', + sample: 1.0, + testPipeline: true, + payloadWaitTime: SLOT_LOAD_WAIT_TIME + } + }); + + // set current time + clock = sinon.useFakeTimers(CURRENT_TIME); + + simulateAuction([ + [AUCTION_INIT, auctionInit], + [BID_REQUESTED, bidRequestedOpenX], + [BID_REQUESTED, bidRequestedCloseX], + [BID_RESPONSE, bidResponseOpenX], + [BID_RESPONSE, bidResponseCloseX], + [AUCTION_END, auctionEnd], + [BID_WON, bidWonOpenX] + ]); + + clock.tick(SLOT_LOAD_WAIT_TIME + 1); + + // ******* + let compressedPayload = server.requests[0].requestBody; + let payloadBuffer = new Buffer(compressedPayload); + let unCompressedPayload = zlib.gunzipSync(payloadBuffer).toString(); + v1Auction = JSON.parse(unCompressedPayload); + + v2Auction = JSON.parse(server.requests[1].requestBody)[0]; + }); + + afterEach(function () { + clock.restore(); + openxAdapter.reset(); + openxAdapter.disableAnalytics(); + }); + + it('should send out both payloads', function(){ + expect(server.requests.length).to.equal(2); + }); + + it('should track the bidder as the winner in both requests', function () { + let biddersRequests = []; + let biddersResponded = []; + v1Auction.events.forEach(function (event) { + if (event.eventType === AUCTION_END) { + event.args.bidderRequests.forEach(function(bidRequestInfo) { + if (bidRequestInfo.bids.length > 0) { + biddersRequests.push(bidRequestInfo.bids[0].bidder); + } + }); + event.args.bidsReceived.forEach(function(bidsInfo) { + biddersResponded.push(bidsInfo); + }); + } + }); + + expect(biddersRequests.length).to.equal(2); + expect(biddersRequests[0]).to.equal('openx'); + expect(biddersRequests[1]).to.equal('closex'); + expect(biddersResponded.length).to.equal(2); + + let bidWonEventInfoList = v1Auction.events.filter(function (event) { + return event.eventType === BID_WON && event.args.auctionId === bidWonOpenX.auctionId; + }); + + expect(bidWonEventInfoList.length).to.equal(1); + + + let openxBidder = v2Auction.bidRequests.find(bidderRequest => bidderRequest.bidder === 'openx'); + expect(openxBidder.bidResponses[0]).to.contain({winner: true}); + }); + }); + }); });