UNPKG

mk9-prebid

Version:

Header Bidding Management Library

590 lines (546 loc) 23.1 kB
import { expect } from 'chai'; import { config } from 'src/config.js'; // Use require since we need to be able to write to these vars const utils = require('../../src/utils'); let { newUserSync, USERSYNC_DEFAULT_CONFIG } = require('../../src/userSync'); describe('user sync', function () { let triggerPixelStub; let logWarnStub; let timeoutStub; let shuffleStub; let getUniqueIdentifierStrStub; let insertUserSyncIframeStub; let idPrefix = 'test-generated-id-'; let lastId = 0; let defaultUserSyncConfig = config.getConfig('userSync'); function getUserSyncConfig(userSyncConfig) { return Object.assign({}, defaultUserSyncConfig, userSyncConfig); } function newTestUserSync(configOverrides, disableBrowserCookies) { const thisConfig = Object.assign({}, defaultUserSyncConfig, configOverrides); return newUserSync({ config: thisConfig, browserSupportsCookies: !disableBrowserCookies, }) } let clock; before(function () { clock = sinon.useFakeTimers(); }); after(function () { clock.restore(); }); beforeEach(function () { config.setConfig({ userSync: USERSYNC_DEFAULT_CONFIG }); triggerPixelStub = sinon.stub(utils, 'triggerPixel'); logWarnStub = sinon.stub(utils, 'logWarn'); shuffleStub = sinon.stub(utils, 'shuffle').callsFake((array) => array.reverse()); getUniqueIdentifierStrStub = sinon.stub(utils, 'getUniqueIdentifierStr').callsFake(() => idPrefix + (lastId += 1)); insertUserSyncIframeStub = sinon.stub(utils, 'insertUserSyncIframe'); }); afterEach(function () { triggerPixelStub.restore(); logWarnStub.restore(); shuffleStub.restore(); getUniqueIdentifierStrStub.restore(); insertUserSyncIframeStub.restore(); config.resetConfig(); }); it('should register and fire a pixel URL', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); }); it('should clear queue after sync', function () { const userSync = newTestUserSync(); userSync.syncUsers(); expect(triggerPixelStub.callCount).to.equal(0); }); it('should delay firing a pixel by the expected amount', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'testBidder', 'http://example.com'); // This implicitly tests cookie and browser support userSync.syncUsers(999); clock.tick(1000); expect(triggerPixelStub.getCall(0)).to.not.be.null; }); it('should register and fires multiple pixel URLs', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.include('http://example.com/'); expect(triggerPixelStub.getCall(1)).to.not.be.null; expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.include('http://example.com/'); expect(triggerPixelStub.getCall(2)).to.be.null; }); it('should not register pixel URL since it is not supported', function () { const userSync = newTestUserSync({filterSettings: { image: { bidders: '*', filter: 'exclude' } }}); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.be.null; }); it('should register and load an iframe', function () { const userSync = newTestUserSync({filterSettings: { iframe: { bidders: '*', filter: 'include' } }}); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.syncUsers(); expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); }); it('should only trigger syncs once per page per bidder', function () { const userSync = newTestUserSync({ pixelEnabled: true }); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.syncUsers(); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); userSync.registerSync('image', 'testBidder2', 'http://example.com/3'); userSync.syncUsers(); expect(triggerPixelStub.callCount).to.equal(2); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(triggerPixelStub.getCall(1)).to.not.be.null; expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.equal('http://example.com/3'); }); it('should not fire syncs if cookies are not supported', function () { const userSync = newTestUserSync({ pixelEnabled: true }, true); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.be.null; }); it('should prevent registering invalid type', function () { const userSync = newTestUserSync(); userSync.registerSync('invalid', 'testBidder', 'http://example.com'); expect(logWarnStub.getCall(0).args[0]).to.exist; }); it('should expose the syncUsers method for the publisher to manually trigger syncs', function () { // triggerUserSyncs should do nothing by default let userSync = newTestUserSync(); let syncUsersSpy = sinon.spy(userSync, 'syncUsers'); userSync.triggerUserSyncs(); expect(syncUsersSpy.notCalled).to.be.true; // triggerUserSyncs should trigger syncUsers if enableOverride is on userSync = newTestUserSync({ enableOverride: true }); syncUsersSpy = sinon.spy(userSync, 'syncUsers'); userSync.triggerUserSyncs(); expect(syncUsersSpy.called).to.be.true; }); it('should limit the number of syncs per bidder', function () { const userSync = newTestUserSync({ syncsPerBidder: 2 }); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); userSync.registerSync('image', 'testBidder', 'http://example.com/3'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2]/); expect(triggerPixelStub.getCall(1)).to.not.be.null; expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2]/); expect(triggerPixelStub.getCall(2)).to.be.null; }); it('should not limit the number of syncs per bidder when set to 0', function () { const userSync = newTestUserSync({ syncsPerBidder: 0 }); userSync.registerSync('image', 'testBidder', 'http://example.com/1'); userSync.registerSync('image', 'testBidder', 'http://example.com/2'); userSync.registerSync('image', 'testBidder', 'http://example.com/3'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2|3]/); expect(triggerPixelStub.getCall(1)).to.not.be.null; expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2|3]/); expect(triggerPixelStub.getCall(2)).to.not.be.null; expect(triggerPixelStub.getCall(2).args[0]).to.exist.and.to.match(/^http:\/\/example\.com\/[1|2|3]/); }); it('should balance out bidder requests', function () { const userSync = newTestUserSync(); userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync.registerSync('image', 'atestBidder', 'http://example.com/3'); userSync.registerSync('image', 'btestBidder', 'http://example.com/2'); userSync.syncUsers(); // The stubbed shuffle function should just reverse the order expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/2'); expect(triggerPixelStub.getCall(1)).to.not.be.null; expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.equal('http://example.com/3'); expect(triggerPixelStub.getCall(2)).to.not.be.null; expect(triggerPixelStub.getCall(2).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(triggerPixelStub.getCall(3)).to.be.null; }); it('should disable user sync', function () { const userSync = newTestUserSync({ syncEnabled: false }); userSync.registerSync('pixel', 'testBidder', 'http://example.com'); expect(logWarnStub.getCall(0).args[0]).to.exist; userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.be.null; }); it('should only sync enabled bidders', function () { const userSync = newTestUserSync({filterSettings: { image: { bidders: ['testBidderA'], filter: 'include' } }}); userSync.registerSync('image', 'testBidderA', 'http://example.com/1'); userSync.registerSync('image', 'testBidderB', 'http://example.com/2'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.include('http://example.com/'); expect(triggerPixelStub.getCall(1)).to.be.null; }); it('should register config set after instantiation', function () { // start with userSync off const userSync = newTestUserSync({ syncEnabled: false }); // turn it on with setConfig() config.setConfig({ userSync: { syncEnabled: true } }); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); }); it('should register both image and iframe pixels with filterSettings.all config', function () { const userSync = newTestUserSync({ filterSettings: { all: { bidders: ['atestBidder', 'testBidder'], filter: 'include' }, } }); userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.not.be.null; expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); }); it('should register iframe and not register image pixels based on filterSettings config', function () { const userSync = newTestUserSync({ filterSettings: { image: { bidders: '*', filter: 'exclude' }, iframe: { bidders: ['testBidder'] } } }); userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.be.null; expect(insertUserSyncIframeStub.getCall(0)).to.not.be.null; expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); }); it('should throw a warning and default to basic resgistration rules when filterSettings config is invalid', function () { // invalid config - passed invalid filter option const userSync1 = newTestUserSync({ filterSettings: { iframe: { bidders: ['testBidder'], filter: 'includes' } } }); userSync1.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync1.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync1.syncUsers(); expect(logWarnStub.getCall(0).args[0]).to.exist; expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; // invalid config - bidders is not an array of strings const userSync2 = newTestUserSync({ filterSettings: { iframe: { bidders: ['testBidder', 0], filter: 'include' } } }); userSync2.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync2.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync2.syncUsers(); expect(logWarnStub.getCall(1).args[0]).to.exist; expect(triggerPixelStub.getCall(1)).to.not.be.null; expect(triggerPixelStub.getCall(1).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; // invalid config - bidders list includes wildcard const userSync3 = newTestUserSync({ filterSettings: { iframe: { bidders: ['testBidder', '*'], filter: 'include' } } }); userSync3.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync3.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync3.syncUsers(); expect(logWarnStub.getCall(2).args[0]).to.exist; expect(triggerPixelStub.getCall(2)).to.not.be.null; expect(triggerPixelStub.getCall(2).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; // invalid config - incorrect wildcard const userSync4 = newTestUserSync({ filterSettings: { iframe: { bidders: '***', filter: 'include' } } }); userSync4.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync4.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync4.syncUsers(); expect(logWarnStub.getCall(3).args[0]).to.exist; expect(triggerPixelStub.getCall(3)).to.not.be.null; expect(triggerPixelStub.getCall(3).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; // invalid config - missing bidders field const userSync5 = newTestUserSync({ filterSettings: { iframe: { filter: 'include' } } }); userSync5.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync5.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync5.syncUsers(); expect(logWarnStub.getCall(4).args[0]).to.exist; expect(triggerPixelStub.getCall(4)).to.not.be.null; expect(triggerPixelStub.getCall(4).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; }); it('should overwrite logic of deprecated fields when filterSettings is defined', function () { const userSync = newTestUserSync({ pixelsEnabled: false, iframeEnabled: true, enabledBidders: ['ctestBidder'], filterSettings: { image: { bidders: '*', filter: 'include' }, iframe: { bidders: ['testBidder'], filter: 'exclude' } } }); userSync.registerSync('image', 'atestBidder', 'http://example.com/1'); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.syncUsers(); expect(logWarnStub.getCall(0).args[0]).to.exist; expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com/1'); expect(insertUserSyncIframeStub.getCall(0)).to.be.null; }); it('should still allow default image syncs if setConfig only defined iframe', function () { const userSync = newUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); config.setConfig({ userSync: { filterSettings: { iframe: { bidders: ['bidderXYZ'], filter: 'include' } } } }); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.registerSync('iframe', 'bidderXYZ', 'http://example.com/iframe'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); }); it('should override default image syncs if setConfig used image filter', function () { const userSync = newUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); config.setConfig({ userSync: { filterSettings: { image: { bidders: ['bidderXYZ'], filter: 'exclude' } } } }); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.registerSync('image', 'bidderXYZ', 'http://example.com/image-blocked'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); expect(triggerPixelStub.getCall(1)).to.be.null; }); it('should override default image syncs if setConfig used all filter', function() { const userSync = newUserSync({ config: config.getConfig('userSync'), browserSupportsCookies: true }); config.setConfig({ userSync: { filterSettings: { all: { bidders: ['bidderXYZ'], filter: 'exclude' } } } }); userSync.registerSync('image', 'testBidder', 'http://example.com'); userSync.registerSync('image', 'bidderXYZ', 'http://example.com/image-blocked'); userSync.registerSync('iframe', 'testBidder', 'http://example.com/iframe'); userSync.registerSync('iframe', 'bidderXYZ', 'http://example.com/iframe-blocked'); userSync.syncUsers(); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.equal('http://example.com'); expect(triggerPixelStub.getCall(1)).to.be.null; expect(insertUserSyncIframeStub.getCall(0).args[0]).to.equal('http://example.com/iframe'); expect(insertUserSyncIframeStub.getCall(1)).to.be.null; }); describe('publicAPI', function () { describe('canBidderRegisterSync', function () { describe('with filterSettings', function () { it('should return false if filter settings does not allow it', function () { const userSync = newUserSync({ config: { filterSettings: { image: { bidders: '*', filter: 'include' }, iframe: { bidders: ['testBidder'], filter: 'include' } } } }); expect(userSync.canBidderRegisterSync('iframe', 'otherTestBidder')).to.equal(false); }); it('should return false for iframe if there is no iframe filterSettings', function () { const userSync = newUserSync({ config: { syncEnabled: true, filterSettings: { image: { bidders: '*', filter: 'include' } }, syncsPerBidder: 5, syncDelay: 3000, auctionDelay: 0 } }); expect(userSync.canBidderRegisterSync('iframe', 'otherTestBidder')).to.equal(false); }); it('should return true if filter settings does allow it', function () { const userSync = newUserSync({ config: { filterSettings: { image: { bidders: '*', filter: 'include' }, iframe: { bidders: ['testBidder'], filter: 'include' } } } }); expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(true); }); }); describe('almost deprecated - without filterSettings', function () { describe('enabledBidders contains testBidder', function () { it('should return false if type is iframe and iframeEnabled is false', function () { const userSync = newUserSync({ config: { filterSettings: { iframe: { bidders: ['testBidder'], filter: 'exclude' } } } }); expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(false); }); it('should return true if type is iframe and iframeEnabled is true', function () { const userSync = newUserSync({ config: { pixelEnabled: true, iframeEnabled: true, enabledBidders: ['testBidder'], } }); expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(true); }); it('should return false if type is image and pixelEnabled is false', function () { const userSync = newUserSync({ config: { filterSettings: { image: { bidders: ['testBidder'], filter: 'exclude' } } } }); expect(userSync.canBidderRegisterSync('image', 'testBidder')).to.equal(false); }); it('should return true if type is image and pixelEnabled is true', function () { const userSync = newUserSync({ config: { pixelEnabled: true, iframeEnabled: true, enabledBidders: ['testBidder'], } }); expect(userSync.canBidderRegisterSync('image', 'testBidder')).to.equal(true); }); }); describe('enabledBidders does not container testBidder', function () { it('should return false since testBidder is not in enabledBidders', function () { const userSync = newUserSync({ config: { filterSettings: { image: { bidders: ['otherTestBidder'], filter: 'include' }, iframe: { bidders: ['otherTestBidder'], filter: 'include' } } } }); expect(userSync.canBidderRegisterSync('iframe', 'testBidder')).to.equal(false); }); }); }); }); }); });