1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-12 11:15:04 +02:00
video.js/test/unit/tracks/text-tracks.test.js

650 lines
18 KiB
JavaScript
Raw Normal View History

/* eslint-env qunit */
import ChaptersButton from '../../../src/js/control-bar/text-track-controls/chapters-button.js';
import DescriptionsButton from '../../../src/js/control-bar/text-track-controls/descriptions-button.js';
import SubtitlesButton from '../../../src/js/control-bar/text-track-controls/subtitles-button.js';
import CaptionsButton from '../../../src/js/control-bar/text-track-controls/captions-button.js';
import SubsCapsButton from '../../../src/js/control-bar/text-track-controls/subs-caps-button.js';
import TextTrack from '../../../src/js/tracks/text-track.js';
import TextTrackDisplay from '../../../src/js/tracks/text-track-display.js';
import Html5 from '../../../src/js/tech/html5.js';
import Tech from '../../../src/js/tech/tech.js';
Broke up Lib and Util into smaller libraries of functions Broke out bind, guid, and element data functions from Lib Separated out more dom functions in to dom.js Broke out URL functions into url.js Removed setLocalStorage since it wasn't being used Moved browser tests out of lib Moved log functions into their own file Removed trim() since it wasn't being used Moved formatTime into its own file Moved round into its own file and renamed roundFloat() Moved capitalize into its own file and renamed as toTitleCase() Moved createTimeRange into its own file Removed Lib.arr.forEach infavor of the native forEach Removed Lib.obj.create in favor of native Object.create (ES6-sham) Removed obj.each in favor of native Object.getOwnPropertyNames().forEach() Removed obj.merge and copy. Using lodash.assign instead. Replaced Lib.obj.isPlain with lodash.isPlainObject Removed Lib.obj.isArray in favor of the native Array.isArray Also removed the lib.js tests file as all tests have been moved or removed. Removed Lib.isEmpty in favor of !Object.getOwnPropertyNames().length Switched Util.mergeOptions and deepMerge to use new mergeOptions() Moved Lib.TEST_VID to Html5.TEST_VID Removed Lib references everywhere. Woo! Attempting to fix sourcemap test errors by setting grunt-browserify version Switched to object.assign from lodash.assign Removed unused 'inherits' dependency Reorganzied test files and added '.test' to file names Combined js/core.js and js/video.js Moved events.js into the utils directory
2015-05-04 01:12:38 +02:00
import * as browser from '../../../src/js/utils/browser.js';
import TestHelpers from '../test-helpers.js';
import document from 'global/document';
import sinon from 'sinon';
QUnit.module('Text Tracks', {
beforeEach(assert) {
this.clock = sinon.useFakeTimers();
},
afterEach(assert) {
this.clock.restore();
}
});
QUnit.test('should place title list item into ul', function(assert) {
const player = TestHelpers.makePlayer();
const chaptersButton = new ChaptersButton(player);
const menuContentElement = chaptersButton.el().getElementsByTagName('UL')[0];
const titleElement = menuContentElement.children[0];
assert.ok(titleElement.innerHTML === 'Chapters', 'title element placed in ul');
player.dispose();
chaptersButton.dispose();
});
QUnit.test('Player track methods call the tech', function(assert) {
const player = TestHelpers.makePlayer();
let calls = 0;
player.tech_.textTracks = function() {
calls++;
};
player.tech_.addTextTrack = function() {
calls++;
};
player.addTextTrack();
player.textTracks();
assert.equal(calls, 2, 'both textTrack and addTextTrack defer to the tech');
player.dispose();
});
QUnit.test('TextTrackDisplay initializes tracks on player ready', function(assert) {
let calls = 0;
const ttd = new TextTrackDisplay({
on() {},
addTextTracks() {
calls--;
},
getChild() {
calls--;
},
ready() {
calls++;
}
}, {});
assert.equal(calls, 1, 'only a player.ready call was made');
ttd.dispose();
});
QUnit.test('listen to remove and add track events in native text tracks', function(assert) {
const oldTestVid = Html5.TEST_VID;
const oldTextTracks = Html5.prototype.textTracks;
const events = {};
Html5.prototype.textTracks = function() {
return {
removeEventListener() {},
addEventListener(type, handler) {
events[type] = true;
}
};
};
Broke up Lib and Util into smaller libraries of functions Broke out bind, guid, and element data functions from Lib Separated out more dom functions in to dom.js Broke out URL functions into url.js Removed setLocalStorage since it wasn't being used Moved browser tests out of lib Moved log functions into their own file Removed trim() since it wasn't being used Moved formatTime into its own file Moved round into its own file and renamed roundFloat() Moved capitalize into its own file and renamed as toTitleCase() Moved createTimeRange into its own file Removed Lib.arr.forEach infavor of the native forEach Removed Lib.obj.create in favor of native Object.create (ES6-sham) Removed obj.each in favor of native Object.getOwnPropertyNames().forEach() Removed obj.merge and copy. Using lodash.assign instead. Replaced Lib.obj.isPlain with lodash.isPlainObject Removed Lib.obj.isArray in favor of the native Array.isArray Also removed the lib.js tests file as all tests have been moved or removed. Removed Lib.isEmpty in favor of !Object.getOwnPropertyNames().length Switched Util.mergeOptions and deepMerge to use new mergeOptions() Moved Lib.TEST_VID to Html5.TEST_VID Removed Lib references everywhere. Woo! Attempting to fix sourcemap test errors by setting grunt-browserify version Switched to object.assign from lodash.assign Removed unused 'inherits' dependency Reorganzied test files and added '.test' to file names Combined js/core.js and js/video.js Moved events.js into the utils directory
2015-05-04 01:12:38 +02:00
Html5.TEST_VID = {
textTracks: []
};
const player = {
// Function.prototype is a built-in no-op function.
controls() {},
ready() {},
options() {
return {};
},
addChild() {},
id() {},
el() {
return {
insertBefore() {},
appendChild() {}
};
}
};
player.player_ = player;
player.options_ = {};
const html = new Html5({});
assert.ok(events.removetrack, 'removetrack listener was added');
assert.ok(events.addtrack, 'addtrack listener was added');
Broke up Lib and Util into smaller libraries of functions Broke out bind, guid, and element data functions from Lib Separated out more dom functions in to dom.js Broke out URL functions into url.js Removed setLocalStorage since it wasn't being used Moved browser tests out of lib Moved log functions into their own file Removed trim() since it wasn't being used Moved formatTime into its own file Moved round into its own file and renamed roundFloat() Moved capitalize into its own file and renamed as toTitleCase() Moved createTimeRange into its own file Removed Lib.arr.forEach infavor of the native forEach Removed Lib.obj.create in favor of native Object.create (ES6-sham) Removed obj.each in favor of native Object.getOwnPropertyNames().forEach() Removed obj.merge and copy. Using lodash.assign instead. Replaced Lib.obj.isPlain with lodash.isPlainObject Removed Lib.obj.isArray in favor of the native Array.isArray Also removed the lib.js tests file as all tests have been moved or removed. Removed Lib.isEmpty in favor of !Object.getOwnPropertyNames().length Switched Util.mergeOptions and deepMerge to use new mergeOptions() Moved Lib.TEST_VID to Html5.TEST_VID Removed Lib references everywhere. Woo! Attempting to fix sourcemap test errors by setting grunt-browserify version Switched to object.assign from lodash.assign Removed unused 'inherits' dependency Reorganzied test files and added '.test' to file names Combined js/core.js and js/video.js Moved events.js into the utils directory
2015-05-04 01:12:38 +02:00
Html5.TEST_VID = oldTestVid;
Html5.prototype.textTracks = oldTextTracks;
html.dispose();
});
QUnit.test('update texttrack buttons on removetrack or addtrack', function(assert) {
let update = 0;
const events = {};
const oldCaptionsUpdate = CaptionsButton.prototype.update;
const oldSubsUpdate = SubtitlesButton.prototype.update;
const oldDescriptionsUpdate = DescriptionsButton.prototype.update;
const oldChaptersUpdate = ChaptersButton.prototype.update;
const oldSubsCapsUpdate = SubsCapsButton.prototype.update;
CaptionsButton.prototype.update = function() {
update++;
oldCaptionsUpdate.call(this);
};
SubtitlesButton.prototype.update = function() {
update++;
oldSubsUpdate.call(this);
};
DescriptionsButton.prototype.update = function() {
update++;
oldDescriptionsUpdate.call(this);
};
ChaptersButton.prototype.update = function() {
update++;
oldChaptersUpdate.call(this);
};
SubsCapsButton.prototype.update = function() {
update++;
oldSubsCapsUpdate.call(this);
};
const oldFeaturesNativeTextTracks = Tech.prototype.featuresNativeTextTracks;
const oldTextTracks = Tech.prototype.textTracks;
Tech.prototype.featuresNativeTextTracks = true;
Tech.prototype.textTracks = function() {
return {
length: 0,
addEventListener(type, handler) {
if (!events[type]) {
events[type] = [];
}
events[type].push(handler);
},
// Requrired in player.dispose()
removeEventListener() {}
};
};
const tag = document.createElement('video');
const track1 = document.createElement('track');
const track2 = document.createElement('track');
track1.kind = 'captions';
track1.label = 'en';
track1.language = 'English';
track1.src = '#en.vtt';
tag.appendChild(track1);
track2.kind = 'captions';
track2.label = 'es';
track2.language = 'Spanish';
track2.src = '#es.vtt';
tag.appendChild(track2);
const player = TestHelpers.makePlayer({
controlBar: {
captionsButton: true,
subtitlesButton: true
}
}, tag);
player.player_ = player;
assert.equal(update, 5, 'update was called on the five buttons during init');
for (let i = 0; i < events.removetrack.length; i++) {
events.removetrack[i]();
}
assert.equal(update, 10, 'update was called on the five buttons for remove track');
for (let i = 0; i < events.addtrack.length; i++) {
events.addtrack[i]();
}
assert.equal(update, 15, 'update was called on the five buttons for remove track');
Tech.prototype.textTracks = oldTextTracks;
Tech.prototype.featuresNativeTextTracks = oldFeaturesNativeTextTracks;
CaptionsButton.prototype.update = oldCaptionsUpdate;
SubtitlesButton.prototype.update = oldSubsUpdate;
ChaptersButton.prototype.update = oldChaptersUpdate;
SubsCapsButton.prototype.update = oldSubsCapsUpdate;
DescriptionsButton.prototype.update = oldDescriptionsUpdate;
player.dispose();
});
QUnit.test('emulated tracks are always used, except in safari', function(assert) {
const oldTestVid = Html5.TEST_VID;
const oldIsAnySafari = browser.IS_ANY_SAFARI;
Broke up Lib and Util into smaller libraries of functions Broke out bind, guid, and element data functions from Lib Separated out more dom functions in to dom.js Broke out URL functions into url.js Removed setLocalStorage since it wasn't being used Moved browser tests out of lib Moved log functions into their own file Removed trim() since it wasn't being used Moved formatTime into its own file Moved round into its own file and renamed roundFloat() Moved capitalize into its own file and renamed as toTitleCase() Moved createTimeRange into its own file Removed Lib.arr.forEach infavor of the native forEach Removed Lib.obj.create in favor of native Object.create (ES6-sham) Removed obj.each in favor of native Object.getOwnPropertyNames().forEach() Removed obj.merge and copy. Using lodash.assign instead. Replaced Lib.obj.isPlain with lodash.isPlainObject Removed Lib.obj.isArray in favor of the native Array.isArray Also removed the lib.js tests file as all tests have been moved or removed. Removed Lib.isEmpty in favor of !Object.getOwnPropertyNames().length Switched Util.mergeOptions and deepMerge to use new mergeOptions() Moved Lib.TEST_VID to Html5.TEST_VID Removed Lib references everywhere. Woo! Attempting to fix sourcemap test errors by setting grunt-browserify version Switched to object.assign from lodash.assign Removed unused 'inherits' dependency Reorganzied test files and added '.test' to file names Combined js/core.js and js/video.js Moved events.js into the utils directory
2015-05-04 01:12:38 +02:00
Html5.TEST_VID = {
textTracks: []
};
2019-08-30 20:56:41 +02:00
browser.stub_IS_ANY_SAFARI(false);
assert.ok(!Html5.supportsNativeTextTracks(), 'Html5 does not support native text tracks, in non-safari');
2019-08-30 20:56:41 +02:00
browser.stub_IS_ANY_SAFARI(true);
assert.ok(Html5.supportsNativeTextTracks(), 'Html5 does support native text tracks in safari');
Broke up Lib and Util into smaller libraries of functions Broke out bind, guid, and element data functions from Lib Separated out more dom functions in to dom.js Broke out URL functions into url.js Removed setLocalStorage since it wasn't being used Moved browser tests out of lib Moved log functions into their own file Removed trim() since it wasn't being used Moved formatTime into its own file Moved round into its own file and renamed roundFloat() Moved capitalize into its own file and renamed as toTitleCase() Moved createTimeRange into its own file Removed Lib.arr.forEach infavor of the native forEach Removed Lib.obj.create in favor of native Object.create (ES6-sham) Removed obj.each in favor of native Object.getOwnPropertyNames().forEach() Removed obj.merge and copy. Using lodash.assign instead. Replaced Lib.obj.isPlain with lodash.isPlainObject Removed Lib.obj.isArray in favor of the native Array.isArray Also removed the lib.js tests file as all tests have been moved or removed. Removed Lib.isEmpty in favor of !Object.getOwnPropertyNames().length Switched Util.mergeOptions and deepMerge to use new mergeOptions() Moved Lib.TEST_VID to Html5.TEST_VID Removed Lib references everywhere. Woo! Attempting to fix sourcemap test errors by setting grunt-browserify version Switched to object.assign from lodash.assign Removed unused 'inherits' dependency Reorganzied test files and added '.test' to file names Combined js/core.js and js/video.js Moved events.js into the utils directory
2015-05-04 01:12:38 +02:00
Html5.TEST_VID = oldTestVid;
2019-08-30 20:56:41 +02:00
browser.stub_IS_ANY_SAFARI(oldIsAnySafari);
});
QUnit.test('when switching techs, we should not get a new text track', function(assert) {
const player = TestHelpers.makePlayer();
player.loadTech_('TechFaker');
const firstTracks = player.textTracks();
player.loadTech_('TechFaker');
const secondTracks = player.textTracks();
assert.ok(firstTracks === secondTracks, 'the tracks are equal');
player.dispose();
});
if (Html5.supportsNativeTextTracks()) {
QUnit.test('listen to native remove and add track events in native text tracks', function(assert) {
const done = assert.async();
const el = document.createElement('video');
const html = new Html5({el});
const tt = el.textTracks;
const emulatedTt = html.textTracks();
const track = document.createElement('track');
el.appendChild(track);
const addtrack = function() {
assert.equal(emulatedTt.length, tt.length, 'we have matching tracks length');
assert.equal(emulatedTt.length, 1, 'we have one text track');
el.removeChild(track);
};
emulatedTt.one('addtrack', addtrack);
emulatedTt.one('removetrack', function() {
assert.equal(emulatedTt.length, tt.length, 'we have matching tracks length');
assert.equal(emulatedTt.length, 0, 'we have no more text tracks');
html.dispose();
done();
});
});
QUnit.test('should have removed tracks on dispose', function(assert) {
const done = assert.async();
const el = document.createElement('video');
const html = new Html5({el});
const tt = el.textTracks;
const emulatedTt = html.textTracks();
const track = document.createElement('track');
el.appendChild(track);
const addtrack = function() {
assert.equal(emulatedTt.length, tt.length, 'we have matching tracks length');
assert.equal(emulatedTt.length, 1, 'we have one text track');
emulatedTt.off('addtrack', addtrack);
html.dispose();
assert.equal(emulatedTt.length, tt.length, 'we have matching tracks length');
assert.equal(emulatedTt.length, 0, 'we have no more text tracks');
done();
};
emulatedTt.on('addtrack', addtrack);
});
}
QUnit.test('should check for text track changes when emulating text tracks', function(assert) {
const tech = new Tech();
let numTextTrackChanges = 0;
tech.on('texttrackchange', function() {
numTextTrackChanges++;
});
tech.emulateTextTracks();
assert.equal(numTextTrackChanges, 1, 'we got a texttrackchange event');
tech.dispose();
});
QUnit.test('removes cuechange event when text track is hidden for emulated tracks', function(assert) {
const player = TestHelpers.makePlayer();
const tt = new TextTrack({
tech: player.tech_,
mode: 'showing'
});
tt.addCue({
id: '1',
startTime: 2,
endTime: 5
});
player.tech_.textTracks().addTrack(tt);
let numTextTrackChanges = 0;
player.tech_.on('texttrackchange', function() {
numTextTrackChanges++;
});
tt.mode = 'showing';
this.clock.tick(1);
assert.equal(
numTextTrackChanges, 1,
'texttrackchange should be called once for mode change'
);
tt.mode = 'showing';
this.clock.tick(1);
assert.equal(
numTextTrackChanges, 2,
'texttrackchange should be called once for mode change'
);
player.tech_.currentTime = function() {
return 3;
};
player.tech_.trigger('timeupdate');
assert.equal(
numTextTrackChanges, 3,
'texttrackchange should be triggered once for the cuechange'
);
tt.mode = 'hidden';
this.clock.tick(1);
assert.equal(
numTextTrackChanges, 4,
'texttrackchange should be called once for the mode change'
);
player.tech_.currentTime = function() {
return 7;
};
player.tech_.trigger('timeupdate');
assert.equal(
numTextTrackChanges, 4,
'texttrackchange should be not be called since mode is hidden'
);
player.dispose();
});
QUnit.test('should return correct remote text track values', function(assert) {
const fixture = document.getElementById('qunit-fixture');
const html = `
<video id="example_1" class="video-js" autoplay preload="none">
<source src="http://google.com" type="video/mp4">
<source src="http://google.com" type="video/webm">
<track kind="captions" label="label">
</video>
`;
fixture.innerHTML += html;
const tag = document.getElementById('example_1');
const player = TestHelpers.makePlayer({}, tag);
this.clock.tick(10);
assert.equal(player.remoteTextTracks().length, 1, 'add text track via html');
assert.equal(player.remoteTextTrackEls().length, 1, 'add html track element via html');
const htmlTrackElement = player.addRemoteTextTrack({
kind: 'captions',
label: 'label'
}, true);
assert.equal(player.remoteTextTracks().length, 2, 'add text track via method');
assert.equal(player.remoteTextTrackEls().length, 2, 'add html track element via method');
player.removeRemoteTextTrack(htmlTrackElement.track);
assert.equal(player.remoteTextTracks().length, 1, 'remove text track via method');
assert.equal(
player.remoteTextTrackEls().length,
1,
'remove html track element via method'
);
player.dispose();
});
QUnit.test('should uniformly create html track element when adding text track', function(assert) {
const player = TestHelpers.makePlayer();
const track = {
kind: 'kind',
src: 'src',
language: 'language',
label: 'label',
default: 'default'
};
assert.equal(player.remoteTextTrackEls().length, 0, 'no html text tracks');
const htmlTrackElement = player.addRemoteTextTrack(track, true);
assert.equal(
htmlTrackElement.kind,
htmlTrackElement.track.kind,
'verify html track element kind'
);
assert.equal(
htmlTrackElement.src,
htmlTrackElement.track.src,
'verify html track element src'
);
assert.equal(
htmlTrackElement.srclang,
htmlTrackElement.track.language,
'verify html track element language'
);
assert.equal(
htmlTrackElement.label,
htmlTrackElement.track.label,
'verify html track element label'
);
assert.equal(
htmlTrackElement.default,
htmlTrackElement.track.default,
'verify html track element default'
);
assert.equal(player.remoteTextTrackEls().length, 1, 'html track element exist');
assert.equal(
player.remoteTextTrackEls().getTrackElementByTrack_(htmlTrackElement.track),
htmlTrackElement,
'verify same html track element'
);
player.dispose();
});
// disable in Firefox and IE because while the code works in practice,
// during the tests, somehow the text track object isn't ready and thus it won't
// allow us to change the mode of the track rendering the test non-functional.
if (!browser.IS_FIREFOX && !browser.IE_VERSION === 11) {
QUnit.test('remote text tracks change event should fire when using native text tracks', function(assert) {
const done = assert.async();
const player = TestHelpers.makePlayer({
techOrder: ['html5'],
html5: { nativeTextTracks: true }
});
player.remoteTextTracks().on('change', function(e) {
assert.ok(true, 'change event triggered');
player.dispose();
done();
});
const track = {
kind: 'kind',
src: 'src',
language: 'language',
label: 'label',
default: 'default'
};
player.addRemoteTextTrack(track, true);
});
}
QUnit.test('default text tracks should show by default', function(assert) {
const tag = TestHelpers.makeTag();
const capt = document.createElement('track');
capt.setAttribute('kind', 'captions');
capt.setAttribute('default', 'default');
tag.appendChild(capt);
const player = TestHelpers.makePlayer({
html5: {
nativeTextTracks: false
}
}, tag);
// native tracks are initialized after the player is ready
this.clock.tick(1);
const tracks = player.textTracks();
assert.equal(tracks[0].kind, 'captions', 'the captions track is present');
assert.equal(tracks[0].mode, 'showing', 'the captions track is showing');
player.dispose();
});
QUnit.test('default captions take precedence over default descriptions', function(assert) {
const tag = TestHelpers.makeTag();
const desc = document.createElement('track');
const capt = document.createElement('track');
desc.setAttribute('kind', 'descriptions');
desc.setAttribute('default', 'default');
capt.setAttribute('kind', 'captions');
capt.setAttribute('default', 'default');
tag.appendChild(desc);
tag.appendChild(capt);
const player = TestHelpers.makePlayer({
html5: {
nativeTextTracks: false
}
}, tag);
// native tracks are initialized after the player is ready
this.clock.tick(1);
const tracks = player.textTracks();
assert.equal(tracks[0].kind, 'descriptions', 'the descriptions track is first');
assert.equal(tracks[0].mode, 'disabled', 'the descriptions track is disabled');
assert.equal(tracks[1].kind, 'captions', 'the captions track is second');
assert.equal(tracks[1].mode, 'showing', 'the captions track is showing');
player.dispose();
});
QUnit.test('removeRemoteTextTrack should be able to take both a track and the response from addRemoteTextTrack', function(assert) {
const player = TestHelpers.makePlayer();
const track = {
kind: 'kind',
src: 'src',
language: 'language',
label: 'label',
default: 'default'
};
let htmlTrackElement = player.addRemoteTextTrack(track, true);
assert.equal(player.remoteTextTrackEls().length, 1, 'html track element exist');
player.removeRemoteTextTrack(htmlTrackElement);
assert.equal(
player.remoteTextTrackEls().length,
0,
'the track element was removed correctly'
);
htmlTrackElement = player.addRemoteTextTrack(track, true);
assert.equal(player.remoteTextTrackEls().length, 1, 'html track element exist');
player.removeRemoteTextTrack(htmlTrackElement.track);
assert.equal(
player.remoteTextTrackEls().length,
0,
'the track element was removed correctly'
);
player.dispose();
});
if (Html5.isSupported()) {
QUnit.test('auto remove tracks should not clean up tracks added while source is being added', function(assert) {
const player = TestHelpers.makePlayer({
techOrder: ['html5'],
html5: {
nativeTextTracks: false
}
});
const track = {
kind: 'kind',
src: 'src',
language: 'language',
label: 'label',
default: 'default'
};
player.src({src: 'example.mp4', type: 'video/mp4'});
player.addRemoteTextTrack(track, false);
this.clock.tick(1);
assert.equal(player.textTracks().length, 1, 'we have one text track');
player.dispose();
});
QUnit.test('auto remove tracks added right before a source change will be cleaned up', function(assert) {
const player = TestHelpers.makePlayer({
techOrder: ['html5'],
html5: {
nativeTextTracks: false
}
});
const track = {
kind: 'kind',
src: 'src',
language: 'language',
label: 'label',
default: 'default'
};
player.addRemoteTextTrack(track, false);
player.src({src: 'example.mp4', type: 'video/mp4'});
this.clock.tick(1);
assert.equal(player.textTracks().length, 0, 'we do not have any tracks left');
player.dispose();
});
}