diff --git a/libraries/appnexusKeywords/anKeywords.js b/libraries/appnexusKeywords/anKeywords.js new file mode 100644 index 00000000000..e4c770a0b18 --- /dev/null +++ b/libraries/appnexusKeywords/anKeywords.js @@ -0,0 +1,106 @@ +import {_each, getValueString, isArray, isStr, mergeDeep, isNumber} from '../../src/utils.js'; +import {getAllOrtbKeywords} from '../keywords/keywords.js'; + +/** + * Converts an object of arrays (either strings or numbers) into an array of objects containing key and value properties + * normally read from bidder params + * eg { foo: ['bar', 'baz'], fizz: ['buzz'] } + * becomes [{ key: 'foo', value: ['bar', 'baz']}, {key: 'fizz', value: ['buzz']}] + * @param {Object} keywords object of arrays representing keyvalue pairs + * @param {string} paramName name of parent object (eg 'keywords') containing keyword data, used in error handling + * @returns {Array<{key, value}>} + */ +export function transformBidderParamKeywords(keywords, paramName = 'keywords') { + const arrs = []; + + _each(keywords, (v, k) => { + if (isArray(v)) { + let values = []; + _each(v, (val) => { + val = getValueString(paramName + '.' + k, val); + if (val || val === '') { + values.push(val); + } + }); + v = values; + } else { + v = getValueString(paramName + '.' + k, v); + if (isStr(v)) { + v = [v]; + } else { + return; + } // unsuported types - don't send a key + } + v = v.filter(kw => kw !== '') + const entry = {key: k} + if (v.length > 0) { + entry.value = v; + } + arrs.push(entry); + }); + + return arrs; +} + +// converts a comma separated list of keywords into the standard keyword object format used in appnexus bid params +// 'genre=rock,genre=pop,pets=dog,music' goes to { 'genre': ['rock', 'pop'], 'pets': ['dog'], 'music': [''] } +export function convertKeywordStringToANMap(keyStr) { + if (isStr(keyStr) && keyStr !== '') { + // will split based on commas and will eat white space before/after the comma + return convertKeywordsToANMap(keyStr.split(/\s*(?:,)\s*/)); + } else { + return {} + } +} + +/** + * @param {Array} kwarray: keywords as an array of strings + * @return {{}} appnexus-style keyword map + */ +function convertKeywordsToANMap(kwarray) { + const result = {}; + kwarray.forEach(kw => { + // if = exists, then split + if (kw.indexOf('=') !== -1) { + let kwPair = kw.split('='); + let key = kwPair[0]; + let val = kwPair[1]; + + // then check for existing key in result > if so add value to the array > if not, add new key and create value array + if (result.hasOwnProperty(key)) { + result[key].push(val); + } else { + result[key] = [val]; + } + } else { + if (!result.hasOwnProperty(kw)) { + result[kw] = []; + } + } + }) + return result; +} + +/** + * @param ortb2 + * @return {{}} appnexus-style keyword map using all keywords contained in ortb2 + */ +export function getANMapFromOrtb(ortb2) { + return convertKeywordsToANMap(getAllOrtbKeywords(ortb2)); +} + +export function getANKewyordParamFromMaps(...anKeywordMaps) { + return transformBidderParamKeywords( + mergeDeep(...anKeywordMaps.map(kwMap => Object.fromEntries( + Object.entries(kwMap || {}) + .map(([k, v]) => [k, (isNumber(v) || isStr(v)) ? [v] : v]) + ))) + ) +} + +export function getANKeywordParam(ortb2, ...anKeywordsMaps) { + return getANKewyordParamFromMaps( + getANMapFromOrtb(ortb2), + ...anKeywordsMaps + ) +} diff --git a/libraries/keywords/keywords.js b/libraries/keywords/keywords.js new file mode 100644 index 00000000000..645c9c8d38f --- /dev/null +++ b/libraries/keywords/keywords.js @@ -0,0 +1,31 @@ +import {CLIENT_SECTIONS} from '../../src/fpd/oneClient.js'; +import {deepAccess} from '../../src/utils.js'; + +const ORTB_KEYWORDS_PATHS = ['user.keywords'].concat( + CLIENT_SECTIONS.flatMap((prefix) => ['keywords', 'content.keywords'].map(suffix => `${prefix}.${suffix}`)) +); + +/** + * @param commaSeparatedKeywords: any number of either keyword arrays, or comma-separated keyword strings + * @returns an array with all unique keywords contained across all inputs + */ +export function mergeKeywords(...commaSeparatedKeywords) { + const keywords = new Set(); + commaSeparatedKeywords + .filter(kwds => kwds) + .flatMap(kwds => Array.isArray(kwds) ? kwds : kwds.split(',')) + .map(kw => kw.replace(/^\s*/, '').replace(/\s*$/, '')) + .filter(kw => kw) + .forEach(kw => keywords.add(kw)); + return Array.from(keywords.keys()); +} + +/** + * Get an array with all keywords contained in an ortb2 object. + */ +export function getAllOrtbKeywords(ortb2, ...extraCommaSeparatedKeywords) { + return mergeKeywords( + ...ORTB_KEYWORDS_PATHS.map(path => deepAccess(ortb2, path)), + ...extraCommaSeparatedKeywords + ) +} diff --git a/modules/adprimeBidAdapter.js b/modules/adprimeBidAdapter.js index 49c18c2d067..55ee1f0900c 100644 --- a/modules/adprimeBidAdapter.js +++ b/modules/adprimeBidAdapter.js @@ -3,6 +3,7 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { isFn, deepAccess, logMessage } from '../src/utils.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const BIDDER_CODE = 'adprime'; const AD_URL = 'https://delta.adprime.com/pbjs'; @@ -128,7 +129,7 @@ export const spec = { wPlayer: sizes ? sizes[0] : 0, hPlayer: sizes ? sizes[1] : 0, schain: bid.schain || {}, - keywords: bid.params.keywords || [], + keywords: getAllOrtbKeywords(bidderRequest.ortb2, bid.params.keywords), audiences: bid.params.audiences || [], identeties, bidFloor: getBidFloor(bid) diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 40cfe18f025..cf785a1fc87 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -13,8 +13,7 @@ import { isStr, logError, logMessage, - logWarn, - transformBidderParamKeywords + logWarn } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -22,6 +21,7 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'adrelevantis'; const URL = 'https://ssp.adrelevantis.com/prebid'; @@ -194,10 +194,6 @@ export const spec = { params.use_pmt_rule = (typeof params.usePaymentRule === 'boolean') ? params.usePaymentRule : false; if (params.usePaymentRule) { delete params.usePaymentRule; } - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -211,16 +207,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function formatRequest(payload, bidderRequest) { let request = []; @@ -475,14 +461,7 @@ function bidToTag(bid) { if (bid.params.externalImpId) { tag.external_imp_id = bid.params.externalImpId; } - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; - } + tag.keywords = getANKeywordParam(bid.ortb2, bid.params.keywords) if (bid.params.category) { tag.category = bid.params.category; } diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index c92b45622b6..3549b9e651e 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -22,9 +22,7 @@ import { logError, logInfo, logMessage, - logWarn, - mergeDeep, - transformBidderParamKeywords + logWarn } from '../src/utils.js'; import {Renderer} from '../src/Renderer.js'; import {config} from '../src/config.js'; @@ -36,8 +34,14 @@ import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {getStorageManager} from '../src/storageManager.js'; import {bidderSettings} from '../src/bidderSettings.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import { APPNEXUS_CATEGORY_MAPPING } from '../libraries/categoryTranslationMapping/index.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import {APPNEXUS_CATEGORY_MAPPING} from '../libraries/categoryTranslationMapping/index.js'; +import { + convertKeywordStringToANMap, + getANKewyordParamFromMaps, + getANKeywordParam, + transformBidderParamKeywords +} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'appnexus'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -250,31 +254,12 @@ export const spec = { payload.app = appIdObj; } - function grabOrtb2Keywords(ortb2Obj) { - const fields = ['site.keywords', 'site.content.keywords', 'user.keywords', 'app.keywords', 'app.content.keywords']; - let result = []; - - fields.forEach(path => { - let keyStr = deepAccess(ortb2Obj, path); - if (isStr(keyStr)) result.push(keyStr); - }); - return result; - } - // grab the ortb2 keyword data (if it exists) and convert from the comma list string format to object format let ortb2 = deepClone(bidderRequest && bidderRequest.ortb2); - let ortb2KeywordsObjList = grabOrtb2Keywords(ortb2).map(keyStr => convertStringToKeywordsObj(keyStr)); let anAuctionKeywords = deepClone(config.getConfig('appnexusAuctionKeywords')) || {}; - // need to convert the string values into array of strings, to properly merge values with other existing keys later - Object.keys(anAuctionKeywords).forEach(k => { if (isStr(anAuctionKeywords[k]) || isNumber(anAuctionKeywords[k])) anAuctionKeywords[k] = [anAuctionKeywords[k]] }); - // combine all sources of keywords (converted from string comma list to object format) into one object (that combines the values for shared keys) - let mergedAuctionKeywords = mergeDeep({}, anAuctionKeywords, ...ortb2KeywordsObjList); - - // convert to final format used by adserver - let auctionKeywords = transformBidderParamKeywords(mergedAuctionKeywords); + let auctionKeywords = getANKeywordParam(ortb2, anAuctionKeywords) if (auctionKeywords.length > 0) { - auctionKeywords.forEach(deleteValues); payload.keywords = auctionKeywords; } @@ -468,10 +453,6 @@ export const spec = { }, params); if (isOpenRtb) { - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -488,16 +469,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function strIsAppnexusViewabilityScript(str) { if (!str || str === '') return false; @@ -812,24 +783,9 @@ function bidToTag(bid) { tag.external_imp_id = bid.params.external_imp_id; } - let ortb2ImpKwStr = deepAccess(bid, 'ortb2Imp.ext.data.keywords'); - if ((isStr(ortb2ImpKwStr) && ortb2ImpKwStr !== '') || !isEmpty(bid.params.keywords)) { - // convert ortb2 from comma list string format to bid param object format - let ortb2ImpKwObj = convertStringToKeywordsObj(ortb2ImpKwStr); - - let bidParamsKwObj = (isPlainObject(bid.params.keywords)) ? deepClone(bid.params.keywords) : {}; - // need to convert the string values into an array of strings, to properly merge values with other existing keys later - Object.keys(bidParamsKwObj).forEach(k => { if (isStr(bidParamsKwObj[k]) || isNumber(bidParamsKwObj[k])) bidParamsKwObj[k] = [bidParamsKwObj[k]] }); - - // combine both sources of keywords into one merged object (that combines the values for shared keys) - let keywordsObj = mergeDeep({}, bidParamsKwObj, ortb2ImpKwObj); - - // convert to final format used by adserver - let keywordsUt = transformBidderParamKeywords(keywordsObj); - if (keywordsUt.length > 0) { - keywordsUt.forEach(deleteValues); - tag.keywords = keywordsUt; - } + const auKeywords = getANKewyordParamFromMaps(convertKeywordStringToANMap(deepAccess(bid, 'ortb2Imp.ext.data.keywords')), bid.params?.keywords); + if (auKeywords.length > 0) { + tag.keywords = auKeywords; } let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); @@ -1258,37 +1214,4 @@ function convertKeywordsToString(keywords) { return result; } -// converts a comma separated list of keywords into the standard keyword object format used in appnexus bid params -// 'genre=rock,genre=pop,pets=dog,music' goes to { 'genre': ['rock', 'pop'], 'pets': ['dog'], 'music': [''] } -function convertStringToKeywordsObj(keyStr) { - let result = {}; - - if (isStr(keyStr) && keyStr !== '') { - // will split based on commas and will eat white space before/after the comma - let keywordList = keyStr.split(/\s*(?:,)\s*/); - keywordList.forEach(kw => { - // if = exists, then split - if (kw.indexOf('=') !== -1) { - let kwPair = kw.split('='); - let key = kwPair[0]; - let val = kwPair[1]; - - // then check for existing key in result > if so add value to the array > if not, add new key and create value array - if (result.hasOwnProperty(key)) { - result[key].push(val); - } else { - result[key] = [val]; - } - } else { - // make a key with '' value; if key already exists > don't add - if (!result.hasOwnProperty(kw)) { - result[kw] = ['']; - } - } - }); - } - - return result; -} - registerBidder(spec); diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index fd41f352cb9..0b3c6d9cae8 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -1,7 +1,18 @@ -import { deepAccess, isArray, isStr, logWarn, triggerPixel, buildUrl, logInfo, getValue, getBidIdParameter } from '../src/utils.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; +import { + buildUrl, + deepAccess, + getBidIdParameter, + getValue, + isArray, + logInfo, + logWarn, + triggerPixel +} from '../src/utils.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; + const BIDDER_CODE = 'beop'; const ENDPOINT_URL = 'https://hb.beop.io/bid'; const TCF_VENDOR_ID = 666; @@ -41,18 +52,8 @@ export const spec = { const gdpr = bidderRequest.gdprConsent; const firstSlot = slots[0]; const kwdsFromRequest = firstSlot.kwds; - let keywords = []; - if (kwdsFromRequest) { - if (isArray(kwdsFromRequest)) { - keywords = kwdsFromRequest; - } else if (isStr(kwdsFromRequest)) { - if (kwdsFromRequest.indexOf(',') != -1) { - keywords = kwdsFromRequest.split(',').map((e) => { return e.trim() }); - } else { - keywords.push(kwdsFromRequest); - } - } - } + let keywords = getAllOrtbKeywords(bidderRequest.ortb2, kwdsFromRequest); + const payloadObject = { at: new Date().toString(), nid: firstSlot.nid, diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index d1017e85bc8..8f7821173c1 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -1,12 +1,4 @@ -import { - convertCamelToUnderscore, - convertTypes, - getBidRequest, - isArray, - isEmpty, - logError, - transformBidderParamKeywords -} from '../src/utils.js'; +import {convertCamelToUnderscore, convertTypes, getBidRequest, logError} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {auctionManager} from '../src/auctionManager.js'; @@ -14,7 +6,8 @@ import {find, includes} from '../src/polyfill.js'; import {getStorageManager} from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'craft'; const URL_BASE = 'https://gacraft.jp/prebid-v3'; @@ -114,9 +107,6 @@ export const spec = { 'keywords': transformBidderParamKeywords, }, params); if (isOpenRtb) { - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -136,16 +126,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function formatRequest(payload, bidderRequest) { let options = {}; if (!hasPurpose1Consent(bidderRequest?.gdprConsent)) { @@ -200,11 +180,8 @@ function bidToTag(bid) { tag.primary_size = tag.sizes[0]; tag.ad_types = []; tag.uuid = bid.bidId; - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } + const keywords = getANKeywordParam(bid.ortb2, bid.params.keywords); + if (keywords.length) { tag.keywords = keywords; } // TODO: why does this need to iterate through every ad unit? diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js index d46cc8ee309..3d34f2c8b26 100644 --- a/modules/displayioBidAdapter.js +++ b/modules/displayioBidAdapter.js @@ -3,6 +3,7 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {getWindowFromDocument, logWarn} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const ADAPTER_VERSION = '1.1.0'; const BIDDER_CODE = 'displayio'; @@ -105,7 +106,7 @@ function getPayload (bid, bidderRequest) { renderURL, data: { pagecat: pageCategory ? pageCategory.split(',').map(k => k.trim()) : [], - keywords: keywords ? keywords.split(',').map(k => k.trim()) : [], + keywords: getAllOrtbKeywords(bidderRequest.ortb2, keywords), lang_content: document.documentElement.lang, lang: window.navigator.language, domain: refererInfo.domain, diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index 363adccc01c..4768931950c 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -20,8 +20,7 @@ import { isStr, logError, logInfo, - logMessage, - transformBidderParamKeywords + logMessage } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -32,6 +31,7 @@ import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { APPNEXUS_CATEGORY_MAPPING } from '../libraries/categoryTranslationMapping/index.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'goldbach'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -398,10 +398,6 @@ export const spec = { params.use_pmt_rule = (typeof params.usePaymentRule === 'boolean') ? params.usePaymentRule : false; if (params.usePaymentRule) { delete params.usePaymentRule; } - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -425,16 +421,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function reloadViewabilityScriptWithCorrectParameters(bid) { let viewJsPayload = getAppnexusViewabilityScriptFromJsTrackers(bid.native.javascriptTrackers); @@ -779,14 +765,7 @@ function bidToTag(bid) { if (bid.params.externalImpId) { tag.external_imp_id = bid.params.externalImpId; } - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; - } + tag.keywords = getANKeywordParam(bid.ortb2, bid.params.keywords); let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index f13c3cf25f9..625190fba47 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -1,16 +1,44 @@ -import { convertCamelToUnderscore, isArray, isNumber, isPlainObject, logError, logInfo, deepAccess, logMessage, convertTypes, isStr, getParameterByName, deepClone, chunk, logWarn, getBidRequest, createTrackPixelHtml, isEmpty, transformBidderParamKeywords, getMaxValueFromArray, fill, getMinValueFromArray, isArrayOfNums, isFn } from '../src/utils.js'; -import { Renderer } from '../src/Renderer.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO, ADPOD } from '../src/mediaTypes.js'; -import { auctionManager } from '../src/auctionManager.js'; +import { + chunk, + convertCamelToUnderscore, + convertTypes, + createTrackPixelHtml, + deepAccess, + deepClone, + fill, + getBidRequest, + getMaxValueFromArray, + getMinValueFromArray, + getParameterByName, + isArray, + isArrayOfNums, + isEmpty, + isFn, + isNumber, + isPlainObject, + isStr, + logError, + logInfo, + logMessage, + logWarn +} from '../src/utils.js'; +import {Renderer} from '../src/Renderer.js'; +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; -import { OUTSTREAM, INSTREAM } from '../src/video.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { bidderSettings } from '../src/bidderSettings.js'; +import {INSTREAM, OUTSTREAM} from '../src/video.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {bidderSettings} from '../src/bidderSettings.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import { APPNEXUS_CATEGORY_MAPPING } from '../libraries/categoryTranslationMapping/index.js'; +import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import {APPNEXUS_CATEGORY_MAPPING} from '../libraries/categoryTranslationMapping/index.js'; +import { + getANKewyordParamFromMaps, + getANKeywordParam, + transformBidderParamKeywords +} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'mediafuse'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -191,16 +219,8 @@ export const spec = { payload.app = appIdObj; } - let auctionKeywords = config.getConfig('mediafuseAuctionKeywords'); - if (isPlainObject(auctionKeywords)) { - let aucKeywords = transformBidderParamKeywords(auctionKeywords); - - if (aucKeywords.length > 0) { - aucKeywords.forEach(deleteValues); - } - - payload.keywords = aucKeywords; - } + let mfKeywords = config.getConfig('mediafuseAuctionKeywords'); + payload.keywords = getANKeywordParam(bidderRequest?.ortb2, mfKeywords); if (config.getConfig('adpod.brandCategoryExclusion')) { payload.brand_category_uniqueness = true; @@ -343,10 +363,6 @@ export const spec = { params.use_pmt_rule = (typeof params.usePaymentRule === 'boolean') ? params.usePaymentRule : false; if (params.usePaymentRule) { delete params.usePaymentRule; } - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach(paramKey => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -370,16 +386,6 @@ export const spec = { } }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function reloadViewabilityScriptWithCorrectParameters(bid) { let viewJsPayload = getMediafuseViewabilityScriptFromJsTrackers(bid.native.javascriptTrackers); @@ -747,14 +753,8 @@ function bidToTag(bid) { tag.external_imp_id = bid.params.externalImpId; } if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; + tag.keywords = getANKewyordParamFromMaps(bid.params.keywords); } - let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { tag.gpid = gpid; diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js index 6fc866b5ff9..608ba20aa5f 100644 --- a/modules/pixfutureBidAdapter.js +++ b/modules/pixfutureBidAdapter.js @@ -1,20 +1,12 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; -import { find, includes } from '../src/polyfill.js'; -import { - convertCamelToUnderscore, - deepAccess, - isArray, - isEmpty, - isFn, - isNumber, - isPlainObject, - transformBidderParamKeywords -} from '../src/utils.js'; -import { auctionManager } from '../src/auctionManager.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; +import {find, includes} from '../src/polyfill.js'; +import {convertCamelToUnderscore, deepAccess, isArray, isFn, isNumber, isPlainObject} from '../src/utils.js'; +import {auctionManager} from '../src/auctionManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; +import {getANKeywordParam} from '../libraries/appnexusKeywords/anKeywords.js'; const SOURCE = 'pbjs'; const storageManager = getStorageManager({bidderCode: 'pixfuture'}); @@ -282,14 +274,7 @@ function bidToTag(bid) { if (bid.params.externalImpId) { tag.external_imp_id = bid.params.externalImpId; } - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; - } + tag.keywords = getANKeywordParam(bid.ortb2, bid.params.keywords) let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { @@ -369,14 +354,4 @@ function getBidFloor(bid) { return null; } -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - registerBidder(spec); diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js index 94e577d75fe..7c9108f60b1 100644 --- a/modules/prismaBidAdapter.js +++ b/modules/prismaBidAdapter.js @@ -1,8 +1,8 @@ import {ajax} from '../src/ajax.js'; import {config} from '../src/config.js'; -import { transformBidderParamKeywords } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {getANKeywordParam} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'prisma'; const BIDDER_URL = 'https://prisma.nexx360.io/prebid'; @@ -76,7 +76,7 @@ export const spec = { mediatypes: adunitValue.mediaTypes, bidfloor: 0, bidfloorCurrency: 'USD', - keywords: adunitValue.params.keywords ? transformBidderParamKeywords(adunitValue.params.keywords) : [], + keywords: getANKeywordParam(bidderRequest.ortb2, adunitValue.params.keywords) } adUnits.push(foo); if (adunitValue.userIdAsEids) userEids = adunitValue.userIdAsEids; diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js index f8ca260d490..a7aceb107b9 100644 --- a/modules/rasBidAdapter.js +++ b/modules/rasBidAdapter.js @@ -1,6 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const BIDDER_CODE = 'ras'; const VERSION = '1.0'; @@ -39,8 +40,9 @@ function parseParams(params, bidderRequest) { if (pageContext.dv) { newParams.DV = pageContext.dv; } - if (pageContext.keyWords && Array.isArray(pageContext.keyWords)) { - newParams.kwrd = pageContext.keyWords.join('+'); + const keywords = getAllOrtbKeywords(bidderRequest?.ortb2, pageContext.keyWords) + if (keywords.length > 0) { + newParams.kwrd = keywords.join('+') } if (pageContext.capping) { newParams.local_capping = pageContext.capping; diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 56ceaaa7444..36435a35807 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -1,8 +1,9 @@ -import {isEmpty, deepAccess, isStr} from '../src/utils.js'; +import {deepAccess, isStr} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import { Renderer } from '../src/Renderer.js'; +import {Renderer} from '../src/Renderer.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const BIDDER_CODE = 'richaudience'; let REFERER = ''; @@ -54,7 +55,7 @@ export const spec = { videoData: raiGetVideoInfo(bid), scr_rsl: raiGetResolution(), cpuc: (typeof window.navigator != 'undefined' ? window.navigator.hardwareConcurrency : null), - kws: (!isEmpty(bid.params.keywords) ? bid.params.keywords : null), + kws: getAllOrtbKeywords(bidderRequest.ortb2, bid.params.keywords).join(','), schain: bid.schain }; diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 20dbf1e4995..5fad246f27d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -20,6 +20,7 @@ import { mergeDeep, parseSizesInput, _each } from '../src/utils.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; @@ -168,7 +169,7 @@ export const converter = ortbConverter({ deepSetValue(data, 'ext.prebid.analytics', {'rubicon': {'client-analytics': true}}); } - addOrtbFirstPartyData(data, bidRequests); + addOrtbFirstPartyData(data, bidRequests, bidderRequest.ortb2); delete data?.ext?.prebid?.storedrequest; @@ -1191,9 +1192,9 @@ function setBidFloors(bidRequest, imp) { } } -function addOrtbFirstPartyData(data, nonBannerRequests) { +function addOrtbFirstPartyData(data, nonBannerRequests, ortb2) { let fpd = {}; - const keywords = new Set(); + const keywords = getAllOrtbKeywords(ortb2, ...nonBannerRequests.map(req => req.params.keywords)) nonBannerRequests.forEach(bidRequest => { const bidFirstPartyData = { user: {ext: {data: {...bidRequest.params.visitor}}}, @@ -1208,10 +1209,6 @@ function addOrtbFirstPartyData(data, nonBannerRequests) { } } - if (bidRequest.params.keywords) { - const keywordsArray = (!Array.isArray(bidRequest.params.keywords) ? bidRequest.params.keywords.split(',') : bidRequest.params.keywords); - keywordsArray.forEach(keyword => keywords.add(keyword)); - } fpd = mergeDeep(fpd, bidRequest.ortb2 || {}, bidFirstPartyData); // add user.id from config. @@ -1222,8 +1219,8 @@ function addOrtbFirstPartyData(data, nonBannerRequests) { mergeDeep(data, fpd); - if (keywords && keywords.size) { - deepSetValue(data, 'site.keywords', Array.from(keywords.values()).join(',')); + if (keywords && keywords.length) { + deepSetValue(data, 'site.keywords', keywords.join(',')); } delete data?.ext?.prebid?.storedrequest; } diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 6760a3c18ab..c99d0ab011d 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -5,6 +5,7 @@ import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { userSync } from '../src/userSync.js'; import { bidderSettings } from '../src/bidderSettings.js'; +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const BIDDER_CODE = 'sonobi'; const STR_ENDPOINT = 'https://apex.go.sonobi.com/trinity.json'; const PAGEVIEW_ID = generateUUID(); @@ -140,7 +141,7 @@ export const spec = { payload.eids = JSON.stringify(eids); } - let keywords = validBidRequests[0].params.keywords; // a CSV of keywords + let keywords = getAllOrtbKeywords(bidderRequest.ortb2, ...validBidRequests.map(br => br.params.keywords)).join(','); if (keywords) { payload.kw = keywords; diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index 28f2ddb7c6d..5cc063d16b4 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -5,12 +5,10 @@ import { getBidRequest, getParameterByName, isArray, - isEmpty, isFn, isNumber, isPlainObject, - logError, - transformBidderParamKeywords + logError } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -18,6 +16,7 @@ import {BANNER} from '../src/mediaTypes.js'; import {find, includes} from '../src/polyfill.js'; import {getStorageManager} from '../src/storageManager.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'winr'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -330,10 +329,6 @@ export const spec = { delete params.usePaymentRule; } - if (isPopulatedArray(params.keywords)) { - params.keywords.forEach(deleteValues); - } - Object.keys(params).forEach((paramKey) => { let convertedKey = convertCamelToUnderscore(paramKey); if (convertedKey !== paramKey) { @@ -347,16 +342,6 @@ export const spec = { }, }; -function isPopulatedArray(arr) { - return !!(isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function formatRequest(payload, bidderRequest) { let request = []; let options = { @@ -498,14 +483,7 @@ function bidToTag(bid) { if (bid.params.externalImpId) { tag.external_imp_id = bid.params.externalImpId; } - if (!isEmpty(bid.params.keywords)) { - let keywords = transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - tag.keywords = keywords; - } + tag.keywords = getANKeywordParam(bid.ortb2, bid.params.keywords) let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { diff --git a/src/utils.js b/src/utils.js index 61cd136b850..ece29732723 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,9 +1,10 @@ -import { config } from './config.js'; +import {config} from './config.js'; import clone from 'just-clone'; import {find, includes} from './polyfill.js'; import CONSTANTS from './constants.json'; import {GreedyPromise} from './utils/promise.js'; import {getGlobal} from './prebidGlobal.js'; + export { default as deepAccess } from 'dlv/index.js'; export { dset as deepSetValue } from 'dset'; @@ -1067,39 +1068,6 @@ export function pick(obj, properties) { }, {}); } -/** - * Converts an object of arrays (either strings or numbers) into an array of objects containing key and value properties - * normally read from bidder params - * eg { foo: ['bar', 'baz'], fizz: ['buzz'] } - * becomes [{ key: 'foo', value: ['bar', 'baz']}, {key: 'fizz', value: ['buzz']}] - * @param {Object} keywords object of arrays representing keyvalue pairs - * @param {string} paramName name of parent object (eg 'keywords') containing keyword data, used in error handling - */ -export function transformBidderParamKeywords(keywords, paramName = 'keywords') { - let arrs = []; - - _each(keywords, (v, k) => { - if (isArray(v)) { - let values = []; - _each(v, (val) => { - val = getValueString(paramName + '.' + k, val); - if (val || val === '') { values.push(val); } - }); - v = values; - } else { - v = getValueString(paramName + '.' + k, v); - if (isStr(v)) { - v = [v]; - } else { - return; - } // unsuported types - don't send a key - } - arrs.push({key: k, value: v}); - }); - - return arrs; -} - /** * Try to convert a value to a type. * If it can't be done, the value will be returned. diff --git a/test/spec/appnexusKeywords_spec.js b/test/spec/appnexusKeywords_spec.js new file mode 100644 index 00000000000..9bf567a27c5 --- /dev/null +++ b/test/spec/appnexusKeywords_spec.js @@ -0,0 +1,70 @@ +import {transformBidderParamKeywords} from '../../libraries/appnexusKeywords/anKeywords.js'; +import {expect} from 'chai/index.js'; +import * as utils from '../../src/utils.js'; + +describe('transformBidderParamKeywords', function () { + it('returns an array of objects when keyvalue is an array', function () { + let keywords = { + genre: ['rock', 'pop'] + }; + let result = transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'genre', + value: ['rock', 'pop'] + }]); + }); + + it('returns an array of objects when keyvalue is a string', function () { + let keywords = { + genre: 'opera' + }; + let result = transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'genre', + value: ['opera'] + }]); + }); + + it('returns an array of objects when keyvalue is a number', function () { + let keywords = { + age: 15 + }; + let result = transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'age', + value: ['15'] + }]); + }); + + it('returns an array of objects when using multiple keys with values of differing types', function () { + let keywords = { + genre: 'classical', + mix: ['1', 2, '3', 4], + age: 10 + }; + let result = transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'genre', + value: ['classical'] + }, { + key: 'mix', + value: ['1', '2', '3', '4'] + }, { + key: 'age', + value: ['10'] + }]); + }); + + it('returns an array of objects when the keyvalue uses an empty string', function () { + let keywords = { + test: [''], + test2: '' + }; + let result = transformBidderParamKeywords(keywords); + expect(result).to.deep.equal([{ + key: 'test', + }, { + key: 'test2', + }]); + }); +}); diff --git a/test/spec/keywords_spec.js b/test/spec/keywords_spec.js new file mode 100644 index 00000000000..e048ead3b98 --- /dev/null +++ b/test/spec/keywords_spec.js @@ -0,0 +1,102 @@ +import {getAllOrtbKeywords, mergeKeywords} from '../../libraries/keywords/keywords.js'; + +describe('mergeKeywords', () => { + Object.entries({ + 'single list': { + input: [ + 'one, two' + ], + output: [ + 'one', + 'two' + ] + }, + 'multiple lists': { + input: [ + 'one, two', + 'two, three' + ], + output: [ + 'one', + 'two', + 'three' + ] + }, + 'null lists': { + input: [ + undefined, + 'one, two', + null, + 'three' + ], + output: [ + 'one', + 'two', + 'three' + ] + }, + 'empty keywords': { + input: [ + 'one,,two' + ], + output: [ + 'one', + 'two' + ] + }, + 'extra whitespace': { + input: [ + ' one, two , three ' + ], + output: [ + 'one', + 'two', + 'three' + ] + }, + 'mixed with arrays': { + input: [ + ['one'], + 'one, two', + ['three', 'two'] + ], + output: [ + 'one', + 'two', + 'three' + ] + } + }).forEach(([t, {input, output}]) => { + it(`can merge ${t}`, () => { + expect(mergeKeywords(...input)).to.have.members(output); + }) + }) +}); + +describe('getAllOrtbKeywodrs', () => { + const SAMPLE_ORTB = { + app: { + keywords: 'one, two' + }, + site: { + content: { + keywords: 'one, three' + } + }, + user: { + keywords: 'four' + } + } + + it('can extract keywords from ortb', () => { + expect(getAllOrtbKeywords(SAMPLE_ORTB)).to.have.members([ + 'one', 'two', 'three', 'four' + ]); + }); + + it('merges with extra comma-separated keywords', () => { + expect(getAllOrtbKeywords(SAMPLE_ORTB, 'two,five')).to.have.members([ + 'one', 'two', 'three', 'four', 'five' + ]) + }) +}) diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index acbe558659d..7d351e03453 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -18,6 +18,18 @@ describe('AppNexusAdapter', function () { }); }); + function expectKeywords(actual, expected) { + expect(actual.length).to.equal(expected.length); + actual.forEach(el => { + const match = expected.find(ob => ob.key === el.key); + if (el.value) { + expect(el.value).to.have.members(match.value); + } else { + expect(match.value).to.not.exist; + } + }) + } + describe('isBidRequestValid', function () { let bid = { 'bidder': 'appnexus', @@ -737,7 +749,7 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest], bidderRequest); const payload = JSON.parse(request.data); - expect(payload.keywords).to.deep.equal([{ + expectKeywords(payload.keywords, [{ 'key': 'gender', 'value': ['m'] }, { @@ -872,26 +884,28 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); - expect(payload.tags[0].keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['5'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); + expectKeywords(payload.tags[0].keywords, [ + { + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + } + ]) }); it('should convert adUnit ortb2 keywords (when there are no bid param keywords) to proper form and attaches to request', function () { @@ -911,7 +925,7 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); - expect(payload.tags[0].keywords).to.deep.equal([{ + expectKeywords(payload.tags[0].keywords, [{ 'key': 'ortb2', 'value': ['yes'] }, { @@ -955,7 +969,7 @@ describe('AppNexusAdapter', function () { const request = spec.buildRequests([bidRequest]); const payload = JSON.parse(request.data); - expect(payload.tags[0].keywords).to.deep.equal([{ + expectKeywords(payload.tags[0].keywords, [{ 'key': 'single', 'value': ['val'] }, { diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 9199b259ad6..e26683074c8 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1,8 +1,8 @@ -import { getAdServerTargeting } from 'test/fixtures/fixtures.js'; -import { expect } from 'chai'; +import {getAdServerTargeting} from 'test/fixtures/fixtures.js'; +import {expect} from 'chai'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; -import {memoize, deepEqual, waitForElementToLoad} from 'src/utils.js'; +import {deepEqual, memoize, waitForElementToLoad} from 'src/utils.js'; var assert = require('assert'); @@ -894,93 +894,24 @@ describe('Utils', function () { }); }); - describe('transformBidderParamKeywords', function () { - it('returns an array of objects when keyvalue is an array', function () { - let keywords = { - genre: ['rock', 'pop'] - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'genre', - value: ['rock', 'pop'] - }]); - }); - - it('returns an array of objects when keyvalue is a string', function () { - let keywords = { - genre: 'opera' - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'genre', - value: ['opera'] - }]); - }); - - it('returns an array of objects when keyvalue is a number', function () { - let keywords = { - age: 15 - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'age', - value: ['15'] - }]); - }); - - it('returns an array of objects when using multiple keys with values of differing types', function () { - let keywords = { - genre: 'classical', - mix: ['1', 2, '3', 4], - age: 10 - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'genre', - value: ['classical'] - }, { - key: 'mix', - value: ['1', '2', '3', '4'] - }, { - key: 'age', - value: ['10'] - }]); - }); - - it('returns an array of objects when the keyvalue uses an empty string', function() { - let keywords = { - test: [''], - test2: '' - }; - let result = utils.transformBidderParamKeywords(keywords); - expect(result).to.deep.equal([{ - key: 'test', - value: [''] - }, { - key: 'test2', - value: [''] - }]); - }); - - describe('insertElement', function () { - it('returns a node at the top of the target by default', function () { - const toInsert = document.createElement('div'); - const target = document.getElementsByTagName('body')[0]; - const inserted = utils.insertElement(toInsert, document, 'body'); - expect(inserted).to.equal(target.firstChild); - }); - it('returns a node at bottom of target if 4th argument is true', function () { - const toInsert = document.createElement('div'); - const target = document.getElementsByTagName('html')[0]; - const inserted = utils.insertElement(toInsert, document, 'html', true); - expect(inserted).to.equal(target.lastChild); - }); - it('returns a node at top of the head if no target is given', function () { - const toInsert = document.createElement('div'); - const target = document.getElementsByTagName('head')[0]; - const inserted = utils.insertElement(toInsert); - expect(inserted).to.equal(target.firstChild); - }); + describe('insertElement', function () { + it('returns a node at the top of the target by default', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('body')[0]; + const inserted = utils.insertElement(toInsert, document, 'body'); + expect(inserted).to.equal(target.firstChild); + }); + it('returns a node at bottom of target if 4th argument is true', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('html')[0]; + const inserted = utils.insertElement(toInsert, document, 'html', true); + expect(inserted).to.equal(target.lastChild); + }); + it('returns a node at top of the head if no target is given', function () { + const toInsert = document.createElement('div'); + const target = document.getElementsByTagName('head')[0]; + const inserted = utils.insertElement(toInsert); + expect(inserted).to.equal(target.firstChild); }); });