From 949b19a6384502f3cb73fb3ec6d2bb25bb445468 Mon Sep 17 00:00:00 2001 From: Juan Pablo Franco Date: Tue, 11 Sep 2018 16:23:03 -0700 Subject: [PATCH 1/6] [PLAYER-4216] Added unit tests for TextTrackMap --- tests/setup.js | 10 + .../text_track/text-track-map-tests.js | 214 ++++++++++++++++++ tests/unit/main_html5/wrapper-api-tests.js | 9 - 3 files changed, 224 insertions(+), 9 deletions(-) create mode 100644 tests/unit/main_html5/text_track/text-track-map-tests.js diff --git a/tests/setup.js b/tests/setup.js index c038f2e8..9504a8f2 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -31,6 +31,16 @@ global._ = OO._; // In a browser environment, all of the properties of "window" (like navigator) are in the global scope: OO._.extend(global, window); + +OO.CONSTANTS = { + CLOSED_CAPTIONS: { + SHOWING: "showing", + HIDDEN: "hidden", + DISABLED: "disabled" + }, + SEEK_TO_END_LIMIT: 3 +}; + require.requireActual("../html5-common/js/utils/InitModules/InitOOHazmat.js"); jest.dontMock('./utils/mock_vtc.js'); diff --git a/tests/unit/main_html5/text_track/text-track-map-tests.js b/tests/unit/main_html5/text_track/text-track-map-tests.js new file mode 100644 index 00000000..51437872 --- /dev/null +++ b/tests/unit/main_html5/text_track/text-track-map-tests.js @@ -0,0 +1,214 @@ +import TextTrackMap from "../../../../src/main/js/text_track/text_track_map"; + +describe('TextTrackMap', function() { + let textTrackMap; + + beforeEach(function() { + textTrackMap = new TextTrackMap(); + }); + + it('should create a TextTrackMap instance', function() { + expect(textTrackMap).to.be.ok(); + expect(textTrackMap.textTracks).to.eql([]); + }); + + describe('addEntry', function() { + + it('should add an external track entry and auto-generate id', function() { + const isExternal = true; + textTrackMap.addEntry({ data: 'data' }, isExternal); + expect(textTrackMap.textTracks.length).to.be(1); + expect(textTrackMap.textTracks[0]).to.eql({ + id: 'VTT1', + data: 'data', + isExternal: true + }); + }); + + it('should add an internal track entry and auto-generate id', function() { + const isExternal = false; + textTrackMap.addEntry({ data: 'data' }, isExternal); + expect(textTrackMap.textTracks.length).to.be(1); + expect(textTrackMap.textTracks[0]).to.eql({ + id: 'CC1', + data: 'data', + isExternal: false + }); + }); + + it('should generate sequential ids', function() { + let isExternal = false; + textTrackMap.addEntry({ data: 'data1' }, isExternal); + textTrackMap.addEntry({ data: 'data2' }, isExternal); + isExternal = true; + textTrackMap.addEntry({ data: 'data3' }, true); + textTrackMap.addEntry({ data: 'data4' }, true); + expect(textTrackMap.textTracks.length).to.be(4); + expect(textTrackMap.textTracks).to.eql([{ + id: 'CC1', + data: 'data1', + isExternal: false + }, { + id: 'CC2', + data: 'data2', + isExternal: false + }, { + id: 'VTT1', + data: 'data3', + isExternal: true + }, { + id: 'VTT2', + data: 'data4', + isExternal: true + }]); + }); + + it('should override values of properties that are used internally', function() { + let isExternal = true; + textTrackMap.addEntry({ + id: 'noodles', + data: 'data1', + isExternal: 'maybe' + }, isExternal); + expect(textTrackMap.textTracks[0]).to.eql({ + id: 'VTT1', + data: 'data1', + isExternal: true + }); + }); + }); + + describe('findEntry', function() { + + it('should return undefined when there are no matches', function() { + expect(textTrackMap.findEntry()).to.be(undefined); + }); + + it('should return the entry that matches searchOptions', function() { + let isExternal = false; + textTrackMap.addEntry({ data: 'data1' }, isExternal); + textTrackMap.addEntry({ data: 'data2' }, isExternal); + expect(textTrackMap.findEntry({ data: 'data2' })).to.be(textTrackMap.textTracks[1]); + }); + + it('should not match an entry unless all searchOptions are matched', function() { + let isExternal = false; + textTrackMap.addEntry({ data: 'data1' }, isExternal); + textTrackMap.addEntry({ data: 'data2' }, isExternal); + expect(textTrackMap.findEntry({ data: 'data2', rando: 2 })).to.be(undefined); + }); + + it('should match all search options', function() { + let isExternal = false; + textTrackMap.addEntry({ data: 'data1' }, isExternal); + textTrackMap.addEntry({ data: 'data2', prop: 'prop1' }, isExternal); + textTrackMap.addEntry({ data: 'data3' }, isExternal); + expect(textTrackMap.findEntry({ + data: 'data2', + prop: 'prop1' + })).to.be(textTrackMap.textTracks[1]); + }); + }); + + describe('existsEntry', function() { + + it('should return true when entry exists', function() { + let isExternal = false; + textTrackMap.addEntry({ data: 'data1' }, isExternal); + expect(textTrackMap.existsEntry({ + id: 'CC1', + })).to.be(true); + }); + + it('should return false when entry doesn\'t exist', function() { + expect(textTrackMap.existsEntry({ + id: 'CC1', + })).to.be(false); + }); + }); + + describe('tryUpdateEntry', function() { + + it('should update entry if found', function() { + let isExternal = false; + textTrackMap.addEntry({ data: 'data1' }, isExternal); + textTrackMap.tryUpdateEntry({ + id: 'CC1' + }, { + data: 'updatedData', + newData: 'newData1' + }); + expect(textTrackMap.textTracks[0]).to.eql({ + id: 'CC1', + data: 'updatedData', + newData: 'newData1', + isExternal: false + }); + }); + }); + + describe('getInternalEntries', function() { + + it('should return all internal entries', function() { + textTrackMap.addEntry({ data: 'data1' }, false); + textTrackMap.addEntry({ data: 'data2' }, true); + textTrackMap.addEntry({ data: 'data3' }, false); + expect(textTrackMap.getInternalEntries()).to.eql([ + textTrackMap.textTracks[0], + textTrackMap.textTracks[2], + ]); + }); + }); + + describe('getExternalEntries', function() { + + it('should return all external entries', function() { + textTrackMap.addEntry({ data: 'data1' }, true); + textTrackMap.addEntry({ data: 'data2' }, false); + textTrackMap.addEntry({ data: 'data3' }, true); + expect(textTrackMap.getExternalEntries()).to.eql([ + textTrackMap.textTracks[0], + textTrackMap.textTracks[2], + ]); + }); + }); + + describe('areAllDisabled', function() { + + it('should return true when all tracks are disabled', function() { + textTrackMap.addEntry({ mode: 'disabled' }, true); + textTrackMap.addEntry({ mode: 'disabled' }, false); + textTrackMap.addEntry({ mode: 'disabled' }, true); + expect(textTrackMap.areAllDisabled()).to.be(true); + }); + + it('should return false when not all tracks are disabled', function() { + textTrackMap.addEntry({ mode: 'disabled' }, true); + textTrackMap.addEntry({ mode: 'showing' }, false); + textTrackMap.addEntry({ mode: 'disabled' }, true); + expect(textTrackMap.areAllDisabled()).to.be(false); + }); + }); + + describe('clear', function() { + + it('should clear all entries', function() { + textTrackMap.addEntry({ mode: 'disabled' }, true); + textTrackMap.addEntry({ mode: 'showing' }, false); + textTrackMap.addEntry({ mode: 'disabled' }, true); + expect(textTrackMap.textTracks.length).to.be(3); + textTrackMap.clear(); + expect(textTrackMap.textTracks.length).to.be(0); + }); + + it('should restart id sequence after clearing', function() { + textTrackMap.addEntry({ mode: 'disabled' }, true); + textTrackMap.addEntry({ mode: 'showing' }, false); + textTrackMap.clear(); + textTrackMap.addEntry({ mode: 'disabled' }, true); + textTrackMap.addEntry({ mode: 'showing' }, false); + expect(textTrackMap.textTracks[0].id).to.be('VTT1'); + expect(textTrackMap.textTracks[1].id).to.be('CC1'); + }); + }); +}); diff --git a/tests/unit/main_html5/wrapper-api-tests.js b/tests/unit/main_html5/wrapper-api-tests.js index d9d716a1..840e9574 100644 --- a/tests/unit/main_html5/wrapper-api-tests.js +++ b/tests/unit/main_html5/wrapper-api-tests.js @@ -10,15 +10,6 @@ describe('main_html5 wrapper tests', function () { // Setup OO.Video = { plugin: function(plugin) { pluginFactory = plugin; } }; - OO.CONSTANTS = { - CLOSED_CAPTIONS: { - SHOWING: "showing", - HIDDEN: "hidden", - DISABLED: "disabled" - }, - SEEK_TO_END_LIMIT: 3 - }; - var TRACK_CLASS = "track_cc"; var closedCaptions = { locale: { en: "English" }, From adc954a6f1c0a5c91aa050970ffdd45c6696746f Mon Sep 17 00:00:00 2001 From: Juan Pablo Franco Date: Tue, 11 Sep 2018 17:38:15 -0700 Subject: [PATCH 2/6] [PLAYER-4216] Added unit tests for TextTrackHelper --- tests/setup.js | 4 +- .../text_track/text-track-helper-tests.js | 231 ++++++++++++++++++ 2 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 tests/unit/main_html5/text_track/text-track-helper-tests.js diff --git a/tests/setup.js b/tests/setup.js index 9504a8f2..bcefd14a 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -95,9 +95,7 @@ jest.mock('../src/main/js/text_track/text_track_helper', () => { this.video.textTracks.push({ id: trackData.id, language: trackData.srclang, - // Note that the implementation initially sets the label to track id in - // order to be able to recognize the TextTrack object on the addtrack event - label: trackData.id, + label: trackData.label, kind: trackData.kind, mode: 'disabled' }); diff --git a/tests/unit/main_html5/text_track/text-track-helper-tests.js b/tests/unit/main_html5/text_track/text-track-helper-tests.js new file mode 100644 index 00000000..eda31871 --- /dev/null +++ b/tests/unit/main_html5/text_track/text-track-helper-tests.js @@ -0,0 +1,231 @@ +import TextTrackHelper from "../../../../src/main/js/text_track/text_track_helper"; +import TextTrackMap from "../../../../src/main/js/text_track/text_track_map"; + +describe('TextTrackHelper', function() { + let textTrackHelper, textTrackMap; + + const getDummyTrack = (overrides = {}) => { + return { + id: overrides.id || `id-${Date.now()}`, + kind: overrides.kind || 'kind', + label: overrides.label || 'label', + srclang: overrides.srclang || 'srclang', + src: overrides.src || 'src' + }; + }; + + const mapTracks = (isExternal = false) => { + for (let textTrack of textTrackHelper.video.textTracks) { + const hasBeenMaped = textTrackMap.existsEntry({ + textTrack: textTrack + }); + + if (!hasBeenMaped) { + textTrackMap.addEntry({ + label: textTrack.label, + language: textTrack.language, + mode: textTrack.mode, + textTrack: textTrack + }, isExternal); + } + } + }; + + beforeEach(function() { + textTrackHelper = new TextTrackHelper(document.createElement('video')); + textTrackMap = new TextTrackMap(); + }); + + it('should create a TextTrackHelper instance', function() { + expect(textTrackHelper).to.be.ok(); + }); + + describe('addTrack', function() { + + it('should create a track element with the specified attributes', function() { + textTrackHelper.addTrack({ + id: 'id', + kind: 'kind', + label: 'label', + srclang: 'srclang', + src: 'src' + }); + const trackElement = textTrackHelper.video.querySelector('track'); + expect(trackElement.getAttribute('id')).to.be('id'); + expect(trackElement.getAttribute('kind')).to.be('kind'); + expect(trackElement.getAttribute('label')).to.be('label'); + expect(trackElement.getAttribute('srclang')).to.be('srclang'); + expect(trackElement.getAttribute('src')).to.be('src'); + }); + }); + + describe('updateTrackLabel', function() { + + it('should update the label of the matching track element', function() { + const id = 'id1'; + textTrackHelper.addTrack(getDummyTrack({ + id: id, + label: 'label' + })); + const trackElement = textTrackHelper.video.querySelector(`#${id}`); + expect(trackElement.getAttribute('label')).to.be('label'); + textTrackHelper.updateTrackLabel(id, 'marlonrando'); + expect(trackElement.getAttribute('label')).to.be('marlonrando'); + }); + }); + + describe('forEach', function() { + + it('should execute a call back for each text track on the video element', function() { + let callCount = 0; + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.forEach(() => callCount++); + expect(callCount).to.be(4); + }); + }); + + describe('filter', function() { + + it('should return the elements that match the search criteria', function() { + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack({ id: 'match1', label: 'match' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'match2', label: 'match' })); + textTrackHelper.addTrack(getDummyTrack()); + const result = textTrackHelper.filter(track => track.label === 'match'); + expect(result.length).to.be(2); + expect(result[0].id).to.be('match1'); + expect(result[1].id).to.be('match2'); + }); + + it('should return an empty array if no matches are found', function() { + const result = textTrackHelper.filter(track => track.label === 'match'); + expect(result).to.eql([]); + }); + }); + + describe('find', function() { + + it('should return the first track to match the search criteria', function() { + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack({ id: 'match1', label: 'match' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'match2', label: 'match' })); + textTrackHelper.addTrack(getDummyTrack()); + const result = textTrackHelper.find(track => track.label === 'match'); + expect(result.id).to.be('match1'); + }); + + it('should return undefined if no matches are found', function() { + const result = textTrackHelper.find(track => track.label === 'match'); + expect(result).to.be(undefined); + }); + }); + + describe('findTrackByKey', function() { + + it('should return the first external track that matches the language', function() { + let isExternal; + textTrackHelper.addTrack(getDummyTrack({ id: 'CC1' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'CC2' })); + isExternal = false; + mapTracks(isExternal); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2', srclang: 'en' })); + isExternal = true; + mapTracks(isExternal); + const result = textTrackHelper.findTrackByKey('en', textTrackMap); + expect(result.id).to.be('VTT2'); + }); + + it('should return the first internal track that matches the track id', function() { + let isExternal; + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2' })); + isExternal = true; + mapTracks(isExternal); + textTrackHelper.addTrack(getDummyTrack({ id: 'CC1', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'CC2', srclang: 'en' })); + isExternal = false; + mapTracks(isExternal); + const result = textTrackHelper.findTrackByKey('CC2', textTrackMap); + expect(result.id).to.be('CC2'); + }); + + it('should ignore external tracks that match the track id', function() { + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2', srclang: 'en' })); + let isExternal = true; + mapTracks(isExternal); + const result = textTrackHelper.findTrackByKey('VTT2', textTrackMap); + expect(result).to.be(undefined); + }); + + it('should ignore internal tracks that match the language', function() { + let isExternal; + textTrackHelper.addTrack(getDummyTrack({ id: 'CC1', srclang: 'en' })); + isExternal = false; + mapTracks(isExternal); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1', srclang: 'en' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2', srclang: 'es' })); + isExternal = true; + mapTracks(isExternal); + const result = textTrackHelper.findTrackByKey('en', textTrackMap); + expect(result.id).to.be('VTT1'); + }); + }); + + describe('filterChangedTracks', function() { + + it('should return all tracks that have a mode that differs from the one stored in the TextTrackMap', function() { + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + mapTracks(); + textTrackHelper.video.textTracks[1].mode = 'showing'; + textTrackHelper.video.textTracks[2].mode = 'showing'; + const result = textTrackHelper.filterChangedTracks(textTrackMap); + expect(result.length).to.be(2); + expect(result).to.eql([ + textTrackHelper.video.textTracks[1], + textTrackHelper.video.textTracks[2] + ]); + }); + + it('should return an empty array if no tracks have been changed', function() { + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrack()); + mapTracks(); + const result = textTrackHelper.filterChangedTracks(textTrackMap); + expect(result.length).to.be(0); + expect(result).to.eql([]); + }); + }); + + describe('removeExternalTracks', function() { + + it('should remove all tracks marked as isExternal in the given TextTrackMap', function() { + let isExternal, trackElements; + textTrackHelper.addTrack(getDummyTrack({ id: 'CC1' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'CC2' })); + isExternal = false; + mapTracks(isExternal); + // Note: These need to match the ids generated by TextTrackMap + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1' })); + textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2' })); + isExternal = true; + mapTracks(isExternal); + trackElements = textTrackHelper.video.querySelectorAll('track'); + expect(trackElements.length).to.be(4); + textTrackHelper.removeExternalTracks(textTrackMap); + trackElements = textTrackHelper.video.querySelectorAll('track'); + expect(trackElements.length).to.be(2); + expect(trackElements[0].id).to.be('CC1'); + expect(trackElements[1].id).to.be('CC2'); + }); + }); +}); From f78b6b482863bac36ab8e8dfd0a36b51e3d22d3e Mon Sep 17 00:00:00 2001 From: Juan Pablo Franco Date: Wed, 12 Sep 2018 10:22:31 -0700 Subject: [PATCH 3/6] [PLAYER-4216] Renamed getDummyTrack function --- .../text_track/text-track-helper-tests.js | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/tests/unit/main_html5/text_track/text-track-helper-tests.js b/tests/unit/main_html5/text_track/text-track-helper-tests.js index eda31871..f7b2dab9 100644 --- a/tests/unit/main_html5/text_track/text-track-helper-tests.js +++ b/tests/unit/main_html5/text_track/text-track-helper-tests.js @@ -4,7 +4,7 @@ import TextTrackMap from "../../../../src/main/js/text_track/text_track_map"; describe('TextTrackHelper', function() { let textTrackHelper, textTrackMap; - const getDummyTrack = (overrides = {}) => { + const getDummyTrackData = (overrides = {}) => { return { id: overrides.id || `id-${Date.now()}`, kind: overrides.kind || 'kind', @@ -63,7 +63,7 @@ describe('TextTrackHelper', function() { it('should update the label of the matching track element', function() { const id = 'id1'; - textTrackHelper.addTrack(getDummyTrack({ + textTrackHelper.addTrack(getDummyTrackData({ id: id, label: 'label' })); @@ -78,10 +78,10 @@ describe('TextTrackHelper', function() { it('should execute a call back for each text track on the video element', function() { let callCount = 0; - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); textTrackHelper.forEach(() => callCount++); expect(callCount).to.be(4); }); @@ -90,10 +90,10 @@ describe('TextTrackHelper', function() { describe('filter', function() { it('should return the elements that match the search criteria', function() { - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack({ id: 'match1', label: 'match' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'match2', label: 'match' })); - textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData({ id: 'match1', label: 'match' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'match2', label: 'match' })); + textTrackHelper.addTrack(getDummyTrackData()); const result = textTrackHelper.filter(track => track.label === 'match'); expect(result.length).to.be(2); expect(result[0].id).to.be('match1'); @@ -109,10 +109,10 @@ describe('TextTrackHelper', function() { describe('find', function() { it('should return the first track to match the search criteria', function() { - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack({ id: 'match1', label: 'match' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'match2', label: 'match' })); - textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData({ id: 'match1', label: 'match' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'match2', label: 'match' })); + textTrackHelper.addTrack(getDummyTrackData()); const result = textTrackHelper.find(track => track.label === 'match'); expect(result.id).to.be('match1'); }); @@ -127,12 +127,12 @@ describe('TextTrackHelper', function() { it('should return the first external track that matches the language', function() { let isExternal; - textTrackHelper.addTrack(getDummyTrack({ id: 'CC1' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'CC2' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC1' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC2' })); isExternal = false; mapTracks(isExternal); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1', srclang: 'es' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2', srclang: 'en' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT1', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT2', srclang: 'en' })); isExternal = true; mapTracks(isExternal); const result = textTrackHelper.findTrackByKey('en', textTrackMap); @@ -141,12 +141,12 @@ describe('TextTrackHelper', function() { it('should return the first internal track that matches the track id', function() { let isExternal; - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT1' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT2' })); isExternal = true; mapTracks(isExternal); - textTrackHelper.addTrack(getDummyTrack({ id: 'CC1', srclang: 'es' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'CC2', srclang: 'en' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC1', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC2', srclang: 'en' })); isExternal = false; mapTracks(isExternal); const result = textTrackHelper.findTrackByKey('CC2', textTrackMap); @@ -154,8 +154,8 @@ describe('TextTrackHelper', function() { }); it('should ignore external tracks that match the track id', function() { - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1', srclang: 'es' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2', srclang: 'en' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT1', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT2', srclang: 'en' })); let isExternal = true; mapTracks(isExternal); const result = textTrackHelper.findTrackByKey('VTT2', textTrackMap); @@ -164,11 +164,11 @@ describe('TextTrackHelper', function() { it('should ignore internal tracks that match the language', function() { let isExternal; - textTrackHelper.addTrack(getDummyTrack({ id: 'CC1', srclang: 'en' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC1', srclang: 'en' })); isExternal = false; mapTracks(isExternal); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1', srclang: 'en' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2', srclang: 'es' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT1', srclang: 'en' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT2', srclang: 'es' })); isExternal = true; mapTracks(isExternal); const result = textTrackHelper.findTrackByKey('en', textTrackMap); @@ -179,10 +179,10 @@ describe('TextTrackHelper', function() { describe('filterChangedTracks', function() { it('should return all tracks that have a mode that differs from the one stored in the TextTrackMap', function() { - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); mapTracks(); textTrackHelper.video.textTracks[1].mode = 'showing'; textTrackHelper.video.textTracks[2].mode = 'showing'; @@ -195,10 +195,10 @@ describe('TextTrackHelper', function() { }); it('should return an empty array if no tracks have been changed', function() { - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); - textTrackHelper.addTrack(getDummyTrack()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); + textTrackHelper.addTrack(getDummyTrackData()); mapTracks(); const result = textTrackHelper.filterChangedTracks(textTrackMap); expect(result.length).to.be(0); @@ -210,13 +210,13 @@ describe('TextTrackHelper', function() { it('should remove all tracks marked as isExternal in the given TextTrackMap', function() { let isExternal, trackElements; - textTrackHelper.addTrack(getDummyTrack({ id: 'CC1' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'CC2' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC1' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'CC2' })); isExternal = false; mapTracks(isExternal); // Note: These need to match the ids generated by TextTrackMap - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT1' })); - textTrackHelper.addTrack(getDummyTrack({ id: 'VTT2' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT1' })); + textTrackHelper.addTrack(getDummyTrackData({ id: 'VTT2' })); isExternal = true; mapTracks(isExternal); trackElements = textTrackHelper.video.querySelectorAll('track'); From 49eb739761855a14ffca32d75c7179fa80afd9e1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Franco Date: Thu, 13 Sep 2018 16:44:26 -0700 Subject: [PATCH 4/6] [PLAYER-4216] Added unit tests --- tests/setup.js | 5 + tests/unit/main_html5/wrapper-api-tests.js | 215 ++++++++++++++++++- tests/unit/main_html5/wrapper-event-tests.js | 45 +++- 3 files changed, 252 insertions(+), 13 deletions(-) diff --git a/tests/setup.js b/tests/setup.js index bcefd14a..9a4c19e0 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -99,6 +99,11 @@ jest.mock('../src/main/js/text_track/text_track_helper', () => { kind: trackData.kind, mode: 'disabled' }); + // Trigger add track handler in order to fully simulate + // browser behavior + if (this.video.textTracks.onaddtrack) { + this.video.textTracks.onaddtrack(); + } } }); diff --git a/tests/unit/main_html5/wrapper-api-tests.js b/tests/unit/main_html5/wrapper-api-tests.js index 840e9574..b24b629a 100644 --- a/tests/unit/main_html5/wrapper-api-tests.js +++ b/tests/unit/main_html5/wrapper-api-tests.js @@ -1,6 +1,7 @@ /* * https://github.com/Automattic/expect.js */ +import TextTrackHelper from '../../../src/main/js/text_track/text_track_helper' const sinon = require('sinon'); @@ -10,7 +11,6 @@ describe('main_html5 wrapper tests', function () { // Setup OO.Video = { plugin: function(plugin) { pluginFactory = plugin; } }; - var TRACK_CLASS = "track_cc"; var closedCaptions = { locale: { en: "English" }, closed_captions_vtt: { @@ -113,7 +113,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions(language, closedCaptions, params); - element.textTracks.onaddtrack(); // Make sure tracks are actually there before we remove them expect(element.children.length > 0).to.be(true); expect(element.children[0].tagName).to.eql("TRACK"); @@ -125,11 +124,9 @@ describe('main_html5 wrapper tests', function () { wrapper.setVideoUrl("url"); $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); - element.textTracks.onaddtrack(); wrapper.setClosedCaptions(language, closedCaptions, params); wrapper.setVideoUrl("url"); $(element).triggerHandler("loadedmetadata"); - element.textTracks.onaddtrack(); expect(element.children.length > 0).to.be(true); expect(element.children[0].tagName).to.eql("TRACK"); }); @@ -1146,7 +1143,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions(language, closedCaptions, params); - element.textTracks.onaddtrack(); expect(element.children.length > 0).to.be(true); expect(element.children[0].tagName).to.eql("TRACK"); expect(element.children[0].getAttribute("kind")).to.eql("subtitles"); @@ -1186,7 +1182,6 @@ describe('main_html5 wrapper tests', function () { expect(element.textTracks[1].mode).to.eql(OO.CONSTANTS.CLOSED_CAPTIONS.SHOWING); wrapper.setClosedCaptions("en", closedCaptions, {mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN}); // this adds external captions - element.textTracks.onaddtrack(); expect(element.textTracks[0].mode).to.eql(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); expect(element.textTracks[1].mode).to.eql(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); expect(element.textTracks[2].mode).to.eql(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); @@ -1213,7 +1208,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions(language, closedCaptions, params); - element.textTracks.onaddtrack(); expect(element.children.length > 0).to.be(true); expect(element.children[0].tagName).to.eql("TRACK"); expect(element.textTracks[0].mode).to.eql(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); @@ -1346,4 +1340,211 @@ describe('main_html5 wrapper tests', function () { expect($(parentElement).has(":first-child").length).to.eql(0); }); + describe('Text Tracks', function() { + let ccData, ccParams; + + beforeEach(function() { + ccData = { + locale: { + en: 'English', + fr: 'Français' + }, + closed_captions_vtt: { + en: { + name: 'English', + url: 'http://ooyala.com/en' + }, + fr: { + name: 'Français', + url: 'http://ooyala.com/fr' + } + } + }; + ccParams = { + mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN + }; + }); + + it('should set crossorigin to "anonymous" when calling setClosedCaptions() but only if external captions are provided', function() { + expect(element.getAttribute('crossorigin')).to.be(null); + wrapper.setClosedCaptions('en', ccData, ccParams); + expect(element.getAttribute('crossorigin')).to.be('anonymous'); + element.removeAttribute('crossorigin'); + expect(element.getAttribute('crossorigin')).to.be(null); + wrapper.setClosedCaptions('en', {}, ccParams); + expect(element.getAttribute('crossorigin')).to.be(null); + }); + + it('should defer external track addition until "canplay" event is fired', function() { + const ccData2 = { + locale: { es: 'Español' }, + closed_captions_vtt: { + es: { + name: 'Español', + url: 'http://ooyala.com/es' + } + } + }; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + wrapper.setClosedCaptions('en', ccData, ccParams); + wrapper.setClosedCaptions('es', ccData2, ccParams); + expect(element.textTracks.length).to.be(0); + $(element).triggerHandler('canplay'); + expect(element.textTracks.length).to.be(3); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(element.textTracks[2].mode).to.be(ccParams.mode); + }); + + it('should clear current subtitle cue when a different language is selected', function() { + const spy = sinon.spy( + vtc.interface, 'notify' + ).withArgs( + vtc.interface.EVENTS.CLOSED_CAPTION_CUE_CHANGED, '' + ); + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData, ccParams); + wrapper.setClosedCaptions('fr', ccData, ccParams); + expect(spy.callCount).to.be(1); + expect(spy.args[0]).to.eql([vtc.interface.EVENTS.CLOSED_CAPTION_CUE_CHANGED, '']); + }); + + it('should disable all tracks except for the target track when a track is selected', function() { + const targetLanguage = 'en'; + element.textTracks = [ + { language: "", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN }, + { language: "", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN }, + { language: "", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN } + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + $(element).triggerHandler('canplay'); + for (let textTrack of element.textTracks) { + expect(textTrack.mode).to.not.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + } + wrapper.setClosedCaptions(targetLanguage, ccData, ccParams); + for (let textTrack of element.textTracks) { + if (textTrack.language === targetLanguage) { + expect(textTrack.mode).to.be(ccParams.mode); + } else { + expect(textTrack.mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + } + } + }); + + it('should set the target mode on a newly added target track after it is successfully added', function() { + ccParams.mode = OO.CONSTANTS.CLOSED_CAPTIONS.SHOWING; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + expect(element.textTracks.length).to.be(0); + wrapper.setClosedCaptions('en', ccData, ccParams); + expect(element.textTracks.length).to.be(2); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.SHOWING); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + }); + + it('should NOT add external VTT track with a source url that has already been added', function() { + const ccData1 = { + locale: { en: 'English' }, + closed_captions_vtt: { + en: { + name: 'English', + url: 'http://same.old.url' + } + } + }; + const ccData2 = { + locale: { es: 'Español' }, + closed_captions_vtt: { + es: { + name: 'Español', + url: 'http://same.old.url' + } + } + }; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData1, ccParams); + wrapper.setClosedCaptions('en', ccData2, ccParams); + expect(element.textTracks.length).to.be(1); + expect(element.textTracks[0].language).to.be('en'); + }); + + it('should generate sequential track ids for external tracks', function() { + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData, ccParams); + const trackElements = element.querySelectorAll('track'); + expect(trackElements.length).to.be(2); + expect(trackElements[0].id).to.be('VTT1'); + expect(trackElements[1].id).to.be('VTT2'); + }); + + it('should manually trigger addtrack event on Edge after adding external tracks', function() { + OO.isEdge = true; + const spy = sinon.spy(vtc.interface, 'notify'); + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData, ccParams); + const captionsFoundCount = spy.args.reduce((result, args) => ( + result += args[0] === vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING ? 1 : 0 + ), 0); + // Explanation: + // Two tracks were added. The mock implementation in setup.js will + // notify once per each track, plus there will be one additional notification + // per track due to this being Edge + expect(captionsFoundCount).to.be(4); + }); + + it('should add external tracks with id stored in label attribute and set actual label after TextTrack object is added', function() { + const spy = sinon.spy(TextTrackHelper.prototype, 'addTrack'); + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData, ccParams); + const expectedId = 'VTT1'; + const addTrackArgs = spy.args[0][0]; + const trackElements = element.querySelectorAll('track'); + expect(addTrackArgs.id).to.be(expectedId); + expect(addTrackArgs.label).to.be(expectedId); + expect(trackElements[0].id).to.be(expectedId); + expect(trackElements[0].label).to.be(ccData.closed_captions_vtt.en.name); + spy.restore(); + }); + + it('should set oncuechange handler on active tracks', function() { + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData, ccParams); + expect(typeof element.textTracks[0].oncuechange).to.be('function'); + expect(typeof element.textTracks[1].oncuechange).to.not.be('function'); + }); + + it('should give priority to external tracks that match the target language when enabling tracks', function() { + element.textTracks = [ + { language: "en", label: "Internal English", kind: "subtitles" }, + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('es', ccData, ccParams); + wrapper.setClosedCaptions('en', ccData, ccParams); + expect(element.textTracks[0].label).to.be('Internal English'); + expect(element.textTracks[0].language).to.be('en'); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(element.textTracks[1].language).to.be('en'); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + }); + }); + }); diff --git a/tests/unit/main_html5/wrapper-event-tests.js b/tests/unit/main_html5/wrapper-event-tests.js index 2a9d5a92..a867a2b9 100644 --- a/tests/unit/main_html5/wrapper-event-tests.js +++ b/tests/unit/main_html5/wrapper-event-tests.js @@ -446,7 +446,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions("en", closedCaptions, { mode: "hidden" }); // creates text tracks for external CCs - element.textTracks.onaddtrack(); expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING, { languages: ['en', 'CC1'], locale: { @@ -456,6 +455,23 @@ describe('main_html5 wrapper tests', function () { }]); }); + it('should ignore metadata tracks when notifying CAPTIONS_FOUND_ON_PLAYING', function(){ + element.textTracks = [ + { language: "en", label: "", kind: "subtitles" }, + { language: "es", label: "", kind: "metadata" } + ]; + wrapper.setVideoUrl("url", OO.VIDEO.ENCODING.HLS); + $(element).triggerHandler("loadedmetadata"); + $(element).triggerHandler("canplay"); + element.textTracks.onaddtrack(); + expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING, { + languages: ['CC1'], + locale: { + CC1: 'en', + } + }]); + }); + it('should notify CAPTIONS_FOUND_ON_PLAYING with multiple in-manifest/in-stream captions', function() { element.textTracks = [ { language: "", label: "", kind: "subtitles" }, @@ -466,7 +482,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions("en", closedCaptions, { mode: "hidden" }); - element.textTracks.onaddtrack(); expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING, { languages: ['en', 'CC1', 'CC2', 'CC3'], locale: { @@ -478,6 +493,28 @@ describe('main_html5 wrapper tests', function () { }]); }); + it('should notify CAPTIONS_FOUND_ON_PLAYING giving priority to external tracks that have the same language as in-manifest/in-stream tracks', function() { + element.textTracks = [ + { language: "en", label: "Internal EN", kind: "subtitles" }, + { language: "es", label: "Internal ES", kind: "subtitles" }, + { language: "", label: "", kind: "subtitles" } + ]; + wrapper.setVideoUrl("url", OO.VIDEO.ENCODING.HLS); + $(element).triggerHandler("loadedmetadata"); + $(element).triggerHandler("canplay"); + wrapper.setClosedCaptions("en", closedCaptions, { mode: "hidden" }); + // Only one english track is reported, internal tracks that don't collide + // with external are also reported + expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING, { + languages: ['en', 'CC2', 'CC3'], + locale: { + en: 'English', + CC2: 'Internal ES', + CC3: 'Captions (CC3)' + } + }]); + }); + it('should notify CAPTIONS_FOUND_ON_PLAYING with multiple in-manifest/in-stream captions using label and language metadata when available', function() { element.textTracks = [ { language: "", label: "", kind: "subtitles" }, @@ -488,7 +525,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions("en", closedCaptions, { mode: "hidden" }); - element.textTracks.onaddtrack(); expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING, { languages: ['en', 'CC1', 'CC2', 'CC3'], locale: { @@ -542,7 +578,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions("en", closedCaptions, { mode: "hidden" }); - element.textTracks.onaddtrack(); expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CAPTIONS_FOUND_ON_PLAYING, { languages: ['en', 'CC1', 'CC2', 'CC3'], locale: { @@ -566,7 +601,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions("en", closedCaptions, {mode: "hidden"}); - element.textTracks.onaddtrack(); element.textTracks[0].oncuechange(event); expect(vtc.notifyParameters).to.eql([vtc.interface.EVENTS.CLOSED_CAPTION_CUE_CHANGED, event.currentTarget.activeCues[0].text]); }); @@ -585,7 +619,6 @@ describe('main_html5 wrapper tests', function () { $(element).triggerHandler("loadedmetadata"); $(element).triggerHandler("canplay"); wrapper.setClosedCaptions("en", closedCaptions, {mode: "hidden"}); - element.textTracks.onaddtrack(); element.textTracks[0].oncuechange(event); expect(vtc.notifyParameters).to.eql([ vtc.interface.EVENTS.CLOSED_CAPTION_CUE_CHANGED, From 2cc651cf67e592c587d1c47e8269783356179a1e Mon Sep 17 00:00:00 2001 From: Juan Pablo Franco Date: Thu, 13 Sep 2018 17:38:59 -0700 Subject: [PATCH 5/6] [PLAYER-4216] Added unit tests for text track change detection --- tests/unit/main_html5/wrapper-api-tests.js | 109 +++++++++++++++++++++ tests/utils/mock_vtc.js | 3 +- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/tests/unit/main_html5/wrapper-api-tests.js b/tests/unit/main_html5/wrapper-api-tests.js index b24b629a..25a1667e 100644 --- a/tests/unit/main_html5/wrapper-api-tests.js +++ b/tests/unit/main_html5/wrapper-api-tests.js @@ -1545,6 +1545,115 @@ describe('main_html5 wrapper tests', function () { expect(element.textTracks[1].language).to.be('en'); expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); }); + + it('should ignore any changes that happen before canplay is fired', function() { + element.textTracks = [ + { language: "en", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "es", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + element.textTracks[0].mode = OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN; + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + element.textTracks.onchange(); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(vtc.notifyParameters[0]).to.not.be(vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE); + }); + + it('should ignore any changes to a single track that happen when not all tracks were previously disabled', function() { + element.textTracks = [ + { language: "en", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "es", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "fr", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + $(element).triggerHandler('canplay'); + // Enable first track before changing second + wrapper.setClosedCaptions('CC1', {}, ccParams); + element.textTracks[1].mode = OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN; + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + element.textTracks.onchange(); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(vtc.notifyParameters[0]).to.not.be(vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE); + }); + + it('should revert changed tracks to last known mode and notify CAPTIONS_LANGUAGE_CHANGE with key of enabled track', function() { + element.textTracks = [ + { language: "en", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN }, + { language: "es", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "fr", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + $(element).triggerHandler('canplay'); + element.textTracks[0].mode = OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED; + element.textTracks[1].mode = OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN; + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + element.textTracks.onchange(); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(vtc.notifyParameters).to.eql([ + vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE, + { language: 'CC2'} + ]); + }); + + it('should revert changed track to last known mode and notify CAPTIONS_LANGUAGE_CHANGE with a value of "none" when active track is disabled', function() { + element.textTracks = [ + { language: "en", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN }, + { language: "es", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "fr", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + $(element).triggerHandler('canplay'); + element.textTracks[0].mode = OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED; + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + element.textTracks.onchange(); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + expect(vtc.notifyParameters).to.eql([ + vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE, + { language: 'none'} + ]); + }); + + it('should notify CAPTIONS_LANGUAGE_CHANGE with language code of equivalent external track when internal track is enabled', function() { + element.textTracks = [ + { language: "en", label: "Internal EN", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "fr", label: "Internal FR", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + { language: "es", label: "Internal ES", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, + ]; + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + element.textTracks.onaddtrack(); + $(element).triggerHandler('canplay'); + // Add and enable external english track + wrapper.setClosedCaptions('en', ccData, ccParams); + // Disable external english track and enable internal french track + element.textTracks[3].mode = OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED; + element.textTracks[1].mode = OO.CONSTANTS.CLOSED_CAPTIONS.SHOWING; + // Make sure we're modifying the right tracks + expect(element.textTracks[3].id).to.be('VTT1'); + expect(element.textTracks[3].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(element.textTracks[1].label).to.be('Internal FR'); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.SHOWING); + element.textTracks.onchange(); + // Back to external english enabled, internal french disabled + expect(element.textTracks[3].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + // Expect notification with 'fr', which will match the EXTERNAL french track, + // instead of CC2, which would've matched the internal french track + expect(vtc.notifyParameters).to.eql([ + vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE, + { language: 'fr'} + ]); + }); }); }); diff --git a/tests/utils/mock_vtc.js b/tests/utils/mock_vtc.js index cb33cce4..d4206759 100644 --- a/tests/utils/mock_vtc.js +++ b/tests/utils/mock_vtc.js @@ -37,7 +37,8 @@ global.mock_vtc = function() { MUTED_PLAYBACK_SUCCEEDED: "mutedPlaybackSucceeded", MUTED_PLAYBACK_FAILED: "unmutedPlaybackFailed", MULTI_AUDIO_AVAILABLE: "multiAudioAvailable", - MULTI_AUDIO_CHANGED: "multiAudioChanged" + MULTI_AUDIO_CHANGED: "multiAudioChanged", + CAPTIONS_LANGUAGE_CHANGE: "captionsLanguageChange" }, notify: function(){ if (arguments.length > 0) { From b783da480ad37f52b61f52acdeb7c7d089896a2e Mon Sep 17 00:00:00 2001 From: Juan Pablo Franco Date: Fri, 14 Sep 2018 10:23:11 -0700 Subject: [PATCH 6/6] [PLAYER-4216] Added unit tests --- tests/unit/main_html5/wrapper-api-tests.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/unit/main_html5/wrapper-api-tests.js b/tests/unit/main_html5/wrapper-api-tests.js index 25a1667e..6f9bfcec 100644 --- a/tests/unit/main_html5/wrapper-api-tests.js +++ b/tests/unit/main_html5/wrapper-api-tests.js @@ -1580,7 +1580,7 @@ describe('main_html5 wrapper tests', function () { expect(vtc.notifyParameters[0]).to.not.be(vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE); }); - it('should revert changed tracks to last known mode and notify CAPTIONS_LANGUAGE_CHANGE with key of enabled track', function() { + it('should revert changed tracks to last known mode and notify CAPTIONS_LANGUAGE_CHANGE with id of enabled internal track', function() { element.textTracks = [ { language: "en", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN }, { language: "es", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED }, @@ -1603,6 +1603,24 @@ describe('main_html5 wrapper tests', function () { ]); }); + it('should revert changed tracks to last known mode and notify CAPTIONS_LANGUAGE_CHANGE with language of enabled external track', function() { + wrapper.setVideoUrl('url'); + $(element).triggerHandler('loadedmetadata'); + $(element).triggerHandler('canplay'); + wrapper.setClosedCaptions('en', ccData, ccParams); + element.textTracks[0].mode = OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED; + element.textTracks[1].mode = OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN; + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + element.textTracks.onchange(); + expect(element.textTracks[0].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN); + expect(element.textTracks[1].mode).to.be(OO.CONSTANTS.CLOSED_CAPTIONS.DISABLED); + expect(vtc.notifyParameters).to.eql([ + vtc.interface.EVENTS.CAPTIONS_LANGUAGE_CHANGE, + { language: 'fr'} + ]); + }); + it('should revert changed track to last known mode and notify CAPTIONS_LANGUAGE_CHANGE with a value of "none" when active track is disabled', function() { element.textTracks = [ { language: "en", label: "", kind: "subtitles", mode: OO.CONSTANTS.CLOSED_CAPTIONS.HIDDEN },