diff --git a/docs/api/vast-parser.md b/docs/api/vast-parser.md index b25c96b1..5b35d15b 100644 --- a/docs/api/vast-parser.md +++ b/docs/api/vast-parser.md @@ -17,6 +17,7 @@ Whenever an error occurs during the VAST parsing, the parser will call automatic - `no_ad`: The VAST document is empty - VAST error `101`: VAST schema validation error. +- VAST error `102`: VAST version of response not supported. - VAST error `301`: Timeout of VAST URI provided in Wrapper element. - VAST error `302`: Wrapper limit reached. - VAST error `303`: No VAST response after one or more Wrappers. diff --git a/spec/samples/outdated-vast.xml b/spec/samples/outdated-vast.xml new file mode 100644 index 00000000..74a5e6bb --- /dev/null +++ b/spec/samples/outdated-vast.xml @@ -0,0 +1,18 @@ + + + + example-1 + 123456 + + + + diff --git a/spec/samples/wrapper-invalid-xmlfile.xml b/spec/samples/wrapper-invalid-xmlfile.xml deleted file mode 100644 index 625599f1..00000000 --- a/spec/samples/wrapper-invalid-xmlfile.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - VAST - invalid-xmlfile.xml - http://example.com/wrapper-invalid-xmlfile_wrapper-error - http://example.com/wrapper-invalid-xmlfile_impression - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spec/vast_parser.spec.js b/spec/vast_parser.spec.js index 98433aa2..25ebccd4 100644 --- a/spec/vast_parser.spec.js +++ b/spec/vast_parser.spec.js @@ -27,42 +27,18 @@ const nodeUrlHandler = { const wrapperAVastUrl = urlFor('wrapper-a.xml'); const wrapperBVastUrl = urlFor('wrapper-b.xml'); const inlineSampleVastUrl = urlFor('sample.xml'); -const inlineVpaidVastUrl = urlFor('vpaid.xml'); const inlineInvalidVastUrl = urlFor('invalid-xmlfile.xml'); -const wrapperWithAttributesVastUrl = urlFor( - 'wrapper-attributes-multiple-ads.xml' -); -const wrapperInvalidVastUrl = urlFor('wrapper-invalid-xmlfile.xml'); -const vastWithErrorUrl = urlFor('empty-no-ad.xml'); -const ad = { - id: null, - sequence: 1, - system: { value: 'VAST', version: null }, - title: null, - description: null, - advertiser: null, - pricing: null, - survey: null, - errorURLTemplates: ['http://example.com/wrapperA-error'], - impressionURLTemplates: [], - creatives: [], - extensions: [], - adVerifications: [], - trackingEvents: { nonlinear: [], linear: [] }, - videoClickTrackingURLTemplates: [], - videoCustomClickURLTemplates: [], - viewableImpression: [], -}; describe('VASTParser', () => { let vastClient; let VastParser; let fetcher; - let inlineXml, invalidXml, errorXml, wrapperXml; + let inlineXml, invalidXml, errorXml, wrapperXml, outdatedXml; beforeAll(async () => { inlineXml = await nodeUrlHandler.get('./spec/samples/sample.xml'); errorXml = await nodeUrlHandler.get('./spec/samples/empty-no-ad.xml'); + outdatedXml = await nodeUrlHandler.get('./spec/samples/outdated-vast.xml'); wrapperXml = await nodeUrlHandler.get( './spec/samples/wrapper-attributes-multiple-ads.xml' ); @@ -136,6 +112,23 @@ describe('VASTParser', () => { } }); + it('throw a error for non supported XML vast', () => { + try { + VastParser.parseVastXml(outdatedXml.xml, { + isRootVAST: true, + url: null, + wrapperDepth: 0, + }); + } catch (e) { + expect(e.message).toBe('VAST response version not supported'); + expect(VastParser.emit).toHaveBeenLastCalledWith('VAST-ad-parsed', { + type: 'ERROR', + url: null, + wrapperDepth: 0, + }); + } + }); + it('gets vast version from original vast', () => { VastParser.parseVastXml(inlineXml.xml, { isRootVAST: true, @@ -480,7 +473,7 @@ describe('VASTParser', () => { }); describe('Wrapper URL unavailable/timeout', () => { - it('sould emits a VAST-error and track', async () => { + it('should emits a VAST-error and track', async () => { const url = './spec/samples/wrapper-unavailable-url.xml'; const response = await nodeUrlHandler.get(url); @@ -488,12 +481,12 @@ describe('VASTParser', () => { fetcher.setOptions({ ...options, url: url, previousUrl: url }); VastParser.fetchingCallback = fetcher.fetchVAST.bind(fetcher); - const vastXML = await VastParser.parseVAST(response.xml, { + const vast = await VastParser.parseVAST(response.xml, { url: url, previousUrl: url, }); // Response doesn't have any ads - expect(vastXML.ads).toEqual([]); + expect(vast.ads).toEqual([]); // Error has been trigered expect(dataTriggered.length).toBe(1); expect(dataTriggered[0].ERRORCODE).toBe(301); diff --git a/src/fetcher/url_handler.js b/src/fetcher/url_handler.js index d6d7f350..3d88bc86 100644 --- a/src/fetcher/url_handler.js +++ b/src/fetcher/url_handler.js @@ -36,7 +36,7 @@ async function handleResponse(response) { */ function handleError(response) { if ( - window.location.protocol === 'https:' && + window?.location?.protocol === 'https:' && response.url.includes('http://') ) { return 'URLHandler: Cannot go from HTTPS to HTTP.'; @@ -66,8 +66,7 @@ async function get(url, options) { ...options, signal: controller.signal, credentials: options.withCredentials ? 'include' : 'omit', - }) - .finally(()=>{ + }).finally(() => { clearTimeout(timer); }); diff --git a/src/parser/vast_parser.js b/src/parser/vast_parser.js index 3c6d0919..e6e84e1a 100644 --- a/src/parser/vast_parser.js +++ b/src/parser/vast_parser.js @@ -10,7 +10,8 @@ const DEFAULT_EVENT_DATA = { ERRORCODE: 900, extensions: [], }; - +const INVALID_VAST_ERROR = 'Invalid VAST XMLDocument'; +const NON_SUPPORTED_VAST_VERSION = 'VAST response version not supported'; /** * This class provides methods to fetch and parse a VAST document. * @export @@ -25,7 +26,10 @@ export class VASTParser extends EventEmitter { constructor({ fetcher } = {}) { super(); this.maxWrapperDepth = null; + this.rootErrorURLTemplates = []; + this.errorURLTemplates = []; this.remainingAds = []; + this.parsingOptions = {}; this.fetcher = fetcher || null; } @@ -173,7 +177,12 @@ export class VASTParser extends EventEmitter { url, wrapperDepth, }); - throw new Error('Invalid VAST XMLDocument'); + // VideoAdServingTemplate node is used for VAST 1.0 + const isNonSupportedVast = + vastXml?.documentElement?.nodeName === 'VideoAdServingTemplate'; + throw new Error( + isNonSupportedVast ? NON_SUPPORTED_VAST_VERSION : INVALID_VAST_ERROR + ); } const ads = []; @@ -414,8 +423,8 @@ export class VASTParser extends EventEmitter { }) .catch((err) => { // Timeout of VAST URI provided in Wrapper element, or of VAST URI provided in a subsequent Wrapper element. - // (URI was either unavailable or reached a timeout as defined by the video player.) - ad.errorCode = 301; + // (URI was either unavailable or reached a timeout as defined by the video player) + ad.errorCode = err.message === NON_SUPPORTED_VAST_VERSION ? 102 : 301; ad.errorMessage = err.message; resolve(ad); }); @@ -440,7 +449,10 @@ export class VASTParser extends EventEmitter { // - No Creative case - The parser has dealt with soma or/and an elements // but no creative was found const ad = vastResponse.ads[index]; - if ((ad.errorCode || ad.creatives.length === 0) && !ad.VASTAdTagURI) { + const noMediaFilesAvailable = !ad.creatives.some( + (creative) => creative.mediaFiles?.length > 0 + ); + if ((ad.errorCode || noMediaFilesAvailable) && !ad.VASTAdTagURI) { // If VASTAdTagURI is in the vastResponse, it means we are dealing with a Wrapper when using parseVAST from the VASTParser. // In that case, we dont want to modify the vastResponse since the creatives node is not required in a wrapper. this.trackVastError(