diff options
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.js | 572 |
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(); +}); |
