summaryrefslogtreecommitdiff
path: root/javascript/videojs/test/unit/tracks/text-track-controls.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'javascript/videojs/test/unit/tracks/text-track-controls.test.js')
-rw-r--r--javascript/videojs/test/unit/tracks/text-track-controls.test.js572
1 files changed, 572 insertions, 0 deletions
diff --git a/javascript/videojs/test/unit/tracks/text-track-controls.test.js b/javascript/videojs/test/unit/tracks/text-track-controls.test.js
new file mode 100644
index 0000000..e96ff94
--- /dev/null
+++ b/javascript/videojs/test/unit/tracks/text-track-controls.test.js
@@ -0,0 +1,572 @@
+/* eslint-env qunit */
+import TextTrackMenuItem from '../../../src/js/control-bar/text-track-controls/text-track-menu-item.js';
+import TestHelpers from '../test-helpers.js';
+import sinon from 'sinon';
+
+QUnit.module('Text Track Controls', {
+ beforeEach(assert) {
+ this.clock = sinon.useFakeTimers();
+ },
+ afterEach(assert) {
+ this.clock.restore();
+ }
+});
+
+const track = {
+ kind: 'captions',
+ label: 'test'
+};
+
+QUnit.test('should be displayed when text tracks list is not empty', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [track]
+ });
+
+ this.clock.tick(1000);
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-hidden'),
+ 'control is displayed'
+ );
+ assert.equal(player.textTracks().length, 1, 'textTracks contains one item');
+
+ player.dispose();
+});
+
+QUnit.test('should be displayed when a text track is added to an empty track list', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ player.addRemoteTextTrack(track, true);
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-hidden'),
+ 'control is displayed'
+ );
+ assert.equal(player.textTracks().length, 1, 'textTracks contains one item');
+
+ player.dispose();
+});
+
+QUnit.test('should not be displayed when text tracks list is empty', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ assert.ok(
+ player.controlBar.subsCapsButton.hasClass('vjs-hidden'),
+ 'control is not displayed'
+ );
+ assert.equal(player.textTracks().length, 0, 'textTracks is empty');
+
+ player.dispose();
+});
+
+QUnit.test('should not be displayed when last text track is removed', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [track]
+ });
+
+ player.removeRemoteTextTrack(player.textTracks()[0]);
+
+ assert.ok(
+ player.controlBar.subsCapsButton.hasClass('vjs-hidden'),
+ 'control is not displayed'
+ );
+ assert.equal(player.textTracks().length, 0, 'textTracks is empty');
+
+ player.dispose();
+});
+
+QUnit.test('menu should contain "Settings", "Off" and one track', function(assert) {
+ const player = TestHelpers.makePlayer({
+ language: 'en',
+ tracks: [track]
+ });
+
+ this.clock.tick(1000);
+
+ const menuItems = player.controlBar.subsCapsButton.items;
+
+ assert.equal(menuItems.length, 3, 'menu contains three items');
+ assert.equal(
+ menuItems[0].track.label,
+ 'captions settings',
+ 'menu contains "captions settings"'
+ );
+ assert.equal(menuItems[1].track.label, 'captions off', 'menu contains "captions off"');
+ assert.equal(menuItems[2].track.label, 'test', 'menu contains "test" track');
+
+ player.dispose();
+});
+
+QUnit.test('menu should contain "Settings", "Off", one captions and one subtitles track', function(assert) {
+ const player = TestHelpers.makePlayer({
+ language: 'en',
+ tracks: [track, {
+ kind: 'subtitles',
+ label: 'test subs'
+ }]
+ });
+
+ this.clock.tick(1000);
+
+ const menuItems = player.controlBar.subsCapsButton.items;
+
+ assert.equal(menuItems.length, 4, 'menu contains three items');
+ assert.equal(
+ menuItems[0].track.label,
+ 'captions settings',
+ 'menu contains "captions settings"'
+ );
+ assert.equal(menuItems[1].track.label, 'captions off', 'menu contains "captions off"');
+ assert.equal(menuItems[2].track.label, 'test', 'menu contains "test" track');
+
+ player.dispose();
+});
+
+QUnit.test('menu should update with addRemoteTextTrack', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [track]
+ });
+
+ this.clock.tick(1000);
+
+ player.addRemoteTextTrack(track, true);
+
+ assert.equal(
+ player.controlBar.subsCapsButton.items.length,
+ 4,
+ 'menu does contain added track'
+ );
+ assert.equal(player.textTracks().length, 2, 'textTracks contains two items');
+
+ player.dispose();
+});
+
+QUnit.test('menu should update with removeRemoteTextTrack', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [track, track]
+ });
+
+ this.clock.tick(1000);
+
+ player.removeRemoteTextTrack(player.textTracks()[0]);
+
+ assert.equal(
+ player.controlBar.subsCapsButton.items.length,
+ 3,
+ 'menu does not contain removed track'
+ );
+ assert.equal(player.textTracks().length, 1, 'textTracks contains one item');
+
+ player.dispose();
+});
+
+const descriptionstrack = {
+ kind: 'descriptions',
+ label: 'desc'
+};
+
+QUnit.test('descriptions should be displayed when text tracks list is not empty', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [descriptionstrack]
+ });
+
+ this.clock.tick(1000);
+
+ assert.ok(
+ !player.controlBar.descriptionsButton.hasClass('vjs-hidden'),
+ 'descriptions control is displayed'
+ );
+ assert.equal(player.textTracks().length, 1, 'textTracks contains one item');
+
+ player.dispose();
+});
+
+QUnit.test('descriptions should be displayed when a text track is added to an empty track list', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ player.addRemoteTextTrack(descriptionstrack, true);
+
+ assert.ok(
+ !player.controlBar.descriptionsButton.hasClass('vjs-hidden'),
+ 'control is displayed'
+ );
+ assert.equal(player.textTracks().length, 1, 'textTracks contains one item');
+
+ player.dispose();
+});
+
+QUnit.test('descriptions should not be displayed when text tracks list is empty', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ assert.ok(
+ player.controlBar.descriptionsButton.hasClass('vjs-hidden'),
+ 'control is not displayed'
+ );
+ assert.equal(player.textTracks().length, 0, 'textTracks is empty');
+
+ player.dispose();
+});
+
+QUnit.test('descriptions should not be displayed when last text track is removed', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [descriptionstrack]
+ });
+
+ player.removeRemoteTextTrack(player.textTracks()[0]);
+
+ assert.ok(
+ player.controlBar.descriptionsButton.hasClass('vjs-hidden'),
+ 'control is not displayed'
+ );
+ assert.equal(player.textTracks().length, 0, 'textTracks is empty');
+
+ player.dispose();
+});
+
+QUnit.test('descriptions menu should contain "Off" and one track', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [descriptionstrack]
+ });
+
+ this.clock.tick(1000);
+
+ const menuItems = player.controlBar.descriptionsButton.items;
+
+ assert.equal(menuItems.length, 2, 'descriptions menu contains two items');
+ assert.equal(
+ menuItems[0].track.label,
+ 'descriptions off',
+ 'menu contains "descriptions off"'
+ );
+ assert.equal(menuItems[1].track.label, 'desc', 'menu contains "desc" track');
+
+ player.dispose();
+});
+
+QUnit.test('enabling a captions track should disable the descriptions menu button', function(assert) {
+ assert.expect(14);
+
+ const player = TestHelpers.makePlayer({
+ tracks: [track, descriptionstrack]
+ });
+
+ this.clock.tick(1000);
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-hidden'),
+ 'captions control is displayed'
+ );
+ assert.ok(
+ !player.controlBar.descriptionsButton.hasClass('vjs-hidden'),
+ 'descriptions control is displayed'
+ );
+ assert.equal(player.textTracks().length, 2, 'textTracks contains two items');
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-disabled'),
+ 'captions control is NOT disabled'
+ );
+ assert.ok(
+ !player.controlBar.descriptionsButton.hasClass('vjs-disabled'),
+ 'descriptions control is NOT disabled'
+ );
+
+ for (let i = 0; i < player.textTracks().length; i++) {
+ if (player.textTracks()[i].kind === 'descriptions') {
+ player.textTracks()[i].mode = 'showing';
+ assert.ok(
+ player.textTracks()[i].kind === 'descriptions' &&
+ player.textTracks()[i].mode === 'showing',
+ 'descriptions mode set to showing'
+ );
+ }
+ }
+
+ this.clock.tick(1000);
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-disabled'),
+ 'captions control is NOT disabled'
+ );
+ assert.ok(
+ !player.controlBar.descriptionsButton.hasClass('vjs-disabled'),
+ 'descriptions control is NOT disabled'
+ );
+
+ for (let i = 0; i < player.textTracks().length; i++) {
+ if (player.textTracks()[i].kind === 'captions') {
+ player.textTracks()[i].mode = 'showing';
+ assert.ok(
+ player.textTracks()[i].kind === 'captions' &&
+ player.textTracks()[i].mode === 'showing',
+ 'captions mode set to showing'
+ );
+ }
+ }
+
+ this.clock.tick(1000);
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-disabled'),
+ 'captions control is NOT disabled'
+ );
+ assert.ok(
+ player.controlBar.descriptionsButton.hasClass('vjs-disabled'),
+ 'descriptions control IS disabled'
+ );
+
+ for (let i = 0; i < player.textTracks().length; i++) {
+ if (player.textTracks()[i].kind === 'captions') {
+ player.textTracks()[i].mode = 'disabled';
+ assert.ok(
+ player.textTracks()[i].kind === 'captions' &&
+ player.textTracks()[i].mode === 'disabled',
+ 'captions mode set to disabled'
+ );
+ }
+ }
+
+ this.clock.tick(1000);
+
+ assert.ok(
+ !player.controlBar.subsCapsButton.hasClass('vjs-disabled'),
+ 'captions control is NOT disabled'
+ );
+ assert.ok(
+ !player.controlBar.descriptionsButton.hasClass('vjs-disabled'),
+ 'descriptions control is NOT disabled'
+ );
+
+ player.dispose();
+});
+
+// This test tests a specific with iOS7 where
+// the TextTrackList doesn't report track mode changes.
+QUnit.test('menu items should polyfill mode change events', function(assert) {
+ const player = TestHelpers.makePlayer({});
+ let changes;
+
+ // emulate a TextTrackList that doesn't report track mode changes,
+ // like iOS7
+ player.textTracks().onchange = undefined;
+ const trackMenuItem = new TextTrackMenuItem(player, {
+ track
+ });
+
+ player.textTracks().on('change', function() {
+ changes++;
+ });
+ changes = 0;
+ trackMenuItem.trigger('tap');
+ assert.equal(changes, 1, 'taps trigger change events');
+
+ trackMenuItem.trigger('click');
+ assert.equal(changes, 2, 'clicks trigger change events');
+
+ player.dispose();
+ trackMenuItem.dispose();
+ player.textTracks().off('change');
+});
+
+const chaptersTrack = {
+ kind: 'chapters',
+ label: 'Test Chapters'
+};
+
+QUnit.test('chapters should not be displayed when text tracks list is empty', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ assert.ok(player.controlBar.chaptersButton.hasClass('vjs-hidden'), 'control is not displayed');
+ assert.equal(player.textTracks().length, 0, 'textTracks is empty');
+
+ player.dispose();
+});
+
+QUnit.test('chapters should not be displayed when there is chapters track but no cues', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [chaptersTrack]
+ });
+
+ this.clock.tick(1000);
+
+ assert.ok(player.controlBar.chaptersButton.hasClass('vjs-hidden'), 'chapters menu is not displayed');
+ assert.equal(player.textTracks().length, 1, 'textTracks contains one item');
+
+ player.dispose();
+});
+
+QUnit.test('chapters should be displayed when cues added to initial track and button updated', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [chaptersTrack]
+ });
+
+ this.clock.tick(1000);
+
+ const chapters = player.textTracks()[0];
+
+ chapters.addCue({
+ startTime: 0,
+ endTime: 2,
+ text: 'Chapter 1'
+ });
+ chapters.addCue({
+ startTime: 2,
+ endTime: 4,
+ text: 'Chapter 2'
+ });
+ assert.equal(chapters.cues.length, 2);
+
+ player.controlBar.chaptersButton.update();
+
+ assert.ok(!player.controlBar.chaptersButton.hasClass('vjs-hidden'), 'chapters menu is displayed');
+
+ const menuItems = player.controlBar.chaptersButton.items;
+
+ assert.equal(menuItems.length, 2, 'menu contains two item');
+
+ player.dispose();
+});
+
+QUnit.test('chapters should be displayed when a track and its cures added and button updated', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ this.clock.tick(1000);
+
+ const chapters = player.addTextTrack('chapters', 'Test Chapters', 'en');
+
+ chapters.addCue({
+ startTime: 0,
+ endTime: 2,
+ text: 'Chapter 1'
+ });
+ chapters.addCue({
+ startTime: 2,
+ endTime: 4,
+ text: 'Chapter 2'
+ });
+ assert.equal(chapters.cues.length, 2);
+
+ player.controlBar.chaptersButton.update();
+
+ assert.ok(!player.controlBar.chaptersButton.hasClass('vjs-hidden'), 'chapters menu is displayed');
+
+ const menuItems = player.controlBar.chaptersButton.items;
+
+ assert.equal(menuItems.length, 2, 'menu contains two item');
+
+ player.dispose();
+});
+
+QUnit.test('chapters menu should use track label as menu title', function(assert) {
+ const player = TestHelpers.makePlayer({
+ tracks: [chaptersTrack]
+ });
+
+ this.clock.tick(1000);
+
+ const chapters = player.textTracks()[0];
+
+ chapters.addCue({
+ startTime: 0,
+ endTime: 2,
+ text: 'Chapter 1'
+ });
+ chapters.addCue({
+ startTime: 2,
+ endTime: 4,
+ text: 'Chapter 2'
+ });
+ assert.equal(chapters.cues.length, 2);
+
+ player.controlBar.chaptersButton.update();
+
+ const menu = player.controlBar.chaptersButton.menu;
+ const titleEl = menu.contentEl().firstChild;
+ const menuTitle = titleEl.textContent || titleEl.innerText;
+
+ assert.equal(menuTitle, 'Test Chapters', 'menu gets track label as title');
+
+ player.dispose();
+});
+
+QUnit.test('chapters should be displayed when remote track added and load event fired', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ this.clock.tick(1000);
+
+ const chaptersEl = player.addRemoteTextTrack(chaptersTrack, true);
+
+ chaptersEl.track.addCue({
+ startTime: 0,
+ endTime: 2,
+ text: 'Chapter 1'
+ });
+ chaptersEl.track.addCue({
+ startTime: 2,
+ endTime: 4,
+ text: 'Chapter 2'
+ });
+
+ assert.equal(chaptersEl.track.cues.length, 2);
+
+ // Anywhere where we support using native text tracks, we can trigger a custom DOM event.
+ // On IE8 and other places where we have emulated tracks, either we cannot trigger custom
+ // DOM events (like IE8 with the custom DOM element) or we aren't using a DOM element at all.
+ // In those cases just trigger `load` directly on the chaptersEl object.
+ if (player.tech_.featuresNativeTextTracks) {
+ TestHelpers.triggerDomEvent(chaptersEl, 'load');
+ } else {
+ chaptersEl.trigger('load');
+ }
+
+ assert.ok(!player.controlBar.chaptersButton.hasClass('vjs-hidden'), 'chapters menu is displayed');
+
+ const menuItems = player.controlBar.chaptersButton.items;
+
+ assert.equal(menuItems.length, 2, 'menu contains two item');
+
+ player.dispose();
+});
+
+QUnit.test('chapters button should update selected menu item', function(assert) {
+ const player = TestHelpers.makePlayer();
+
+ this.clock.tick(1000);
+
+ const chaptersEl = player.addRemoteTextTrack(chaptersTrack, true);
+
+ chaptersEl.track.addCue({
+ startTime: 0,
+ endTime: 2,
+ text: 'Chapter 1'
+ });
+ chaptersEl.track.addCue({
+ startTime: 2,
+ endTime: 4,
+ text: 'Chapter 2'
+ });
+
+ assert.equal(chaptersEl.track.cues.length, 2);
+
+ if (player.tech_.featuresNativeTextTracks) {
+ TestHelpers.triggerDomEvent(chaptersEl, 'load');
+ } else {
+ chaptersEl.trigger('load');
+ }
+
+ const menuItems = player.controlBar.chaptersButton.items;
+
+ assert.ok(menuItems.find(i => i.isSelected_) === menuItems[0], 'item with startTime 0 selected on init');
+
+ player.currentTime(4);
+ chaptersEl.track.timeupdateHandler();
+
+ assert.ok(menuItems.find(i => i.isSelected_) === menuItems[1], 'second item selected on cuechange');
+
+ player.currentTime(1);
+ chaptersEl.track.timeupdateHandler();
+
+ assert.ok(menuItems.find(i => i.isSelected_) === menuItems[0], 'first item selected on cuechange');
+
+ player.dispose();
+});