diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 9317786fb8d..3aff3c6aac6 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -37,6 +37,8 @@ let _moduleParams = {}; let _data = null; /** @type {null | function} */ let _dataReadyCallback = null; +/** @type {string} */ +const DEF_KEYNAME = 'browsiViewability'; /** * add browsi script to page @@ -117,16 +119,14 @@ function waitForData(callback) { function sendDataToModule(adUnits, onDone) { try { waitForData(_predictionsData => { - const _predictions = _predictionsData.p; - if (!_predictions || !Object.keys(_predictions).length) { - return onDone({}); - } + const _predictions = _predictionsData.p || {}; let dataToReturn = adUnits.reduce((rp, cau) => { const adUnitCode = cau && cau.code; if (!adUnitCode) { return rp } const adSlot = getSlotByCode(adUnitCode); const identifier = adSlot ? getMacroId(_predictionsData.pmd, adSlot) : adUnitCode; const predictionData = _predictions[identifier]; + rp[adUnitCode] = getKVObject(-1, _predictionsData.kn); if (!predictionData) { return rp } if (predictionData.p) { @@ -160,7 +160,7 @@ function getAllSlots() { function getKVObject(p, keyName) { const prValue = p < 0 ? 'NA' : (Math.floor(p * 10) / 10).toFixed(2); let prObject = {}; - prObject[((_moduleParams['keyName'] || keyName).toString())] = prValue.toString(); + prObject[((_moduleParams['keyName'] || keyName || DEF_KEYNAME).toString())] = prValue.toString(); return prObject; } /** @@ -231,7 +231,7 @@ function evaluate(macro, divId, adUnit, replacer) { * @param {string} url server url with query params */ function getPredictionsFromServer(url) { - let ajax = ajaxBuilder(_moduleParams.auctionDelay || _moduleParams.timeout || DEF_TIMEOUT); + let ajax = ajaxBuilder(_moduleParams.auctionDelay || _moduleParams.timeout); ajax(url, { @@ -286,21 +286,26 @@ export const browsiSubmodule = { * @param {adUnit[]} adUnits * @param {function} onDone */ - getData: sendDataToModule + getData: sendDataToModule, + init: init }; -export function init(config) { +function init(config, gdpr, usp) { + return true; +} + +export function beforeInit(config) { const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => { try { _moduleParams = realTimeData.dataProviders && realTimeData.dataProviders.filter( pr => pr.name && pr.name.toLowerCase() === 'browsi')[0].params; + confListener(); _moduleParams.auctionDelay = realTimeData.auctionDelay; - _moduleParams.timeout = realTimeData.timeout; + _moduleParams.timeout = realTimeData.timeout || DEF_TIMEOUT; } catch (e) { _moduleParams = {}; } if (_moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url) { - confListener(); collectData(); } else { utils.logError('missing params for Browsi provider'); @@ -308,5 +313,8 @@ export function init(config) { }); } -submodule('realTimeData', browsiSubmodule); -init(config); +function registerSubModule() { + submodule('realTimeData', browsiSubmodule); +} +registerSubModule(); +beforeInit(config); diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 3aa7753d204..9acd484cec8 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -23,14 +23,56 @@ */ /** - * @interface ModuleConfig + * @property + * @summary used to link submodule with config + * @name RtdSubmodule#config + * @type {Object} */ /** - * @property - * @summary sub module name - * @name ModuleConfig#name - * @type {string} + * @function + * @summary init sub module + * @name RtdSubmodule#init + * @param {Object} config + * @param {Object} gdpr settings + * @param {Object} usp settings + * @return {boolean} false to remove sub module + */ + +/** + * @function? + * @summary on auction init event + * @name RtdSubmodule#auctionInit + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @function? + * @summary on auction end event + * @name RtdSubmodule#auctionEnd + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @function? + * @summary on bid request event + * @name RtdSubmodule#updateBidRequest + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @function? + * @summary on bid response event + * @name RtdSubmodule#updateBidResponse + * @param {Object} data + * @param {SubmoduleConfig} config + */ + +/** + * @interface ModuleConfig */ /** @@ -40,18 +82,43 @@ * @type {number} */ +/** + * @property + * @summary timeout (if no auction dealy) + * @name ModuleConfig#timeout + * @type {number} + */ + +/** + * @property + * @summary list of sub modules + * @name ModuleConfig#dataProviders + * @type {SubmoduleConfig[]} + */ + +/** + * @interface SubModuleConfig + */ + /** * @property * @summary params for provide (sub module) - * @name ModuleConfig#params + * @name SubModuleConfig#params * @type {Object} */ /** * @property - * @summary timeout (if no auction dealy) - * @name ModuleConfig#timeout - * @type {number} + * @summary name + * @name ModuleConfig#name + * @type {string} + */ + +/** + * @property + * @summary delay auction for this sub module + * @name ModuleConfig#waitForIt + * @type {boolean} */ import {getGlobal} from '../../src/prebidGlobal.js'; @@ -59,15 +126,21 @@ import {config} from '../../src/config.js'; import {targeting} from '../../src/targeting.js'; import {getHook, module} from '../../src/hook.js'; import * as utils from '../../src/utils.js'; +import events from '../../src/events.js'; +import CONSTANTS from '../../src/constants.json'; +import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; +import find from 'core-js-pure/features/array/find.js'; /** @type {string} */ const MODULE_NAME = 'realTimeData'; /** @type {number} */ const DEF_TIMEOUT = 1000; /** @type {RtdSubmodule[]} */ -let subModules = []; +export let subModules = []; /** @type {ModuleConfig} */ let _moduleConfig; +/** @type {SubmoduleConfig[]} */ +let _dataProviders = []; /** * enable submodule in User ID @@ -85,6 +158,9 @@ export function init(config) { } confListener(); // unsubscribe config listener _moduleConfig = realTimeData; + _dataProviders = realTimeData.dataProviders; + getHook('makeBidRequests').before(initSubModules); + setEventsListeners(); if (typeof (_moduleConfig.auctionDelay) === 'undefined') { _moduleConfig.auctionDelay = 0; } @@ -97,61 +173,82 @@ export function init(config) { }); } +/** + * call each sub module init function by config order + * if no init function / init return failure / module not configured - remove it from submodules list + */ +export function initSubModules(next, adUnits, auctionStart, auctionId, cbTimeout, labels) { + let subModulesByOrder = []; + _dataProviders.forEach(provider => { + const sm = find(subModules, s => s.name === provider.name); + const initResponse = sm && sm.init && sm.init(provider, gdprDataHandler.getConsentData(), uspDataHandler.getConsentData()); + if (initResponse) { + subModulesByOrder.push(Object.assign(sm, {config: provider})); + } + }); + subModules = subModulesByOrder; + next(adUnits, auctionStart, auctionId, cbTimeout, labels) +} + +/** + * call each sub module event function by config order + */ +function setEventsListeners() { + events.on(CONSTANTS.EVENTS.AUCTION_INIT, (args) => { + subModules.forEach(sm => { sm.auctionInit && sm.auctionInit(args, sm.config) }) + }); + events.on(CONSTANTS.EVENTS.AUCTION_END, (args) => { + subModules.forEach(sm => { sm.auctionEnd && sm.auctionEnd(args, sm.config) }) + }); + events.on(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, (args) => { + subModules.forEach(sm => { sm.updateBidRequest && sm.updateBidRequest(args, sm.config) }) + }); + events.on(CONSTANTS.EVENTS.BID_RESPONSE, (args) => { + subModules.forEach(sm => { sm.updateBidResponse && sm.updateBidResponse(args, sm.config) }) + }); +} + /** * get data from sub module * @param {AdUnit[]} adUnits received from auction * @param {function} callback callback function on data received */ -function getProviderData(adUnits, callback) { - const callbackExpected = subModules.length; - let dataReceived = []; - let processDone = false; - const dataWaitTimeout = setTimeout(() => { - processDone = true; - callback(dataReceived); - }, _moduleConfig.auctionDelay || _moduleConfig.timeout || DEF_TIMEOUT); +export function getProviderData(adUnits, callback) { + /** + * invoke callback if one of the conditions met: + * timeout reached + * all submodules answered + * all sub modules configured "waitForIt:true" answered (as long as there is at least one configured) + */ + const waitForSubModulesLength = subModules.filter(sm => sm.config && sm.config.waitForIt).length; + let callbacksExpected = waitForSubModulesLength || subModules.length; + const shouldWaitForAllSubModules = waitForSubModulesLength === 0; + let dataReceived = {}; + let processDone = false; + const dataWaitTimeout = setTimeout(done, _moduleConfig.auctionDelay || _moduleConfig.timeout || DEF_TIMEOUT); subModules.forEach(sm => { - sm.getData(adUnits, onDataReceived); + sm.getData(adUnits, onDataReceived.bind(sm)); }); function onDataReceived(data) { if (processDone) { return } - dataReceived.push(data); - if (dataReceived.length === callbackExpected) { - processDone = true; + dataReceived[this.name] = data; + if (shouldWaitForAllSubModules || (this.config && this.config.waitForIt)) { + callbacksExpected-- + } + if (callbacksExpected <= 0) { clearTimeout(dataWaitTimeout); - callback(dataReceived); + done(); } } -} -/** - * delete invalid data received from provider - * this is to ensure working flow for GPT - * @param {Object} data received from provider - * @return {Object} valid data for GPT targeting - */ -export function validateProviderDataForGPT(data) { - // data must be an object, contains object with string as value - if (typeof data !== 'object') { - return {}; - } - for (let key in data) { - if (data.hasOwnProperty(key)) { - for (let innerKey in data[key]) { - if (data[key].hasOwnProperty(innerKey)) { - if (typeof data[key][innerKey] !== 'string') { - utils.logWarn(`removing ${key}: {${innerKey}:${data[key][innerKey]} } from GPT targeting because of invalid type (must be string)`); - delete data[key][innerKey]; - } - } - } - } + function done() { + processDone = true; + callback(dataReceived); } - return data; } /** @@ -163,7 +260,7 @@ export function validateProviderDataForGPT(data) { export function setTargetsAfterRequestBids(next, adUnits) { getProviderData(adUnits, (data) => { if (data && Object.keys(data).length) { - const _mergedData = deepMerge(data); + const _mergedData = deepMerge(setDataOrderByProvider(subModules, data)); if (Object.keys(_mergedData).length) { setDataForPrimaryAdServer(_mergedData); } @@ -172,6 +269,22 @@ export function setTargetsAfterRequestBids(next, adUnits) { }); } +/** + * return an array providers data in reverse order,so the data merge will be according to correct config order + * @param {Submodule[]} modules + * @param {Object} data - data retrieved from providers + * @return {array} reversed order ready for merge + */ +function setDataOrderByProvider(modules, data) { + let rd = []; + for (let i = modules.length; i--; i > 0) { + if (data[modules[i].name]) { + rd.push(data[modules[i].name]) + } + } + return rd; +} + /** * deep merge array of objects * @param {array} arr - objects array @@ -207,7 +320,7 @@ export function deepMerge(arr) { export function requestBidsHook(fn, reqBidsConfigObj) { getProviderData(reqBidsConfigObj.adUnits || getGlobal().adUnits, (data) => { if (data && Object.keys(data).length) { - const _mergedData = deepMerge(data); + const _mergedData = deepMerge(setDataOrderByProvider(subModules, data)); if (Object.keys(_mergedData).length) { setDataForPrimaryAdServer(_mergedData); addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, _mergedData); @@ -222,7 +335,6 @@ export function requestBidsHook(fn, reqBidsConfigObj) { * @param {Object} data - key values to set */ function setDataForPrimaryAdServer(data) { - data = validateProviderDataForGPT(data); if (utils.isGptPubadsDefined()) { targeting.setTargetingForGPT(data, null) } else { @@ -247,5 +359,5 @@ function addIdDataToAdUnitBids(adUnits, data) { }); } -init(config); module('realTimeData', attachRealTimeDataProvider); +init(config); diff --git a/src/targeting.js b/src/targeting.js index 5873eeeb559..1b1e14fd4a6 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -325,7 +325,10 @@ export function newTargeting(auctionManager) { Object.keys(targetingConfig).filter(customSlotMatching ? customSlotMatching(slot) : isAdUnitCodeMatchingSlot(slot)) .forEach(targetId => Object.keys(targetingConfig[targetId]).forEach(key => { - let valueArr = targetingConfig[targetId][key].split(','); + let valueArr = targetingConfig[targetId][key]; + if (typeof valueArr === 'string') { + valueArr = valueArr.split(','); + } valueArr = (valueArr.length > 1) ? [valueArr] : valueArr; valueArr.map((value) => { utils.logMessage(`Attempting to set key value for slot: ${slot.getSlotElementId()} key: ${key} value: ${value}`); diff --git a/test/spec/modules/realTimeModule_spec.js b/test/spec/modules/realTimeModule_spec.js index ca149fe7a44..f47068724d1 100644 --- a/test/spec/modules/realTimeModule_spec.js +++ b/test/spec/modules/realTimeModule_spec.js @@ -1,27 +1,176 @@ -import { - init, - requestBidsHook, - setTargetsAfterRequestBids, - deepMerge, - validateProviderDataForGPT -} from 'modules/rtdModule/index.js'; -import { - init as browsiInit, - addBrowsiTag, - isIdMatchingAdUnit, - setData, - getMacroId -} from 'modules/browsiRtdProvider.js'; -import { - init as audigentInit, - setData as setAudigentData -} from 'modules/audigentRtdProvider.js'; +import * as rtdModule from 'modules/rtdModule/index.js'; import { config } from 'src/config.js'; -import { makeSlot } from '../integration/faker/googletag.js'; - -let expect = require('chai').expect; +import {makeSlot} from '../integration/faker/googletag.js'; +import * as browsiRTD from '../../../modules/browsiRtdProvider.js'; + +const validSM = { + name: 'validSM', + init: () => { return true }, + getData: (adUnits, onDone) => { + setTimeout(() => { + return onDone({'key': 'validSM'}) + }, 500) + } +}; + +const validSMWait = { + name: 'validSMWait', + init: () => { return true }, + getData: (adUnits, onDone) => { + setTimeout(() => { + return onDone({'ad1': {'key': 'validSMWait'}}) + }, 50) + } +}; + +const invalidSM = { + name: 'invalidSM' +}; + +const failureSM = { + name: 'failureSM', + init: () => { return false } +}; + +const nonConfSM = { + name: 'nonConfSM', + init: () => { return true } +}; + +const conf = { + 'realTimeData': { + 'auctionDelay': 250, + dataProviders: [ + { + 'name': 'validSMWait', + 'waitForIt': true, + }, + { + 'name': 'validSM', + 'waitForIt': false, + }, + { + 'name': 'invalidSM' + }, + { + 'name': 'failureSM' + }] + } +}; + +function getAdUnitMock(code = 'adUnit-code') { + return { + code, + mediaTypes: { banner: {}, native: {} }, + sizes: [[300, 200], [300, 600]], + bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] + }; +} describe('Real time module', function () { + after(function () { + config.resetConfig(); + }); + + beforeEach(function () { + config.setConfig(conf); + }); + + it('should use only valid modules', function (done) { + rtdModule.attachRealTimeDataProvider(validSM); + rtdModule.attachRealTimeDataProvider(invalidSM); + rtdModule.attachRealTimeDataProvider(failureSM); + rtdModule.attachRealTimeDataProvider(nonConfSM); + rtdModule.attachRealTimeDataProvider(validSMWait); + rtdModule.initSubModules(afterInitSubModules); + function afterInitSubModules() { + expect(rtdModule.subModules).to.eql([validSMWait, validSM]); + done(); + } + rtdModule.init(config); + }); + + it('should only wait for must have sub modules', function (done) { + rtdModule.getProviderData([], (data) => { + expect(data).to.eql({validSMWait: {'ad1': {'key': 'validSMWait'}}}); + done(); + }) + }); + + it('deep merge object', function () { + const obj1 = { + id1: { + key: 'value', + key2: 'value2' + }, + id2: { + k: 'v' + } + }; + const obj2 = { + id1: { + key3: 'value3' + } + }; + const obj3 = { + id3: { + key: 'value' + } + }; + const expected = { + id1: { + key: 'value', + key2: 'value2', + key3: 'value3' + }, + id2: { + k: 'v' + }, + id3: { + key: 'value' + } + }; + + const merged = rtdModule.deepMerge([obj1, obj2, obj3]); + assert.deepEqual(expected, merged); + }); + + it('check module using bidsBackCallback', function (done) { + // set slot + const slot = makeSlot({ code: '/code1', divId: 'ad1' }); + window.googletag.pubads().setSlots([slot]); + + function afterBidHook() { + expect(slot.getTargeting().length).to.equal(1); + expect(slot.getTargeting()[0].key).to.equal('validSMWait'); + done(); + } + rtdModule.setTargetsAfterRequestBids(afterBidHook, []); + }); + + it('check module using requestBidsHook', function (done) { + // set slot + const slotsB = makeSlot({ code: '/code1', divId: 'ad1' }); + window.googletag.pubads().setSlots([slotsB]); + let adUnits = [getAdUnitMock('ad1')]; + + function afterBidHook(data) { + expect(slotsB.getTargeting().length).to.equal(1); + expect(slotsB.getTargeting()[0].key).to.equal('validSMWait'); + + data.adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid.realTimeData).to.have.property('key'); + expect(bid.realTimeData.key).to.equal('validSMWait'); + }); + }); + done(); + } + rtdModule.requestBidsHook(afterBidHook, { adUnits: adUnits }); + }); +}); + +describe('browsi Real time data sub module', function () { const conf = { 'realTimeData': { 'auctionDelay': 250, @@ -33,250 +182,93 @@ describe('Real time module', function () { 'pubKey': 'testPub', 'keyName': 'bv' } - }, { - 'name': 'audigent' }] } }; - const predictions = { - p: { - 'browsiAd_2': { - 'w': [ - '/57778053/Browsi_Demo_Low', - '/57778053/Browsi_Demo_300x250' - ], - 'p': 0.07 - }, - 'browsiAd_1': { - 'w': [], - 'p': 0.06 - }, - 'browsiAd_3': { - 'w': [], - 'p': 0.53 - }, - 'browsiAd_4': { - 'w': [ - '/57778053/Browsi_Demo' - ], - 'p': 0.85 - } - } - }; - - const audigentSegments = { - audigent_segments: { 'a': 1, 'b': 2 } - } - - function getAdUnitMock(code = 'adUnit-code') { - return { - code, - mediaTypes: { banner: {}, native: {} }, - sizes: [[300, 200], [300, 600]], - bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] - }; - } - - function createSlots() { - const slot1 = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - const slot2 = makeSlot({ code: '/57778053/Browsi', divId: 'browsiAd_1' }); - return [slot1, slot2]; - } - - describe('Real time module with browsi provider', function () { - afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); - }); + beforeEach(function () { + config.setConfig(conf); + }); - after(function () { - config.resetConfig(); - }); + after(function () { + config.resetConfig(); + }); - it('check module using bidsBackCallback', function () { - let adUnits1 = [getAdUnitMock('browsiAd_1')]; - let targeting = []; - init(config); - browsiInit(config); - config.setConfig(conf); - setData(predictions); - - // set slot - const slots = createSlots(); - window.googletag.pubads().setSlots(slots); - - function afterBidHook() { - slots.map(s => { - targeting = []; - s.getTargeting().map(value => { - targeting.push(Object.keys(value).toString()); - }); - }); + it('should init and return true', function () { + browsiRTD.beforeInit(config); + expect(browsiRTD.browsiSubmodule.init()).to.equal(true) + }); - expect(targeting.indexOf('bv')).to.be.greaterThan(-1); - } - setTargetsAfterRequestBids(afterBidHook, adUnits1, true); - }); + it('should create browsi script', function () { + const script = browsiRTD.addBrowsiTag('scriptUrl.com'); + expect(script.getAttribute('data-sitekey')).to.equal('testKey'); + expect(script.getAttribute('data-pubkey')).to.equal('testPub'); + expect(script.async).to.equal(true); + expect(script.prebidData.kn).to.equal(conf.realTimeData.dataProviders[0].params.keyName); + }); - it('check module using requestBidsHook', function () { - let adUnits1 = [getAdUnitMock('browsiAd_1')]; - let targeting = []; - let dataReceived = null; - - // set slot - const slotsB = createSlots(); - window.googletag.pubads().setSlots(slotsB); - - function afterBidHook(data) { - dataReceived = data; - slotsB.map(s => { - targeting = []; - s.getTargeting().map(value => { - targeting.push(Object.keys(value).toString()); - }); - }); + it('should match placement with ad unit', function () { + const slot = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - expect(targeting.indexOf('bv')).to.be.greaterThan(-1); - dataReceived.adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid.realTimeData).to.have.property('bv'); - }); - }); - } - requestBidsHook(afterBidHook, { adUnits: adUnits1 }); - }); + const test1 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250']); // true + const test2 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true + const test3 = browsiRTD.isIdMatchingAdUnit(slot, ['/57778053/Browsi_Demo_Low']); // false + const test4 = browsiRTD.isIdMatchingAdUnit(slot, []); // true - it('check object deep merge', function () { - const obj1 = { - id1: { - key: 'value', - key2: 'value2' - }, - id2: { - k: 'v' - } - }; - const obj2 = { - id1: { - key3: 'value3' - } - }; - const obj3 = { - id3: { - key: 'value' - } - }; - const expected = { - id1: { - key: 'value', - key2: 'value2', - key3: 'value3' - }, - id2: { - k: 'v' - }, - id3: { - key: 'value' - } - }; + expect(test1).to.equal(true); + expect(test2).to.equal(true); + expect(test3).to.equal(false); + expect(test4).to.equal(true); + }); - const merged = deepMerge([obj1, obj2, obj3]); - assert.deepEqual(expected, merged); - }); + it('should return correct macro values', function () { + const slot = makeSlot({ code: '/57778053/Browsi_Demo_300x250', divId: 'browsiAd_1' }); - it('check data validation for GPT targeting', function () { - // non strings values should be removed - const obj = { - valid: {'key': 'value'}, - invalid: {'key': ['value']}, - combine: { - 'a': 'value', - 'b': [] - } - }; + slot.setTargeting('test', ['test', 'value']); + // slot getTargeting doesn't act like GPT so we can't expect real value + const macroResult = browsiRTD.getMacroId({p: '/'}, slot); + expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); - const expected = { - valid: {'key': 'value'}, - invalid: {}, - combine: { - 'a': 'value', - } - }; - const validationResult = validateProviderDataForGPT(obj); - assert.deepEqual(expected, validationResult); - }); + const macroResultB = browsiRTD.getMacroId({}, slot); + expect(macroResultB).to.equal('browsiAd_1'); - it('check browsi sub module', function () { - const script = addBrowsiTag('scriptUrl.com'); - expect(script.getAttribute('data-sitekey')).to.equal('testKey'); - expect(script.getAttribute('data-pubkey')).to.equal('testPub'); - expect(script.async).to.equal(true); - - const slots = createSlots(); - const test1 = isIdMatchingAdUnit(slots[0], ['/57778053/Browsi_Demo_300x250']); // true - const test2 = isIdMatchingAdUnit(slots[0], ['/57778053/Browsi_Demo_300x250', '/57778053/Browsi']); // true - const test3 = isIdMatchingAdUnit(slots[0], ['/57778053/Browsi_Demo_Low']); // false - const test4 = isIdMatchingAdUnit(slots[0], []); // true - - expect(test1).to.equal(true); - expect(test2).to.equal(true); - expect(test3).to.equal(false); - expect(test4).to.equal(true); - - // macro results - slots[0].setTargeting('test', ['test', 'value']); - // slot getTargeting doesn't act like GPT so we can't expect real value - const macroResult = getMacroId({p: '/'}, slots[0]); - expect(macroResult).to.equal('/57778053/Browsi_Demo_300x250/NA'); - - const macroResultB = getMacroId({}, slots[0]); - expect(macroResultB).to.equal('browsiAd_1'); - - const macroResultC = getMacroId({p: '', s: {s: 0, e: 1}}, slots[0]); - expect(macroResultC).to.equal('/'); - }) + const macroResultC = browsiRTD.getMacroId({p: '', s: {s: 0, e: 1}}, slot); + expect(macroResultC).to.equal('/'); }); - describe('Real time module with Audigent provider', function () { - before(function () { - init(config); - audigentInit(config); - config.setConfig(conf); - setAudigentData(audigentSegments); + describe('should return data to RTD module', function () { + it('should return empty if no ad units defined', function (done) { + browsiRTD.setData({}); + browsiRTD.browsiSubmodule.getData([], onDone); + function onDone(data) { + expect(data).to.eql({}); + done(); + } }); - afterEach(function () { - $$PREBID_GLOBAL$$.requestBids.removeAll(); - config.resetConfig(); + it('should return NA if no prediction for ad unit', function (done) { + const adUnits = [getAdUnitMock('adMock')]; + browsiRTD.setData({}); + browsiRTD.browsiSubmodule.getData(adUnits, onDone); + function onDone(data) { + expect(data).to.eql({adMock: {bv: 'NA'}}); + done(); + } }); - it('check module using requestBidsHook', function () { - let adUnits1 = [getAdUnitMock('audigentAd_1')]; - let targeting = []; - let dataReceived = null; - - // set slot - const slotsB = createSlots(); - window.googletag.pubads().setSlots(slotsB); - - function afterBidHook(data) { - dataReceived = data; - slotsB.map(s => { - targeting = []; - s.getTargeting().map(value => { - targeting.push(Object.keys(value).toString()); - }); - }); - - dataReceived.adUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid.realTimeData).to.have.property('audigent_segments'); - expect(bid.realTimeData.audigent_segments).to.deep.equal(audigentSegments.audigent_segments); - }); - }); + it('should return prediction from server', function (done) { + const adUnits = [getAdUnitMock('hasPrediction')]; + const data = { + p: {'hasPrediction': {p: 0.234}}, + kn: 'bv', + pmd: undefined + }; + browsiRTD.setData(data); + browsiRTD.browsiSubmodule.getData(adUnits, onDone); + function onDone(data) { + expect(data).to.eql({hasPrediction: {bv: '0.20'}}); + done(); } - - requestBidsHook(afterBidHook, { adUnits: adUnits1 }); - }); - }); + }) + }) });