From 253cbf4de5d575a70a288b3249d7bf4f1f7ddfc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=9B=A8=E8=BB=92=20=D0=9F=D0=B5=D1=82=D1=80?= Date: Mon, 22 Apr 2019 20:09:08 +0200 Subject: [PATCH] Improve emoteevBidAdapter (#3673) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve emoteevBidAdapter ** Squash several pending changes - Improve test coverage - Extreme programming: 100% test coverage - Document important constants - Extreme programming: document all functions - Imperative shell, functional core - Send events onBidWon and onTimeout - Report GDPR relevance and consent Code documentation uses JSDoc tags wherever possible. See the following link for general explanation of the notation used: https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler ** Test coverage - 100% Statements 110/110 - 100% Branches 55/55 - 100% Functions 28/28 - 100% Lines 99/99 ** Integration tests Tested against production endpoint with the possible combinations of these following parameters: - Browser: · Chrome Canary 75.0.3739.0 · Firefox Developer Edition 67.0b3 (64-bit) · Safari version 12.0.3 (14606.4.5) · Tor Browser 8.0.6 (based on Mozilla Firefox 60.5.1esr) - Integration page: · integrationExamples/gpt/hello_world_emoteev.html · https://jsfiddle.net/8aqotw1k/6/ Localhost test server launched with: $ npm install && gulp serve * Tentative CI fix for IE and Edge webGL test https://circleci.com/gh/prebid/Prebid.js/2052 * Tentative CI fix for IE and Edge webGL test #2 * Give up on webGL tests Has anybody an idea to make it pass? https://circleci.com/gh/prebid/Prebid.js/2056 New test coverage: - 94.5% Statements 103/109 - 98.18% Branches 54/55 - 100% Functions 28/28 - 93.88% Lines 92/98 * Avoid useless noice in diff * Remove unallowed metric pixel ON_ADAPTER_CALLED --- .../gpt/hello_world_emoteev.html | 117 ++- modules/emoteevBidAdapter.js | 465 ++++++++-- ...teevBidAdapter.md => emoteevBidAdapter.md} | 0 test/spec/modules/emoteevBidAdapter_spec.js | 860 +++++++++++++----- 4 files changed, 1056 insertions(+), 386 deletions(-) rename modules/{emokteevBidAdapter.md => emoteevBidAdapter.md} (100%) diff --git a/integrationExamples/gpt/hello_world_emoteev.html b/integrationExamples/gpt/hello_world_emoteev.html index 5e4d0716d2b..5a33e2d9701 100644 --- a/integrationExamples/gpt/hello_world_emoteev.html +++ b/integrationExamples/gpt/hello_world_emoteev.html @@ -5,74 +5,69 @@ + setTimeout(function () { + initAdserver(); + }, FAILSAFE_TIMEOUT); + googletag.cmd.push(function () { + googletag.defineSlot('/19968336/header-bid-tag-1', sizes, 'div-1') + .addService(googletag.pubads()); + googletag.pubads().enableSingleRequest(); + googletag.enableServices(); + }); + @@ -80,9 +75,9 @@

Basic Prebid.js Example

Div-1
diff --git a/modules/emoteevBidAdapter.js b/modules/emoteevBidAdapter.js index 9b03b357818..4436d39bb70 100644 --- a/modules/emoteevBidAdapter.js +++ b/modules/emoteevBidAdapter.js @@ -1,76 +1,322 @@ +/** + * This file contains Emoteev bid adpater. + * + * It is organised as follows: + * - Constants values; + * - Spec API functions, which should be pristine pure; + * - Ancillary functions, which should be as pure as possible; + * - Adapter API, where unpure side-effects happen. + * + * The code style is « functional core, imperative shell ». + * + * @link https://www.emoteev.io + * @file This files defines the spec of EmoteevBidAdapter. + * @author Emoteev Engineering . + */ + import {registerBidder} from '../src/adapters/bidderFactory'; import {BANNER} from '../src/mediaTypes'; -import * as utils from '../src/utils'; +import { + triggerPixel, + getUniqueIdentifierStr, + contains, + deepAccess, + isArray, + getParameterByName +} from '../src/utils'; import {config} from '../src/config'; +import * as url from '../src/url'; +import {getCookie} from './pubCommonId'; export const BIDDER_CODE = 'emoteev'; -export const AK_PBJS_VERSION = '1.35.0'; -export const EMOTEEV_BASE_URL = 'https://prebid.emoteev.io'; -export const EMOTEEV_BASE_URL_STAGING = 'https://prebid-staging.emoteev.io'; -export const EMOTEEV_BASE_URL_DEVELOPMENT = 'http://localhost:3000'; +/** + * Version number of the adapter API. + */ +export const ADAPTER_VERSION = '1.35.0'; + +export const DOMAIN = 'prebid.emoteev.io'; +export const DOMAIN_STAGING = 'prebid-staging.emoteev.io'; +export const DOMAIN_DEVELOPMENT = 'localhost:3000'; -export const ENDPOINT_PATH = '/api/prebid/bid'; -export const USER_SYNC_IFRAME_URL_PATH = '/api/prebid/sync-iframe'; -export const USER_SYNC_IMAGE_URL_PATH = '/api/prebid/sync-image'; +/** + * Path of Emoteev endpoint for events. + */ +export const EVENTS_PATH = '/api/ad_event.json'; + +/** + * Path of Emoteev bidder. + */ +export const BIDDER_PATH = '/api/prebid/bid'; +export const USER_SYNC_IFRAME_PATH = '/api/prebid/sync-iframe'; +export const USER_SYNC_IMAGE_PATH = '/api/prebid/sync-image'; export const PRODUCTION = 'production'; export const STAGING = 'staging'; export const DEVELOPMENT = 'development'; export const DEFAULT_ENV = PRODUCTION; -export const conformBidRequest = bidRequest => { +export const ON_ADAPTER_CALLED = 'on_adapter_called'; +export const ON_BID_WON = 'on_bid_won'; +export const ON_BIDDER_TIMEOUT = 'on_bidder_timeout'; + +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#valid-build-requests-array for detailed semantic. + * + * @param {AdUnit.bidRequest} bidRequest + * @returns {boolean} Is this bidRequest valid? + */ +export const isBidRequestValid = (bidRequest) => { + return !!( + bidRequest && + bidRequest.params && + deepAccess(bidRequest, 'params.adSpaceId') && + bidRequest.bidder === BIDDER_CODE && + validateSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'))); +}; + +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#serverrequest-objects for detailed semantic. + * + * @param {string} env Emoteev environment parameter + * @param {boolean} debug Pbjs debug parameter. + * @param {string} currency See http://prebid.org/dev-docs/modules/currency.html for detailed semantic. + * @param {Array} validBidRequests Takes an array of bid requests, which are guaranteed to have passed the isBidRequestValid() test. + * @param bidderRequest General context for a bidder request being constructed + * @returns {ServerRequest} + */ +export const buildRequests = (env, debug, currency, validBidRequests, bidderRequest) => { return { - params: bidRequest.params, - crumbs: bidRequest.crumbs, - sizes: bidRequest.sizes, - bidId: bidRequest.bidId, - bidderRequestId: bidRequest.bidderRequestId, + method: 'POST', + url: bidderUrl(env), + data: JSON.stringify(requestsPayload(debug, currency, validBidRequests, bidderRequest)) }; }; -export const emoteevDebug = (parameterDebug, configDebug) => { - if (parameterDebug && parameterDebug.length && parameterDebug.length > 0) return JSON.parse(parameterDebug); - else if (configDebug) return configDebug; - else return false; -}; +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#interpreting-the-response for detailed semantic. + * + * @param {Array} serverResponse.body The body of the server response is an array of bid objects. + * @returns {Array} + */ +export const interpretResponse = (serverResponse) => serverResponse.body; -export const emoteevEnv = (parameteremoteevEnv, configemoteevEnv) => { - if (utils.contains([PRODUCTION, STAGING, DEVELOPMENT], parameteremoteevEnv)) return parameteremoteevEnv; - else if (utils.contains([PRODUCTION, STAGING, DEVELOPMENT], configemoteevEnv)) return configemoteevEnv; - else return DEFAULT_ENV; +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-set-targeting for detailed semantic. + * + * @param {string} env Emoteev environment parameter. + * @param {BidRequest} bidRequest + * @returns {UrlObject} + */ +export function onAdapterCalled(env, bidRequest) { + return { + protocol: 'https', + hostname: domain(env), + pathname: EVENTS_PATH, + search: { + eventName: ON_ADAPTER_CALLED, + pubcId: deepAccess(bidRequest, 'crumbs.pubcid'), + bidId: bidRequest.bidId, + adSpaceId: deepAccess(bidRequest, 'params.adSpaceId'), + cache_buster: getUniqueIdentifierStr() + } + }; +} + +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-bid-won for detailed semantic. + * + * @param {string} env Emoteev environment parameter. + * @param {string} pubcId Publisher common id. See http://prebid.org/dev-docs/modules/pubCommonId.html for detailed semantic. + * @param bidObject + * @returns {UrlObject} + */ +export const onBidWon = (env, pubcId, bidObject) => { + const bidId = bidObject.requestId; + return { + protocol: 'https', + hostname: domain(env), + pathname: EVENTS_PATH, + search: { + eventName: ON_BID_WON, + pubcId, + bidId, + cache_buster: getUniqueIdentifierStr() + } + }; }; -export const emoteevOverrides = (parameteremoteevOverrides, configemoteevOverrides) => { - if (parameteremoteevOverrides && parameteremoteevOverrides.length !== 0) { - let parsedParams = null; - try { - parsedParams = JSON.parse(parameteremoteevOverrides); - } catch (error) { - parsedParams = null; +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-timeout for detailed semantic. + * + * @param {string} env Emoteev environment parameter. + * @param {BidRequest} bidRequest + * @returns {UrlObject} + */ +export const onTimeout = (env, bidRequest) => { + return { + protocol: 'https', + hostname: domain(env), + pathname: EVENTS_PATH, + search: { + eventName: ON_BIDDER_TIMEOUT, + pubcId: deepAccess(bidRequest, 'crumbs.pubcid'), + bidId: bidRequest.bidId, + adSpaceId: deepAccess(bidRequest, 'params.adSpaceId'), + timeout: bidRequest.timeout, + cache_buster: getUniqueIdentifierStr() } - if (parsedParams) return parsedParams; } - if (configemoteevOverrides && Object.keys(configemoteevOverrides).length !== 0) return configemoteevOverrides; - else return {}; }; -export const akUrl = (environment) => { - switch (environment) { +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-user-syncs for detailed semantic. + * + * @param {string} env Emoteev environment parameter + * @param {SyncOptions} syncOptions + * @returns userSyncs + */ +export const getUserSyncs = (env, syncOptions) => { + let syncs = []; + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: userSyncImageUrl(env), + }); + } + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: userSyncIframeUrl(env), + }); + } + return syncs; +}; + +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The domain for network calls to Emoteev. + */ +export const domain = (env) => { + switch (env) { case DEVELOPMENT: - return EMOTEEV_BASE_URL_DEVELOPMENT; + return DOMAIN_DEVELOPMENT; case STAGING: - return EMOTEEV_BASE_URL_STAGING; + return DOMAIN_STAGING; default: - return EMOTEEV_BASE_URL; + return DOMAIN; } }; -export const endpointUrl = (parameteremoteevEnv, configemoteevEnv) => akUrl(emoteevEnv(parameteremoteevEnv, configemoteevEnv)).concat(ENDPOINT_PATH); -export const userSyncIframeUrl = (parameteremoteevEnv, configemoteevEnv) => akUrl(emoteevEnv(parameteremoteevEnv, configemoteevEnv)).concat(USER_SYNC_IFRAME_URL_PATH); -export const userSyncImageUrl = (parameteremoteevEnv, configemoteevEnv) => akUrl(emoteevEnv(parameteremoteevEnv, configemoteevEnv)).concat(USER_SYNC_IMAGE_URL_PATH); +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL which events is sent to. + */ +export const eventsUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: EVENTS_PATH +}); -export const getViewDimensions = () => { +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL which bidderRequest is sent to. + */ +export const bidderUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: BIDDER_PATH +}); + +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL called for iframe-based user sync + */ +export const userSyncIframeUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: USER_SYNC_IFRAME_PATH +}); + +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL called for image-based user sync + */ +export const userSyncImageUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: USER_SYNC_IMAGE_PATH +}); + +/** + * Pure function. + * + * @param {Array>} sizes + * @returns {boolean} are sizes valid? + */ +const validateSizes = sizes => isArray(sizes) && sizes.some(size => isArray(size) && size.length === 2); + +/** + * Pure function. + * + * @param {BidRequest} bidRequest + * @returns {object} An object which represents a BidRequest for Emoteev server side. + */ +export const conformBidRequest = bidRequest => { + return { + params: bidRequest.params, + crumbs: bidRequest.crumbs, + sizes: bidRequest.sizes, + bidId: bidRequest.bidId, + bidderRequestId: bidRequest.bidderRequestId, + }; +}; + +/** + * Pure function. + * + * @param {boolean} debug Pbjs debug parameter + * @param {string} currency See http://prebid.org/dev-docs/modules/currency.html for detailed information + * @param {BidRequest} validBidRequests + * @param {object} bidderRequest + * @returns + */ +export const requestsPayload = (debug, currency, validBidRequests, bidderRequest) => { + return { + akPbjsVersion: ADAPTER_VERSION, + bidRequests: validBidRequests.map(conformBidRequest), + currency: currency, + debug: debug, + language: navigator.language, + refererInfo: bidderRequest.refererInfo, + deviceInfo: getDeviceInfo( + getDeviceDimensions(window), + getViewDimensions(window, document), + getDocumentDimensions(document), + isWebGLEnabled(document)), + userAgent: navigator.userAgent, + gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies'), + gdprConsent: deepAccess(bidderRequest, 'gdprConsent.consentString'), + }; +}; + +/** + * Pure function + * @param {Window} window + * @param {Document} document + * @returns {{width: number, height: number}} View dimensions + */ +export const getViewDimensions = (window, document) => { let w = window; let prefix = 'inner'; @@ -85,14 +331,24 @@ export const getViewDimensions = () => { }; }; -export const getDeviceDimensions = () => { +/** + * Pure function + * @param {Window} window + * @returns {{width: number, height: number}} Device dimensions + */ +export const getDeviceDimensions = (window) => { return { width: window.screen ? window.screen.width : '', height: window.screen ? window.screen.height : '', }; }; -export const getDocumentDimensions = () => { +/** + * Pure function + * @param {Document} document + * @returns {{width: number, height: number}} Document dimensions + */ +export const getDocumentDimensions = (document) => { const de = document.documentElement; const be = document.body; @@ -112,7 +368,12 @@ export const getDocumentDimensions = () => { }; }; -export const isWebGLEnabled = () => { +/** + * Unpure function + * @param {Document} document + * @returns {boolean} Is WebGL enabled? + */ +export const isWebGLEnabled = (document) => { // Create test canvas let canvas = document.createElement('canvas'); @@ -141,6 +402,14 @@ export const isWebGLEnabled = () => { return !!gl; }; +/** + * Pure function + * @param {{width: number, height: number}} deviceDimensions + * @param {{width: number, height: number}} viewDimensions + * @param {{width: number, height: number}} documentDimensions + * @param {boolean} webGL + * @returns {object} Device information + */ export const getDeviceInfo = (deviceDimensions, viewDimensions, documentDimensions, webGL) => { return { browserWidth: viewDimensions.width, @@ -153,62 +422,62 @@ export const getDeviceInfo = (deviceDimensions, viewDimensions, documentDimensio }; }; -const validateSizes = sizes => utils.isArray(sizes) && sizes.some(size => utils.isArray(size) && size.length === 2); +/** + * Pure function + * @param {object} config pbjs config value + * @param {string} parameter Environment override from URL query param. + * @returns One of [PRODUCTION, STAGING, DEVELOPMENT]. + */ +export const resolveEnv = (config, parameter) => { + const configEnv = deepAccess(config, 'emoteev.env'); + + if (contains([PRODUCTION, STAGING, DEVELOPMENT], parameter)) return parameter; + else if (contains([PRODUCTION, STAGING, DEVELOPMENT], configEnv)) return configEnv; + else return DEFAULT_ENV; +}; + +/** + * Pure function + * @param {object} config pbjs config value + * @param {string} parameter Debug override from URL query param. + * @returns {boolean} + */ +export const resolveDebug = (config, parameter) => { + if (parameter && parameter.length && parameter.length > 0) return JSON.parse(parameter); + else if (config.debug) return config.debug; + else return false; +}; +/** + * EmoteevBidAdapter spec + * @access public + * @type {BidderSpec} + */ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], - - isBidRequestValid: (bid) => { - return !!( - bid && - bid.params && - bid.params.adSpaceId && - bid.bidder === BIDDER_CODE && - validateSizes(bid.mediaTypes.banner.sizes) - ); - }, - - buildRequests: (validBidRequests, bidderRequest) => { - const payload = Object.assign({}, - { - akPbjsVersion: AK_PBJS_VERSION, - bidRequests: validBidRequests.map(conformBidRequest), - currency: config.getConfig('currency'), - debug: emoteevDebug(utils.getParameterByName('emoteevDebug'), config.getConfig('emoteev.debug')), - language: navigator.language, - refererInfo: bidderRequest.refererInfo, - deviceInfo: getDeviceInfo(getDeviceDimensions(), getViewDimensions(), getDocumentDimensions(), isWebGLEnabled()), - userAgent: navigator.userAgent, - }, - emoteevOverrides(utils.getParameterByName('emoteevOverrides'), config.getConfig('emoteev.overrides'))); - - return { - method: 'POST', - url: endpointUrl(utils.getParameterByName('emoteevEnv'), config.getConfig('emoteev.env')), - data: JSON.stringify(payload), - }; - }, - - interpretResponse: (serverResponse) => serverResponse.body, - - getUserSyncs: (syncOptions, serverResponses) => { - const parameteremoteevEnv = utils.getParameterByName('emoteev.env'); - const configemoteevEnv = config.getConfig('emoteev.env'); - const syncs = []; - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: userSyncIframeUrl(parameteremoteevEnv, configemoteevEnv), - }); - } - if (syncOptions.pixelEnabled && serverResponses.length > 0) { - syncs.push({ - type: 'image', - url: userSyncImageUrl(parameteremoteevEnv, configemoteevEnv), - }); - } - return syncs; - }, + isBidRequestValid: isBidRequestValid, + buildRequests: (validBidRequests, bidderRequest) => + buildRequests( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + resolveDebug(config.getConfig(), getParameterByName('debug')), + config.getConfig('currency'), + validBidRequests, + bidderRequest), + interpretResponse: interpretResponse, + onBidWon: (bidObject) => + triggerPixel(url.format(onBidWon( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + getCookie('_pubcid'), + bidObject))), + onTimeout: (bidRequest) => + triggerPixel(url.format(onTimeout( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + bidRequest))), + getUserSyncs: (syncOptions) => + getUserSyncs( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + syncOptions), }; + registerBidder(spec); diff --git a/modules/emokteevBidAdapter.md b/modules/emoteevBidAdapter.md similarity index 100% rename from modules/emokteevBidAdapter.md rename to modules/emoteevBidAdapter.md diff --git a/test/spec/modules/emoteevBidAdapter_spec.js b/test/spec/modules/emoteevBidAdapter_spec.js index a5f5c439e6f..a5460ab939d 100644 --- a/test/spec/modules/emoteevBidAdapter_spec.js +++ b/test/spec/modules/emoteevBidAdapter_spec.js @@ -1,26 +1,49 @@ -import {expect} from 'chai'; import { - AK_PBJS_VERSION, - EMOTEEV_BASE_URL, - EMOTEEV_BASE_URL_STAGING, - emoteevDebug, - emoteevEnv, - emoteevOverrides, - akUrl, + assert, expect +} from 'chai'; +import { + ADAPTER_VERSION, + DOMAIN, + DOMAIN_DEVELOPMENT, + DOMAIN_STAGING, + domain, + BIDDER_PATH, + bidderUrl, + buildRequests, conformBidRequest, DEFAULT_ENV, - ENDPOINT_PATH, - endpointUrl, + DEVELOPMENT, + EVENTS_PATH, + eventsUrl, + getDeviceDimensions, + getDeviceInfo, + getDocumentDimensions, + getUserSyncs, + getViewDimensions, + interpretResponse, + isBidRequestValid, + isWebGLEnabled, + ON_ADAPTER_CALLED, + ON_BID_WON, + ON_BIDDER_TIMEOUT, + onBidWon, + onAdapterCalled, + onTimeout, PRODUCTION, + requestsPayload, + resolveDebug, + resolveEnv, spec, STAGING, - USER_SYNC_IFRAME_URL_PATH, - USER_SYNC_IMAGE_URL_PATH, + USER_SYNC_IFRAME_PATH, + USER_SYNC_IMAGE_PATH, userSyncIframeUrl, userSyncImageUrl, } from 'modules/emoteevBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; -import {config} from 'src/config'; +import * as url from '../../../src/url'; +import * as utils from '../../../src/utils'; +import * as pubCommonId from '../../../modules/pubCommonId'; +import {config} from '../../../src/config'; const cannedValidBidRequests = [{ adUnitCode: '/19968336/header-bid-tag-1', @@ -48,7 +71,11 @@ const cannedBidderRequest = { stack: ['http://localhost:9999/integrationExamples/gpt/hello_world_emoteev.html'] }, start: 1544200012839, - timeout: 3000 + timeout: 3000, + gdprConsent: { + gdprApplies: true, + consentString: 'my consentString' + } }; const serverResponse = { @@ -68,106 +95,11 @@ const serverResponse = }; describe('emoteevBidAdapter', function () { - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('conformBidRequest', function () { - it('returns a bid-request', function () { - expect(conformBidRequest(cannedValidBidRequests[0])).to.deep.equal({ - params: cannedValidBidRequests[0].params, - crumbs: cannedValidBidRequests[0].crumbs, - sizes: cannedValidBidRequests[0].sizes, - bidId: cannedValidBidRequests[0].bidId, - bidderRequestId: cannedValidBidRequests[0].bidderRequestId, - }); - }) - }); - - describe('emoteevDebug', function () { - expect(emoteevDebug(null, null)).to.deep.equal(false) - }); - describe('emoteevDebug', function () { - expect(emoteevDebug(null, true)).to.deep.equal(true) - }); - describe('emoteevDebug', function () { - expect(emoteevDebug(JSON.stringify(true), null)).to.deep.equal(true) - }); - - describe('emoteevEnv', function () { - expect(emoteevEnv(null, null)).to.deep.equal(DEFAULT_ENV) - }); - describe('emoteevEnv', function () { - expect(emoteevEnv(null, STAGING)).to.deep.equal(STAGING) - }); - describe('emoteevEnv', function () { - expect(emoteevEnv(STAGING, null)).to.deep.equal(STAGING) - }); - - describe('emoteevOverrides', function () { - expect(emoteevOverrides(null, null)).to.deep.equal({}) - }); - describe('emoteevOverrides', function () { - expect(emoteevOverrides(JSON.stringify({a: 1}), null)).to.deep.equal({a: 1}) - }); - describe('emoteevOverrides', function () { - expect(emoteevOverrides('incorrect', null)).to.deep.equal({}) - }); // expect no exception - describe('emoteevOverrides', function () { - expect(emoteevOverrides(null, {a: 1})).to.deep.equal({a: 1}) - }); - - describe('akUrl', function () { - expect(akUrl(null)).to.deep.equal(EMOTEEV_BASE_URL) - }); - describe('akUrl', function () { - expect(akUrl('anything')).to.deep.equal(EMOTEEV_BASE_URL) - }); - describe('akUrl', function () { - expect(akUrl(STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING) - }); - describe('akUrl', function () { - expect(akUrl('production')).to.deep.equal(EMOTEEV_BASE_URL) - }); - - describe('endpointUrl', function () { - expect(endpointUrl(null, null)).to.deep.equal(EMOTEEV_BASE_URL.concat(ENDPOINT_PATH)) - }); - describe('endpointUrl', function () { - expect(endpointUrl(null, STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(ENDPOINT_PATH)) - }); - describe('endpointUrl', function () { - expect(endpointUrl(STAGING, null)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(ENDPOINT_PATH)) - }); - - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(null, null)).to.deep.equal(EMOTEEV_BASE_URL.concat(USER_SYNC_IFRAME_URL_PATH)) - }); - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(null, STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IFRAME_URL_PATH)) - }); - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(STAGING, null)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IFRAME_URL_PATH)) - }); - - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(null, null)).to.deep.equal(EMOTEEV_BASE_URL.concat(USER_SYNC_IMAGE_URL_PATH)) - }); - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(null, STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IMAGE_URL_PATH)) - }); - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(STAGING, null)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IMAGE_URL_PATH)) - }); - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { + it('should return true when valid', function () { const validBid = { bidder: 'emoteev', + bidId: '23a45b4e3', params: { adSpaceId: 12345, }, @@ -177,11 +109,14 @@ describe('emoteevBidAdapter', function () { } }, }; - expect(spec.isBidRequestValid(validBid)).to.equal(true); + expect(isBidRequestValid(validBid)).to.equal(true); + + expect(spec.isBidRequestValid(validBid)).to.exist.and.to.be.a('boolean'); + expect(spec.isBidRequestValid({})).to.exist.and.to.be.a('boolean'); }); it('should return false when required params are invalid', function () { - expect(spec.isBidRequestValid({ + expect(isBidRequestValid({ bidder: '', // invalid bidder params: { adSpaceId: 12345, @@ -192,7 +127,7 @@ describe('emoteevBidAdapter', function () { } }, })).to.equal(false); - expect(spec.isBidRequestValid({ + expect(isBidRequestValid({ bidder: 'emoteev', params: { adSpaceId: '', // invalid adSpaceId @@ -203,7 +138,7 @@ describe('emoteevBidAdapter', function () { } }, })).to.equal(false); - expect(spec.isBidRequestValid({ + expect(isBidRequestValid({ bidder: 'emoteev', params: { adSpaceId: 12345, @@ -219,131 +154,602 @@ describe('emoteevBidAdapter', function () { describe('buildRequests', function () { const + env = DEFAULT_ENV, + debug = true, currency = 'EUR', - emoteevEnv = STAGING, - emoteevDebug = true, - emoteevOverrides = { - iAmOverride: 'iAmOverride' - }; - config.setConfig({ // asynchronous - currency, - emoteev: { - env: STAGING, - debug: emoteevDebug, - overrides: emoteevOverrides - } - }); + request = buildRequests(env, debug, currency, cannedValidBidRequests, cannedBidderRequest); - config.getConfig('emoteev', function () { - const request = spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); - - it('creates a request object with correct method, url and data', function () { - expect(request).to.exist.and.have.all.keys( - 'method', - 'url', - 'data', - ); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(endpointUrl(emoteevEnv, emoteevEnv)); - - let requestData = JSON.parse(request.data); - expect(requestData).to.exist.and.have.all.keys( - 'akPbjsVersion', - 'bidRequests', - 'currency', - 'debug', - 'iAmOverride', - 'language', - 'refererInfo', - 'deviceInfo', - 'userAgent', - ); - - expect(requestData.bidRequests[0]).to.exist.and.have.all.keys( - 'params', - 'crumbs', - 'sizes', - 'bidId', - 'bidderRequestId', - ); - - expect(requestData.akPbjsVersion).to.deep.equal(AK_PBJS_VERSION); - expect(requestData.bidRequests[0].params).to.deep.equal(cannedValidBidRequests[0].params); - expect(requestData.bidRequests[0].crumbs).to.deep.equal(cannedValidBidRequests[0].crumbs); - expect(requestData.bidRequests[0].mediaTypes).to.deep.equal(cannedValidBidRequests[0].mediaTypes); - expect(requestData.bidRequests[0].bidId).to.deep.equal(cannedValidBidRequests[0].bidId); - expect(requestData.bidRequests[0].bidderRequestId).to.deep.equal(cannedValidBidRequests[0].bidderRequestId); - expect(requestData.currency).to.deep.equal(currency); - expect(requestData.debug).to.deep.equal(emoteevDebug); - expect(requestData.iAmOverride).to.deep.equal('iAmOverride'); - expect(requestData.language).to.deep.equal(navigator.language); - expect(requestData.deviceInfo).to.exist.and.have.all.keys( - 'browserWidth', - 'browserHeight', - 'deviceWidth', - 'deviceHeight', - 'documentWidth', - 'documentHeight', - 'webGL', - ); - expect(requestData.userAgent).to.deep.equal(navigator.userAgent); - }); - }); + expect(request).to.exist.and.have.all.keys( + 'method', + 'url', + 'data', + ); + + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(bidderUrl(env)); + + expect(spec.buildRequests(cannedValidBidRequests, cannedBidderRequest)).to.exist.and.to.be.an('object'); }); describe('interpretResponse', function () { it('bid objects from response', function () { - const bidResponses = spec.interpretResponse(serverResponse); - expect(bidResponses).to.be.an('array').that.is.not.empty; // yes, syntax is correct - expect(bidResponses[0]).to.have.all.keys( - 'requestId', - 'cpm', - 'width', - 'height', - 'ad', - 'ttl', - 'creativeId', - 'netRevenue', - 'currency', - ); - - expect(bidResponses[0].requestId).to.equal(cannedValidBidRequests[0].bidId); - expect(bidResponses[0].cpm).to.equal(serverResponse.body[0].cpm); - expect(bidResponses[0].width).to.equal(serverResponse.body[0].width); - expect(bidResponses[0].height).to.equal(serverResponse.body[0].height); - expect(bidResponses[0].ad).to.equal(serverResponse.body[0].ad); - expect(bidResponses[0].ttl).to.equal(serverResponse.body[0].ttl); - expect(bidResponses[0].creativeId).to.equal(serverResponse.body[0].creativeId); - expect(bidResponses[0].netRevenue).to.equal(serverResponse.body[0].netRevenue); - expect(bidResponses[0].currency).to.equal(serverResponse.body[0].currency); + const bidResponses = interpretResponse(serverResponse); + expect(bidResponses).to.be.an('array').that.is.not.empty; + expect(bidResponses[0]).to.have.property('requestId', cannedValidBidRequests[0].bidId); + expect(bidResponses[0]).to.have.property('cpm', serverResponse.body[0].cpm); + expect(bidResponses[0]).to.have.property('width', serverResponse.body[0].width); + expect(bidResponses[0]).to.have.property('height', serverResponse.body[0].height); + expect(bidResponses[0]).to.have.property('ad', serverResponse.body[0].ad); + expect(bidResponses[0]).to.have.property('ttl', serverResponse.body[0].ttl); + expect(bidResponses[0]).to.have.property('creativeId', serverResponse.body[0].creativeId); + expect(bidResponses[0]).to.have.property('netRevenue', serverResponse.body[0].netRevenue); + expect(bidResponses[0]).to.have.property('currency', serverResponse.body[0].currency); }); }); - describe('getUserSyncs', function () { - config.setConfig({emoteevEnv: PRODUCTION}); - expect(spec.getUserSyncs({ - iframeEnabled: true - }, [{}])).to.deep.equal([{ - type: 'iframe', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IFRAME_URL_PATH) - }]); + describe('onAdapterCalled', function () { + const + bidRequest = cannedValidBidRequests[0], + url = onAdapterCalled(DEFAULT_ENV, bidRequest); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_ADAPTER_CALLED); + expect(url).to.have.nested.property('search.pubcId', bidRequest.crumbs.pubcid); + expect(url).to.have.nested.property('search.bidId', bidRequest.bidId); + expect(url).to.have.nested.property('search.adSpaceId', bidRequest.params.adSpaceId); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('onBidWon', function () { + const + pubcId = cannedValidBidRequests[0].crumbs.pubcid, + bidObject = serverResponse.body[0], + url = onBidWon(DEFAULT_ENV, pubcId, bidObject); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_BID_WON); + expect(url).to.have.nested.property('search.pubcId', pubcId); + expect(url).to.have.nested.property('search.bidId', bidObject.requestId); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('onTimeout', function () { + const + data = { + ...cannedValidBidRequests[0], + timeout: 123, + }, + url = onTimeout(DEFAULT_ENV, data); - expect(spec.getUserSyncs({ - pixelEnabled: true - }, [{}])).to.deep.equal([{ + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_BIDDER_TIMEOUT); + expect(url).to.have.nested.property('search.bidId', data.bidId); + expect(url).to.have.nested.property('search.pubcId', data.crumbs.pubcid); + expect(url).to.have.nested.property('search.adSpaceId', data.params.adSpaceId); + expect(url).to.have.nested.property('search.timeout', data.timeout); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('getUserSyncs', function () { + expect(getUserSyncs( + DEFAULT_ENV, + { + iframeEnabled: false, + pixelEnabled: false + })).to.deep.equal([]); + expect(getUserSyncs( + PRODUCTION, + { + iframeEnabled: false, + pixelEnabled: true + })).to.deep.equal([{ type: 'image', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IMAGE_URL_PATH) + url: userSyncImageUrl(PRODUCTION) }]); - - expect(spec.getUserSyncs({ - iframeEnabled: true, - pixelEnabled: true - }, [{}])).to.deep.equal([{ + expect(getUserSyncs( + STAGING, + { + iframeEnabled: true, + pixelEnabled: false + })).to.deep.equal([{ type: 'iframe', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IFRAME_URL_PATH) - }, { + url: userSyncIframeUrl(STAGING) + }]); + expect(getUserSyncs( + DEVELOPMENT, + { + iframeEnabled: true, + pixelEnabled: true + })).to.deep.equal([{ type: 'image', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IMAGE_URL_PATH) + url: userSyncImageUrl(DEVELOPMENT) + }, { + type: 'iframe', + url: userSyncIframeUrl(DEVELOPMENT) }]); }); + + describe('domain', function () { + expect(domain(null)).to.deep.equal(DOMAIN); + expect(domain('anything')).to.deep.equal(DOMAIN); + expect(domain(PRODUCTION)).to.deep.equal(DOMAIN); + expect(domain(STAGING)).to.deep.equal(DOMAIN_STAGING); + expect(domain(DEVELOPMENT)).to.deep.equal(DOMAIN_DEVELOPMENT); + }); + + describe('eventsUrl', function () { + expect(eventsUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: EVENTS_PATH + })); + expect(eventsUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: EVENTS_PATH + })); + expect(eventsUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: EVENTS_PATH + })); + expect(eventsUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: EVENTS_PATH + })); + expect(eventsUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: EVENTS_PATH + })); + }); + + describe('bidderUrl', function () { + expect(bidderUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: BIDDER_PATH + })); + expect(bidderUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: BIDDER_PATH + })); + expect(bidderUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: BIDDER_PATH + })); + expect(bidderUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: BIDDER_PATH + })); + expect(bidderUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: BIDDER_PATH + })); + }); + + describe('userSyncIframeUrl', function () { + expect(userSyncIframeUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: USER_SYNC_IFRAME_PATH + })); + }); + + describe('userSyncImageUrl', function () { + expect(userSyncImageUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: USER_SYNC_IMAGE_PATH + })); + }); + + describe('conformBidRequest', function () { + expect(conformBidRequest(cannedValidBidRequests[0])).to.deep.equal({ + params: cannedValidBidRequests[0].params, + crumbs: cannedValidBidRequests[0].crumbs, + sizes: cannedValidBidRequests[0].sizes, + bidId: cannedValidBidRequests[0].bidId, + bidderRequestId: cannedValidBidRequests[0].bidderRequestId, + }); + }); + + describe('requestsPayload', function () { + const + currency = 'EUR', + debug = true; + + const payload = requestsPayload(debug, currency, cannedValidBidRequests, cannedBidderRequest); + + expect(payload).to.exist.and.have.all.keys( + 'akPbjsVersion', + 'bidRequests', + 'currency', + 'debug', + 'language', + 'refererInfo', + 'deviceInfo', + 'userAgent', + 'gdprApplies', + 'gdprConsent' + ); + + expect(payload.bidRequests[0]).to.exist.and.have.all.keys( + 'params', + 'crumbs', + 'sizes', + 'bidId', + 'bidderRequestId', + ); + + expect(payload.akPbjsVersion).to.deep.equal(ADAPTER_VERSION); + expect(payload.bidRequests[0].params).to.deep.equal(cannedValidBidRequests[0].params); + expect(payload.bidRequests[0].crumbs).to.deep.equal(cannedValidBidRequests[0].crumbs); + expect(payload.bidRequests[0].mediaTypes).to.deep.equal(cannedValidBidRequests[0].mediaTypes); + expect(payload.bidRequests[0].bidId).to.deep.equal(cannedValidBidRequests[0].bidId); + expect(payload.bidRequests[0].bidderRequestId).to.deep.equal(cannedValidBidRequests[0].bidderRequestId); + expect(payload.currency).to.deep.equal(currency); + expect(payload.debug).to.deep.equal(debug); + expect(payload.language).to.deep.equal(navigator.language); + expect(payload.deviceInfo).to.exist.and.have.all.keys( + 'browserWidth', + 'browserHeight', + 'deviceWidth', + 'deviceHeight', + 'documentWidth', + 'documentHeight', + 'webGL', + ); + expect(payload.userAgent).to.deep.equal(navigator.userAgent); + expect(payload.gdprApplies).to.deep.equal(cannedBidderRequest.gdprConsent.gdprApplies); + expect(payload.gdprConsent).to.deep.equal(cannedBidderRequest.gdprConsent.consentString); + }); + + describe('getViewDimensions', function () { + const window = { + innerWidth: 1024, + innerHeight: 768 + }; + const documentWithElement = { + documentElement: + { + clientWidth: 512, + clientHeight: 384 + } + }; + const documentWithBody = { + body: + { + clientWidth: 512, + clientHeight: 384 + } + }; + expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ + width: 1024, + height: 768 + }); + expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); + expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ + width: 1024, + height: 768 + }); + expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); + expect(getViewDimensions({}, documentWithElement)).to.deep.equal({width: 512, height: 384}); + expect(getViewDimensions({}, documentWithBody)).to.deep.equal({width: 512, height: 384}); + }); + + describe('getDeviceDimensions', function () { + const window = {screen: {width: 1024, height: 768}}; + expect(getDeviceDimensions(window)).to.deep.equal({width: 1024, height: 768}); + expect(getDeviceDimensions({})).to.deep.equal({width: '', height: ''}); + }); + + describe('getDocumentDimensions', function () { + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 1, + clientHeight: 1, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 0, + scrollHeight: 0, + }, + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 1, + clientHeight: 1, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 0, + scrollHeight: 0, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 0, + clientHeight: 0, + offsetWidth: 1, + offsetHeight: 1, + scrollWidth: 0, + scrollHeight: 0, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 0, + clientHeight: 0, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 1, + scrollHeight: 1, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: undefined, + clientHeight: undefined, + offsetWidth: undefined, + offsetHeight: undefined, + scrollWidth: undefined, + scrollHeight: undefined, + }, + body: { + scrollHeight: undefined, + offsetHeight: undefined, + } + })).to.deep.equal({width: '', height: ''}); + }); + + // describe('isWebGLEnabled', function () { + // it('handles no webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').returns(undefined); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles webgl exception', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').throws(DOMException); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles experimental webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').returns(true); + // expect(isWebGLEnabled(document)).to.equal(true); + // }); + // + // it('handles experimental webgl exception', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').throws(DOMException); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(true); + // expect(isWebGLEnabled(document)).to.equal(true); + // }); + // }); + + describe('getDeviceInfo', function () { + expect(getDeviceInfo( + {width: 1, height: 2}, + {width: 3, height: 4}, + {width: 5, height: 6}, + true + )).to.deep.equal({ + deviceWidth: 1, + deviceHeight: 2, + browserWidth: 3, + browserHeight: 4, + documentWidth: 5, + documentHeight: 6, + webGL: true + }); + }); + + describe('resolveEnv', function () { + it('defaults to production', function () { + expect(resolveEnv({}, null)).to.deep.equal(DEFAULT_ENV); + }); + expect(resolveEnv({}, PRODUCTION)).to.deep.equal(PRODUCTION); + expect(resolveEnv({}, STAGING)).to.deep.equal(STAGING); + expect(resolveEnv({}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); + expect(resolveEnv({emoteev: {env: PRODUCTION}}, null)).to.deep.equal(PRODUCTION); + expect(resolveEnv({emoteev: {env: STAGING}}, null)).to.deep.equal(STAGING); + expect(resolveEnv({emoteev: {env: DEVELOPMENT}}, null)).to.deep.equal(DEVELOPMENT); + it('prioritizes parameter over configuration', function () { + expect(resolveEnv({emoteev: {env: STAGING}}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); + }); + }); + + describe('resolveDebug', function () { + it('defaults to production', function () { + expect(resolveDebug({}, null)).to.deep.equal(false); + }); + expect(resolveDebug({}, 'false')).to.deep.equal(false); + expect(resolveDebug({}, 'true')).to.deep.equal(true); + expect(resolveDebug({debug: true}, null)).to.deep.equal(true); + it('prioritizes parameter over configuration', function () { + expect(resolveDebug({debug: true}, 'false')).to.deep.equal(false); + }); + }); + + describe('side effects', function () { + let triggerPixelSpy; + let getCookieSpy; + let getConfigSpy; + let getParameterByNameSpy; + beforeEach(function () { + triggerPixelSpy = sinon.spy(utils, 'triggerPixel'); + getCookieSpy = sinon.spy(pubCommonId, 'getCookie'); + getConfigSpy = sinon.spy(config, 'getConfig'); + getParameterByNameSpy = sinon.spy(utils, 'getParameterByName'); + }); + afterEach(function () { + triggerPixelSpy.restore(); + getCookieSpy.restore(); + getConfigSpy.restore(); + getParameterByNameSpy.restore(); + }); + + describe('isBidRequestValid', function () { + it('has intended side-effects', function () { + const validBidRequest = { + bidder: 'emoteev', + bidId: '23a45b4e3', + params: { + adSpaceId: 12345, + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + }; + spec.isBidRequestValid(validBidRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + it('has intended side-effects', function () { + const invalidBidRequest = {}; + spec.isBidRequestValid(invalidBidRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('buildRequests', function () { + it('has intended side-effects', function () { + spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.callCount(config.getConfig, 3); + sinon.assert.callCount(utils.getParameterByName, 2); + }); + }); + describe('interpretResponse', function () { + it('has intended side-effects', function () { + spec.interpretResponse(serverResponse); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('onBidWon', function () { + it('has intended side-effects', function () { + const bidObject = serverResponse.body[0]; + spec.onBidWon(bidObject); + sinon.assert.calledOnce(utils.triggerPixel); + sinon.assert.calledOnce(pubCommonId.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + describe('onTimeout', function () { + it('has intended side-effects', function () { + spec.onTimeout(cannedValidBidRequests[0]); + sinon.assert.calledOnce(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + describe('getUserSyncs', function () { + it('has intended side-effects', function () { + spec.getUserSyncs({}); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + }); });