negromate/web/static/js/videojs-playlist-ui/test/plugin.test.js

577 lines
19 KiB
JavaScript

/* eslint-disable no-console */
import document from 'global/document';
import window from 'global/window';
import QUnit from 'qunit';
import videojs from 'video.js';
import 'videojs-playlist';
import '../src/plugin';
const playlist = [{
name: 'Movie 1',
description: 'Movie 1 description',
duration: 100,
data: {
id: '1',
foo: 'bar'
},
sources: [{
src: '//example.com/movie1.mp4',
type: 'video/mp4'
}]
}, {
sources: [{
src: '//example.com/movie2.mp4',
type: 'video/mp4'
}],
thumbnail: '//example.com/movie2.jpg'
}];
const resolveUrl = url => {
const a = document.createElement('a');
a.href = url;
return a.href;
};
const dom = videojs.dom || videojs;
const Html5 = videojs.getTech('Html5');
QUnit.test('the environment is sane', function(assert) {
assert.ok(true, 'everything is swell');
});
function setup() {
this.oldVideojsBrowser = videojs.browser;
videojs.browser = videojs.mergeOptions({}, videojs.browser);
this.fixture = document.querySelector('#qunit-fixture');
// force HTML support so the tests run in a reasonable
// environment under phantomjs
this.realIsHtmlSupported = Html5.isSupported;
Html5.isSupported = function() {
return true;
};
// create a video element
const video = document.createElement('video');
this.fixture.appendChild(video);
// create a video.js player
this.player = videojs(video);
// Create two playlist container elements.
this.fixture.appendChild(dom.createEl('div', {className: 'vjs-playlist'}));
this.fixture.appendChild(dom.createEl('div', {className: 'vjs-playlist'}));
}
function teardown() {
videojs.browser = this.oldVideojsBrowser;
Html5.isSupported = this.realIsHtmlSupported;
this.player.dispose();
this.player = null;
dom.emptyEl(this.fixture);
}
QUnit.module('videojs-playlist-ui', {beforeEach: setup, afterEach: teardown});
QUnit.test('registers itself', function(assert) {
assert.ok(this.player.playlistUi, 'registered the plugin');
});
QUnit.test('errors if used without the playlist plugin', function(assert) {
assert.throws(function() {
this.player.playlist = null;
this.player.playlistUi();
}, 'threw on init');
});
QUnit.test('is empty if the playlist plugin isn\'t initialized', function(assert) {
this.player.playlistUi();
const items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.ok(this.fixture.querySelector('.vjs-playlist'), 'created the menu');
assert.strictEqual(items.length, 0, 'displayed no items');
});
QUnit.test('can be initialized with an element (deprecated form)', function(assert) {
const elem = dom.createEl('div');
this.player.playlist(playlist);
this.player.playlistUi(elem);
assert.strictEqual(
elem.querySelectorAll('li.vjs-playlist-item').length,
playlist.length,
'created an element for each playlist item'
);
});
QUnit.test('can be initialized with an element', function(assert) {
const elem = dom.createEl('div');
this.player.playlist(playlist);
this.player.playlistUi({el: elem});
assert.strictEqual(
elem.querySelectorAll('li.vjs-playlist-item').length,
playlist.length,
'created an element for each playlist item'
);
});
QUnit.test('can look for an element with the class "vjs-playlist" that is not already in use', function(assert) {
const firstEl = this.fixture.querySelectorAll('.vjs-playlist')[0];
const secondEl = this.fixture.querySelectorAll('.vjs-playlist')[1];
// Give the firstEl a child, so the plugin thinks it is in use and moves on
// to the next one.
firstEl.appendChild(dom.createEl('div'));
this.player.playlist(playlist);
this.player.playlistUi();
assert.strictEqual(this.player.playlistMenu.el(), secondEl, 'used the first matching/empty element');
assert.strictEqual(
secondEl.querySelectorAll('li.vjs-playlist-item').length,
playlist.length,
'found an element for each playlist item'
);
});
QUnit.test('can look for an element with a custom class that is not already in use', function(assert) {
const firstEl = dom.createEl('div', {className: 'super-playlist'});
const secondEl = dom.createEl('div', {className: 'super-playlist'});
// Give the firstEl a child, so the plugin thinks it is in use and moves on
// to the next one.
firstEl.appendChild(dom.createEl('div'));
this.fixture.appendChild(firstEl);
this.fixture.appendChild(secondEl);
this.player.playlist(playlist);
this.player.playlistUi({
className: 'super-playlist'
});
assert.strictEqual(this.player.playlistMenu.el(), secondEl, 'used the first matching/empty element');
assert.strictEqual(
this.fixture.querySelectorAll('li.vjs-playlist-item').length,
playlist.length,
'created an element for each playlist item'
);
});
QUnit.test('specializes the class name if touch input is absent', function(assert) {
videojs.browser.TOUCH_ENABLED = false;
this.player.playlist(playlist);
this.player.playlistUi();
assert.ok(this.player.playlistMenu.hasClass('vjs-mouse'), 'marked the playlist menu');
});
QUnit.test('can be re-initialized without doubling the contents of the list', function(assert) {
const el = this.fixture.querySelectorAll('.vjs-playlist')[0];
this.player.playlist(playlist);
this.player.playlistUi();
this.player.playlistUi();
this.player.playlistUi();
assert.strictEqual(this.player.playlistMenu.el(), el, 'used the first matching/empty element');
assert.strictEqual(
el.querySelectorAll('li.vjs-playlist-item').length,
playlist.length,
'found an element for each playlist item'
);
});
QUnit.module('videojs-playlist-ui: Components', {beforeEach: setup, afterEach: teardown});
// --------------------
// Creation and Updates
// --------------------
QUnit.test('includes the video name if provided', function(assert) {
this.player.playlist(playlist);
this.player.playlistUi();
const items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items[0].querySelector('.vjs-playlist-name').textContent,
playlist[0].name,
'wrote the name');
assert.strictEqual(items[1].querySelector('.vjs-playlist-name').textContent,
'Untitled Video',
'wrote a placeholder for the name');
});
QUnit.test('includes custom data attribute if provided', function(assert) {
this.player.playlist(playlist);
this.player.playlistUi();
const items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items[0].dataset.id,
playlist[0].data.id,
'set a single data attribute');
assert.strictEqual(items[0].dataset.id,
'1',
'set a single data attribute (actual value)');
assert.strictEqual(items[0].dataset.foo,
playlist[0].data.foo,
'set an addtional data attribute');
assert.strictEqual(items[0].dataset.foo,
'bar',
'set an addtional data attribute');
});
QUnit.test('outputs a <picture> for simple thumbnails', function(assert) {
this.player.playlist(playlist);
this.player.playlistUi();
const pictures = this.fixture.querySelectorAll('.vjs-playlist-item picture');
assert.strictEqual(pictures.length, 1, 'output one picture');
const imgs = pictures[0].querySelectorAll('img');
assert.strictEqual(imgs.length, 1, 'output one img');
assert.strictEqual(imgs[0].src, window.location.protocol + playlist[1].thumbnail, 'set the src attribute');
});
QUnit.test('outputs a <picture> for responsive thumbnails', function(assert) {
const playlistOverride = [{
sources: [{
src: '//example.com/movie.mp4',
type: 'video/mp4'
}],
thumbnail: [{
srcset: '/test/example/oceans.jpg',
type: 'image/jpeg',
media: '(min-width: 400px;)'
}, {
src: '/test/example/oceans-low.jpg'
}]
}];
this.player.playlist(playlistOverride);
this.player.playlistUi();
const sources = this.fixture.querySelectorAll('.vjs-playlist-item picture source');
const imgs = this.fixture.querySelectorAll('.vjs-playlist-item picture img');
assert.strictEqual(sources.length, 1, 'output one source');
assert.strictEqual(sources[0].srcset,
playlistOverride[0].thumbnail[0].srcset,
'wrote the srcset attribute');
assert.strictEqual(sources[0].type,
playlistOverride[0].thumbnail[0].type,
'wrote the type attribute');
assert.strictEqual(sources[0].media,
playlistOverride[0].thumbnail[0].media,
'wrote the type attribute');
assert.strictEqual(imgs.length, 1, 'output one img');
assert.strictEqual(imgs[0].src,
resolveUrl(playlistOverride[0].thumbnail[1].src),
'output the img src attribute');
});
QUnit.test('outputs a placeholder for items without thumbnails', function(assert) {
this.player.playlist(playlist);
this.player.playlistUi();
const thumbnails = this.fixture.querySelectorAll('.vjs-playlist-item .vjs-playlist-thumbnail');
assert.strictEqual(thumbnails.length, playlist.length, 'output two thumbnails');
assert.strictEqual(thumbnails[0].nodeName.toLowerCase(), 'div', 'the second is a placeholder');
});
QUnit.test('includes the duration if one is provided', function(assert) {
this.player.playlist(playlist);
this.player.playlistUi();
const durations = this.fixture.querySelectorAll('.vjs-playlist-item .vjs-playlist-duration');
assert.strictEqual(durations.length, 1, 'skipped the item without a duration');
assert.strictEqual(durations[0].textContent,
'1:40',
'wrote the duration');
assert.strictEqual(durations[0].getAttribute('datetime'),
'PT0H0M' + playlist[0].duration + 'S',
'wrote a machine-readable datetime');
});
QUnit.test('marks the selected playlist item on startup', function(assert) {
this.player.playlist(playlist);
this.player.currentSrc = () => playlist[0].sources[0].src;
this.player.playlistUi();
const selectedItems = this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected');
assert.strictEqual(selectedItems.length, 1, 'marked one playlist item');
assert.strictEqual(selectedItems[0].querySelector('.vjs-playlist-name').textContent,
playlist[0].name,
'marked the first playlist item');
});
QUnit.test('updates the selected playlist item on loadstart', function(assert) {
this.player.playlist(playlist);
this.player.playlistUi();
this.player.playlist.currentItem(1);
this.player.currentSrc = () => playlist[1].sources[0].src;
this.player.trigger('loadstart');
const selectedItems = this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected');
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist-item').length,
playlist.length,
'displayed the correct number of items');
assert.strictEqual(selectedItems.length, 1, 'marked one playlist item');
assert.strictEqual(selectedItems[0].querySelector('img').src,
resolveUrl(playlist[1].thumbnail),
'marked the second playlist item');
});
QUnit.test('selects no item if the playlist is not in use', function(assert) {
this.player.playlist(playlist);
this.player.playlist.currentItem = () => -1;
this.player.playlistUi();
this.player.trigger('loadstart');
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist-item.vjs-selected').length,
0,
'no items selected');
});
QUnit.test('updates on "playlistchange", different lengths', function(assert) {
this.player.playlist([]);
this.player.playlistUi();
let items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, 0, 'no items initially');
this.player.playlist(playlist);
this.player.trigger('playlistchange');
items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, playlist.length, 'updated with the new items');
});
QUnit.test('updates on "playlistchange", equal lengths', function(assert) {
this.player.playlist([{sources: []}, {sources: []}]);
this.player.playlistUi();
let items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, 2, 'two items initially');
this.player.playlist(playlist);
this.player.trigger('playlistchange');
items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, playlist.length, 'updated with the new items');
assert.strictEqual(this.player.playlistMenu.items[0].item, playlist[0], 'we have updated items');
assert.strictEqual(this.player.playlistMenu.items[1].item, playlist[1], 'we have updated items');
});
QUnit.test('updates on "playlistchange", update selection', function(assert) {
this.player.playlist(playlist);
this.player.currentSrc = function() {
return playlist[0].sources[0].src;
};
this.player.playlistUi();
let items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, 2, 'two items initially');
assert.ok((/vjs-selected/).test(items[0].getAttribute('class')), 'first item is selected by default');
this.player.playlist.currentItem(1);
this.player.currentSrc = function() {
return playlist[1].sources[0].src;
};
this.player.trigger('playlistchange');
items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, playlist.length, 'updated with the new items');
assert.ok((/vjs-selected/).test(items[1].getAttribute('class')), 'second item is selected after update');
assert.ok(!(/vjs-selected/).test(items[0].getAttribute('class')), 'first item is not selected after update');
});
QUnit.test('updates on "playlistsorted", different lengths', function(assert) {
this.player.playlist([]);
this.player.playlistUi();
let items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, 0, 'no items initially');
this.player.playlist(playlist);
this.player.trigger('playlistsorted');
items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, playlist.length, 'updated with the new items');
});
QUnit.test('updates on "playlistsorted", equal lengths', function(assert) {
this.player.playlist([{sources: []}, {sources: []}]);
this.player.playlistUi();
let items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, 2, 'two items initially');
this.player.playlist(playlist);
this.player.trigger('playlistsorted');
items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, playlist.length, 'updated with the new items');
assert.strictEqual(this.player.playlistMenu.items[0].item, playlist[0], 'we have updated items');
assert.strictEqual(this.player.playlistMenu.items[1].item, playlist[1], 'we have updated items');
});
QUnit.test('updates on "playlistsorted", update selection', function(assert) {
this.player.playlist(playlist);
this.player.currentSrc = function() {
return playlist[0].sources[0].src;
};
this.player.playlistUi();
let items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, 2, 'two items initially');
assert.ok((/vjs-selected/).test(items[0].getAttribute('class')), 'first item is selected by default');
this.player.playlist.currentItem(1);
this.player.currentSrc = function() {
return playlist[1].sources[0].src;
};
this.player.trigger('playlistsorted');
items = this.fixture.querySelectorAll('.vjs-playlist-item');
assert.strictEqual(items.length, playlist.length, 'updated with the new items');
assert.ok((/vjs-selected/).test(items[1].getAttribute('class')), 'second item is selected after update');
assert.ok(!(/vjs-selected/).test(items[0].getAttribute('class')), 'first item is not selected after update');
});
QUnit.test('tracks when an ad is playing', function(assert) {
this.player.playlist([]);
this.player.playlistUi();
this.player.duration = () => 5;
const playlistMenu = this.player.playlistMenu;
assert.ok(!playlistMenu.hasClass('vjs-ad-playing'),
'does not have class vjs-ad-playing');
this.player.trigger('adstart');
assert.ok(playlistMenu.hasClass('vjs-ad-playing'),
'has class vjs-ad-playing');
this.player.trigger('adend');
assert.ok(!playlistMenu.hasClass('vjs-ad-playing'),
'does not have class vjs-ad-playing');
});
// -----------
// Interaction
// -----------
QUnit.test('changes the selection when tapped', function(assert) {
let playCalled = false;
this.player.playlist(playlist);
this.player.playlistUi({playOnSelect: true});
this.player.play = function() {
playCalled = true;
};
let sources;
this.player.src = (src) => {
if (src) {
sources = src;
}
return sources[0];
};
this.player.currentSrc = () => sources[0].src;
this.player.playlistMenu.items[1].trigger('tap');
// trigger a loadstart synchronously to simplify the test
this.player.trigger('loadstart');
assert.ok(this.player.playlistMenu.items[1].hasClass('vjs-selected'),
'selected the new item');
assert.ok(!this.player.playlistMenu.items[0].hasClass('vjs-selected'),
'deselected the old item');
assert.strictEqual(playCalled, true, 'play gets called if option is set');
});
QUnit.test('play should not get called by default upon selection of menu items ', function(assert) {
let playCalled = false;
this.player.playlist(playlist);
this.player.playlistUi();
this.player.play = function() {
playCalled = true;
};
let sources;
this.player.src = (src) => {
if (src) {
sources = src;
}
return sources[0];
};
this.player.currentSrc = () => sources[0].src;
this.player.playlistMenu.items[1].trigger('tap');
// trigger a loadstart synchronously to simplify the test
this.player.trigger('loadstart');
assert.strictEqual(playCalled, false, 'play should not get called by default');
});
QUnit.test('disposing the playlist menu nulls out the player\'s reference to it', function(assert) {
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist').length, 2, 'there are two playlist containers at the start');
this.player.playlist(playlist);
this.player.playlistUi();
this.player.playlistMenu.dispose();
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist').length, 1, 'only the unused playlist container is left');
assert.strictEqual(this.player.playlistMenu, null, 'the playlistMenu property is null');
});
QUnit.test('disposing the playlist menu removes playlist menu items', function(assert) {
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist').length, 2, 'there are two playlist containers at the start');
this.player.playlist(playlist);
this.player.playlistUi();
// Cache some references so we can refer to them after disposal.
const items = [].concat(this.player.playlistMenu.items);
this.player.playlistMenu.dispose();
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist').length, 1, 'only the unused playlist container is left');
assert.strictEqual(this.player.playlistMenu, null, 'the playlistMenu property is null');
items.forEach(i => {
assert.strictEqual(i.el_, null, `the item "${i.id_}" has been disposed`);
});
});
QUnit.test('disposing the player also disposes the playlist menu', function(assert) {
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist').length, 2, 'there are two playlist containers at the start');
this.player.playlist(playlist);
this.player.playlistUi();
this.player.dispose();
assert.strictEqual(this.fixture.querySelectorAll('.vjs-playlist').length, 1, 'only the unused playlist container is left');
assert.strictEqual(this.player.playlistMenu, null, 'the playlistMenu property is null');
});