Skip to content

Commit

Permalink
feat(VAST-35): Add Complete VAST specs (4.3) (#26)
Browse files Browse the repository at this point in the history
* VAST-33 : refactor wip

* comment

* VAST-33 add nonlinear and companion

* read multiple ads (WIP)

* VAST-33 add companions in provided slotId dom elements

* VAST-33 condition listeners for non linearCreative as linearCreative can be the only ad

* VAST-33 don't use canplay event as it is triggered when autoplay is false and can provoke a race

* VAST-33 remove unneeded console

* VAST-33 watcher on bundle for demo page

* VAST-33 add some more VAST examples

* VAST-33 adpods

- refactor to be compatible with adpods
- use 'ad' events only
- remove companions domElements

* VAST-33 icons

* VAST-33 macros

* VAST-33 move big functions outside of the class

* VAST-33 remove console

* VAST-33 ads verification

* VAST-33 reuse domElements array

* VAST-33 import Vast for static methods

* VAST-33 vmap wip

* VAST-33 vmap WIP + eslint

* VAST-33 vmap midroll

* VAST-33 manage different offset format

* VAST-33 clean

* VAST-33 add skip button

* VAST-33 fix ad timeout

* VAST-33 add ctaDiv to domElements array

* Add E2E tests and demo page 😍

* add missing skip example

* VAST-33 clean options and complete doc

* VAST-33: fix timeout option bug

* VAST-33: add macros for OMID

---------

Co-authored-by: kasty <mariegosse@gmail.com>
  • Loading branch information
privaloops and kasty committed May 3, 2023
1 parent 12bccc1 commit 24bad42
Show file tree
Hide file tree
Showing 35 changed files with 17,488 additions and 4,675 deletions.
21 changes: 21 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: 'airbnb-base',
overrides: [
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'no-console': 0,
'no-unused-vars': 1,
'import/no-extraneous-dependencies': ['error', { devDependencies: true, optionalDependencies: true, peerDependencies: true }],
'import/no-cycle': [2, { maxDepth: 1 }],
'import/prefer-default-export': 0,
'no-param-reassign': ['error', { props: false }],
},
};
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,14 @@ By default the plugin handle the cta clickzone. You can disable this default beh

This plugin currently supports a handful of options that might help you customize it to your needs. Below you can find a description of the options supported to this date. Please bear in mind this is a work in progress and more options should be available in the future, especially if requested through this repository.

* **vastURL** (string - required) - The URL where the plugin will fetch the VAST manifest from. The
* **isLimitedTracking** (boolean) - According to the Vast [documentation](https://interactiveadvertisingbureau.github.io/vast/vast4macros/vast4-macros-latest.html#macro-spec-limitadtracking), relates to the LIMITADTRACKING macro
* **timeout** (milliseconds - int) - Max amount of time the plugin should wait for the manifest URL to respond and the assets to load. Will throw an error if this value is exceeded.
* **debug** (boolean) - Display detailed logging in the browser console.
* **addCtaClickZone** (boolean) - Add or not a clickzone for the cta url
* **vastUrl** (string) - The URL where the plugin will fetch the VAST manifest from
* **vmapUrl** (string) - The URL where the plugin will fetch the VMAP manifest from
* **isLimitedTracking** (boolean) - According to the Vast [documentation](https://interactiveadvertisingbureau.github.io/vast/vast4macros/vast4-macros-latest.html#macro-spec-limitadtracking), relates to the LIMITADTRACKING macro. ***Default : false***
* **timeout** (milliseconds - int) - Max amount of time the plugin should wait for the manifest URL to respond and the assets to load. Will throw an error if this value is exceeded. ***Default: 5000***
* **verificationTimeout** (milliseconds - int) - Max amount of time the plugin should wait for the OMID verification URLs to respond and the assets to load. ***Default: 2000***
* **debug** (boolean) - Display detailed logging in the browser console. ***Default: false***
* **addCtaClickZone** (boolean) - Add or not a clickzone for the cta url. ***Default: true***
* **addSkipButton** (boolean) - Add or not a skip button for skippable ads. ***Default: true***

#### Events

Expand Down Expand Up @@ -133,6 +136,7 @@ Current contributors and maintainers:
[privaloops](https://github.com/privaloops)
[fafaschiavo](https://github.com/fafaschiavo)
[kachanovskyi](https://github.com/kachanovskyi)
[kasty](https://github.com/kasty)

#### License
This plugin, just like Video.js, is licensed under the Apache License, Version 2.0.
19 changes: 0 additions & 19 deletions __mocks__/video.js/index.js

This file was deleted.

Empty file.
1 change: 0 additions & 1 deletion babel.config.js

This file was deleted.

16 changes: 16 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { defineConfig } = require('cypress')

module.exports = defineConfig({
chromeWebSecurity: false,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
// eslint-disable-next-line global-require
return require('./cypress/plugins/index')(on, config);
},
baseUrl: 'http://localhost:3000',
video: false,
experimentalWebKitSupport: true,
},
});
216 changes: 216 additions & 0 deletions cypress/e2e/all.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/* eslint-disable max-len */
/* eslint-disable array-callback-return */
/* eslint-disable no-undef */
import { VASTParser } from '@dailymotion/vast-client';

describe('Linear Test : Inline', () => {
it('Player source is ad source', () => {
const vastUrl = 'https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%204.2%20Samples/Inline_Simple.xml';
cy.intercept('GET', vastUrl).as('vastFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
cy.wait('@vastFile').then((req) => {
const vastXml = (new window.DOMParser()).parseFromString(req.response.body, 'text/xml');
const vastParser = new VASTParser();
vastParser.parseVAST(vastXml)
.then((parsedVAST) => {
const linearAd = parsedVAST.ads[0].creatives.filter((creative) => creative.type === 'linear')[0];
cy.window().then((win) => {
win.adsPlugin.player.on('play', (data) => {
cy.get('video').should('have.prop', 'src', linearAd.mediaFiles[0].fileURL);
});
cy.get('.vjs-big-play-button').click();
});
expect(req.state).to.eq('Complete');
});
});
});
});

describe('Linear Test : Wrapper', () => {
it('Creative is reachable', () => {
const vastUrl = 'https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%204.2%20Samples/Wrapper_Tag-test.xml';
cy.intercept('GET', vastUrl).as('vastFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
cy.wait('@vastFile').then((req) => {
const vastXml = (new window.DOMParser()).parseFromString(req.response.body, 'text/xml');
const vastParser = new VASTParser();
vastParser.parseVAST(vastXml)
.then((parsedVAST) => {
expect(parsedVAST.ads[0].creatives.length).to.be.greaterThan(0);
});
cy.wait(100);
expect(req.state).to.eq('Complete');
});
});
});

describe('Linear : skip', () => {
it('Skip button should be present', () => {
const vastUrl = 'https://www.arte.tv/static/artevpv7/vast/vast_skip.xml';
cy.intercept('GET', vastUrl).as('vastFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
cy.wait('@vastFile').then(() => {
cy.get('.vjs-big-play-button').click();
cy.get('#videojs-vast-skipButton').should('exist');
});
});
});

describe('Linear : icon', () => {
it('Icon has been added', () => {
const vastUrl = 'https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%204.2%20Samples/IconClickFallbacks.xml';
cy.intercept('GET', vastUrl).as('vastFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
cy.wait('@vastFile').then((req) => {
const vastXml = (new window.DOMParser()).parseFromString(req.response.body, 'text/xml');
const vastParser = new VASTParser();
vastParser.parseVAST(vastXml)
.then((parsedVAST) => {
const linearAd = parsedVAST.ads[0].creatives.filter((creative) => creative.type === 'linear')[0];
cy.get('.vjs-big-play-button').click();
cy.get(`img[src="${linearAd.icons[0].staticResource}"]`).should('exist');
cy.get(`img[src="${linearAd.icons[0].staticResource}"]`).should('have.attr', 'width', linearAd.icons[0].width);
});
});
});
});

describe('Linear Test : companions', () => {
it('Player should display companions', () => {
const vastUrl = 'https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%204.2%20Samples/Inline_Companion_Tag-test.xml';
cy.intercept('GET', vastUrl).as('vastFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
cy.wait('@vastFile').then((req) => {
const vastXml = (new window.DOMParser()).parseFromString(req.response.body, 'text/xml');
const vastParser = new VASTParser();
vastParser.parseVAST(vastXml)
.then((parsedVAST) => {
const linearAd = parsedVAST.ads[0].creatives.filter((creative) => creative.type === 'companion')[0];
cy.window().then((win) => {
cy.get('.vjs-big-play-button').click();
win.adsPlugin.player.on('play', (data) => {
linearAd.variations.map((variation) => {
// image
if (variation.staticResources && variation.staticResources.length > 0) {
variation.staticResources.map((staticResource) => {
cy.get(`img[src="${staticResource.url}"]`).should('exist');
});
}
// html
if (variation.htmlResources) {
variation.htmlResources.map((htmlResource) => {
const textArea = document.createElement('textarea');
textArea.innerText = htmlResource;
// cy.get('.video-js').invoke('html').contains(textArea.innerHTML);
});
}
// iframe
if (variation.iframeResources) {
variation.iframeResources.map((iframeResource) => {
cy.get(`iframe[src="${iframeResource}"]`).should('exist');
});
}
});
});
});
});
});
});
});

describe('Linear Test : adPods', () => {
it('Player should play all ads of adpods', () => {
const vastUrl = 'https://raw.githubusercontent.com/dailymotion/vast-client-js/b5a72b04226a6880da1e00191033edb150f6b956/test/vastfiles/wrapper-ad-pod.xml';
cy.intercept('GET', vastUrl).as('vastFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
cy.wait('@vastFile').then((req) => {
cy.get('.vjs-big-play-button').click();
cy.window().then((win) => {
win.adsPlugin.player.on('adplay', cy.stub().as('adplay'));
});
cy.get('@adplay', { timeout: 60000 }).should('have.callCount', 2);
expect(req.state).to.eq('Complete');
});
});
});

describe('Linear Test : empty VAST', () => {
it('Player should play normal video and no vast event', () => {
const vastUrl = 'https://raw.githubusercontent.com/dailymotion/vast-client-js/b5a72b04226a6880da1e00191033edb150f6b956/test/vastfiles/empty-no-ad.xml';
cy.intercept('GET', vastUrl).as('vastFile');
// cy.intercept('GET', videoFile).as('videoFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
// cy.wait('@videoFile');
cy.wait('@vastFile').then((req) => {
cy.window().then((win) => {
win.adsPlugin.player.on('timeupdate', cy.stub().as('timeupdate'));
cy.get('.vjs-big-play-button').click();
cy.get('@timeupdate').should('have.been.called');
});
});
});
});

describe('Linear Test : Impression', () => {
it.skip('Impression are tracked', () => {
const vastUrl = 'https://raw.githubusercontent.com/dailymotion/vast-client-js/b5a72b04226a6880da1e00191033edb150f6b956/test/vastfiles/wrapper-ad-pod.xml';
// intercept final vast
cy.intercept('GET', 'inline-linear.xml').as('vastFile');
// cy.intercept('GET', videoFile).as('videoFile');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
// cy.intercept('GET', videoFile).as('videoFile');
cy.wait('@vastFile').then((req) => {
const vastXml = (new window.DOMParser()).parseFromString(req.response.body, 'text/xml');
const vastParser = new VASTParser();
vastParser.parseVAST(vastXml)
.then((parsedVAST) => {
const linearAd = parsedVAST.ads[0].creatives.filter((creative) => creative.type === 'linear')[0];
cy.intercept('GET', linearAd.trackingEvents.firstQuartile[0]).as('firstQuartile');
cy.intercept('GET', linearAd.trackingEvents.midpoint[0]).as('midpoint');
cy.intercept('GET', linearAd.trackingEvents.thirdQuartile[0]).as('thirdQuartile');
cy.intercept('GET', linearAd.trackingEvents.complete[0]).as('complete');
cy.wait(5000);
cy.window().then((win) => {
cy.get('.vjs-big-play-button').click();
cy.wait('@firstQuartile').then((req) => {
cy.log(req);
expect(req.state).to.eq('Complete');
});
cy.wait(3000);
cy.wait('@midpoint').then((req) => {
cy.log(req);
expect(req.state).to.eq('Complete');
});
cy.wait(3000);
cy.wait('@thirdQuartile').then((req) => {
cy.log(req);
expect(req.state).to.eq('Complete');
});
cy.wait(5000);
cy.wait('@complete').then((req) => {
cy.log(req);
expect(req.state).to.eq('Complete');
});
});
});
expect(req.state).to.eq('Complete');
});
});
});

describe('Linear Test : verification', () => {
it('Verification script are loaded', () => {
const vastUrl = 'https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%204.0%20Samples/Ad_Verification-test.xml';
cy.intercept('GET', vastUrl).as('vastFile');
// cy.intercept('GET', videoFile).as('videoFile');
cy.intercept('GET', 'https://verificationcompany1.com/verification_script1.js').as('verificationScript1');
cy.intercept('GET', 'https://verificationcompany.com/untrusted.js').as('verificationScript2');
cy.visit(`http://localhost:3000/?vastUrl=${encodeURIComponent(vastUrl)}`);
// cy.wait('@videoFile');
cy.wait('@vastFile');
cy.get('.vjs-big-play-button').click();
// done with erros but done
cy.wait('@verificationScript1').should('have.property', 'error');
cy.wait('@verificationScript2').should('have.property', 'error');
});
});
22 changes: 22 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
20 changes: 20 additions & 0 deletions cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
Loading

0 comments on commit 24bad42

Please sign in to comment.