From c07bbde3da66beb49f7ade6750f9c8cb95e672d9 Mon Sep 17 00:00:00 2001 From: Michael Moschovas Date: Wed, 25 Sep 2024 12:39:01 -0400 Subject: [PATCH] refactor to targeting file for readability and efficiency update --- src/targeting.js | 383 +++++++++++++------------- src/utils.js | 4 + test/fixtures/fixtures.js | 31 ++- test/spec/unit/core/targeting_spec.js | 11 +- test/spec/unit/pbjs_api_spec.js | 15 +- test/spec/utils_spec.js | 2 +- 6 files changed, 242 insertions(+), 204 deletions(-) diff --git a/src/targeting.js b/src/targeting.js index 9a2ea5d66fa3..022bf5470e0f 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -29,6 +29,7 @@ import { logInfo, logMessage, logWarn, + sortByHighestCpm, timestamp, uniques, } from './utils.js'; @@ -67,7 +68,7 @@ export function isBidUsable(bid) { // If two bids are found for same adUnitCode, we will use the highest one to take part in auction // This can happen in case of concurrent auctions // If adUnitBidLimit is set above 0 return top N number of bids -export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { +export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, winReducer, adUnitBidLimit = 0, hasModified = false, winSorter = sortByHighestCpm) { if (!hasModified) { const bids = []; const dealPrioritization = config.getConfig('sendBidsControl.dealPrioritization'); @@ -77,12 +78,13 @@ export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, Object.keys(buckets).forEach(bucketKey => { let bucketBids = []; let bidsByBidder = groupBy(buckets[bucketKey], 'bidderCode'); - Object.keys(bidsByBidder).forEach(key => bucketBids.push(bidsByBidder[key].reduce(highestCpmCallback))); + Object.keys(bidsByBidder).forEach(key => bucketBids.push(bidsByBidder[key].reduce(winReducer))); // if adUnitBidLimit is set, pass top N number bids - if (adUnitBidLimit > 0) { + if (adUnitBidLimit) { bucketBids = dealPrioritization ? bucketBids.sort(sortByDealAndPriceBucketOrCpm(true)) : bucketBids.sort((a, b) => b.cpm - a.cpm); bids.push(...bucketBids.slice(0, adUnitBidLimit)); } else { + bucketBids = bucketBids.sort(winSorter) bids.push(...bucketBids); } }); @@ -149,6 +151,17 @@ export function getGPTSlotsForAdUnits(adUnitCodes, customSlotMatching, getSlots }, Object.fromEntries(adUnitCodes.map(au => [au, []]))); } +/** + * Clears targeting for bids + */ +function clearTargeting(slot) { + pbTargetingKeys.forEach(key => { + if (slot.getTargeting(key)) { + slot.clearTargeting(key) + } + }) +} + /** * @typedef {Object.} targeting * @property {string} targeting_key @@ -169,12 +182,10 @@ export function newTargeting(auctionManager) { targeting.resetPresetTargeting = function(adUnitCode, customSlotMatching) { if (isGptPubadsDefined()) { const adUnitCodes = getAdUnitCodes(adUnitCode); - let unsetKeys = pbTargetingKeys.reduce((reducer, key) => { - reducer[key] = null; - return reducer; - }, {}); Object.values(getGPTSlotsForAdUnits(adUnitCodes, customSlotMatching)).forEach((slots) => { - slots.forEach(slot => slot.updateTargetingFromMap(unsetKeys)) + slots.forEach(slot => { + clearTargeting(slot) + }) }) } }; @@ -196,39 +207,35 @@ export function newTargeting(auctionManager) { }); }; - /** - * checks if bid has targeting set and belongs based on matching ad unit codes - * @return {boolean} true or false - */ - function bidShouldBeAddedToTargeting(bid, adUnitCodes) { - return bid.adserverTargeting && adUnitCodes && - ((isArray(adUnitCodes) && includes(adUnitCodes, bid.adUnitCode)) || - (typeof adUnitCodes === 'string' && bid.adUnitCode === adUnitCodes)); - }; + function addBidToTargeting(bids, bidderLevelTargetingEnabled = false, deals = false) { + if (!bidderLevelTargetingEnabled) return []; - /** - * Returns targeting for any bids which have deals if alwaysIncludeDeals === true - */ - function getDealBids(adUnitCodes, bidsReceived) { - if (config.getConfig('targetingControls.alwaysIncludeDeals') === true) { - const standardKeys = FEATURES.NATIVE ? TARGETING_KEYS_ARR.concat(NATIVE_TARGETING_KEYS) : TARGETING_KEYS_ARR.slice(); - - // we only want the top bid from bidders who have multiple entries per ad unit code - const bids = getHighestCpmBidsFromBidPool(bidsReceived, getHighestCpm); - - // populate targeting keys for the remaining bids if they have a dealId - return bids.map(bid => { - if (bid.dealId && bidShouldBeAddedToTargeting(bid, adUnitCodes)) { - return { - [bid.adUnitCode]: getTargetingMap(bid, standardKeys.filter( - key => typeof bid.adserverTargeting[key] !== 'undefined') - ) - }; + const standardKeys = FEATURES.NATIVE ? TARGETING_KEYS_ARR.concat(NATIVE_TARGETING_KEYS) : TARGETING_KEYS_ARR.slice(); + const allowSendAllBidsTargetingKeys = config.getConfig('targetingControls.allowSendAllBidsTargetingKeys'); + + const allowedSendAllBidTargeting = allowSendAllBidsTargetingKeys + ? allowSendAllBidsTargetingKeys.map((key) => TARGETING_KEYS[key]) + : standardKeys; + + return bids.reduce((result, bid) => { + if ((!deals || bid.dealId)) { + const targetingValue = getTargetingMap(bid, standardKeys.filter( + key => typeof bid.adserverTargeting[key] !== 'undefined' && + (deals || allowedSendAllBidTargeting.indexOf(key) !== -1))); + + if (targetingValue) { + result.push({[bid.adUnitCode]: targetingValue}) } - }).filter(bid => bid); // removes empty elements in array - } - return []; - }; + } + return result; + }, []); + } + + function getBidderTargeting(bids) { + const alwaysIncludeDeals = config.getConfig('targetingControls.alwaysIncludeDeals'); + const bidderLevelTargetingEnabled = config.getConfig('enableSendAllBids') || alwaysIncludeDeals; + return addBidToTargeting(bids, bidderLevelTargetingEnabled, alwaysIncludeDeals); + } /** * Returns filtered ad server targeting for custom and allowed keys. @@ -277,26 +284,15 @@ export function newTargeting(auctionManager) { * @param {string=} adUnitCode * @return {Object.} targeting */ - targeting.getAllTargeting = function(adUnitCode, bidsReceived = getBidsReceived()) { + targeting.getAllTargeting = function(adUnitCode, bidLimit, bidsReceived, winReducer = getHighestCpm, winSorter = sortByHighestCpm) { + bidsReceived ||= getBidsReceived(winReducer, winSorter); const adUnitCodes = getAdUnitCodes(adUnitCode); - - // Get targeting for the winning bid. Add targeting for any bids that have - // `alwaysUseBid=true`. If sending all bids is enabled, add targeting for losing bids. - var targeting = getWinningBidTargeting(adUnitCodes, bidsReceived) - .concat(getCustomBidTargeting(adUnitCodes, bidsReceived)) - .concat(config.getConfig('enableSendAllBids') ? getBidLandscapeTargeting(adUnitCodes, bidsReceived) : getDealBids(adUnitCodes, bidsReceived)) - .concat(getAdUnitTargeting(adUnitCodes)); - - // store a reference of the targeting keys - targeting.map(adUnitCode => { - Object.keys(adUnitCode).map(key => { - adUnitCode[key].map(targetKey => { - if (pbTargetingKeys.indexOf(Object.keys(targetKey)[0]) === -1) { - pbTargetingKeys = Object.keys(targetKey).concat(pbTargetingKeys); - } - }); - }); - }); + const sendAllBids = config.getConfig('enableSendAllBids'); + const bidLimitConfigValue = config.getConfig('sendBidsControl.bidLimit'); + const adUnitBidLimit = (sendAllBids && (bidLimit || bidLimitConfigValue)) || 0; + const { customKeysByUnit, filteredBids } = getfilteredBidsAndCustomKeys(adUnitCodes, bidsReceived); + const bidsSorted = getHighestCpmBidsFromBidPool(filteredBids, winReducer, adUnitBidLimit, undefined, winSorter); + let targeting = getTargetingLevels(bidsSorted, customKeysByUnit); const defaultKeys = Object.keys(Object.assign({}, DEFAULT_TARGETING_KEYS, NATIVE_KEYS)); let allowedKeys = config.getConfig(CFG_ALLOW_TARGETING_KEYS); @@ -332,6 +328,61 @@ export function newTargeting(auctionManager) { return targeting; }; + function updatePBTargetingKeys(adUnitCode) { + (Object.keys(adUnitCode)).forEach(key => { + adUnitCode[key].forEach(targetKey => { + const targetKeys = Object.keys(targetKey); + if (pbTargetingKeys.indexOf(targetKeys[0]) === -1) { + pbTargetingKeys = targetKeys.concat(pbTargetingKeys); + } + }); + }); + } + + function getTargetingLevels(bidsSorted, customKeysByUnit) { + const targeting = getWinningBidTargeting(bidsSorted) + .concat(getCustomBidTargeting(bidsSorted, customKeysByUnit)) + .concat(getBidderTargeting(bidsSorted)) + .concat(getAdUnitTargeting()); + + targeting.forEach(adUnitCode => { + updatePBTargetingKeys(adUnitCode); + }); + + return targeting; + } + + function getfilteredBidsAndCustomKeys(adUnitCodes, bidsReceived) { + const filteredBids = []; + const customKeysByUnit = {}; + + bidsReceived.forEach(bid => { + const adUnitIsEligible = includes(adUnitCodes, bid.adUnitCode); + const cpmAllowed = bidderSettings.get(bid.bidderCode, 'allowZeroCpmBids') === true ? bid.cpm >= 0 : bid.cpm > 0; + + if (adUnitIsEligible && cpmAllowed) { + filteredBids.push(bid); + Object.keys(bid.adserverTargeting) + .filter(getCustomKeys()) + .forEach(key => { + const truncKey = key.substring(0, MAX_DFP_KEYLENGTH); + const data = customKeysByUnit[bid.adUnitCode] || {}; + const value = [bid.adserverTargeting[key]]; + + if (data[truncKey]) { + data[truncKey] = data[truncKey].concat(value).filter(uniques); + } else { + data[truncKey] = value; + } + + customKeysByUnit[bid.adUnitCode] = data; + }) + } + }); + + return {filteredBids, customKeysByUnit}; + } + // warn about conflicting configuration config.getConfig('targetingControls', function (config) { if (deepAccess(config, CFG_ALLOW_TARGETING_KEYS) != null && deepAccess(config, CFG_ADD_TARGETING_KEYS) != null) { @@ -423,27 +474,30 @@ export function newTargeting(auctionManager) { }; }).reduce((p, c) => Object.assign(c, p), {}) }; - }).reduce(function (accumulator, targeting) { + }) + + targetingObj = targetingObj.reduce(function (accumulator, targeting) { var key = Object.keys(targeting)[0]; accumulator[key] = Object.assign({}, accumulator[key], targeting[key]); return accumulator; }, {}); + return targetingObj; } targeting.setTargetingForGPT = hook('sync', function (adUnit, customSlotMatching) { - // get our ad unit codes + // get our ad unit codes let targetingSet = targeting.getAllTargeting(adUnit); let resetMap = Object.fromEntries(pbTargetingKeys.map(key => [key, null])); Object.entries(getGPTSlotsForAdUnits(Object.keys(targetingSet), customSlotMatching)).forEach(([targetId, slots]) => { slots.forEach(slot => { - // now set new targeting keys + // now set new targeting keys Object.keys(targetingSet[targetId]).forEach(key => { let value = targetingSet[targetId][key]; if (typeof value === 'string' && value.indexOf(',') !== -1) { - // due to the check the array will be formed only if string has ',' else plain string will be assigned as value + // due to the check the array will be formed only if string has ',' else plain string will be assigned as value value = value.split(','); } targetingSet[targetId][key] = value; @@ -485,50 +539,53 @@ export function newTargeting(auctionManager) { return auctionManager.getAdUnitCodes() || []; } - function getBidsReceived() { - let bidsReceived = auctionManager.getBidsReceived(); - - if (!config.getConfig('useBidCache')) { - // don't use bid cache (i.e. filter out bids not in the latest auction) - bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId) - } else { - // if custom bid cache filter function exists, run for each bid from - // previous auctions. If it returns true, include bid in bid pool + function getBidsReceived(winReducer = getOldestHighestCpmBid, winSorter = undefined) { + let bidsReceived = auctionManager.getBidsReceived().reduce((bids, bid) => { + const bidCacheEnabled = config.getConfig('useBidCache'); const filterFunction = config.getConfig('bidCacheFilterFunction'); - if (typeof filterFunction === 'function') { - bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId || !!filterFunction(bid)) - } - } + const isBidFromLastAuction = latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId; + const filterFunctionResult = bidCacheEnabled && !isBidFromLastAuction && typeof filterFunction === 'function' ? !!filterFunction(bid) : true; + const cacheFilter = bidCacheEnabled || isBidFromLastAuction; + const bidFilter = cacheFilter && filterFunctionResult; - bidsReceived = bidsReceived - .filter(bid => deepAccess(bid, 'video.context') !== ADPOD) - .filter(isBidUsable); - - bidsReceived - .forEach(bid => { + if (bidFilter && deepAccess(bid, 'video.context') !== ADPOD && isBidUsable(bid)) { bid.latestTargetedAuctionId = latestAuctionForAdUnit[bid.adUnitCode]; - return bid; - }); + bids.push(bid) + } - return getHighestCpmBidsFromBidPool(bidsReceived, getOldestHighestCpmBid); + return bids; + }, []); + + return getHighestCpmBidsFromBidPool(bidsReceived, winReducer, undefined, undefined, undefined, winSorter); } /** * Returns top bids for a given adUnit or set of adUnits. * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes - * @param {Array} [bidsReceived=getBidsReceived()] - The received bids, defaulting to the result of getBidsReceived(). - * @return {Array} - An array of winning bids. + * @param {Array|undefined} [bids] - The received bids, defaulting to the result of getBidsReceived(). + * @param {function|undefined} [winReducer = getHighestCpm] - reducer method + * @param {function|undefined} [bids = sortByHighestCpm] - sorter method + * @return {[type]} [description] */ - targeting.getWinningBids = function(adUnitCode, bidsReceived = getBidsReceived()) { + targeting.getWinningBids = function(adUnitCode, bids, winReducer = getHighestCpm, winSorter = sortByHighestCpm) { + const usedCodes = []; + const bidsReceived = bids || getBidsReceived(winReducer, winSorter); const adUnitCodes = getAdUnitCodes(adUnitCode); + return bidsReceived - .filter(bid => includes(adUnitCodes, bid.adUnitCode)) - .filter(bid => (bidderSettings.get(bid.bidderCode, 'allowZeroCpmBids') === true) ? bid.cpm >= 0 : bid.cpm > 0) - .map(bid => bid.adUnitCode) - .filter(uniques) - .map(adUnitCode => bidsReceived - .filter(bid => bid.adUnitCode === adUnitCode ? bid : null) - .reduce(getHighestCpm)); + .reduce((result, bid) => { + const code = bid.adUnitCode; + const eligible = includes(adUnitCodes, code) && + ((bidderSettings.get(code, 'allowZeroCpmBids') === true) ? bid.cpm >= 0 : bid.cpm > 0) && + !includes(usedCodes, code); + + if (eligible) { + result.push(bid); + usedCodes.push(code); + } + + return result; + }, []); }; /** @@ -568,8 +625,17 @@ export function newTargeting(auctionManager) { * @param {string[]} adUnitCodes code array * @return {targetingArray} winning bids targeting */ - function getWinningBidTargeting(adUnitCodes, bidsReceived) { - let winners = targeting.getWinningBids(adUnitCodes, bidsReceived); + function getWinningBidTargeting(bidsReceived) { + let usedAdUnitCodes = []; + let winners = bidsReceived + .reduce((bids, bid) => { + if (!includes(usedAdUnitCodes, bid.adUnitCode)) { + bids.push(bid); + usedAdUnitCodes.push(bid.adUnitCode); + } + return bids; + }, []); + let standardKeys = getStandardKeys(); winners = winners.map(winner => { @@ -601,44 +667,6 @@ export function newTargeting(auctionManager) { .concat(TARGETING_KEYS_ARR).filter(uniques); // standard keys defined in the library. } - /** - * Merge custom adserverTargeting with same key name for same adUnitCode. - * e.g: Appnexus defining custom keyvalue pair foo:bar and Rubicon defining custom keyvalue pair foo:baz will be merged to foo: ['bar','baz'] - * - * @param {Object[]} acc Accumulator for reducer. It will store updated bidResponse objects - * @param {Object} bid BidResponse - * @param {number} index current index - * @param {Array} arr original array - */ - function mergeAdServerTargeting(acc, bid, index, arr) { - function concatTargetingValue(key) { - return function(currentBidElement) { - if (!isArray(currentBidElement.adserverTargeting[key])) { - currentBidElement.adserverTargeting[key] = [currentBidElement.adserverTargeting[key]]; - } - currentBidElement.adserverTargeting[key] = currentBidElement.adserverTargeting[key].concat(bid.adserverTargeting[key]).filter(uniques); - delete bid.adserverTargeting[key]; - } - } - - function hasSameAdunitCodeAndKey(key) { - return function(currentBidElement) { - return currentBidElement.adUnitCode === bid.adUnitCode && currentBidElement.adserverTargeting[key] - } - } - - Object.keys(bid.adserverTargeting) - .filter(getCustomKeys()) - .forEach(key => { - if (acc.length) { - acc.filter(hasSameAdunitCodeAndKey(key)) - .forEach(concatTargetingValue(key)); - } - }); - acc.push(bid); - return acc; - } - function getCustomKeys() { let standardKeys = getStandardKeys(); if (FEATURES.NATIVE) { @@ -649,71 +677,41 @@ export function newTargeting(auctionManager) { } } - function truncateCustomKeys(bid) { - return { - [bid.adUnitCode]: Object.keys(bid.adserverTargeting) - // Get only the non-standard keys of the losing bids, since we - // don't want to override the standard keys of the winning bid. - .filter(getCustomKeys()) - .map(key => { - return { - [key.substring(0, MAX_DFP_KEYLENGTH)]: [bid.adserverTargeting[key]] - }; - }) - } - } - /** * Get custom targeting key value pairs for bids. * @param {string[]} adUnitCodes code array * @return {targetingArray} bids with custom targeting defined in bidderSettings */ - function getCustomBidTargeting(adUnitCodes, bidsReceived) { - return bidsReceived - .filter(bid => includes(adUnitCodes, bid.adUnitCode)) - .map(bid => Object.assign({}, bid)) - .reduce(mergeAdServerTargeting, []) - .map(truncateCustomKeys) - .filter(bid => bid); // removes empty elements in array; - } - - /** - * Get targeting key value pairs for non-winning bids. - * @param {string[]} adUnitCodes code array - * @return {targetingArray} all non-winning bids targeting - */ - function getBidLandscapeTargeting(adUnitCodes, bidsReceived) { - const standardKeys = FEATURES.NATIVE ? TARGETING_KEYS_ARR.concat(NATIVE_TARGETING_KEYS) : TARGETING_KEYS_ARR.slice(); - const adUnitBidLimit = config.getConfig('sendBidsControl.bidLimit'); - const bids = getHighestCpmBidsFromBidPool(bidsReceived, getHighestCpm, adUnitBidLimit); - const allowSendAllBidsTargetingKeys = config.getConfig('targetingControls.allowSendAllBidsTargetingKeys'); + function getCustomBidTargeting(bidsSorted, customKeysByUnit) { + return bidsSorted + .reduce((acc, bid) => { + const newBid = Object.assign({}, bid); + const customKeysForUnit = customKeysByUnit[newBid.adUnitCode]; + const targeting = []; + + if (customKeysForUnit) { + Object.keys(customKeysForUnit).forEach(key => { + if (key && customKeysForUnit[key]) targeting.push({[key]: customKeysForUnit[key]}); + }) + } - const allowedSendAllBidTargeting = allowSendAllBidsTargetingKeys - ? allowSendAllBidsTargetingKeys.map((key) => TARGETING_KEYS[key]) - : standardKeys; + acc.push({[newBid.adUnitCode]: targeting}); - // populate targeting keys for the remaining bids - return bids.map(bid => { - if (bidShouldBeAddedToTargeting(bid, adUnitCodes)) { - return { - [bid.adUnitCode]: getTargetingMap(bid, standardKeys.filter( - key => typeof bid.adserverTargeting[key] !== 'undefined' && - allowedSendAllBidTargeting.indexOf(key) !== -1) - ) - }; - } - }).filter(bid => bid); // removes empty elements in array + return acc; + }, []); } function getTargetingMap(bid, keys) { - return keys.map(key => { - return { - [`${key}_${bid.bidderCode}`.substring(0, MAX_DFP_KEYLENGTH)]: [bid.adserverTargeting[key]] - }; - }); + return keys.reduce((targeting, key) => { + const value = bid.adserverTargeting[key]; + if (value) { + targeting.push({[`${key}_${bid.bidderCode}`.substring(0, MAX_DFP_KEYLENGTH)]: [bid.adserverTargeting[key]]}) + } + return targeting; + }, []); } - function getAdUnitTargeting(adUnitCodes) { + function getAdUnitTargeting() { function getTargetingObj(adUnit) { return deepAccess(adUnit, JSON_MAPPING.ADSERVER_TARGETING); } @@ -730,10 +728,13 @@ export function newTargeting(auctionManager) { } return auctionManager.getAdUnits() - .filter(adUnit => includes(adUnitCodes, adUnit.code) && getTargetingObj(adUnit)) - .map(adUnit => { - return {[adUnit.code]: getTargetingValues(adUnit)} - }); + .filter(adUnit => getTargetingObj(adUnit)) + .reduce((result, adUnit) => { + const targetingValues = getTargetingValues(adUnit); + + if (targetingValues)result.push({[adUnit.code]: targetingValues}); + return result; + }, []); } targeting.isApntagDefined = function() { diff --git a/src/utils.js b/src/utils.js index b30702af1a0f..015b1142d470 100644 --- a/src/utils.js +++ b/src/utils.js @@ -606,6 +606,10 @@ export function isApnGetTagDefined() { } } +export const sortByHighestCpm = (a, b) => { + return b.cpm - a.cpm; +} + /** * Fisher–Yates shuffle * http://stackoverflow.com/a/6274398 diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js index fb6cfe036e5c..5eaecac16ee3 100644 --- a/test/fixtures/fixtures.js +++ b/test/fixtures/fixtures.js @@ -966,7 +966,7 @@ export function getBidResponsesFromAPI() { export function getAdServerTargeting() { return { '/19968336/header-bid-tag-0': convertTargetingsFromOldToNew({ - 'foobar': '0x0,300x250,300x600', + 'foobar': '300x250,300x600,0x0', 'hb_size': '300x250', 'hb_pb': '10.00', 'hb_adid': '233bcbee889d46d', @@ -1035,11 +1035,36 @@ export function getTargetingKeys() { ], [ 'foobar', - ['0x0', '300x250', '300x600'] + ['300x250', '300x600', '0x0'] ] ]; } +export function getTargetingKeys2() { + return [ + [ + 'foobar', + '300x250' + ], + [ + TARGETING_KEYS.SIZE, + '300x250' + ], + [ + TARGETING_KEYS.PRICE_BUCKET, + '10.00' + ], + [ + TARGETING_KEYS.AD_ID, + '233bcbee889d46d' + ], + [ + TARGETING_KEYS.BIDDER, + 'appnexus' + ], + ]; +} + // Key/values used to set ad server targeting when bid landscape // targeting is on. export function getTargetingKeysBidLandscape() { @@ -1062,7 +1087,7 @@ export function getTargetingKeysBidLandscape() { ], [ 'foobar', - ['0x0', '300x250', '300x600'] + ['300x250', '300x600', '0x0'] ], [ TARGETING_KEYS.BIDDER + '_triplelift', diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index f6cfeededd3b..42a4bdae5c10 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -319,7 +319,7 @@ describe('targeting tests', function () { let bidsReceived; beforeEach(function () { - bidsReceived = [bid1, bid2, bid3]; + bidsReceived = [bid1, bid2, bid3].map(deepClone); amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { return bidsReceived; @@ -392,7 +392,7 @@ describe('targeting tests', function () { beforeEach(function() { bid4 = utils.deepClone(bid1); bid4.adserverTargeting['hb_bidder'] = bid4.bidder = bid4.bidderCode = 'appnexus'; - bid4.cpm = 0; + bid4.cpm = 0.01; enableSendAllBids = true; bidsReceived.push(bid4); @@ -543,8 +543,8 @@ describe('targeting tests', function () { }); after(function() { - bidsReceived = [bid1, bid2, bid3]; $$PREBID_GLOBAL$$.bidderSettings = bidderSettingsStorage; + enableSendAllBids = false; }) it('targeting should not include a 0 cpm by default', function() { @@ -560,6 +560,8 @@ describe('targeting tests', function () { } }; + enableSendAllBids = true; + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_pb', 'hb_bidder', 'hb_adid', 'hb_bidder_appnexus', 'hb_adid_appnexus', 'hb_pb_appnexus'); expect(targeting['/123456/header-bid-tag-0']['hb_pb']).to.equal('0.0') @@ -815,6 +817,7 @@ describe('targeting tests', function () { hb_pb: '3.0', hb_adid: '111111', hb_bidder: 'pubmatic', + foobar: '300x250' }; bid5.bidder = bid5.bidderCode = 'pubmatic'; bid5.cpm = 3.0; // winning bid! @@ -899,6 +902,7 @@ describe('targeting tests', function () { let bidExpiryStub; beforeEach(function () { + enableSendAllBids = false; amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { return []; }); @@ -921,6 +925,7 @@ describe('targeting tests', function () { let bidExpiryStub; let auctionManagerStub; beforeEach(function () { + enableSendAllBids = false; bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); auctionManagerStub = sandbox.stub(auctionManager, 'getBidsReceived'); }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 5a08447ce953..69e608e0d656 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -294,9 +294,12 @@ describe('Unit: Prebid Module', function () { it('should return targeting info as a string', function () { const adUnitCode = config.adUnitCodes[0]; $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: true }); - var expected = 'foobar=0x0%2C300x250%2C300x600&' + TARGETING_KEYS.SIZE + '=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '=10.00&' + TARGETING_KEYS.AD_ID + '=233bcbee889d46d&' + TARGETING_KEYS.BIDDER + '=appnexus&' + TARGETING_KEYS.SIZE + '_triplelift=0x0&' + TARGETING_KEYS.PRICE_BUCKET + '_triplelift=10.00&' + TARGETING_KEYS.AD_ID + '_triplelift=222bb26f9e8bd&' + TARGETING_KEYS.BIDDER + '_triplelift=triplelift&' + TARGETING_KEYS.SIZE + '_appnexus=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_appnexus=10.00&' + TARGETING_KEYS.AD_ID + '_appnexus=233bcbee889d46d&' + TARGETING_KEYS.BIDDER + '_appnexus=appnexus&' + TARGETING_KEYS.SIZE + '_pagescience=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_pagescience=10.00&' + TARGETING_KEYS.AD_ID + '_pagescience=25bedd4813632d7&' + TARGETING_KEYS.BIDDER + '_pagescienc=pagescience&' + TARGETING_KEYS.SIZE + '_brightcom=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_brightcom=10.00&' + TARGETING_KEYS.AD_ID + '_brightcom=26e0795ab963896&' + TARGETING_KEYS.BIDDER + '_brightcom=brightcom&' + TARGETING_KEYS.SIZE + '_brealtime=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_brealtime=10.00&' + TARGETING_KEYS.AD_ID + '_brealtime=275bd666f5a5a5d&' + TARGETING_KEYS.BIDDER + '_brealtime=brealtime&' + TARGETING_KEYS.SIZE + '_pubmatic=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_pubmatic=10.00&' + TARGETING_KEYS.AD_ID + '_pubmatic=28f4039c636b6a7&' + TARGETING_KEYS.BIDDER + '_pubmatic=pubmatic&' + TARGETING_KEYS.SIZE + '_rubicon=300x600&' + TARGETING_KEYS.PRICE_BUCKET + '_rubicon=10.00&' + TARGETING_KEYS.AD_ID + '_rubicon=29019e2ab586a5a&' + TARGETING_KEYS.BIDDER + '_rubicon=rubicon'; + var expectedResults = [`foobar=300x250%2C300x600%2C0x0`, `${TARGETING_KEYS.SIZE}=300x250`, `${TARGETING_KEYS.PRICE_BUCKET}=10.00`, `${TARGETING_KEYS.AD_ID}=233bcbee889d46d`, `${TARGETING_KEYS.BIDDER}=appnexus`, `${TARGETING_KEYS.SIZE}_triplelift=0x0`, `${TARGETING_KEYS.PRICE_BUCKET}_triplelift=10.00`, `${TARGETING_KEYS.AD_ID}_triplelift=222bb26f9e8bd`, `${TARGETING_KEYS.BIDDER}_triplelift=triplelift`, `${TARGETING_KEYS.SIZE}_appnexus=300x250`, `${TARGETING_KEYS.PRICE_BUCKET}_appnexus=10.00`, `${TARGETING_KEYS.AD_ID}_appnexus=233bcbee889d46d`, `${TARGETING_KEYS.BIDDER}_appnexus=appnexus`, `${TARGETING_KEYS.SIZE}_pagescience=300x250`, `${TARGETING_KEYS.PRICE_BUCKET}_pagescience=10.00`, `${TARGETING_KEYS.AD_ID}_pagescience=25bedd4813632d7`, `${TARGETING_KEYS.BIDDER}_pagescienc=pagescience`, `${TARGETING_KEYS.SIZE}_brightcom=300x250`, `${TARGETING_KEYS.PRICE_BUCKET}_brightcom=10.00`, `${TARGETING_KEYS.AD_ID}_brightcom=26e0795ab963896`, `${TARGETING_KEYS.BIDDER}_brightcom=brightcom`, `${TARGETING_KEYS.SIZE}_brealtime=300x250`, `${TARGETING_KEYS.PRICE_BUCKET}_brealtime=10.00`, `${TARGETING_KEYS.AD_ID}_brealtime=275bd666f5a5a5d`, `${TARGETING_KEYS.BIDDER}_brealtime=brealtime`, `${TARGETING_KEYS.SIZE}_pubmatic=300x250`, `${TARGETING_KEYS.PRICE_BUCKET}_pubmatic=10.00`, `${TARGETING_KEYS.AD_ID}_pubmatic=28f4039c636b6a7`, `${TARGETING_KEYS.BIDDER}_pubmatic=pubmatic`, `${TARGETING_KEYS.SIZE}_rubicon=300x600`, `${TARGETING_KEYS.PRICE_BUCKET}_rubicon=10.00`, `${TARGETING_KEYS.AD_ID}_rubicon=29019e2ab586a5a`, `${TARGETING_KEYS.BIDDER}_rubicon=rubicon`]; var result = $$PREBID_GLOBAL$$.getAdserverTargetingForAdUnitCodeStr(adUnitCode); - assert.equal(expected, result, 'returns expected string of ad targeting info'); + + expectedResults.forEach(expected => { + expect(result).to.include(expected); + }) }); it('should log message if adunitCode param is falsey', function () { @@ -339,7 +342,7 @@ describe('Unit: Prebid Module', function () { var targeting = $$PREBID_GLOBAL$$.getAdserverTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); var expected = { '/19968336/header-bid-tag-0': { - foobar: '0x0,300x250,300x600', + foobar: '300x250,300x600,0x0', [TARGETING_KEYS.SIZE]: '300x250', [TARGETING_KEYS.PRICE_BUCKET]: '10.00', [TARGETING_KEYS.AD_ID]: '233bcbee889d46d', @@ -477,8 +480,8 @@ describe('Unit: Prebid Module', function () { var expected = { '/19968336/header-bid-tag-0': { - foobar: '0x0,300x250,300x600', - custom_ad_id: '222bb26f9e8bd,233bcbee889d46d,25bedd4813632d7,26e0795ab963896,275bd666f5a5a5d,28f4039c636b6a7,29019e2ab586a5a' + foobar: '300x250,300x600,0x0', + custom_ad_id: '233bcbee889d46d,28f4039c636b6a7,29019e2ab586a5a,25bedd4813632d7,275bd666f5a5a5d,26e0795ab963896,222bb26f9e8bd' }, '/19968336/header-bid-tag1': { foobar: '728x90', @@ -2856,7 +2859,7 @@ describe('Unit: Prebid Module', function () { let result = targeting.getAllTargeting(['/19968336/header-bid-tag-0', '/19968336/header-bid-tag1']); // $$PREBID_GLOBAL$$.getAdserverTargeting(); let expected = { '/19968336/header-bid-tag-0': { - 'foobar': '0x0,300x250,300x600', + 'foobar': '300x250,300x600,0x0', [TARGETING_KEYS.SIZE]: '300x250', [TARGETING_KEYS.PRICE_BUCKET]: '10.00', [TARGETING_KEYS.AD_ID]: '233bcbee889d46d', diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index a6df236ee46f..1b4cc3764f65 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -112,7 +112,7 @@ describe('Utils', function () { var obj = getAdServerTargeting(); var output = utils.transformAdServerTargetingObj(obj[Object.keys(obj)[0]]); - var expected = 'foobar=0x0%2C300x250%2C300x600&' + TARGETING_KEYS.SIZE + '=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '=10.00&' + TARGETING_KEYS.AD_ID + '=233bcbee889d46d&' + TARGETING_KEYS.BIDDER + '=appnexus&' + TARGETING_KEYS.SIZE + '_triplelift=0x0&' + TARGETING_KEYS.PRICE_BUCKET + '_triplelift=10.00&' + TARGETING_KEYS.AD_ID + '_triplelift=222bb26f9e8bd&' + TARGETING_KEYS.BIDDER + '_triplelift=triplelift&' + TARGETING_KEYS.SIZE + '_appnexus=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_appnexus=10.00&' + TARGETING_KEYS.AD_ID + '_appnexus=233bcbee889d46d&' + TARGETING_KEYS.BIDDER + '_appnexus=appnexus&' + TARGETING_KEYS.SIZE + '_pagescience=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_pagescience=10.00&' + TARGETING_KEYS.AD_ID + '_pagescience=25bedd4813632d7&' + TARGETING_KEYS.BIDDER + '_pagescienc=pagescience&' + TARGETING_KEYS.SIZE + '_brightcom=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_brightcom=10.00&' + TARGETING_KEYS.AD_ID + '_brightcom=26e0795ab963896&' + TARGETING_KEYS.BIDDER + '_brightcom=brightcom&' + TARGETING_KEYS.SIZE + '_brealtime=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_brealtime=10.00&' + TARGETING_KEYS.AD_ID + '_brealtime=275bd666f5a5a5d&' + TARGETING_KEYS.BIDDER + '_brealtime=brealtime&' + TARGETING_KEYS.SIZE + '_pubmatic=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_pubmatic=10.00&' + TARGETING_KEYS.AD_ID + '_pubmatic=28f4039c636b6a7&' + TARGETING_KEYS.BIDDER + '_pubmatic=pubmatic&' + TARGETING_KEYS.SIZE + '_rubicon=300x600&' + TARGETING_KEYS.PRICE_BUCKET + '_rubicon=10.00&' + TARGETING_KEYS.AD_ID + '_rubicon=29019e2ab586a5a&' + TARGETING_KEYS.BIDDER + '_rubicon=rubicon'; + var expected = 'foobar=300x250%2C300x600%2C0x0&' + TARGETING_KEYS.SIZE + '=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '=10.00&' + TARGETING_KEYS.AD_ID + '=233bcbee889d46d&' + TARGETING_KEYS.BIDDER + '=appnexus&' + TARGETING_KEYS.SIZE + '_triplelift=0x0&' + TARGETING_KEYS.PRICE_BUCKET + '_triplelift=10.00&' + TARGETING_KEYS.AD_ID + '_triplelift=222bb26f9e8bd&' + TARGETING_KEYS.BIDDER + '_triplelift=triplelift&' + TARGETING_KEYS.SIZE + '_appnexus=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_appnexus=10.00&' + TARGETING_KEYS.AD_ID + '_appnexus=233bcbee889d46d&' + TARGETING_KEYS.BIDDER + '_appnexus=appnexus&' + TARGETING_KEYS.SIZE + '_pagescience=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_pagescience=10.00&' + TARGETING_KEYS.AD_ID + '_pagescience=25bedd4813632d7&' + TARGETING_KEYS.BIDDER + '_pagescienc=pagescience&' + TARGETING_KEYS.SIZE + '_brightcom=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_brightcom=10.00&' + TARGETING_KEYS.AD_ID + '_brightcom=26e0795ab963896&' + TARGETING_KEYS.BIDDER + '_brightcom=brightcom&' + TARGETING_KEYS.SIZE + '_brealtime=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_brealtime=10.00&' + TARGETING_KEYS.AD_ID + '_brealtime=275bd666f5a5a5d&' + TARGETING_KEYS.BIDDER + '_brealtime=brealtime&' + TARGETING_KEYS.SIZE + '_pubmatic=300x250&' + TARGETING_KEYS.PRICE_BUCKET + '_pubmatic=10.00&' + TARGETING_KEYS.AD_ID + '_pubmatic=28f4039c636b6a7&' + TARGETING_KEYS.BIDDER + '_pubmatic=pubmatic&' + TARGETING_KEYS.SIZE + '_rubicon=300x600&' + TARGETING_KEYS.PRICE_BUCKET + '_rubicon=10.00&' + TARGETING_KEYS.AD_ID + '_rubicon=29019e2ab586a5a&' + TARGETING_KEYS.BIDDER + '_rubicon=rubicon'; assert.equal(output, expected); });