UNPKG

react-native-vast-client

Version:
1,277 lines (1,105 loc) 41.4 kB
import path from 'path'; import should from 'should'; import sinon from 'sinon'; import { VASTParser } from '../src/parser/vast_parser'; import { VASTResponse } from '../src/vast_response'; import { nodeURLHandler } from '../src/urlhandlers/node_url_handler'; import { parserUtils } from '../src/parser/parser_utils.js'; import { util } from '../src/util/util'; const vastParser = new VASTParser(); const urlfor = relpath => `file://${path .resolve(path.dirname(module.filename), 'vastfiles', relpath) .replace(/\\/g, '/')}`; describe('VASTParser', function() { describe('#getAndParseVAST', function() { this.response = null; let _response = null; this.templateFilterCalls = []; let eventsTriggered = null; const options = { urlhandler: nodeURLHandler }; before(done => { eventsTriggered = []; vastParser.on('VAST-resolving', variables => eventsTriggered.push({ name: 'VAST-resolving', data: variables }) ); vastParser.on('VAST-resolved', variables => eventsTriggered.push({ name: 'VAST-resolved', data: variables }) ); vastParser.addURLTemplateFilter(url => { this.templateFilterCalls.push(url); return url; }); vastParser .getAndParseVAST(urlfor('wrapper-notracking.xml'), options) .then(response => { this.response = response; _response = this.response; done(); }); }); after(() => { eventsTriggered = []; vastParser.removeAllListeners(); vastParser.clearURLTemplateFilters(); }); it('should have 1 filter defined', () => { vastParser.countURLTemplateFilters().should.equal(1); }); it('should have called 4 times URLtemplateFilter ', () => { this.templateFilterCalls.should.have.length(4); this.templateFilterCalls.should.eql([ urlfor('wrapper-notracking.xml'), urlfor('wrapper-a.xml'), urlfor('wrapper-b.xml'), urlfor('sample.xml') ]); }); it('should have emitted resolving/resolved events', () => { eventsTriggered.should.eql([ { name: 'VAST-resolving', data: { url: urlfor('wrapper-notracking.xml'), wrapperDepth: undefined, originalUrl: undefined } }, { name: 'VAST-resolved', data: { url: urlfor('wrapper-notracking.xml'), error: null } }, { name: 'VAST-resolving', data: { url: urlfor('wrapper-a.xml'), wrapperDepth: 1, originalUrl: urlfor('wrapper-a.xml') } }, { name: 'VAST-resolved', data: { url: urlfor('wrapper-a.xml'), error: null } }, { name: 'VAST-resolving', data: { url: urlfor('wrapper-b.xml'), wrapperDepth: 2, originalUrl: urlfor('wrapper-b.xml') } }, { name: 'VAST-resolved', data: { url: urlfor('wrapper-b.xml'), error: null } }, { name: 'VAST-resolving', data: { url: urlfor('sample.xml'), wrapperDepth: 3, originalUrl: urlfor('sample.xml') } }, { name: 'VAST-resolved', data: { url: urlfor('sample.xml'), error: null } } ]); }); it('should have found 2 ads', () => { this.response.ads.should.have.length(2); }); it('should have returned a VAST response object', () => { this.response.should.be.an.instanceOf(VASTResponse); }); it('should have retrived root VAST version', () => { this.response.version.should.eql('2.0'); }); describe('#duration', () => [ null, undefined, -1, 0, 1, '1', '00:00', '00:00:00:00', 'test', '00:test:01', '00:00:01.001', '00:00:01.test' ].map(item => (item => it(`should not return NaN for \`${item}\``, () => isNaN(parserUtils.parseDuration(item)).should.eql(false)))(item) )); describe('#duration', () => [ null, undefined, -1, 0, 1, '1', '00:00', '00:00:00:00', 'test', '00:test:01', '00:00:01.001', '00:00:01.test' ].map(item => (item => it(`should not return NaN for \`${item}\``, () => isNaN(parserUtils.parseDuration(item)).should.eql(false)))(item) )); describe('#For the 1st ad', function() { let ad1 = null; before(() => { ad1 = _response.ads[0]; }); after(() => { ad1 = null; }); it('should have retrieved Ad attributes', () => { ad1.id.should.eql('ad_id_0001'); ad1.sequence.should.eql('1'); }); it('should have retrieved Ad sub-elements values', () => { ad1.system.value.should.eql('AdServer'); ad1.system.version.should.eql('2.0'); ad1.title.should.eql('Ad title'); ad1.advertiser.should.eql('Advertiser name'); ad1.description.should.eql('Description text'); ad1.pricing.value.should.eql('1.09'); ad1.pricing.model.should.eql('CPM'); ad1.pricing.currency.should.eql('USD'); ad1.survey.should.eql('http://example.com/survey'); }); it('should have merged wrapped ad error URLs', () => { ad1.errorURLTemplates.should.eql([ 'http://example.com/wrapperNoTracking-error', 'http://example.com/wrapperA-error', 'http://example.com/wrapperB-error', 'http://example.com/error_[ERRORCODE]' ]); }); it('should have merged impression URLs', () => { ad1.impressionURLTemplates.should.eql([ 'http://example.com/wrapperNoTracking-impression', 'http://example.com/wrapperA-impression', 'http://example.com/wrapperB-impression1', 'http://example.com/wrapperB-impression2', 'http://example.com/impression1_asset:[ASSETURI]_[CACHEBUSTING]', 'http://example.com/impression2_[random]', 'http://example.com/impression3_[RANDOM]' ]); }); it('should have 3 creatives', () => { ad1.creatives.should.have.length(3); }); it('should have 4 extensions', () => { ad1.extensions.should.have.length(4); }); it('validate first extension', () => { ad1.extensions[0].attributes['type'].should.eql('WrapperExtension'); ad1.extensions[0].children.should.have.length(1); ad1.extensions[0].children[0].name.should.eql('extension_tag'); ad1.extensions[0].children[0].value.should.eql('extension_value'); }); it('validate second extension', () => { ad1.extensions[1].attributes['type'].should.eql('Pricing'); ad1.extensions[1].children.should.have.length(1); ad1.extensions[1].children[0].name.should.eql('Price'); ad1.extensions[1].children[0].value.should.eql('0'); ad1.extensions[1].children[0].attributes['model'].should.eql('CPM'); ad1.extensions[1].children[0].attributes['currency'].should.eql('USD'); ad1.extensions[1].children[0].attributes['source'].should.eql( 'someone' ); }); it('validate third extension', () => { ad1.extensions[2].attributes['type'].should.eql('Count'); ad1.extensions[2].children.should.have.length(1); ad1.extensions[2].children[0].name.should.eql('#cdata-section'); ad1.extensions[2].children[0].value.should.eql('4'); }); it('validate fourth extension', () => { ad1.extensions[3].attributes.should.eql({}); ad1.extensions[3].children.should.have.length(1); ad1.extensions[3].children[0].name.should.eql('#text'); ad1.extensions[3].children[0].value.should.eql('{ foo: bar }'); }); it('should not have trackingEvents property', () => { should.equal(ad1.trackingEvents, undefined); }); it('should not have videoClickTrackingURLTemplates property', () => { should.equal(ad1.videoClickTrackingURLTemplates, undefined); }); it('should not have videoClickThroughURLTemplate property', () => { should.equal(ad1.videoClickThroughURLTemplate, undefined); }); it('should not have videoCustomClickURLTemplates property', () => { should.equal(ad1.videoCustomClickURLTemplates, undefined); }); //Linear describe('1st creative (Linear)', function() { let linear = null; before(() => { linear = _response.ads[0].creatives[0]; }); after(() => { linear = null; }); it('should have linear type', () => { linear.type.should.equal('linear'); }); it('should have an id', () => { linear.id.should.equal('id130984'); }); it('should have an adId', () => { linear.adId.should.equal('adId345690'); }); it('should have a sequence', () => { linear.sequence.should.equal('1'); }); it('should not have an apiFramework', () => { should.equal(linear.apiFramework, null); }); it('should have a duration of 90.123s', () => { linear.duration.should.equal(90.123); }); it('should have 2 media file', () => { linear.mediaFiles.should.have.length(2); }); it('should have parsed 1st media file attributes', () => { linear.mediaFiles[0].width.should.equal(512); linear.mediaFiles[0].height.should.equal(288); linear.mediaFiles[0].mimeType.should.equal('video/mp4'); linear.mediaFiles[0].fileURL.should.equal( 'http://example.com/linear-asset.mp4' ); }); it('should have parsed 2nd media file attributes', () => { linear.mediaFiles[1].width.should.equal(512); linear.mediaFiles[1].height.should.equal(288); linear.mediaFiles[1].mimeType.should.equal('application/javascript'); linear.mediaFiles[1].apiFramework.should.equal('VPAID'); linear.mediaFiles[1].deliveryType.should.equal('progressive'); linear.mediaFiles[1].fileURL.should.equal( 'parser.js?adData=http%3A%2F%2Fad.com%2F%3Fcb%3D%5Btime%5D' ); }); it('should have 1 URL for clickthrough', () => { linear.videoClickThroughURLTemplate.should.eql( 'http://example.com/linear-clickthrough' ); }); it('should have 5 URLs for clicktracking', () => { linear.videoClickTrackingURLTemplates.should.eql([ 'http://example.com/linear-clicktracking1_ts:[TIMESTAMP]', 'http://example.com/linear-clicktracking2', 'http://example.com/wrapperB-linear-clicktracking', 'http://example.com/wrapperA-linear-clicktracking1', 'http://example.com/wrapperA-linear-clicktracking2', 'http://example.com/wrapperA-linear-clicktracking3' ]); }); it('should have 2 URLs for customclick', () => { linear.videoCustomClickURLTemplates.should.eql([ 'http://example.com/linear-customclick', 'http://example.com/wrapperA-linear-customclick' ]); }); it('should have 8 tracking events', () => { linear.trackingEvents.should.have.keys( 'start', 'close', 'midpoint', 'complete', 'firstQuartile', 'thirdQuartile', 'progress-30', 'progress-60%' ); }); it('should have 4 URLs for start event', () => { linear.trackingEvents['start'].should.eql([ 'http://example.com/linear-start', 'http://example.com/wrapperB-linear-start', 'http://example.com/wrapperA-linear-start1', 'http://example.com/wrapperA-linear-start2' ]); }); it('should have 3 URLs for complete event', () => { linear.trackingEvents['complete'].should.eql([ 'http://example.com/linear-complete', 'http://example.com/wrapperB-linear-complete', 'http://example.com/wrapperA-linear-complete' ]); }); it('should have 3 URLs for progress-30 event VAST 3.0', () => { linear.trackingEvents['progress-30'].should.eql([ 'http://example.com/linear-progress-30sec', 'http://example.com/wrapperB-linear-progress-30sec', 'http://example.com/wrapperA-linear-progress-30sec' ]); }); it('should have 3 URLs for progress-60% event VAST 3.0', () => { linear.trackingEvents['progress-60%'].should.eql([ 'http://example.com/linear-progress-60%', 'http://example.com/wrapperB-linear-progress-60%', 'http://example.com/wrapperA-linear-progress-60%' ]); }); it('should have 3 URLs for progress-90% event VAST 3.0', () => { linear.trackingEvents['progress-90%'].should.eql([ 'http://example.com/wrapperA-linear-progress-90%' ]); }); it('should have parsed icons element', () => { const icon = linear.icons[0]; icon.program.should.equal('ad1'); icon.height.should.equal(20); icon.width.should.equal(60); icon.xPosition.should.equal('left'); icon.yPosition.should.equal('bottom'); icon.apiFramework.should.equal('VPAID'); icon.offset.should.equal(15); icon.duration.should.equal(90); icon.type.should.equal('image/gif'); icon.staticResource.should.equal( 'http://example.com/linear-icon.gif' ); icon.iconClickThroughURLTemplate.should.equal( 'http://example.com/linear-clickthrough' ); icon.iconClickTrackingURLTemplates.should.eql([ 'http://example.com/linear-clicktracking1', 'http://example.com/linear-clicktracking2' ]); icon.iconViewTrackingURLTemplate.should.equal( 'http://example.com/linear-viewtracking' ); }); }); //Companions describe('2nd creative (Companions)', function() { let companions = null; before(() => { companions = _response.ads[0].creatives[1]; }); after(() => { companions = null; }); it('should have companion type', () => { companions.type.should.equal('companion'); }); it('should have an id', () => { companions.id.should.equal('id130985'); }); it('should have an adId', () => { companions.adId.should.equal('adId345691'); }); it('should have a sequence', () => { companions.sequence.should.equal('2'); }); it('should not have an apiFramework', () => { should.equal(companions.apiFramework, null); }); it('should have 3 variations', () => { companions.variations.should.have.length(3); }); //Companion describe('#Companion', function() { let companion = null; describe('as image/jpeg', function() { before(() => { companion = companions.variations[0]; }); after(() => { companion = null; }); it('should have parsed size and type attributes', () => { companion.width.should.equal('300'); companion.height.should.equal('60'); companion.type.should.equal('image/jpeg'); }); it('should have 1 tracking event', () => { companion.trackingEvents.should.have.keys('creativeView'); }); it('should have 1 url for creativeView event', () => { companion.trackingEvents['creativeView'].should.eql([ 'http://example.com/companion1-creativeview' ]); }); it('should have checked that AltText exists', () => { companion.should.have.property('altText'); }); it('should have parsed AltText for companion and its equal', () => { companion.altText.should.equal('Sample Alt Text Content!!!!'); }); it('should have 1 companion clickthrough url', () => { companion.companionClickThroughURLTemplate.should.equal( 'http://example.com/companion1-clickthrough' ); }); it('should store the first companion clicktracking url', () => { companion.companionClickTrackingURLTemplate.should.equal( 'http://example.com/companion1-clicktracking-first' ); }); it('should have 2 companion clicktracking urls', () => { companion.companionClickTrackingURLTemplates.should.eql([ 'http://example.com/companion1-clicktracking-first', 'http://example.com/companion1-clicktracking-second' ]); }); }); describe('as IFrameResource', function() { before(() => { companion = companions.variations[1]; }); after(() => { companion = null; }); it('should have parsed size and type attributes', () => { companion.width.should.equal('300'); companion.height.should.equal('60'); companion.type.should.equal(0); }); it('does not have tracking events', () => { companion.trackingEvents.should.be.empty; }); it('has the #iframeResource set', () => companion.iframeResource.should.equal( 'http://www.example.com/companion2-example.php' )); }); describe('as text/html', function() { before(() => { companion = companions.variations[2]; }); after(() => { companion = null; }); it('should have parsed size and type attributes', () => { companion.width.should.equal('300'); companion.height.should.equal('60'); companion.type.should.equal('text/html'); }); it('should have 1 tracking event', () => { companion.trackingEvents.should.be.empty; }); it('should have 1 companion clickthrough url', () => { companion.companionClickThroughURLTemplate.should.equal( 'http://www.example.com/companion3-clickthrough' ); }); it('has #htmlResource available', () => companion.htmlResource.should.equal( '<a href="http://www.example.com" target="_blank">Some call to action HTML!</a>' )); }); }); }); //Nonlinear describe('3rd creative (Nonlinears)', function() { let nonlinears = null; before(() => { nonlinears = _response.ads[0].creatives[2]; }); after(() => { nonlinears = null; }); it('should have nonlinear type', () => { nonlinears.type.should.equal('nonlinear'); }); it('should not have an id', () => { should.equal(nonlinears.id, null); }); it('should not have an adId', () => { should.equal(nonlinears.adId, null); }); it('should not have a sequence', () => { should.equal(nonlinears.sequence, null); }); it('should not have an apiFramework', () => { should.equal(nonlinears.apiFramework, null); }); it('should have 1 variation', () => { nonlinears.variations.should.have.length(1); }); //NonLinear describe('#NonLinear', function() { let nonlinear = null; describe('trackingEvents', function() { it('should have 6 tracking events', () => { nonlinears.trackingEvents.should.have.keys( 'start', 'close', 'midpoint', 'complete', 'firstQuartile', 'thirdQuartile' ); }); it('should have 3 URLs for start event', () => { nonlinears.trackingEvents['start'].should.eql([ 'http://example.com/nonlinear-start', 'http://example.com/wrapperB-nonlinear-start', 'http://example.com/wrapperA-nonlinear-start' ]); }); it('should have 3 URLs for complete event', () => { nonlinears.trackingEvents['complete'].should.eql([ 'http://example.com/nonlinear-complete', 'http://example.com/wrapperB-nonlinear-complete', 'http://example.com/wrapperA-nonlinear-complete' ]); }); }); describe('as image/jpeg', function() { before(() => { nonlinear = nonlinears.variations[0]; }); after(() => { nonlinear = null; }); it('should have parsed attributes', () => { nonlinear.width.should.equal('300'); nonlinear.height.should.equal('200'); nonlinear.expandedWidth.should.equal('600'); nonlinear.expandedHeight.should.equal('400'); nonlinear.scalable.should.equal(false); nonlinear.maintainAspectRatio.should.equal(true); nonlinear.minSuggestedDuration.should.equal(100); nonlinear.apiFramework.should.equal('someAPI'); nonlinear.type.should.equal('image/jpeg'); }); it('should have 1 nonlinear clickthrough url', () => { nonlinear.nonlinearClickThroughURLTemplate.should.equal( 'http://example.com/nonlinear-clickthrough' ); }); it('should have 2 nonlinear clicktracking urls', () => { nonlinear.nonlinearClickTrackingURLTemplates.should.eql([ 'http://example.com/nonlinear-clicktracking-1', 'http://example.com/nonlinear-clicktracking-2' ]); }); it('should have AdParameter', () => { nonlinear.adParameters.should.equal('{"key":"value"}'); }); }); }); }); }); describe('#For the 2nd ad', function() { let ad2 = null; before(() => { ad2 = _response.ads[1]; }); after(() => { ad2 = null; }); it('should have retrieved Ad attributes', () => { _response.ads[1].id.should.eql('ad_id_0002'); }); it("should have ignored the wrapper's sequence", () => { should.equal(_response.ads[1].sequence, null); }); it('should have retrieved Ad sub-elements values', () => { ad2.system.value.should.eql('AdServer2'); ad2.system.version.should.eql('2.1'); ad2.title.should.eql('Ad title 2'); should.equal(ad2.advertiser, null); should.equal(ad2.description, null); should.equal(ad2.pricing, null); should.equal(ad2.survey, null); }); it('should have merged error URLs', () => { ad2.errorURLTemplates.should.eql([ 'http://example.com/wrapperNoTracking-error', 'http://example.com/wrapperA-error', 'http://example.com/wrapperB-error' ]); }); it('should have merged impression URLs', () => { ad2.impressionURLTemplates.should.eql([ 'http://example.com/wrapperNoTracking-impression', 'http://example.com/wrapperA-impression', 'http://example.com/wrapperB-impression1', 'http://example.com/wrapperB-impression2', 'http://example.com/impression1' ]); }); it('should have 1 creative', () => { ad2.creatives.should.have.length(1); }); it('should have 1 extension (from the wrapper)', () => { ad2.extensions.should.have.length(1); }); it('validate the extension', () => { ad2.extensions[0].attributes['type'].should.eql('WrapperExtension'); ad2.extensions[0].children.should.have.length(1); ad2.extensions[0].children[0].name.should.eql('extension_tag'); ad2.extensions[0].children[0].value.should.eql('extension_value'); }); //Linear describe('1st creative (Linear)', function() { let linear = null; before(() => { linear = ad2.creatives[0]; }); after(() => { linear = null; }); it('should have linear type', () => { linear.type.should.equal('linear'); }); it('should have an id', () => { linear.id.should.equal('id873421'); }); it('should have an adId', () => { linear.adId.should.equal('adId221144'); }); it('should not have a sequence', () => { should.equal(linear.sequence, null); }); it('should have an apiFramework', () => { linear.apiFramework.should.equal('VPAID'); }); it('should have a duration of 30', () => { linear.duration.should.equal(30); }); it('should have wrapper clickthrough URL', () => { linear.videoClickThroughURLTemplate.should.eql( 'http://example.com/wrapperB-linear-clickthrough' ); }); it('should have wrapper customclick URL', () => { linear.videoCustomClickURLTemplates.should.eql([ 'http://example.com/wrapperA-linear-customclick' ]); }); it('should have 5 URLs for clicktracking', () => { linear.videoClickTrackingURLTemplates.should.eql([ 'http://example.com/linear-clicktracking', 'http://example.com/wrapperB-linear-clicktracking', 'http://example.com/wrapperA-linear-clicktracking1', 'http://example.com/wrapperA-linear-clicktracking2', 'http://example.com/wrapperA-linear-clicktracking3' ]); }); }); }); describe('#For the wrapper-1 ad', function() { this.response = null; this.templateFilterCalls = []; before(done => { vastParser.addURLTemplateFilter(url => { this.templateFilterCalls.push(url); return url; }); vastParser .getAndParseVAST(urlfor('wrapper-sequence.xml'), options) .then(response => { this.response = response; done(); }); }); it('should have called 2 times URLtemplateFilter ', () => { this.templateFilterCalls.should.have.length(2); this.templateFilterCalls.should.eql([ urlfor('wrapper-sequence.xml'), urlfor('sample-wrapped.xml') ]); }); it('should have carried sequence over from wrapper', () => { this.response.ads[0].sequence.should.eql('1'); }); }); describe('#VPAID', function() { this.response = null; before(done => { this.wrapperSpy = sinon.spy(parserUtils, 'resolveVastAdTagURI'); vastParser .getAndParseVAST(urlfor('vpaid.xml'), options) .then(response => { this.response = response; done(); }); }); it('should not try to resolve wrappers', () => { sinon.assert.notCalled(this.wrapperSpy); }); it('should have apiFramework set', () => { this.response.ads[0].creatives[0].mediaFiles[0].apiFramework.should.be.equal( 'VPAID' ); }); it('should have duration set to -1', () => { this.response.ads[0].creatives[0].duration.should.be.equal(-1); }); }); describe('#Ad Pod', function() { this.response = null; before(done => { vastParser .getAndParseVAST(urlfor('wrapper-ad-pod.xml'), options) .then(response => { this.response = response; done(); }); }); it('should have parsed 2 ads', () => { this.response.ads.should.have.length(2); }); it('should have maintened the sequence when resolving wrappers', () => { this.response.ads[0].sequence.should.be.equal('1'); this.response.ads[1].sequence.should.be.equal('2'); }); }); }); describe('#parseVAST', function() { const options = { urlhandler: nodeURLHandler }; this.response = null; this.templateFilterCalls = []; before(done => { vastParser.addURLTemplateFilter(url => { this.templateFilterCalls.push(url); return url; }); const url = urlfor('wrapper-notracking.xml'); vastParser.urlHandler.get(url, {}, (err, xml) => { // `VAST > Wrapper > VASTAdTagURI` in the VAST must be an absolute URL for (let nodeKey in xml.documentElement.childNodes) { const node = xml.documentElement.childNodes[nodeKey]; if (node.nodeName === 'Ad') { for (let adNodeKey in node.childNodes) { const adNode = node.childNodes[adNodeKey]; if (adNode.nodeName === 'Wrapper') { for (let wrapperNodeKey in adNode.childNodes) { const wrapperNode = adNode.childNodes[wrapperNodeKey]; if (wrapperNode.nodeName === 'VASTAdTagURI') { wrapperNode.textContent = urlfor( parserUtils.parseNodeText(wrapperNode) ); break; } } } } } } vastParser.parseVAST(xml, options).then(response => { this.response = response; done(); }); }); }); after(() => { vastParser.clearURLTemplateFilters(); }); it('should have 1 filter defined', () => { vastParser.countURLTemplateFilters().should.equal(1); }); it('should have called 3 times URLtemplateFilter ', () => { this.templateFilterCalls.should.have.length(3); this.templateFilterCalls.should.eql([ urlfor('wrapper-a.xml'), urlfor('wrapper-b.xml'), urlfor('sample.xml') ]); }); it('should have found 2 ads', () => { this.response.ads.should.have.length(2); }); it('should have returned a VAST response object', () => { this.response.should.be.an.instanceOf(VASTResponse); }); }); describe('#Tracking', function() { let trackCalls = null; let dataTriggered = null; const options = { urlhandler: nodeURLHandler }; beforeEach(() => { vastParser.removeAllListeners(); dataTriggered = []; trackCalls = []; vastParser.on('VAST-error', variables => dataTriggered.push(variables)); util.track = (templates, variables) => { trackCalls.push({ templates, variables }); }; }); describe('#No-Ad', function() { it('emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('empty-no-ad.xml'), options) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(303); dataTriggered[0].extensions.should.eql([]); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/empty-no-ad' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 303 }); done(); }); }); it('when wrapped, emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('wrapper-empty.xml'), options) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(303); dataTriggered[0].extensions[0].children[0].name.should.eql( 'paramWrapperEmptyNoAd' ); dataTriggered[0].extensions[0].children[0].value.should.eql( 'valueWrapperEmptyNoAd' ); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/wrapper-empty_wrapper-error', 'http://example.com/empty-no-ad' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 303 }); done(); }); }); }); describe('#Ad with no creatives', function() { it('emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('empty-no-creative.xml'), options) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(303); dataTriggered[0].extensions[0].children[0].name.should.eql( 'paramEmptyNoCreative' ); dataTriggered[0].extensions[0].children[0].value.should.eql( 'valueEmptyNoCreative' ); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/empty-no-creative_inline-error' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 303 }); done(); }); }); it('when wrapped, emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('wrapper-empty-no-creative.xml'), options) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(303); dataTriggered[0].extensions[0].children[0].name.should.eql( 'paramWrapperEmptyNoCreative' ); dataTriggered[0].extensions[0].children[0].value.should.eql( 'valueWrapperEmptyNoCreative' ); dataTriggered[0].extensions[1].children[0].name.should.eql( 'paramEmptyNoCreative' ); dataTriggered[0].extensions[1].children[0].value.should.eql( 'valueEmptyNoCreative' ); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/wrapper-no-creative_wrapper-error', 'http://example.com/empty-no-creative_inline-error' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 303 }); done(); }); }); }); describe('#Invalid XML file (parsing error)', function() { it('returns an error', done => { vastParser .getAndParseVAST(urlfor('invalid-xmlfile.xml'), options) .catch(err => { // Error returned err.should.be .instanceof(Error) .and.have.property('message', 'Invalid VAST XMLDocument'); done(); }); }); it('when wrapped, emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('wrapper-invalid-xmlfile.xml'), options) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(301); dataTriggered[0].extensions[0].children[0].name.should.eql( 'paramWrapperInvalidXmlfile' ); dataTriggered[0].extensions[0].children[0].value.should.eql( 'valueWrapperInvalidXmlfile' ); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/wrapper-invalid-xmlfile_wrapper-error' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 301 }); done(); }); }); }); describe('#Wrapper URL unavailable/timeout', () => { it('emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('wrapper-unavailable-url.xml'), options) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(301); dataTriggered[0].extensions[0].children[0].name.should.eql( 'paramWrapperInvalidXmlfile' ); dataTriggered[0].extensions[0].children[0].value.should.eql( 'valueWrapperInvalidXmlfile' ); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/wrapper-invalid-xmlfile_wrapper-error' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 301 }); done(); }); }); }); describe('#Wrapper limit reached', () => { it('emits a VAST-error & track', done => { vastParser .getAndParseVAST(urlfor('wrapper-a.xml'), { wrapperLimit: 1, urlhandler: nodeURLHandler }) .then(response => { // Response doesn't have any ads response.ads.should.eql([]); // Error has been triggered dataTriggered.length.should.eql(1); dataTriggered[0].ERRORCODE.should.eql(302); dataTriggered[0].extensions[0].children[0].name.should.eql( 'extension_tag' ); dataTriggered[0].extensions[0].children[0].value.should.eql( 'extension_value' ); // Tracking has been done trackCalls.length.should.eql(1); trackCalls[0].templates.should.eql([ 'http://example.com/wrapperA-error' ]); trackCalls[0].variables.should.eql({ ERRORCODE: 302 }); done(); }); }); }); }); describe('#legacy', function() { const options = { urlhandler: nodeURLHandler }; beforeEach(() => { vastParser.removeAllListeners(); }); it('correctly loads a wrapped ad, even with the VASTAdTagURL-Tag', done => { vastParser .getAndParseVAST(urlfor('wrapper-legacy.xml'), options) .then(response => { it('should have found 1 ad', () => { response.ads.should.have.length(1); }); it('should have returned a VAST response object', () => { response.should.be.an.instanceOf(VASTResponse); }); // we just want to make sure that the sample.xml was loaded correctly const linear = response.ads[0].creatives[0]; it('should have parsed media file attributes', () => { const mediaFile = linear.mediaFiles[0]; mediaFile.width.should.equal(512); mediaFile.height.should.equal(288); mediaFile.mimeType.should.equal('video/mp4'); mediaFile.fileURL.should.equal('http://example.com/asset.mp4'); }); done(); }); }); }); // Leave at the end describe('parsing events', function() { describe('failed wrapper resolution', function() { let lastErr = null; const vastParser = new VASTParser(); before(done => { const options = { urlhandler: nodeURLHandler }; vastParser.on('VAST-resolved', variables => { lastErr = variables.error; }); vastParser .getAndParseVAST(urlfor('wrapper-unavailable-url.xml'), options) .then(() => { done(); }); }); it('should show error when emitting resolved event', function() { lastErr.should.not.equal(null); }); }); }); });