From 93317e6f5f15007f3ac03765ff3fe7a8d61dcc13 Mon Sep 17 00:00:00 2001 From: jenda Date: Tue, 12 Mar 2024 16:24:29 +0100 Subject: [PATCH 1/3] r2b2 analytic adapter --- modules/r2b2AnalyticsAdapter.js | 574 ++++++++++ modules/r2b2AnalyticsAdapter.md | 32 + .../spec/modules/r2b2AnalytiscAdapter_spec.js | 1010 +++++++++++++++++ 3 files changed, 1616 insertions(+) create mode 100644 modules/r2b2AnalyticsAdapter.js create mode 100644 modules/r2b2AnalyticsAdapter.md create mode 100644 test/spec/modules/r2b2AnalytiscAdapter_spec.js diff --git a/modules/r2b2AnalyticsAdapter.js b/modules/r2b2AnalyticsAdapter.js new file mode 100644 index 00000000000..6bf9f5a0f3e --- /dev/null +++ b/modules/r2b2AnalyticsAdapter.js @@ -0,0 +1,574 @@ +import {ajax} from '../src/ajax.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import {EVENTS} from 'src/constants.js'; +import adapterManager from '../src/adapterManager.js'; +import {getGlobal} from '../src/prebidGlobal.js'; +import {logWarn, logError, isNumber, isStr, isPlainObject} from '../src/utils.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {config} from '../src/config.js'; + +const ADAPTER_VERSION = '1.1.0'; +const ADAPTER_CODE = 'r2b2'; +const MODULE_NAME = 'R2B2 Analytics' +const GVLID = 1235; +const analyticsType = 'endpoint'; + +const DEFAULT_SERVER = 'log.r2b2.cz'; +const DEFAULT_EVENT_PATH = 'prebid/events'; +const DEFAULT_ERROR_PATH = 'error'; +const DEFAULT_PROTOCOL = 'https'; + +const ERROR_MAX = 10; +const BATCH_SIZE = 50; +const BATCH_DELAY = 500; +const REPORTED_URL = getRefererInfo().page || getRefererInfo().topmostLocation; + +const START_TIME = Date.now(); + +const EVENT_MAP = {}; +EVENT_MAP[EVENTS.NO_BID] = 'noBid'; +EVENT_MAP[EVENTS.AUCTION_INIT] = 'init'; +EVENT_MAP[EVENTS.BID_REQUESTED] = 'request'; +EVENT_MAP[EVENTS.BID_TIMEOUT] = 'timeout'; +EVENT_MAP[EVENTS.BID_RESPONSE] = 'response'; +EVENT_MAP[EVENTS.BID_REJECTED] = 'reject'; +EVENT_MAP[EVENTS.BIDDER_ERROR] = 'bidError'; +EVENT_MAP[EVENTS.BIDDER_DONE] = 'bidderDone'; +EVENT_MAP[EVENTS.AUCTION_END] = 'auction'; +EVENT_MAP[EVENTS.BID_WON] = 'bidWon'; +EVENT_MAP[EVENTS.SET_TARGETING] = 'targeting'; +EVENT_MAP[EVENTS.STALE_RENDER] = 'staleRender'; +EVENT_MAP[EVENTS.AD_RENDER_SUCCEEDED] = 'render'; +EVENT_MAP[EVENTS.AD_RENDER_FAILED] = 'renderFail'; +EVENT_MAP[EVENTS.BID_VIEWABLE] = 'view'; + +/* CONFIGURATION */ +let WEBSITE = 0; +let CONFIG_ID = 0; +let CONFIG_VERSION = 0; +let LOG_SERVER = DEFAULT_SERVER; + +/* CACHED DATA */ +let orderedAuctions = []; +let auctionsData = {}; +let bidsData = {}; +let adServerCurrency = ''; + +let flushTimer; +let eventBuffer = []; +let errors = 0; +function flushEvents () { + let events = { prebid: { e: eventBuffer, c: adServerCurrency } }; + eventBuffer = []; + reportEvents(events) +} + +export function resetAnalyticAdapter(){ + orderedAuctions = []; + auctionsData = {}; + bidsData = {}; + adServerCurrency = ''; + clearTimeout(flushTimer); + eventBuffer = []; + errors = []; + + WEBSITE = 0; + CONFIG_ID = 0; + CONFIG_VERSION = 0; + LOG_SERVER = DEFAULT_SERVER; +} +function processEvent (event) { + // console.log('process event:', event); + // console.log(JSON.stringify(event)); + if (!event) { + return + } + eventBuffer.push(event); + if (flushTimer) { + clearTimeout(flushTimer); + flushTimer = null + } + if (eventBuffer.length >= BATCH_SIZE) { + flushEvents(); + } else { + flushTimer = setTimeout(flushEvents, BATCH_DELAY); + } +} + +function processErrorParams(params) { + if (isPlainObject(params)) { + try { + return JSON.stringify(params); + } catch (e) { /* do nothing */ } + } + return null +} +function reportError (message, params) { + errors++; + if (errors > ERROR_MAX) return; + params = processErrorParams(params); + message = `[ANALYTICS-${ADAPTER_VERSION}] ${message}`; + const url = r2b2Analytics.getErrorUrl() + + `?d=${encodeURIComponent(WEBSITE)}` + + `&m=${encodeURIComponent(message)}` + + `&t=prebid` + + `&p=1` + + (params ? `&pr=${encodeURIComponent(params)}` : '') + + (CONFIG_ID ? `&conf=${encodeURIComponent(CONFIG_ID)}` : '') + + (CONFIG_VERSION ? `&conf_ver=${encodeURIComponent(CONFIG_VERSION)}` : '') + + `&u=${encodeURIComponent(REPORTED_URL)}`; + ajax(url, null, null, {}); +} +function reportEvents (events) { + try { + let data = 'events=' + JSON.stringify(events); + let url = r2b2Analytics.getUrl() + + `?v=${encodeURIComponent(ADAPTER_VERSION)}` + + `&hbDomain=${encodeURIComponent(WEBSITE)}` + + (CONFIG_ID ? `&conf=${encodeURIComponent(CONFIG_ID)}` : '') + + (CONFIG_VERSION ? `&conf_ver=${encodeURIComponent(CONFIG_VERSION)}` : '') + + `&u=${encodeURIComponent(REPORTED_URL)}`; + let headers = { + contentType: 'application/x-www-form-urlencoded' + } + data = data.replace(/&/g, '%26'); + ajax(url, null, data, headers); + } catch (e) { + const msg = `Error sending events - ${e.message}`; + logError(`${MODULE_NAME}: ${msg}`); + reportError(msg); + } +} + +function getStandardTargeting (obj) { + if (obj) { + return { + b: obj.hb_bidder || '', + sz: obj.hb_size || '', + pb: obj.hb_pb || '', + fmt: obj.hb_format || '' + } + } +} +function getEventTimestamps (eventName, auctionId) { + const timestamps = { + t: Date.now() - START_TIME + }; + if (!auctionId || !auctionsData[auctionId]) { + return timestamps + } + const auctionData = auctionsData[auctionId]; + + timestamps.to = auctionData.timeout; + timestamps.ts = auctionData.start - START_TIME; + if (auctionData.end) { + timestamps.te = auctionData.end - START_TIME; + } + if (eventName === EVENT_MAP[EVENTS.AUCTION_INIT] && orderedAuctions.length > 1) { + const prevAuctionId = orderedAuctions[orderedAuctions.length - 2]; + timestamps.tprev = auctionsData[prevAuctionId].start - START_TIME; + } + return timestamps +} + +function createEvent (name, data, auctionId) { + if (!auctionId || !auctionsData[auctionId]) { + reportError('No auction data when creating event', { + event: name, + auctionId: !!auctionId + }); + return null + } + if (auctionsData[auctionId] && auctionsData[auctionId].empty) { + return null + } + + data = data || {}; + data.ai = auctionId; + + return { + e: name, + d: data, + t: getEventTimestamps(name, auctionId) + } +} + +function createAuctionData (auction, empty) { + const auctionId = auction.auctionId; + orderedAuctions.push(auctionId); + auctionsData[auctionId] = { + start: auction.timestamp, + end: auction.auctionEnd ? auction.auctionEnd : null, + timeout: auction.timeout, + empty: !!empty + }; +} +function handleAuctionInit (args) { + // console.log('auction init:', arguments); + createAuctionData(args); + const auctionId = args.auctionId; + const bidderRequests = args.bidderRequests || []; + const data = { + o: orderedAuctions.length, + u: bidderRequests.reduce((result, bidderRequest) => { + bidderRequest.bids.forEach((bid) => { + if (!result[bid.adUnitCode]) { + result[bid.adUnitCode] = [] + } + result[bid.adUnitCode].push(bid.bidder) + }); + return result + }, {}) + }; + const event = createEvent(EVENT_MAP[EVENTS.AUCTION_INIT], data, auctionId); + processEvent(event); +} +function handleBidRequested (args) { + // console.log('bid request:', arguments); + const data = { + b: args.bidderCode, + u: args.bids.reduce((result, bid) => { + if (!result[bid.adUnitCode]) { + result[bid.adUnitCode] = 1 + } else { + result[bid.adUnitCode]++ + } + return result + }, {}) + }; + const event = createEvent(EVENT_MAP[EVENTS.BID_REQUESTED], data, args.auctionId); + processEvent(event); +} +function handleBidTimeout (args) { + // console.log('bid timeout:', arguments); + const auctionId = args.length ? args[0].auctionId : null; + if (auctionId) { + let bidders = args.reduce((result, bid) => { + if (!result[bid.bidder]) { + result[bid.bidder] = {} + } + const bidderData = result[bid.bidder]; + if (!bidderData[bid.adUnitCode]) { + bidderData[bid.adUnitCode] = 1 + } else { + bidderData[bid.adUnitCode]++ + } + return result + }, {}); + + const data = { + b: bidders, + } + const event = createEvent(EVENT_MAP[EVENTS.BID_TIMEOUT], data, auctionId); + processEvent(event); + } +} +function handleNoBid (args) { + // console.log('no bid:', arguments); + const data = { + b: args.bidder, + u: args.adUnitCode + }; + const event = createEvent(EVENT_MAP[EVENTS.NO_BID], data, args.auctionId); + processEvent(event); +} +function handleBidResponse (args) { + // console.log('bid response:', arguments); + bidsData[args.adId] = { + id: args.requestId, + auctionId: args.auctionId + }; + const data = { + b: args.bidder, + u: args.adUnitCode, + p: args.cpm, + op: args.originalCpm, + c: args.currency, + oc: args.originalCurrency, + sz: args.size, + st: args.status, + rt: args.timeToRespond, + bi: args.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.BID_RESPONSE], data, args.auctionId); + processEvent(event); +} +function handleBidRejected (args) { + // console.log('bid rejected:', arguments); + const data = { + b: args.bidder, + u: args.adUnitCode, + p: args.cpm, + c: args.currency, + r: args.rejectionReason, + bi: args.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.BID_REJECTED], data, args.auctionId); + processEvent(event); +} +function handleBidderDone (args) { + // console.log('bidder done:', arguments); + const data = { + b: args.bidderCode + }; + const event = createEvent(EVENT_MAP[EVENTS.BIDDER_DONE], data, args.auctionId); + processEvent(event); +} +function getAuctionUnitsData (auctionObject) { + let unitsData = {}; + const {bidsReceived, bidsRejected} = auctionObject; + let _unitsDataBidReducer = function(data, bid, key) { + const {adUnitCode, bidder} = bid; + data[adUnitCode] = data[adUnitCode] || {}; + data[adUnitCode][key] = data[adUnitCode][key] || {}; + data[adUnitCode][key][bidder] = (data[adUnitCode][key][bidder] || 0) + 1; + return data + }; + unitsData = bidsReceived.reduce((data, bid) => { + if (!bid.cpm) return data; + return _unitsDataBidReducer(data, bid, 'b') + }, unitsData); + unitsData = bidsRejected.reduce((data, bid) => { + return _unitsDataBidReducer(data, bid, 'rj') + }, unitsData); + return unitsData +} +function handleEmptyAuction(auction) { + let auctionId = auction.auctionId; + if (!auctionsData[auctionId]) { + createAuctionData(auction, true); + } +} +function handleAuctionEnd (args) { + // console.log('auction end:', arguments); + if (!args.bidderRequests.length) { + handleEmptyAuction(args); + return + } + auctionsData[args.auctionId].end = args.auctionEnd; + let winningBids = getGlobal().getHighestCpmBids() || []; + if (winningBids.length === 0) { + winningBids = getGlobal().getAllWinningBids() || []; + } + const wins = []; + winningBids.forEach((bid) => { + if (bid.auctionId === args.auctionId) { + wins.push({ + b: bid.bidder, + u: bid.adUnitCode, + p: bid.cpm, + c: bid.currency, + sz: bid.size, + bi: bid.requestId, + }) + } + }); + const data = { + wins, + u: getAuctionUnitsData(args), + o: orderedAuctions.length, + bc: args.bidsReceived.length, + nbc: args.noBids.length, + rjc: args.bidsRejected.length, + brc: args.bidderRequests.reduce((count, bidderRequest) => { + const c = bidderRequest.bids.length || 0; + return count + c + }, 0) + }; + const event = createEvent(EVENT_MAP[EVENTS.AUCTION_END], data, args.auctionId); + processEvent(event); +} +function handleBidWon (args) { + // console.log('bid won:', arguments); + const data = { + b: args.bidder, + u: args.adUnitCode, + p: args.cpm, + op: args.originalCpm, + c: args.currency, + oc: args.originalCurrency, + sz: args.size, + mt: args.mediaType, + at: getStandardTargeting(args.adserverTargeting), + o: orderedAuctions.length, + bi: args.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.BID_WON], data, args.auctionId); + processEvent(event); +} +function handleSetTargeting (args) { + // console.log('set targeting:', arguments); + let adId; + const filteredTargetings = {}; + Object.keys(args).forEach((unit) => { + if (Object.keys(args[unit]).length) { + if (!adId) { + adId = args[unit].hb_adid + } + filteredTargetings[unit] = getStandardTargeting(args[unit]); + } + }); + if (adId) { + const auctionId = bidsData[adId].auctionId; + const data = { + u: filteredTargetings + } + const event = createEvent(EVENT_MAP[EVENTS.SET_TARGETING], data, auctionId); + processEvent(event); + } +} +function handleStaleRender (args) { + // console.log('stale render:', arguments); + const data = { + b: args.bidder, + u: args.adUnitCode, + p: args.cpm, + c: args.currency, + bi: args.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.STALE_RENDER], data, args.auctionId); + processEvent(event); +} +function handleRenderSuccess (args) { + // console.log('render success:', arguments); + const {bid} = args; + bidsData[bid.adId].renderTime = Date.now(); + const data = { + b: bid.bidder, + u: bid.adUnitCode, + p: bid.cpm, + c: bid.currency, + sz: bid.size, + mt: bid.mediaType, + bi: bid.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.AD_RENDER_SUCCEEDED], data, bid.auctionId); + processEvent(event); +} +function handleRenderFailed (args) { + // console.log('render failed:', arguments); + const {bid, reason} = args; + const data = { + b: bid.bidder, + u: bid.adUnitCode, + p: bid.cpm, + c: bid.currency, + r: reason, + bi: bid.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.AD_RENDER_FAILED], data, bid.auctionId); + processEvent(event); +} +function handleBidViewable (args) { + // console.log('bid viewable:', arguments); + const renderTime = bidsData[args.adId].renderTime; + const data = { + b: args.bidder, + u: args.adUnitCode, + rt: Date.now() - renderTime, + bi: args.requestId, + }; + const event = createEvent(EVENT_MAP[EVENTS.BID_VIEWABLE], data, args.auctionId); + processEvent(event); +} + +let baseAdapter = adapter({analyticsType}); +let r2b2Analytics = Object.assign({}, baseAdapter, { + getUrl() { + return `${DEFAULT_PROTOCOL}://${LOG_SERVER}/${DEFAULT_EVENT_PATH}` + }, + getErrorUrl() { + return `${DEFAULT_PROTOCOL}://${LOG_SERVER}/${DEFAULT_ERROR_PATH}` + }, + enableAnalytics(conf = {}) { + if (isPlainObject(conf.options)) { + const {domain, configId, configVer, server} = conf.options; + if (!domain || !isStr(domain)) { + logWarn(`${MODULE_NAME}: Mandatory parameter 'domain' not configured, analytics disabled`); + return + } + WEBSITE = domain + if (server && isStr(server)) { + LOG_SERVER = server + } + if (configId && isNumber(configId)) { + CONFIG_ID = configId + } + if (configVer && isNumber(configVer)) { + CONFIG_VERSION = configVer + } + } + baseAdapter.enableAnalytics.call(this, conf); + }, + track(event) { + const {eventType, args} = event; + try { + if (!adServerCurrency) { + const currencyObj = config.getConfig('currency'); + adServerCurrency = (currencyObj && currencyObj.adServerCurrency) || 'USD'; + } + switch (eventType) { + case EVENTS.NO_BID: + handleNoBid(args) + break; + case EVENTS.AUCTION_INIT: + handleAuctionInit(args) + break; + case EVENTS.BID_REQUESTED: + handleBidRequested(args) + break; + case EVENTS.BID_TIMEOUT: + handleBidTimeout(args) + break; + case EVENTS.BID_RESPONSE: + handleBidResponse(args) + break; + case EVENTS.BID_REJECTED: + handleBidRejected(args) + break; + case EVENTS.BIDDER_DONE: + handleBidderDone(args) + break; + case EVENTS.AUCTION_END: + handleAuctionEnd(args) + break; + case EVENTS.BID_WON: + handleBidWon(args) + break; + case EVENTS.SET_TARGETING: + handleSetTargeting(args) + break; + case EVENTS.STALE_RENDER: + handleStaleRender(args) + break; + case EVENTS.AD_RENDER_SUCCEEDED: + handleRenderSuccess(args) + break; + case EVENTS.AD_RENDER_FAILED: + handleRenderFailed(args) + break; + case EVENTS.BID_VIEWABLE: + handleBidViewable(args) + break; + } + } catch (e) { + reportError(`${eventType} - ${e.message}`) + } + } +}); + +// save the base class function +r2b2Analytics.originEnableAnalytics = r2b2Analytics.enableAnalytics; + +// override enableAnalytics so we can get access to the config passed in from the page +r2b2Analytics.enableAnalytics = function (config) { + r2b2Analytics.originEnableAnalytics(config); // call the base class function +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: r2b2Analytics, + code: ADAPTER_CODE, + gvlid: GVLID +}); + +export default r2b2Analytics; diff --git a/modules/r2b2AnalyticsAdapter.md b/modules/r2b2AnalyticsAdapter.md new file mode 100644 index 00000000000..484339be106 --- /dev/null +++ b/modules/r2b2AnalyticsAdapter.md @@ -0,0 +1,32 @@ +# Overview + +``` +Module Name: R2B2 Analytics Adapter +Module Type: Analytics Adapter +Maintainer: dev@r2b2.cz +``` + +## Description + +The R2B2 Analytics Adapter enables data collection for analysis and reporting purposes. Access to collected data and the ability to start data collection require prior approval from R2B2. For approval, please contact our account team on partner@r2b2.io. + +## How to configure? + +``` +pbjs.enableAnalytics({ + provider: 'r2b2', + options: { + domain: 'example.com', + configId: 1, + configVer: 1 + } +}); +``` + +### Options + +| Name | Scope | Example | Type | Description | +|-----------|----------|-------------|----------|----------------------------------------------------------------| +| `domain` | required | example.com | `string` | R2B2 approved domain where data collection occurs | +| `configId` | optional | 1 | `int` | Identifier for different configurations under the same domain (e.g., 1 for mobile, 2 for desktop) | +| `configVer` | optional | 1 | `int` | Version number for configurations sharing the same `configId` | diff --git a/test/spec/modules/r2b2AnalytiscAdapter_spec.js b/test/spec/modules/r2b2AnalytiscAdapter_spec.js new file mode 100644 index 00000000000..72ef13b3225 --- /dev/null +++ b/test/spec/modules/r2b2AnalytiscAdapter_spec.js @@ -0,0 +1,1010 @@ +import r2b2Analytics from "../../../modules/r2b2AnalyticsAdapter"; +import {resetAnalyticAdapter} from "../../../modules/r2b2AnalyticsAdapter"; +import { expect } from "chai"; +import {EVENTS, AD_RENDER_FAILED_REASON, REJECTION_REASON} from 'src/constants.js'; +import * as pbEvents from "src/events.js"; +import * as ajax from "src/ajax.js"; +import * as utils from "src/utils"; +import {getGlobal} from "src/prebidGlobal"; +import * as prebidGlobal from "src/prebidGlobal"; +let adapterManager = require('src/adapterManager').default; + +const { NO_BID, AUCTION_INIT, BID_REQUESTED, BID_TIMEOUT, BID_RESPONSE, BID_REJECTED, BIDDER_DONE, + AUCTION_END, BID_WON, SET_TARGETING, STALE_RENDER, AD_RENDER_SUCCEEDED, AD_RENDER_FAILED, BID_VIEWABLE +} = EVENTS; + +const BANNER_SETTING_1 = { "sizes": [[300, 300],[300, 250]]}; +const BANNER_SETTING_2 = {"sizes": [[320, 150], [320, 50]]}; + +const AD_UNIT_1_CODE = "prebid_300x300"; +const AD_UNIT_2_CODE = "prebid_320x150"; +const R2B2_PID_1 = "test.cz/s2s/300x300/mobile"; +const R2B2_PID_2 = "test.cz/s2s/320x150/mobile"; +const AD_UNIT_1_TID = "0b3464bb-d80a-490e-8367-a65201a37ba3" +const AD_UNIT_2_TID = "c8c3643c-9de0-43ea-bcd6-cc0072ec9b45"; +const AD_UNIT_2_AD_ID = "22c828c62d44da5"; +const AD_UNIT_1 = { + "code": AD_UNIT_1_CODE, + "mediaTypes": { + "banner": BANNER_SETTING_1 + }, + "bids": [{ + "bidder": "r2b2", + "params": {"pid": R2B2_PID_1} + }, { + "bidder": "adf", + "params": {"mid": 1799592} + }], + "sizes": BANNER_SETTING_1.sizes, + "transactionId": AD_UNIT_1_TID, + "ortb2Imp": { + "ext": { + "tid": AD_UNIT_1_TID + } + } +} +const AD_UNIT_2 = { + "code": AD_UNIT_2_CODE, + "mediaTypes": { + "banner": BANNER_SETTING_2 + }, + "bids": [{ + "bidder": "r2b2", + "params": {"pid": R2B2_PID_2} + }, { + "bidder": "stroeerCore", + "params": { "sid": "9532ef8d-e630-45a9-88f6-3eb3eb265d58" } + } + ], + "sizes": BANNER_SETTING_2.sizes, + "transactionId": AD_UNIT_2_TID, + "ortb2Imp": { + "ext": { + "tid": AD_UNIT_2_TID + } + } +}; +const AUCTION_ID = "5b912b08-ce23-463c-a6cf-1792f7344430"; +const R2B2_BIDDER_REQUEST = { + "bidderCode": "r2b2", + "auctionId": AUCTION_ID, + "bidderRequestId": "1e5fae5d0ee471", + "bids": [ + { + "bidder": "r2b2", + "params": {"pid": R2B2_PID_1}, + "mediaTypes": { "banner": BANNER_SETTING_1}, + "adUnitCode": AD_UNIT_1_CODE, + "transactionId": "0b3464bb-d80a-490e-8367-a65201a37ba3", + "sizes": BANNER_SETTING_1.sizes, + "bidId": "27434062b8cc94", + "bidderRequestId": "1e5fae5d0ee471", + "auctionId": AUCTION_ID, + }, + { + "bidder": "r2b2", + "params": {"pid": R2B2_PID_2}, + "mediaTypes": { "banner": BANNER_SETTING_2 }, + "adUnitCode": AD_UNIT_2_CODE, + "transactionId": "c8c3643c-9de0-43ea-bcd6-cc0072ec9b45", + "sizes": BANNER_SETTING_2.sizes, + "bidId": "3c296eca6b08f4", + "bidderRequestId": "1e5fae5d0ee471", + "auctionId": AUCTION_ID, + } + ], + "auctionStart": 1727160493004, + "timeout": 10000, + "start": 1727160493009 +}; +const ADFORM_BIDDER_REQUEST = { + "bidderCode": "adf", + "auctionId": AUCTION_ID, + "bidderRequestId": "49241b449c60b4", + "bids": [{ + "bidder": "adf", + "params": { + "mid": 1799592 + }, + "mediaTypes": { "banner": BANNER_SETTING_1}, + "adUnitCode": AD_UNIT_1_CODE, + "transactionId": "0b3464bb-d80a-490e-8367-a65201a37ba3", + "sizes": BANNER_SETTING_1.sizes, + "bidId": "54ef5ac3c45b93", + "bidderRequestId": "49241b449c60b4", + "auctionId": AUCTION_ID, + }, + ], + "auctionStart": 1727160493004, + "timeout": 10000, + "start": 1727160493016 +} +const STROEER_BIDDER_REQUEST = { + "bidderCode": "stroeerCore", + "auctionId": AUCTION_ID, + "bidderRequestId": "13f374632545075", + "bids": [ + { + "bidder": "stroeerCore", + "params": { + "sid": "9532ef8d-e630-45a9-88f6-3eb3eb265d58" + }, + "mediaTypes": { "banner": BANNER_SETTING_2}, + "adUnitCode": AD_UNIT_2_CODE, + "transactionId": "0b3464bb-d80a-490e-8367-a65201a37ba3", + "sizes": BANNER_SETTING_2.sizes, + "bidId": "14fc718193b4da3", + "bidderRequestId": "13f374632545075", + "auctionId": AUCTION_ID, + }, + ], + "auctionStart": 1727160493004, + "timeout": 10000, + "start": 1727160493023 +} +const R2B2_AD_UNIT_2_BID = { + "bidderCode": "r2b2", + "width": 300, + "height": 100, + "statusMessage": "Bid available", + "adId": "22c828c62d44da5", + "requestId": "3c296eca6b08f4", + "transactionId": "c8c3643c-9de0-43ea-bcd6-cc0072ec9b45", + "auctionId": AUCTION_ID, + "mediaType": "banner", + "source": "client", + "cpm": 1.5, + "creativeId": "76190558", + "ttl": 360, + "netRevenue": true, + "currency": "USD", + "ad": "
Test creative
", + "adapterCode": "r2b2", + "originalCpm": 1.5, + "originalCurrency": "USD", + "meta": {}, + "responseTimestamp": 1727160493863, + "requestTimestamp": 1727160493009, + "bidder": "r2b2", + "adUnitCode": AD_UNIT_2_CODE, + "timeToRespond": 854, + "size": "300x100", + "adserverTargeting": { + "hb_bidder": "r2b2", + "hb_adid": AD_UNIT_2_AD_ID, + "hb_pb": "0.20", + "hb_size": "300x100", + "hb_source": "client", + "hb_format": "banner", + "hb_adomain": "", + "hb_crid": "76190558" + }, + "latestTargetedAuctionId": AUCTION_ID, + "status": 1 +} + +const MOCK = { + AUCTION_INIT: { + adUnitCodes: [AD_UNIT_1_CODE, AD_UNIT_2_CODE], + adUnits: [AD_UNIT_1, AD_UNIT_2], + bidderRequests: [R2B2_BIDDER_REQUEST, ADFORM_BIDDER_REQUEST, STROEER_BIDDER_REQUEST], + auctionId: AUCTION_ID, + }, + BID_REQUESTED: R2B2_BIDDER_REQUEST, + BID_RESPONSE: R2B2_AD_UNIT_2_BID, + BIDDER_DONE: R2B2_BIDDER_REQUEST, + AUCTION_END: { + auctionId: AUCTION_ID, + adUnitCodes: [AD_UNIT_1_CODE, AD_UNIT_2_CODE], + adUnits: [AD_UNIT_1, AD_UNIT_2], + bidderRequests: [R2B2_BIDDER_REQUEST, ADFORM_BIDDER_REQUEST, STROEER_BIDDER_REQUEST], + bidsReceived: [R2B2_AD_UNIT_2_BID], + bidsRejected: [], + noBids: [], + auctionEnd: 1727160493104 + }, + BID_WON: R2B2_AD_UNIT_2_BID, + SET_TARGETING: { + [AD_UNIT_2_CODE]: R2B2_AD_UNIT_2_BID.adserverTargeting + }, + NO_BID: { + bidder: "r2b2", + params: {pid: R2B2_PID_1}, + mediaTypes: { banner: BANNER_SETTING_1}, + adUnitCode: AD_UNIT_1_CODE, + transactionId: "a0b9d621-6b74-47ce-b7e0-cee5f8e3c124", + adUnitId: "b87edd48-9572-431d-a508-e7f956332cec", + sizes: BANNER_SETTING_1.sizes, + bidId: "121b6373a78e56b", + bidderRequestId: "104126936185f0b", + auctionId: AUCTION_ID, + src: "client", + }, + BID_TIMEOUT: [ + { + bidder: "r2b2", + mediaTypes: { "banner": BANNER_SETTING_1}, + adUnitCode: AD_UNIT_1_CODE, + transactionId: "5629f772-9eae-49fa-a749-119f4d6295f9", + adUnitId: "fb3536c6-7bcd-41a2-b96a-cb1764a06675", + sizes: BANNER_SETTING_1.sizes, + bidId: "25522556ba65bb72", + bidderRequestId: "2544c8d7e5b5aba4", + auctionId: AUCTION_ID, + timeout: 1000 + }, + { + bidder: "r2b2", + mediaTypes: { "banner": BANNER_SETTING_1}, + adUnitCode: AD_UNIT_1_CODE, + transactionId: "5629f772-9eae-49fa-a749-119f4d6295f9", + adUnitId: "fb3536c6-7bcd-41a2-b96a-cb1764a06675", + sizes: BANNER_SETTING_1.sizes, + bidId: "25522556ba65bb72", + bidderRequestId: "2544c8d7e5b5aba4", + auctionId: AUCTION_ID, + timeout: 1000 + } + ], + AD_RENDER_SUCCEEDED: { + 'doc': { + 'location': { + 'href': 'http://localhost:63342/test/prebid.html', + 'protocol': 'http:', + 'host': 'localhost:63342', + 'hostname': 'localhost', + 'port': '63342', + 'pathname': '/test/prebid.html', + 'hash': '', + 'origin': 'http://localhost:63342', + 'ancestorOrigins': { + '0': 'http://localhost:63342' + } + } + }, + 'bid': R2B2_AD_UNIT_2_BID, + 'adId': R2B2_AD_UNIT_2_BID.adId + }, + AD_RENDER_FAILED: { + bidId: "3c296eca6b08f4", + reason: AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + message: 'message', + bid: R2B2_AD_UNIT_2_BID + }, + STALE_RENDER: R2B2_AD_UNIT_2_BID, + BID_VIEWABLE: R2B2_AD_UNIT_2_BID +} +function fireEvents(events) { + return events.map((ev, i) => { + ev = Array.isArray(ev) ? ev : [ev, {i: i}]; + pbEvents.emit.apply(null, ev) + return ev; + }); +} + +function expectEvents(events, sandbox) { + events = fireEvents(events); + return { + to: { + beTrackedBy(trackFn) { + events.forEach(([eventType, args]) => { + sandbox.assert.calledWithMatch(trackFn, sandbox.match({eventType, args})); + }); + }, + beBundledTo(bundleFn) { + events.forEach(([eventType, args]) => { + sandbox.assert.calledWithMatch(bundleFn, sandbox.match.any, eventType, sandbox.match(args)) + }); + }, + }, + }; +} + +function validateAndExtractEvents(ajaxStub){ + expect(ajaxStub.calledOnce).to.equal(true); + let eventArgs = ajaxStub.firstCall.args[2]; + expect(typeof eventArgs).to.be.equal("string"); + expect(eventArgs.indexOf("events=")).to.be.equal(0); + let eventsString = eventArgs.substring(7); + let events = tryParseJSON(eventsString); + expect(events).to.not.be.undefined; + + return events; +} + +function getQueryData(url, decode = false) { + const queryArgs = url.split('?')[1].split('&'); + return queryArgs.reduce((data, arg) => { + let [key, val] = arg.split('='); + if (decode) { + val = decodeURIComponent(val); + } + if (data[key] !== undefined) { + if (!Array.isArray(data[key])) { + data[key] = [data[key]]; + } + data[key].push(val); + } else { + data[key] = val; + } + return data; + }, {}); +} + +function getPrebidEvents(events) { + return events && events.prebid && events.prebid.e; +} +function getPrebidEventsByName(events, name){ + let prebidEvents = getPrebidEvents(events); + if(!prebidEvents) return []; + + let result = []; + for(let i = 0; i < prebidEvents.length; i++){ + let event = prebidEvents[i]; + if(event.e === name){ + result.push(event); + } + } + + return result; +} + +function tryParseJSON(value) { + try { + return JSON.parse(value); + } catch (e) { + } +} +describe('r2b2 Analytics', function () { + let sandbox; + let clock; + let ajaxStub; + let getGlobalStub; + let enableAnalytics; + + before(() => { + enableAnalytics = r2b2Analytics.enableAnalytics; + }) + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + clock = sandbox.useFakeTimers(); + sandbox.stub(pbEvents, 'getEvents').returns([]); + getGlobalStub = sandbox.stub(prebidGlobal, 'getGlobal').returns({ + getHighestCpmBids: () => [R2B2_AD_UNIT_2_BID] + }); + ajaxStub = sandbox.stub(ajax, 'ajax'); + + adapterManager.registerAnalyticsAdapter({ + code: 'r2b2', + adapter: r2b2Analytics + }); + + r2b2Analytics.enableAnalytics = enableAnalytics; + }); + + afterEach(() => { + resetAnalyticAdapter(); + sandbox.restore(); + getGlobalStub.restore(); + ajaxStub.restore(); + r2b2Analytics.disableAnalytics(); + }); + + describe("config", () => { + it("missing domain", () => { + let logWarnStub = sandbox.stub(utils, 'logWarn'); + + adapterManager.enableAnalytics({ + provider: 'r2b2', + options: {} + }); + + expect(logWarnStub.calledOnce).to.be.true; + expect(logWarnStub.firstCall.args[0]).to.be.equal("R2B2 Analytics: Mandatory parameter 'domain' not configured, analytics disabled"); + logWarnStub.restore(); + }); + + it("all params error reporting", () => { + adapterManager.enableAnalytics({ + provider: 'r2b2', + options: { + domain: "test.cz", + configId: 11, + configVer: 7, + server: "delivery.local", + } + }); + + fireEvents([ + [BID_RESPONSE, MOCK.BID_RESPONSE], + ]); + + expect(ajaxStub.calledOnce).to.be.true; + expect(typeof ajaxStub.firstCall.args[0]).to.be.equal("string"); + let query = getQueryData(ajaxStub.firstCall.args[0], true); + expect(query["d"]).to.be.equal("test.cz"); + expect(query["conf"]).to.be.equal("11"); + expect(query["conf_ver"]).to.be.equal("7"); + }); + + it("all params events reporting", (done) => { + adapterManager.enableAnalytics({ + provider: 'r2b2', + options: { + domain: "test.cz", + configId: 11, + configVer: 7, + server: "delivery.local", + } + }); + + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_RESPONSE, MOCK.BID_RESPONSE], + ]); + + setTimeout(() => { + expect(ajaxStub.calledOnce).to.be.true; + expect(typeof ajaxStub.firstCall.args[0]).to.be.equal("string"); + let query = getQueryData(ajaxStub.firstCall.args[0], true); + expect(query["hbDomain"]).to.be.equal("test.cz"); + expect(query["conf"]).to.be.equal("11"); + expect(query["conf_ver"]).to.be.equal("7"); + done(); + }, 500); + + clock.tick(500); + }); + }); + + describe("events", () =>{ + beforeEach(() => { + adapterManager.enableAnalytics({ + provider: 'r2b2', + options: { + domain: 'test.com', + } + }); + }); + + it('should catch all events', function () { + sandbox.spy(r2b2Analytics, 'track'); + + expectEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_REQUESTED, MOCK.BID_REQUESTED], + [BID_RESPONSE, MOCK.BID_RESPONSE], + [AUCTION_END, MOCK.AUCTION_END], + [SET_TARGETING, MOCK.SET_TARGETING], + [BID_WON, MOCK.BID_WON], + ], sandbox).to.beTrackedBy(r2b2Analytics.track); + }); + + it('should send ajax after delay', (done) => { + fireEvents([[AUCTION_INIT, MOCK.AUCTION_INIT]]); + setTimeout(() => { + expect(ajaxStub.calledOnce).to.equal(true); + done(); + }, 500); + + clock.tick(500); + }) + + it('auction init content', (done) => { + fireEvents([[AUCTION_INIT, MOCK.AUCTION_INIT]]); + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let initEvents = getPrebidEventsByName(events, "init"); + expect(initEvents.length).to.be.equal(1); + let initEvent = initEvents[0]; + expect(initEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + u: { + [AD_UNIT_1_CODE]: ["r2b2", "adf"], + [AD_UNIT_2_CODE]: ["r2b2", "stroeerCore"] + }, + o: 1 + }) + + done(); + }, 500); + + clock.tick(500); + }) + + it('auction multiple init', (done) => { + let auction_init = MOCK.AUCTION_INIT; + let auction_init_2 = utils.deepClone(MOCK.AUCTION_INIT); + auction_init_2.auctionId = "different_auction_id"; + + fireEvents([[AUCTION_INIT, auction_init], [AUCTION_INIT, auction_init_2]]); + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let initEvents = getPrebidEventsByName(events, "init"); + expect(initEvents.length).to.be.equal(2); + done(); + }, 500); + + clock.tick(500); + }); + + it("bid requested content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_REQUESTED, MOCK.BID_REQUESTED], + [BID_REQUESTED, ADFORM_BIDDER_REQUEST], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let bidRequestedEvents= getPrebidEventsByName(events, "request"); + expect(bidRequestedEvents.length).to.be.equal(2); + let r2b2BidRequest = bidRequestedEvents[0]; + let adformBidRequest = bidRequestedEvents[1]; + expect(r2b2BidRequest.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: { + [AD_UNIT_1_CODE]: 1, + [AD_UNIT_2_CODE]: 1 + } + }); + expect(adformBidRequest.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "adf", + u: {[AD_UNIT_1_CODE]: 1} + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("no bid content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [NO_BID, MOCK.NO_BID] + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let noBidEvents= getPrebidEventsByName(events, "noBid"); + expect(noBidEvents.length).to.be.equal(1); + let noBidEvent = noBidEvents[0]; + expect(noBidEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_1_CODE + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("bid timeout content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_TIMEOUT, MOCK.BID_TIMEOUT] + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let timeoutEvents= getPrebidEventsByName(events, "timeout"); + expect(timeoutEvents.length).to.be.equal(1); + let timeoutEvent = timeoutEvents[0]; + expect(timeoutEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: { + r2b2: {[AD_UNIT_1_CODE]: 2} + } + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("bidder done content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BIDDER_DONE, MOCK.BIDDER_DONE] + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let bidderDoneEvents= getPrebidEventsByName(events, "bidderDone"); + expect(bidderDoneEvents.length).to.be.equal(1); + let bidderDoneEvent = bidderDoneEvents[0]; + expect(bidderDoneEvent.d).to.be.deep.equal({ ai: AUCTION_ID, b: "r2b2"}); + + done(); + }, 500); + + clock.tick(500); + }); + + it("auction end content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [AUCTION_END, MOCK.AUCTION_END] + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let auctionEndEvents= getPrebidEventsByName(events, "auction"); + expect(auctionEndEvents.length).to.be.equal(1); + let auctionEnd = auctionEndEvents[0]; + expect(auctionEnd.d).to.be.deep.equal({ + ai: AUCTION_ID, + wins:[{ + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, c: "USD", + sz: "300x100", + bi: R2B2_AD_UNIT_2_BID.requestId, + }], + u: {[AD_UNIT_2_CODE]: {b: {r2b2: 1}}}, + o: 1, + bc: 1, + nbc: 0, + rjc: 0, + brc: 4 + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("auction end empty auction", (done) => { + let noBidderRequestsEnd = utils.deepClone(MOCK.AUCTION_END); + noBidderRequestsEnd.bidderRequests = []; + + fireEvents([ + [AUCTION_END, noBidderRequestsEnd] + ]); + + setTimeout(() => { + expect(ajaxStub.calledOnce).to.be.false; + + done(); + }, 500); + + clock.tick(500); + }); + + it("bid response content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_RESPONSE, MOCK.BID_RESPONSE], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let bidResponseEvents= getPrebidEventsByName(events, "response"); + expect(bidResponseEvents.length).to.be.equal(1); + let bidResponseEvent = bidResponseEvents[0]; + expect(bidResponseEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + op: 1.5, + c: "USD", + oc: "USD", + sz: "300x100", + st: 1, + rt: 854, + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("bid rejected content", (done) => { + let rejectedBid = utils.deepClone(R2B2_AD_UNIT_2_BID); + rejectedBid.rejectionReason = REJECTION_REASON.FLOOR_NOT_MET; + + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_REJECTED, rejectedBid], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let rejectedBidsEvents= getPrebidEventsByName(events, "reject"); + expect(rejectedBidsEvents.length).to.be.equal(1); + let rejectedBidEvent = rejectedBidsEvents[0]; + expect(rejectedBidEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + c: "USD", + r: REJECTION_REASON.FLOOR_NOT_MET, + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("bid won content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_WON, MOCK.BID_WON], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let bidWonEvents= getPrebidEventsByName(events, "bidWon"); + expect(bidWonEvents.length).to.be.equal(1); + let bidWonEvent = bidWonEvents[0]; + expect(bidWonEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + op: 1.5, + c: "USD", + oc: "USD", + sz: "300x100", + mt: "banner", + at: { + b: "r2b2", + sz: "300x100", + pb: "0.20", + fmt: "banner" + }, + o: 1, + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("bid won content no targeting", (done) => { + let bidWonWithoutTargeting = utils.deepClone(MOCK.BID_WON); + bidWonWithoutTargeting.adserverTargeting = {}; + + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_WON, bidWonWithoutTargeting], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let bidWonEvents= getPrebidEventsByName(events, "bidWon"); + expect(bidWonEvents.length).to.be.equal(1); + let bidWonEvent = bidWonEvents[0]; + expect(bidWonEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + op: 1.5, + c: "USD", + oc: "USD", + sz: "300x100", + mt: "banner", + at: { + b: "", + sz: "", + pb: "", + fmt: "" + }, + o: 1, + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("targeting content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_RESPONSE, MOCK.BID_RESPONSE], + [SET_TARGETING, MOCK.SET_TARGETING] + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let setTargetingEvents= getPrebidEventsByName(events, "targeting"); + expect(setTargetingEvents.length).to.be.equal(1); + expect(setTargetingEvents[0].d).to.be.deep.equal({ + ai: AUCTION_ID, + u: { + [AD_UNIT_2_CODE]: { + b: "r2b2", + sz: "300x100", + pb: "0.20", + fmt: "banner" + } + } + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("ad render succeeded content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_RESPONSE, MOCK.BID_RESPONSE], + [AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let setTargetingEvents= getPrebidEventsByName(events, "render"); + expect(setTargetingEvents.length).to.be.equal(1); + let setTargeting = setTargetingEvents[0]; + expect(setTargeting.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + c: "USD", + sz: "300x100", + mt: "banner", + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("ad render failed content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_RESPONSE, MOCK.BID_RESPONSE], + [AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let renderFailedEvents= getPrebidEventsByName(events, "renderFail"); + expect(renderFailedEvents.length).to.be.equal(1); + let renderFailed = renderFailedEvents[0]; + expect(renderFailed.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + c: "USD", + r: AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("stale render content", (done) => { + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [STALE_RENDER, MOCK.STALE_RENDER], + ]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let staleRenderEvents= getPrebidEventsByName(events, "staleRender"); + expect(staleRenderEvents.length).to.be.equal(1); + let staleRenderEvent = staleRenderEvents[0]; + expect(staleRenderEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + p: 1.5, + c: "USD", + bi: R2B2_AD_UNIT_2_BID.requestId, + }); + + done(); + }, 500); + + clock.tick(500); + }); + + it("bid viewable content", (done) => { + let dateStub = sandbox.stub(Date, 'now'); + dateStub.returns(100); + + fireEvents([ + [AUCTION_INIT, MOCK.AUCTION_INIT], + [BID_RESPONSE, MOCK.BID_RESPONSE], + [AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED] + ]); + + dateStub.returns(150); + + fireEvents([[BID_VIEWABLE, MOCK.BID_VIEWABLE]]); + + setTimeout(() => { + let events = validateAndExtractEvents(ajaxStub); + let bidViewableEvents= getPrebidEventsByName(events, "view"); + expect(bidViewableEvents.length).to.be.equal(1); + let bidViewableEvent = bidViewableEvents[0]; + expect(bidViewableEvent.d).to.be.deep.equal({ + ai: AUCTION_ID, + b: "r2b2", + u: AD_UNIT_2_CODE, + rt: 50, + bi: R2B2_AD_UNIT_2_BID.requestId + }); + + done(); + }, 500); + + clock.tick(500); + dateStub.restore(); + }); + + it("no auction data error", (done) => { + fireEvents([ + [BID_RESPONSE, MOCK.BID_RESPONSE], + ]); + + setTimeout(() => { + expect(ajaxStub.calledOnce).to.be.true; + expect(typeof ajaxStub.firstCall.args[0]).to.be.equal("string"); + let query = getQueryData(ajaxStub.firstCall.args[0], true); + expect(typeof query.m).to.be.equal("string"); + expect(query.m.indexOf("No auction data when creating event")).to.not.be.equal(-1); + + done(); + }, 500); + + clock.tick(500); + }); + + it("empty auction", (done) => { + let emptyAuctionInit = utils.deepClone(MOCK.AUCTION_INIT); + emptyAuctionInit.bidderRequests = undefined; + let emptyAuctionEnd = utils.deepClone(MOCK.AUCTION_END); + emptyAuctionEnd.bidderRequests = []; + + fireEvents([ + [AUCTION_INIT, emptyAuctionInit], + [AUCTION_END, emptyAuctionEnd], + ]) + + setTimeout(() => { + expect(ajaxStub.calledOnce).to.be.true; + let events = validateAndExtractEvents(ajaxStub); + let initEvents = getPrebidEventsByName(events, "init"); + let auctionEndEvents = getPrebidEventsByName(events, "auction"); + + expect(initEvents.length).to.be.equal(1); + expect(auctionEndEvents.length).to.be.equal(0); + + done(); + }, 500); + + clock.tick(500); + }); + }); +}); From 1e5c6bb8f15c3c620f485b9aa4416de26c8aa7a9 Mon Sep 17 00:00:00 2001 From: "jan.sima" Date: Fri, 6 Dec 2024 10:27:00 +0100 Subject: [PATCH 2/3] fix import --- modules/r2b2AnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2b2AnalyticsAdapter.js b/modules/r2b2AnalyticsAdapter.js index 6bf9f5a0f3e..13939a3b8f9 100644 --- a/modules/r2b2AnalyticsAdapter.js +++ b/modules/r2b2AnalyticsAdapter.js @@ -1,6 +1,6 @@ import {ajax} from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {EVENTS} from 'src/constants.js'; +import {EVENTS} from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; import {logWarn, logError, isNumber, isStr, isPlainObject} from '../src/utils.js'; From 1e319fd42481669a7d994431401d03e65eb97bad Mon Sep 17 00:00:00 2001 From: "jan.sima" Date: Fri, 6 Dec 2024 11:20:56 +0100 Subject: [PATCH 3/3] fix lint errors --- modules/r2b2AnalyticsAdapter.js | 2 +- .../spec/modules/r2b2AnalytiscAdapter_spec.js | 559 +++++++++--------- 2 files changed, 280 insertions(+), 281 deletions(-) diff --git a/modules/r2b2AnalyticsAdapter.js b/modules/r2b2AnalyticsAdapter.js index 13939a3b8f9..8e3da8269c9 100644 --- a/modules/r2b2AnalyticsAdapter.js +++ b/modules/r2b2AnalyticsAdapter.js @@ -63,7 +63,7 @@ function flushEvents () { reportEvents(events) } -export function resetAnalyticAdapter(){ +export function resetAnalyticAdapter() { orderedAuctions = []; auctionsData = {}; bidsData = {}; diff --git a/test/spec/modules/r2b2AnalytiscAdapter_spec.js b/test/spec/modules/r2b2AnalytiscAdapter_spec.js index 72ef13b3225..5658821e95e 100644 --- a/test/spec/modules/r2b2AnalytiscAdapter_spec.js +++ b/test/spec/modules/r2b2AnalytiscAdapter_spec.js @@ -1,186 +1,184 @@ -import r2b2Analytics from "../../../modules/r2b2AnalyticsAdapter"; -import {resetAnalyticAdapter} from "../../../modules/r2b2AnalyticsAdapter"; -import { expect } from "chai"; +import r2b2Analytics from '../../../modules/r2b2AnalyticsAdapter'; +import {resetAnalyticAdapter} from '../../../modules/r2b2AnalyticsAdapter'; +import { expect } from 'chai'; import {EVENTS, AD_RENDER_FAILED_REASON, REJECTION_REASON} from 'src/constants.js'; -import * as pbEvents from "src/events.js"; -import * as ajax from "src/ajax.js"; -import * as utils from "src/utils"; -import {getGlobal} from "src/prebidGlobal"; -import * as prebidGlobal from "src/prebidGlobal"; +import * as pbEvents from 'src/events.js'; +import * as ajax from 'src/ajax.js'; +import * as utils from 'src/utils'; +import {getGlobal} from 'src/prebidGlobal'; +import * as prebidGlobal from 'src/prebidGlobal'; let adapterManager = require('src/adapterManager').default; const { NO_BID, AUCTION_INIT, BID_REQUESTED, BID_TIMEOUT, BID_RESPONSE, BID_REJECTED, BIDDER_DONE, AUCTION_END, BID_WON, SET_TARGETING, STALE_RENDER, AD_RENDER_SUCCEEDED, AD_RENDER_FAILED, BID_VIEWABLE } = EVENTS; -const BANNER_SETTING_1 = { "sizes": [[300, 300],[300, 250]]}; -const BANNER_SETTING_2 = {"sizes": [[320, 150], [320, 50]]}; +const BANNER_SETTING_1 = { 'sizes': [[300, 300], [300, 250]] }; +const BANNER_SETTING_2 = { 'sizes': [[320, 150], [320, 50]] }; -const AD_UNIT_1_CODE = "prebid_300x300"; -const AD_UNIT_2_CODE = "prebid_320x150"; -const R2B2_PID_1 = "test.cz/s2s/300x300/mobile"; -const R2B2_PID_2 = "test.cz/s2s/320x150/mobile"; -const AD_UNIT_1_TID = "0b3464bb-d80a-490e-8367-a65201a37ba3" -const AD_UNIT_2_TID = "c8c3643c-9de0-43ea-bcd6-cc0072ec9b45"; -const AD_UNIT_2_AD_ID = "22c828c62d44da5"; +const AD_UNIT_1_CODE = 'prebid_300x300'; +const AD_UNIT_2_CODE = 'prebid_320x150'; +const R2B2_PID_1 = 'test.cz/s2s/300x300/mobile'; +const R2B2_PID_2 = 'test.cz/s2s/320x150/mobile'; +const AD_UNIT_1_TID = '0b3464bb-d80a-490e-8367-a65201a37ba3' +const AD_UNIT_2_TID = 'c8c3643c-9de0-43ea-bcd6-cc0072ec9b45'; +const AD_UNIT_2_AD_ID = '22c828c62d44da5'; const AD_UNIT_1 = { - "code": AD_UNIT_1_CODE, - "mediaTypes": { - "banner": BANNER_SETTING_1 - }, - "bids": [{ - "bidder": "r2b2", - "params": {"pid": R2B2_PID_1} - }, { - "bidder": "adf", - "params": {"mid": 1799592} - }], - "sizes": BANNER_SETTING_1.sizes, - "transactionId": AD_UNIT_1_TID, - "ortb2Imp": { - "ext": { - "tid": AD_UNIT_1_TID + 'code': AD_UNIT_1_CODE, + 'mediaTypes': { + 'banner': BANNER_SETTING_1 + }, + 'bids': [{ + 'bidder': 'r2b2', + 'params': {'pid': R2B2_PID_1} + }, { + 'bidder': 'adf', + 'params': {'mid': 1799592} + }], + 'sizes': BANNER_SETTING_1.sizes, + 'transactionId': AD_UNIT_1_TID, + 'ortb2Imp': { + 'ext': { + 'tid': AD_UNIT_1_TID } } } const AD_UNIT_2 = { - "code": AD_UNIT_2_CODE, - "mediaTypes": { - "banner": BANNER_SETTING_2 + 'code': AD_UNIT_2_CODE, + 'mediaTypes': { + 'banner': BANNER_SETTING_2 }, - "bids": [{ - "bidder": "r2b2", - "params": {"pid": R2B2_PID_2} - }, { - "bidder": "stroeerCore", - "params": { "sid": "9532ef8d-e630-45a9-88f6-3eb3eb265d58" } - } - ], - "sizes": BANNER_SETTING_2.sizes, - "transactionId": AD_UNIT_2_TID, - "ortb2Imp": { - "ext": { - "tid": AD_UNIT_2_TID + 'bids': [{ + 'bidder': 'r2b2', + 'params': {'pid': R2B2_PID_2} + }, { + 'bidder': 'stroeerCore', + 'params': { 'sid': '9532ef8d-e630-45a9-88f6-3eb3eb265d58' } + }], + 'sizes': BANNER_SETTING_2.sizes, + 'transactionId': AD_UNIT_2_TID, + 'ortb2Imp': { + 'ext': { + 'tid': AD_UNIT_2_TID } } }; -const AUCTION_ID = "5b912b08-ce23-463c-a6cf-1792f7344430"; +const AUCTION_ID = '5b912b08-ce23-463c-a6cf-1792f7344430'; const R2B2_BIDDER_REQUEST = { - "bidderCode": "r2b2", - "auctionId": AUCTION_ID, - "bidderRequestId": "1e5fae5d0ee471", - "bids": [ + 'bidderCode': 'r2b2', + 'auctionId': AUCTION_ID, + 'bidderRequestId': '1e5fae5d0ee471', + 'bids': [ { - "bidder": "r2b2", - "params": {"pid": R2B2_PID_1}, - "mediaTypes": { "banner": BANNER_SETTING_1}, - "adUnitCode": AD_UNIT_1_CODE, - "transactionId": "0b3464bb-d80a-490e-8367-a65201a37ba3", - "sizes": BANNER_SETTING_1.sizes, - "bidId": "27434062b8cc94", - "bidderRequestId": "1e5fae5d0ee471", - "auctionId": AUCTION_ID, + 'bidder': 'r2b2', + 'params': {'pid': R2B2_PID_1}, + 'mediaTypes': { 'banner': BANNER_SETTING_1 }, + 'adUnitCode': AD_UNIT_1_CODE, + 'transactionId': '0b3464bb-d80a-490e-8367-a65201a37ba3', + 'sizes': BANNER_SETTING_1.sizes, + 'bidId': '27434062b8cc94', + 'bidderRequestId': '1e5fae5d0ee471', + 'auctionId': AUCTION_ID, }, { - "bidder": "r2b2", - "params": {"pid": R2B2_PID_2}, - "mediaTypes": { "banner": BANNER_SETTING_2 }, - "adUnitCode": AD_UNIT_2_CODE, - "transactionId": "c8c3643c-9de0-43ea-bcd6-cc0072ec9b45", - "sizes": BANNER_SETTING_2.sizes, - "bidId": "3c296eca6b08f4", - "bidderRequestId": "1e5fae5d0ee471", - "auctionId": AUCTION_ID, + 'bidder': 'r2b2', + 'params': {'pid': R2B2_PID_2}, + 'mediaTypes': { 'banner': BANNER_SETTING_2 }, + 'adUnitCode': AD_UNIT_2_CODE, + 'transactionId': 'c8c3643c-9de0-43ea-bcd6-cc0072ec9b45', + 'sizes': BANNER_SETTING_2.sizes, + 'bidId': '3c296eca6b08f4', + 'bidderRequestId': '1e5fae5d0ee471', + 'auctionId': AUCTION_ID, } ], - "auctionStart": 1727160493004, - "timeout": 10000, - "start": 1727160493009 + 'auctionStart': 1727160493004, + 'timeout': 10000, + 'start': 1727160493009 }; const ADFORM_BIDDER_REQUEST = { - "bidderCode": "adf", - "auctionId": AUCTION_ID, - "bidderRequestId": "49241b449c60b4", - "bids": [{ - "bidder": "adf", - "params": { - "mid": 1799592 - }, - "mediaTypes": { "banner": BANNER_SETTING_1}, - "adUnitCode": AD_UNIT_1_CODE, - "transactionId": "0b3464bb-d80a-490e-8367-a65201a37ba3", - "sizes": BANNER_SETTING_1.sizes, - "bidId": "54ef5ac3c45b93", - "bidderRequestId": "49241b449c60b4", - "auctionId": AUCTION_ID, + 'bidderCode': 'adf', + 'auctionId': AUCTION_ID, + 'bidderRequestId': '49241b449c60b4', + 'bids': [{ + 'bidder': 'adf', + 'params': { + 'mid': 1799592 }, - ], - "auctionStart": 1727160493004, - "timeout": 10000, - "start": 1727160493016 + 'mediaTypes': { 'banner': BANNER_SETTING_1 }, + 'adUnitCode': AD_UNIT_1_CODE, + 'transactionId': '0b3464bb-d80a-490e-8367-a65201a37ba3', + 'sizes': BANNER_SETTING_1.sizes, + 'bidId': '54ef5ac3c45b93', + 'bidderRequestId': '49241b449c60b4', + 'auctionId': AUCTION_ID, + }], + 'auctionStart': 1727160493004, + 'timeout': 10000, + 'start': 1727160493016 } const STROEER_BIDDER_REQUEST = { - "bidderCode": "stroeerCore", - "auctionId": AUCTION_ID, - "bidderRequestId": "13f374632545075", - "bids": [ + 'bidderCode': 'stroeerCore', + 'auctionId': AUCTION_ID, + 'bidderRequestId': '13f374632545075', + 'bids': [ { - "bidder": "stroeerCore", - "params": { - "sid": "9532ef8d-e630-45a9-88f6-3eb3eb265d58" + 'bidder': 'stroeerCore', + 'params': { + 'sid': '9532ef8d-e630-45a9-88f6-3eb3eb265d58' }, - "mediaTypes": { "banner": BANNER_SETTING_2}, - "adUnitCode": AD_UNIT_2_CODE, - "transactionId": "0b3464bb-d80a-490e-8367-a65201a37ba3", - "sizes": BANNER_SETTING_2.sizes, - "bidId": "14fc718193b4da3", - "bidderRequestId": "13f374632545075", - "auctionId": AUCTION_ID, + 'mediaTypes': { 'banner': BANNER_SETTING_2 }, + 'adUnitCode': AD_UNIT_2_CODE, + 'transactionId': '0b3464bb-d80a-490e-8367-a65201a37ba3', + 'sizes': BANNER_SETTING_2.sizes, + 'bidId': '14fc718193b4da3', + 'bidderRequestId': '13f374632545075', + 'auctionId': AUCTION_ID, }, ], - "auctionStart": 1727160493004, - "timeout": 10000, - "start": 1727160493023 + 'auctionStart': 1727160493004, + 'timeout': 10000, + 'start': 1727160493023 } const R2B2_AD_UNIT_2_BID = { - "bidderCode": "r2b2", - "width": 300, - "height": 100, - "statusMessage": "Bid available", - "adId": "22c828c62d44da5", - "requestId": "3c296eca6b08f4", - "transactionId": "c8c3643c-9de0-43ea-bcd6-cc0072ec9b45", - "auctionId": AUCTION_ID, - "mediaType": "banner", - "source": "client", - "cpm": 1.5, - "creativeId": "76190558", - "ttl": 360, - "netRevenue": true, - "currency": "USD", - "ad": "
Test creative
", - "adapterCode": "r2b2", - "originalCpm": 1.5, - "originalCurrency": "USD", - "meta": {}, - "responseTimestamp": 1727160493863, - "requestTimestamp": 1727160493009, - "bidder": "r2b2", - "adUnitCode": AD_UNIT_2_CODE, - "timeToRespond": 854, - "size": "300x100", - "adserverTargeting": { - "hb_bidder": "r2b2", - "hb_adid": AD_UNIT_2_AD_ID, - "hb_pb": "0.20", - "hb_size": "300x100", - "hb_source": "client", - "hb_format": "banner", - "hb_adomain": "", - "hb_crid": "76190558" + 'bidderCode': 'r2b2', + 'width': 300, + 'height': 100, + 'statusMessage': 'Bid available', + 'adId': '22c828c62d44da5', + 'requestId': '3c296eca6b08f4', + 'transactionId': 'c8c3643c-9de0-43ea-bcd6-cc0072ec9b45', + 'auctionId': AUCTION_ID, + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 1.5, + 'creativeId': '76190558', + 'ttl': 360, + 'netRevenue': true, + 'currency': 'USD', + 'ad': '
Test creative
', + 'adapterCode': 'r2b2', + 'originalCpm': 1.5, + 'originalCurrency': 'USD', + 'meta': {}, + 'responseTimestamp': 1727160493863, + 'requestTimestamp': 1727160493009, + 'bidder': 'r2b2', + 'adUnitCode': AD_UNIT_2_CODE, + 'timeToRespond': 854, + 'size': '300x100', + 'adserverTargeting': { + 'hb_bidder': 'r2b2', + 'hb_adid': AD_UNIT_2_AD_ID, + 'hb_pb': '0.20', + 'hb_size': '300x100', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': '', + 'hb_crid': '76190558' }, - "latestTargetedAuctionId": AUCTION_ID, - "status": 1 + 'latestTargetedAuctionId': AUCTION_ID, + 'status': 1 } const MOCK = { @@ -208,40 +206,40 @@ const MOCK = { [AD_UNIT_2_CODE]: R2B2_AD_UNIT_2_BID.adserverTargeting }, NO_BID: { - bidder: "r2b2", - params: {pid: R2B2_PID_1}, - mediaTypes: { banner: BANNER_SETTING_1}, + bidder: 'r2b2', + params: { pid: R2B2_PID_1 }, + mediaTypes: { banner: BANNER_SETTING_1 }, adUnitCode: AD_UNIT_1_CODE, - transactionId: "a0b9d621-6b74-47ce-b7e0-cee5f8e3c124", - adUnitId: "b87edd48-9572-431d-a508-e7f956332cec", + transactionId: 'a0b9d621-6b74-47ce-b7e0-cee5f8e3c124', + adUnitId: 'b87edd48-9572-431d-a508-e7f956332cec', sizes: BANNER_SETTING_1.sizes, - bidId: "121b6373a78e56b", - bidderRequestId: "104126936185f0b", + bidId: '121b6373a78e56b', + bidderRequestId: '104126936185f0b', auctionId: AUCTION_ID, - src: "client", + src: 'client', }, BID_TIMEOUT: [ { - bidder: "r2b2", - mediaTypes: { "banner": BANNER_SETTING_1}, + bidder: 'r2b2', + mediaTypes: { 'banner': BANNER_SETTING_1 }, adUnitCode: AD_UNIT_1_CODE, - transactionId: "5629f772-9eae-49fa-a749-119f4d6295f9", - adUnitId: "fb3536c6-7bcd-41a2-b96a-cb1764a06675", + transactionId: '5629f772-9eae-49fa-a749-119f4d6295f9', + adUnitId: 'fb3536c6-7bcd-41a2-b96a-cb1764a06675', sizes: BANNER_SETTING_1.sizes, - bidId: "25522556ba65bb72", - bidderRequestId: "2544c8d7e5b5aba4", + bidId: '25522556ba65bb72', + bidderRequestId: '2544c8d7e5b5aba4', auctionId: AUCTION_ID, timeout: 1000 }, { - bidder: "r2b2", - mediaTypes: { "banner": BANNER_SETTING_1}, + bidder: 'r2b2', + mediaTypes: { 'banner': BANNER_SETTING_1 }, adUnitCode: AD_UNIT_1_CODE, - transactionId: "5629f772-9eae-49fa-a749-119f4d6295f9", - adUnitId: "fb3536c6-7bcd-41a2-b96a-cb1764a06675", + transactionId: '5629f772-9eae-49fa-a749-119f4d6295f9', + adUnitId: 'fb3536c6-7bcd-41a2-b96a-cb1764a06675', sizes: BANNER_SETTING_1.sizes, - bidId: "25522556ba65bb72", - bidderRequestId: "2544c8d7e5b5aba4", + bidId: '25522556ba65bb72', + bidderRequestId: '2544c8d7e5b5aba4', auctionId: AUCTION_ID, timeout: 1000 } @@ -266,7 +264,7 @@ const MOCK = { 'adId': R2B2_AD_UNIT_2_BID.adId }, AD_RENDER_FAILED: { - bidId: "3c296eca6b08f4", + bidId: '3c296eca6b08f4', reason: AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, message: 'message', bid: R2B2_AD_UNIT_2_BID @@ -300,11 +298,11 @@ function expectEvents(events, sandbox) { }; } -function validateAndExtractEvents(ajaxStub){ +function validateAndExtractEvents(ajaxStub) { expect(ajaxStub.calledOnce).to.equal(true); let eventArgs = ajaxStub.firstCall.args[2]; - expect(typeof eventArgs).to.be.equal("string"); - expect(eventArgs.indexOf("events=")).to.be.equal(0); + expect(typeof eventArgs).to.be.equal('string'); + expect(eventArgs.indexOf('events=')).to.be.equal(0); let eventsString = eventArgs.substring(7); let events = tryParseJSON(eventsString); expect(events).to.not.be.undefined; @@ -334,14 +332,14 @@ function getQueryData(url, decode = false) { function getPrebidEvents(events) { return events && events.prebid && events.prebid.e; } -function getPrebidEventsByName(events, name){ +function getPrebidEventsByName(events, name) { let prebidEvents = getPrebidEvents(events); - if(!prebidEvents) return []; + if (!prebidEvents) return []; let result = []; - for(let i = 0; i < prebidEvents.length; i++){ + for (let i = 0; i < prebidEvents.length; i++) { let event = prebidEvents[i]; - if(event.e === name){ + if (event.e === name) { result.push(event); } } @@ -391,8 +389,8 @@ describe('r2b2 Analytics', function () { r2b2Analytics.disableAnalytics(); }); - describe("config", () => { - it("missing domain", () => { + describe('config', () => { + it('missing domain', () => { let logWarnStub = sandbox.stub(utils, 'logWarn'); adapterManager.enableAnalytics({ @@ -401,18 +399,18 @@ describe('r2b2 Analytics', function () { }); expect(logWarnStub.calledOnce).to.be.true; - expect(logWarnStub.firstCall.args[0]).to.be.equal("R2B2 Analytics: Mandatory parameter 'domain' not configured, analytics disabled"); + expect(logWarnStub.firstCall.args[0]).to.be.equal('R2B2 Analytics: Mandatory parameter \'domain\' not configured, analytics disabled'); logWarnStub.restore(); }); - it("all params error reporting", () => { + it('all params error reporting', () => { adapterManager.enableAnalytics({ provider: 'r2b2', options: { - domain: "test.cz", + domain: 'test.cz', configId: 11, configVer: 7, - server: "delivery.local", + server: 'delivery.local', } }); @@ -421,21 +419,21 @@ describe('r2b2 Analytics', function () { ]); expect(ajaxStub.calledOnce).to.be.true; - expect(typeof ajaxStub.firstCall.args[0]).to.be.equal("string"); + expect(typeof ajaxStub.firstCall.args[0]).to.be.equal('string'); let query = getQueryData(ajaxStub.firstCall.args[0], true); - expect(query["d"]).to.be.equal("test.cz"); - expect(query["conf"]).to.be.equal("11"); - expect(query["conf_ver"]).to.be.equal("7"); + expect(query['d']).to.be.equal('test.cz'); + expect(query['conf']).to.be.equal('11'); + expect(query['conf_ver']).to.be.equal('7'); }); - it("all params events reporting", (done) => { + it('all params events reporting', (done) => { adapterManager.enableAnalytics({ provider: 'r2b2', options: { - domain: "test.cz", + domain: 'test.cz', configId: 11, configVer: 7, - server: "delivery.local", + server: 'delivery.local', } }); @@ -446,11 +444,11 @@ describe('r2b2 Analytics', function () { setTimeout(() => { expect(ajaxStub.calledOnce).to.be.true; - expect(typeof ajaxStub.firstCall.args[0]).to.be.equal("string"); + expect(typeof ajaxStub.firstCall.args[0]).to.be.equal('string'); let query = getQueryData(ajaxStub.firstCall.args[0], true); - expect(query["hbDomain"]).to.be.equal("test.cz"); - expect(query["conf"]).to.be.equal("11"); - expect(query["conf_ver"]).to.be.equal("7"); + expect(query['hbDomain']).to.be.equal('test.cz'); + expect(query['conf']).to.be.equal('11'); + expect(query['conf_ver']).to.be.equal('7'); done(); }, 500); @@ -458,7 +456,7 @@ describe('r2b2 Analytics', function () { }); }); - describe("events", () =>{ + describe('events', () => { beforeEach(() => { adapterManager.enableAnalytics({ provider: 'r2b2', @@ -495,14 +493,14 @@ describe('r2b2 Analytics', function () { fireEvents([[AUCTION_INIT, MOCK.AUCTION_INIT]]); setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let initEvents = getPrebidEventsByName(events, "init"); + let initEvents = getPrebidEventsByName(events, 'init'); expect(initEvents.length).to.be.equal(1); let initEvent = initEvents[0]; expect(initEvent.d).to.be.deep.equal({ ai: AUCTION_ID, u: { - [AD_UNIT_1_CODE]: ["r2b2", "adf"], - [AD_UNIT_2_CODE]: ["r2b2", "stroeerCore"] + [AD_UNIT_1_CODE]: ['r2b2', 'adf'], + [AD_UNIT_2_CODE]: ['r2b2', 'stroeerCore'] }, o: 1 }) @@ -516,12 +514,12 @@ describe('r2b2 Analytics', function () { it('auction multiple init', (done) => { let auction_init = MOCK.AUCTION_INIT; let auction_init_2 = utils.deepClone(MOCK.AUCTION_INIT); - auction_init_2.auctionId = "different_auction_id"; + auction_init_2.auctionId = 'different_auction_id'; fireEvents([[AUCTION_INIT, auction_init], [AUCTION_INIT, auction_init_2]]); setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let initEvents = getPrebidEventsByName(events, "init"); + let initEvents = getPrebidEventsByName(events, 'init'); expect(initEvents.length).to.be.equal(2); done(); }, 500); @@ -529,7 +527,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid requested content", (done) => { + it('bid requested content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_REQUESTED, MOCK.BID_REQUESTED], @@ -538,13 +536,13 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let bidRequestedEvents= getPrebidEventsByName(events, "request"); + let bidRequestedEvents = getPrebidEventsByName(events, 'request'); expect(bidRequestedEvents.length).to.be.equal(2); let r2b2BidRequest = bidRequestedEvents[0]; let adformBidRequest = bidRequestedEvents[1]; expect(r2b2BidRequest.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: { [AD_UNIT_1_CODE]: 1, [AD_UNIT_2_CODE]: 1 @@ -552,7 +550,7 @@ describe('r2b2 Analytics', function () { }); expect(adformBidRequest.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "adf", + b: 'adf', u: {[AD_UNIT_1_CODE]: 1} }); @@ -562,7 +560,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("no bid content", (done) => { + it('no bid content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [NO_BID, MOCK.NO_BID] @@ -570,12 +568,12 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let noBidEvents= getPrebidEventsByName(events, "noBid"); + let noBidEvents = getPrebidEventsByName(events, 'noBid'); expect(noBidEvents.length).to.be.equal(1); let noBidEvent = noBidEvents[0]; expect(noBidEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_1_CODE }); @@ -585,7 +583,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid timeout content", (done) => { + it('bid timeout content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_TIMEOUT, MOCK.BID_TIMEOUT] @@ -593,7 +591,7 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let timeoutEvents= getPrebidEventsByName(events, "timeout"); + let timeoutEvents = getPrebidEventsByName(events, 'timeout'); expect(timeoutEvents.length).to.be.equal(1); let timeoutEvent = timeoutEvents[0]; expect(timeoutEvent.d).to.be.deep.equal({ @@ -609,7 +607,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bidder done content", (done) => { + it('bidder done content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BIDDER_DONE, MOCK.BIDDER_DONE] @@ -617,10 +615,10 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let bidderDoneEvents= getPrebidEventsByName(events, "bidderDone"); + let bidderDoneEvents = getPrebidEventsByName(events, 'bidderDone'); expect(bidderDoneEvents.length).to.be.equal(1); let bidderDoneEvent = bidderDoneEvents[0]; - expect(bidderDoneEvent.d).to.be.deep.equal({ ai: AUCTION_ID, b: "r2b2"}); + expect(bidderDoneEvent.d).to.be.deep.equal({ ai: AUCTION_ID, b: 'r2b2' }); done(); }, 500); @@ -628,7 +626,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("auction end content", (done) => { + it('auction end content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [AUCTION_END, MOCK.AUCTION_END] @@ -636,16 +634,17 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let auctionEndEvents= getPrebidEventsByName(events, "auction"); + let auctionEndEvents = getPrebidEventsByName(events, 'auction'); expect(auctionEndEvents.length).to.be.equal(1); let auctionEnd = auctionEndEvents[0]; expect(auctionEnd.d).to.be.deep.equal({ ai: AUCTION_ID, - wins:[{ - b: "r2b2", + wins: [{ + b: 'r2b2', u: AD_UNIT_2_CODE, - p: 1.5, c: "USD", - sz: "300x100", + p: 1.5, + c: 'USD', + sz: '300x100', bi: R2B2_AD_UNIT_2_BID.requestId, }], u: {[AD_UNIT_2_CODE]: {b: {r2b2: 1}}}, @@ -662,7 +661,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("auction end empty auction", (done) => { + it('auction end empty auction', (done) => { let noBidderRequestsEnd = utils.deepClone(MOCK.AUCTION_END); noBidderRequestsEnd.bidderRequests = []; @@ -679,7 +678,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid response content", (done) => { + it('bid response content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_RESPONSE, MOCK.BID_RESPONSE], @@ -687,18 +686,18 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let bidResponseEvents= getPrebidEventsByName(events, "response"); + let bidResponseEvents = getPrebidEventsByName(events, 'response'); expect(bidResponseEvents.length).to.be.equal(1); let bidResponseEvent = bidResponseEvents[0]; expect(bidResponseEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, op: 1.5, - c: "USD", - oc: "USD", - sz: "300x100", + c: 'USD', + oc: 'USD', + sz: '300x100', st: 1, rt: 854, bi: R2B2_AD_UNIT_2_BID.requestId, @@ -710,7 +709,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid rejected content", (done) => { + it('bid rejected content', (done) => { let rejectedBid = utils.deepClone(R2B2_AD_UNIT_2_BID); rejectedBid.rejectionReason = REJECTION_REASON.FLOOR_NOT_MET; @@ -721,15 +720,15 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let rejectedBidsEvents= getPrebidEventsByName(events, "reject"); + let rejectedBidsEvents = getPrebidEventsByName(events, 'reject'); expect(rejectedBidsEvents.length).to.be.equal(1); let rejectedBidEvent = rejectedBidsEvents[0]; expect(rejectedBidEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, - c: "USD", + c: 'USD', r: REJECTION_REASON.FLOOR_NOT_MET, bi: R2B2_AD_UNIT_2_BID.requestId, }); @@ -740,7 +739,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid won content", (done) => { + it('bid won content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_WON, MOCK.BID_WON], @@ -748,24 +747,24 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let bidWonEvents= getPrebidEventsByName(events, "bidWon"); + let bidWonEvents = getPrebidEventsByName(events, 'bidWon'); expect(bidWonEvents.length).to.be.equal(1); let bidWonEvent = bidWonEvents[0]; expect(bidWonEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, op: 1.5, - c: "USD", - oc: "USD", - sz: "300x100", - mt: "banner", + c: 'USD', + oc: 'USD', + sz: '300x100', + mt: 'banner', at: { - b: "r2b2", - sz: "300x100", - pb: "0.20", - fmt: "banner" + b: 'r2b2', + sz: '300x100', + pb: '0.20', + fmt: 'banner' }, o: 1, bi: R2B2_AD_UNIT_2_BID.requestId, @@ -777,7 +776,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid won content no targeting", (done) => { + it('bid won content no targeting', (done) => { let bidWonWithoutTargeting = utils.deepClone(MOCK.BID_WON); bidWonWithoutTargeting.adserverTargeting = {}; @@ -788,24 +787,24 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let bidWonEvents= getPrebidEventsByName(events, "bidWon"); + let bidWonEvents = getPrebidEventsByName(events, 'bidWon'); expect(bidWonEvents.length).to.be.equal(1); let bidWonEvent = bidWonEvents[0]; expect(bidWonEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, op: 1.5, - c: "USD", - oc: "USD", - sz: "300x100", - mt: "banner", + c: 'USD', + oc: 'USD', + sz: '300x100', + mt: 'banner', at: { - b: "", - sz: "", - pb: "", - fmt: "" + b: '', + sz: '', + pb: '', + fmt: '' }, o: 1, bi: R2B2_AD_UNIT_2_BID.requestId, @@ -817,7 +816,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("targeting content", (done) => { + it('targeting content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_RESPONSE, MOCK.BID_RESPONSE], @@ -826,16 +825,16 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let setTargetingEvents= getPrebidEventsByName(events, "targeting"); + let setTargetingEvents = getPrebidEventsByName(events, 'targeting'); expect(setTargetingEvents.length).to.be.equal(1); expect(setTargetingEvents[0].d).to.be.deep.equal({ ai: AUCTION_ID, u: { [AD_UNIT_2_CODE]: { - b: "r2b2", - sz: "300x100", - pb: "0.20", - fmt: "banner" + b: 'r2b2', + sz: '300x100', + pb: '0.20', + fmt: 'banner' } } }); @@ -846,7 +845,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("ad render succeeded content", (done) => { + it('ad render succeeded content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_RESPONSE, MOCK.BID_RESPONSE], @@ -855,17 +854,17 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let setTargetingEvents= getPrebidEventsByName(events, "render"); + let setTargetingEvents = getPrebidEventsByName(events, 'render'); expect(setTargetingEvents.length).to.be.equal(1); let setTargeting = setTargetingEvents[0]; expect(setTargeting.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, - c: "USD", - sz: "300x100", - mt: "banner", + c: 'USD', + sz: '300x100', + mt: 'banner', bi: R2B2_AD_UNIT_2_BID.requestId, }); @@ -875,7 +874,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("ad render failed content", (done) => { + it('ad render failed content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [BID_RESPONSE, MOCK.BID_RESPONSE], @@ -884,15 +883,15 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let renderFailedEvents= getPrebidEventsByName(events, "renderFail"); + let renderFailedEvents = getPrebidEventsByName(events, 'renderFail'); expect(renderFailedEvents.length).to.be.equal(1); let renderFailed = renderFailedEvents[0]; expect(renderFailed.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, - c: "USD", + c: 'USD', r: AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, bi: R2B2_AD_UNIT_2_BID.requestId, }); @@ -903,7 +902,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("stale render content", (done) => { + it('stale render content', (done) => { fireEvents([ [AUCTION_INIT, MOCK.AUCTION_INIT], [STALE_RENDER, MOCK.STALE_RENDER], @@ -911,15 +910,15 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let staleRenderEvents= getPrebidEventsByName(events, "staleRender"); + let staleRenderEvents = getPrebidEventsByName(events, 'staleRender'); expect(staleRenderEvents.length).to.be.equal(1); let staleRenderEvent = staleRenderEvents[0]; expect(staleRenderEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, p: 1.5, - c: "USD", + c: 'USD', bi: R2B2_AD_UNIT_2_BID.requestId, }); @@ -929,7 +928,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("bid viewable content", (done) => { + it('bid viewable content', (done) => { let dateStub = sandbox.stub(Date, 'now'); dateStub.returns(100); @@ -945,12 +944,12 @@ describe('r2b2 Analytics', function () { setTimeout(() => { let events = validateAndExtractEvents(ajaxStub); - let bidViewableEvents= getPrebidEventsByName(events, "view"); + let bidViewableEvents = getPrebidEventsByName(events, 'view'); expect(bidViewableEvents.length).to.be.equal(1); let bidViewableEvent = bidViewableEvents[0]; expect(bidViewableEvent.d).to.be.deep.equal({ ai: AUCTION_ID, - b: "r2b2", + b: 'r2b2', u: AD_UNIT_2_CODE, rt: 50, bi: R2B2_AD_UNIT_2_BID.requestId @@ -963,17 +962,17 @@ describe('r2b2 Analytics', function () { dateStub.restore(); }); - it("no auction data error", (done) => { + it('no auction data error', (done) => { fireEvents([ [BID_RESPONSE, MOCK.BID_RESPONSE], ]); setTimeout(() => { expect(ajaxStub.calledOnce).to.be.true; - expect(typeof ajaxStub.firstCall.args[0]).to.be.equal("string"); + expect(typeof ajaxStub.firstCall.args[0]).to.be.equal('string'); let query = getQueryData(ajaxStub.firstCall.args[0], true); - expect(typeof query.m).to.be.equal("string"); - expect(query.m.indexOf("No auction data when creating event")).to.not.be.equal(-1); + expect(typeof query.m).to.be.equal('string'); + expect(query.m.indexOf('No auction data when creating event')).to.not.be.equal(-1); done(); }, 500); @@ -981,7 +980,7 @@ describe('r2b2 Analytics', function () { clock.tick(500); }); - it("empty auction", (done) => { + it('empty auction', (done) => { let emptyAuctionInit = utils.deepClone(MOCK.AUCTION_INIT); emptyAuctionInit.bidderRequests = undefined; let emptyAuctionEnd = utils.deepClone(MOCK.AUCTION_END); @@ -995,8 +994,8 @@ describe('r2b2 Analytics', function () { setTimeout(() => { expect(ajaxStub.calledOnce).to.be.true; let events = validateAndExtractEvents(ajaxStub); - let initEvents = getPrebidEventsByName(events, "init"); - let auctionEndEvents = getPrebidEventsByName(events, "auction"); + let initEvents = getPrebidEventsByName(events, 'init'); + let auctionEndEvents = getPrebidEventsByName(events, 'auction'); expect(initEvents.length).to.be.equal(1); expect(auctionEndEvents.length).to.be.equal(0);