diff --git a/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html b/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html
new file mode 100644
index 00000000000..7c73312c5c3
--- /dev/null
+++ b/integrationExamples/videoModule/adPlayerPro/bidRequestScheduling.html
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+ AdPlayer.Pro bid request scheduling
+
+
+
+
+
+
+
+
+AdPlayer.Pro Event Listeners
+
+Div-1: Player placeholder div
+
+
+
+
diff --git a/integrationExamples/videoModule/adPlayerPro/eventListeners.html b/integrationExamples/videoModule/adPlayerPro/eventListeners.html
new file mode 100644
index 00000000000..3c26ef42bee
--- /dev/null
+++ b/integrationExamples/videoModule/adPlayerPro/eventListeners.html
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+ AdPlayer.Pro Event Listeners
+
+
+
+
+
+
+
+
+AdPlayer.Pro Event Listeners
+
+Div-1: Player placeholder div
+
+
+
+
diff --git a/libraries/video/constants/vendorCodes.js b/libraries/video/constants/vendorCodes.js
index 370c151b997..4e3550ce431 100644
--- a/libraries/video/constants/vendorCodes.js
+++ b/libraries/video/constants/vendorCodes.js
@@ -1,6 +1,7 @@
// Video Vendors
export const JWPLAYER_VENDOR = 1;
export const VIDEO_JS_VENDOR = 2;
+export const AD_PLAYER_PRO_VENDOR = 3;
// Ad Server Vendors
export const GAM_VENDOR = 'gam';
diff --git a/modules/.submodules.json b/modules/.submodules.json
index 6b4b0d21d54..1adb1e8cf8e 100644
--- a/modules/.submodules.json
+++ b/modules/.submodules.json
@@ -109,7 +109,8 @@
],
"videoModule": [
"jwplayerVideoProvider",
- "videojsVideoProvider"
+ "videojsVideoProvider",
+ "adplayerproVideoProvider"
],
"paapi": [
"paapiForGpt",
diff --git a/modules/adplayerproVideoProvider.js b/modules/adplayerproVideoProvider.js
new file mode 100644
index 00000000000..826aee257ec
--- /dev/null
+++ b/modules/adplayerproVideoProvider.js
@@ -0,0 +1,439 @@
+import {
+ API_FRAMEWORKS,
+ PLACEMENT,
+ PLAYBACK_METHODS,
+ PROTOCOLS,
+ VIDEO_MIME_TYPE,
+ VPAID_MIME_TYPE
+} from '../libraries/video/constants/ortb.js';
+import {
+ AD_CLICK,
+ AD_COMPLETE,
+ AD_ERROR,
+ AD_IMPRESSION,
+ AD_LOADED,
+ AD_PAUSE,
+ AD_PLAY,
+ AD_REQUEST,
+ AD_SKIPPED,
+ AD_STARTED,
+ DESTROYED,
+ ERROR,
+ MUTE,
+ PLAYER_RESIZE,
+ SETUP_COMPLETE,
+ SETUP_FAILED,
+ VOLUME
+} from '../libraries/video/constants/events.js';
+import {AD_PLAYER_PRO_VENDOR} from '../libraries/video/constants/vendorCodes.js';
+import {getEventHandler} from '../libraries/video/shared/eventHandler.js';
+import {submodule} from '../src/hook.js';
+
+const setupFailMessage = 'Failed to instantiate the player';
+
+/**
+ * @constructor
+ * @param {Object} config - videoProviderConfig
+ * @param {function} adPlayerPro_
+ * @param {CallbackStorage} callbackStorage_
+ * @param {Object} utils
+ * @returns {Object} - VideoProvider
+ */
+export function AdPlayerProProvider(config, adPlayerPro_, callbackStorage_, utils) {
+ const adPlayerPro = adPlayerPro_;
+ let player = null;
+ let playerVersion = null;
+ const playerConfig = config.playerConfig;
+ const divId = config.divId;
+ let callbackStorage = callbackStorage_;
+ let supportedMediaTypes = null;
+ let setupCompleteCallbacks = [];
+ let setupFailedCallbacks = [];
+ const MEDIA_TYPES = [
+ VIDEO_MIME_TYPE.MP4,
+ VIDEO_MIME_TYPE.OGG,
+ VIDEO_MIME_TYPE.WEBM,
+ VIDEO_MIME_TYPE.AAC,
+ VIDEO_MIME_TYPE.HLS
+ ];
+
+ function init() {
+ if (!adPlayerPro) {
+ triggerSetupFailure(-1, setupFailMessage + ': player not present');
+ return;
+ }
+
+ // if (playerVersion < minimumSupportedPlayerVersion) {
+ // triggerSetupFailure(-2, setupFailMessage + ': player version not supported');
+ // return;
+ // }
+
+ if (!document.getElementById(divId)) {
+ triggerSetupFailure(-3, setupFailMessage + ': No div found with id ' + divId);
+ return;
+ }
+
+ if (!playerConfig || !playerConfig.placementId) {
+ triggerSetupFailure(-4, setupFailMessage + ': placementId is required in playerConfig');
+ return;
+ }
+
+ triggerSetupComplete();
+ }
+
+ function getId() {
+ return divId;
+ }
+
+ function getOrtbVideo() {
+ supportedMediaTypes = supportedMediaTypes || utils.getSupportedMediaTypes(MEDIA_TYPES);
+
+ const video = {
+ mimes: supportedMediaTypes,
+ protocols: [
+ PROTOCOLS.VAST_2_0,
+ PROTOCOLS.VAST_3_0,
+ PROTOCOLS.VAST_4_0,
+ PROTOCOLS.VAST_2_0_WRAPPER,
+ PROTOCOLS.VAST_3_0_WRAPPER,
+ PROTOCOLS.VAST_4_0_WRAPPER
+ ],
+ // h: player.getHeight(),
+ // w: player.getWidth(),
+ placement: utils.getPlacement(playerConfig.advertising),
+ maxextended: -1, // extension is allowed, and there is no time limit imposed.
+ boxingallowed: 1,
+ playbackmethod: [utils.getPlaybackMethod(config)],
+ playbackend: 1,
+ api: [
+ API_FRAMEWORKS.VPAID_2_0,
+ API_FRAMEWORKS.OMID_1_0
+ ],
+ };
+
+ return video;
+ }
+
+ function getOrtbContent() {
+ }
+
+ function setAdTagUrl(adTagUrl, options) {
+ setupPlayer(playerConfig, adTagUrl || options.adXml)
+ }
+
+ function onEvent(externalEventName, callback, basePayload) {
+ if (externalEventName === SETUP_COMPLETE) {
+ setupCompleteCallbacks.push(callback);
+ return;
+ }
+
+ if (externalEventName === SETUP_FAILED) {
+ setupFailedCallbacks.push(callback);
+ return;
+ }
+
+ let getEventPayload;
+
+ switch (externalEventName) {
+ case AD_REQUEST:
+ case AD_PLAY:
+ case AD_PAUSE:
+ case AD_LOADED:
+ case AD_STARTED:
+ case AD_IMPRESSION:
+ case AD_CLICK:
+ case AD_SKIPPED:
+ case AD_ERROR:
+ case AD_COMPLETE:
+ case MUTE:
+ case VOLUME:
+ case ERROR:
+ case PLAYER_RESIZE:
+ getEventPayload = e => ({
+ height: player.getAdHeight(),
+ width: player.getAdWidth(),
+ });
+ break;
+ default:
+ return;
+ }
+
+ // eslint-disable-next-line no-unreachable
+ const playerEventName = utils.getPlayerEvent(externalEventName);
+ const eventHandler = getEventHandler(externalEventName, callback, basePayload, getEventPayload)
+ player && player.on(playerEventName, eventHandler);
+ callbackStorage.storeCallback(playerEventName, eventHandler, callback);
+ }
+
+ function offEvent(event, callback) {
+ const playerEventName = utils.getPlayerEvent(event);
+ const eventHandler = callbackStorage.getCallback(playerEventName, callback);
+ if (eventHandler) {
+ player && player.off(playerEventName, eventHandler);
+ } else {
+ player && player.off(playerEventName);
+ }
+ callbackStorage.clearCallback(playerEventName, callback);
+ }
+
+ function destroy() {
+ if (!player) {
+ return;
+ }
+ player.remove();
+ player = null;
+ }
+
+ return {
+ init,
+ getId,
+ getOrtbVideo,
+ getOrtbContent,
+ setAdTagUrl,
+ onEvent,
+ offEvent,
+ destroy
+ };
+
+ function setupPlayer(config, urlOrXml) {
+ if (!config || player) {
+ return;
+ }
+ const playerConfig = utils.getConfig(config, urlOrXml);
+
+ if (!playerConfig) {
+ return;
+ }
+
+ player = adPlayerPro(divId);
+ callbackStorage.addAllCallbacks(player.on);
+ player.on('AdStopped', () => player = null);
+ player.setup(playerConfig);
+ }
+
+ function triggerSetupComplete() {
+ if (!setupCompleteCallbacks.length) {
+ return;
+ }
+
+ const payload = getSetupCompletePayload();
+ setupCompleteCallbacks.forEach(callback => callback(SETUP_COMPLETE, payload));
+ setupCompleteCallbacks = [];
+ }
+
+ function getSetupCompletePayload() {
+ return {
+ divId,
+ playerVersion,
+ type: SETUP_COMPLETE
+ };
+ }
+
+ function triggerSetupFailure(errorCode, msg, sourceError) {
+ if (!setupFailedCallbacks.length) {
+ return;
+ }
+
+ const payload = {
+ divId,
+ playerVersion,
+ type: SETUP_FAILED,
+ errorCode: errorCode,
+ errorMessage: msg,
+ sourceError: sourceError
+ };
+
+ setupFailedCallbacks.forEach(callback => callback(SETUP_FAILED, payload));
+ setupFailedCallbacks = [];
+ }
+}
+
+/**
+ * @param {Object} config - videoProviderConfig
+ * @param {sharedUtils} sharedUtils
+ * @returns {Object} - VideoProvider
+ */
+const adPlayerProSubmoduleFactory = function (config, sharedUtils) {
+ const callbackStorage = callbackStorageFactory();
+ return AdPlayerProProvider(config, window.playerPro, callbackStorage, utils);
+}
+
+adPlayerProSubmoduleFactory.vendorCode = AD_PLAYER_PRO_VENDOR;
+submodule('video', adPlayerProSubmoduleFactory);
+export default adPlayerProSubmoduleFactory;
+
+// HELPERS
+
+export const utils = {
+ getConfig: function (config, urlOrXml) {
+ if (!config || !urlOrXml) {
+ return;
+ }
+
+ const params = config.params || {};
+ params.placementId = config.placementId;
+ params.advertising = params.advertising || {};
+ params.advertising.tag = params.advertising.tag || {};
+
+ params._pType = 'pbjs';
+ params.advertising.tag.url = urlOrXml;
+ return params;
+ },
+
+ getPlayerEvent: function (eventName) {
+ switch (eventName) {
+ case DESTROYED:
+ return 'AdStopped';
+
+ case AD_REQUEST:
+ return 'AdRequest';
+ case AD_LOADED:
+ return 'AdLoaded';
+ case AD_STARTED:
+ return 'AdStarted';
+ case AD_IMPRESSION:
+ return 'AdImpression';
+ case AD_PLAY:
+ return 'AdPlaying';
+ case AD_PAUSE:
+ return 'AdPaused';
+ case AD_CLICK:
+ return 'AdClickThru';
+ case AD_SKIPPED:
+ return 'AdSkipped';
+ case AD_ERROR:
+ return 'AdError';
+ case AD_COMPLETE:
+ return 'AdCompleted';
+ case VOLUME:
+ return 'AdVolumeChange';
+ case PLAYER_RESIZE:
+ return 'AdSizeChange';
+ // case FULLSCREEN:
+ // return FULLSCREEN;
+ default:
+ return eventName;
+ }
+ },
+
+ getSupportedMediaTypes: function (mediaTypes = []) {
+ const el = document.createElement('video');
+ return mediaTypes
+ .filter(mediaType => el.canPlayType(mediaType))
+ .concat(VPAID_MIME_TYPE); // Always allow VPAIDs.
+ },
+
+ /**
+ * Determine the ad placement
+ * @param {Object} adConfig
+ * @return {PLACEMENT|undefined}
+ */
+ getPlacement: function (adConfig) {
+ adConfig = adConfig || {};
+
+ switch (adConfig.type) {
+ case 'inPage':
+ return PLACEMENT.ARTICLE;
+ case 'rewarded':
+ case 'inView':
+ return PLACEMENT.INTERSTITIAL_SLIDER_FLOATING;
+ default:
+ return PLACEMENT.BANNER;
+ }
+ },
+
+ getPlaybackMethod: function ({autoplay, mute}) {
+ if (autoplay) {
+ return mute ? PLAYBACK_METHODS.AUTOPLAY_MUTED : PLAYBACK_METHODS.AUTOPLAY;
+ }
+ return PLAYBACK_METHODS.CLICK_TO_PLAY;
+ }
+}
+
+/**
+ * Tracks which functions are attached to events
+ * @typedef CallbackStorage
+ * @function storeCallback
+ * @function getCallback
+ * @function clearCallback
+ * @function addAllCallbacks
+ * @function clearStorage
+ */
+
+/**
+ * @returns {CallbackStorage}
+ */
+export function callbackStorageFactory() {
+ let storage = {};
+ let storageHandlers = {};
+
+ function storeCallback(eventType, eventHandler, callback) {
+ let eventHandlers = storage[eventType];
+ if (!eventHandlers) {
+ eventHandlers = storage[eventType] = {};
+ }
+
+ eventHandlers[callback] = eventHandler;
+ addHandler(eventType, eventHandler);
+ }
+
+ function getCallback(eventType, callback) {
+ let eventHandlers = storage[eventType];
+ if (eventHandlers) {
+ return eventHandlers[callback];
+ }
+ }
+
+ function clearCallback(eventType, callback) {
+ if (!callback) {
+ delete storage[eventType];
+ delete storageHandlers[eventType];
+ return;
+ }
+ let eventHandlers = storage[eventType];
+ if (eventHandlers) {
+ const eventHandler = eventHandlers[callback];
+ if (eventHandler) {
+ delete eventHandlers[callback];
+ clearHandler(eventType, eventHandler);
+ }
+ }
+ }
+
+ function clearStorage() {
+ storage = {};
+ storageHandlers = {};
+ }
+
+ function addHandler(eventType, eventHandler) {
+ let eventHandlers = storageHandlers[eventType];
+ if (!eventHandlers) {
+ eventHandlers = storageHandlers[eventType] = [];
+ }
+ eventHandlers.push(eventHandler);
+ }
+
+ function clearHandler(eventType, eventHandler) {
+ let eventHandlers = storageHandlers[eventType];
+ eventHandlers = eventHandlers.filter(handler => handler !== eventHandler);
+ if (eventHandlers.length) {
+ storageHandlers[eventType] = eventHandlers;
+ } else {
+ delete storageHandlers[eventType];
+ }
+ }
+
+ function addAllCallbacks(functionOnPlayer) {
+ for (let eventType in storageHandlers) {
+ storageHandlers[eventType].forEach(handler => functionOnPlayer(eventType, handler));
+ }
+ }
+
+ return {
+ storeCallback,
+ getCallback,
+ clearCallback,
+ addAllCallbacks,
+ clearStorage,
+ }
+}
diff --git a/modules/adplayerproVideoProvider.md b/modules/adplayerproVideoProvider.md
new file mode 100644
index 00000000000..d0b0601afeb
--- /dev/null
+++ b/modules/adplayerproVideoProvider.md
@@ -0,0 +1,54 @@
+# Overview
+
+Module Name: AdPlayer.Pro Video Provider
+Module Type: Video Submodule
+Video Player: AdPlayer.Pro
+Player website: https://adplayer.pro
+Maintainer: support@adplayer.pro
+
+# Description
+
+Video provider to connect the Prebid Video Module to AdPlayer.Pro.
+
+# Requirements
+
+Your page must embed a build of AdPlayer.Pro.
+i.e.
+```html
+
+
+
+```
+
+# Configuration
+
+The AdPlayer.Pro Video Provider requires the following configuration:
+
+```javascript
+pbjs.setConfig({
+ video: {
+ providers: [{
+ divId: 'player', // required, this is the id of the div element where the player will be placed
+ vendorCode: 3, // AdPlayer.Pro vendorCode
+ playerConfig: {
+ placementId: 'c9gebfehcqjE', // required, this placementId is only for demo purposes
+ params: {
+ 'type': 'inView',
+ 'muted': true,
+ 'autoStart': true,
+ 'advertising': {
+ 'controls': true,
+ 'closeButton': true,
+ },
+ 'width': '600',
+ 'height': '300'
+ }
+ },
+ }]
+ }
+});
+```
+
+[Additional embed instructions](https://docs.adplayer.pro)
+
+[Obtaining a license](https://adplayer.pro/contacts)
diff --git a/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js b/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js
new file mode 100644
index 00000000000..1a792411497
--- /dev/null
+++ b/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js
@@ -0,0 +1,521 @@
+// Using require style imports for fine grained control of import time
+import {
+ AD_CLICK,
+ AD_COMPLETE,
+ AD_ERROR,
+ AD_IMPRESSION,
+ AD_LOADED,
+ AD_PAUSE,
+ AD_PLAY,
+ AD_REQUEST,
+ AD_SKIPPED,
+ AD_STARTED,
+ DESTROYED,
+ PLAYER_RESIZE,
+ SETUP_COMPLETE,
+ SETUP_FAILED,
+ VOLUME
+} from 'libraries/video/constants/events.js';
+import adPlayerProSubmoduleFactory, {callbackStorageFactory} from '../../../../../modules/adplayerproVideoProvider.js';
+import {PLACEMENT} from '../../../../../libraries/video/constants/ortb';
+import sinon from 'sinon';
+
+const {AdPlayerProProvider, utils} = require('modules/adplayerproVideoProvider.js');
+
+const {
+ PROTOCOLS, API_FRAMEWORKS, VIDEO_MIME_TYPE, PLAYBACK_METHODS, VPAID_MIME_TYPE
+} = require('libraries/video/constants/ortb.js');
+
+function getPlayerMock() {
+ return {
+ setup: function () {
+ return this;
+ },
+ load: function () {
+ },
+ resize: function () {
+ },
+ remove: function () {
+ },
+ on: function () {
+ return this;
+ },
+ off: function () {
+ return this;
+ },
+ getAdWidth: function () {
+ return 600
+ },
+ getAdHeight: function () {
+ return 400;
+ }
+ };
+}
+
+function makePlayerFactoryMock(playerMock_) {
+ return () => playerMock_;
+}
+
+function getUtilsMock() {
+ return {
+ getConfig: function () {
+ },
+ getPlayerEvent: event => event,
+ getSupportedMediaTypes: function () {
+ },
+ getPlacement: function () {
+ },
+ getPlaybackMethod: function () {
+ }
+ };
+}
+
+function addDiv() {
+ const div = document.createElement('div');
+ div.setAttribute('id', 'test');
+ document.body.appendChild(div);
+}
+
+function removeDiv() {
+ const div = document.getElementById('test');
+ if (div) {
+ div.remove();
+ }
+}
+
+describe('AdPlayerProProvider', function () {
+ let config;
+ let callbackStorage;
+ let utilsMock;
+ let player;
+
+ beforeEach(() => {
+ addDiv();
+ config = {divId: 'test', playerConfig: {placementId: 'testId'}};
+ callbackStorage = callbackStorageFactory();
+ utilsMock = getUtilsMock();
+ player = getPlayerMock();
+ });
+
+ afterEach(() => {
+ removeDiv();
+ });
+
+ describe('init', function () {
+ it('should trigger failure when Adplayer.Pro is missing', function () {
+ const provider = AdPlayerProProvider(config, null, callbackStorage, utilsMock);
+ const setupFailed = sinon.spy();
+ provider.onEvent(SETUP_FAILED, setupFailed, {});
+ provider.init();
+ expect(setupFailed.calledOnce).to.be.true;
+ const payload = setupFailed.args[0][1];
+ expect(payload.errorCode).to.be.equal(-1);
+ });
+
+ it('should trigger failure when the div is not found', function () {
+ config.divId = 'fake-div'
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utilsMock);
+ const setupFailed = sinon.spy();
+ provider.onEvent(SETUP_FAILED, setupFailed, {});
+ provider.init();
+ expect(setupFailed.calledOnce).to.be.true;
+ const payload = setupFailed.args[0][1];
+ expect(payload.errorCode).to.be.equal(-3);
+ });
+
+ it('should trigger failure when the placementId is not found', function () {
+ config.playerConfig.placementId = '';
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utilsMock);
+ const setupFailed = sinon.spy();
+ provider.onEvent(SETUP_FAILED, setupFailed, {});
+ provider.init();
+ expect(setupFailed.calledOnce).to.be.true;
+ const payload = setupFailed.args[0][1];
+ expect(payload.errorCode).to.be.equal(-4);
+ });
+
+ it('should instantiate the player after setAdTagUrl', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ expect(setupSpy.calledOnce).to.be.false;
+ provider.setAdTagUrl('https://test.com', {});
+ expect(setupSpy.calledOnce).to.be.true;
+ const payload = setupSpy.args[0][0];
+ expect(payload.advertising.tag.url).to.be.equal('https://test.com');
+ });
+
+ it('should instantiate the player after setAdTagUrl for adPlayerProSubmoduleFactory', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ window.playerPro = makePlayerFactoryMock(player);
+ const provider = adPlayerProSubmoduleFactory(config, {});
+ provider.init();
+ expect(setupSpy.calledOnce).to.be.false;
+ provider.setAdTagUrl('https://test.com', {});
+ expect(setupSpy.calledOnce).to.be.true;
+ const payload = setupSpy.args[0][0];
+ expect(payload.advertising.tag.url).to.be.equal('https://test.com');
+ });
+
+ it('should trigger setup complete when player is already instantiated', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ const setupComplete = sinon.spy();
+ provider.onEvent(SETUP_COMPLETE, setupComplete, {});
+ provider.init();
+ expect(setupComplete.calledOnce).to.be.true;
+ expect(setupSpy.called).to.be.false;
+ });
+
+ it('should support multiple setup complete event handlers', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ const setupComplete = sinon.spy();
+ const setupComplete2 = sinon.spy();
+ provider.onEvent(SETUP_COMPLETE, setupComplete, {});
+ provider.onEvent(SETUP_COMPLETE, setupComplete2, {});
+ provider.init();
+ expect(setupComplete.calledOnce).to.be.true;
+ expect(setupComplete2.calledOnce).to.be.true;
+ expect(setupSpy.called).to.be.false;
+ });
+
+ it('should not reinstantiate player', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ const onSpy = player.on = sinon.spy(player.on);
+
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ expect(setupSpy.calledOnce).to.be.false;
+ provider.setAdTagUrl('https://test.com', {});
+ expect(setupSpy.calledOnce).to.be.true;
+ const payload = setupSpy.args[0][0];
+ expect(payload.advertising.tag.url).to.be.equal('https://test.com');
+
+ // test that the player is not reinitialized
+ provider.setAdTagUrl('https://test.com', {});
+ expect(setupSpy.calledOnce).to.be.true;
+
+ // get and call AdStopped event
+ const args = onSpy.args[0];
+ expect(args[0]).to.be.equal('AdStopped');
+ args[1]();
+
+ provider.setAdTagUrl('https://test.com', {});
+ expect(setupSpy.calledTwice).to.be.true;
+ });
+ });
+
+ describe('getId', function () {
+ it('should return configured div id', function () {
+ const provider = AdPlayerProProvider(config, undefined, undefined, utils);
+ expect(provider.getId()).to.be.equal('test');
+ });
+ });
+
+ describe('getOrtbVideo', function () {
+ it('should populate oRTB Video params', function () {
+ const test_media_type = VIDEO_MIME_TYPE.MP4;
+ const test_placement = PLACEMENT.ARTICLE;
+ const test_playback_method = PLAYBACK_METHODS.CLICK_TO_PLAY;
+
+ utilsMock.getSupportedMediaTypes = () => [test_media_type];
+ utilsMock.getPlacement = () => test_placement;
+ utilsMock.getPlaybackMethod = () => test_playback_method;
+
+ const provider = AdPlayerProProvider(config, null, null, utilsMock);
+ provider.init();
+ let video = provider.getOrtbVideo();
+
+ expect(video.mimes).to.include(VIDEO_MIME_TYPE.MP4);
+ expect(video.protocols).to.include.members([
+ PROTOCOLS.VAST_2_0,
+ PROTOCOLS.VAST_3_0,
+ PROTOCOLS.VAST_4_0,
+ PROTOCOLS.VAST_2_0_WRAPPER,
+ PROTOCOLS.VAST_3_0_WRAPPER,
+ PROTOCOLS.VAST_4_0_WRAPPER
+ ]);
+ expect(video.placement).to.equal(test_placement);
+ expect(video.maxextended).to.equal(-1);
+ expect(video.boxingallowed).to.equal(1);
+ expect(video.playbackmethod).to.include(test_playback_method);
+ expect(video.playbackend).to.equal(1);
+ expect(video.api).to.have.length(2);
+ expect(video.api).to.include.members([API_FRAMEWORKS.VPAID_2_0, API_FRAMEWORKS.OMID_1_0]); //
+ });
+ });
+
+ describe('getOrtbContent', function () {
+ it('should populate oRTB Content params', function () {
+ const provider = AdPlayerProProvider(config, null, null, utils);
+ provider.init();
+ expect(provider.getOrtbContent()).to.be.undefined;
+ });
+ });
+
+ describe('setAdTagUrl', function () {
+ it('should call setup', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('', {adXml: 'https://test.com'});
+ expect(setupSpy.calledOnce).to.be.true;
+ });
+
+ it('should not call setup', function () {
+ const setupSpy = player.setup = sinon.spy(player.setup);
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('', {});
+ expect(setupSpy.calledOnce).to.be.false;
+ });
+ });
+
+ describe('events', function () {
+ it('should register event listener on player', function () {
+ const onSpy = player.on = sinon.spy();
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('https://test.com', {});
+ const callback = () => {
+ };
+ provider.onEvent(AD_REQUEST, callback, {});
+ provider.onEvent('test', callback, {});
+ expect(onSpy.calledTwice).to.be.true;
+ expect(onSpy.args[0][0]).to.be.equal('AdStopped');
+ expect(onSpy.args[1][0]).to.be.equal('AdRequest');
+ });
+
+ it('should remove event listener on player', function () {
+ const offSpy = player.off = sinon.spy();
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('https://test.com', {});
+ const callback = () => {
+ };
+ provider.onEvent(AD_IMPRESSION, callback, {});
+ provider.offEvent(AD_IMPRESSION, callback);
+ expect(offSpy.calledOnce).to.be.true;
+ const eventName = offSpy.args[0][0];
+ const eventCallback = offSpy.args[0][1];
+ expect(eventName).to.be.equal('AdImpression');
+ expect(eventCallback).to.be.exist;
+ });
+
+ it('should remove event listener on player by eventName', function () {
+ const offSpy = player.off = sinon.spy();
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('https://test.com', {});
+ const callback = () => {
+ };
+ provider.onEvent(AD_IMPRESSION, callback, {});
+ provider.offEvent(AD_IMPRESSION);
+ expect(offSpy.calledOnce).to.be.true;
+ const eventName = offSpy.args[0][0];
+ expect(eventName).to.be.equal('AdImpression');
+ });
+
+ it('should call event player resize', function () {
+ const onSpy = player.on = sinon.spy();
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('https://test.com', {});
+ const callbackSpy = sinon.spy();
+ provider.onEvent(PLAYER_RESIZE, callbackSpy, {});
+ expect(onSpy.calledTwice).to.be.true;
+ expect(onSpy.args[1][0]).to.be.equal('AdSizeChange');
+ expect(callbackSpy.notCalled).to.be.true;
+ onSpy.args[1][1]();
+ expect(callbackSpy.calledOnce).to.be.true;
+ expect(callbackSpy.args[0][1].width).to.be.equal(600);
+ expect(callbackSpy.args[0][1].height).to.be.equal(400);
+ });
+ });
+
+ describe('destroy', function () {
+ it('should remove and null the player', function () {
+ const removeSpy = player.remove = sinon.spy();
+ player.remove = removeSpy;
+ const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils);
+ provider.init();
+ provider.setAdTagUrl('https://test.com', {});
+ provider.destroy();
+ provider.destroy();
+ expect(removeSpy.calledOnce).to.be.true;
+ });
+ });
+});
+
+describe('AdPlayerProProvider utils', function () {
+ it('getConfig', function () {
+ expect(utils.getConfig()).to.be.undefined;
+ expect(utils.getConfig({})).to.be.undefined;
+ const config = utils.getConfig({}, 'https://test.com');
+ expect(config.advertising.tag.url).to.be.equal('https://test.com');
+ expect(config._pType).to.be.equal('pbjs');
+ });
+
+ it('getPlayerEvent', function () {
+ function test(event, expected) {
+ expect(utils.getPlayerEvent(event)).to.be.equal(expected);
+ }
+
+ test(DESTROYED, 'AdStopped');
+ test(AD_REQUEST, 'AdRequest');
+ test(AD_LOADED, 'AdLoaded');
+ test(AD_STARTED, 'AdStarted');
+ test(AD_IMPRESSION, 'AdImpression');
+ test(AD_PLAY, 'AdPlaying');
+ test(AD_PAUSE, 'AdPaused');
+ test(AD_CLICK, 'AdClickThru');
+ test(AD_SKIPPED, 'AdSkipped');
+ test(AD_ERROR, 'AdError');
+ test(AD_COMPLETE, 'AdCompleted');
+ test(VOLUME, 'AdVolumeChange');
+ test(PLAYER_RESIZE, 'AdSizeChange');
+ test('test', 'test');
+ });
+
+ it('getSupportedMediaTypes', function () {
+ let supportedMediaTypes = utils.getSupportedMediaTypes([]);
+ expect(supportedMediaTypes).to.include(VPAID_MIME_TYPE);
+
+ supportedMediaTypes = utils.getSupportedMediaTypes([VIDEO_MIME_TYPE.MP4]);
+ expect(supportedMediaTypes).to.include(VPAID_MIME_TYPE);
+ });
+
+ it('getPlacement', function () {
+ function test(config, expected) {
+ expect(utils.getPlacement(config)).to.be.equal(expected);
+ }
+
+ test(false, PLACEMENT.BANNER);
+ test({}, PLACEMENT.BANNER);
+ test({type: 'test'}, PLACEMENT.BANNER);
+ test({type: 'inPage'}, PLACEMENT.ARTICLE);
+ test({type: 'rewarded'}, PLACEMENT.INTERSTITIAL_SLIDER_FLOATING);
+ test({type: 'inView'}, PLACEMENT.INTERSTITIAL_SLIDER_FLOATING);
+ });
+
+ it('getPlaybackMethod', function () {
+ function test(autoplay, mute, expected) {
+ expect(utils.getPlaybackMethod({autoplay, mute})).to.be.equal(expected);
+ }
+
+ test(false, false, PLAYBACK_METHODS.CLICK_TO_PLAY);
+ test(false, true, PLAYBACK_METHODS.CLICK_TO_PLAY);
+ test(true, false, PLAYBACK_METHODS.AUTOPLAY);
+ test(true, true, PLAYBACK_METHODS.AUTOPLAY_MUTED);
+ });
+});
+
+describe('AdPlayerProProvider callbackStorageFactory', function () {
+ let player;
+ let callbackStorage;
+
+ beforeEach(() => {
+ player = getPlayerMock();
+ callbackStorage = callbackStorageFactory();
+ });
+
+ it('storeCallback and getCallback', function () {
+ const eventType = 'test';
+ const callback = () => 11;
+ const eventHandler = () => 12;
+ callbackStorage.storeCallback(eventType, eventHandler, callback);
+ expect(callbackStorage.getCallback(eventType, callback)).to.be.equal(eventHandler);
+
+ const callback2 = () => 21;
+ const eventHandler2 = () => 22;
+ callbackStorage.storeCallback(eventType, eventHandler2, callback2);
+ expect(callbackStorage.getCallback(eventType, callback2)).to.be.equal(eventHandler2);
+
+ expect(callbackStorage.getCallback(eventType, callback)).to.be.equal(eventHandler);
+ });
+
+ it('clearCallback', function () {
+ const eventType = 'test';
+ const callback = () => 11;
+ const callback2 = () => 21;
+ callbackStorage.storeCallback(eventType, () => 12, callback);
+ callbackStorage.storeCallback(eventType, () => 22, callback2);
+
+ expect(callbackStorage.getCallback(eventType, callback)()).to.be.equal(12);
+ expect(callbackStorage.getCallback(eventType, callback2)()).to.be.equal(22);
+
+ callbackStorage.clearCallback(eventType);
+
+ expect(callbackStorage.getCallback(eventType, callback)).to.be.undefined;
+ expect(callbackStorage.getCallback(eventType, callback2)).to.be.undefined;
+
+ callbackStorage.storeCallback(eventType, () => 12, callback);
+ callbackStorage.storeCallback(eventType, () => 22, callback2);
+
+ callbackStorage.clearCallback(eventType, callback);
+
+ expect(callbackStorage.getCallback(eventType, callback)).to.be.undefined;
+ expect(callbackStorage.getCallback(eventType, callback2)()).to.be.equal(22);
+ });
+
+ it('addAllCallbacks', function () {
+ const eventType = 'test';
+ const eventType2 = 'test2';
+ const callback = () => 11;
+ const callback2 = () => 21;
+ const eventHandler = () => 12;
+ const eventHandler2 = () => 22;
+ callbackStorage.storeCallback(eventType, eventHandler, callback);
+ callbackStorage.storeCallback(eventType, eventHandler2, callback2);
+ callbackStorage.storeCallback(eventType2, eventHandler, callback);
+ callbackStorage.storeCallback(eventType2, eventHandler2, callback2);
+
+ let spy = sinon.spy();
+ callbackStorage.addAllCallbacks(spy);
+ expect(spy.callCount).to.be.equal(4);
+ expect(spy.calledWith(eventType, eventHandler)).to.be.true;
+ expect(spy.calledWith(eventType, eventHandler2)).to.be.true;
+ expect(spy.calledWith(eventType2, eventHandler)).to.be.true;
+ expect(spy.calledWith(eventType2, eventHandler2)).to.be.true;
+
+ callbackStorage.clearCallback(eventType, callback);
+ spy = sinon.spy();
+ callbackStorage.addAllCallbacks(spy);
+ expect(spy.callCount).to.be.equal(3);
+ expect(spy.calledWith(eventType, eventHandler2)).to.be.true;
+ expect(spy.calledWith(eventType2, eventHandler)).to.be.true;
+ expect(spy.calledWith(eventType2, eventHandler2)).to.be.true;
+
+ callbackStorage.clearCallback(eventType2);
+ spy = sinon.spy();
+ callbackStorage.addAllCallbacks(spy);
+ expect(spy.callCount).to.be.equal(1);
+ expect(spy.calledWith(eventType, eventHandler2)).to.be.true;
+
+ callbackStorage.storeCallback(eventType, eventHandler, callback);
+ callbackStorage.storeCallback(eventType2, eventHandler, callback);
+ spy = sinon.spy();
+ callbackStorage.addAllCallbacks(spy);
+ expect(spy.callCount).to.be.equal(3);
+ expect(spy.calledWith(eventType, eventHandler)).to.be.true;
+ expect(spy.calledWith(eventType, eventHandler2)).to.be.true;
+ expect(spy.calledWith(eventType2, eventHandler)).to.be.true;
+
+ callbackStorage.clearStorage();
+ spy = sinon.spy();
+ callbackStorage.addAllCallbacks(spy);
+ expect(spy.callCount).to.be.equal(0);
+ });
+
+ it('clearStorage', function () {
+ const eventType = 'test';
+ const callback = () => 11;
+ const eventHandler = () => 12;
+ callbackStorage.storeCallback(eventType, eventHandler, callback);
+ expect(callbackStorage.getCallback(eventType, callback)).to.be.equal(eventHandler);
+
+ callbackStorage.clearStorage();
+ expect(callbackStorage.getCallback(eventType, callback)).to.be.undefined;
+ });
+});