diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index 01a956d1bd8..d9e14554483 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -11,9 +11,12 @@ const NET_REVENUE = true; const TTL = 120; const NULL_SIZE = '1x1'; const FILE = 'file'; +const STORAGE_RENDER_PREFIX = 'pbsr_'; +const STORAGE_VIEW_PREFIX = 'pbvi_'; export const spec = { code: BIDDER_CODE, + isBidRequestValid: function(bid) { return Boolean(bid.params.ci) || Boolean(bid.params.t); }, @@ -26,21 +29,26 @@ export const spec = { let params; const urlConfig = getUrlConfig(bidRequests); const pcrs = getCharset(); - + const spaces = getSpaces(bidRequests); if (urlConfig.t) { url = urlConfig.isv + '/layers/t_pbjs_2.json'; params = {}; } else { url = '//' + (urlConfig.sv || DEFAULT_SV) + '/hb/1/' + urlConfig.ci + '/' + dfpClientId + '/' + (utils.getTopWindowLocation().hostname || FILE) + '/' + sec; const referrerUrl = utils.getTopWindowReferrer(); - const spacesString = getSpacesString(bidRequests); + + if (utils.hasLocalStorage()) { + registerViewabilityAllBids(bidRequests); + } + params = { rnd: rnd, - e: spacesString, + e: spaces.str, ur: utils.getTopWindowUrl() || FILE, r: 'pbjs', pbv: '$prebid.version$', - ncb: '1' + ncb: '1', + vs: spaces.vs }; if (pcrs) { @@ -56,7 +64,7 @@ export const spec = { method: method, url: url, data: params, - adUnitToBidId: getBidIdMap(bidRequests), + adUnitToBidId: spaces.map, }; }, interpretResponse: function(serverResponse, request) { @@ -111,9 +119,6 @@ export const spec = { }, } -function cleanName(name) { - return name.replace(/_|\.|-|\//g, '').replace(/\)\(|\(|\)|:/g, '_').replace(/^_+|_+$/g, ''); -} function getUrlConfig(bidRequests) { if (isTestRequest(bidRequests)) { return getTestConfig(bidRequests.filter(br => br.params.t)); @@ -135,9 +140,12 @@ function getUrlConfig(bidRequests) { return config; } function isTestRequest(bidRequests) { - let isTest = false; - bidRequests.forEach(bid => isTest = bid.params.t); - return isTest; + for (let i = 0; i < bidRequests.length; i++) { + if (bidRequests[i].params.t) { + return true; + } + } + return false; } function getTestConfig(bidRequests) { let isv; @@ -147,14 +155,55 @@ function getTestConfig(bidRequests) { isv: '//' + (isv || DEFAULT_ISV) }; } -function getSpacesString(bids) { - const spacesString = bids.map(bid => - cleanName(bid.adUnitCode) + ':' + (bid.sizes && bid.sizes.length ? utils.parseSizesInput(bid.sizes).join(',') : NULL_SIZE) - ).join('+'); - return spacesString; +function getSize(bid, first) { + return bid.sizes && bid.sizes.length ? utils.parseSizesInput(first ? bid.sizes[0] : bid.sizes).join(',') : NULL_SIZE; +} + +function getSpacesStruct(bids) { + let e = {}; + bids.forEach(bid => { + let size = getSize(bid, true); + e[size] = e[size] ? e[size] : []; + e[size].push(bid); + }); + + return e; +} + +function getSpaces(bidRequests) { + let spacesStruct = getSpacesStruct(bidRequests); + let es = {str: '', vs: '', map: {}}; + es.str = Object.keys(spacesStruct).map(size => spacesStruct[size].map((bid, i) => { + es.vs += getVs(bid); + let name = getSize(bid, true) + '_' + i; + es.map[name] = bid.bidId; + return name + ':' + getSize(bid); + }).join('+')).join('+'); + return es; +} + +function getVs(bid) { + let s; + let vs = ''; + if (utils.hasLocalStorage()) { + s = getViewabilityData(bid); + vs += s.render >= 4 ? s.ratio.toString(16) : 'F'; + } else { + vs += 'F'; + } + return vs; } +function getViewabilityData(bid) { + let r = utils.getDataFromLocalStorage(STORAGE_RENDER_PREFIX + bid.adUnitCode) || 0; + let v = utils.getDataFromLocalStorage(STORAGE_VIEW_PREFIX + bid.adUnitCode) || 0; + let ratio = r > 0 ? (v / r) : 0; + return { + render: r, + ratio: window.parseInt(ratio * 10, 10) + }; +} function getCharset() { try { return window.top.document.charset || window.top.document.characterSet; @@ -163,10 +212,184 @@ function getCharset() { } } -function getBidIdMap(bidRequests) { - let map = {}; - bidRequests.forEach(bid => map[cleanName(bid.adUnitCode)] = bid.bidId); - return map; +function waitForElementsPresent(elements) { + const observer = new MutationObserver(function (mutationList, observer) { + if (mutationList && Array.isArray(mutationList)) { + mutationList.forEach(mr => { + if (mr && mr.addedNodes && Array.isArray(mr.addedNodes)) { + mr.addedNodes.forEach(ad => { + let index = elements.indexOf(ad.id); + if (index >= 0) { + registerViewability(ad); + elements.splice(index, 1); + if (!elements.length) { + observer.disconnect(); + } + } + }); + } + }); + } + }); + const config = {childList: true, subtree: true, characterData: true, attributes: true, attributeOldValue: true}; + observer.observe(document.body, config); +} + +function registerViewability(div) { + visibilityHandler({ + name: div.id, + div: div + }); +} + +function registerViewabilityAllBids(bids) { + let elementsNotPresent = []; + bids.forEach(bid => { + let div = document.getElementById(bid.adUnitCode); + if (div) { + registerViewability(div); + } else { + elementsNotPresent.push(bid.adUnitCode); + } + }); + if (elementsNotPresent.length) { + waitForElementsPresent(elementsNotPresent); + } } +function getViewabilityTracker() { + let TIME_PARTITIONS = 5; + let VIEWABILITY_TIME = 1000; + let VIEWABILITY_MIN_RATIO = 0.5; + let publicApi; + let context; + + function segmentIsOutsideTheVisibleRange(visibleRangeEnd, p1, p2) { + return p1 > visibleRangeEnd || p2 < 0; + } + + function segmentBeginsBeforeTheVisibleRange(p1) { + return p1 < 0; + } + + function segmentEndsAfterTheVisibleRange(visibleRangeEnd, p2) { + return p2 < visibleRangeEnd; + } + + function axialVisibilityRatio(visibleRangeEnd, p1, p2) { + let visibilityRatio = 0; + if (!segmentIsOutsideTheVisibleRange(visibleRangeEnd, p1, p2)) { + if (segmentBeginsBeforeTheVisibleRange(p1)) { + visibilityRatio = p2 / (p2 - p1); + } else { + visibilityRatio = segmentEndsAfterTheVisibleRange(visibleRangeEnd, p2) ? 1 : (visibleRangeEnd - p1) / (p2 - p1); + } + } + return visibilityRatio; + } + + function isNotHiddenByNonFriendlyIframe() { + return (window === window.top) || window.frameElement; + } + + function defineContext(e) { + context = e && window.document.body.contains(e) ? window : (window.top.document.body.contains(e) ? top : undefined); + return context; + } + + function getContext(e) { + return context; + } + + function verticalVisibilityRatio(position) { + return axialVisibilityRatio(getContext().innerHeight, position.top, position.bottom); + } + + function horizontalVisibilityRatio(position) { + return axialVisibilityRatio(getContext().innerWidth, position.left, position.right); + } + + function itIsNotHiddenByBannerAreaPosition(e) { + let position = e.getBoundingClientRect(); + return (verticalVisibilityRatio(position) * horizontalVisibilityRatio(position)) > VIEWABILITY_MIN_RATIO; + } + + function itIsNotHiddenByDisplayStyleCascade(e) { + return e.offsetHeight > 0 && e.offsetWidth > 0; + } + + function itIsNotHiddenByOpacityStyleCascade(e) { + let s = e.style; + let p = e.parentNode; + return !(s && parseFloat(s.opacity) === 0) && (!p || itIsNotHiddenByOpacityStyleCascade(p)); + } + + function itIsNotHiddenByVisibilityStyleCascade(e) { + return getContext().getComputedStyle(e).visibility !== 'hidden'; + } + + function itIsNotHiddenByTabFocus() { + return getContext().top.document.hasFocus(); + } + + function isDefined(e) { + return (e !== null) && (typeof e !== 'undefined'); + } + + function itIsNotHiddenByOrphanBranch() { + return isDefined(getContext()); + } + + function isContextInAnIframe() { + return isDefined(getContext().frameElement); + } + + function processIntervalVisibilityStatus(elapsedVisibleIntervals, element, callback) { + let visibleIntervals = isVisible(element) ? (elapsedVisibleIntervals + 1) : 0; + if (visibleIntervals === TIME_PARTITIONS) { + callback(); + } else { + setTimeout(processIntervalVisibilityStatus.bind(this, visibleIntervals, element, callback), VIEWABILITY_TIME / TIME_PARTITIONS); + } + } + + function isVisible(element) { + defineContext(element); + return isNotHiddenByNonFriendlyIframe() && + itIsNotHiddenByOrphanBranch() && + itIsNotHiddenByTabFocus() && + itIsNotHiddenByDisplayStyleCascade(element) && + itIsNotHiddenByVisibilityStyleCascade(element) && + itIsNotHiddenByOpacityStyleCascade(element) && + itIsNotHiddenByBannerAreaPosition(element) && + (!isContextInAnIframe() || isVisible(getContext().frameElement)); + } + + publicApi = { + isVisible: isVisible, + onView: processIntervalVisibilityStatus.bind(this, 0) + }; + + return publicApi; +}; + +function visibilityHandler(obj) { + if (obj.div) { + registerAuction(STORAGE_RENDER_PREFIX + obj.name); + getViewabilityTracker().onView(obj.div, registerAuction.bind(undefined, STORAGE_VIEW_PREFIX + obj.name)); + } +} + +function registerAuction(storageID) { + let value; + try { + value = utils.getDataFromLocalStorage(storageID); + value = value ? window.parseInt(value, 10) + 1 : 1; + utils.setDataInLocalStorage(storageID, value); + } catch (exc) { + return false; + } + + return true; +} registerBidder(spec); diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 93290229ce3..3152c43c6ff 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -8,10 +8,14 @@ describe('E-Planning Adapter', function () { const CI = '12345'; const ADUNIT_CODE = 'adunit-co:de'; const ADUNIT_CODE2 = 'adunit-code-dos'; - const CLEAN_ADUNIT_CODE2 = 'adunitcodedos'; - const CLEAN_ADUNIT_CODE = 'adunitco_de'; + const ADUNIT_CODE_VIEW = 'adunit-code-view'; + const ADUNIT_CODE_VIEW2 = 'adunit-code-view2'; + const ADUNIT_CODE_VIEW3 = 'adunit-code-view3'; + const CLEAN_ADUNIT_CODE2 = '300x250_1'; + const CLEAN_ADUNIT_CODE = '300x250_0'; const BID_ID = '123456789'; const BID_ID2 = '987654321'; + const BID_ID3 = '998877665'; const CPM = 1.3; const W = '300'; const H = '250'; @@ -37,6 +41,33 @@ describe('E-Planning Adapter', function () { 'adUnitCode': ADUNIT_CODE2, 'sizes': [[300, 250], [300, 600]], }; + const validBidView = { + 'bidder': 'eplanning', + 'bidId': BID_ID, + 'params': { + 'ci': CI, + }, + 'adUnitCode': ADUNIT_CODE_VIEW, + 'sizes': [[300, 250], [300, 600]], + }; + const validBidView2 = { + 'bidder': 'eplanning', + 'bidId': BID_ID2, + 'params': { + 'ci': CI, + }, + 'adUnitCode': ADUNIT_CODE_VIEW2, + 'sizes': [[300, 250], [300, 600]], + }; + const validBidView3 = { + 'bidder': 'eplanning', + 'bidId': BID_ID3, + 'params': { + 'ci': CI, + }, + 'adUnitCode': ADUNIT_CODE_VIEW3, + 'sizes': [[300, 250], [300, 600]], + }; const testBid = { 'bidder': 'eplanning', 'params': { @@ -212,7 +243,7 @@ describe('E-Planning Adapter', function () { it('should return e parameter with value according to the adunit sizes', function () { const e = spec.buildRequests(bidRequests).data.e; - expect(e).to.equal(CLEAN_ADUNIT_CODE + ':300x250,300x600'); + expect(e).to.equal('300x250_0:300x250,300x600'); }); it('should return correct e parameter with more than one adunit', function () { @@ -229,7 +260,7 @@ describe('E-Planning Adapter', function () { bidRequests.push(anotherBid); const e = spec.buildRequests(bidRequests).data.e; - expect(e).to.equal(CLEAN_ADUNIT_CODE + ':300x250,300x600+' + CLEAN_NEW_CODE + ':100x100'); + expect(e).to.equal('300x250_0:300x250,300x600+100x100_0:100x100'); }); it('should return correct e parameter when the adunit has no size', function () { @@ -242,7 +273,7 @@ describe('E-Planning Adapter', function () { }; const e = spec.buildRequests([noSizeBid]).data.e; - expect(e).to.equal(CLEAN_ADUNIT_CODE + ':1x1'); + expect(e).to.equal('1x1_0:1x1'); }); it('should return ur parameter with current window url', function () { @@ -360,4 +391,334 @@ describe('E-Planning Adapter', function () { expect(responses[1].requestId).to.equal(BID_ID2); }); }); + describe('viewability', function() { + let storageIdRender = 'pbsr_' + validBidView.adUnitCode; + let storageIdView = 'pbvi_' + validBidView.adUnitCode; + let bidRequests = [validBidView]; + let bidRequestMultiple = [validBidView, validBidView2, validBidView3]; + let getLocalStorageSpy; + let setDataInLocalStorageSpy; + let hasLocalStorageStub; + let clock; + let element; + let getBoundingClientRectStub; + let sandbox = sinon.sandbox.create(); + let focusStub; + function createElement(id) { + element = document.createElement('div'); + element.id = id || ADUNIT_CODE_VIEW; + element.style.width = '50px'; + element.style.height = '50px'; + if (frameElement) { + frameElement.style.width = '100px'; + frameElement.style.height = '100px'; + } + element.style.background = 'black'; + document.body.appendChild(element); + } + function createElementVisible(id) { + createElement(id); + sandbox.stub(element, 'getBoundingClientRect').returns({ + x: 0, + y: 0, + width: 50, + height: 50, + top: 0, + right: 50, + bottom: 50, + left: 0, + }); + } + function createElementOutOfView(id) { + createElement(id); + sandbox.stub(element, 'getBoundingClientRect').returns({ + x: 100, + y: 100, + width: 250, + height: 250, + top: 100, + right: 350, + bottom: 350, + left: 100, + }); + } + + function createPartiallyVisibleElement(id) { + createElement(id); + sandbox.stub(element, 'getBoundingClientRect').returns({ + x: 0, + y: 0, + width: 50, + height: 150, + top: 0, + right: 50, + bottom: 150, + left: 0, + }); + } + function createPartiallyInvisibleElement(id) { + createElement(id); + sandbox.stub(element, 'getBoundingClientRect').returns({ + x: 0, + y: 0, + width: 150, + height: 150, + top: 0, + right: 150, + bottom: 150, + left: 0, + }); + } + function createElementOutOfRange(id) { + createElement(id); + sandbox.stub(element, 'getBoundingClientRect').returns({ + x: 200, + y: 200, + width: 350, + height: 350, + top: 200, + right: 350, + bottom: 350, + left: 200, + }); + } + beforeEach(function () { + getLocalStorageSpy = sandbox.spy(utils, 'getDataFromLocalStorage'); + setDataInLocalStorageSpy = sandbox.spy(utils, 'setDataInLocalStorage'); + + hasLocalStorageStub = sandbox.stub(utils, 'hasLocalStorage'); + hasLocalStorageStub.returns(true); + + clock = sandbox.useFakeTimers(); + + focusStub = sandbox.stub(window.top.document, 'hasFocus'); + focusStub.returns(true); + }); + afterEach(function () { + sandbox.restore(); + if (document.getElementById(ADUNIT_CODE_VIEW)) { + document.body.removeChild(element); + } + window.top.localStorage.removeItem(storageIdRender); + window.top.localStorage.removeItem(storageIdView); + }); + + it('should create the url correctly without LocalStorage', function() { + createElementVisible(); + hasLocalStorageStub.returns(false); + const response = spec.buildRequests(bidRequests); + + expect(response.url).to.equal('//ads.us.e-planning.net/hb/1/' + CI + '/1/localhost/ROS'); + expect(response.data.vs).to.equal('F'); + + sinon.assert.notCalled(getLocalStorageSpy); + sinon.assert.notCalled(setDataInLocalStorageSpy); + }); + + it('should create the url correctly with LocalStorage', function() { + createElementVisible(); + const response = spec.buildRequests(bidRequests); + expect(response.url).to.equal('//ads.us.e-planning.net/hb/1/' + CI + '/1/localhost/ROS'); + + expect(response.data.vs).to.equal('F'); + + sinon.assert.called(getLocalStorageSpy); + sinon.assert.called(setDataInLocalStorageSpy); + sinon.assert.calledWith(getLocalStorageSpy, storageIdRender); + sinon.assert.calledWith(setDataInLocalStorageSpy, storageIdRender); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + }); + + context('when element is fully in view', function() { + let respuesta; + beforeEach(function () { + createElementVisible(); + }); + it('when you have a render', function() { + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + + expect(respuesta.data.vs).to.equal('F'); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('1'); + }); + it('when you have more than four render', function() { + utils.setDataInLocalStorage(storageIdRender, 4); + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + + expect(respuesta.data.vs).to.equal('0'); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('5'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('1'); + }); + it('when you have more than four render and already record visibility', function() { + utils.setDataInLocalStorage(storageIdRender, 4); + utils.setDataInLocalStorage(storageIdView, 4); + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + + expect(respuesta.data.vs).to.equal('a'); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('5'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('5'); + }); + }); + + context('when element is out of view', function() { + let respuesta; + beforeEach(function () { + createElementOutOfView(); + }); + + it('when you have a render', function() { + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + expect(respuesta.data.vs).to.equal('F'); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + it('when you have more than four render', function() { + utils.setDataInLocalStorage(storageIdRender, 4); + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + expect(respuesta.data.vs).to.equal('0'); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('5'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + }); + + context('when element is partially in view', function() { + let respuesta; + it('should register visibility with more than 50%', function() { + createPartiallyVisibleElement(); + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('1'); + }); + it('you should not register visibility with less than 50%', function() { + createPartiallyInvisibleElement(); + respuesta = spec.buildRequests(bidRequests); + clock.tick(1005); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + }); + context('when width or height of the element is zero', function() { + beforeEach(function () { + createElementVisible(); + }); + it('if the width is zero but the height is within the range', function() { + element.style.width = '0px'; + spec.buildRequests(bidRequests) + clock.tick(1005); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + it('if the height is zero but the width is within the range', function() { + element.style.height = '0px'; + spec.buildRequests(bidRequests) + clock.tick(1005); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + it('if both are zero', function() { + element.style.height = '0px'; + element.style.width = '0px'; + spec.buildRequests(bidRequests) + clock.tick(1005); + + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + }); + context('when tab is inactive', function() { + it('I should not register if it is not in focus', function() { + createElementVisible(); + focusStub.returns(false); + spec.buildRequests(bidRequests); + clock.tick(1005); + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + }); + context('segmentBeginsBeforeTheVisibleRange', function() { + it('segmentBeginsBeforeTheVisibleRange', function() { + createElementOutOfRange(); + spec.buildRequests(bidRequests); + clock.tick(1005); + expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + }); + }); + context('when there are multiple adunit', function() { + let respuesta; + beforeEach(function () { + [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { + utils.setDataInLocalStorage('pbsr_' + ac, 5); + utils.setDataInLocalStorage('pbvi_' + ac, 5); + }); + }); + afterEach(function () { + [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { + if (document.getElementById(ac)) { + document.body.removeChild(document.getElementById(ac)); + } + window.top.localStorage.removeItem(ac); + window.top.localStorage.removeItem(ac); + }); + }); + it('all visibles', function() { + createElementVisible(ADUNIT_CODE_VIEW); + createElementVisible(ADUNIT_CODE_VIEW2); + createElementVisible(ADUNIT_CODE_VIEW3); + + respuesta = spec.buildRequests(bidRequestMultiple); + clock.tick(1005); + [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { + expect(utils.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); + expect(utils.getDataFromLocalStorage('pbvi_' + ac)).to.equal('6'); + }); + expect('aaa').to.equal(respuesta.data.vs); + }); + it('none visible', function() { + createElementOutOfView(ADUNIT_CODE_VIEW); + createElementOutOfView(ADUNIT_CODE_VIEW2); + createElementOutOfView(ADUNIT_CODE_VIEW3); + + respuesta = spec.buildRequests(bidRequestMultiple); + clock.tick(1005); + [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { + expect(utils.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); + expect(utils.getDataFromLocalStorage('pbvi_' + ac)).to.equal('5'); + }); + + expect('aaa').to.equal(respuesta.data.vs); + }); + it('some visible and others not visible', function() { + createElementVisible(ADUNIT_CODE_VIEW); + createElementOutOfView(ADUNIT_CODE_VIEW2); + createElementOutOfView(ADUNIT_CODE_VIEW3); + + respuesta = spec.buildRequests(bidRequestMultiple); + clock.tick(1005); + expect(utils.getDataFromLocalStorage('pbsr_' + ADUNIT_CODE_VIEW)).to.equal('6'); + expect(utils.getDataFromLocalStorage('pbvi_' + ADUNIT_CODE_VIEW)).to.equal('6'); + [ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { + expect(utils.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); + expect(utils.getDataFromLocalStorage('pbvi_' + ac)).to.equal('5'); + }); + expect('aaa').to.equal(respuesta.data.vs); + }); + }); + }); });