From 58b6b928db330ce1de174d18fe6820d748a41326 Mon Sep 17 00:00:00 2001 From: "vivek.yada" Date: Wed, 21 Jan 2026 19:01:38 +0530 Subject: [PATCH 1/3] ADD changes for adding the vast trackers and test cases --- libraries/vastTrackers/vastTrackers.js | 169 +++++++++++++--- src/videoCache.ts | 50 ++++- test/spec/libraries/vastTrackers_spec.js | 246 ++++++++++++++++++++++- 3 files changed, 415 insertions(+), 50 deletions(-) diff --git a/libraries/vastTrackers/vastTrackers.js b/libraries/vastTrackers/vastTrackers.js index 7ab1650e9f9..4adb643f936 100644 --- a/libraries/vastTrackers/vastTrackers.js +++ b/libraries/vastTrackers/vastTrackers.js @@ -1,11 +1,24 @@ import {callPrebidCache} from '../../src/auction.js'; import {VIDEO} from '../../src/mediaTypes.js'; -import {logError} from '../../src/utils.js'; +import {logError, isEmptyStr} from '../../src/utils.js'; +import {isArray, isPlainObject, isStr} from '../../src/utils/objects.js'; import {isActivityAllowed} from '../../src/activities/rules.js'; import {ACTIVITY_REPORT_ANALYTICS} from '../../src/activities/activities.js'; import {activityParams} from '../../src/activities/activityParams.js'; import {auctionManager} from '../../src/auctionManager.js'; +/** + * VAST Trackers Structure: + * { + * impression: string[], // Array of impression pixel URLs + * error: string[], // Array of error pixel URLs + * trackingEvents: Array<{ // Array of video playback tracking events + * event: string, // Event name (e.g., 'start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete') + * url: string // Tracking pixel URL + * }> + * } + */ + const vastTrackers = []; let enabled = false; @@ -33,10 +46,7 @@ export function cacheVideoBidHook({index = auctionManager.index} = {}) { const vastTrackers = getVastTrackers(bidResponse, {index}); if (vastTrackers) { bidResponse.vastXml = insertVastTrackers(vastTrackers, bidResponse.vastXml); - const impTrackers = vastTrackers.get('impressions'); - if (impTrackers) { - bidResponse.vastImpUrl = [].concat([...impTrackers]).concat(bidResponse.vastImpUrl).filter(t => t); - } + bidResponse.vastTrackers = vastTrackers; } } next(auctionInstance, bidResponse, afterBidAdded, videoMediaType); @@ -58,13 +68,25 @@ export function insertVastTrackers(trackers, vastXml) { try { if (wrappers.length) { wrappers.forEach(wrapper => { - if (trackers.get('impressions')) { - trackers.get('impressions').forEach(trackingUrl => { + if (isArray(trackers.impression)) { + trackers.impression.forEach(trackingUrl => { const impression = doc.createElement('Impression'); impression.appendChild(doc.createCDATASection(trackingUrl)); wrapper.appendChild(impression); }); } + + if (isArray(trackers.error)) { + trackers.error.forEach(trackingUrl => { + const errorElement = doc.createElement('Error'); + errorElement.appendChild(doc.createCDATASection(trackingUrl)); + wrapper.appendChild(errorElement); + }); + } + + if (isArray(trackers.trackingEvents)) { + insertLinearTrackingEvents(doc, wrapper, trackers.trackingEvents); + } }); vastXml = new XMLSerializer().serializeToString(doc); } @@ -74,8 +96,65 @@ export function insertVastTrackers(trackers, vastXml) { return vastXml; } +/** + * Inserts tracking events into under elements. + * If doesn't exist, it will be created. + * @param {Document} doc - The parsed VAST XML document + * @param {Element} wrapper - The Wrapper or InLine element + * @param {Array<{event: string, url: string}>} trackers - Array of tracking event objects + */ +function insertLinearTrackingEvents(doc, wrapper, trackers) { + const linearElements = wrapper.querySelectorAll('Creatives Creative Linear'); + + if (linearElements.length > 0) { + linearElements.forEach(linear => { + let trackingEvents = linear.querySelector('TrackingEvents'); + if (!trackingEvents) { + trackingEvents = doc.createElement('TrackingEvents'); + linear.appendChild(trackingEvents); + } + appendTrackingElements(doc, trackingEvents, trackers); + }); + } else { + let creatives = wrapper.querySelector('Creatives'); + if (!creatives) { + creatives = doc.createElement('Creatives'); + wrapper.appendChild(creatives); + } + + const creative = doc.createElement('Creative'); + const linear = doc.createElement('Linear'); + const trackingEvents = doc.createElement('TrackingEvents'); + + appendTrackingElements(doc, trackingEvents, trackers); + linear.appendChild(trackingEvents); + creative.appendChild(linear); + creatives.appendChild(creative); + } +} + +/** + * Appends Tracking elements to a TrackingEvents element + * @param {Document} doc - The parsed VAST XML document + * @param {Element} trackingEvents - The TrackingEvents element to append to + * @param {Array<{event: string, url: string}>} trackers - Array of tracking event objects + */ +function appendTrackingElements(doc, trackingEvents, trackers) { + trackers.forEach(({event, url}) => { + const trackingElement = doc.createElement('Tracking'); + trackingElement.setAttribute('event', event); + trackingElement.appendChild(doc.createCDATASection(url)); + trackingEvents.appendChild(trackingElement); + }); +} + export function getVastTrackers(bid, {index = auctionManager.index}) { - const trackers = []; + const mergedTrackers = { + impression: [], + error: [], + trackingEvents: [] + }; + vastTrackers.filter( ({ moduleType, @@ -86,37 +165,67 @@ export function getVastTrackers(bid, {index = auctionManager.index}) { const auction = index.getAuction(bid).getProperties(); const bidRequest = index.getBidRequest(bid); const trackersToAdd = trackerFn(bid, {auction, bidRequest}); - trackersToAdd.forEach(trackerToAdd => { - if (isValidVastTracker(trackers, trackerToAdd)) { - trackers.push(trackerToAdd); + + if (isPlainObject(trackersToAdd)) { + if (isArray(trackersToAdd.impression)) { + trackersToAdd.impression.forEach(url => { + if (isStr(url) && !isEmptyStr(url)) { + mergedTrackers.impression.push(url); + } + }); } - }); + + if (isArray(trackersToAdd.error)) { + trackersToAdd.error.forEach(url => { + if (isStr(url) && !isEmptyStr(url)) { + mergedTrackers.error.push(url); + } + }); + } + + if (isArray(trackersToAdd.trackingEvents)) { + trackersToAdd.trackingEvents.forEach(tracker => { + if (isValidTrackingEvent(tracker)) { + mergedTrackers.trackingEvents.push(tracker); + } + }); + } + } }); - const trackersMap = trackersToMap(trackers); - return (trackersMap.size ? trackersMap : null); -}; -function isValidVastTracker(trackers, trackerToAdd) { - return trackerToAdd.hasOwnProperty('event') && trackerToAdd.hasOwnProperty('url'); + const hasTrackers = mergedTrackers.impression.length || + mergedTrackers.error.length || + mergedTrackers.trackingEvents.length; + + return hasTrackers ? mergedTrackers : null; } -function trackersToMap(trackers) { - return trackers.reduce((map, {url, event}) => { - !map.has(event) && map.set(event, new Set()); - map.get(event).add(url); - return map; - }, new Map()); +/** + * Validates a tracking event object + * @param {Object} tracker - The tracker object to validate + * @returns {boolean} - True if valid, false otherwise + */ +function isValidTrackingEvent(tracker) { + return isPlainObject(tracker) && + isStr(tracker.event) && !isEmptyStr(tracker.event) && + isStr(tracker.url) && !isEmptyStr(tracker.url); } -export function addImpUrlToTrackers(bid, trackersMap) { +export function addImpUrlToTrackers(bid, trackers) { if (bid.vastImpUrl) { - if (!trackersMap) { - trackersMap = new Map(); + if (!trackers) { + trackers = { + impression: [], + error: [], + trackingEvents: [] + }; + } + if (!trackers.impression) { + trackers.impression = []; } - if (!trackersMap.get('impressions')) { - trackersMap.set('impressions', new Set()); + if (!trackers.impression.includes(bid.vastImpUrl)) { + trackers.impression.push(bid.vastImpUrl); } - trackersMap.get('impressions').add(bid.vastImpUrl); } - return trackersMap; + return trackers; } diff --git a/src/videoCache.ts b/src/videoCache.ts index b63baf7dd43..8a9c9b0398e 100644 --- a/src/videoCache.ts +++ b/src/videoCache.ts @@ -25,26 +25,54 @@ const ttlBufferInSeconds = 15; export const vastLocalCache = new Map(); +/** + * VAST Trackers interface for video cache + */ +export interface VastTrackers { + impression?: string[]; + error?: string[]; + trackingEvents?: Array<{ event: string; url: string }>; +} + /** * Function which wraps a URI that serves VAST XML, so that it can be loaded. * * @param uri The URI where the VAST content can be found. - * @param impTrackerURLs An impression tracker URL for the delivery of the video ad + * @param trackers VAST trackers object containing impression, error, and trackingEvents * @return A VAST URL which loads XML from the given URI. */ -function wrapURI(uri: string, impTrackerURLs: string | string[]) { - impTrackerURLs = impTrackerURLs && (Array.isArray(impTrackerURLs) ? impTrackerURLs : [impTrackerURLs]); +function wrapURI(uri: string, trackers?: VastTrackers) { // Technically, this is vulnerable to cross-script injection by sketchy vastUrl bids. // We could make sure it's a valid URI... but since we're loading VAST XML from the // URL they provide anyway, that's probably not a big deal. - const impressions = impTrackerURLs ? impTrackerURLs.map(trk => ``).join('') : ''; + + // Build Impression tags + const impressions = trackers?.impression?.length + ? trackers.impression.map(trk => ``).join('') + : ''; + + // Build Error tags + const errors = trackers?.error?.length + ? trackers.error.map(trk => ``).join('') + : ''; + + // Build TrackingEvents for Linear creative + let trackingEventsXml = ''; + if (trackers?.trackingEvents?.length) { + const trackingTags = trackers.trackingEvents + .map(({event, url}) => ``) + .join(''); + trackingEventsXml = `${trackingTags}`; + } + return ` prebid.org wrapper ${impressions} - + ${errors} + ${trackingEventsXml} `; @@ -53,9 +81,9 @@ function wrapURI(uri: string, impTrackerURLs: string | string[]) { declare module './bidfactory' { interface VideoBidResponseProperties { /** - * VAST impression trackers to attach to this bid. + * VAST trackers to attach to this bid (impression, error, and tracking events). */ - vastImpUrl?: string | string [] + vastTrackers?: VastTrackers /** * Cache key to use for caching this bid's VAST. */ @@ -185,8 +213,12 @@ function shimStorageCallback(done: VideoCacheStoreCallback) { } function getVastXml(bid) { - return bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl); -}; + if (bid.vastXml) { + return bid.vastXml; + } + + return wrapURI(bid.vastUrl, bid.vastTrackers); +} /** * If the given bid is for a Video ad, generate a unique ID and cache it somewhere server-side. diff --git a/test/spec/libraries/vastTrackers_spec.js b/test/spec/libraries/vastTrackers_spec.js index ddd80e98f9d..55c0fe7be67 100644 --- a/test/spec/libraries/vastTrackers_spec.js +++ b/test/spec/libraries/vastTrackers_spec.js @@ -37,9 +37,11 @@ describe('vast trackers', () => { sandbox = sinon.createSandbox(); index = new AuctionIndex(() => [auction]); tracker = sinon.stub().callsFake(function (bidResponse) { - return [ - {'event': 'impressions', 'url': `https://vasttracking.mydomain.com/vast?cpm=${bidResponse.cpm}`} - ]; + return { + impression: [`https://vasttracking.mydomain.com/vast?cpm=${bidResponse.cpm}`], + error: [], + trackingEvents: [] + }; }); registerVastTrackers(MODULE_TYPE_ANALYTICS, 'test', tracker); }) @@ -52,9 +54,9 @@ describe('vast trackers', () => { it('insert into tracker list', function () { const trackers = getVastTrackers(bid, {index}); - expect(trackers).to.be.a('map'); - expect(trackers.get('impressions')).to.exists; - expect(trackers.get('impressions').has('https://vasttracking.mydomain.com/vast?cpm=1')).to.be.true; + expect(trackers).to.be.an('object'); + expect(trackers.impression).to.be.an('array'); + expect(trackers.impression).to.include('https://vasttracking.mydomain.com/vast?cpm=1'); }); it('insert trackers in vastXml', function () { @@ -72,17 +74,239 @@ describe('vast trackers', () => { it('test addImpUrlToTrackers', function () { const trackers = addImpUrlToTrackers({'vastImpUrl': 'imptracker.com'}, getVastTrackers(bid, {index})); - expect(trackers).to.be.a('map'); - expect(trackers.get('impressions')).to.exists; - expect(trackers.get('impressions').has('imptracker.com')).to.be.true; + expect(trackers).to.be.an('object'); + expect(trackers.impression).to.be.an('array'); + expect(trackers.impression).to.include('imptracker.com'); }); if (FEATURES.VIDEO) { it('should add trackers to bid response', () => { cacheVideoBidHook({index})(sinon.stub(), 'au', bid); - expect(bid.vastImpUrl).to.eql([ + expect(bid.vastTrackers).to.be.an('object'); + expect(bid.vastTrackers.impression).to.eql([ 'https://vasttracking.mydomain.com/vast?cpm=1' - ]) + ]); + expect(bid.vastTrackers.error).to.eql([]); + expect(bid.vastTrackers.trackingEvents).to.eql([]); }); } + + describe('error tracking', () => { + beforeEach(() => { + reset(); + }); + + it('should insert error trackers in vastXml', function () { + const errorTracker = sinon.stub().callsFake(function (bidResponse) { + return { + impression: [], + error: [`https://error.mydomain.com/error?cpm=${bidResponse.cpm}`], + trackingEvents: [] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'errorTest', errorTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = ''; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + }); + + it('should insert multiple error trackers', function () { + const errorTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: ['https://error1.mydomain.com/error', 'https://error2.mydomain.com/error'], + trackingEvents: [] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'errorTest', errorTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = ''; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should insert both impression and error trackers', function () { + const mixedTracker = sinon.stub().callsFake(function () { + return { + impression: ['https://impression.mydomain.com/imp'], + error: ['https://error.mydomain.com/error'], + trackingEvents: [] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'mixedTest', mixedTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = ''; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + }); + + describe('video playback tracking events', () => { + beforeEach(() => { + reset(); + }); + + it('should insert start tracker in vastXml with existing Linear element', function () { + const playbackTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: [], + trackingEvents: [ + {event: 'start', url: 'https://tracking.mydomain.com/start'} + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'playbackTest', playbackTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = '00:00:30'; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should insert quartile trackers in vastXml', function () { + const playbackTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: [], + trackingEvents: [ + {event: 'firstQuartile', url: 'https://tracking.mydomain.com/firstQuartile'}, + {event: 'midpoint', url: 'https://tracking.mydomain.com/midpoint'}, + {event: 'thirdQuartile', url: 'https://tracking.mydomain.com/thirdQuartile'}, + {event: 'complete', url: 'https://tracking.mydomain.com/complete'} + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'quartileTest', playbackTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = '00:00:30'; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should append to existing TrackingEvents element', function () { + const playbackTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: [], + trackingEvents: [ + {event: 'start', url: 'https://tracking.mydomain.com/start'} + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'appendTest', playbackTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = '00:00:30'; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should create Linear structure when not present', function () { + const playbackTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: [], + trackingEvents: [ + {event: 'start', url: 'https://tracking.mydomain.com/start'} + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'createStructureTest', playbackTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = ''; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should insert all tracker types together', function () { + const allTracker = sinon.stub().callsFake(function () { + return { + impression: ['https://tracking.mydomain.com/impression'], + error: ['https://tracking.mydomain.com/error'], + trackingEvents: [ + {event: 'start', url: 'https://tracking.mydomain.com/start'}, + {event: 'firstQuartile', url: 'https://tracking.mydomain.com/firstQuartile'}, + {event: 'complete', url: 'https://tracking.mydomain.com/complete'} + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'allTypesTest', allTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = '00:00:30'; + vastXml = insertVastTrackers(trackers, vastXml); + + // Check impression tracker + expect(vastXml).to.contain(''); + // Check error tracker + expect(vastXml).to.contain(''); + // Check video playback trackers + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should insert additional tracking events like pause and mute', function () { + const additionalTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: [], + trackingEvents: [ + {event: 'pause', url: 'https://tracking.mydomain.com/pause'}, + {event: 'mute', url: 'https://tracking.mydomain.com/mute'}, + {event: 'fullscreen', url: 'https://tracking.mydomain.com/fullscreen'} + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'additionalTest', additionalTracker); + + const trackers = getVastTrackers(bid, {index}); + let vastXml = '00:00:30'; + vastXml = insertVastTrackers(trackers, vastXml); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + expect(vastXml).to.contain(''); + }); + + it('should validate tracking events have both event and url', function () { + const invalidTracker = sinon.stub().callsFake(function () { + return { + impression: [], + error: [], + trackingEvents: [ + {event: 'start', url: 'https://tracking.mydomain.com/start'}, + {event: 'midpoint'}, // missing url + {url: 'https://tracking.mydomain.com/invalid'}, // missing event + {event: '', url: 'https://tracking.mydomain.com/empty'}, // empty event + {event: 'complete', url: ''} // empty url + ] + }; + }); + registerVastTrackers(MODULE_TYPE_ANALYTICS, 'validationTest', invalidTracker); + + const trackers = getVastTrackers(bid, {index}); + // Only the valid tracker should be included + expect(trackers.trackingEvents).to.have.lengthOf(1); + expect(trackers.trackingEvents[0].event).to.equal('start'); + }); + + }); }) From 5c172158edb277ed5dc013a06e0857aae6937d5a Mon Sep 17 00:00:00 2001 From: "vivek.yada" Date: Thu, 22 Jan 2026 18:24:30 +0530 Subject: [PATCH 2/3] FIX failing test cases --- libraries/vastTrackers/vastTrackers.js | 25 +------ src/videoCache.ts | 22 +++--- test/spec/libraries/vastTrackers_spec.js | 86 +----------------------- test/spec/videoCache_spec.js | 78 ++++++++++++--------- 4 files changed, 61 insertions(+), 150 deletions(-) diff --git a/libraries/vastTrackers/vastTrackers.js b/libraries/vastTrackers/vastTrackers.js index 4adb643f936..68c20b5a955 100644 --- a/libraries/vastTrackers/vastTrackers.js +++ b/libraries/vastTrackers/vastTrackers.js @@ -68,7 +68,7 @@ export function insertVastTrackers(trackers, vastXml) { try { if (wrappers.length) { wrappers.forEach(wrapper => { - if (isArray(trackers.impression)) { + if (isArray(trackers.impression) && trackers.impression.length) { trackers.impression.forEach(trackingUrl => { const impression = doc.createElement('Impression'); impression.appendChild(doc.createCDATASection(trackingUrl)); @@ -76,7 +76,7 @@ export function insertVastTrackers(trackers, vastXml) { }); } - if (isArray(trackers.error)) { + if (isArray(trackers.error) && trackers.error.length) { trackers.error.forEach(trackingUrl => { const errorElement = doc.createElement('Error'); errorElement.appendChild(doc.createCDATASection(trackingUrl)); @@ -84,7 +84,7 @@ export function insertVastTrackers(trackers, vastXml) { }); } - if (isArray(trackers.trackingEvents)) { + if (isArray(trackers.trackingEvents) && trackers.trackingEvents.length) { insertLinearTrackingEvents(doc, wrapper, trackers.trackingEvents); } }); @@ -210,22 +210,3 @@ function isValidTrackingEvent(tracker) { isStr(tracker.event) && !isEmptyStr(tracker.event) && isStr(tracker.url) && !isEmptyStr(tracker.url); } - -export function addImpUrlToTrackers(bid, trackers) { - if (bid.vastImpUrl) { - if (!trackers) { - trackers = { - impression: [], - error: [], - trackingEvents: [] - }; - } - if (!trackers.impression) { - trackers.impression = []; - } - if (!trackers.impression.includes(bid.vastImpUrl)) { - trackers.impression.push(bid.vastImpUrl); - } - } - return trackers; -} diff --git a/src/videoCache.ts b/src/videoCache.ts index 8a9c9b0398e..1c34e19db9a 100644 --- a/src/videoCache.ts +++ b/src/videoCache.ts @@ -65,17 +65,17 @@ function wrapURI(uri: string, trackers?: VastTrackers) { trackingEventsXml = `${trackingTags}`; } - return ` - - - prebid.org wrapper - - ${impressions} - ${errors} - ${trackingEventsXml} - - - `; + return '' + + '' + + '' + + 'prebid.org wrapper' + + '' + + impressions + + errors + + '' + trackingEventsXml + '' + + '' + + '' + + ''; } declare module './bidfactory' { diff --git a/test/spec/libraries/vastTrackers_spec.js b/test/spec/libraries/vastTrackers_spec.js index 55c0fe7be67..8a8a4011ea5 100644 --- a/test/spec/libraries/vastTrackers_spec.js +++ b/test/spec/libraries/vastTrackers_spec.js @@ -1,6 +1,4 @@ import { - addImpUrlToTrackers, - addTrackersToResponse, getVastTrackers, insertVastTrackers, registerVastTrackers, @@ -72,13 +70,6 @@ describe('vast trackers', () => { sinon.assert.calledWith(tracker, bid, sinon.match({auction: auction.getProperties(), bidRequest})) }) - it('test addImpUrlToTrackers', function () { - const trackers = addImpUrlToTrackers({'vastImpUrl': 'imptracker.com'}, getVastTrackers(bid, {index})); - expect(trackers).to.be.an('object'); - expect(trackers.impression).to.be.an('array'); - expect(trackers.impression).to.include('imptracker.com'); - }); - if (FEATURES.VIDEO) { it('should add trackers to bid response', () => { cacheVideoBidHook({index})(sinon.stub(), 'au', bid); @@ -152,7 +143,7 @@ describe('vast trackers', () => { reset(); }); - it('should insert start tracker in vastXml with existing Linear element', function () { + it('should insert video playback tracker in vastXml with existing Linear element', function () { const playbackTracker = sinon.stub().callsFake(function () { return { impression: [], @@ -171,30 +162,6 @@ describe('vast trackers', () => { expect(vastXml).to.contain(''); }); - it('should insert quartile trackers in vastXml', function () { - const playbackTracker = sinon.stub().callsFake(function () { - return { - impression: [], - error: [], - trackingEvents: [ - {event: 'firstQuartile', url: 'https://tracking.mydomain.com/firstQuartile'}, - {event: 'midpoint', url: 'https://tracking.mydomain.com/midpoint'}, - {event: 'thirdQuartile', url: 'https://tracking.mydomain.com/thirdQuartile'}, - {event: 'complete', url: 'https://tracking.mydomain.com/complete'} - ] - }; - }); - registerVastTrackers(MODULE_TYPE_ANALYTICS, 'quartileTest', playbackTracker); - - const trackers = getVastTrackers(bid, {index}); - let vastXml = '00:00:30'; - vastXml = insertVastTrackers(trackers, vastXml); - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - }); - it('should append to existing TrackingEvents element', function () { const playbackTracker = sinon.stub().callsFake(function () { return { @@ -236,56 +203,6 @@ describe('vast trackers', () => { expect(vastXml).to.contain(''); }); - it('should insert all tracker types together', function () { - const allTracker = sinon.stub().callsFake(function () { - return { - impression: ['https://tracking.mydomain.com/impression'], - error: ['https://tracking.mydomain.com/error'], - trackingEvents: [ - {event: 'start', url: 'https://tracking.mydomain.com/start'}, - {event: 'firstQuartile', url: 'https://tracking.mydomain.com/firstQuartile'}, - {event: 'complete', url: 'https://tracking.mydomain.com/complete'} - ] - }; - }); - registerVastTrackers(MODULE_TYPE_ANALYTICS, 'allTypesTest', allTracker); - - const trackers = getVastTrackers(bid, {index}); - let vastXml = '00:00:30'; - vastXml = insertVastTrackers(trackers, vastXml); - - // Check impression tracker - expect(vastXml).to.contain(''); - // Check error tracker - expect(vastXml).to.contain(''); - // Check video playback trackers - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - }); - - it('should insert additional tracking events like pause and mute', function () { - const additionalTracker = sinon.stub().callsFake(function () { - return { - impression: [], - error: [], - trackingEvents: [ - {event: 'pause', url: 'https://tracking.mydomain.com/pause'}, - {event: 'mute', url: 'https://tracking.mydomain.com/mute'}, - {event: 'fullscreen', url: 'https://tracking.mydomain.com/fullscreen'} - ] - }; - }); - registerVastTrackers(MODULE_TYPE_ANALYTICS, 'additionalTest', additionalTracker); - - const trackers = getVastTrackers(bid, {index}); - let vastXml = '00:00:30'; - vastXml = insertVastTrackers(trackers, vastXml); - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - expect(vastXml).to.contain(''); - }); - it('should validate tracking events have both event and url', function () { const invalidTracker = sinon.stub().callsFake(function () { return { @@ -307,6 +224,5 @@ describe('vast trackers', () => { expect(trackers.trackingEvents).to.have.lengthOf(1); expect(trackers.trackingEvents[0].event).to.equal('start'); }); - }); }) diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index 76a3bea0127..496f4c76b9f 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -97,43 +97,57 @@ describe('The video cache', function () { }); it('should make the expected request when store() is called on an ad with a vastUrl', function () { - const expectedValue = ` - - - prebid.org wrapper - \n \n - - - `; + const expectedValue = 'prebid.org wrapper'; assertRequestMade({ vastUrl: 'my-mock-url.com', ttl: 25 }, expectedValue) }); - it('should make the expected request when store() is called on an ad with a vastUrl and a vastImpUrl', function () { - const expectedValue = ` - - - prebid.org wrapper - - - - - - `; - assertRequestMade({ vastUrl: 'my-mock-url.com', vastImpUrl: 'imptracker.com', ttl: 25 }, expectedValue) + it('should make the expected request when store() is called on an ad with a vastUrl and impression trackers', function () { + const expectedValue = 'prebid.org wrapper'; + assertRequestMade({ vastUrl: 'my-mock-url.com', vastTrackers: { impression: ['imptracker.com'] }, ttl: 25 }, expectedValue) }); - it('should include multiple vastImpUrl when it\'s an array', function() { - const expectedValue = ` - - - prebid.org wrapper - - - - - - `; - assertRequestMade({ vastUrl: 'my-mock-url.com', vastImpUrl: ['https://vasttracking.mydomain.com/vast?cpm=1.2', 'imptracker.com'], ttl: 25, cpm: 1.2 }, expectedValue) + it('should include multiple impression trackers', function() { + const expectedValue = 'prebid.org wrapper'; + assertRequestMade({ vastUrl: 'my-mock-url.com', vastTrackers: { impression: ['https://vasttracking.mydomain.com/vast?cpm=1.2', 'imptracker.com'] }, ttl: 25, cpm: 1.2 }, expectedValue) + }); + + it('should include error trackers', function() { + const expectedValue = 'prebid.org wrapper'; + assertRequestMade({ vastUrl: 'my-mock-url.com', vastTrackers: { error: ['https://error.mydomain.com/error'] }, ttl: 25 }, expectedValue) + }); + + it('should include both impression and error trackers', function() { + const expectedValue = 'prebid.org wrapper'; + assertRequestMade({ vastUrl: 'my-mock-url.com', vastTrackers: { impression: ['imptracker.com'], error: ['https://error.mydomain.com/error'] }, ttl: 25 }, expectedValue) + }); + + it('should include tracking events', function() { + const expectedValue = 'prebid.org wrapper'; + assertRequestMade({ + vastUrl: 'my-mock-url.com', + vastTrackers: { + trackingEvents: [ + { event: 'start', url: 'https://tracking.mydomain.com/start' }, + { event: 'complete', url: 'https://tracking.mydomain.com/complete' } + ] + }, + ttl: 25 + }, expectedValue) + }); + + it('should include all tracker types together', function() { + const expectedValue = 'prebid.org wrapper'; + assertRequestMade({ + vastUrl: 'my-mock-url.com', + vastTrackers: { + impression: ['imptracker.com'], + error: ['https://error.mydomain.com/error'], + trackingEvents: [ + { event: 'start', url: 'https://tracking.mydomain.com/start' } + ] + }, + ttl: 25 + }, expectedValue) }); it('should make the expected request when store() is called on an ad with vastXml', function () { From e3309dea7925d2b228b64ad73f2640f6f47b3f08 Mon Sep 17 00:00:00 2001 From: "vivek.yada" Date: Mon, 2 Feb 2026 17:10:10 +0530 Subject: [PATCH 3/3] MODIFY syntactical changes --- src/videoCache.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/videoCache.ts b/src/videoCache.ts index 1c34e19db9a..350a9c26909 100644 --- a/src/videoCache.ts +++ b/src/videoCache.ts @@ -213,12 +213,8 @@ function shimStorageCallback(done: VideoCacheStoreCallback) { } function getVastXml(bid) { - if (bid.vastXml) { - return bid.vastXml; - } - - return wrapURI(bid.vastUrl, bid.vastTrackers); -} + return bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastTrackers); +}; /** * If the given bid is for a Video ad, generate a unique ID and cache it somewhere server-side.