From 3d5760a4c785bd79945f37712a648886b89d632c Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Mon, 25 Nov 2019 19:15:01 +0530 Subject: [PATCH 01/41] implement size bucket filtration logic --- modules/sizeMapping-2.0.js | 178 +++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 modules/sizeMapping-2.0.js diff --git a/modules/sizeMapping-2.0.js b/modules/sizeMapping-2.0.js new file mode 100644 index 00000000000..a50765ca0b1 --- /dev/null +++ b/modules/sizeMapping-2.0.js @@ -0,0 +1,178 @@ +import { deepAccess, getDefinedParams, logWarn } from '../src/utils'; +import { processNativeAdUnitParams } from '../src/native'; +/** + * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active or not based on label checks on the Ad unit/tBid object + * @param {Object} bidOrAdUnit Either the Ad Unit object or the Bid object + * @param {Array} activeLabels List of active labels passed as an argument to pbjs.requestBids function + * @returns {boolean} Represents if the Ad Unit or the Bid is active or not + */ +function isLabelActivated(bidOrAdUnit, activeLabels) { + let labelOperator; + const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); + if (labelsFound && labelsFound.length > 1) { + const lastDeclaredOperator = labelsFound[labelsFound.length - 1]; + logWarn(`Ad Unit ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); + labelOperator = lastDeclaredOperator; + } else { + labelOperator = labelsFound[0]; + } + if (labelOperator === 'labelAll') { + if (bidOrAdUnit.labelAll.length === 0) { + logWarn(`Ad Unit ${bidOrAdUnit.code} has property labelAll with an empty array. Ad Unit is still enabled!`); + return true; + } + return bidOrAdUnit.labelAll.every(label => activeLabels.includes(label)); + } else if (labelOperator === 'labelAny') { + if (bidOrAdUnit.labelAny.length === 0) { + logWarn(`Ad Unit ${bidOrAdUnit.code} has property labelAny with an empty array. Ad Unit is still enabled!`); + return true; + } + return bidOrAdUnit.labelAny.some(label => activeLabels.includes(label)); + } + return true; +} + +/** + * Processes the MediaTypes object and calculates the active size buckets for each Media Type. Uses `window.innerWidth` and `window.innerHeight` + * to calculate the width and height of the active Viewport. + * @param {MediaTypes} mediaTypes Contains information about supported media types for an Ad Unit and size information for each of those types + * @returns {MediaTypes} Filtered mediaTypes object with relevant media types filterer by size buckets based on activeViewPort size + */ + +// WIP: Functionality of transforming the mediaTypes object is still pending +function getFilteredMediaTypes(mediaTypes) { + let + activeViewportWidth, + activeViewportHeight, + transformedMediaTypes; + + let activeSizeBucket = { + banner: undefined, + video: undefined, + native: undefined + } + + try { + activeViewportWidth = getWindowTop().innerWidth; + activeViewportHeight = getWindowTop().innerHeight; + } catch (e) { + logWarn('Unfriendly iFrame blocks Viewport size to be evaluated correctly'); + activeViewportWidth = window.innerWidth; + activeViewportHeight = window.innerHeight; + } + const activeViewport = [activeViewportWidth, activeViewportHeight]; + transformedMediaTypes = Object.keys(mediaTypes).map(mediaType => { + const sizeConfig = mediaTypes[mediaType].sizeConfig; + if (sizeConfig) { + activeSizeBucket[mediaType] = getActiveSizeBucket(sizeConfig, activeViewport); + const filteredSizeConfig = sizeConfig.filter(config => config.minViewPort === activeSizeBucket[mediaType] && isSizeConfigActivated(mediaType, config)); + mediaTypes[mediaType] = Object.assign({ filteredSizeConfig }, mediaTypes[mediaType]); + + // transform mediaTypes object + return getTransformedMediaTypes(mediaTypes, mediaType); + } + }).filter(transformedMediaType => { + console.log('transformedMediaType', transformedMediaType); + }); + return { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes }; +}; + +/** + * This function takes information out of the sizeConfig property of the mediaTypes object and replaces it with the suitable property + * for that media type. For example, 'sizes' in the case of 'banner' media type and 'playerSize' in the case of 'video' media type. + * @param {object} mediaTypes The mediaTypes object with the sizeConfig property + * @returns {object} The mediaTypes object in the expected format + */ + +// WIP +function getTransformedMediaTypes(mediaTypes, mediaType) { + const transformedMediaTypes = Object.assign({}, mediaTypes); + const config = { + banner: 'sizes', + video: 'playerSize' + }; + + if (transformedMediaTypes[mediaType].filteredSizeConfig.length > 0) { + if (mediaType !== 'native') { + transformedMediaTypes[mediaType][config[mediaType]] = transformedMediaTypes[mediaType].filteredSizeConfig[0][config[mediaType]]; + } + } + + return transformedMediaTypes; +} + +/** + * Evaluates the given sizeConfig object and checks for various properties to determine if the sizeConfig is active or not. For example, + * let's suppose the sizeConfig is for a Banner media type. Then, if the sizes property is found empty, it return false, else returns true. + * In case of a Video media type, it checks the playerSize property. If found empty, returns false, else returns true. + * In case of a Native media type, it checks the active property. If found false, returns false, if found true, returns true. + * @param {string} mediaType It can be 'banner', 'native' or 'video' + * @param {Object} sizeConfig Represents the sizeConfig object which is active based on the current viewport size + * @returns {boolean} Represents if the size config active or not + */ +function isSizeConfigActivated(mediaType, sizeConfig) { + switch (mediaType) { + case 'banner': + return sizeConfig.sizes && sizeConfig.sizes.length > 0; + case 'video': + return sizeConfig.playerSize && sizeConfig.playerSize.length > 0; + case 'native': + return sizeConfig.active; + default: + return false; + } +} + +/** + * Returns the active size bucket for a given media type + * @param {Array} sizeConfig SizeConfig defines the characteristics of an Ad Unit categorised into multiple size buckets per media type + * @param {Array} activeViewport Viewport size of the browser in the form [w, h] (w -> width, h -> height) + * Calculated at the time of making call to pbjs.requestBids function + * @returns {Array} The active size bucket matching the activeViewPort + */ +function getActiveSizeBucket(sizeConfig, activeViewport) { + let activeSizeBucket = []; + sizeConfig + .sort((a, b) => a.minViewPort[0] - b.minViewPort[0]) + .forEach(config => { + if (activeViewport[0] >= config.minViewPort[0]) { + if (activeViewport[1] >= config.minViewPort[1]) { + activeSizeBucket = config.minViewPort; + } else { + activeSizeBucket = []; + } + } + }) + return activeSizeBucket; +} + +// WIP +// function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { +// return adUnits.reduce((result, adUnit) => { +// if (isLabelActivated(adUnit)) { +// if (adUnit.mediaTypes) { +// const filteredMediaTypes = getFilteredMediaTypes(adUnit.mediaTypes); +// const transformMediaTypes = getTransformedMediaTypes(filteredMediaTypes); +// adUnit.mediaTypes = transformMediaTypes; +// result +// .push(adUnit.bids.filter(bid => bid.bidder === bidderCode)) +// .reduce((bids, bid) => { +// const nativeParams = adUnit.nativeParams || deepAccess(adUnit, 'mediaTypes.native'); +// if (nativeParams) { +// bid = Object.assign({}, bid, { +// nativeParams: processNativeAdUnitParams(nativeParams) +// }); +// } + +// bid = Object.assign({}, bid, getDefinedParams(adUnit, ['mediaType', 'renderer'])); + +// if (bid.sizeConfig) { + +// } +// }, []); +// } +// } else { +// logInfo(`Ad Unit ${adUnit.code} is disabled due to a failing label check.`); +// } +// }, []); +// } From 240cfce44ce7068a16589edc1b96ad470f891fc1 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Tue, 26 Nov 2019 20:54:44 +0530 Subject: [PATCH 02/41] finish implmenation of getFilteredMediaTypes --- modules/sizeMapping-2.0.js | 96 ++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/modules/sizeMapping-2.0.js b/modules/sizeMapping-2.0.js index a50765ca0b1..411ee5f82fc 100644 --- a/modules/sizeMapping-2.0.js +++ b/modules/sizeMapping-2.0.js @@ -1,7 +1,8 @@ -import { deepAccess, getDefinedParams, logWarn } from '../src/utils'; -import { processNativeAdUnitParams } from '../src/native'; +// import { deepAccess, getDefinedParams, console.log } from '../src/utils'; +// import { processNativeAdUnitParams } from '../src/native'; + /** - * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active or not based on label checks on the Ad unit/tBid object + * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active based on label checks on the Ad unit/Bid object * @param {Object} bidOrAdUnit Either the Ad Unit object or the Bid object * @param {Array} activeLabels List of active labels passed as an argument to pbjs.requestBids function * @returns {boolean} Represents if the Ad Unit or the Bid is active or not @@ -11,20 +12,20 @@ function isLabelActivated(bidOrAdUnit, activeLabels) { const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); if (labelsFound && labelsFound.length > 1) { const lastDeclaredOperator = labelsFound[labelsFound.length - 1]; - logWarn(`Ad Unit ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); + console.log(`Ad Unit ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); labelOperator = lastDeclaredOperator; } else { labelOperator = labelsFound[0]; } if (labelOperator === 'labelAll') { if (bidOrAdUnit.labelAll.length === 0) { - logWarn(`Ad Unit ${bidOrAdUnit.code} has property labelAll with an empty array. Ad Unit is still enabled!`); + console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAll with an empty array. Ad Unit is still enabled!`); return true; } return bidOrAdUnit.labelAll.every(label => activeLabels.includes(label)); } else if (labelOperator === 'labelAny') { if (bidOrAdUnit.labelAny.length === 0) { - logWarn(`Ad Unit ${bidOrAdUnit.code} has property labelAny with an empty array. Ad Unit is still enabled!`); + console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAny with an empty array. Ad Unit is still enabled!`); return true; } return bidOrAdUnit.labelAny.some(label => activeLabels.includes(label)); @@ -46,6 +47,8 @@ function getFilteredMediaTypes(mediaTypes) { activeViewportHeight, transformedMediaTypes; + transformedMediaTypes = Object.assign({}, mediaTypes); + let activeSizeBucket = { banner: undefined, video: undefined, @@ -53,54 +56,41 @@ function getFilteredMediaTypes(mediaTypes) { } try { - activeViewportWidth = getWindowTop().innerWidth; - activeViewportHeight = getWindowTop().innerHeight; + // activeViewportWidth = getWindowTop().innerWidth; + // activeViewportHeight = getWindowTop().innerHeight; + activeViewportWidth = 820; + activeViewportHeight = 300; } catch (e) { - logWarn('Unfriendly iFrame blocks Viewport size to be evaluated correctly'); + console.log('Unfriendly iFrame blocks Viewport size to be evaluated correctly'); activeViewportWidth = window.innerWidth; activeViewportHeight = window.innerHeight; } const activeViewport = [activeViewportWidth, activeViewportHeight]; - transformedMediaTypes = Object.keys(mediaTypes).map(mediaType => { + Object.keys(mediaTypes).map(mediaType => { const sizeConfig = mediaTypes[mediaType].sizeConfig; if (sizeConfig) { activeSizeBucket[mediaType] = getActiveSizeBucket(sizeConfig, activeViewport); const filteredSizeConfig = sizeConfig.filter(config => config.minViewPort === activeSizeBucket[mediaType] && isSizeConfigActivated(mediaType, config)); - mediaTypes[mediaType] = Object.assign({ filteredSizeConfig }, mediaTypes[mediaType]); + transformedMediaTypes[mediaType] = Object.assign({ filteredSizeConfig }, mediaTypes[mediaType]); // transform mediaTypes object - return getTransformedMediaTypes(mediaTypes, mediaType); + const config = { + banner: 'sizes', + video: 'playerSize' + }; + + if (transformedMediaTypes[mediaType].filteredSizeConfig.length > 0) { + if (mediaType !== 'native') { + transformedMediaTypes[mediaType][config[mediaType]] = transformedMediaTypes[mediaType].filteredSizeConfig[0][config[mediaType]]; + } + } else { + delete transformedMediaTypes[mediaType]; + } } - }).filter(transformedMediaType => { - console.log('transformedMediaType', transformedMediaType); - }); + }) return { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes }; }; -/** - * This function takes information out of the sizeConfig property of the mediaTypes object and replaces it with the suitable property - * for that media type. For example, 'sizes' in the case of 'banner' media type and 'playerSize' in the case of 'video' media type. - * @param {object} mediaTypes The mediaTypes object with the sizeConfig property - * @returns {object} The mediaTypes object in the expected format - */ - -// WIP -function getTransformedMediaTypes(mediaTypes, mediaType) { - const transformedMediaTypes = Object.assign({}, mediaTypes); - const config = { - banner: 'sizes', - video: 'playerSize' - }; - - if (transformedMediaTypes[mediaType].filteredSizeConfig.length > 0) { - if (mediaType !== 'native') { - transformedMediaTypes[mediaType][config[mediaType]] = transformedMediaTypes[mediaType].filteredSizeConfig[0][config[mediaType]]; - } - } - - return transformedMediaTypes; -} - /** * Evaluates the given sizeConfig object and checks for various properties to determine if the sizeConfig is active or not. For example, * let's suppose the sizeConfig is for a Banner media type. Then, if the sizes property is found empty, it return false, else returns true. @@ -146,6 +136,34 @@ function getActiveSizeBucket(sizeConfig, activeViewport) { return activeSizeBucket; } +// const mediaTypes = { +// banner: { +// sizeConfig: [ +// { minViewPort: [0, 0], sizes: [] }, +// { minViewPort: [800, 0], sizes: [[300, 250], [300, 600]] }, +// { minViewPort: [850, 0], sizes: [[200, 200], [400, 400]] } +// ] +// }, +// video: { +// context: 'outstream', +// sizeConfig: [ +// { minViewPort: [0, 0], playerSize: [] }, // no video if < 800px +// { minViewPort: [800, 0], playerSize: [640, 480] }, +// { minViewPort: [1000, 1200], playerSize: [700, 600] } +// ], +// renderer: {} +// }, +// native: { +// sizeConfig: [ +// { minViewPort: [0, 0], active: false }, +// { minViewPort: [900, 0], active: true } +// ], +// image: { sizes: [[150, 50]] } +// } +// }; + +// console.log('getFilteredMediaTypes', JSON.stringify(getFilteredMediaTypes(mediaTypes))); + // WIP // function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { // return adUnits.reduce((result, adUnit) => { From 8d7d35d451b0984301ac53734326884a08d9cd3d Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Mon, 2 Dec 2019 13:24:47 +0530 Subject: [PATCH 03/41] implement getBids function --- integrationExamples/gpt/basic_wo_labels.html | 135 ++++++++++ modules/sizeMappingV2.js | 245 +++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 integrationExamples/gpt/basic_wo_labels.html create mode 100644 modules/sizeMappingV2.js diff --git a/integrationExamples/gpt/basic_wo_labels.html b/integrationExamples/gpt/basic_wo_labels.html new file mode 100644 index 00000000000..b04bee2f133 --- /dev/null +++ b/integrationExamples/gpt/basic_wo_labels.html @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + \ No newline at end of file diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js new file mode 100644 index 00000000000..c565860d5f3 --- /dev/null +++ b/modules/sizeMappingV2.js @@ -0,0 +1,245 @@ +// import { adunitCounter } from "../src/adUnits"; + +import { flatten, deepAccess, getDefinedParams, getUniqueIdentifierStr } from '../src/utils'; +import { processNativeAdUnitParams } from '../src/native'; +import { adunitCounter } from '../src/adUnits'; + +import { getHook } from '../src/hook'; + +getHook('getBids').before(function (fn, obj) { + return fn(obj, true, getBids(obj)); +}); + +/** + * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active based on label checks on the Ad unit/Bid object + * @param {Object} bidOrAdUnit Either the Ad Unit object or the Bid object + * @param {Array} activeLabels List of active labels passed as an argument to pbjs.requestBids function + * @returns {boolean} Represents if the Ad Unit or the Bid is active or not + */ +function isLabelActivated(bidOrAdUnit, activeLabels) { + let labelOperator; + const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); + if (labelsFound && labelsFound.length > 1) { + const lastDeclaredOperator = labelsFound[labelsFound.length - 1]; + console.log(`Ad Unit ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); + labelOperator = lastDeclaredOperator; + } else { + labelOperator = labelsFound[0]; + } + if (labelOperator === 'labelAll') { + if (bidOrAdUnit.labelAll.length === 0) { + console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAll with an empty array. Ad Unit is still enabled!`); + return true; + } + return bidOrAdUnit.labelAll.every(label => activeLabels.includes(label)); + } else if (labelOperator === 'labelAny') { + if (bidOrAdUnit.labelAny.length === 0) { + console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAny with an empty array. Ad Unit is still enabled!`); + return true; + } + return bidOrAdUnit.labelAny.some(label => activeLabels.includes(label)); + } + return true; +} + +/** + * Processes the MediaTypes object and calculates the active size buckets for each Media Type. Uses `window.innerWidth` and `window.innerHeight` + * to calculate the width and height of the active Viewport. + * @param {MediaTypes} mediaTypes Contains information about supported media types for an Ad Unit and size information for each of those types + * @returns {MediaTypes} Filtered mediaTypes object with relevant media types filterer by size buckets based on activeViewPort size + */ + +// WIP: Functionality of transforming the mediaTypes object is still pending +function getFilteredMediaTypes(mediaTypes) { + let + activeViewportWidth, + activeViewportHeight, + transformedMediaTypes; + + transformedMediaTypes = Object.assign({}, mediaTypes); + + let activeSizeBucket = { + banner: undefined, + video: undefined, + native: undefined + } + + try { + activeViewportWidth = getWindowTop().innerWidth; + activeViewportHeight = getWindowTop().innerHeight; + // activeViewportWidth = 820; + // activeViewportHeight = 300; + } catch (e) { + console.log('Unfriendly iFrame blocks Viewport size to be evaluated correctly'); + activeViewportWidth = window.innerWidth; + activeViewportHeight = window.innerHeight; + } + const activeViewport = [activeViewportWidth, activeViewportHeight]; + Object.keys(mediaTypes).map(mediaType => { + const sizeConfig = mediaTypes[mediaType].sizeConfig; + if (sizeConfig) { + activeSizeBucket[mediaType] = getActiveSizeBucket(sizeConfig, activeViewport); + const filteredSizeConfig = sizeConfig.filter(config => config.minViewPort === activeSizeBucket[mediaType] && isSizeConfigActivated(mediaType, config)); + transformedMediaTypes[mediaType] = Object.assign({ filteredSizeConfig }, mediaTypes[mediaType]); + + // transform mediaTypes object + const config = { + banner: 'sizes', + video: 'playerSize' + }; + + if (transformedMediaTypes[mediaType].filteredSizeConfig.length > 0) { + if (mediaType !== 'native') { + transformedMediaTypes[mediaType][config[mediaType]] = transformedMediaTypes[mediaType].filteredSizeConfig[0][config[mediaType]]; + } + } else { + delete transformedMediaTypes[mediaType]; + } + } + }) + return { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes }; +}; + +/** + * Evaluates the given sizeConfig object and checks for various properties to determine if the sizeConfig is active or not. For example, + * let's suppose the sizeConfig is for a Banner media type. Then, if the sizes property is found empty, it return false, else returns true. + * In case of a Video media type, it checks the playerSize property. If found empty, returns false, else returns true. + * In case of a Native media type, it checks the active property. If found false, returns false, if found true, returns true. + * @param {string} mediaType It can be 'banner', 'native' or 'video' + * @param {Object} sizeConfig Represents the sizeConfig object which is active based on the current viewport size + * @returns {boolean} Represents if the size config active or not + */ +function isSizeConfigActivated(mediaType, sizeConfig) { + switch (mediaType) { + case 'banner': + return sizeConfig.sizes && sizeConfig.sizes.length > 0; + case 'video': + return sizeConfig.playerSize && sizeConfig.playerSize.length > 0; + case 'native': + return sizeConfig.active; + default: + return false; + } +} + +/** + * Returns the active size bucket for a given media type + * @param {Array} sizeConfig SizeConfig defines the characteristics of an Ad Unit categorised into multiple size buckets per media type + * @param {Array} activeViewport Viewport size of the browser in the form [w, h] (w -> width, h -> height) + * Calculated at the time of making call to pbjs.requestBids function + * @returns {Array} The active size bucket matching the activeViewPort + */ +function getActiveSizeBucket(sizeConfig, activeViewport) { + let activeSizeBucket = []; + sizeConfig + .sort((a, b) => a.minViewPort[0] - b.minViewPort[0]) + .forEach(config => { + if (activeViewport[0] >= config.minViewPort[0]) { + if (activeViewport[1] >= config.minViewPort[1]) { + activeSizeBucket = config.minViewPort; + } else { + activeSizeBucket = []; + } + } + }) + return activeSizeBucket; +} + +// const mediaTypes = { +// banner: { +// sizeConfig: [ +// { minViewPort: [0, 0], sizes: [] }, +// { minViewPort: [800, 0], sizes: [[300, 250], [300, 600]] }, +// { minViewPort: [850, 0], sizes: [[200, 200], [400, 400]] } +// ] +// }, +// video: { +// context: 'outstream', +// sizeConfig: [ +// { minViewPort: [0, 0], playerSize: [] }, // no video if < 800px +// { minViewPort: [800, 0], playerSize: [640, 480] }, +// { minViewPort: [1000, 1200], playerSize: [700, 600] } +// ], +// renderer: {} +// }, +// native: { +// sizeConfig: [ +// { minViewPort: [0, 0], active: false }, +// { minViewPort: [900, 0], active: true } +// ], +// image: { sizes: [[150, 50]] } +// } +// }; + +// console.log('getFilteredMediaTypes', JSON.stringify(getFilteredMediaTypes(mediaTypes))); + +function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { + return adUnits.reduce((result, adUnit) => { + if (isLabelActivated(adUnit, labels)) { + if (adUnit.mediaTypes) { + const { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); + // console.log(`INFO:: SizeMapping-2.0:: Size of the viewport detected?`); + // console.log(`INFO:: SizeMapping-2.0:: What are the active size buckets?`); + // console.log(`INFO:: SizeMapping-2.0:: Which mediaTypes are active? Which ones got filtered?`); + // console.log(`INFO:: SizeMapping-2.0:: Old sizes object, new sizes object?`); + console.log('mediaTypes', mediaTypes); + console.log('activeSizeBucket', activeSizeBucket); + console.log('activeViewPort', activeViewport); + console.log('transformedMediaTypes', transformedMediaTypes); + result + .push(adUnit.bids.filter(bid => bid.bidder === bidderCode) + .reduce((bids, bid) => { + if (isLabelActivated(bid, labels)) { + // handle native params + const nativeParams = adUnit.nativeParams || deepAccess(adUnit, 'mediaTypes.native'); + if (nativeParams) { + bid = Object.assign({}, bid, { + nativeParams: processNativeAdUnitParams(nativeParams) + }); + } + + bid = Object.assign({}, bid, getDefinedParams(adUnit, ['mediaType', 'renderer'])); + + if (bid.sizeConfig) { + const relevantMediaTypes = bid.sizeConfig.filter(config => config.minViewPort === activeViewPort)[0].relevantMediaTypes; + if (relevantMediaTypes[0] !== 'none') { + const bidderMediaTypes = Object + .keys(transformedMediaTypes) + .filter(mt => relevantMediaTypes.indexOf(mt) > -1) + .reduce((mediaTypes, mediaType) => { + mediaTypes[mediaType] = transformedMediaTypes[mediaType]; + return mediaTypes; + }, {}); + + if (Object.keys(bidderMediaTypes).length > 0) { + bid = Object.assign({}, bid, bidderMediaTypes); + } else { + return console.log(`INFO:: SizeMapping-2.0:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); + } + } else { + return console.log(`INFO:: SizeMapping-2.0:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing sizeConfig check.`); + } + } + bids.push(Object.assign({}, bid, { + adUnitCode: adUnit.code, + transactionId: adUnit.transactionId, + sizes: deepAccess(transformedMediaTypes, 'banner.sizes') || deepAccess(transformedMediaTypes, 'video.playerSize') || [], + mediaTypes: bid.mediaTypes || transformedMediaTypes, + bidId: bid.bid_id || getUniqueIdentifierStr(), + bidderRequestId, + auctionId, + src, + bidRequestsCound: adunitCounter.getCounter(adUnit.code) + })); + return bids; + } else { + console.log(`INFO:: SizeMapping-2.0:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + } + }, [])); + } + } else { + console.log(`INFO:: SizeMapping-2.0:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + } + return result; + }, []).reduce(flatten, []).filter(val => val !== ''); +} From 52643734e541281e65d2f297fce1231cf777b1c8 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Mon, 2 Dec 2019 17:25:57 +0530 Subject: [PATCH 04/41] log useful information to the console --- modules/sizeMapping-2.0.js | 196 ------------------------------------- modules/sizeMappingV2.js | 70 +++---------- 2 files changed, 16 insertions(+), 250 deletions(-) delete mode 100644 modules/sizeMapping-2.0.js diff --git a/modules/sizeMapping-2.0.js b/modules/sizeMapping-2.0.js deleted file mode 100644 index 411ee5f82fc..00000000000 --- a/modules/sizeMapping-2.0.js +++ /dev/null @@ -1,196 +0,0 @@ -// import { deepAccess, getDefinedParams, console.log } from '../src/utils'; -// import { processNativeAdUnitParams } from '../src/native'; - -/** - * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active based on label checks on the Ad unit/Bid object - * @param {Object} bidOrAdUnit Either the Ad Unit object or the Bid object - * @param {Array} activeLabels List of active labels passed as an argument to pbjs.requestBids function - * @returns {boolean} Represents if the Ad Unit or the Bid is active or not - */ -function isLabelActivated(bidOrAdUnit, activeLabels) { - let labelOperator; - const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); - if (labelsFound && labelsFound.length > 1) { - const lastDeclaredOperator = labelsFound[labelsFound.length - 1]; - console.log(`Ad Unit ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); - labelOperator = lastDeclaredOperator; - } else { - labelOperator = labelsFound[0]; - } - if (labelOperator === 'labelAll') { - if (bidOrAdUnit.labelAll.length === 0) { - console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAll with an empty array. Ad Unit is still enabled!`); - return true; - } - return bidOrAdUnit.labelAll.every(label => activeLabels.includes(label)); - } else if (labelOperator === 'labelAny') { - if (bidOrAdUnit.labelAny.length === 0) { - console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAny with an empty array. Ad Unit is still enabled!`); - return true; - } - return bidOrAdUnit.labelAny.some(label => activeLabels.includes(label)); - } - return true; -} - -/** - * Processes the MediaTypes object and calculates the active size buckets for each Media Type. Uses `window.innerWidth` and `window.innerHeight` - * to calculate the width and height of the active Viewport. - * @param {MediaTypes} mediaTypes Contains information about supported media types for an Ad Unit and size information for each of those types - * @returns {MediaTypes} Filtered mediaTypes object with relevant media types filterer by size buckets based on activeViewPort size - */ - -// WIP: Functionality of transforming the mediaTypes object is still pending -function getFilteredMediaTypes(mediaTypes) { - let - activeViewportWidth, - activeViewportHeight, - transformedMediaTypes; - - transformedMediaTypes = Object.assign({}, mediaTypes); - - let activeSizeBucket = { - banner: undefined, - video: undefined, - native: undefined - } - - try { - // activeViewportWidth = getWindowTop().innerWidth; - // activeViewportHeight = getWindowTop().innerHeight; - activeViewportWidth = 820; - activeViewportHeight = 300; - } catch (e) { - console.log('Unfriendly iFrame blocks Viewport size to be evaluated correctly'); - activeViewportWidth = window.innerWidth; - activeViewportHeight = window.innerHeight; - } - const activeViewport = [activeViewportWidth, activeViewportHeight]; - Object.keys(mediaTypes).map(mediaType => { - const sizeConfig = mediaTypes[mediaType].sizeConfig; - if (sizeConfig) { - activeSizeBucket[mediaType] = getActiveSizeBucket(sizeConfig, activeViewport); - const filteredSizeConfig = sizeConfig.filter(config => config.minViewPort === activeSizeBucket[mediaType] && isSizeConfigActivated(mediaType, config)); - transformedMediaTypes[mediaType] = Object.assign({ filteredSizeConfig }, mediaTypes[mediaType]); - - // transform mediaTypes object - const config = { - banner: 'sizes', - video: 'playerSize' - }; - - if (transformedMediaTypes[mediaType].filteredSizeConfig.length > 0) { - if (mediaType !== 'native') { - transformedMediaTypes[mediaType][config[mediaType]] = transformedMediaTypes[mediaType].filteredSizeConfig[0][config[mediaType]]; - } - } else { - delete transformedMediaTypes[mediaType]; - } - } - }) - return { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes }; -}; - -/** - * Evaluates the given sizeConfig object and checks for various properties to determine if the sizeConfig is active or not. For example, - * let's suppose the sizeConfig is for a Banner media type. Then, if the sizes property is found empty, it return false, else returns true. - * In case of a Video media type, it checks the playerSize property. If found empty, returns false, else returns true. - * In case of a Native media type, it checks the active property. If found false, returns false, if found true, returns true. - * @param {string} mediaType It can be 'banner', 'native' or 'video' - * @param {Object} sizeConfig Represents the sizeConfig object which is active based on the current viewport size - * @returns {boolean} Represents if the size config active or not - */ -function isSizeConfigActivated(mediaType, sizeConfig) { - switch (mediaType) { - case 'banner': - return sizeConfig.sizes && sizeConfig.sizes.length > 0; - case 'video': - return sizeConfig.playerSize && sizeConfig.playerSize.length > 0; - case 'native': - return sizeConfig.active; - default: - return false; - } -} - -/** - * Returns the active size bucket for a given media type - * @param {Array} sizeConfig SizeConfig defines the characteristics of an Ad Unit categorised into multiple size buckets per media type - * @param {Array} activeViewport Viewport size of the browser in the form [w, h] (w -> width, h -> height) - * Calculated at the time of making call to pbjs.requestBids function - * @returns {Array} The active size bucket matching the activeViewPort - */ -function getActiveSizeBucket(sizeConfig, activeViewport) { - let activeSizeBucket = []; - sizeConfig - .sort((a, b) => a.minViewPort[0] - b.minViewPort[0]) - .forEach(config => { - if (activeViewport[0] >= config.minViewPort[0]) { - if (activeViewport[1] >= config.minViewPort[1]) { - activeSizeBucket = config.minViewPort; - } else { - activeSizeBucket = []; - } - } - }) - return activeSizeBucket; -} - -// const mediaTypes = { -// banner: { -// sizeConfig: [ -// { minViewPort: [0, 0], sizes: [] }, -// { minViewPort: [800, 0], sizes: [[300, 250], [300, 600]] }, -// { minViewPort: [850, 0], sizes: [[200, 200], [400, 400]] } -// ] -// }, -// video: { -// context: 'outstream', -// sizeConfig: [ -// { minViewPort: [0, 0], playerSize: [] }, // no video if < 800px -// { minViewPort: [800, 0], playerSize: [640, 480] }, -// { minViewPort: [1000, 1200], playerSize: [700, 600] } -// ], -// renderer: {} -// }, -// native: { -// sizeConfig: [ -// { minViewPort: [0, 0], active: false }, -// { minViewPort: [900, 0], active: true } -// ], -// image: { sizes: [[150, 50]] } -// } -// }; - -// console.log('getFilteredMediaTypes', JSON.stringify(getFilteredMediaTypes(mediaTypes))); - -// WIP -// function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { -// return adUnits.reduce((result, adUnit) => { -// if (isLabelActivated(adUnit)) { -// if (adUnit.mediaTypes) { -// const filteredMediaTypes = getFilteredMediaTypes(adUnit.mediaTypes); -// const transformMediaTypes = getTransformedMediaTypes(filteredMediaTypes); -// adUnit.mediaTypes = transformMediaTypes; -// result -// .push(adUnit.bids.filter(bid => bid.bidder === bidderCode)) -// .reduce((bids, bid) => { -// const nativeParams = adUnit.nativeParams || deepAccess(adUnit, 'mediaTypes.native'); -// if (nativeParams) { -// bid = Object.assign({}, bid, { -// nativeParams: processNativeAdUnitParams(nativeParams) -// }); -// } - -// bid = Object.assign({}, bid, getDefinedParams(adUnit, ['mediaType', 'renderer'])); - -// if (bid.sizeConfig) { - -// } -// }, []); -// } -// } else { -// logInfo(`Ad Unit ${adUnit.code} is disabled due to a failing label check.`); -// } -// }, []); -// } diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index c565860d5f3..440a3fc2265 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -1,6 +1,4 @@ -// import { adunitCounter } from "../src/adUnits"; - -import { flatten, deepAccess, getDefinedParams, getUniqueIdentifierStr } from '../src/utils'; +import { flatten, deepAccess, getDefinedParams, getUniqueIdentifierStr, logInfo, logWarn } from '../src/utils'; import { processNativeAdUnitParams } from '../src/native'; import { adunitCounter } from '../src/adUnits'; @@ -21,20 +19,20 @@ function isLabelActivated(bidOrAdUnit, activeLabels) { const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); if (labelsFound && labelsFound.length > 1) { const lastDeclaredOperator = labelsFound[labelsFound.length - 1]; - console.log(`Ad Unit ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); + logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); labelOperator = lastDeclaredOperator; } else { labelOperator = labelsFound[0]; } if (labelOperator === 'labelAll') { if (bidOrAdUnit.labelAll.length === 0) { - console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAll with an empty array. Ad Unit is still enabled!`); + logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAll with an empty array. Ad Unit is still enabled!`); return true; } return bidOrAdUnit.labelAll.every(label => activeLabels.includes(label)); } else if (labelOperator === 'labelAny') { if (bidOrAdUnit.labelAny.length === 0) { - console.log(`Ad Unit ${bidOrAdUnit.code} has property labelAny with an empty array. Ad Unit is still enabled!`); + logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAny with an empty array. Ad Unit is still enabled!`); return true; } return bidOrAdUnit.labelAny.some(label => activeLabels.includes(label)); @@ -46,10 +44,8 @@ function isLabelActivated(bidOrAdUnit, activeLabels) { * Processes the MediaTypes object and calculates the active size buckets for each Media Type. Uses `window.innerWidth` and `window.innerHeight` * to calculate the width and height of the active Viewport. * @param {MediaTypes} mediaTypes Contains information about supported media types for an Ad Unit and size information for each of those types - * @returns {MediaTypes} Filtered mediaTypes object with relevant media types filterer by size buckets based on activeViewPort size + * @returns {FilteredMediaTypes} Filtered mediaTypes object with relevant media types filtered by size buckets based on activeViewPort size */ - -// WIP: Functionality of transforming the mediaTypes object is still pending function getFilteredMediaTypes(mediaTypes) { let activeViewportWidth, @@ -67,10 +63,8 @@ function getFilteredMediaTypes(mediaTypes) { try { activeViewportWidth = getWindowTop().innerWidth; activeViewportHeight = getWindowTop().innerHeight; - // activeViewportWidth = 820; - // activeViewportHeight = 300; } catch (e) { - console.log('Unfriendly iFrame blocks Viewport size to be evaluated correctly'); + logWarn(`SizeMappingv2:: Unfriendly iframe blocks viewport size to be evaluated correctly`); activeViewportWidth = window.innerWidth; activeViewportHeight = window.innerHeight; } @@ -102,12 +96,12 @@ function getFilteredMediaTypes(mediaTypes) { /** * Evaluates the given sizeConfig object and checks for various properties to determine if the sizeConfig is active or not. For example, - * let's suppose the sizeConfig is for a Banner media type. Then, if the sizes property is found empty, it return false, else returns true. + * let's suppose the sizeConfig is for a Banner media type. Then, if the sizes property is found empty, it returns false, else returns true. * In case of a Video media type, it checks the playerSize property. If found empty, returns false, else returns true. * In case of a Native media type, it checks the active property. If found false, returns false, if found true, returns true. * @param {string} mediaType It can be 'banner', 'native' or 'video' * @param {Object} sizeConfig Represents the sizeConfig object which is active based on the current viewport size - * @returns {boolean} Represents if the size config active or not + * @returns {boolean} Represents if the size config is active or not */ function isSizeConfigActivated(mediaType, sizeConfig) { switch (mediaType) { @@ -145,47 +139,15 @@ function getActiveSizeBucket(sizeConfig, activeViewport) { return activeSizeBucket; } -// const mediaTypes = { -// banner: { -// sizeConfig: [ -// { minViewPort: [0, 0], sizes: [] }, -// { minViewPort: [800, 0], sizes: [[300, 250], [300, 600]] }, -// { minViewPort: [850, 0], sizes: [[200, 200], [400, 400]] } -// ] -// }, -// video: { -// context: 'outstream', -// sizeConfig: [ -// { minViewPort: [0, 0], playerSize: [] }, // no video if < 800px -// { minViewPort: [800, 0], playerSize: [640, 480] }, -// { minViewPort: [1000, 1200], playerSize: [700, 600] } -// ], -// renderer: {} -// }, -// native: { -// sizeConfig: [ -// { minViewPort: [0, 0], active: false }, -// { minViewPort: [900, 0], active: true } -// ], -// image: { sizes: [[150, 50]] } -// } -// }; - -// console.log('getFilteredMediaTypes', JSON.stringify(getFilteredMediaTypes(mediaTypes))); - function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { if (isLabelActivated(adUnit, labels)) { if (adUnit.mediaTypes) { const { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); - // console.log(`INFO:: SizeMapping-2.0:: Size of the viewport detected?`); - // console.log(`INFO:: SizeMapping-2.0:: What are the active size buckets?`); - // console.log(`INFO:: SizeMapping-2.0:: Which mediaTypes are active? Which ones got filtered?`); - // console.log(`INFO:: SizeMapping-2.0:: Old sizes object, new sizes object?`); - console.log('mediaTypes', mediaTypes); - console.log('activeSizeBucket', activeSizeBucket); - console.log('activeViewPort', activeViewport); - console.log('transformedMediaTypes', transformedMediaTypes); + logInfo(`SizeMappingV2:: Size of the viewport detected: `, activeViewport); + logInfo(`SizeMappingV2:: Active size buckets after filtration: `, activeSizeBucket); + logInfo(`SizeMappingV2:: Transformed mediaTypes after filtration: `, transformedMediaTypes); + logInfo(`SizeMappingV2:: mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); result .push(adUnit.bids.filter(bid => bid.bidder === bidderCode) .reduce((bids, bid) => { @@ -214,10 +176,10 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src if (Object.keys(bidderMediaTypes).length > 0) { bid = Object.assign({}, bid, bidderMediaTypes); } else { - return console.log(`INFO:: SizeMapping-2.0:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); + return logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); } } else { - return console.log(`INFO:: SizeMapping-2.0:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing sizeConfig check.`); + return logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing sizeConfig check.`); } } bids.push(Object.assign({}, bid, { @@ -233,12 +195,12 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src })); return bids; } else { - console.log(`INFO:: SizeMapping-2.0:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + return logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing label check.`); } }, [])); } } else { - console.log(`INFO:: SizeMapping-2.0:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + return logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); } return result; }, []).reduce(flatten, []).filter(val => val !== ''); From ac0007d33dfb8ba21a5e3fb2ca9007e13d942819 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Mon, 2 Dec 2019 20:56:14 +0530 Subject: [PATCH 05/41] enable test workflow --- integrationExamples/gpt/hello_world.html | 243 ++++++++++++++++------- modules/sizeMappingV2.js | 15 +- src/adapterManager.js | 10 +- src/prebid.js | 2 +- 4 files changed, 187 insertions(+), 83 deletions(-) diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index d68e65011be..21b58a4b6a3 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -8,84 +8,177 @@ --> + - - - - - - - + + + + + + + -

Prebid.js Test

-
Div-1
-
- -
+

Prebid.js Test

+
Div-1
+
+ +
+ diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 440a3fc2265..6fecd40e4dc 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -139,15 +139,20 @@ function getActiveSizeBucket(sizeConfig, activeViewport) { return activeSizeBucket; } +function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { + const activeSizeBucket = getActiveSizeBucket(sizeConfig, activeViewport); + return sizeConfig.filter(config => config.minViewPort === activeSizeBucket)[0]['relevantMediaTypes']; +} + function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { if (isLabelActivated(adUnit, labels)) { if (adUnit.mediaTypes) { const { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); - logInfo(`SizeMappingV2:: Size of the viewport detected: `, activeViewport); - logInfo(`SizeMappingV2:: Active size buckets after filtration: `, activeSizeBucket); - logInfo(`SizeMappingV2:: Transformed mediaTypes after filtration: `, transformedMediaTypes); - logInfo(`SizeMappingV2:: mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); + logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Size of the viewport detected: `, activeViewport); + logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Active size buckets after filtration: `, activeSizeBucket); + logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Transformed mediaTypes after filtration: `, transformedMediaTypes); + logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); result .push(adUnit.bids.filter(bid => bid.bidder === bidderCode) .reduce((bids, bid) => { @@ -163,7 +168,7 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src bid = Object.assign({}, bid, getDefinedParams(adUnit, ['mediaType', 'renderer'])); if (bid.sizeConfig) { - const relevantMediaTypes = bid.sizeConfig.filter(config => config.minViewPort === activeViewPort)[0].relevantMediaTypes; + const relevantMediaTypes = getRelevantMediaTypesForBidder(bid.sizeConfig, activeViewport); if (relevantMediaTypes[0] !== 'none') { const bidderMediaTypes = Object .keys(transformedMediaTypes) diff --git a/src/adapterManager.js b/src/adapterManager.js index fa7b7cbb58d..f72393f095f 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -10,6 +10,7 @@ import includes from 'core-js/library/fn/array/includes'; import find from 'core-js/library/fn/array/find'; import { adunitCounter } from './adUnits'; import { getRefererInfo } from './refererDetection'; +import { hook } from './hook'; var utils = require('./utils.js'); var CONSTANTS = require('./constants.json'); @@ -35,7 +36,10 @@ var _analyticsRegistry = {}; * @property {Array} activeLabels the labels specified as being active by requestBids */ -function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) { +function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}, a, b) { + if (a === true) { + return b; + } return adUnits.reduce((result, adUnit) => { let { active, @@ -111,6 +115,8 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) }, []).reduce(flatten, []).filter(val => val !== ''); } +const hookedGetBids = hook('sync', getBids, 'getBids'); + function getAdUnitCopyForPrebidServer(adUnits) { let adaptersServerSide = _s2sConfig.bidders; let adUnitsCopy = utils.deepClone(adUnits); @@ -243,7 +249,7 @@ adapterManager.makeBidRequests = function(adUnits, auctionStart, auctionId, cbTi bidderCode, auctionId, bidderRequestId, - bids: getBids({bidderCode, auctionId, bidderRequestId, 'adUnits': utils.deepClone(adUnitsClientCopy), labels, src: 'client'}), + bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': utils.deepClone(adUnitsClientCopy), labels, src: 'client'}), auctionStart: auctionStart, timeout: cbTimeout, refererInfo diff --git a/src/prebid.js b/src/prebid.js index 44649d7d444..22159d3a298 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -420,7 +420,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo adUnitCodes = adUnits && adUnits.map(unit => unit.code); } - adUnits = checkAdUnitSetup(adUnits); + // adUnits = checkAdUnitSetup(adUnits); /* * for a given adunit which supports a set of mediaTypes From 4b876a694bafddb385ca7ed5320de34bd4791c5f Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Wed, 4 Dec 2019 21:06:15 +0530 Subject: [PATCH 06/41] modify checkAdUnitSetup function to account for the new sizeConfig object --- src/prebid.js | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index 22159d3a298..62b1624fd76 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,7 +1,7 @@ /** @module pbjs */ import { getGlobal } from './prebidGlobal'; -import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, getLatestHighestCpmBid, isArrayOfNums } from './utils'; +import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, getLatestHighestCpmBid, isArrayOfNums, checkSizeConfigSetup } from './utils'; import { listenMessagesFromCreative } from './secureCreatives'; import { userSync } from './userSync.js'; import { loadScript } from './adloader'; @@ -80,8 +80,20 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { // make sure we always send [[h,w]] format banner.sizes = normalizedSize; adUnit.sizes = normalizedSize; + } else if (banner.sizeConfig && Array.isArray(banner.sizeConfig)) { + // verify if all config objects include "minViewPort" and "sizes" property. + // if not, remove the mediaTypes.banner object + banner.sizeConfig.forEach(config => { + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is not configured correctly. Removing the invalid mediaTypes.banner from request!`); + return delete adUnit.mediaTypes.banner; + } + // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. + config.sizes = (Array.isArray(config.sizes[0])) ? config.sizes : [config.sizes]; + }); } else { - utils.logError('Detected a mediaTypes.banner object did not include sizes. This is a required field for the mediaTypes.banner object. Removing invalid mediaTypes.banner object from request.'); + utils.logError('Detected a mediaTypes.banner object did not include sizes or sizeConfig. Removing invalid mediaTypes.banner object from request.'); delete adUnit.mediaTypes.banner; } } else if (adUnit.sizes) { @@ -103,6 +115,18 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); delete adUnit.mediaTypes.video.playerSize; } + } else if (video.sizeConfig && Array.isArray(video.sizeConfig)) { + // verify if all config objects include "minViewPort" and "playerSize" property. + // if not, remove the mediaTypes.video object + video.sizeConfig.forEach(config => { + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is not configured correctly. Removing the invalid mediaTypes.video from request!`); + return delete adUnit.mediaTypes.video; + } + // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. + config.playerSize = (Array.isArray(config.playerSize[0])) ? config.playerSize : [config.playerSize]; + }); } } @@ -120,6 +144,17 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { utils.logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.'); delete adUnit.mediaTypes.native.icon.sizes; } + if (nativeObj.sizeConfig) { + // verify if all config objects include "minViewPort" and "active" property. + // if not, remove the mediaTypes.native object + nativeObj.sizeConfig.forEach(config => { + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is not configured correctly. Removing the invalid mediaTypes.native from request!`); + return delete adUnit.mediaTypes.native; + } + }); + } } }); return adUnits; @@ -420,7 +455,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo adUnitCodes = adUnits && adUnits.map(unit => unit.code); } - // adUnits = checkAdUnitSetup(adUnits); + adUnits = checkAdUnitSetup(adUnits); /* * for a given adunit which supports a set of mediaTypes From c7b381638e90251792dd825fb77433db3d0816c6 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 5 Dec 2019 15:34:50 +0530 Subject: [PATCH 07/41] remove unrelated example --- integrationExamples/gpt/basic_wo_labels.html | 135 ------------------- 1 file changed, 135 deletions(-) delete mode 100644 integrationExamples/gpt/basic_wo_labels.html diff --git a/integrationExamples/gpt/basic_wo_labels.html b/integrationExamples/gpt/basic_wo_labels.html deleted file mode 100644 index b04bee2f133..00000000000 --- a/integrationExamples/gpt/basic_wo_labels.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - \ No newline at end of file From 751ceff3adfa57be65f74a1af4974f1480da84ba Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 6 Dec 2019 15:13:54 +0530 Subject: [PATCH 08/41] add decider function to choose between sizeMapping v1 and sizeMapping v2 --- modules/sizeMappingV2.js | 60 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 6fecd40e4dc..0f59850eb19 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -1,11 +1,65 @@ -import { flatten, deepAccess, getDefinedParams, getUniqueIdentifierStr, logInfo, logWarn } from '../src/utils'; +import { flatten, deepAccess, getDefinedParams, getUniqueIdentifierStr, logInfo, logWarn, logError } from '../src/utils'; import { processNativeAdUnitParams } from '../src/native'; import { adunitCounter } from '../src/adUnits'; +import includes from 'core-js/library/fn/array/includes'; import { getHook } from '../src/hook'; -getHook('getBids').before(function (fn, obj) { - return fn(obj, true, getBids(obj)); +// Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. +const _sizeMappingUsageMap = {}; + +function isUsingNewSizeMapping(adUnits, auctionId) { + let isUsingSizeMappingV2 = false; + adUnits.forEach(adUnit => { + if (adUnit.mediaTypes) { + // checks for the presence of sizeConfig property at the adUnit.mediaTypes object + Object.keys(adUnit.mediaTypes).forEach(mediaType => { + if (adUnit.mediaTypes[mediaType].sizeConfig) { + if (isUsingSizeMappingV2 === false) { + isUsingSizeMappingV2 = true; + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + } + } + }); + + // checks for the presence of sizeConfig property at the adUnit.bids.bidder object + adUnit.bids.forEach(bidder => { + if (bidder.sizeConfig && checkBidderSizeConfigFormat(bidder.sizeConfig)) { + if (isUsingNewSizeMapping === false) { + isUsingSizeMappingV2 = true; + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + } + } + }); + } + }); + return isUsingSizeMappingV2; +} + +function checkBidderSizeConfigFormat(sizeConfig) { + if (Array.isArray(sizeConfig)) { + sizeConfig.forEach(config => { + const keys = Object.keys(config); + if ((includes(keys, 'minViewPort') && includes(keys, 'relevantMediaTypes'))) { + if (Array.isArray(config.minViewPort) && Array.isArray(config.relevantMediaTypes)) { + return true; + } + } + }); + } + return false; +} + +getHook('getBids').before(function (fn, bidderInfo) { + // check if the adUnit is using sizeMappingV1 specs or sizeMappingV2 specs. + if (typeof _sizeMappingUsageMap[bidderInfo.auctionId] === 'undefined') { + isUsingNewSizeMapping(bidderInfo.adUnits, bidderInfo.auctionId); + } + if (_sizeMappingUsageMap[bidderInfo.auctionId]) { + return fn.call(this, bidderInfo, getBids(bidderInfo)); + } else { + return fn.call(this, bidderInfo); + } }); /** From cbf2a61e19242b1edcf7474be3501c2e5250a8e2 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 6 Dec 2019 15:15:31 +0530 Subject: [PATCH 09/41] add getBidsHook for s2s bidders --- src/adapterManager.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/adapterManager.js b/src/adapterManager.js index f72393f095f..d1addc797b8 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -36,10 +36,12 @@ var _analyticsRegistry = {}; * @property {Array} activeLabels the labels specified as being active by requestBids */ -function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}, a, b) { - if (a === true) { - return b; +function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}, bidsInfoFromSizeMappingV2) { + // if the bids information is returned from sizeMappingV2 module, just return that. + if (bidsInfoFromSizeMappingV2) { + return bidsInfoFromSizeMappingV2; } + return adUnits.reduce((result, adUnit) => { let { active, @@ -214,7 +216,7 @@ adapterManager.makeBidRequests = function(adUnits, auctionStart, auctionId, cbTi auctionId, bidderRequestId, tid, - bids: getBids({bidderCode, auctionId, bidderRequestId, 'adUnits': utils.deepClone(adUnitsS2SCopy), labels, src: CONSTANTS.S2S.SRC}), + bids: hookedGetBids({bidderCode, auctionId, bidderRequestId, 'adUnits': utils.deepClone(adUnitsS2SCopy), labels, src: CONSTANTS.S2S.SRC}), auctionStart: auctionStart, timeout: _s2sConfig.timeout, src: CONSTANTS.S2S.SRC, From 60847f0742e8a5ea99f42d181434a44d820ee323 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 6 Dec 2019 17:36:10 +0530 Subject: [PATCH 10/41] handle edge case where all mediaTypes get filtered out and the case when bidder gets filtered out --- modules/sizeMappingV2.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 0f59850eb19..4e38dc735e3 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -160,9 +160,9 @@ function getFilteredMediaTypes(mediaTypes) { function isSizeConfigActivated(mediaType, sizeConfig) { switch (mediaType) { case 'banner': - return sizeConfig.sizes && sizeConfig.sizes.length > 0; + return sizeConfig.sizes && sizeConfig.sizes[0].length > 0; case 'video': - return sizeConfig.playerSize && sizeConfig.playerSize.length > 0; + return sizeConfig.playerSize && sizeConfig.playerSize[0].length > 0; case 'native': return sizeConfig.active; default: @@ -207,6 +207,13 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Active size buckets after filtration: `, activeSizeBucket); logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Transformed mediaTypes after filtration: `, transformedMediaTypes); logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); + + // check if adUnit has any active media types remaining, if not drop the adUnit from auction, + // else proceed to evaluate the bids object. + if (Object.keys(transformedMediaTypes).length === 0) { + logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled since there are no active media types after sizeConfig filtration.`); + return result; + } result .push(adUnit.bids.filter(bid => bid.bidder === bidderCode) .reduce((bids, bid) => { @@ -235,10 +242,12 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src if (Object.keys(bidderMediaTypes).length > 0) { bid = Object.assign({}, bid, bidderMediaTypes); } else { - return logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); + logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); + return bids; } } else { - return logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing sizeConfig check.`); + logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing sizeConfig check.`); + return bids; } } bids.push(Object.assign({}, bid, { From 5c35aed3f5d990945cf0838fe2b132a3d473a525 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sat, 7 Dec 2019 15:59:19 +0530 Subject: [PATCH 11/41] add test examples for banner media type --- .../gpt/sizeMappingv2/banner_ad.html | 93 ++++++++++++++++++ .../banner_ad_with_bidder_level_checks.html | 98 +++++++++++++++++++ ...ad_with_label_and_size_mapping_checks.html | 0 3 files changed, 191 insertions(+) create mode 100644 integrationExamples/gpt/sizeMappingv2/banner_ad.html create mode 100644 integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html create mode 100644 integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad.html b/integrationExamples/gpt/sizeMappingv2/banner_ad.html new file mode 100644 index 00000000000..4d072d7c98a --- /dev/null +++ b/integrationExamples/gpt/sizeMappingv2/banner_ad.html @@ -0,0 +1,93 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html new file mode 100644 index 00000000000..087806be853 --- /dev/null +++ b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html new file mode 100644 index 00000000000..e69de29bb2d From ca521ba3fd025d1aa59c5d3b3cc816854bd66420 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sat, 7 Dec 2019 16:06:28 +0530 Subject: [PATCH 12/41] update label check to pick up the fist label operator instead of the last on incase there are multiple lables present --- modules/sizeMappingV2.js | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 4e38dc735e3..ac158b6e382 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -1,8 +1,16 @@ -import { flatten, deepAccess, getDefinedParams, getUniqueIdentifierStr, logInfo, logWarn, logError } from '../src/utils'; +import { + flatten, + deepAccess, + getDefinedParams, + getUniqueIdentifierStr, + logInfo, + logWarn, + logError, + isValidMediaTypes +} from '../src/utils'; import { processNativeAdUnitParams } from '../src/native'; import { adunitCounter } from '../src/adUnits'; import includes from 'core-js/library/fn/array/includes'; - import { getHook } from '../src/hook'; // Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. @@ -72,12 +80,10 @@ function isLabelActivated(bidOrAdUnit, activeLabels) { let labelOperator; const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); if (labelsFound && labelsFound.length > 1) { - const lastDeclaredOperator = labelsFound[labelsFound.length - 1]; - logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has multiple label operators. Using the last declared operator ${lastDeclaredOperator}`); - labelOperator = lastDeclaredOperator; - } else { - labelOperator = labelsFound[0]; + logError(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has multiple label operators. Using the first declared operator ${labelsFound[0]}`); } + labelOperator = labelsFound[0]; + if (labelOperator === 'labelAll') { if (bidOrAdUnit.labelAll.length === 0) { logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAll with an empty array. Ad Unit is still enabled!`); @@ -201,12 +207,11 @@ function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { if (isLabelActivated(adUnit, labels)) { - if (adUnit.mediaTypes) { + if (adUnit.mediaTypes && isValidMediaTypes(adUnit.mediaTypes)) { const { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); - logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Size of the viewport detected: `, activeViewport); - logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Active size buckets after filtration: `, activeSizeBucket); - logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, Transformed mediaTypes after filtration: `, transformedMediaTypes); - logInfo(`SizeMappingV2:: AdUnit:: ${adUnit.code}, mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Active size buckets after filtration: `, activeSizeBucket); + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Transformed mediaTypes after filtration: `, transformedMediaTypes); + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); // check if adUnit has any active media types remaining, if not drop the adUnit from auction, // else proceed to evaluate the bids object. @@ -263,9 +268,12 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src })); return bids; } else { - return logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + return bids; } }, [])); + } else { + logWarn(`SizeMappingV2:: Ad Unit: ${adUnit.code} has declared invalid mediaTypes or has not declared a mediaTypes property`); } } else { return logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); From 9ca28cff30938515cbf2107459eae560e9436cd4 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sat, 7 Dec 2019 17:12:29 +0530 Subject: [PATCH 13/41] added example for label checks with banner ad --- ...ad_with_label_and_size_mapping_checks.html | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html index e69de29bb2d..e14fdb23755 100644 --- a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html +++ b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html @@ -0,0 +1,130 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + From eeb07e756992ca01aa38e004245d15fd885c0a8a Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 13:29:56 +0530 Subject: [PATCH 14/41] add checkAdUnit setup hook on sizeMappingV2 modules to check presence of sizeConfig property in addition to doing normal adUnit checks --- modules/sizeMappingV2.js | 137 ++++++++++++++++++++++--- src/prebid.js | 170 ++++++++++++++++---------------- test/spec/unit/pbjs_api_spec.js | 6 +- 3 files changed, 214 insertions(+), 99 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index ac158b6e382..b828e5070b1 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -5,13 +5,19 @@ import { getUniqueIdentifierStr, logInfo, logWarn, - logError, - isValidMediaTypes + isValidMediaTypes, + isArrayOfNums } from '../src/utils'; import { processNativeAdUnitParams } from '../src/native'; import { adunitCounter } from '../src/adUnits'; import includes from 'core-js/library/fn/array/includes'; import { getHook } from '../src/hook'; +import { + validateBannerMediaType, + validateVideoMediaType, + validateNativeMediaType, + validateSizes +} from '../src/prebid'; // Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. const _sizeMappingUsageMap = {}; @@ -25,7 +31,9 @@ function isUsingNewSizeMapping(adUnits, auctionId) { if (adUnit.mediaTypes[mediaType].sizeConfig) { if (isUsingSizeMappingV2 === false) { isUsingSizeMappingV2 = true; - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + if (auctionId) { + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + } } } }); @@ -33,9 +41,11 @@ function isUsingNewSizeMapping(adUnits, auctionId) { // checks for the presence of sizeConfig property at the adUnit.bids.bidder object adUnit.bids.forEach(bidder => { if (bidder.sizeConfig && checkBidderSizeConfigFormat(bidder.sizeConfig)) { - if (isUsingNewSizeMapping === false) { + if (isUsingNewSizeMappingV2 === false) { isUsingSizeMappingV2 = true; - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + if (auctionId) { + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + } } } }); @@ -44,12 +54,110 @@ function isUsingNewSizeMapping(adUnits, auctionId) { return isUsingSizeMappingV2; } +function checkAdUnitSetupHook(adUnits) { + adUnits.forEach(adUnit => { + const mediaTypes = adUnit.mediaTypes; + if (mediaTypes && mediaTypes.banner) { + const banner = mediaTypes.banner; + if (banner.sizes) { + adUnit = validateBannerMediaType(adUnit); + } else if (banner.sizeConfig && Array.isArray(banner.sizeConfig)) { + banner.sizeConfig.forEach(config => { + // verify if all config objects include "minViewPort" and "sizes" property. + // if not, remove the mediaTypes.banner object + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is missing required property minViewPort or sizes or both. Removing the invalid mediaTypes.banner from request!`); + return delete adUnit.mediaTypes.banner; + } + // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. + const bannerSizes = validateSizes(config.sizes); + if (bannerSizes.length > 0 && isArrayOfNums(config.minViewPort, 2)) { + config.sizes = bannerSizes; + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has properties minViewPort or sizes decalared with invalid values. Removing the invalid object mediaTypes.banner from request!`); + return delete adUnit.mediaTypes.banner; + } + }); + } else { + logError('Detected a mediaTypes.banner object did not include required property sizes. Removing invalid mediaTypes.banner object from request.'); + delete adUnit.mediaTypes.banner; + } + } else if (adUnit.sizes) { + logWarn('Usage of adUnits.sizes will eventually be deprecated. Please define size dimensions within the corresponding area of the mediaTypes. (eg mediaTypes.banner.sizes).'); + const bannerSizes = validateSizes(adUnit.sizes); + if (bannerSizes.length > 0) { + adUnit.sizes = bannerSizes; + } + } + + if (mediaTypes && mediaTypes.video) { + const video = mediaTypes.video; + if (video.playerSize) { + adUnit = validateVideoMediaType(adUnit); + } else if (video.sizeConfig) { + video.sizeConfig.forEach(config => { + // verify if all config objects include "minViewPort" and "playerSize" property. + // if not, remove the mediaTypes.video object + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from request!`); + return delete adUnit.mediaTypes.video.sizeConfig; + } + // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. + let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; + const videoSizes = validateSizes(config.playerSize, tarPlayerSizeLen); + if (videoSizes.length > 0 && isArrayOfNums(config.minViewPort, 2)) { + config.playerSize = videoSizes; + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has properties minViewPort or playerSize decalared with invalid values. Removing the invalid property mediaTypes.video.sizeConfig from request!`); + return delete adUnit.mediaTypes.video.sizeConfig; + } + }); + } + } + + if (mediaTypes && mediaTypes.native) { + adUnit = validateNativeMediaType(adUnit); + + if (mediaTypes.native.sizeConfig) { + nativeObj.sizeConfig.forEach(config => { + // verify if all config objects include "minViewPort" and "active" property. + // if not, remove the mediaTypes.native object + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from request!`); + return delete adUnit.mediaTypes.native.sizeConfig; + } + + if (!(isArrayOfNums(config.minViewPort, 2) && typeof config.active === 'boolean')) { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from request!`); + return delete adUnit.mediaTypes.native.sizeConfig; + } + }); + } + } + }); + return adUnits; +} + +getHook('checkAdUnitSetup').before(function(fn, adUnits) { + const usingNewSizeMapping = isUsingNewSizeMapping(adUnits); + if (usingNewSizeMapping) { + return fn.call(this, adUnits, checkAdUnitSetupHook); + } else { + return fn.call(this, adUnits); + } +}); + function checkBidderSizeConfigFormat(sizeConfig) { if (Array.isArray(sizeConfig)) { sizeConfig.forEach(config => { const keys = Object.keys(config); if ((includes(keys, 'minViewPort') && includes(keys, 'relevantMediaTypes'))) { - if (Array.isArray(config.minViewPort) && Array.isArray(config.relevantMediaTypes)) { + if (isArrayOfNums(config.minViewPort, 2) && + Array.isArray(config.relevantMediaTypes) && + config.relevantMediaTypes.every(mt => typeof mt === 'string')) { return true; } } @@ -76,11 +184,13 @@ getHook('getBids').before(function (fn, bidderInfo) { * @param {Array} activeLabels List of active labels passed as an argument to pbjs.requestBids function * @returns {boolean} Represents if the Ad Unit or the Bid is active or not */ -function isLabelActivated(bidOrAdUnit, activeLabels) { +function isLabelActivated(bidOrAdUnit, activeLabels, adUnitCode) { let labelOperator; const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); if (labelsFound && labelsFound.length > 1) { - logError(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has multiple label operators. Using the first declared operator ${labelsFound[0]}`); + logWarn(`SizeMappingV2:: ${(bidOrAdUnit.code) + ? (`Ad Unit: ${bidOrAdUnit.code} has multiple label operators. Using the first declared operator: ${labelsFound[0]}`) + : (`Bidder: ${bidOrAdUnit.bidder} in Ad Unit: ${adUnitCode} has multiple label operators. Using the first declared operator: ${labelsFound[0]}`)}`); } labelOperator = labelsFound[0]; @@ -89,13 +199,13 @@ function isLabelActivated(bidOrAdUnit, activeLabels) { logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAll with an empty array. Ad Unit is still enabled!`); return true; } - return bidOrAdUnit.labelAll.every(label => activeLabels.includes(label)); + return bidOrAdUnit.labelAll.every(label => includes(activeLabels, label)); } else if (labelOperator === 'labelAny') { if (bidOrAdUnit.labelAny.length === 0) { logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAny with an empty array. Ad Unit is still enabled!`); return true; } - return bidOrAdUnit.labelAny.some(label => activeLabels.includes(label)); + return bidOrAdUnit.labelAny.some(label => includes(activeLabels, label)); } return true; } @@ -206,7 +316,7 @@ function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { - if (isLabelActivated(adUnit, labels)) { + if (isLabelActivated(adUnit, labels, adUnit.code)) { if (adUnit.mediaTypes && isValidMediaTypes(adUnit.mediaTypes)) { const { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Active size buckets after filtration: `, activeSizeBucket); @@ -222,7 +332,7 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src result .push(adUnit.bids.filter(bid => bid.bidder === bidderCode) .reduce((bids, bid) => { - if (isLabelActivated(bid, labels)) { + if (isLabelActivated(bid, labels, adUnit.code)) { // handle native params const nativeParams = adUnit.nativeParams || deepAccess(adUnit, 'mediaTypes.native'); if (nativeParams) { @@ -276,7 +386,8 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src logWarn(`SizeMappingV2:: Ad Unit: ${adUnit.code} has declared invalid mediaTypes or has not declared a mediaTypes property`); } } else { - return logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + return result; } return result; }, []).reduce(flatten, []).filter(val => val !== ''); diff --git a/src/prebid.js b/src/prebid.js index 62b1624fd76..3b4b1ee4ab6 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,7 +1,7 @@ /** @module pbjs */ import { getGlobal } from './prebidGlobal'; -import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, getLatestHighestCpmBid, isArrayOfNums, checkSizeConfigSetup } from './utils'; +import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, getLatestHighestCpmBid, isArrayOfNums } from './utils'; import { listenMessagesFromCreative } from './secureCreatives'; import { userSync } from './userSync.js'; import { loadScript } from './adloader'; @@ -69,92 +69,96 @@ function setRenderSize(doc, width, height) { } } -export const checkAdUnitSetup = hook('sync', function (adUnits) { - adUnits.forEach((adUnit) => { - const mediaTypes = adUnit.mediaTypes; - const normalizedSize = utils.getAdUnitSizes(adUnit); +export function validateSizes(sizes, targLength) { + let cleanSizes = []; + if (utils.isArray(sizes) && ((targLength) ? sizes.length === targLength : sizes.length > 0)) { + // check if an array of arrays or array of numbers + if (sizes.every(sz => isArrayOfNums(sz, 2))) { + cleanSizes = sizes; + } else if (isArrayOfNums(sizes, 2)) { + cleanSizes.push(sizes); + } + } + return cleanSizes; +} + +export function validateBannerMediaType(adUnit) { + const banner = adUnit.mediaTypes.banner; + const bannerSizes = validateSizes(banner.sizes); + if (bannerSizes.length > 0) { + banner.sizes = bannerSizes; + // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.banner.sizes + adUnit.sizes = bannerSizes; + } else { + utils.logError('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.'); + delete adUnit.mediaTypes.banner + } + return adUnit; +} + +export function validateVideoMediaType(adUnit) { + const video = adUnit.mediaTypes.video; + let tarPlayerSizeLen = (typeof video.playerSize[0] === 'number') ? 2 : 1; + const videoSizes = validateSizes(video.playerSize, tarPlayerSizeLen); + if (videoSizes.length > 0) { + if (tarPlayerSizeLen === 2) { + utils.logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); + } + video.playerSize = videoSizes; + // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.video.playerSize + adUnit.sizes = videoSizes; + } else { + utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); + delete adUnit.mediaTypes.video.playerSize; + } + return adUnit; +} + +export function validateNativeMediaType(adUnit) { + const native = adUnit.mediaTypes.native; + if (native.image && native.image.sizes && !Array.isArray(native.image.sizes)) { + utils.logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.'); + delete adUnit.mediaTypes.native.image.sizes; + } + if (native.image && native.image.aspect_ratios && !Array.isArray(native.image.aspect_ratios)) { + utils.logError('Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request.'); + delete adUnit.mediaTypes.native.image.aspect_ratios; + } + if (native.icon && native.icon.sizes && !Array.isArray(native.icon.sizes)) { + utils.logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.'); + delete adUnit.mediaTypes.native.icon.sizes; + } + return adUnit; +} + +export const checkAdUnitSetup = hook('sync', function (adUnits, filteredAdUnitsFromSizeMappingV2) { + if (filteredAdUnitsFromSizeMappingV2) { + return filteredAdUnitsFromSizeMappingV2; + } + adUnits.forEach(adUnit => { + const mediaTypes = adUnit.mediaTypes; if (mediaTypes && mediaTypes.banner) { - const banner = mediaTypes.banner; - if (banner.sizes) { - // make sure we always send [[h,w]] format - banner.sizes = normalizedSize; - adUnit.sizes = normalizedSize; - } else if (banner.sizeConfig && Array.isArray(banner.sizeConfig)) { - // verify if all config objects include "minViewPort" and "sizes" property. - // if not, remove the mediaTypes.banner object - banner.sizeConfig.forEach(config => { - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is not configured correctly. Removing the invalid mediaTypes.banner from request!`); - return delete adUnit.mediaTypes.banner; - } - // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. - config.sizes = (Array.isArray(config.sizes[0])) ? config.sizes : [config.sizes]; - }); + if (mediaTypes.banner.sizes) { + adUnit = validateBannerMediaType(adUnit); } else { - utils.logError('Detected a mediaTypes.banner object did not include sizes or sizeConfig. Removing invalid mediaTypes.banner object from request.'); + utils.logError('Detected a mediaTypes.banner object did not include required property sizes. Removing invalid mediaTypes.banner object from request.'); delete adUnit.mediaTypes.banner; } } else if (adUnit.sizes) { - utils.logWarn('Usage of adUnits.sizes will eventually be deprecated. Please define size dimensions within the corresponding area of the mediaTypes. (eg mediaTypes.banner.sizes).'); - adUnit.sizes = normalizedSize; + utils.logWarn('Usage of adUnits.sizes will eventually be deprecated. Please define size dimensions within the corresponding area of the mediaTypes. (eg mediaTypes.banner.sizes).'); + const bannerSizes = validateSizes(adUnit.sizes); + if (bannerSizes.length > 0) { + adUnit.sizes = bannerSizes; + } } - if (mediaTypes && mediaTypes.video) { - const video = mediaTypes.video; - if (video.playerSize) { - if (Array.isArray(video.playerSize) && video.playerSize.length === 1 && video.playerSize.every(plySize => isArrayOfNums(plySize, 2))) { - adUnit.sizes = video.playerSize; - } else if (isArrayOfNums(video.playerSize, 2)) { - let newPlayerSize = []; - newPlayerSize.push(video.playerSize); - utils.logInfo(`Transforming video.playerSize from [${video.playerSize}] to [[${newPlayerSize}]] so it's in the proper format.`); - adUnit.sizes = video.playerSize = newPlayerSize; - } else { - utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); - delete adUnit.mediaTypes.video.playerSize; - } - } else if (video.sizeConfig && Array.isArray(video.sizeConfig)) { - // verify if all config objects include "minViewPort" and "playerSize" property. - // if not, remove the mediaTypes.video object - video.sizeConfig.forEach(config => { - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is not configured correctly. Removing the invalid mediaTypes.video from request!`); - return delete adUnit.mediaTypes.video; - } - // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. - config.playerSize = (Array.isArray(config.playerSize[0])) ? config.playerSize : [config.playerSize]; - }); - } + if (mediaTypes && mediaTypes.video && mediaTypes.video.playerSize) { + adUnit = validateVideoMediaType(adUnit); } if (mediaTypes && mediaTypes.native) { - const nativeObj = mediaTypes.native; - if (nativeObj.image && nativeObj.image.sizes && !Array.isArray(nativeObj.image.sizes)) { - utils.logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.'); - delete adUnit.mediaTypes.native.image.sizes; - } - if (nativeObj.image && nativeObj.image.aspect_ratios && !Array.isArray(nativeObj.image.aspect_ratios)) { - utils.logError('Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request.'); - delete adUnit.mediaTypes.native.image.aspect_ratios; - } - if (nativeObj.icon && nativeObj.icon.sizes && !Array.isArray(nativeObj.icon.sizes)) { - utils.logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.'); - delete adUnit.mediaTypes.native.icon.sizes; - } - if (nativeObj.sizeConfig) { - // verify if all config objects include "minViewPort" and "active" property. - // if not, remove the mediaTypes.native object - nativeObj.sizeConfig.forEach(config => { - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { - utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is not configured correctly. Removing the invalid mediaTypes.native from request!`); - return delete adUnit.mediaTypes.native; - } - }); - } + adUnit = validateNativeMediaType(adUnit); } }); return adUnits; @@ -190,7 +194,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { * @alias module:pbjs.getAdserverTargetingForAdUnitCode * @returns {Object} returnObj return bids */ -$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) { +$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function (adUnitCode) { return $$PREBID_GLOBAL$$.getAdserverTargeting(adUnitCode)[adUnitCode]; }; @@ -298,7 +302,7 @@ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function (adUnit, customSlotMatching * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes * @alias module:pbjs.setTargetingForAst */ -$$PREBID_GLOBAL$$.setTargetingForAst = function(adUnitCodes) { +$$PREBID_GLOBAL$$.setTargetingForAst = function (adUnitCodes) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.setTargetingForAn', arguments); if (!targeting.isApntagDefined()) { utils.logError('window.apntag is not defined on the page'); @@ -465,7 +469,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo */ adUnits.forEach(adUnit => { // get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present - const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || {'banner': 'banner'}); + const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }); // get the bidder's mediaTypes const allBidders = adUnit.bids.map(bid => bid.bidder); @@ -509,7 +513,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo return; } - const auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId}); + const auction = auctionManager.createAuction({ adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId }); let adUnitsLen = adUnits.length; if (adUnitsLen > 15) { @@ -839,7 +843,7 @@ $$PREBID_GLOBAL$$.que.push(() => listenMessagesFromCreative()); * the Prebid script has been fully loaded. * @alias module:pbjs.cmd.push */ -$$PREBID_GLOBAL$$.cmd.push = function(command) { +$$PREBID_GLOBAL$$.cmd.push = function (command) { if (typeof command === 'function') { try { command.call(); @@ -854,7 +858,7 @@ $$PREBID_GLOBAL$$.cmd.push = function(command) { $$PREBID_GLOBAL$$.que.push = $$PREBID_GLOBAL$$.cmd.push; function processQueue(queue) { - queue.forEach(function(cmd) { + queue.forEach(function (cmd) { if (typeof cmd.called === 'undefined') { try { cmd.call(); @@ -869,7 +873,7 @@ function processQueue(queue) { /** * @alias module:pbjs.processQueue */ -$$PREBID_GLOBAL$$.processQueue = function() { +$$PREBID_GLOBAL$$.processQueue = function () { hook.ready(); processQueue($$PREBID_GLOBAL$$.que); processQueue($$PREBID_GLOBAL$$.cmd); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 485dd5cf077..a3dacb320dc 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1601,7 +1601,7 @@ describe('Unit: Prebid Module', function () { }); expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[300, 250], [300, 600]]); expect(auctionArgs.adUnits[0].mediaTypes.banner).to.be.undefined; - assert.ok(logErrorSpy.calledWith('Detected a mediaTypes.banner object did not include sizes. This is a required field for the mediaTypes.banner object. Removing invalid mediaTypes.banner object from request.')); + assert.ok(logErrorSpy.calledWith('Detected a mediaTypes.banner object did not include required property sizes. Removing invalid mediaTypes.banner object from request.')); let badVideo1 = [{ code: 'testb2', @@ -1619,7 +1619,7 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[600, 600]]); expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.be.undefined; expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; - assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); + assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); let badVideo2 = [{ code: 'testb3', @@ -1637,7 +1637,7 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[600, 600]]); expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.be.undefined; expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; - assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); + assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); let badNativeImgSize = [{ code: 'testb4', From 0d6e27c8c9e299178ef7d31da0b7cd92f6a531bc Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 14:08:36 +0530 Subject: [PATCH 15/41] restore old hello world example --- integrationExamples/gpt/hello_world.html | 243 +++++++---------------- 1 file changed, 75 insertions(+), 168 deletions(-) diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index 21b58a4b6a3..d68e65011be 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -8,177 +8,84 @@ --> - - - - - - - - + + + + + + + -

Prebid.js Test

-
Div-1
-
- -
+

Prebid.js Test

+
Div-1
+
+ +
- From 208861ac0a2a96999eea07ca7def380d2e084436 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 15:58:06 +0530 Subject: [PATCH 16/41] give free pass to video mediaTypes configured with sizeConfig property, while keeping in place the essential sizeConfig checks --- modules/adpod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adpod.js b/modules/adpod.js index 8c352b67d04..1067e40f740 100644 --- a/modules/adpod.js +++ b/modules/adpod.js @@ -271,7 +271,7 @@ export function checkAdUnitSetupHook(fn, adUnits) { let errMsg = `Detected missing or incorrectly setup fields for an adpod adUnit. Please review the following fields of adUnitCode: ${adUnit.code}. This adUnit will be removed from the auction.`; - let playerSize = !!(videoConfig.playerSize && utils.isArrayOfNums(videoConfig.playerSize)); + let playerSize = !!((videoConfig.playerSize && utils.isArrayOfNums(videoConfig.playerSize)) || (videoConfig.sizeConfig)); let adPodDurationSec = !!(videoConfig.adPodDurationSec && utils.isNumber(videoConfig.adPodDurationSec) && videoConfig.adPodDurationSec > 0); let durationRangeSec = !!(videoConfig.durationRangeSec && utils.isArrayOfNums(videoConfig.durationRangeSec) && videoConfig.durationRangeSec.every(range => range > 0)); From bbb18887f5b52f269872e08def1bd4032a7b5c9e Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 15:59:21 +0530 Subject: [PATCH 17/41] fixing minor bus and enchancing bidder level sizeConfig checks --- modules/sizeMappingV2.js | 60 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index b828e5070b1..9413d21dd43 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -4,6 +4,7 @@ import { getDefinedParams, getUniqueIdentifierStr, logInfo, + logError, logWarn, isValidMediaTypes, isArrayOfNums @@ -23,16 +24,16 @@ import { const _sizeMappingUsageMap = {}; function isUsingNewSizeMapping(adUnits, auctionId) { - let isUsingSizeMappingV2 = false; + let isUsingSizeMappingBool = false; adUnits.forEach(adUnit => { if (adUnit.mediaTypes) { // checks for the presence of sizeConfig property at the adUnit.mediaTypes object Object.keys(adUnit.mediaTypes).forEach(mediaType => { if (adUnit.mediaTypes[mediaType].sizeConfig) { - if (isUsingSizeMappingV2 === false) { - isUsingSizeMappingV2 = true; + if (isUsingSizeMappingBool === false) { + isUsingSizeMappingBool = true; if (auctionId) { - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingBool; } } } @@ -40,18 +41,18 @@ function isUsingNewSizeMapping(adUnits, auctionId) { // checks for the presence of sizeConfig property at the adUnit.bids.bidder object adUnit.bids.forEach(bidder => { - if (bidder.sizeConfig && checkBidderSizeConfigFormat(bidder.sizeConfig)) { - if (isUsingNewSizeMappingV2 === false) { - isUsingSizeMappingV2 = true; + if (bidder.sizeConfig) { + if (isUsingSizeMappingBool === false) { + isUsingSizeMappingBool = true; if (auctionId) { - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingV2; + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingBool; } } } }); } }); - return isUsingSizeMappingV2; + return isUsingSizeMappingBool; } function checkAdUnitSetupHook(adUnits) { @@ -67,15 +68,15 @@ function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.banner object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is missing required property minViewPort or sizes or both. Removing the invalid mediaTypes.banner from request!`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is missing required property minViewPort or sizes or both. Removing the invalid mediaTypes.banner from request.`); return delete adUnit.mediaTypes.banner; } // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. const bannerSizes = validateSizes(config.sizes); - if (bannerSizes.length > 0 && isArrayOfNums(config.minViewPort, 2)) { + if (isArrayOfNums(config.minViewPort, 2)) { config.sizes = bannerSizes; } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has properties minViewPort or sizes decalared with invalid values. Removing the invalid object mediaTypes.banner from request!`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has properties minViewPort or sizes decalared with invalid values. Removing the invalid object mediaTypes.banner from request.`); return delete adUnit.mediaTypes.banner; } }); @@ -101,16 +102,16 @@ function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.video object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from request!`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from request.`); return delete adUnit.mediaTypes.video.sizeConfig; } // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; const videoSizes = validateSizes(config.playerSize, tarPlayerSizeLen); - if (videoSizes.length > 0 && isArrayOfNums(config.minViewPort, 2)) { + if (isArrayOfNums(config.minViewPort, 2)) { config.playerSize = videoSizes; } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has properties minViewPort or playerSize decalared with invalid values. Removing the invalid property mediaTypes.video.sizeConfig from request!`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has properties minViewPort or playerSize decalared with invalid values. Removing the invalid property mediaTypes.video.sizeConfig from request.`); return delete adUnit.mediaTypes.video.sizeConfig; } }); @@ -126,12 +127,12 @@ function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.native object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from request!`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from request.`); return delete adUnit.mediaTypes.native.sizeConfig; } if (!(isArrayOfNums(config.minViewPort, 2) && typeof config.active === 'boolean')) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from request!`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from request.`); return delete adUnit.mediaTypes.native.sizeConfig; } }); @@ -141,29 +142,32 @@ function checkAdUnitSetupHook(adUnits) { return adUnits; } -getHook('checkAdUnitSetup').before(function(fn, adUnits) { +getHook('checkAdUnitSetup').before(function (fn, adUnits) { const usingNewSizeMapping = isUsingNewSizeMapping(adUnits); if (usingNewSizeMapping) { - return fn.call(this, adUnits, checkAdUnitSetupHook); + return fn.call(this, adUnits, checkAdUnitSetupHook(adUnits)); } else { return fn.call(this, adUnits); } }); function checkBidderSizeConfigFormat(sizeConfig) { + let didCheckPass = true; if (Array.isArray(sizeConfig)) { sizeConfig.forEach(config => { const keys = Object.keys(config); if ((includes(keys, 'minViewPort') && includes(keys, 'relevantMediaTypes'))) { if (isArrayOfNums(config.minViewPort, 2) && Array.isArray(config.relevantMediaTypes) && - config.relevantMediaTypes.every(mt => typeof mt === 'string')) { - return true; + config.relevantMediaTypes.every(mt => (includes(['banner', 'video', 'native'], mt)) || (mt === 'none'))) { + didCheckPass = didCheckPass && true; } + } else { + didCheckPass = false; } }); } - return false; + return didCheckPass; } getHook('getBids').before(function (fn, bidderInfo) { @@ -310,8 +314,11 @@ function getActiveSizeBucket(sizeConfig, activeViewport) { } function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { - const activeSizeBucket = getActiveSizeBucket(sizeConfig, activeViewport); - return sizeConfig.filter(config => config.minViewPort === activeSizeBucket)[0]['relevantMediaTypes']; + if (checkBidderSizeConfigFormat(sizeConfig)) { + const activeSizeBucket = getActiveSizeBucket(sizeConfig, activeViewport); + return sizeConfig.filter(config => config.minViewPort === activeSizeBucket)[0]['relevantMediaTypes']; + } + return []; } function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { @@ -345,7 +352,10 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src if (bid.sizeConfig) { const relevantMediaTypes = getRelevantMediaTypesForBidder(bid.sizeConfig, activeViewport); - if (relevantMediaTypes[0] !== 'none') { + if (relevantMediaTypes.length === 0) { + logError(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} has not configured sizeConfig property correctly. This bidder won't be eligible for sizeConfig checks and will remain active.`); + bid = Object.assign({}, bid); + } else if (relevantMediaTypes[0] !== 'none') { const bidderMediaTypes = Object .keys(transformedMediaTypes) .filter(mt => relevantMediaTypes.indexOf(mt) > -1) From 72b5607ad0d75de18aeb9161a8b0742dbf092cca Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 23:05:23 +0530 Subject: [PATCH 18/41] bugfix: checkBidderSizeConfigFormat --- modules/sizeMappingV2.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 9413d21dd43..7d86be73cf4 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -119,10 +119,11 @@ function checkAdUnitSetupHook(adUnits) { } if (mediaTypes && mediaTypes.native) { + const native = mediaTypes.native; adUnit = validateNativeMediaType(adUnit); if (mediaTypes.native.sizeConfig) { - nativeObj.sizeConfig.forEach(config => { + native.sizeConfig.forEach(config => { // verify if all config objects include "minViewPort" and "active" property. // if not, remove the mediaTypes.native object const keys = Object.keys(config); @@ -156,12 +157,12 @@ function checkBidderSizeConfigFormat(sizeConfig) { if (Array.isArray(sizeConfig)) { sizeConfig.forEach(config => { const keys = Object.keys(config); - if ((includes(keys, 'minViewPort') && includes(keys, 'relevantMediaTypes'))) { - if (isArrayOfNums(config.minViewPort, 2) && - Array.isArray(config.relevantMediaTypes) && - config.relevantMediaTypes.every(mt => (includes(['banner', 'video', 'native'], mt)) || (mt === 'none'))) { - didCheckPass = didCheckPass && true; - } + if ((includes(keys, 'minViewPort') && + includes(keys, 'relevantMediaTypes')) && + isArrayOfNums(config.minViewPort, 2) && + Array.isArray(config.relevantMediaTypes) && + config.relevantMediaTypes.every(mt => (includes(['banner', 'video', 'native'], mt)) || (mt === 'none'))) { + didCheckPass = didCheckPass && true; } else { didCheckPass = false; } @@ -280,9 +281,9 @@ function getFilteredMediaTypes(mediaTypes) { function isSizeConfigActivated(mediaType, sizeConfig) { switch (mediaType) { case 'banner': - return sizeConfig.sizes && sizeConfig.sizes[0].length > 0; + return sizeConfig.sizes && sizeConfig.sizes.length > 0; case 'video': - return sizeConfig.playerSize && sizeConfig.playerSize[0].length > 0; + return sizeConfig.playerSize && sizeConfig.playerSize.length > 0; case 'native': return sizeConfig.active; default: @@ -365,7 +366,7 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }, {}); if (Object.keys(bidderMediaTypes).length > 0) { - bid = Object.assign({}, bid, bidderMediaTypes); + bid = Object.assign({}, bid, { mediaTypes: bidderMediaTypes }); } else { logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); return bids; From d5548660238676ac6e949842f53950a1454b87f6 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 23:06:15 +0530 Subject: [PATCH 19/41] add more scenarios for testing sizeMapping V2 --- .../gpt/sizeMappingv2/instream_video_ad.html | 108 ++++++++++++++ .../gpt/sizeMappingv2/multi_format_ad.html | 130 ++++++++++++++++ ...ti_format_ad_with_bidder_level_checks.html | 139 ++++++++++++++++++ .../only_bidder_level_checks.html | 122 +++++++++++++++ 4 files changed, 499 insertions(+) create mode 100644 integrationExamples/gpt/sizeMappingv2/instream_video_ad.html create mode 100644 integrationExamples/gpt/sizeMappingv2/multi_format_ad.html create mode 100644 integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html create mode 100644 integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html diff --git a/integrationExamples/gpt/sizeMappingv2/instream_video_ad.html b/integrationExamples/gpt/sizeMappingv2/instream_video_ad.html new file mode 100644 index 00000000000..4b8141c9df3 --- /dev/null +++ b/integrationExamples/gpt/sizeMappingv2/instream_video_ad.html @@ -0,0 +1,108 @@ + + + + + + + + Prebid.js JW Platform Example + + + + +

Prebid Video - JW Platform

+
+
+
+ + + + + + + diff --git a/integrationExamples/gpt/sizeMappingv2/multi_format_ad.html b/integrationExamples/gpt/sizeMappingv2/multi_format_ad.html new file mode 100644 index 00000000000..ef79001342a --- /dev/null +++ b/integrationExamples/gpt/sizeMappingv2/multi_format_ad.html @@ -0,0 +1,130 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + diff --git a/integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html b/integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html new file mode 100644 index 00000000000..3996991f8d3 --- /dev/null +++ b/integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html @@ -0,0 +1,139 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + diff --git a/integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html b/integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html new file mode 100644 index 00000000000..2ba8903c84f --- /dev/null +++ b/integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html @@ -0,0 +1,122 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + From ff0d348329637a57c469f5bfdc610a8dfb986845 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sun, 8 Dec 2019 23:12:52 +0530 Subject: [PATCH 20/41] small docs changes --- modules/sizeMappingV2.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 7d86be73cf4..d4aafb92edb 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -184,9 +184,10 @@ getHook('getBids').before(function (fn, bidderInfo) { }); /** - * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active based on label checks on the Ad unit/Bid object + * Given an Ad Unit or a Bid as an input, returns a boolean telling if the Ad Unit/ Bid is active based on label checks * @param {Object} bidOrAdUnit Either the Ad Unit object or the Bid object * @param {Array} activeLabels List of active labels passed as an argument to pbjs.requestBids function + * @param {string} adUnitCode Unique string identifier for an Ad Unit. * @returns {boolean} Represents if the Ad Unit or the Bid is active or not */ function isLabelActivated(bidOrAdUnit, activeLabels, adUnitCode) { From 0c30a75d7811bedb9ca9f5d956418462dfb6be29 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Wed, 11 Dec 2019 17:50:14 +0530 Subject: [PATCH 21/41] feedback1 changes --- modules/sizeMappingV2.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index d4aafb92edb..255aab9d527 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -1,5 +1,6 @@ import { flatten, + deepClone, deepAccess, getDefinedParams, getUniqueIdentifierStr, @@ -32,9 +33,6 @@ function isUsingNewSizeMapping(adUnits, auctionId) { if (adUnit.mediaTypes[mediaType].sizeConfig) { if (isUsingSizeMappingBool === false) { isUsingSizeMappingBool = true; - if (auctionId) { - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingBool; - } } } }); @@ -44,14 +42,14 @@ function isUsingNewSizeMapping(adUnits, auctionId) { if (bidder.sizeConfig) { if (isUsingSizeMappingBool === false) { isUsingSizeMappingBool = true; - if (auctionId) { - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingBool; - } } } }); } }); + if (auctionId) { + _sizeMappingUsageMap[auctionId] = isUsingSizeMappingBool; + } return isUsingSizeMappingBool; } @@ -81,15 +79,9 @@ function checkAdUnitSetupHook(adUnits) { } }); } else { - logError('Detected a mediaTypes.banner object did not include required property sizes. Removing invalid mediaTypes.banner object from request.'); + logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from request.'); delete adUnit.mediaTypes.banner; } - } else if (adUnit.sizes) { - logWarn('Usage of adUnits.sizes will eventually be deprecated. Please define size dimensions within the corresponding area of the mediaTypes. (eg mediaTypes.banner.sizes).'); - const bannerSizes = validateSizes(adUnit.sizes); - if (bannerSizes.length > 0) { - adUnit.sizes = bannerSizes; - } } if (mediaTypes && mediaTypes.video) { @@ -228,7 +220,7 @@ function getFilteredMediaTypes(mediaTypes) { activeViewportHeight, transformedMediaTypes; - transformedMediaTypes = Object.assign({}, mediaTypes); + transformedMediaTypes = deepClone(mediaTypes); let activeSizeBucket = { banner: undefined, @@ -259,6 +251,9 @@ function getFilteredMediaTypes(mediaTypes) { }; if (transformedMediaTypes[mediaType].filteredSizeConfig.length > 0) { + // map sizes or playerSize property in filteredSizeConfig object to transformedMediaTypes.banner.sizes if mediaType is banner + // or transformedMediaTypes.video.playerSize if the mediaType in video. + // doesn't apply to native mediaType since native doesn't have any property defining 'sizes' or 'playerSize'. if (mediaType !== 'native') { transformedMediaTypes[mediaType][config[mediaType]] = transformedMediaTypes[mediaType].filteredSizeConfig[0][config[mediaType]]; } From 613a830b1b563ede653bac8da17ed7a1d7153db1 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 12 Dec 2019 12:58:43 +0530 Subject: [PATCH 22/41] modify logic for bailing out --- modules/sizeMappingV2.js | 14 +++++++++++--- src/adapterManager.js | 7 +------ src/prebid.js | 5 +---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 255aab9d527..b749a61b0dd 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -138,8 +138,12 @@ function checkAdUnitSetupHook(adUnits) { getHook('checkAdUnitSetup').before(function (fn, adUnits) { const usingNewSizeMapping = isUsingNewSizeMapping(adUnits); if (usingNewSizeMapping) { - return fn.call(this, adUnits, checkAdUnitSetupHook(adUnits)); + // if adUnits are found using the sizeMappingV2 spec, we run additional checks on them for checking the validity of sizeConfig object + // in addition to running the base checks on the mediaType object and return the adUnit without calling the base function. + adUnits = checkAdUnitSetupHook(adUnits); + return fn.bail(adUnits); } else { + // if presence of sizeMappingV2 spec is not detected on adUnits, we default back to the original checks defined in the base function. return fn.call(this, adUnits); } }); @@ -164,13 +168,17 @@ function checkBidderSizeConfigFormat(sizeConfig) { } getHook('getBids').before(function (fn, bidderInfo) { - // check if the adUnit is using sizeMappingV1 specs or sizeMappingV2 specs. + // check if the adUnit is using sizeMappingV2 specs and store the result in _sizeMappingUsageMap. if (typeof _sizeMappingUsageMap[bidderInfo.auctionId] === 'undefined') { isUsingNewSizeMapping(bidderInfo.adUnits, bidderInfo.auctionId); } if (_sizeMappingUsageMap[bidderInfo.auctionId]) { - return fn.call(this, bidderInfo, getBids(bidderInfo)); + // if adUnit is found using sizeMappingV2 specs, run the getBids function which processes the sizeConfig object + // and returns the bids array for a particular bidder. + const bids = getBids(bidderInfo); + return fn.bail(bids); } else { + // if not using sizeMappingV2, default back to the getBids function defined in adapterManager. return fn.call(this, bidderInfo); } }); diff --git a/src/adapterManager.js b/src/adapterManager.js index d1addc797b8..cead3dd6b1f 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -36,12 +36,7 @@ var _analyticsRegistry = {}; * @property {Array} activeLabels the labels specified as being active by requestBids */ -function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}, bidsInfoFromSizeMappingV2) { - // if the bids information is returned from sizeMappingV2 module, just return that. - if (bidsInfoFromSizeMappingV2) { - return bidsInfoFromSizeMappingV2; - } - +function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) { return adUnits.reduce((result, adUnit) => { let { active, diff --git a/src/prebid.js b/src/prebid.js index 3b4b1ee4ab6..85fbce15cd3 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -132,10 +132,7 @@ export function validateNativeMediaType(adUnit) { return adUnit; } -export const checkAdUnitSetup = hook('sync', function (adUnits, filteredAdUnitsFromSizeMappingV2) { - if (filteredAdUnitsFromSizeMappingV2) { - return filteredAdUnitsFromSizeMappingV2; - } +export const checkAdUnitSetup = hook('sync', function (adUnits) { adUnits.forEach(adUnit => { const mediaTypes = adUnit.mediaTypes; if (mediaTypes && mediaTypes.banner) { From 4040fad334186f972b5d59681602012fe02c81f3 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 12 Dec 2019 13:11:05 +0530 Subject: [PATCH 23/41] add module description --- modules/sizeMappingV2.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index b749a61b0dd..e135ae3aa33 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -1,3 +1,8 @@ +/** + * This modules adds support for the new Size Mapping spec described here. https://github.com/prebid/Prebid.js/issues/4129 + * This implementation replaces global sizeConfig with a adUnit/bidder level sizeConfig with support for labels. + */ + import { flatten, deepClone, From 1efc71c3b0e81f60777c7ddce304be975e85617c Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 13 Dec 2019 14:43:32 +0530 Subject: [PATCH 24/41] refactor isUsingNewSizeMapping function by making it a pure function --- modules/sizeMappingV2.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index e135ae3aa33..ae0109543ec 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -29,7 +29,7 @@ import { // Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. const _sizeMappingUsageMap = {}; -function isUsingNewSizeMapping(adUnits, auctionId) { +function isUsingNewSizeMapping(adUnits) { let isUsingSizeMappingBool = false; adUnits.forEach(adUnit => { if (adUnit.mediaTypes) { @@ -52,9 +52,6 @@ function isUsingNewSizeMapping(adUnits, auctionId) { }); } }); - if (auctionId) { - _sizeMappingUsageMap[auctionId] = isUsingSizeMappingBool; - } return isUsingSizeMappingBool; } @@ -175,7 +172,10 @@ function checkBidderSizeConfigFormat(sizeConfig) { getHook('getBids').before(function (fn, bidderInfo) { // check if the adUnit is using sizeMappingV2 specs and store the result in _sizeMappingUsageMap. if (typeof _sizeMappingUsageMap[bidderInfo.auctionId] === 'undefined') { - isUsingNewSizeMapping(bidderInfo.adUnits, bidderInfo.auctionId); + const isUsingSizeMappingBool = isUsingNewSizeMapping(bidderInfo.adUnits); + + // populate _sizeMappingUsageMap for the first time for a particular auction + _sizeMappingUsageMap[bidderInfo.auctionId] = isUsingSizeMappingBool; } if (_sizeMappingUsageMap[bidderInfo.auctionId]) { // if adUnit is found using sizeMappingV2 specs, run the getBids function which processes the sizeConfig object From 723c2980fa41fb3b60c0cd2f4743fda419be66f6 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 13 Dec 2019 14:48:08 +0530 Subject: [PATCH 25/41] add unit test case for isUsingNewSizeMapping function --- modules/sizeMappingV2.js | 4 +- test/spec/modules/sizeMappingV2_spec.js | 167 ++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 test/spec/modules/sizeMappingV2_spec.js diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index ae0109543ec..22ac5f7888a 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -29,7 +29,7 @@ import { // Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. const _sizeMappingUsageMap = {}; -function isUsingNewSizeMapping(adUnits) { +export function isUsingNewSizeMapping(adUnits) { let isUsingSizeMappingBool = false; adUnits.forEach(adUnit => { if (adUnit.mediaTypes) { @@ -42,7 +42,7 @@ function isUsingNewSizeMapping(adUnits) { } }); - // checks for the presence of sizeConfig property at the adUnit.bids.bidder object + // checks for the presence of sizeConfig property at the adUnit.bids[].bidder object adUnit.bids.forEach(bidder => { if (bidder.sizeConfig) { if (isUsingSizeMappingBool === false) { diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js new file mode 100644 index 00000000000..dee9977239d --- /dev/null +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -0,0 +1,167 @@ +import { expect } from 'chai'; +import { deepClone } from '../../../src/utils'; + +import { isUsingNewSizeMapping } from '../../../modules/sizeMappingV2'; + +const AD_UNITS = [{ + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, // remove if < 750px + { minViewPort: [750, 0], sizes: [[300, 250], [300, 600]] }, // between 750px and 1199px + { minViewPort: [1200, 0], sizes: [[970, 90], [728, 90], [300, 250]] }, // between 1200px and 1599px + { minViewPort: [1600, 0], sizes: [[1000, 300], [970, 90], [728, 90], [300, 250]] } // greater than 1600px + ] + }, + video: { + context: 'instream', + sizeConfig: [ + { minViewPort: [0, 0], playerSize: [] }, + { minViewPort: [800, 0], playerSize: [[640, 400]] }, + { minViewPort: [1200, 0], playerSize: [] } + ] + }, + native: { + image: { + required: true, + sizes: [150, 50] + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + }, + sizeConfig: [ + { minViewPort: [0, 0], active: false }, + { minViewPort: [600, 0], active: true }, + { minViewPort: [1000, 0], active: false } + ] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }] +}, { + code: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }, { + bidder: 'rubicon', + params: { + accountId: 14062, + siteId: 70608, + zoneId: 498816 + }, + sizeConfig: [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [800, 0], relevantMediaTypes: ['banner'] }, + { minViewPort: [1600, 0], relevantMediaTypes: ['none'] } + ] + }] +}]; + +// deletes the sizeConfig property from the adUnits either at the Ad Unit level or at the Bids level or at both. +function deleteSizeConfig(adUnits, config) { + const { adUnitLevel, bidsLevel } = config; + adUnits.forEach(adUnit => { + const mediaTypes = adUnit.mediaTypes; + if (adUnitLevel) { + Object.keys(mediaTypes).forEach(mediaType => { + if (mediaTypes[mediaType].sizeConfig) { + delete adUnit.mediaTypes[mediaType].sizeConfig; + if (mediaType === 'banner') { + adUnit.mediaTypes[mediaType].sizes = [[300, 600], [300, 200]]; + } else if (mediaType === 'video') { + adUnit.mediaTypes[mediaType].playerSize = [[640, 400]]; + } + } + }); + } + + if (bidsLevel) { + adUnit.bids.forEach(bid => { + if (bid.sizeConfig) { + delete bid.sizeConfig; + } + }); + } + }); + return adUnits; +} + +describe.only('sizeMappingV2', function () { + describe('isUsingNewSizeMaping(adUntis, auctionId)', function () { + it('should return "false" if sizeConfig is not declared both at the adUnits level and the bids level', function () { + let adUnits = deepClone(AD_UNITS); + + // delete the sizeConfig property from AD_UNITS object at the Ad Unit and the Bids level both. + adUnits = deleteSizeConfig(adUnits, { adUnitLevel: true, bidsLevel: true }); + const usingNewSizeMappingBool = isUsingNewSizeMapping(adUnits); + + // isUsingNewSizeMapping should return false because sizeConfig object is not present both at the Ad Units and the Bids level + // for all the adUnits that are checked. + expect(usingNewSizeMappingBool).to.be.false; + }); + + it('should return "true" if sizeConfig is declared at the adUnits level but not at the bids level', function () { + let adUnits = deepClone(AD_UNITS); + + // delete the sizeConfig property from AD_UNITS object at the Bids level but not at the Ad Unit level. + adUnits = deleteSizeConfig(adUnits, { adUnitLevel: false, bidsLevel: true }); + const usingNewSizeMappingBool = isUsingNewSizeMapping(adUnits); + + // isUsingNewSizeMapping should return true because sizeConfig object is present at the Ad Units level but not at the + // Bids level. + expect(usingNewSizeMappingBool).to.be.true; + }); + + it('should return "true" if sizeConfig is declared at the bids level but not at the adUnits level', function () { + let adUnits = deepClone(AD_UNITS); + + // delete the sizeConfig property from AD_UNITS object at the Ad Unit level but not at the Bids level. + adUnits = deleteSizeConfig(adUnits, { adUnitLevel: true, bidsLevel: false }); + const usingNewSizeMappingBool = isUsingNewSizeMapping(adUnits); + + // isUsingNewSizeMapping should return true because sizeConfig object is present at the Bids level but not at the + // Ad Unit level. + expect(usingNewSizeMappingBool).to.be.true; + }); + + it('should return "true" if sizeConfig is declared both at the adUnits level and at the bids level', function() { + let adUnits = deepClone(AD_UNITS); + + const usingNewSizeMappingBool = isUsingNewSizeMapping(adUnits); + + // isUsingNewSizeMapping should return true because sizeConfig object is present both at the Ad Unit level and at the + // Bids level. + expect(usingNewSizeMappingBool).to.be.true; + }); + }); +}); From 4c20b1b8499eb7a666ab823b9964a53561d85fec Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 13 Dec 2019 21:18:12 +0530 Subject: [PATCH 26/41] made adUnit checks more robusts and fully make adUnit.mediaTypes mandatory --- modules/sizeMappingV2.js | 126 +++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 1ad21403f5f..2dee40253e1 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -29,6 +29,8 @@ import { // Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. const _sizeMappingUsageMap = {}; +// returns "true" if atleast one of the adUnit in the adUnits array has declared a Ad Unit or(and) Bid level sizeConfig +// returns "false" otherwise export function isUsingNewSizeMapping(adUnits) { let isUsingSizeMappingBool = false; adUnits.forEach(adUnit => { @@ -55,64 +57,99 @@ export function isUsingNewSizeMapping(adUnits) { return isUsingSizeMappingBool; } +// returns "adUnits" array which have passed sizeConfig validation checks in addition to mediaTypes checks +// deletes properties from adUnit which fail validation. function checkAdUnitSetupHook(adUnits) { - adUnits.forEach(adUnit => { + return adUnits.filter(adUnit => { const mediaTypes = adUnit.mediaTypes; - if (mediaTypes && mediaTypes.banner) { + if (!mediaTypes || Object.keys(mediaTypes).length === 0) { + logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); + return false; + } + + if (mediaTypes.banner) { const banner = mediaTypes.banner; if (banner.sizes) { adUnit = validateBannerMediaType(adUnit); - } else if (banner.sizeConfig && Array.isArray(banner.sizeConfig)) { - banner.sizeConfig.forEach(config => { - // verify if all config objects include "minViewPort" and "sizes" property. - // if not, remove the mediaTypes.banner object - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is missing required property minViewPort or sizes or both. Removing the invalid mediaTypes.banner from request.`); - return delete adUnit.mediaTypes.banner; - } - // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. - const bannerSizes = validateSizes(config.sizes); - if (isArrayOfNums(config.minViewPort, 2)) { - config.sizes = bannerSizes; - } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has properties minViewPort or sizes decalared with invalid values. Removing the invalid object mediaTypes.banner from request.`); - return delete adUnit.mediaTypes.banner; - } - }); + } else if (banner.sizeConfig) { + if (Array.isArray(banner.sizeConfig)) { + banner.sizeConfig.forEach(config => { + // verify if all config objects include "minViewPort" and "sizes" property. + // if not, remove the mediaTypes.banner object + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is missing required property minViewPort or sizes or both. Removing the invalid mediaTypes.banner from Ad Unit.`); + return delete adUnit.mediaTypes.banner; + } + + // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. + const bannerSizes = validateSizes(config.sizes); + if (isArrayOfNums(config.minViewPort, 2)) { + if (config.sizes.length > 0 && bannerSizes.length > 0) { + config.sizes = bannerSizes; + } else if (config.sizes.length === 0) { + // If a size bucket doesn't have any sizes, sizes is an empty array, i.e. sizes: []. This check takes care of that. + config.sizes = [config.sizes]; + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket. Removing the invalid object mediaTypes.banner from Ad Unit.`); + return delete adUnit.mediaTypes.banner; + } + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0]. Removing the invalid object mediaTypes.banner from Ad Unit.`); + return delete adUnit.mediaTypes.banner; + } + }); + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); + } } else { - logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from request.'); + logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); delete adUnit.mediaTypes.banner; } } - if (mediaTypes && mediaTypes.video) { + if (mediaTypes.video) { const video = mediaTypes.video; if (video.playerSize) { adUnit = validateVideoMediaType(adUnit); } else if (video.sizeConfig) { - video.sizeConfig.forEach(config => { - // verify if all config objects include "minViewPort" and "playerSize" property. - // if not, remove the mediaTypes.video object - const keys = Object.keys(config); - if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from request.`); - return delete adUnit.mediaTypes.video.sizeConfig; - } - // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. - let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; - const videoSizes = validateSizes(config.playerSize, tarPlayerSizeLen); - if (isArrayOfNums(config.minViewPort, 2)) { - config.playerSize = videoSizes; - } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has properties minViewPort or playerSize decalared with invalid values. Removing the invalid property mediaTypes.video.sizeConfig from request.`); - return delete adUnit.mediaTypes.video.sizeConfig; - } - }); + if (Array.isArray(video.sizeConfig)) { + video.sizeConfig.forEach(config => { + // verify if all config objects include "minViewPort" and "playerSize" property. + // if not, remove the mediaTypes.video object + const keys = Object.keys(config); + if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + return delete adUnit.mediaTypes.video.sizeConfig; + } + // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. + let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; + const videoSizes = validateSizes(config.playerSize, tarPlayerSizeLen); + if (isArrayOfNums(config.minViewPort, 2)) { + if (tarPlayerSizeLen === 2) { + logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); + } + if (config.playerSize.length > 0 && videoSizes.length > 0) { + config.playerSize = videoSizes; + } else if (config.playerSize.length === 0) { + // If a size bucket doesn't have any playerSize, playerSize is an empty array, i.e. playerSize: []. This check takes care of that. + config.playerSize = [config.playerSize]; + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + } + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0]. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + return delete adUnit.mediaTypes.video.sizeConfig; + } + }); + } else { + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + return delete adUnit.mediaTypes.video.sizeConfig; + } } } - if (mediaTypes && mediaTypes.native) { + if (mediaTypes.native) { const native = mediaTypes.native; adUnit = validateNativeMediaType(adUnit); @@ -122,19 +159,20 @@ function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.native object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from request.`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); return delete adUnit.mediaTypes.native.sizeConfig; } if (!(isArrayOfNums(config.minViewPort, 2) && typeof config.active === 'boolean')) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from request.`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); return delete adUnit.mediaTypes.native.sizeConfig; } }); } } + + return true; }); - return adUnits; } getHook('checkAdUnitSetup').before(function (fn, adUnits) { From f0a29ca73b05fde12a0c1667b63783689b2772b3 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Tue, 17 Dec 2019 17:12:10 +0530 Subject: [PATCH 27/41] remove redundancy in checAdUnitSetupHook --- modules/sizeMappingV2.js | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 2dee40253e1..67b00dba9b1 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -59,7 +59,7 @@ export function isUsingNewSizeMapping(adUnits) { // returns "adUnits" array which have passed sizeConfig validation checks in addition to mediaTypes checks // deletes properties from adUnit which fail validation. -function checkAdUnitSetupHook(adUnits) { +export function checkAdUnitSetupHook(adUnits) { return adUnits.filter(adUnit => { const mediaTypes = adUnit.mediaTypes; if (!mediaTypes || Object.keys(mediaTypes).length === 0) { @@ -73,13 +73,15 @@ function checkAdUnitSetupHook(adUnits) { adUnit = validateBannerMediaType(adUnit); } else if (banner.sizeConfig) { if (Array.isArray(banner.sizeConfig)) { - banner.sizeConfig.forEach(config => { + let deleteBannerMediaType = false; + banner.sizeConfig.forEach((config, index) => { // verify if all config objects include "minViewPort" and "sizes" property. // if not, remove the mediaTypes.banner object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is missing required property minViewPort or sizes or both. Removing the invalid mediaTypes.banner from Ad Unit.`); - return delete adUnit.mediaTypes.banner; + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] is missing required property minViewPort or sizes or both.`); + deleteBannerMediaType = true; + return; } // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. @@ -91,14 +93,18 @@ function checkAdUnitSetupHook(adUnits) { // If a size bucket doesn't have any sizes, sizes is an empty array, i.e. sizes: []. This check takes care of that. config.sizes = [config.sizes]; } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket. Removing the invalid object mediaTypes.banner from Ad Unit.`); - return delete adUnit.mediaTypes.banner; + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); + deleteBannerMediaType = true; } } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0]. Removing the invalid object mediaTypes.banner from Ad Unit.`); - return delete adUnit.mediaTypes.banner; + logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + deleteBannerMediaType = true; } }); + if (deleteBannerMediaType) { + logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.banner has been removed due to error in sizeConfig.`); + delete adUnit.mediaTypes.banner; + } } else { logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); } @@ -114,13 +120,15 @@ function checkAdUnitSetupHook(adUnits) { adUnit = validateVideoMediaType(adUnit); } else if (video.sizeConfig) { if (Array.isArray(video.sizeConfig)) { - video.sizeConfig.forEach(config => { + let deleteVideoMediaType = false; + video.sizeConfig.forEach((config, index) => { // verify if all config objects include "minViewPort" and "playerSize" property. // if not, remove the mediaTypes.video object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); - return delete adUnit.mediaTypes.video.sizeConfig; + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + deleteVideoMediaType = true; + return; } // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; @@ -135,13 +143,17 @@ function checkAdUnitSetupHook(adUnits) { // If a size bucket doesn't have any playerSize, playerSize is an empty array, i.e. playerSize: []. This check takes care of that. config.playerSize = [config.playerSize]; } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); } } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0]. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); - return delete adUnit.mediaTypes.video.sizeConfig; + logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + deleteVideoMediaType = true; } }); + if (deleteVideoMediaType) { + logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has been removed due to error in sizeConfig.`); + delete adUnit.mediaTypes.video.sizeConfig; + } } else { logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); return delete adUnit.mediaTypes.video.sizeConfig; From 2165a8b7a1207a5c5b405dc40753411efd7afb71 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 19 Dec 2019 17:49:05 +0530 Subject: [PATCH 28/41] add banner units test cases for checkAdUnitSetupHook function --- modules/sizeMappingV2.js | 16 +- src/prebid.js | 15 +- test/spec/modules/sizeMappingV2_spec.js | 274 +++++++++++++++++++++++- 3 files changed, 286 insertions(+), 19 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 67b00dba9b1..68b14828532 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -20,10 +20,7 @@ import { adunitCounter } from '../src/adUnits'; import includes from 'core-js/library/fn/array/includes'; import { getHook } from '../src/hook'; import { - validateBannerMediaType, - validateVideoMediaType, - validateNativeMediaType, - validateSizes + adUnitSetupChecks } from '../src/prebid'; // Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. @@ -70,7 +67,7 @@ export function checkAdUnitSetupHook(adUnits) { if (mediaTypes.banner) { const banner = mediaTypes.banner; if (banner.sizes) { - adUnit = validateBannerMediaType(adUnit); + adUnit = adUnitSetupChecks.validateBannerMediaType(adUnit); } else if (banner.sizeConfig) { if (Array.isArray(banner.sizeConfig)) { let deleteBannerMediaType = false; @@ -85,7 +82,7 @@ export function checkAdUnitSetupHook(adUnits) { } // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. - const bannerSizes = validateSizes(config.sizes); + const bannerSizes = adUnitSetupChecks.validateSizes(config.sizes); if (isArrayOfNums(config.minViewPort, 2)) { if (config.sizes.length > 0 && bannerSizes.length > 0) { config.sizes = bannerSizes; @@ -107,6 +104,7 @@ export function checkAdUnitSetupHook(adUnits) { } } else { logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); + delete adUnit.mediaTypes.banner; } } else { logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); @@ -117,7 +115,7 @@ export function checkAdUnitSetupHook(adUnits) { if (mediaTypes.video) { const video = mediaTypes.video; if (video.playerSize) { - adUnit = validateVideoMediaType(adUnit); + adUnit = adUnitSetupChecks.validateVideoMediaType(adUnit); } else if (video.sizeConfig) { if (Array.isArray(video.sizeConfig)) { let deleteVideoMediaType = false; @@ -132,7 +130,7 @@ export function checkAdUnitSetupHook(adUnits) { } // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; - const videoSizes = validateSizes(config.playerSize, tarPlayerSizeLen); + const videoSizes = adUnitSetupChecks.validateSizes(config.playerSize, tarPlayerSizeLen); if (isArrayOfNums(config.minViewPort, 2)) { if (tarPlayerSizeLen === 2) { logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); @@ -163,7 +161,7 @@ export function checkAdUnitSetupHook(adUnits) { if (mediaTypes.native) { const native = mediaTypes.native; - adUnit = validateNativeMediaType(adUnit); + adUnit = adUnitSetupChecks.validateNativeMediaType(adUnit); if (mediaTypes.native.sizeConfig) { native.sizeConfig.forEach(config => { diff --git a/src/prebid.js b/src/prebid.js index 9969aa36ea4..a22a46c66b3 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -68,7 +68,7 @@ function setRenderSize(doc, width, height) { } } -export function validateSizes(sizes, targLength) { +function validateSizes(sizes, targLength) { let cleanSizes = []; if (utils.isArray(sizes) && ((targLength) ? sizes.length === targLength : sizes.length > 0)) { // check if an array of arrays or array of numbers @@ -81,7 +81,7 @@ export function validateSizes(sizes, targLength) { return cleanSizes; } -export function validateBannerMediaType(adUnit) { +function validateBannerMediaType(adUnit) { const banner = adUnit.mediaTypes.banner; const bannerSizes = validateSizes(banner.sizes); if (bannerSizes.length > 0) { @@ -95,7 +95,7 @@ export function validateBannerMediaType(adUnit) { return adUnit; } -export function validateVideoMediaType(adUnit) { +function validateVideoMediaType(adUnit) { const video = adUnit.mediaTypes.video; let tarPlayerSizeLen = (typeof video.playerSize[0] === 'number') ? 2 : 1; @@ -114,7 +114,7 @@ export function validateVideoMediaType(adUnit) { return adUnit; } -export function validateNativeMediaType(adUnit) { +function validateNativeMediaType(adUnit) { const native = adUnit.mediaTypes.native; if (native.image && native.image.sizes && !Array.isArray(native.image.sizes)) { utils.logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.'); @@ -131,6 +131,13 @@ export function validateNativeMediaType(adUnit) { return adUnit; } +export const adUnitSetupChecks = { + validateBannerMediaType, + validateVideoMediaType, + validateNativeMediaType, + validateSizes +}; + export const checkAdUnitSetup = hook('sync', function (adUnits) { return adUnits.filter(adUnit => { const mediaTypes = adUnit.mediaTypes; diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index f1de74753eb..14546f9c908 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -1,7 +1,10 @@ import { expect } from 'chai'; -import { deepClone } from '../../../src/utils'; +import * as utils from '../../../src/utils'; -import { isUsingNewSizeMapping } from '../../../modules/sizeMappingV2'; +import { isUsingNewSizeMapping, checkAdUnitSetupHook } from '../../../modules/sizeMappingV2'; +// import { validateBannerMediaType } from '../../../src/prebid'; + +import { adUnitSetupChecks } from '../../../src/prebid'; const AD_UNITS = [{ code: 'div-gpt-ad-1460505748561-0', @@ -119,7 +122,7 @@ function deleteSizeConfig(adUnits, config) { describe('sizeMappingV2', function () { describe('isUsingNewSizeMaping(adUntis, auctionId)', function () { it('should return "false" if sizeConfig is not declared both at the adUnits level and the bids level', function () { - let adUnits = deepClone(AD_UNITS); + let adUnits = utils.deepClone(AD_UNITS); // delete the sizeConfig property from AD_UNITS object at the Ad Unit and the Bids level both. adUnits = deleteSizeConfig(adUnits, { adUnitLevel: true, bidsLevel: true }); @@ -131,7 +134,7 @@ describe('sizeMappingV2', function () { }); it('should return "true" if sizeConfig is declared at the adUnits level but not at the bids level', function () { - let adUnits = deepClone(AD_UNITS); + let adUnits = utils.deepClone(AD_UNITS); // delete the sizeConfig property from AD_UNITS object at the Bids level but not at the Ad Unit level. adUnits = deleteSizeConfig(adUnits, { adUnitLevel: false, bidsLevel: true }); @@ -143,7 +146,7 @@ describe('sizeMappingV2', function () { }); it('should return "true" if sizeConfig is declared at the bids level but not at the adUnits level', function () { - let adUnits = deepClone(AD_UNITS); + let adUnits = utils.deepClone(AD_UNITS); // delete the sizeConfig property from AD_UNITS object at the Ad Unit level but not at the Bids level. adUnits = deleteSizeConfig(adUnits, { adUnitLevel: true, bidsLevel: false }); @@ -155,7 +158,7 @@ describe('sizeMappingV2', function () { }); it('should return "true" if sizeConfig is declared both at the adUnits level and at the bids level', function() { - let adUnits = deepClone(AD_UNITS); + let adUnits = utils.deepClone(AD_UNITS); const usingNewSizeMappingBool = isUsingNewSizeMapping(adUnits); @@ -164,4 +167,263 @@ describe('sizeMappingV2', function () { expect(usingNewSizeMappingBool).to.be.true; }); }); + + describe('checkAdUnitSetupHook(adUnits)', function() { + beforeEach(function() { + sinon.spy(utils, 'logError'); + }); + + afterEach(function() { + utils.logError.restore(); + }); + + it('should filter out adUnit if it does not contain the required property mediaTypes', function() { + let adUnits = utils.deepClone(AD_UNITS); + delete adUnits[0].mediaTypes; + // before checkAdUnitSetupHook is called, the length of adUnits should be '2' + expect(adUnits.length).to.equal(2); + adUnits = checkAdUnitSetupHook(adUnits); + + // after checkAdUnitSetupHook is called, the length of adUnits should be '1' + expect(adUnits.length).to.equal(1); + expect(adUnits[0].code).to.equal('div-gpt-ad-1460505748561-1'); + }); + + it('should filter out adUnit if it has declared property mediaTypes with an empty object', function() { + let adUnits = utils.deepClone(AD_UNITS); + adUnits[0].mediaTypes = {}; + // before checkAdUnitSetupHook is called, the length of adUnits should be '2' + expect(adUnits.length).to.equal(2); + adUnits = checkAdUnitSetupHook(adUnits); + + // after checkAdUnitSetupHook is called, the length of adUnits should be '1' + expect(adUnits.length).to.equal(1); + expect(adUnits[0].code).to.equal('div-gpt-ad-1460505748561-1'); + }); + + it('should log an error message if Ad Unit does not contain the required property "mediaTypes"', function() { + let adUnits = utils.deepClone(AD_UNITS); + delete adUnits[0].mediaTypes; + + adUnits = checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, 'Detected adUnit.code \'div-gpt-ad-1460505748561-0\' did not have a \'mediaTypes\' object defined. This is a required field for the auction, so this adUnit has been removed.'); + }); + + describe('banner mediaTypes checks', function() { + it('should delete banner mediaType if it does not constain sizes or sizeConfig property', function() { + let adUnits = utils.deepClone(AD_UNITS); + delete adUnits[0].mediaTypes.banner.sizeConfig; + + // before checkAdUnitSetupHook is called, adUnits[0].mediaTypes.banner property should exist. + expect(adUnits[0].mediaTypes).to.have.property('banner'); + + adUnits = checkAdUnitSetupHook(adUnits); + + // after checkAdUnitSetupHook is called, adUnits[0].mediaTypes.banner property should not exist. + expect(adUnits[0].mediaTypes).to.not.have.property('banner'); + }); + + it('should log an error message if mediaTypes.banner does not contain "sizes" or "sizeConfig" property', function() { + let adUnits = utils.deepClone(AD_UNITS); + // deleteing the sizeConfig property from the first ad unit. + delete adUnits[0].mediaTypes.banner.sizeConfig; + + adUnits = checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, 'Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); + }); + + it('should call function "validateBannerMediaType" if mediaTypes.sizes is present', function() { + const adUnits = utils.deepClone(AD_UNITS); + const spy = sinon.spy(adUnitSetupChecks, 'validateBannerMediaType'); + + checkAdUnitSetupHook(adUnits); + + // since the second Ad Unit in AD_UNITS array uses mediaTypes.sizes, it should get called only once. + sinon.assert.callCount(spy, 1); + sinon.assert.calledWith(spy, adUnits[1]); + }); + + it('should delete mediaTypes.banner object if it\'s property sizeConfig is not declared as an array', function() { + const adUnits = utils.deepClone(AD_UNITS); + const badSizeConfig = { + minViewPort: [0, 0], sizes: [300, 400] + }; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook(adUnits), mediaTypes.banner should be defined + expect(adUnits[0].mediaTypes).to.have.property('banner'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook(adUnits), mediaTypes.banner property should be deleted + expect(validatedAdUnits.length).to.not.have.property('banner'); + }); + + it('should log an error message if sizeConfig property in mediaTypes.banner object is not declared as an array', function() { + const adUnits = utils.deepClone(AD_UNITS); + // badSizeConfig is NOT defined as an Array + const badSizeConfig = { + minViewPort: [0, 0], sizes: [300, 400] + }; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); + }); + + it('should delete mediaTypes.banner object if it\'s property sizeConfig does not contain the required properties "minViewPort" and "sizes"', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[2] does not contain the required "sizes" property + const badSizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [[300, 250], [300, 600]] }, + { minViewPort: [1200, 0] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook(adUnits), mediaTypes.banner should be defined + expect(adUnits[0].mediaTypes).to.have.property('banner'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook(adUnits), mediaTypes.banner property should be deleted + expect(validatedAdUnits.length).to.not.have.property('banner'); + }); + + it('should log an error message if sizeConfig property in mediaTypes.banner object does not contain the required properties "minViewPort" and "sizes"', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[2] does not contain the required "sizes" property + const badSizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [[300, 250], [300, 600]] }, + { minViewPort: [1200, 0] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[2] is missing required property minViewPort or sizes or both.`); + }); + + it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared minViewPort property which is NOT an Array of two integers', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[0].minViewPort is an Array of ONE inteter. It should be an array of two integers to be valid, like [0, 0] + const badSizeConfig = [ + { minViewPort: [0], sizes: [] }, + { minViewPort: [700, 0], sizes: [[300, 250], [300, 600]] }, + { minViewPort: [1200, 0], sizes: [[900, 700], [1000, 1200]] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook(adUnits), mediaTypes.banner should be defined + expect(adUnits[0].mediaTypes).to.have.property('banner'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook(adUnits), mediaTypes.banner property should be deleted + expect(validatedAdUnits.length).to.not.have.property('banner'); + }); + + it('should log an error message if sizeConfig has declared property minViewPort which is not an array of two integers', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[0].minViewPort is an Array of ONE inteter. It should be an array of two integers to be valid, like [0, 0] + const badSizeConfig = [ + { minViewPort: [0], sizes: [] }, + { minViewPort: [700, 0], sizes: [[300, 250], [300, 600]] }, + { minViewPort: [1200, 0], sizes: [[900, 700], [1000, 1200]] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[0] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + }); + + it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared sizes property which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[1].sizes is not declared in the correct format. It should be an Array of TWO integers. + const badSizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [300] }, + { minViewPort: [1200, 0], sizes: [[900, 700], [1000, 1200]] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook(adUnits), mediaTypes.banner should be defined + expect(adUnits[0].mediaTypes).to.have.property('banner'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook(adUnits), mediaTypes.banner property should be deleted + expect(validatedAdUnits.length).to.not.have.property('banner'); + }); + + it('should log an error message if sizeConfig has declared property sizes which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[1].sizes is not declared in the correct format. It should be an Array of TWO integers. + const badSizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [300] }, + { minViewPort: [1200, 0], sizes: [[900, 700], [1000, 1200]] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[1] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); + }); + + it('should convert sizeConfig.sizes to an array of array, i.e., [360, 600] to [[360, 600]]', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[1].sizes is declared as a Array of integers. Correct way to declare is to have it as an Array of Array of Integers, like, [[300, 250]] + // Although, the system won't throw error, it'll internall convert it to the format, [[300, 250]] + const badSizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [300, 600] }, + { minViewPort: [1200, 0], sizes: [[900, 700], [1000, 1200]] } + ]; + adUnits[0].mediaTypes.banner.sizeConfig = badSizeConfig; + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + expect(validatedAdUnits[0].mediaTypes.banner.sizeConfig[1].sizes).to.deep.equal([[300, 600]]); + }); + + it('should allow empty array declaration in sizeConfig.sizes to indicate "No valid sizes for this size bucket", and convert it to an array of array, i.e, [] to [[]]', function() { + const adUnits = utils.deepClone(AD_UNITS); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + expect(validatedAdUnits[0].mediaTypes.banner.sizeConfig[0].sizes).to.deep.equal([[]]); + }); + + it('should NOT delete mediaTypes.banner object if sizeConfig object is declared correctly', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // before calling checkAdUnitSetupHook, the mediaTypes.banner object should be present on both the Ad Units. + expect(adUnits[0].mediaTypes).to.have.property('banner'); + expect(adUnits[1].mediaTypes).to.have.property('banner'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook, the mediaTypes.banner object should still be present for both the Ad Units. + expect(adUnits[0].mediaTypes).to.have.property('banner'); + expect(adUnits[1].mediaTypes).to.have.property('banner'); + }); + }); + + // describe('video mediaTypes checks', function() { + // it('should delete property mediaTypes.video.sizeConfig', function() { + + // }); + // }); + }); }); From 5afc8d1d32e0e4fd460d3a4fabd9e55723f60eab Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Fri, 20 Dec 2019 12:31:41 +0530 Subject: [PATCH 29/41] add video and native mediaTypes units test for checkAdUnitSetupHook function --- modules/sizeMappingV2.js | 1 + test/spec/modules/sizeMappingV2_spec.js | 216 +++++++++++++++++++++++- 2 files changed, 212 insertions(+), 5 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 68b14828532..a843d49490f 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -142,6 +142,7 @@ export function checkAdUnitSetupHook(adUnits) { config.playerSize = [config.playerSize]; } else { logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); + deleteVideoMediaType = true; } } else { logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index 14546f9c908..729a085d4f9 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -2,7 +2,6 @@ import { expect } from 'chai'; import * as utils from '../../../src/utils'; import { isUsingNewSizeMapping, checkAdUnitSetupHook } from '../../../modules/sizeMappingV2'; -// import { validateBannerMediaType } from '../../../src/prebid'; import { adUnitSetupChecks } from '../../../src/prebid'; @@ -68,6 +67,10 @@ const AD_UNITS = [{ mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'instream', + playerSize: [300, 460] } }, bids: [{ @@ -420,10 +423,213 @@ describe('sizeMappingV2', function () { }); }); - // describe('video mediaTypes checks', function() { - // it('should delete property mediaTypes.video.sizeConfig', function() { + describe('video mediaTypes checks', function() { + it('should call function "validateVideoMediaType" if mediaTypes.video.playerSize is present in the Ad Unit', function() { + const adUnits = utils.deepClone(AD_UNITS); + + const spy = sinon.spy(adUnitSetupChecks, 'validateVideoMediaType'); + + checkAdUnitSetupHook(adUnits); + + // since adUntis[1].mediaTypes.video has defined property "playserSize", it should call function "validateVideoMediaType" only once + sinon.assert.callCount(spy, 1); + sinon.assert.calledWith(spy, adUnits[1]); + }); + + it('should delete mediaTypes.video.sizeConfig property if sizeConfig is not declared as an array', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig is declared as an object, it should have been an Array. + const badSizeConfig = { + minViewPort: [0, 0], playerSize: [640, 400] + }; + adUnits[0].mediaTypes.video.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be defined. + expect(adUnits[0].mediaTypes.video).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be deleted. + expect(validatedAdUnits[0].mediaTypes.video).to.not.have.property('sizeConfig'); + + // check if correct logError is written to the console. + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + }); + + it('should delete mediaTypes.video.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "playerSize"', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[0] doest not contain the required property "playerSize". + const badSizeConfig = [ + { minViewPort: [0, 0] }, + { minViewPort: [1200, 0], playerSize: [640, 400] } + ]; + adUnits[0].mediaTypes.video.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be defined. + expect(adUnits[0].mediaTypes.video).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be deleted. + expect(validatedAdUnits[0].mediaTypes.video).to.not.have.property('sizeConfig'); + + // check if correct logError is written to the console. + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[0] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + }); + + it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared minViewPort property which is NOT an Array of two integers', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[1].minViewPort is an Array of Integers. It should have been an Array of Array of Integers. + const badSizeConfig = [ + { minViewPort: [0, 0], playerSize: [] }, + { minViewPort: [1200], playerSize: [640, 400] } + ]; + adUnits[0].mediaTypes.video.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be defined. + expect(adUnits[0].mediaTypes.video).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be deleted. + expect(validatedAdUnits[0].mediaTypes.video).to.not.have.property('sizeConfig'); + + // check if correct logError is written to the console. + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[1] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + }); + + it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared "playerSize" property which is not in the format, [[vw1, vh1]], where vw is viewport width and vh is viewport height', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[0].playerSize property is declared incorrectly. + const badSizeConfig = [ + { minViewPort: [0, 0], playerSize: [600, 400, 500] }, + { minViewPort: [1200, 0], playerSize: [640, 400] } + ]; + adUnits[0].mediaTypes.video.sizeConfig = badSizeConfig; + + // before calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be defined. + expect(adUnits[0].mediaTypes.video).to.have.property('sizeConfig'); - // }); - // }); + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after calling checkAdUnitSetupHook, mediaTypes.video.sizeConfig property should be deleted. + expect(validatedAdUnits[0].mediaTypes.video).to.not.have.property('sizeConfig'); + + // check if correct logError is written to the console. + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[0] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); + }); + + it('should convert sizeConfig.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeConfig[] has declared "playerSize" as an Array of Intergers. It should be an Array of Array of Integers, like [[640, 400]]. + // Although, it won't throw an error if you declare it like this, but internally, the system will convert it to the format listed above. + const badSizeConfig = [ + { minViewPort: [0, 0], playerSize: [] }, + { minViewPort: [1200, 0], playerSize: [360, 600] } + ]; + adUnits[0].mediaTypes.video.sizeConfig = badSizeConfig; + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + expect(validatedAdUnits[0].mediaTypes.video.sizeConfig[0].playerSize).to.deep.equal([[]]); + expect(validatedAdUnits[0].mediaTypes.video.sizeConfig[1].playerSize).to.deep.equal([[360, 600]]); + }); + + it('should convert mediaTypes.video.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function() { + const adUnits = utils.deepClone(AD_UNITS); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + expect(validatedAdUnits[1].mediaTypes.video.playerSize).to.deep.equal([[300, 460]]); + }); + + it('should NOT delete mediaTypes.video.sizeConfig property if sizeConfig property is declared correctly', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // before checkAdUnitSetupHook is called + expect(adUnits[0].mediaTypes.video).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + + // after checkAdUnitSetupHook is called + expect(validatedAdUnits[0].mediaTypes.video).to.have.property('sizeConfig'); + }); + }); + + describe('native mediaTypes checks', function() { + it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined', function() { + const adUnits = utils.deepClone(AD_UNITS); + const spy = sinon.spy(adUnitSetupChecks, 'validateNativeMediaType'); + + checkAdUnitSetupHook(adUnits); + + sinon.assert.callCount(spy, 1); + }); + + it('should delete mediaTypes.native.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "active"', function() { + const adUnits = utils.deepClone(AD_UNITS); + // badSizeConfig[1] doesn't include required property "active" + const badSizeConfig = [ + { minViewPort: [0, 0], active: false }, + { minViewPort: [1200, 0] } + ]; + adUnits[0].mediaTypes.native.sizeConfig = badSizeConfig; + expect(adUnits[0].mediaTypes.native).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + expect(validatedAdUnits[0].mediaTypes.native).to.not.have.property('sizeConfig'); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + }); + + it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].minViewPort is NOT an array of TWO integers', function() { + const adUnits = utils.deepClone(AD_UNITS); + // badSizeConfig[0].minViewPort is an array of three integers. It should ideally be two integers. + const badSizeConfig = [ + { minViewPort: [0, 0, 0], active: false }, + { minViewPort: [1200, 0], active: true } + ]; + adUnits[0].mediaTypes.native.sizeConfig = badSizeConfig; + expect(adUnits[0].mediaTypes.native).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + expect(validatedAdUnits[0].mediaTypes.native).to.not.have.property('sizeConfig'); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + }); + + it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].active is NOT a Boolean', function() { + const adUnits = utils.deepClone(AD_UNITS); + + // badSizeCofnig[0].active is a String value, it should have been a boolean to be valid. + const badSizeConfig = [ + { minViewPort: [0, 0], active: 'false' }, + { minViewPort: [1200, 0], active: true } + ]; + adUnits[0].mediaTypes.native.sizeConfig = badSizeConfig; + expect(adUnits[0].mediaTypes.native).to.have.property('sizeConfig'); + + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + expect(validatedAdUnits[0].mediaTypes.native).to.not.have.property('sizeConfig'); + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + }); + + it('should NOT delete mediaTypes.native.sizeConfig property if sizeConfig property is declared correctly', function() { + const adUnits = utils.deepClone(AD_UNITS); + expect(adUnits[0].mediaTypes.native).to.have.property('sizeConfig'); + const validatedAdUnits = checkAdUnitSetupHook(adUnits); + expect(validatedAdUnits[0].mediaTypes.native).to.have.property('sizeConfig'); + }); + }); }); }); From 66a43c5f6d8adec0313072f5b70e007f89eaa7f7 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 26 Dec 2019 16:33:55 +0530 Subject: [PATCH 30/41] rewrite some of the log messages --- modules/sizeMappingV2.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index a843d49490f..018163232a3 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -412,7 +412,7 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src if (bid.sizeConfig) { const relevantMediaTypes = getRelevantMediaTypesForBidder(bid.sizeConfig, activeViewport); if (relevantMediaTypes.length === 0) { - logError(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} has not configured sizeConfig property correctly. This bidder won't be eligible for sizeConfig checks and will remain active.`); + logError(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - sizeConfig is not configured properly. This bidder won't be eligible for sizeConfig checks and will remail active.`); bid = Object.assign({}, bid); } else if (relevantMediaTypes[0] !== 'none') { const bidderMediaTypes = Object @@ -426,11 +426,11 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src if (Object.keys(bidderMediaTypes).length > 0) { bid = Object.assign({}, bid, { mediaTypes: bidderMediaTypes }); } else { - logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled.`); + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - 'relevantMediaTypes' for this bidder does not match with any of the active mediaTypes at the Ad Unit level. This bidder is disabled.`); return bids; } } else { - logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing sizeConfig check.`); + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - 'relevantMediaTypes' is set to 'none' in sizeConfig for current viewport size. This bidder is disabled.`); return bids; } } @@ -449,7 +449,7 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src })); return bids; } else { - logInfo(`SizeMappingV2:: Bidder: ${bid.bidder} in Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - Label check for this bidder has failed. This bidder is disabled.`); return bids; } }, [])); From b4cf4904e808f7f9a8bf92575030a6e36f4b53ad Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 26 Dec 2019 23:15:24 +0530 Subject: [PATCH 31/41] redefine log messages to make it simple to the end user --- modules/sizeMappingV2.js | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 018163232a3..aeb8ca04f13 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -324,7 +324,26 @@ function getFilteredMediaTypes(mediaTypes) { } } }) - return { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes }; + + // filter out 'undefined' values from activeSizeBucket object and attach sizes/playerSize information against the active size bucket. + const sizeBucketToSizeMap = Object + .keys(activeSizeBucket) + .filter(mediaType => activeSizeBucket[mediaType] !== undefined) + .reduce((sizeBucketToSizeMap, mediaType) => { + sizeBucketToSizeMap[mediaType] = { + activeSizeBucket: activeSizeBucket[mediaType], + activeSizeDimensions: (mediaType === 'banner') ? ( + // banner mediaType gets deleted incase no sizes are specified for a given size bucket, that's why this check is necessary + (transformedMediaTypes.banner) ? (transformedMediaTypes.banner.sizes) : ([]) + ) : ((mediaType === 'video') ? ( + // video mediaType gets deleted incase no playerSize is specified for a given size bucket, that's why this check is necessary + (transformedMediaTypes.video) ? (transformedMediaTypes.video.playerSize) : ([]) + ) : ('NA')) + }; + return sizeBucketToSizeMap; + }, {}); + + return { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes }; }; /** @@ -339,9 +358,12 @@ function getFilteredMediaTypes(mediaTypes) { function isSizeConfigActivated(mediaType, sizeConfig) { switch (mediaType) { case 'banner': - return sizeConfig.sizes && sizeConfig.sizes.length > 0; + // we need this check, sizeConfig.sizes[0].length > 0, in place because a sizeBucket can have sizes: [], + // gets converted to sizes: [[]] in the checkAdUnitSetupHook function + return sizeConfig.sizes && sizeConfig.sizes.length > 0 && sizeConfig.sizes[0].length > 0; case 'video': - return sizeConfig.playerSize && sizeConfig.playerSize.length > 0; + // for why we need the last check, read the above comment + return sizeConfig.playerSize && sizeConfig.playerSize.length > 0 && sizeConfig.playerSize[0].length > 0; case 'native': return sizeConfig.active; default: @@ -354,7 +376,7 @@ function isSizeConfigActivated(mediaType, sizeConfig) { * @param {Array} sizeConfig SizeConfig defines the characteristics of an Ad Unit categorised into multiple size buckets per media type * @param {Array} activeViewport Viewport size of the browser in the form [w, h] (w -> width, h -> height) * Calculated at the time of making call to pbjs.requestBids function - * @returns {Array} The active size bucket matching the activeViewPort + * @returns {Array} The active size bucket matching the activeViewPort, for example: [750, 0] */ function getActiveSizeBucket(sizeConfig, activeViewport) { let activeSizeBucket = []; @@ -384,11 +406,12 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src return adUnits.reduce((result, adUnit) => { if (isLabelActivated(adUnit, labels, adUnit.code)) { if (adUnit.mediaTypes && isValidMediaTypes(adUnit.mediaTypes)) { - const { mediaTypes, activeSizeBucket, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Active size buckets after filtration: `, activeSizeBucket); - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Transformed mediaTypes after filtration: `, transformedMediaTypes); - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - mediaTypes that got filtered out: `, Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1)); - + const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); + const filteredMediaTypes = Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1) + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Active size buckets after filtration: `, sizeBucketToSizeMap); + if (filteredMediaTypes.length > 0) { + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - mediaTypes that got filtered out: ${filteredMediaTypes}`); + } // check if adUnit has any active media types remaining, if not drop the adUnit from auction, // else proceed to evaluate the bids object. if (Object.keys(transformedMediaTypes).length === 0) { From 57c27e6cbc378e3f7a90a6a4cf12024d7cca6951 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Tue, 31 Dec 2019 14:29:07 +0530 Subject: [PATCH 32/41] code optimization done so that getFilteredMediaTypes function gets called only once per adUnit per auction --- modules/sizeMappingV2.js | 48 ++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index aeb8ca04f13..8dcbf5030aa 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -23,7 +23,9 @@ import { adUnitSetupChecks } from '../src/prebid'; -// Maps auctionId to a boolean value, value is set to true if Adunits are setup to use the new size mapping, else it's set to false. +// '_sizeMappingUsageMap' maps 'auctionId' to an object that contains information whether the particular auction is using size mapping V2 (the new size mapping spec), +// and contains additional information on adUnits for each auction. + const _sizeMappingUsageMap = {}; // returns "true" if atleast one of the adUnit in the adUnits array has declared a Ad Unit or(and) Bid level sizeConfig @@ -224,9 +226,12 @@ getHook('getBids').before(function (fn, bidderInfo) { const isUsingSizeMappingBool = isUsingNewSizeMapping(bidderInfo.adUnits); // populate _sizeMappingUsageMap for the first time for a particular auction - _sizeMappingUsageMap[bidderInfo.auctionId] = isUsingSizeMappingBool; + _sizeMappingUsageMap[bidderInfo.auctionId] = { + usingSizeMappingV2: isUsingSizeMappingBool, + adUnits: [] + }; } - if (_sizeMappingUsageMap[bidderInfo.auctionId]) { + if (_sizeMappingUsageMap[bidderInfo.auctionId].usingSizeMappingV2) { // if adUnit is found using sizeMappingV2 specs, run the getBids function which processes the sizeConfig object // and returns the bids array for a particular bidder. const bids = getBids(bidderInfo); @@ -402,16 +407,41 @@ function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { return []; } +function getAdUnitDetail(auctionId, adUnit) { + const adUnitDetail = _sizeMappingUsageMap[auctionId].adUnits.filter(adUnitDetail => adUnitDetail.adUnitCode === adUnit.code); + + if (adUnitDetail.length > 0) { + return adUnitDetail[0]; + } else { + const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); + + const adUnitDetail = { + adUnitCode: adUnit.code, + mediaTypes, + sizeBucketToSizeMap, + activeViewport, + transformedMediaTypes + }; + + // 'filteredMediaTypes' are the mediaTypes that got removed/filtered-out from adUnit.mediaTypes after sizeConfig filtration. + const filteredMediaTypes = Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1) + + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code} - Active size buckets after filtration: `, sizeBucketToSizeMap); + if (filteredMediaTypes.length > 0) { + logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code} - mediaTypes that got filtered out: ${filteredMediaTypes}`); + } + + _sizeMappingUsageMap[auctionId].adUnits.push(adUnitDetail); + return adUnitDetail; + } +} + function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { if (isLabelActivated(adUnit, labels, adUnit.code)) { if (adUnit.mediaTypes && isValidMediaTypes(adUnit.mediaTypes)) { - const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); - const filteredMediaTypes = Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1) - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - Active size buckets after filtration: `, sizeBucketToSizeMap); - if (filteredMediaTypes.length > 0) { - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - mediaTypes that got filtered out: ${filteredMediaTypes}`); - } + const { activeViewport, transformedMediaTypes } = getAdUnitDetail(auctionId, adUnit); + // check if adUnit has any active media types remaining, if not drop the adUnit from auction, // else proceed to evaluate the bids object. if (Object.keys(transformedMediaTypes).length === 0) { From aa6587662f7a29b61c2d13d491fc9e51616665cb Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Tue, 31 Dec 2019 16:15:57 +0530 Subject: [PATCH 33/41] add code comments --- modules/sizeMappingV2.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 8dcbf5030aa..2faef2bea67 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -24,8 +24,7 @@ import { } from '../src/prebid'; // '_sizeMappingUsageMap' maps 'auctionId' to an object that contains information whether the particular auction is using size mapping V2 (the new size mapping spec), -// and contains additional information on adUnits for each auction. - +// and it also contains additional information on adUnits for each auction. const _sizeMappingUsageMap = {}; // returns "true" if atleast one of the adUnit in the adUnits array has declared a Ad Unit or(and) Bid level sizeConfig @@ -201,6 +200,7 @@ getHook('checkAdUnitSetup').before(function (fn, adUnits) { } }); +// checks if the sizeConfig object declared at the Bidder level is in the right format or not. function checkBidderSizeConfigFormat(sizeConfig) { let didCheckPass = true; if (Array.isArray(sizeConfig)) { @@ -407,6 +407,8 @@ function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { return []; } +// populates '_sizeMappingUsageMap' for a given auctionId with relevant adUnit information returned from the call to 'getFilteredMediaTypes' function +// returns adUnit details object. function getAdUnitDetail(auctionId, adUnit) { const adUnitDetail = _sizeMappingUsageMap[auctionId].adUnits.filter(adUnitDetail => adUnitDetail.adUnitCode === adUnit.code); From 7a02628be3ad88338d0b7c22ad89938e5465dea0 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Thu, 2 Jan 2020 05:35:49 +0530 Subject: [PATCH 34/41] code refactorization and more unit test cases --- modules/sizeMappingV2.js | 185 ++--- test/spec/modules/sizeMappingV2_spec.js | 910 ++++++++++++++++++++++-- 2 files changed, 962 insertions(+), 133 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 2faef2bea67..8b8b439815b 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -3,18 +3,7 @@ * This implementation replaces global sizeConfig with a adUnit/bidder level sizeConfig with support for labels. */ -import { - flatten, - deepClone, - deepAccess, - getDefinedParams, - getUniqueIdentifierStr, - logInfo, - logError, - logWarn, - isValidMediaTypes, - isArrayOfNums -} from '../src/utils'; +import * as utils from '../src/utils'; import { processNativeAdUnitParams } from '../src/native'; import { adunitCounter } from '../src/adUnits'; import includes from 'core-js/library/fn/array/includes'; @@ -23,11 +12,39 @@ import { adUnitSetupChecks } from '../src/prebid'; -// '_sizeMappingUsageMap' maps 'auctionId' to an object that contains information whether the particular auction is using size mapping V2 (the new size mapping spec), -// and it also contains additional information on adUnits for each auction. -const _sizeMappingUsageMap = {}; +// allows for sinon.spy, sinon.stub, etc to unit test calls made to these functions internally +export const internal = { + checkBidderSizeConfigFormat, + getActiveSizeBucket, + getFilteredMediaTypes, + getAdUnitDetail +}; + +// 'sizeMappingInternalStore' contains information whether a particular auction is using size mapping V2 (the new size mapping spec), +// and it also contains additional information on each adUnit, as such, mediaTypes, activeViewport, etc. +// This information is required by the 'getBids' function. +export const sizeMappingInternalStore = createSizeMappingInternalStore(); -// returns "true" if atleast one of the adUnit in the adUnits array has declared a Ad Unit or(and) Bid level sizeConfig +function createSizeMappingInternalStore() { + const sizeMappingInternalStore = {}; + + return { + initializeStore: function(auctionId, isUsingSizeMappingBool) { + sizeMappingInternalStore[auctionId] = { + usingSizeMappingV2: isUsingSizeMappingBool, + adUnits: [] + }; + }, + getAuctionDetail: function(auctionId) { + return sizeMappingInternalStore[auctionId]; + }, + setAuctionDetail: function(auctionId, adUnitDetail) { + sizeMappingInternalStore[auctionId].adUnits.push(adUnitDetail); + } + } +} + +// returns "true" if atleast one of the adUnit in the adUnits array has declared a Ad Unit or(and) Bidder level sizeConfig // returns "false" otherwise export function isUsingNewSizeMapping(adUnits) { let isUsingSizeMappingBool = false; @@ -61,7 +78,7 @@ export function checkAdUnitSetupHook(adUnits) { return adUnits.filter(adUnit => { const mediaTypes = adUnit.mediaTypes; if (!mediaTypes || Object.keys(mediaTypes).length === 0) { - logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); + utils.logError(`Detected adUnit.code '${adUnit.code}' did not have a 'mediaTypes' object defined. This is a required field for the auction, so this adUnit has been removed.`); return false; } @@ -77,38 +94,38 @@ export function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.banner object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'sizes'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] is missing required property minViewPort or sizes or both.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] is missing required property minViewPort or sizes or both.`); deleteBannerMediaType = true; return; } // check if the config.sizes property is in [w, h] format, if yes, change it to [[w, h]] format. const bannerSizes = adUnitSetupChecks.validateSizes(config.sizes); - if (isArrayOfNums(config.minViewPort, 2)) { + if (utils.isArrayOfNums(config.minViewPort, 2)) { if (config.sizes.length > 0 && bannerSizes.length > 0) { config.sizes = bannerSizes; } else if (config.sizes.length === 0) { // If a size bucket doesn't have any sizes, sizes is an empty array, i.e. sizes: []. This check takes care of that. config.sizes = [config.sizes]; } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); deleteBannerMediaType = true; } } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); deleteBannerMediaType = true; } }); if (deleteBannerMediaType) { - logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.banner has been removed due to error in sizeConfig.`); + utils.logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.banner has been removed due to error in sizeConfig.`); delete adUnit.mediaTypes.banner; } } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); delete adUnit.mediaTypes.banner; } } else { - logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); + utils.logError('Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); delete adUnit.mediaTypes.banner; } } @@ -125,16 +142,16 @@ export function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.video object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'playerSize'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); deleteVideoMediaType = true; return; } // check if the config.playerSize property is in [w, h] format, if yes, change it to [[w, h]] format. let tarPlayerSizeLen = (typeof config.playerSize[0] === 'number') ? 2 : 1; const videoSizes = adUnitSetupChecks.validateSizes(config.playerSize, tarPlayerSizeLen); - if (isArrayOfNums(config.minViewPort, 2)) { + if (utils.isArrayOfNums(config.minViewPort, 2)) { if (tarPlayerSizeLen === 2) { - logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); + utils.logInfo('Transforming video.playerSize from [640,480] to [[640,480]] so it\'s in the proper format.'); } if (config.playerSize.length > 0 && videoSizes.length > 0) { config.playerSize = videoSizes; @@ -142,20 +159,20 @@ export function checkAdUnitSetupHook(adUnits) { // If a size bucket doesn't have any playerSize, playerSize is an empty array, i.e. playerSize: []. This check takes care of that. config.playerSize = [config.playerSize]; } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); deleteVideoMediaType = true; } } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig[${index}] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); deleteVideoMediaType = true; } }); if (deleteVideoMediaType) { - logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has been removed due to error in sizeConfig.`); + utils.logInfo(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig has been removed due to error in sizeConfig.`); delete adUnit.mediaTypes.video.sizeConfig; } } else { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); return delete adUnit.mediaTypes.video.sizeConfig; } } @@ -171,12 +188,12 @@ export function checkAdUnitSetupHook(adUnits) { // if not, remove the mediaTypes.native object const keys = Object.keys(config); if (!(includes(keys, 'minViewPort') && includes(keys, 'active'))) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); return delete adUnit.mediaTypes.native.sizeConfig; } - if (!(isArrayOfNums(config.minViewPort, 2) && typeof config.active === 'boolean')) { - logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); + if (!(utils.isArrayOfNums(config.minViewPort, 2) && typeof config.active === 'boolean')) { + utils.logError(`Ad Unit: ${adUnit.code}: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); return delete adUnit.mediaTypes.native.sizeConfig; } }); @@ -201,37 +218,38 @@ getHook('checkAdUnitSetup').before(function (fn, adUnits) { }); // checks if the sizeConfig object declared at the Bidder level is in the right format or not. -function checkBidderSizeConfigFormat(sizeConfig) { +export function checkBidderSizeConfigFormat(sizeConfig) { let didCheckPass = true; - if (Array.isArray(sizeConfig)) { + if (Array.isArray(sizeConfig) && sizeConfig.length > 0) { sizeConfig.forEach(config => { const keys = Object.keys(config); if ((includes(keys, 'minViewPort') && includes(keys, 'relevantMediaTypes')) && - isArrayOfNums(config.minViewPort, 2) && + utils.isArrayOfNums(config.minViewPort, 2) && Array.isArray(config.relevantMediaTypes) && - config.relevantMediaTypes.every(mt => (includes(['banner', 'video', 'native'], mt)) || (mt === 'none'))) { + config.relevantMediaTypes.length > 0 && + (config.relevantMediaTypes.length > 1 ? (config.relevantMediaTypes.every(mt => (includes(['banner', 'video', 'native'], mt)))) + : (['none', 'banner', 'video', 'native'].indexOf(config.relevantMediaTypes[0] > -1)))) { didCheckPass = didCheckPass && true; } else { didCheckPass = false; } }); + } else { + didCheckPass = false; } return didCheckPass; } getHook('getBids').before(function (fn, bidderInfo) { // check if the adUnit is using sizeMappingV2 specs and store the result in _sizeMappingUsageMap. - if (typeof _sizeMappingUsageMap[bidderInfo.auctionId] === 'undefined') { + if (typeof sizeMappingInternalStore.getAuctionDetail(bidderInfo.auctionId) === 'undefined') { const isUsingSizeMappingBool = isUsingNewSizeMapping(bidderInfo.adUnits); - // populate _sizeMappingUsageMap for the first time for a particular auction - _sizeMappingUsageMap[bidderInfo.auctionId] = { - usingSizeMappingV2: isUsingSizeMappingBool, - adUnits: [] - }; + // initialize sizeMappingInternalStore for the first time for a particular auction + sizeMappingInternalStore.initializeStore(bidderInfo.auctionId, isUsingSizeMappingBool); } - if (_sizeMappingUsageMap[bidderInfo.auctionId].usingSizeMappingV2) { + if (sizeMappingInternalStore.getAuctionDetail(bidderInfo.auctionId).usingSizeMappingV2) { // if adUnit is found using sizeMappingV2 specs, run the getBids function which processes the sizeConfig object // and returns the bids array for a particular bidder. const bids = getBids(bidderInfo); @@ -249,25 +267,25 @@ getHook('getBids').before(function (fn, bidderInfo) { * @param {string} adUnitCode Unique string identifier for an Ad Unit. * @returns {boolean} Represents if the Ad Unit or the Bid is active or not */ -function isLabelActivated(bidOrAdUnit, activeLabels, adUnitCode) { +export function isLabelActivated(bidOrAdUnit, activeLabels, adUnitCode) { let labelOperator; const labelsFound = Object.keys(bidOrAdUnit).filter(prop => prop === 'labelAny' || prop === 'labelAll'); if (labelsFound && labelsFound.length > 1) { - logWarn(`SizeMappingV2:: ${(bidOrAdUnit.code) + utils.logWarn(`SizeMappingV2:: ${(bidOrAdUnit.code) ? (`Ad Unit: ${bidOrAdUnit.code} has multiple label operators. Using the first declared operator: ${labelsFound[0]}`) : (`Bidder: ${bidOrAdUnit.bidder} in Ad Unit: ${adUnitCode} has multiple label operators. Using the first declared operator: ${labelsFound[0]}`)}`); } labelOperator = labelsFound[0]; - if (labelOperator === 'labelAll') { + if (labelOperator === 'labelAll' && Array.isArray(bidOrAdUnit[labelOperator])) { if (bidOrAdUnit.labelAll.length === 0) { - logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAll with an empty array. Ad Unit is still enabled!`); + utils.logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAll with an empty array. Ad Unit is still enabled!`); return true; } return bidOrAdUnit.labelAll.every(label => includes(activeLabels, label)); - } else if (labelOperator === 'labelAny') { + } else if (labelOperator === 'labelAny' && Array.isArray(bidOrAdUnit[labelOperator])) { if (bidOrAdUnit.labelAny.length === 0) { - logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAny with an empty array. Ad Unit is still enabled!`); + utils.logWarn(`SizeMappingV2:: Ad Unit: ${bidOrAdUnit.code} has declared property labelAny with an empty array. Ad Unit is still enabled!`); return true; } return bidOrAdUnit.labelAny.some(label => includes(activeLabels, label)); @@ -281,13 +299,13 @@ function isLabelActivated(bidOrAdUnit, activeLabels, adUnitCode) { * @param {MediaTypes} mediaTypes Contains information about supported media types for an Ad Unit and size information for each of those types * @returns {FilteredMediaTypes} Filtered mediaTypes object with relevant media types filtered by size buckets based on activeViewPort size */ -function getFilteredMediaTypes(mediaTypes) { +export function getFilteredMediaTypes(mediaTypes) { let activeViewportWidth, activeViewportHeight, transformedMediaTypes; - transformedMediaTypes = deepClone(mediaTypes); + transformedMediaTypes = utils.deepClone(mediaTypes); let activeSizeBucket = { banner: undefined, @@ -296,10 +314,10 @@ function getFilteredMediaTypes(mediaTypes) { } try { - activeViewportWidth = getWindowTop().innerWidth; - activeViewportHeight = getWindowTop().innerHeight; + activeViewportWidth = utils.getWindowTop().innerWidth; + activeViewportHeight = utils.getWindowTop().innerHeight; } catch (e) { - logWarn(`SizeMappingv2:: Unfriendly iframe blocks viewport size to be evaluated correctly`); + utils.logWarn(`SizeMappingv2:: Unfriendly iframe blocks viewport size to be evaluated correctly`); activeViewportWidth = window.innerWidth; activeViewportHeight = window.innerHeight; } @@ -360,7 +378,7 @@ function getFilteredMediaTypes(mediaTypes) { * @param {Object} sizeConfig Represents the sizeConfig object which is active based on the current viewport size * @returns {boolean} Represents if the size config is active or not */ -function isSizeConfigActivated(mediaType, sizeConfig) { +export function isSizeConfigActivated(mediaType, sizeConfig) { switch (mediaType) { case 'banner': // we need this check, sizeConfig.sizes[0].length > 0, in place because a sizeBucket can have sizes: [], @@ -383,7 +401,7 @@ function isSizeConfigActivated(mediaType, sizeConfig) { * Calculated at the time of making call to pbjs.requestBids function * @returns {Array} The active size bucket matching the activeViewPort, for example: [750, 0] */ -function getActiveSizeBucket(sizeConfig, activeViewport) { +export function getActiveSizeBucket(sizeConfig, activeViewport) { let activeSizeBucket = []; sizeConfig .sort((a, b) => a.minViewPort[0] - b.minViewPort[0]) @@ -399,23 +417,23 @@ function getActiveSizeBucket(sizeConfig, activeViewport) { return activeSizeBucket; } -function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { - if (checkBidderSizeConfigFormat(sizeConfig)) { - const activeSizeBucket = getActiveSizeBucket(sizeConfig, activeViewport); +export function getRelevantMediaTypesForBidder(sizeConfig, activeViewport) { + if (internal.checkBidderSizeConfigFormat(sizeConfig)) { + const activeSizeBucket = internal.getActiveSizeBucket(sizeConfig, activeViewport); return sizeConfig.filter(config => config.minViewPort === activeSizeBucket)[0]['relevantMediaTypes']; } return []; } -// populates '_sizeMappingUsageMap' for a given auctionId with relevant adUnit information returned from the call to 'getFilteredMediaTypes' function +// sets sizeMappingInternalStore for a given auctionId with relevant adUnit information returned from the call to 'getFilteredMediaTypes' function // returns adUnit details object. -function getAdUnitDetail(auctionId, adUnit) { - const adUnitDetail = _sizeMappingUsageMap[auctionId].adUnits.filter(adUnitDetail => adUnitDetail.adUnitCode === adUnit.code); +export function getAdUnitDetail(auctionId, adUnit) { + const adUnitDetail = sizeMappingInternalStore.getAuctionDetail(auctionId).adUnits.filter(adUnitDetail => adUnitDetail.adUnitCode === adUnit.code); if (adUnitDetail.length > 0) { return adUnitDetail[0]; } else { - const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); + const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = internal.getFilteredMediaTypes(adUnit.mediaTypes); const adUnitDetail = { adUnitCode: adUnit.code, @@ -428,26 +446,27 @@ function getAdUnitDetail(auctionId, adUnit) { // 'filteredMediaTypes' are the mediaTypes that got removed/filtered-out from adUnit.mediaTypes after sizeConfig filtration. const filteredMediaTypes = Object.keys(mediaTypes).filter(mt => Object.keys(transformedMediaTypes).indexOf(mt) === -1) - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code} - Active size buckets after filtration: `, sizeBucketToSizeMap); + utils.logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code} - Active size buckets after filtration: `, sizeBucketToSizeMap); if (filteredMediaTypes.length > 0) { - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code} - mediaTypes that got filtered out: ${filteredMediaTypes}`); + utils.logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code} - mediaTypes that got filtered out: ${filteredMediaTypes}`); } - _sizeMappingUsageMap[auctionId].adUnits.push(adUnitDetail); + // set adUnitDetail in sizeMappingInternalStore against the correct 'auctionId'. + sizeMappingInternalStore.setAuctionDetail(auctionId, adUnitDetail); return adUnitDetail; } } -function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { +export function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { if (isLabelActivated(adUnit, labels, adUnit.code)) { - if (adUnit.mediaTypes && isValidMediaTypes(adUnit.mediaTypes)) { - const { activeViewport, transformedMediaTypes } = getAdUnitDetail(auctionId, adUnit); + if (adUnit.mediaTypes && utils.isValidMediaTypes(adUnit.mediaTypes)) { + const { activeViewport, transformedMediaTypes } = internal.getAdUnitDetail(auctionId, adUnit); // check if adUnit has any active media types remaining, if not drop the adUnit from auction, // else proceed to evaluate the bids object. if (Object.keys(transformedMediaTypes).length === 0) { - logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled since there are no active media types after sizeConfig filtration.`); + utils.logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled since there are no active media types after sizeConfig filtration.`); return result; } result @@ -455,19 +474,19 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src .reduce((bids, bid) => { if (isLabelActivated(bid, labels, adUnit.code)) { // handle native params - const nativeParams = adUnit.nativeParams || deepAccess(adUnit, 'mediaTypes.native'); + const nativeParams = adUnit.nativeParams || utils.deepAccess(adUnit, 'mediaTypes.native'); if (nativeParams) { bid = Object.assign({}, bid, { nativeParams: processNativeAdUnitParams(nativeParams) }); } - bid = Object.assign({}, bid, getDefinedParams(adUnit, ['mediaType', 'renderer'])); + bid = Object.assign({}, bid, utils.getDefinedParams(adUnit, ['mediaType', 'renderer'])); if (bid.sizeConfig) { const relevantMediaTypes = getRelevantMediaTypesForBidder(bid.sizeConfig, activeViewport); if (relevantMediaTypes.length === 0) { - logError(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - sizeConfig is not configured properly. This bidder won't be eligible for sizeConfig checks and will remail active.`); + utils.logError(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - sizeConfig is not configured properly. This bidder won't be eligible for sizeConfig checks and will remail active.`); bid = Object.assign({}, bid); } else if (relevantMediaTypes[0] !== 'none') { const bidderMediaTypes = Object @@ -481,20 +500,20 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src if (Object.keys(bidderMediaTypes).length > 0) { bid = Object.assign({}, bid, { mediaTypes: bidderMediaTypes }); } else { - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - 'relevantMediaTypes' for this bidder does not match with any of the active mediaTypes at the Ad Unit level. This bidder is disabled.`); + utils.logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - 'relevantMediaTypes' for this bidder does not match with any of the active mediaTypes at the Ad Unit level. This bidder is disabled.`); return bids; } } else { - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - 'relevantMediaTypes' is set to 'none' in sizeConfig for current viewport size. This bidder is disabled.`); + utils.logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - 'relevantMediaTypes' is set to 'none' in sizeConfig for current viewport size. This bidder is disabled.`); return bids; } } bids.push(Object.assign({}, bid, { adUnitCode: adUnit.code, transactionId: adUnit.transactionId, - sizes: deepAccess(transformedMediaTypes, 'banner.sizes') || deepAccess(transformedMediaTypes, 'video.playerSize') || [], + sizes: utils.deepAccess(transformedMediaTypes, 'banner.sizes') || utils.deepAccess(transformedMediaTypes, 'video.playerSize') || [], mediaTypes: bid.mediaTypes || transformedMediaTypes, - bidId: bid.bid_id || getUniqueIdentifierStr(), + bidId: bid.bid_id || utils.getUniqueIdentifierStr(), bidderRequestId, auctionId, src, @@ -504,17 +523,17 @@ function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src })); return bids; } else { - logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - Label check for this bidder has failed. This bidder is disabled.`); + utils.logInfo(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bid.bidder} - Label check for this bidder has failed. This bidder is disabled.`); return bids; } }, [])); } else { - logWarn(`SizeMappingV2:: Ad Unit: ${adUnit.code} has declared invalid mediaTypes or has not declared a mediaTypes property`); + utils.logWarn(`SizeMappingV2:: Ad Unit: ${adUnit.code} has declared invalid mediaTypes or has not declared a mediaTypes property`); } } else { - logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); + utils.logInfo(`SizeMappingV2:: Ad Unit: ${adUnit.code} is disabled due to failing label check.`); return result; } return result; - }, []).reduce(flatten, []).filter(val => val !== ''); + }, []).reduce(utils.flatten, []).filter(val => val !== ''); } diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index 729a085d4f9..5ea93e5ea7b 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -1,7 +1,20 @@ import { expect } from 'chai'; import * as utils from '../../../src/utils'; -import { isUsingNewSizeMapping, checkAdUnitSetupHook } from '../../../modules/sizeMappingV2'; +import { + isUsingNewSizeMapping, + checkAdUnitSetupHook, + checkBidderSizeConfigFormat, + isLabelActivated, + isSizeConfigActivated, + getActiveSizeBucket, + getRelevantMediaTypesForBidder, + sizeMappingInternalStore, + getAdUnitDetail, + getFilteredMediaTypes, + getBids, + internal +} from '../../../modules/sizeMappingV2'; import { adUnitSetupChecks } from '../../../src/prebid'; @@ -160,7 +173,7 @@ describe('sizeMappingV2', function () { expect(usingNewSizeMappingBool).to.be.true; }); - it('should return "true" if sizeConfig is declared both at the adUnits level and at the bids level', function() { + it('should return "true" if sizeConfig is declared both at the adUnits level and at the bids level', function () { let adUnits = utils.deepClone(AD_UNITS); const usingNewSizeMappingBool = isUsingNewSizeMapping(adUnits); @@ -171,16 +184,16 @@ describe('sizeMappingV2', function () { }); }); - describe('checkAdUnitSetupHook(adUnits)', function() { - beforeEach(function() { + describe('checkAdUnitSetupHook(adUnits)', function () { + beforeEach(function () { sinon.spy(utils, 'logError'); }); - afterEach(function() { + afterEach(function () { utils.logError.restore(); }); - it('should filter out adUnit if it does not contain the required property mediaTypes', function() { + it('should filter out adUnit if it does not contain the required property mediaTypes', function () { let adUnits = utils.deepClone(AD_UNITS); delete adUnits[0].mediaTypes; // before checkAdUnitSetupHook is called, the length of adUnits should be '2' @@ -192,7 +205,7 @@ describe('sizeMappingV2', function () { expect(adUnits[0].code).to.equal('div-gpt-ad-1460505748561-1'); }); - it('should filter out adUnit if it has declared property mediaTypes with an empty object', function() { + it('should filter out adUnit if it has declared property mediaTypes with an empty object', function () { let adUnits = utils.deepClone(AD_UNITS); adUnits[0].mediaTypes = {}; // before checkAdUnitSetupHook is called, the length of adUnits should be '2' @@ -204,7 +217,7 @@ describe('sizeMappingV2', function () { expect(adUnits[0].code).to.equal('div-gpt-ad-1460505748561-1'); }); - it('should log an error message if Ad Unit does not contain the required property "mediaTypes"', function() { + it('should log an error message if Ad Unit does not contain the required property "mediaTypes"', function () { let adUnits = utils.deepClone(AD_UNITS); delete adUnits[0].mediaTypes; @@ -213,8 +226,16 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, 'Detected adUnit.code \'div-gpt-ad-1460505748561-0\' did not have a \'mediaTypes\' object defined. This is a required field for the auction, so this adUnit has been removed.'); }); - describe('banner mediaTypes checks', function() { - it('should delete banner mediaType if it does not constain sizes or sizeConfig property', function() { + describe('banner mediaTypes checks', function () { + beforeEach(function () { + sinon.spy(adUnitSetupChecks, 'validateBannerMediaType'); + }); + + afterEach(function () { + adUnitSetupChecks.validateBannerMediaType.restore(); + }); + + it('should delete banner mediaType if it does not constain sizes or sizeConfig property', function () { let adUnits = utils.deepClone(AD_UNITS); delete adUnits[0].mediaTypes.banner.sizeConfig; @@ -227,7 +248,7 @@ describe('sizeMappingV2', function () { expect(adUnits[0].mediaTypes).to.not.have.property('banner'); }); - it('should log an error message if mediaTypes.banner does not contain "sizes" or "sizeConfig" property', function() { + it('should log an error message if mediaTypes.banner does not contain "sizes" or "sizeConfig" property', function () { let adUnits = utils.deepClone(AD_UNITS); // deleteing the sizeConfig property from the first ad unit. delete adUnits[0].mediaTypes.banner.sizeConfig; @@ -237,18 +258,16 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, 'Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); }); - it('should call function "validateBannerMediaType" if mediaTypes.sizes is present', function() { + it('should call function "validateBannerMediaType" if mediaTypes.sizes is present', function () { const adUnits = utils.deepClone(AD_UNITS); - const spy = sinon.spy(adUnitSetupChecks, 'validateBannerMediaType'); - checkAdUnitSetupHook(adUnits); // since the second Ad Unit in AD_UNITS array uses mediaTypes.sizes, it should get called only once. - sinon.assert.callCount(spy, 1); - sinon.assert.calledWith(spy, adUnits[1]); + sinon.assert.callCount(adUnitSetupChecks.validateBannerMediaType, 1); + sinon.assert.calledWith(adUnitSetupChecks.validateBannerMediaType, adUnits[1]); }); - it('should delete mediaTypes.banner object if it\'s property sizeConfig is not declared as an array', function() { + it('should delete mediaTypes.banner object if it\'s property sizeConfig is not declared as an array', function () { const adUnits = utils.deepClone(AD_UNITS); const badSizeConfig = { minViewPort: [0, 0], sizes: [300, 400] @@ -264,7 +283,7 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits.length).to.not.have.property('banner'); }); - it('should log an error message if sizeConfig property in mediaTypes.banner object is not declared as an array', function() { + it('should log an error message if sizeConfig property in mediaTypes.banner object is not declared as an array', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig is NOT defined as an Array const badSizeConfig = { @@ -277,7 +296,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig is NOT an Array. Removing the invalid object mediaTypes.banner from Ad Unit.`); }); - it('should delete mediaTypes.banner object if it\'s property sizeConfig does not contain the required properties "minViewPort" and "sizes"', function() { + it('should delete mediaTypes.banner object if it\'s property sizeConfig does not contain the required properties "minViewPort" and "sizes"', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[2] does not contain the required "sizes" property @@ -297,7 +316,7 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits.length).to.not.have.property('banner'); }); - it('should log an error message if sizeConfig property in mediaTypes.banner object does not contain the required properties "minViewPort" and "sizes"', function() { + it('should log an error message if sizeConfig property in mediaTypes.banner object does not contain the required properties "minViewPort" and "sizes"', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[2] does not contain the required "sizes" property @@ -313,7 +332,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[2] is missing required property minViewPort or sizes or both.`); }); - it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared minViewPort property which is NOT an Array of two integers', function() { + it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared minViewPort property which is NOT an Array of two integers', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[0].minViewPort is an Array of ONE inteter. It should be an array of two integers to be valid, like [0, 0] @@ -333,7 +352,7 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits.length).to.not.have.property('banner'); }); - it('should log an error message if sizeConfig has declared property minViewPort which is not an array of two integers', function() { + it('should log an error message if sizeConfig has declared property minViewPort which is not an array of two integers', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[0].minViewPort is an Array of ONE inteter. It should be an array of two integers to be valid, like [0, 0] @@ -349,7 +368,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[0] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); }); - it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared sizes property which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function() { + it('should delete mediaTypes.banner object if it\'s property sizeConfig has declared sizes property which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[1].sizes is not declared in the correct format. It should be an Array of TWO integers. @@ -369,7 +388,7 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits.length).to.not.have.property('banner'); }); - it('should log an error message if sizeConfig has declared property sizes which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function() { + it('should log an error message if sizeConfig has declared property sizes which is not in the format, [[vw1, vh1], [vw2, vh2]], where vw is viewport width and vh is viewport height', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[1].sizes is not declared in the correct format. It should be an Array of TWO integers. @@ -385,7 +404,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.banner.sizeConfig[1] has propery sizes declared with invalid value. Please ensure the sizes are listed like: [[300, 250], ...] or like: [] if no sizes are present for that size bucket.`); }); - it('should convert sizeConfig.sizes to an array of array, i.e., [360, 600] to [[360, 600]]', function() { + it('should convert sizeConfig.sizes to an array of array, i.e., [360, 600] to [[360, 600]]', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[1].sizes is declared as a Array of integers. Correct way to declare is to have it as an Array of Array of Integers, like, [[300, 250]] @@ -401,14 +420,14 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits[0].mediaTypes.banner.sizeConfig[1].sizes).to.deep.equal([[300, 600]]); }); - it('should allow empty array declaration in sizeConfig.sizes to indicate "No valid sizes for this size bucket", and convert it to an array of array, i.e, [] to [[]]', function() { + it('should allow empty array declaration in sizeConfig.sizes to indicate "No valid sizes for this size bucket", and convert it to an array of array, i.e, [] to [[]]', function () { const adUnits = utils.deepClone(AD_UNITS); const validatedAdUnits = checkAdUnitSetupHook(adUnits); expect(validatedAdUnits[0].mediaTypes.banner.sizeConfig[0].sizes).to.deep.equal([[]]); }); - it('should NOT delete mediaTypes.banner object if sizeConfig object is declared correctly', function() { + it('should NOT delete mediaTypes.banner object if sizeConfig object is declared correctly', function () { const adUnits = utils.deepClone(AD_UNITS); // before calling checkAdUnitSetupHook, the mediaTypes.banner object should be present on both the Ad Units. @@ -423,20 +442,26 @@ describe('sizeMappingV2', function () { }); }); - describe('video mediaTypes checks', function() { - it('should call function "validateVideoMediaType" if mediaTypes.video.playerSize is present in the Ad Unit', function() { - const adUnits = utils.deepClone(AD_UNITS); + describe('video mediaTypes checks', function () { + beforeEach(function () { + sinon.spy(adUnitSetupChecks, 'validateVideoMediaType'); + }); - const spy = sinon.spy(adUnitSetupChecks, 'validateVideoMediaType'); + afterEach(function () { + adUnitSetupChecks.validateVideoMediaType.restore(); + }); + + it('should call function "validateVideoMediaType" if mediaTypes.video.playerSize is present in the Ad Unit', function () { + const adUnits = utils.deepClone(AD_UNITS); checkAdUnitSetupHook(adUnits); // since adUntis[1].mediaTypes.video has defined property "playserSize", it should call function "validateVideoMediaType" only once - sinon.assert.callCount(spy, 1); - sinon.assert.calledWith(spy, adUnits[1]); + sinon.assert.callCount(adUnitSetupChecks.validateVideoMediaType, 1); + sinon.assert.calledWith(adUnitSetupChecks.validateVideoMediaType, adUnits[1]); }); - it('should delete mediaTypes.video.sizeConfig property if sizeConfig is not declared as an array', function() { + it('should delete mediaTypes.video.sizeConfig property if sizeConfig is not declared as an array', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig is declared as an object, it should have been an Array. @@ -458,7 +483,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig is NOT an Array. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); }); - it('should delete mediaTypes.video.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "playerSize"', function() { + it('should delete mediaTypes.video.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "playerSize"', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[0] doest not contain the required property "playerSize". @@ -481,7 +506,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[0] is missing required property minViewPort or playerSize or both. Removing the invalid property mediaTypes.video.sizeConfig from Ad Unit.`); }); - it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared minViewPort property which is NOT an Array of two integers', function() { + it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared minViewPort property which is NOT an Array of two integers', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[1].minViewPort is an Array of Integers. It should have been an Array of Array of Integers. @@ -504,7 +529,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[1] has property minViewPort decalared with invalid value. Please ensure minViewPort is an Array and is listed like: [700, 0]. Declaring an empty array is not allowed, instead use: [0, 0].`); }); - it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared "playerSize" property which is not in the format, [[vw1, vh1]], where vw is viewport width and vh is viewport height', function() { + it('should delete mediaTypes.video.sizeConfig property if sizeConfig has declared "playerSize" property which is not in the format, [[vw1, vh1]], where vw is viewport width and vh is viewport height', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[0].playerSize property is declared incorrectly. @@ -527,7 +552,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.video.sizeConfig[0] has propery playerSize declared with invalid value. Please ensure the playerSize is listed like: [640, 480] or like: [] if no playerSize is present for that size bucket.`); }); - it('should convert sizeConfig.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function() { + it('should convert sizeConfig.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[] has declared "playerSize" as an Array of Intergers. It should be an Array of Array of Integers, like [[640, 400]]. @@ -544,7 +569,7 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits[0].mediaTypes.video.sizeConfig[1].playerSize).to.deep.equal([[360, 600]]); }); - it('should convert mediaTypes.video.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function() { + it('should convert mediaTypes.video.playerSize to an array of array, i.e., [360, 600] to [[360, 600]]', function () { const adUnits = utils.deepClone(AD_UNITS); const validatedAdUnits = checkAdUnitSetupHook(adUnits); @@ -552,7 +577,7 @@ describe('sizeMappingV2', function () { expect(validatedAdUnits[1].mediaTypes.video.playerSize).to.deep.equal([[300, 460]]); }); - it('should NOT delete mediaTypes.video.sizeConfig property if sizeConfig property is declared correctly', function() { + it('should NOT delete mediaTypes.video.sizeConfig property if sizeConfig property is declared correctly', function () { const adUnits = utils.deepClone(AD_UNITS); // before checkAdUnitSetupHook is called @@ -565,17 +590,22 @@ describe('sizeMappingV2', function () { }); }); - describe('native mediaTypes checks', function() { - it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined', function() { - const adUnits = utils.deepClone(AD_UNITS); - const spy = sinon.spy(adUnitSetupChecks, 'validateNativeMediaType'); + describe('native mediaTypes checks', function () { + beforeEach(function () { + sinon.spy(adUnitSetupChecks, 'validateNativeMediaType'); + }); - checkAdUnitSetupHook(adUnits); + afterEach(function () { + adUnitSetupChecks.validateNativeMediaType.restore(); + }); - sinon.assert.callCount(spy, 1); + it('should call function "validateNativeMediaTypes" if mediaTypes.native is defined', function () { + const adUnits = utils.deepClone(AD_UNITS); + checkAdUnitSetupHook(adUnits); + sinon.assert.callCount(adUnitSetupChecks.validateNativeMediaType, 1); }); - it('should delete mediaTypes.native.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "active"', function() { + it('should delete mediaTypes.native.sizeConfig property if sizeConfig does not contain the required properties "minViewPort" and "active"', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[1] doesn't include required property "active" const badSizeConfig = [ @@ -591,7 +621,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig is missing required property minViewPort or active or both. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); }); - it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].minViewPort is NOT an array of TWO integers', function() { + it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].minViewPort is NOT an array of TWO integers', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeConfig[0].minViewPort is an array of three integers. It should ideally be two integers. const badSizeConfig = [ @@ -607,7 +637,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); }); - it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].active is NOT a Boolean', function() { + it('should delete mediaTypes.native.sizeConfig property if sizeConfig[].active is NOT a Boolean', function () { const adUnits = utils.deepClone(AD_UNITS); // badSizeCofnig[0].active is a String value, it should have been a boolean to be valid. @@ -624,7 +654,7 @@ describe('sizeMappingV2', function () { sinon.assert.calledWith(utils.logError, `Ad Unit: div-gpt-ad-1460505748561-0: mediaTypes.native.sizeConfig has properties minViewPort or active decalared with invalid values. Removing the invalid property mediaTypes.native.sizeConfig from Ad Unit.`); }); - it('should NOT delete mediaTypes.native.sizeConfig property if sizeConfig property is declared correctly', function() { + it('should NOT delete mediaTypes.native.sizeConfig property if sizeConfig property is declared correctly', function () { const adUnits = utils.deepClone(AD_UNITS); expect(adUnits[0].mediaTypes.native).to.have.property('sizeConfig'); const validatedAdUnits = checkAdUnitSetupHook(adUnits); @@ -632,4 +662,784 @@ describe('sizeMappingV2', function () { }); }); }); + + describe('checkBidderSizeConfig(sizeConfig)', function () { + it('should return "false" if sizeConfig is NOT declared as an Array at the Bidder level', function () { + const sizeConfig = { + minViewPort: [600, 0], relevantMediaTypes: ['banner'] + }; + expect(checkBidderSizeConfigFormat(sizeConfig)).to.equal(false); + }); + + it('should return "false" if sizeConfig is declared as an empty Array at the Bidder level', function () { + const sizeConfig = []; + expect(checkBidderSizeConfigFormat(sizeConfig)).to.equal(false); + }); + + it('should return "false" if any of the objects in sizeConfig array has not declared the required properties "minViewPort" and/or "relevantMediaTypes"', function () { + const sizeConfig_1 = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [800, 0] } + ]; + const sizeConfig_2 = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { relevantMediaTypes: ['banner', 'native'] } + ]; + expect(checkBidderSizeConfigFormat(sizeConfig_1)).to.equal(false); + expect(checkBidderSizeConfigFormat(sizeConfig_2)).to.equal(false); + }); + + it('should return "false" if minViewPort is not declared as an array of two integers', function () { + const sizeConfig_1 = [ + { minViewPort: [], relevantMediaTypes: ['none'] } + ]; + const sizeConfig_2 = [ + { minViewPort: [300, 0, 0], relevantMediaTypes: ['banner'] } + ]; + expect(checkBidderSizeConfigFormat(sizeConfig_1)).to.equal(false); + expect(checkBidderSizeConfigFormat(sizeConfig_2)).to.equal(false); + }); + + it('should return "false" if relevantMediaTypes is NOT an Array of one or more of these values, "none", "banner", "video", "native"', function () { + // declaration of relevantMediaTypes as an empty array is not allowed + const sizeConfig_1 = [ + { minViewPort: [0, 0], relevantMediaTypes: [] } + ]; + // relevantMediaTypes can't be an object. It MUST be declared as an array. + const sizeConfig_2 = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [1200, 0], relevantMediaTypes: {} } + ]; + // 'none' and 'banner' can't be together. It should either be only 'none' or 'banner' + const sizeConfig_3 = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none', 'banner'] }, + { minViewPort: [1200, 0], relevantMediaTypes: ['banner'] } + ]; + // relevantMediaTypes can only be an array of ['banner', 'video', 'native'] or any one of them + const sizeConfig_4 = [ + { minViewPort: [1200, 0], relevantMediaTypes: ['banner'] }, + { minViewPort: [0, 0], relevantMediaTypes: ['video', 'somethingRandom'] }, + { minViewPort: [1600, 0], relevantMediaTypes: ['native', 'video'] } + + ]; + expect(checkBidderSizeConfigFormat(sizeConfig_1)).to.equal(false); + expect(checkBidderSizeConfigFormat(sizeConfig_2)).to.equal(false); + expect(checkBidderSizeConfigFormat(sizeConfig_3)).to.equal(false); + expect(checkBidderSizeConfigFormat(sizeConfig_4)).to.equal(false); + }); + + it('should return "true" if the sizeConfig object is being configured properly at the Bidder level', function () { + const sizeConfig = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [600, 0], relevantMediaTypes: ['banner'] }, + { minViewPort: [1200, 0], relevantMediaTypes: ['banner', 'video'] } + ]; + expect(checkBidderSizeConfigFormat(sizeConfig)).to.equal(true); + }); + }); + + describe('isLabelActivated(bidOrAdUnit, activeLabels, adUnitCode)', function () { + const labelAny = ['mobile', 'tablet']; + const labelAll = ['mobile', 'tablet', 'desktop', 'HD-Tv']; + const activeLabels = ['mobile', 'tablet', 'desktop']; + const adUnitCode = 'div-gpt-ad-1460505748561-0'; + + beforeEach(function () { + sinon.spy(utils, 'logWarn'); + }); + + afterEach(function () { + utils.logWarn.restore(); + }); + + it('should throw a warning message if both the label operator, "labelAny"/"labelAll" are configured for an Ad Unit', function () { + const [adUnits] = utils.deepClone(AD_UNITS); + adUnits.labelAny = labelAny; + adUnits.labelAll = labelAll; + + isLabelActivated(adUnits, activeLabels, adUnitCode); + + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Ad Unit: div-gpt-ad-1460505748561-0 has multiple label operators. Using the first declared operator: labelAny`); + }); + + it('should throw a warning message if both the label operator, "labelAny"/"labelAll" are configured for an Bidder', function () { + const [adUnits] = utils.deepClone(AD_UNITS); + + adUnits.bids[0].labelAny = labelAny; + adUnits.bids[0].labelAll = labelAll; + + isLabelActivated(adUnits.bids[0], activeLabels, adUnitCode); + + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Bidder: appnexus in Ad Unit: div-gpt-ad-1460505748561-0 has multiple label operators. Using the first declared operator: labelAny`); + }); + + it('should give priority to the label operator declared first incase two label operators are found on the same Ad Unit or Bidder', function () { + const [adUnits] = utils.deepClone(AD_UNITS); + adUnits.labelAll = labelAll; + adUnits.labelAny = labelAny; + + // activeAdUnit should be "false" + // 'labelAll' -> ['mobile', 'tablet', 'desktop', 'HD-Tv'] will be given priority since it's declared before 'labelAny' + // since, activeLabels -> ['mobile', 'tablet', 'desktop'], doesn't include 'HD-Tv', 'isLabelActivated' function should return "false" + const activeAdUnit = isLabelActivated(adUnits, activeLabels, adUnitCode); + expect(activeAdUnit).to.equal(false); + + // bidder level check + adUnits.bids[0].labelAny = labelAny; + adUnits.bids[0].labelAll = labelAll; + + // activeBidder should be "true" + // 'labelAny' -> ['mobile', 'tablet'] will be given priority since it's declared before 'labelAll' + // since, activeLabels -> ['mobile', 'tablet', 'desktop'] and matches atleast one element in labelAny array, so, it'll return true + const activeBidder = isLabelActivated(adUnits.bids[0], activeLabels, adUnitCode); + expect(activeBidder).to.equal(true); + }); + + it('should throw a warning log message if "labelAll" operator is declared as an empty array', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + adUnit.labelAll = []; + + // adUnit level check + isLabelActivated(adUnit, activeLabels, adUnitCode); + + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Ad Unit: div-gpt-ad-1460505748561-0 has declared property labelAll with an empty array. Ad Unit is still enabled!`); + + // bidder level check + isLabelActivated(adUnit.bids[0], activeLabels, adUnitCode); + + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Ad Unit: div-gpt-ad-1460505748561-0 has declared property labelAll with an empty array. Ad Unit is still enabled!`); + }); + + it('should throw a warning log message if "labelAny" operator is declared as an empty array', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + adUnit.labelAny = []; + + // adUnit level check + isLabelActivated(adUnit, activeLabels, adUnitCode); + + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Ad Unit: div-gpt-ad-1460505748561-0 has declared property labelAny with an empty array. Ad Unit is still enabled!`); + + // bidder level check + isLabelActivated(adUnit.bids[0], activeLabels, adUnitCode); + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Ad Unit: div-gpt-ad-1460505748561-0 has declared property labelAny with an empty array. Ad Unit is still enabled!`); + }); + + it('should return "true" if label operators are not present on the Ad Unit or Bidder', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + + // adUnit level check + const activeAdUnit = isLabelActivated(adUnit, activeLabels, adUnitCode); + expect(activeAdUnit).to.equal(true); + + // bidder level check + const activeBidder = isLabelActivated(adUnit.bids[0], activeLabels, adUnitCode); + expect(activeBidder).to.equal(true); + }); + + it('should filter out the values correctly for the label operators "labelAll"', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + + // adUnit level checks + adUnit.labelAll = labelAll; + + // const labelAll = ['mobile', 'tablet', 'desktop', 'HD-Tv']; + // const activeLabels = ['mobile', 'tablet', 'desktop']; + + const activeAdUnit = isLabelActivated(adUnit, activeLabels, adUnitCode); + expect(activeAdUnit).to.equal(false) + + // bidder level checks + adUnit.bids[0].labelAll = labelAll; + + const activeBidder = isLabelActivated(adUnit.bids[0], activeLabels, adUnitCode); + expect(activeBidder).to.equal(false); + }); + + it('should filter out the values correctly for the label operators "labelAny"', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + + // adUnit level checks + adUnit.labelAny = labelAny; + + // const labelAny = ['mobile', 'tablet']; + // const activeLabels = ['mobile', 'tablet', 'desktop']; + + const activeAdUnit = isLabelActivated(adUnit, activeLabels, adUnitCode); + expect(activeAdUnit).to.equal(true) + + // bidder level checks + adUnit.bids[0].labelAny = labelAny; + + const activeBidder = isLabelActivated(adUnit.bids[0], activeLabels, adUnitCode); + expect(activeBidder).to.equal(true); + }); + }); + + describe('isSizeConfigActivated(mediaType, sizeConfig)', function () { + it('should return "false" for the scenarios where sizeConfig should should not get activated', function () { + // banner test + const sizeConfigBanner = { minViewPort: [0, 0], sizes: [] }; + const bannerActive = isSizeConfigActivated('banner', sizeConfigBanner); + expect(bannerActive).to.equal(false); + + // video test + const sizeConfigVideo = { minViewPort: [0, 0], playerSize: [] }; + const videoActive = isSizeConfigActivated('video', sizeConfigVideo); + expect(videoActive).to.equal(false); + + // native test + const sizeConfigNative = { minViewPort: [0, 0], active: false }; + const nativeActive = isSizeConfigActivated('native', sizeConfigNative); + expect(nativeActive).to.equal(false); + }); + + it('should return "true" for the scenarios where sizeConfig should get activated', function () { + // banner test + const sizeConfigBanner = { minViewPort: [0, 0], sizes: [[300, 600], [970, 1200]] }; + const bannerActive = isSizeConfigActivated('banner', sizeConfigBanner); + expect(bannerActive).to.equal(true); + + // video test + const sizeConfigVideo = { minViewPort: [0, 0], playerSize: [[640, 400]] }; + const videoActive = isSizeConfigActivated('video', sizeConfigVideo); + expect(videoActive).to.equal(true); + + // native test + const sizeConfigNative = { minViewPort: [0, 0], active: true }; + const nativeActive = isSizeConfigActivated('native', sizeConfigNative); + expect(nativeActive).to.equal(true); + }); + + it('should return "false" if mediaType does not match "banner", "video" or "native"', function () { + const sizeConfig = { minViewPort: [0, 0], sizes: [[300, 600], [970, 1200]] }; + const active = isSizeConfigActivated('unknownMediaType', sizeConfig); + expect(active).to.equal(false); + }); + }); + + describe('getActiveSizeBucket(sizeConfig, activeViewport)', function () { + it('should return the correct value of size bucket that is active (based on current viewport size) from a given set of size buckets defined in sizeConfig', function () { + const sizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [720, 500], sizes: [[300, 200], [300, 400]] }, + { minViewPort: [1200, 900], sizes: [[720, 400], [1000, 700]] } + ]; + + // test scenario 1 + const activeViewportA = [1000, 800]; + const activeSizeBucketA = getActiveSizeBucket(sizeConfig, activeViewportA); + expect(activeSizeBucketA).to.deep.equal([720, 500]); + + // test scenario 2 + const activeViewportB = [1300, 600]; + const activeSizeBucketB = getActiveSizeBucket(sizeConfig, activeViewportB); + expect(activeSizeBucketB).to.deep.equal([]); + }); + }); + + describe('getRelevantMediaTypesForBidder(sizeConfig, activeViewport)', function () { + beforeEach(function () { + sinon.spy(internal, 'checkBidderSizeConfigFormat'); + sinon.spy(internal, 'getActiveSizeBucket'); + }); + + afterEach(function () { + internal.checkBidderSizeConfigFormat.restore(); + internal.getActiveSizeBucket.restore(); + }); + it('should return an empty array if the bidder sizeConfig object is not formatted correctly', function () { + const sizeConfig = [ + { minViewPort: [], relevantMediaTypes: ['none'] }, + { minViewPort: [700, 0], relevantMediaTypes: ['banner', 'video'] } + ]; + const activeViewport = [720, 600]; + const relevantMediaTypes = getRelevantMediaTypesForBidder(sizeConfig, activeViewport); + expect(relevantMediaTypes).to.deep.equal([]); + }); + + it('should call function checkBidderSizeConfigFormat() once', function () { + const sizeConfig = [ + { minViewPort: [], relevantMediaTypes: ['none'] }, + { minViewPort: [700, 0], relevantMediaTypes: ['banner', 'video'] } + ]; + const activeViewport = [720, 600]; + getRelevantMediaTypesForBidder(sizeConfig, activeViewport); + + sinon.assert.callCount(internal.checkBidderSizeConfigFormat, 1); + sinon.assert.calledWith(internal.checkBidderSizeConfigFormat, sizeConfig); + }); + + it('should call function getActiveSizeBucket() once', function () { + const sizeConfig = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [700, 0], relevantMediaTypes: ['banner', 'video'] } + ]; + const activeViewport = [720, 600]; + getRelevantMediaTypesForBidder(sizeConfig, activeViewport); + + sinon.assert.callCount(internal.getActiveSizeBucket, 1); + sinon.assert.calledWith(internal.getActiveSizeBucket, sizeConfig, activeViewport); + }); + + it('should return the array contained in "relevantMediaTypes" property whose sizeBucket matches with the current viewport', function () { + const sizeConfig = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [700, 0], relevantMediaTypes: ['banner', 'video'] } + ]; + const activeVewport = [720, 600]; + const relevantMediaTypes = getRelevantMediaTypesForBidder(sizeConfig, activeVewport); + expect(relevantMediaTypes).to.deep.equal(['banner', 'video']); + }); + }); + + describe('getAdUnitDetail(auctionId, adUnit)', function () { + const adUnitDetailFixture_1 = { + adUnitCode: 'div-gpt-ad-1460505748561-0', + mediaTypes: {}, + sizeBucketToSizeMap: {}, + activeViewport: {}, + transformedMediaTypes: {} + }; + const adUnitDetailFixture_2 = { + adUnitCode: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'instream', + playerSize: [300, 460] + } + }, + sizeBucketToSizeMap: {}, + activeViewport: {}, + transformedMediaTypes: { banner: {}, video: {} } + } + beforeEach(function () { + sinon + .stub(sizeMappingInternalStore, 'getAuctionDetail') + .withArgs('a1b2c3') + .returns({ + usingSizeMappingV2: true, + adUnits: [adUnitDetailFixture_1] + }); + + sinon + .stub(sizeMappingInternalStore, 'setAuctionDetail') + .withArgs('a1b2c3', adUnitDetailFixture_2); + + sinon + .stub(internal, 'getFilteredMediaTypes') + .withArgs(adUnitDetailFixture_2.mediaTypes) + .returns(adUnitDetailFixture_2); + + sinon.spy(utils, 'logInfo'); + }); + + afterEach(function () { + sizeMappingInternalStore.getAuctionDetail.restore(); + sizeMappingInternalStore.setAuctionDetail.restore(); + internal.getFilteredMediaTypes.restore(); + utils.logInfo.restore(); + }); + + it('should return adUnit detail object from "sizeMappingInternalStore" if adUnit is alreay present in the store', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + const adUnitDetail = getAdUnitDetail('a1b2c3', adUnit); + sinon.assert.callCount(sizeMappingInternalStore.getAuctionDetail, 1); + expect(adUnitDetail).to.deep.equal(adUnitDetailFixture_1); + }); + + it('should store value in "sizeMappingInterStore" object if adUnit is NOT preset in this object', function () { + const [, adUnit] = utils.deepClone(AD_UNITS); + const adUnitDetail = getAdUnitDetail('a1b2c3', adUnit); + sinon.assert.callCount(sizeMappingInternalStore.setAuctionDetail, 1); + sinon.assert.callCount(internal.getFilteredMediaTypes, 1); + expect(adUnitDetail).to.deep.equal(adUnitDetailFixture_2); + }); + + it('should log info message to show the details for activeSizeBucket', function () { + const [, adUnit] = utils.deepClone(AD_UNITS); + getAdUnitDetail('a1b2c3', adUnit); + sinon.assert.callCount(utils.logInfo, 1); + sinon.assert.calledWith(utils.logInfo, `SizeMappingV2:: AdUnit: div-gpt-ad-1460505748561-1 - Active size buckets after filtration: `, adUnitDetailFixture_2.sizeBucketToSizeMap); + }); + + it('should log info message if any of the mediaTypes defined in adUnit.mediaTypes got filtered out', function () { + const [, adUnit] = utils.deepClone(AD_UNITS); + + internal.getFilteredMediaTypes.restore(); + + const adUnitDetailFixture = { + adUnitCode: 'div-gpt-ad-1460505748561-1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'instream', + playerSize: [300, 460] + } + }, + sizeBucketToSizeMap: {}, + activeViewport: {}, + transformedMediaTypes: { banner: {} } + } + + sinon + .stub(internal, 'getFilteredMediaTypes') + .withArgs(adUnitDetailFixture.mediaTypes) + .returns(adUnitDetailFixture); + + getAdUnitDetail('a1b2c3', adUnit); + + sinon.assert.callCount(utils.logInfo, 2); + sinon.assert.calledWith(utils.logInfo.getCall(1), `SizeMappingV2:: AdUnit: div-gpt-ad-1460505748561-1 - mediaTypes that got filtered out: video`); + }); + }); + + describe('getFilteredMediaTypes(mediaTypes)', function () { + beforeEach(function () { + sinon + .stub(utils, 'getWindowTop') + .returns({ + innerWidth: 1680, + innerHeight: 269 + }); + + sinon.spy(utils, 'logWarn'); + }); + afterEach(function () { + utils.getWindowTop.restore(); + utils.logWarn.restore(); + }); + it('should return filteredMediaTypes object with all four properties (mediaTypes, transformedMediaTypes, activeViewport, sizeBucketToSizeMap) evaluated correctly', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + const expectedMediaTypes = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, // remove if < 750px + { minViewPort: [750, 0], sizes: [[300, 250], [300, 600]] }, // between 750px and 1199px + { minViewPort: [1200, 0], sizes: [[970, 90], [728, 90], [300, 250]] }, // between 1200px and 1599px + { minViewPort: [1600, 0], sizes: [[1000, 300], [970, 90], [728, 90], [300, 250]] } // greater than 1600px + ] + }, + video: { + context: 'instream', + sizeConfig: [ + { minViewPort: [0, 0], playerSize: [] }, + { minViewPort: [800, 0], playerSize: [[640, 400]] }, + { minViewPort: [1200, 0], playerSize: [] } + ] + }, + native: { + image: { + required: true, + sizes: [150, 50] + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + }, + sizeConfig: [ + { minViewPort: [0, 0], active: false }, + { minViewPort: [600, 0], active: true }, + { minViewPort: [1000, 0], active: false } + ] + } + }; + const expectedSizeBucketToSizeMap = { + banner: { + activeSizeBucket: [1600, 0], + activeSizeDimensions: [ + [ + 1000, + 300 + ], + [ + 970, + 90 + ], + [ + 728, + 90 + ], + [ + 300, + 250 + ] + ] + }, + video: { + activeSizeBucket: [ + 1200, + 0 + ], + activeSizeDimensions: [] + }, + native: { + activeSizeBucket: [ + 1000, + 0 + ], + activeSizeDimensions: 'NA' + } + }; + const expectedActiveViewport = [1680, 269]; + const expectedTransformedMediaTypes = { + banner: { + filteredSizeConfig: [ + { + minViewPort: [ + 1600, + 0 + ], + sizes: [ + [ + 1000, + 300 + ], + [ + 970, + 90 + ], + [ + 728, + 90 + ], + [ + 300, + 250 + ] + ] + } + ], + sizeConfig: [ + { + minViewPort: [ + 0, + 0 + ], + sizes: [], + }, + { + minViewPort: [ + 750, + 0 + ], + sizes: [ + [ + 300, + 250 + ], + [ + 300, + 600 + ] + ] + }, + { + minViewPort: [ + 1200, + 0 + ], + sizes: [ + [ + 970, + 90 + ], + [ + 728, + 90 + ], + [ + 300, + 250 + ] + ] + }, + { + minViewPort: [ + 1600, + 0 + ], + sizes: [ + [ + 1000, + 300 + ], + [ + 970, + 90 + ], + [ + 728, + 90 + ], + [ + 300, + 250 + ] + ] + } + ], + sizes: [ + [ + 1000, + 300 + ], + [ + 970, + 90 + ], + [ + 728, + 90 + ], + [ + 300, + 250 + ] + ] + } + } + const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); + expect(mediaTypes).to.deep.equal(expectedMediaTypes); + expect(activeViewport).to.deep.equal(expectedActiveViewport); + expect(sizeBucketToSizeMap).to.deep.equal(expectedSizeBucketToSizeMap); + expect(transformedMediaTypes).to.deep.equal(expectedTransformedMediaTypes); + }); + + it('should throw a warning message if Iframe blocks viewport size to be evaluated correctly', function () { + const [adUnit] = utils.deepClone(AD_UNITS); + utils.getWindowTop.restore(); + sinon + .stub(utils, 'getWindowTop') + .throws(); + getFilteredMediaTypes(adUnit.mediaTypes); + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingv2:: Unfriendly iframe blocks viewport size to be evaluated correctly`); + }); + }); + + describe('getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src })', function () { + const basic_AdUnit = [{ + code: 'adUnit1', + mediaTypes: { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [500, 0], sizes: [[300, 200], [400, 600]] } + ] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }, { + bidder: 'rubicon', + params: { + accountId: 14062, + siteId: 70608, + zoneId: 498816 + }, + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], relevantMediaTypes: ['banner'] } + ] + }], + transactionId: '123456' + }]; + const adUnitDetailFixture = { + adUnitCode: 'adUnit1', + transactionId: '123456', + sizes: [[300, 200], [400, 600]], + mediaTypes: { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [600, 0], sizes: [[300, 200], [400, 600]] } + ] + } + }, + sizeBucketToSizeMap: { + banner: { + activeSizeBucket: [[500, 0]], + activeSizeDimensions: [[300, 200], [400, 600]] + } + }, + activeViewport: [560, 260], + transformedMediaTypes: { + banner: { + filteredSizeConfig: [ + { minViewPort: [500, 0], sizes: [[300, 200], [400, 600]] } + ], + sizeConfig: [ + { minViewPort: [0, 0], sizes: [[]] }, + { minViewPort: [500, 0], sizes: [[300, 200], [400, 600]] } + ], + sizes: [[300, 200], [400, 600]] + } + } + }; + beforeEach(function () { + sinon + .stub(internal, 'getAdUnitDetail') + .withArgs('6d51e2d7-1447-4242-b6af-aaa5525a2c6e', basic_AdUnit[0]) + .returns(adUnitDetailFixture); + }); + + afterEach(function () { + internal.getAdUnitDetail.restore(); + }); + + it('should return an array of bids specific to the bidder', function () { + const expectedMediaTypes = { + banner: { + filteredSizeConfig: [ + { minViewPort: [500, 0], sizes: [[300, 200], [400, 600]] } + ], + sizeConfig: [ + { minViewPort: [0, 0], sizes: [[]] }, + { minViewPort: [500, 0], sizes: [[300, 200], [400, 600]] } + ], + sizes: [[300, 200], [400, 600]] + } + } + + const bidRequests = getBids({ + bidderCode: 'appnexus', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: basic_AdUnit, + labels: [], + src: 'client' + }); + expect(bidRequests[0].mediaTypes).to.deep.equal(expectedMediaTypes); + }); + }); }); From 72b69df813364273c0291e155b045d503e93dd3c Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sat, 4 Jan 2020 03:22:56 +0530 Subject: [PATCH 35/41] more unit tests for getBids function --- modules/sizeMappingV2.js | 10 +- test/spec/modules/sizeMappingV2_spec.js | 403 ++++++++++++++++-------- 2 files changed, 275 insertions(+), 138 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 8b8b439815b..eba68406705 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -17,7 +17,9 @@ export const internal = { checkBidderSizeConfigFormat, getActiveSizeBucket, getFilteredMediaTypes, - getAdUnitDetail + getAdUnitDetail, + getRelevantMediaTypesForBidder, + isLabelActivated }; // 'sizeMappingInternalStore' contains information whether a particular auction is using size mapping V2 (the new size mapping spec), @@ -459,7 +461,7 @@ export function getAdUnitDetail(auctionId, adUnit) { export function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, labels, src }) { return adUnits.reduce((result, adUnit) => { - if (isLabelActivated(adUnit, labels, adUnit.code)) { + if (internal.isLabelActivated(adUnit, labels, adUnit.code)) { if (adUnit.mediaTypes && utils.isValidMediaTypes(adUnit.mediaTypes)) { const { activeViewport, transformedMediaTypes } = internal.getAdUnitDetail(auctionId, adUnit); @@ -472,7 +474,7 @@ export function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, label result .push(adUnit.bids.filter(bid => bid.bidder === bidderCode) .reduce((bids, bid) => { - if (isLabelActivated(bid, labels, adUnit.code)) { + if (internal.isLabelActivated(bid, labels, adUnit.code)) { // handle native params const nativeParams = adUnit.nativeParams || utils.deepAccess(adUnit, 'mediaTypes.native'); if (nativeParams) { @@ -484,7 +486,7 @@ export function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, label bid = Object.assign({}, bid, utils.getDefinedParams(adUnit, ['mediaType', 'renderer'])); if (bid.sizeConfig) { - const relevantMediaTypes = getRelevantMediaTypesForBidder(bid.sizeConfig, activeViewport); + const relevantMediaTypes = internal.getRelevantMediaTypesForBidder(bid.sizeConfig, activeViewport); if (relevantMediaTypes.length === 0) { utils.logError(`SizeMappingV2:: AdUnit: ${adUnit.code}, Bidder: ${bidderCode} - sizeConfig is not configured properly. This bidder won't be eligible for sizeConfig checks and will remail active.`); bid = Object.assign({}, bid); diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index 5ea93e5ea7b..31ee71ea868 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -1173,37 +1173,14 @@ describe('sizeMappingV2', function () { const expectedSizeBucketToSizeMap = { banner: { activeSizeBucket: [1600, 0], - activeSizeDimensions: [ - [ - 1000, - 300 - ], - [ - 970, - 90 - ], - [ - 728, - 90 - ], - [ - 300, - 250 - ] - ] + activeSizeDimensions: [[1000, 300], [970, 90], [728, 90], [300, 250]] }, video: { - activeSizeBucket: [ - 1200, - 0 - ], + activeSizeBucket: [1200, 0], activeSizeDimensions: [] }, native: { - activeSizeBucket: [ - 1000, - 0 - ], + activeSizeBucket: [1000, 0], activeSizeDimensions: 'NA' } }; @@ -1212,116 +1189,16 @@ describe('sizeMappingV2', function () { banner: { filteredSizeConfig: [ { - minViewPort: [ - 1600, - 0 - ], - sizes: [ - [ - 1000, - 300 - ], - [ - 970, - 90 - ], - [ - 728, - 90 - ], - [ - 300, - 250 - ] - ] + minViewPort: [1600, 0], sizes: [[1000, 300], [970, 90], [728, 90], [300, 250]] } ], sizeConfig: [ - { - minViewPort: [ - 0, - 0 - ], - sizes: [], - }, - { - minViewPort: [ - 750, - 0 - ], - sizes: [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ] - }, - { - minViewPort: [ - 1200, - 0 - ], - sizes: [ - [ - 970, - 90 - ], - [ - 728, - 90 - ], - [ - 300, - 250 - ] - ] - }, - { - minViewPort: [ - 1600, - 0 - ], - sizes: [ - [ - 1000, - 300 - ], - [ - 970, - 90 - ], - [ - 728, - 90 - ], - [ - 300, - 250 - ] - ] - } + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [750, 0], sizes: [[300, 250], [300, 600]] }, + { minViewPort: [1200, 0], sizes: [[970, 90], [728, 90], [300, 250]] }, + { minViewPort: [1600, 0], sizes: [[1000, 300], [970, 90], [728, 90], [300, 250]] } ], - sizes: [ - [ - 1000, - 300 - ], - [ - 970, - 90 - ], - [ - 728, - 90 - ], - [ - 300, - 250 - ] + sizes: [[1000, 300], [970, 90], [728, 90], [300, 250] ] } } @@ -1368,7 +1245,7 @@ describe('sizeMappingV2', function () { zoneId: 498816 }, sizeConfig: [ - { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, { minViewPort: [700, 0], relevantMediaTypes: ['banner'] } ] }], @@ -1411,10 +1288,20 @@ describe('sizeMappingV2', function () { .stub(internal, 'getAdUnitDetail') .withArgs('6d51e2d7-1447-4242-b6af-aaa5525a2c6e', basic_AdUnit[0]) .returns(adUnitDetailFixture); + + sinon.spy(internal, 'getRelevantMediaTypesForBidder'); + + sinon.spy(utils, 'logInfo'); + sinon.spy(utils, 'logError'); + sinon.spy(utils, 'logWarn'); }); afterEach(function () { internal.getAdUnitDetail.restore(); + internal.getRelevantMediaTypesForBidder.restore(); + utils.logInfo.restore(); + utils.logError.restore(); + utils.logWarn.restore(); }); it('should return an array of bids specific to the bidder', function () { @@ -1431,7 +1318,211 @@ describe('sizeMappingV2', function () { } } + const bidRequests_1 = getBids({ + bidderCode: 'appnexus', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: basic_AdUnit, + labels: [], + src: 'client' + }); + expect(bidRequests_1[0].mediaTypes).to.deep.equal(expectedMediaTypes); + expect(bidRequests_1[0].bidder).to.equal('appnexus'); + + const bidRequests_2 = getBids({ + bidderCode: 'rubicon', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0aa', + adUnits: basic_AdUnit, + labels: [], + src: 'client' + }); + expect(bidRequests_2[0]).to.be.undefined; + sinon.assert.callCount(internal.getRelevantMediaTypesForBidder, 1); + }); + + it('should log an error message if ad unit is disabled because there are no active media types left after size config filtration', function () { + internal.getAdUnitDetail.restore(); + + const adUnit = utils.deepClone(basic_AdUnit); + adUnit[0].mediaTypes.banner.sizeConfig = [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [600, 0], sizes: [[300, 200], [400, 600]] } + ]; + + const adUnitDetailFixture = { + adUnitCode: 'adUnit1', + mediaTypes: { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [600, 0], sizes: [[300, 200], [400, 600]] } + ] + } + }, + sizeBucketToSizeMap: { + banner: { + activeSizeBucket: [0, 0], + activeSizeDimensions: [[]] + } + }, + activeViewport: [560, 260], + transformedMediaTypes: {} + }; + + sinon + .stub(internal, 'getAdUnitDetail') + .withArgs('6d51e2d7-1447-4242-b6af-aaa5525a2c6e', adUnit[0]) + .returns(adUnitDetailFixture); + const bidRequests = getBids({ + bidderCode: 'appnexus', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: adUnit, + labels: [], + src: 'client' + }); + expect(bidRequests[0]).to.be.undefined; + sinon.assert.callCount(utils.logInfo, 1); + sinon.assert.calledWith(utils.logInfo, `SizeMappingV2:: Ad Unit: adUnit1 is disabled since there are no active media types after sizeConfig filtration.`); + }); + + it('should throw an error if bidder level sizeConfig is not configured properly', function () { + internal.getAdUnitDetail.restore(); + + const adUnit = utils.deepClone(basic_AdUnit); + adUnit[0].bids[1].sizeConfig = [ + { minViewPort: [], relevantMediaTypes: ['none'] }, + { minViewPort: [700, 0], relevantMediaTypes: ['banner'] } + ]; + + sinon + .stub(internal, 'getAdUnitDetail') + .withArgs('6d51e2d7-1447-4242-b6af-aaa5525a2c6e', adUnit[0]) + .returns(adUnitDetailFixture); + + const bidRequests = getBids({ + bidderCode: 'rubicon', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: adUnit, + labels: [], + src: 'client' + }); + + expect(bidRequests[0]).to.not.be.undefined; + sinon.assert.callCount(utils.logError, 1); + sinon.assert.calledWith(utils.logError, `SizeMappingV2:: AdUnit: adUnit1, Bidder: rubicon - sizeConfig is not configured properly. This bidder won't be eligible for sizeConfig checks and will remail active.`); + }); + + it('should ensure bidder relevantMediaTypes is a subset of active media types at the ad unit level', function () { + internal.getAdUnitDetail.restore(); + + const adUnit = utils.deepClone(basic_AdUnit); + adUnit[0].bids[1].sizeConfig = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [400, 0], relevantMediaTypes: ['banner'] } + ]; + + sinon + .stub(internal, 'getAdUnitDetail') + .withArgs('6d51e2d7-1447-4242-b6af-aaa5525a2c6e', adUnit[0]) + .returns(adUnitDetailFixture); + + const bidRequests = getBids({ + bidderCode: 'rubicon', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: adUnit, + labels: [], + src: 'client' + }); + expect(bidRequests[0]).to.not.be.undefined; + expect(bidRequests[0].mediaTypes.banner).to.not.be.undefined; + expect(bidRequests[0].mediaTypes.banner.sizes).to.deep.equal([[300, 200], [400, 600]]); + }); + + it('should logInfo if bidder relevantMediaTypes contains media type that is not active at the ad unit level', function () { + internal.getAdUnitDetail.restore(); + + const adUnit = utils.deepClone(basic_AdUnit); + adUnit[0].mediaTypes = { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [[300, 200], [400, 600]] } + ] + }, + native: { + sizeConfig: [ + { minViewPort: [0, 0], active: false }, + { minViewPort: [400, 0], active: true } + ] + } + }; + + adUnit[0].bids[1].sizeConfig = [ + { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, + { minViewPort: [200, 0], relevantMediaTypes: ['banner'] } + ] + + const adUnitDetailFixture = { + adUnitCode: 'adUnit1', + mediaTypes: { + banner: { + sizeConfig: [ + { minViewPort: [0, 0], sizes: [] }, + { minViewPort: [700, 0], sizes: [[300, 200], [400, 600]] } + ] + }, + native: { + sizeConfig: [ + { minViewPort: [0, 0], active: false }, + { minViewPort: [400, 0], active: true } + ] + } + }, + sizeBucketToSizeMap: { + banner: { + activeSizeBucket: [0, 0], + activeSizeDimensions: [[]] + }, + native: { + activeSizeBucket: [400, 0], + activeSizeDimensions: 'NA' + } + }, + activeViewport: [560, 260], + transformedMediaTypes: { + native: {} + } + }; + + sinon + .stub(internal, 'getAdUnitDetail') + .withArgs('6d51e2d7-1447-4242-b6af-aaa5525a2c6e', adUnit[0]) + .returns(adUnitDetailFixture); + + const bidRequests = getBids({ + bidderCode: 'rubicon', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: adUnit, + labels: [], + src: 'client' + }); + expect(bidRequests[0]).to.be.undefined; + sinon.assert.callCount(utils.logInfo, 1); + sinon.assert.calledWith(utils.logInfo, `SizeMappingV2:: AdUnit: adUnit1, Bidder: rubicon - 'relevantMediaTypes' for this bidder does not match with any of the active mediaTypes at the Ad Unit level. This bidder is disabled.`); + }); + + it('should throw a warning if mediaTypes object is not correctly formatted', function () { + sinon + .stub(utils, 'isValidMediaTypes') + .returns(false); + + getBids({ bidderCode: 'appnexus', auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', bidderRequestId: '393a43193a0ac', @@ -1439,7 +1530,51 @@ describe('sizeMappingV2', function () { labels: [], src: 'client' }); - expect(bidRequests[0].mediaTypes).to.deep.equal(expectedMediaTypes); + sinon.assert.callCount(utils.logWarn, 1); + sinon.assert.calledWith(utils.logWarn, `SizeMappingV2:: Ad Unit: adUnit1 has declared invalid mediaTypes or has not declared a mediaTypes property`); + + utils.isValidMediaTypes.restore(); }); + + it('should log a message if ad unit is disabled due to a failing label check', function () { + sinon + .stub(internal, 'isLabelActivated') + .onFirstCall() + .returns(false); + + getBids({ + bidderCode: 'appnexus', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: basic_AdUnit, + labels: [], + src: 'client' + }); + + sinon.assert.callCount(utils.logInfo, 1); + sinon.assert.calledWith(utils.logInfo, `SizeMappingV2:: Ad Unit: adUnit1 is disabled due to failing label check.`); + + internal.isLabelActivated.restore(); + }); + + it('should log a message if bidder is disabled due to a failing label check', function () { + const stub = sinon.stub(internal, 'isLabelActivated') + stub.onFirstCall().returns(true); + stub.onSecondCall().returns(false); + + getBids({ + bidderCode: 'appnexus', + auctionId: '6d51e2d7-1447-4242-b6af-aaa5525a2c6e', + bidderRequestId: '393a43193a0ac', + adUnits: basic_AdUnit, + labels: [], + src: 'client' + }); + + sinon.assert.callCount(utils.logInfo, 1); + sinon.assert.calledWith(utils.logInfo, `SizeMappingV2:: AdUnit: adUnit1, Bidder: appnexus - Label check for this bidder has failed. This bidder is disabled.`); + + internal.isLabelActivated.restore(); + }) }); }); From ce367224940965db96c2621d95806fe384adc6bc Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sat, 4 Jan 2020 17:21:49 +0530 Subject: [PATCH 36/41] add sizeMapping usage example --- .../gpt/responsiveAds_sizeMappingV2.html | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 integrationExamples/gpt/responsiveAds_sizeMappingV2.html diff --git a/integrationExamples/gpt/responsiveAds_sizeMappingV2.html b/integrationExamples/gpt/responsiveAds_sizeMappingV2.html new file mode 100644 index 00000000000..780b3cd4bba --- /dev/null +++ b/integrationExamples/gpt/responsiveAds_sizeMappingV2.html @@ -0,0 +1,120 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + From 85548912133d234da6358429908791189d07f75a Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Sat, 4 Jan 2020 17:47:27 +0530 Subject: [PATCH 37/41] delete sizeMappingV2 directory --- .../gpt/sizeMappingv2/banner_ad.html | 93 ------------ .../banner_ad_with_bidder_level_checks.html | 98 ------------ ...ad_with_label_and_size_mapping_checks.html | 130 ---------------- .../gpt/sizeMappingv2/instream_video_ad.html | 108 -------------- .../gpt/sizeMappingv2/multi_format_ad.html | 130 ---------------- ...ti_format_ad_with_bidder_level_checks.html | 139 ------------------ .../only_bidder_level_checks.html | 122 --------------- src/adapterManager.js | 1 - 8 files changed, 821 deletions(-) delete mode 100644 integrationExamples/gpt/sizeMappingv2/banner_ad.html delete mode 100644 integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html delete mode 100644 integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html delete mode 100644 integrationExamples/gpt/sizeMappingv2/instream_video_ad.html delete mode 100644 integrationExamples/gpt/sizeMappingv2/multi_format_ad.html delete mode 100644 integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html delete mode 100644 integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad.html b/integrationExamples/gpt/sizeMappingv2/banner_ad.html deleted file mode 100644 index 4d072d7c98a..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/banner_ad.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html deleted file mode 100644 index 087806be853..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_bidder_level_checks.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html b/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html deleted file mode 100644 index e14fdb23755..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/banner_ad_with_label_and_size_mapping_checks.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/sizeMappingv2/instream_video_ad.html b/integrationExamples/gpt/sizeMappingv2/instream_video_ad.html deleted file mode 100644 index 4b8141c9df3..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/instream_video_ad.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - Prebid.js JW Platform Example - - - - -

Prebid Video - JW Platform

-
-
-
- - - - - - - diff --git a/integrationExamples/gpt/sizeMappingv2/multi_format_ad.html b/integrationExamples/gpt/sizeMappingv2/multi_format_ad.html deleted file mode 100644 index ef79001342a..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/multi_format_ad.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html b/integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html deleted file mode 100644 index 3996991f8d3..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/multi_format_ad_with_bidder_level_checks.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html b/integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html deleted file mode 100644 index 2ba8903c84f..00000000000 --- a/integrationExamples/gpt/sizeMappingv2/only_bidder_level_checks.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - - diff --git a/src/adapterManager.js b/src/adapterManager.js index 2d645a0cc75..cffc72b4580 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -11,7 +11,6 @@ import includes from 'core-js/library/fn/array/includes'; import find from 'core-js/library/fn/array/find'; import { adunitCounter } from './adUnits'; import { getRefererInfo } from './refererDetection'; -import { hook } from './hook'; var utils = require('./utils.js'); var CONSTANTS = require('./constants.json'); From f872991c446bd8ca2c888672bdd6dfa5698d836c Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Tue, 7 Jan 2020 17:00:15 +0530 Subject: [PATCH 38/41] add doctype declaration --- integrationExamples/gpt/responsiveAds_sizeMappingV2.html | 1 + 1 file changed, 1 insertion(+) diff --git a/integrationExamples/gpt/responsiveAds_sizeMappingV2.html b/integrationExamples/gpt/responsiveAds_sizeMappingV2.html index 780b3cd4bba..d262af5199a 100644 --- a/integrationExamples/gpt/responsiveAds_sizeMappingV2.html +++ b/integrationExamples/gpt/responsiveAds_sizeMappingV2.html @@ -1,3 +1,4 @@ + From 79d8b16688fc11447046f2679c704ca2340c1f64 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Wed, 8 Jan 2020 12:52:55 +0530 Subject: [PATCH 39/41] fix LGTM alert and revert changes to pbjs_api.spec.js --- modules/sizeMappingV2.js | 6 +++--- src/prebid.js | 27 ++++++++++++--------------- test/spec/unit/pbjs_api_spec.js | 6 +++--- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index eba68406705..6a748c5a055 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -87,7 +87,7 @@ export function checkAdUnitSetupHook(adUnits) { if (mediaTypes.banner) { const banner = mediaTypes.banner; if (banner.sizes) { - adUnit = adUnitSetupChecks.validateBannerMediaType(adUnit); + adUnitSetupChecks.validateBannerMediaType(adUnit); } else if (banner.sizeConfig) { if (Array.isArray(banner.sizeConfig)) { let deleteBannerMediaType = false; @@ -135,7 +135,7 @@ export function checkAdUnitSetupHook(adUnits) { if (mediaTypes.video) { const video = mediaTypes.video; if (video.playerSize) { - adUnit = adUnitSetupChecks.validateVideoMediaType(adUnit); + adUnitSetupChecks.validateVideoMediaType(adUnit); } else if (video.sizeConfig) { if (Array.isArray(video.sizeConfig)) { let deleteVideoMediaType = false; @@ -182,7 +182,7 @@ export function checkAdUnitSetupHook(adUnits) { if (mediaTypes.native) { const native = mediaTypes.native; - adUnit = adUnitSetupChecks.validateNativeMediaType(adUnit); + adUnitSetupChecks.validateNativeMediaType(adUnit); if (mediaTypes.native.sizeConfig) { native.sizeConfig.forEach(config => { diff --git a/src/prebid.js b/src/prebid.js index a22a46c66b3..40dfdffd413 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -89,10 +89,9 @@ function validateBannerMediaType(adUnit) { // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.banner.sizes adUnit.sizes = bannerSizes; } else { - utils.logError('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.'); + utils.logError('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.'); delete adUnit.mediaTypes.banner } - return adUnit; } function validateVideoMediaType(adUnit) { @@ -108,10 +107,9 @@ function validateVideoMediaType(adUnit) { // Deprecation Warning: This property will be deprecated in next release in favor of adUnit.mediaTypes.video.playerSize adUnit.sizes = videoSizes; } else { - utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); + utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.'); delete adUnit.mediaTypes.video.playerSize; } - return adUnit; } function validateNativeMediaType(adUnit) { @@ -128,7 +126,6 @@ function validateNativeMediaType(adUnit) { utils.logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.'); delete adUnit.mediaTypes.native.icon.sizes; } - return adUnit; } export const adUnitSetupChecks = { @@ -147,18 +144,18 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { } if (mediaTypes.banner) { - adUnit = validateBannerMediaType(adUnit); + validateBannerMediaType(adUnit); } if (mediaTypes.video) { const video = mediaTypes.video; if (video.playerSize) { - adUnit = validateVideoMediaType(adUnit); + validateVideoMediaType(adUnit); } } if (mediaTypes.native) { - adUnit = validateNativeMediaType(adUnit); + validateNativeMediaType(adUnit); } return true; }); @@ -194,7 +191,7 @@ $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { * @alias module:pbjs.getAdserverTargetingForAdUnitCode * @returns {Object} returnObj return bids */ -$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function (adUnitCode) { +$$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCode = function(adUnitCode) { return $$PREBID_GLOBAL$$.getAdserverTargeting(adUnitCode)[adUnitCode]; }; @@ -302,7 +299,7 @@ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function (adUnit, customSlotMatching * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes * @alias module:pbjs.setTargetingForAst */ -$$PREBID_GLOBAL$$.setTargetingForAst = function (adUnitCodes) { +$$PREBID_GLOBAL$$.setTargetingForAst = function(adUnitCodes) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.setTargetingForAn', arguments); if (!targeting.isApntagDefined()) { utils.logError('window.apntag is not defined on the page'); @@ -468,7 +465,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo */ adUnits.forEach(adUnit => { // get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present - const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }); + const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || {'banner': 'banner'}); // get the bidder's mediaTypes const allBidders = adUnit.bids.map(bid => bid.bidder); @@ -514,7 +511,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo return; } - const auction = auctionManager.createAuction({ adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId }); + const auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId}); let adUnitsLen = adUnits.length; if (adUnitsLen > 15) { @@ -832,7 +829,7 @@ $$PREBID_GLOBAL$$.que.push(() => listenMessagesFromCreative()); * the Prebid script has been fully loaded. * @alias module:pbjs.cmd.push */ -$$PREBID_GLOBAL$$.cmd.push = function (command) { +$$PREBID_GLOBAL$$.cmd.push = function(command) { if (typeof command === 'function') { try { command.call(); @@ -847,7 +844,7 @@ $$PREBID_GLOBAL$$.cmd.push = function (command) { $$PREBID_GLOBAL$$.que.push = $$PREBID_GLOBAL$$.cmd.push; function processQueue(queue) { - queue.forEach(function (cmd) { + queue.forEach(function(cmd) { if (typeof cmd.called === 'undefined') { try { cmd.call(); @@ -862,7 +859,7 @@ function processQueue(queue) { /** * @alias module:pbjs.processQueue */ -$$PREBID_GLOBAL$$.processQueue = function () { +$$PREBID_GLOBAL$$.processQueue = function() { hook.ready(); processQueue($$PREBID_GLOBAL$$.que); processQueue($$PREBID_GLOBAL$$.cmd); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 7eab76073b1..b59911153e5 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1611,7 +1611,7 @@ describe('Unit: Prebid Module', function () { }); expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[300, 250], [300, 600]]); expect(auctionArgs.adUnits[0].mediaTypes.banner).to.be.undefined; - assert.ok(logErrorSpy.calledWith('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.')); + assert.ok(logErrorSpy.calledWith('Detected a mediaTypes.banner object without a proper sizes field. Please ensure the sizes are listed like: [[300, 250], ...]. Removing invalid mediaTypes.banner object from request.')); let badVideo1 = [{ code: 'testb2', @@ -1629,7 +1629,7 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[600, 600]]); expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.be.undefined; expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; - assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); + assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); let badVideo2 = [{ code: 'testb3', @@ -1647,7 +1647,7 @@ describe('Unit: Prebid Module', function () { expect(auctionArgs.adUnits[0].sizes).to.deep.equal([[600, 600]]); expect(auctionArgs.adUnits[0].mediaTypes.video.playerSize).to.be.undefined; expect(auctionArgs.adUnits[0].mediaTypes.video).to.exist; - assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); + assert.ok(logErrorSpy.calledWith('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request.')); let badNativeImgSize = [{ code: 'testb4', From aa53ccca16bce9dabcc9454ed16fe1b9b606bb0f Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Wed, 8 Jan 2020 13:11:30 +0530 Subject: [PATCH 40/41] fix LGTM alerts in sizeMappingV2_spec file --- test/spec/modules/sizeMappingV2_spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index 31ee71ea868..f26bd147814 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -221,7 +221,7 @@ describe('sizeMappingV2', function () { let adUnits = utils.deepClone(AD_UNITS); delete adUnits[0].mediaTypes; - adUnits = checkAdUnitSetupHook(adUnits); + checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); sinon.assert.calledWith(utils.logError, 'Detected adUnit.code \'div-gpt-ad-1460505748561-0\' did not have a \'mediaTypes\' object defined. This is a required field for the auction, so this adUnit has been removed.'); }); @@ -253,7 +253,7 @@ describe('sizeMappingV2', function () { // deleteing the sizeConfig property from the first ad unit. delete adUnits[0].mediaTypes.banner.sizeConfig; - adUnits = checkAdUnitSetupHook(adUnits); + checkAdUnitSetupHook(adUnits); sinon.assert.callCount(utils.logError, 1); sinon.assert.calledWith(utils.logError, 'Detected a mediaTypes.banner object did not include required property sizes or sizeConfig. Removing invalid mediaTypes.banner object from Ad Unit.'); }); @@ -434,7 +434,7 @@ describe('sizeMappingV2', function () { expect(adUnits[0].mediaTypes).to.have.property('banner'); expect(adUnits[1].mediaTypes).to.have.property('banner'); - const validatedAdUnits = checkAdUnitSetupHook(adUnits); + checkAdUnitSetupHook(adUnits); // after calling checkAdUnitSetupHook, the mediaTypes.banner object should still be present for both the Ad Units. expect(adUnits[0].mediaTypes).to.have.property('banner'); @@ -1201,7 +1201,7 @@ describe('sizeMappingV2', function () { sizes: [[1000, 300], [970, 90], [728, 90], [300, 250] ] } - } + }; const { mediaTypes, sizeBucketToSizeMap, activeViewport, transformedMediaTypes } = getFilteredMediaTypes(adUnit.mediaTypes); expect(mediaTypes).to.deep.equal(expectedMediaTypes); expect(activeViewport).to.deep.equal(expectedActiveViewport); @@ -1316,7 +1316,7 @@ describe('sizeMappingV2', function () { ], sizes: [[300, 200], [400, 600]] } - } + }; const bidRequests_1 = getBids({ bidderCode: 'appnexus', @@ -1465,7 +1465,7 @@ describe('sizeMappingV2', function () { adUnit[0].bids[1].sizeConfig = [ { minViewPort: [0, 0], relevantMediaTypes: ['none'] }, { minViewPort: [200, 0], relevantMediaTypes: ['banner'] } - ] + ]; const adUnitDetailFixture = { adUnitCode: 'adUnit1', From b41fca80eacdfb3fd6716f8016f356fb53305d61 Mon Sep 17 00:00:00 2001 From: Neelanjan Sen Date: Tue, 3 Mar 2020 15:05:47 +0530 Subject: [PATCH 41/41] add file extension for imports --- modules/sizeMappingV2.js | 12 ++++++------ test/spec/modules/sizeMappingV2_spec.js | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 6a748c5a055..ec3604faa97 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -3,14 +3,14 @@ * This implementation replaces global sizeConfig with a adUnit/bidder level sizeConfig with support for labels. */ -import * as utils from '../src/utils'; -import { processNativeAdUnitParams } from '../src/native'; -import { adunitCounter } from '../src/adUnits'; -import includes from 'core-js/library/fn/array/includes'; -import { getHook } from '../src/hook'; +import * as utils from '../src/utils.js'; +import { processNativeAdUnitParams } from '../src/native.js'; +import { adunitCounter } from '../src/adUnits.js'; +import includes from 'core-js/library/fn/array/includes.js'; +import { getHook } from '../src/hook.js'; import { adUnitSetupChecks -} from '../src/prebid'; +} from '../src/prebid.js'; // allows for sinon.spy, sinon.stub, etc to unit test calls made to these functions internally export const internal = { diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index f26bd147814..4b76ce2fa1c 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import * as utils from '../../../src/utils'; +import * as utils from '../../../src/utils.js'; import { isUsingNewSizeMapping, @@ -14,9 +14,9 @@ import { getFilteredMediaTypes, getBids, internal -} from '../../../modules/sizeMappingV2'; +} from '../../../modules/sizeMappingV2.js'; -import { adUnitSetupChecks } from '../../../src/prebid'; +import { adUnitSetupChecks } from '../../../src/prebid.js'; const AD_UNITS = [{ code: 'div-gpt-ad-1460505748561-0',