Commit 08538e89 by Anton Stupak

Merge pull request #3128 from edx/anton/transcript-generation

Update captions logic on front-end.
parents b1a0cc9d 92cd4627
...@@ -201,7 +201,9 @@ def upload_file(_step, file_name): ...@@ -201,7 +201,9 @@ def upload_file(_step, file_name):
@step('I see "([^"]*)" text in the captions') @step('I see "([^"]*)" text in the captions')
def check_text_in_the_captions(_step, text): def check_text_in_the_captions(_step, text):
assert world.browser.is_text_present(text.strip(), 5) world.wait_for(lambda _: world.css_text('.subtitles'))
actual_text = world.css_text('.subtitles')
assert (text in actual_text)
@step('I see value "([^"]*)" in the field "([^"]*)"$') @step('I see value "([^"]*)" in the field "([^"]*)"$')
......
(function (require) {
require(
['video/00_async_process.js'],
function (AsyncProcess) {
var getArrayNthLength = function (n, multiplier) {
var result = [],
mul = multiplier || 1;
for (var i = 0; i < n; i++) {
result[i] = i * mul;
}
return result;
},
items = getArrayNthLength(1000);
describe('AsyncProcess', function () {
it ('Array is processed successfully', function () {
var processedArray,
expectedArray = getArrayNthLength(1000, 2),
process = function (item) {
return 2 * item;
};
runs(function () {
AsyncProcess.array(items, process).done(function (result) {
processedArray = result;
});
});
waitsFor(function () {
return processedArray;
}, 'Array processing takes too much time', WAIT_TIMEOUT);
runs(function () {
expect(processedArray).toEqual(expectedArray);
});
});
it ('If non-array is passed, error callback is called', function () {
var isError,
process = function () {};
runs(function () {
AsyncProcess.array('string', process).fail(function () {
isError = true;
});
});
waitsFor(function () {
return isError;
}, 'Error callback wasn\'t called', WAIT_TIMEOUT);
runs(function () {
expect(isError).toBeTruthy();
});
});
it ('If an empty array is passed, returns initial array', function () {
var processedArray,
process = function () {};
runs(function () {
AsyncProcess.array([], process).done(function (result) {
processedArray = result;
});
});
waitsFor(function () {
return processedArray;
}, 'Array processing takes too much time', WAIT_TIMEOUT);
runs(function () {
expect(processedArray).toEqual([]);
});
});
it ('If no process function passed, returns initial array', function () {
var processedArray;
runs(function () {
AsyncProcess.array(items).done(function (result) {
processedArray = result;
});
});
waitsFor(function () {
return processedArray;
}, 'Array processing takes too much time', WAIT_TIMEOUT);
runs(function () {
expect(processedArray).toEqual(items);
});
});
});
});
}(RequireJS.require));
(function (require) {
require(
['video/00_sjson.js'],
function (Sjson) {
describe('Sjson', function () {
var data = jasmine.stubbedCaption,
sjson;
beforeEach(function() {
sjson = new Sjson(data);
});
it ('returns captions', function () {
expect(sjson.getCaptions()).toEqual(data.text);
});
it ('returns start times', function () {
expect(sjson.getStartTimes()).toEqual(data.start);
});
it ('returns correct length', function () {
expect(sjson.getSize()).toEqual(data.text.length);
});
it('search returns a correct caption index', function () {
expect(sjson.search(0)).toEqual(-1);
expect(sjson.search(3120)).toEqual(1);
expect(sjson.search(6270)).toEqual(2);
expect(sjson.search(8490)).toEqual(2);
expect(sjson.search(21620)).toEqual(4);
expect(sjson.search(24920)).toEqual(5);
});
});
});
}(RequireJS.require));
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
}); });
expect($.ajaxWithPrefix.mostRecentCall.args[0].data) expect($.ajaxWithPrefix.mostRecentCall.args[0].data)
.toEqual({ .toEqual({
videoId: 'abcdefghijkl' videoId: 'cogebirgzzM'
}); });
}); });
}); });
...@@ -237,10 +237,17 @@ ...@@ -237,10 +237,17 @@
describe('when on a non touch-based device', function () { describe('when on a non touch-based device', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
}); });
waitsFor(function () {
return state.videoCaption.rendered;
}, 'Captions are not rendered', WAIT_TIMEOUT);
});
it('render the caption', function () { it('render the caption', function () {
runs(function () {
var captionsData; var captionsData;
captionsData = jasmine.stubbedCaption; captionsData = jasmine.stubbedCaption;
...@@ -255,16 +262,20 @@ ...@@ -255,16 +262,20 @@
expect($(link)).toHaveText(captionsData.text[index]); expect($(link)).toHaveText(captionsData.text[index]);
}); });
}); });
});
it('add a padding element to caption', function () { it('add a padding element to caption', function () {
runs(function () {
expect($('.subtitles li:first').hasClass('spacing')) expect($('.subtitles li:first').hasClass('spacing'))
.toBe(true); .toBe(true);
expect($('.subtitles li:last').hasClass('spacing')) expect($('.subtitles li:last').hasClass('spacing'))
.toBe(true); .toBe(true);
}); });
});
it('bind all the caption link', function () { it('bind all the caption link', function () {
runs(function () {
var handlerList = ['captionMouseOverOut', 'captionClick', var handlerList = ['captionMouseOverOut', 'captionClick',
'captionMouseDown', 'captionFocus', 'captionBlur', 'captionMouseDown', 'captionFocus', 'captionBlur',
'captionKeyDown' 'captionKeyDown'
...@@ -300,12 +311,22 @@ ...@@ -300,12 +311,22 @@
expect(state.videoCaption.captionKeyDown).toHaveBeenCalled(); expect(state.videoCaption.captionKeyDown).toHaveBeenCalled();
}); });
}); });
});
it('set rendered to true', function () { it('set rendered to true', function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
});
waitsFor(function () {
return state.videoCaption.rendered;
}, 'Captions are not rendered', WAIT_TIMEOUT);
runs(function () {
expect(state.videoCaption.rendered).toBeTruthy(); expect(state.videoCaption.rendered).toBeTruthy();
}); });
}); });
});
describe('when on a touch-based device', function () { describe('when on a touch-based device', function () {
beforeEach(function () { beforeEach(function () {
...@@ -345,52 +366,61 @@ ...@@ -345,52 +366,61 @@
beforeEach(function () { beforeEach(function () {
jasmine.Clock.useMock(); jasmine.Clock.useMock();
spyOn(window, 'clearTimeout'); spyOn(window, 'clearTimeout');
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
jasmine.Clock.tick(50);
}); });
describe('when cursor is outside of the caption box', function () { waitsFor(function () {
beforeEach(function () { return state.videoCaption.rendered;
$(window).trigger(jQuery.Event('mousemove')); }, 'Captions are not rendered', WAIT_TIMEOUT);
jasmine.Clock.tick(state.config.captionsFreezeTime);
}); });
describe('when cursor is outside of the caption box', function () {
it('does not set freezing timeout', function () { it('does not set freezing timeout', function () {
runs(function () {
expect(state.videoCaption.frozen).toBeFalsy(); expect(state.videoCaption.frozen).toBeFalsy();
}); });
}); });
});
describe('when cursor is in the caption box', function () { describe('when cursor is in the caption box', function () {
beforeEach(function () { beforeEach(function () {
spyOn(state.videoCaption, 'onMouseLeave'); spyOn(state.videoCaption, 'onMouseLeave');
runs(function () {
$(window).trigger(jQuery.Event('mousemove'));
jasmine.Clock.tick(state.config.captionsFreezeTime);
$('.subtitles').trigger(jQuery.Event('mouseenter')); $('.subtitles').trigger(jQuery.Event('mouseenter'));
jasmine.Clock.tick(state.config.captionsFreezeTime); jasmine.Clock.tick(state.config.captionsFreezeTime);
}); });
});
it('set the freezing timeout', function () { it('set the freezing timeout', function () {
runs(function () {
expect(state.videoCaption.frozen).not.toBeFalsy(); expect(state.videoCaption.frozen).not.toBeFalsy();
expect(state.videoCaption.onMouseLeave).toHaveBeenCalled(); expect(state.videoCaption.onMouseLeave).toHaveBeenCalled();
}); });
describe('when the cursor is moving', function () {
beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mousemove'));
}); });
describe('when the cursor is moving', function () {
it('reset the freezing timeout', function () { it('reset the freezing timeout', function () {
runs(function () {
$('.subtitles').trigger(jQuery.Event('mousemove'));
expect(window.clearTimeout).toHaveBeenCalled(); expect(window.clearTimeout).toHaveBeenCalled();
}); });
}); });
describe('when the mouse is scrolling', function () {
beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mousewheel'));
}); });
describe('when the mouse is scrolling', function () {
it('reset the freezing timeout', function () { it('reset the freezing timeout', function () {
runs(function () {
$('.subtitles').trigger(jQuery.Event('mousewheel'));
expect(window.clearTimeout).toHaveBeenCalled(); expect(window.clearTimeout).toHaveBeenCalled();
}); });
}); });
}); });
});
describe( describe(
'when cursor is moving out of the caption box', 'when cursor is moving out of the caption box',
...@@ -441,25 +471,6 @@ ...@@ -441,25 +471,6 @@
}); });
}); });
it('reRenderCaption', function () {
state = jasmine.initializePlayer();
var Caption = state.videoCaption,
li;
Caption.captions = ['test'];
Caption.start = [500];
spyOn(Caption, 'addPaddings');
Caption.reRenderCaption();
li = $('ol.subtitles li');
expect(Caption.addPaddings).toHaveBeenCalled();
expect(li.length).toBe(1);
expect(li).toHaveData('start', '500');
});
describe('fetchCaption', function () { describe('fetchCaption', function () {
var Caption, msg; var Caption, msg;
...@@ -467,7 +478,6 @@ ...@@ -467,7 +478,6 @@
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
Caption = state.videoCaption; Caption = state.videoCaption;
spyOn($, 'ajaxWithPrefix').andCallThrough(); spyOn($, 'ajaxWithPrefix').andCallThrough();
spyOn(Caption, 'reRenderCaption');
spyOn(Caption, 'renderCaption'); spyOn(Caption, 'renderCaption');
spyOn(Caption, 'bindHandlers'); spyOn(Caption, 'bindHandlers');
spyOn(Caption, 'updatePlayTime'); spyOn(Caption, 'updatePlayTime');
...@@ -511,7 +521,6 @@ ...@@ -511,7 +521,6 @@
expect(Caption.bindHandlers).toHaveBeenCalled(); expect(Caption.bindHandlers).toHaveBeenCalled();
expect(Caption.renderCaption).not.toHaveBeenCalled(); expect(Caption.renderCaption).not.toHaveBeenCalled();
expect(Caption.updatePlayTime).not.toHaveBeenCalled(); expect(Caption.updatePlayTime).not.toHaveBeenCalled();
expect(Caption.reRenderCaption).not.toHaveBeenCalled();
expect(Caption.loaded).toBeTruthy(); expect(Caption.loaded).toBeTruthy();
}); });
...@@ -527,7 +536,6 @@ ...@@ -527,7 +536,6 @@
expect(Caption.bindHandlers).not.toHaveBeenCalled(); expect(Caption.bindHandlers).not.toHaveBeenCalled();
expect(Caption.renderCaption).not.toHaveBeenCalled(); expect(Caption.renderCaption).not.toHaveBeenCalled();
expect(Caption.updatePlayTime).not.toHaveBeenCalled(); expect(Caption.updatePlayTime).not.toHaveBeenCalled();
expect(Caption.reRenderCaption).not.toHaveBeenCalled();
expect(Caption.loaded).toBeTruthy(); expect(Caption.loaded).toBeTruthy();
}); });
...@@ -539,9 +547,8 @@ ...@@ -539,9 +547,8 @@
expect($.ajaxWithPrefix).toHaveBeenCalled(); expect($.ajaxWithPrefix).toHaveBeenCalled();
expect(Caption.bindHandlers).not.toHaveBeenCalled(); expect(Caption.bindHandlers).not.toHaveBeenCalled();
expect(Caption.renderCaption).not.toHaveBeenCalled(); expect(Caption.renderCaption).toHaveBeenCalled();
expect(Caption.updatePlayTime).toHaveBeenCalled(); expect(Caption.updatePlayTime).toHaveBeenCalled();
expect(Caption.reRenderCaption).toHaveBeenCalled();
expect(Caption.loaded).toBeTruthy(); expect(Caption.loaded).toBeTruthy();
}); });
...@@ -553,19 +560,18 @@ ...@@ -553,19 +560,18 @@
expect(Caption.bindHandlers).toHaveBeenCalled(); expect(Caption.bindHandlers).toHaveBeenCalled();
expect(Caption.renderCaption).toHaveBeenCalled(); expect(Caption.renderCaption).toHaveBeenCalled();
expect(Caption.updatePlayTime).not.toHaveBeenCalled(); expect(Caption.updatePlayTime).not.toHaveBeenCalled();
expect(Caption.reRenderCaption).not.toHaveBeenCalled();
expect(Caption.loaded).toBeTruthy(); expect(Caption.loaded).toBeTruthy();
}); });
it('on success: re-rendered correct', function () { it('on success: re-rendered correct', function () {
Caption.loaded = true; Caption.loaded = true;
Caption.rendered = true;
Caption.fetchCaption(); Caption.fetchCaption();
expect($.ajaxWithPrefix).toHaveBeenCalled(); expect($.ajaxWithPrefix).toHaveBeenCalled();
expect(Caption.bindHandlers).not.toHaveBeenCalled(); expect(Caption.bindHandlers).not.toHaveBeenCalled();
expect(Caption.renderCaption).not.toHaveBeenCalled(); expect(Caption.renderCaption).toHaveBeenCalled();
expect(Caption.updatePlayTime).toHaveBeenCalled(); expect(Caption.updatePlayTime).toHaveBeenCalled();
expect(Caption.reRenderCaption).toHaveBeenCalled();
expect(Caption.loaded).toBeTruthy(); expect(Caption.loaded).toBeTruthy();
}); });
...@@ -680,27 +686,23 @@ ...@@ -680,27 +686,23 @@
}); });
}); });
describe('search', function () {
it('return a correct caption index', function () {
state = jasmine.initializePlayer();
expect(state.videoCaption.search(0)).toEqual(-1);
expect(state.videoCaption.search(3120)).toEqual(1);
expect(state.videoCaption.search(6270)).toEqual(2);
expect(state.videoCaption.search(8490)).toEqual(2);
expect(state.videoCaption.search(21620)).toEqual(4);
expect(state.videoCaption.search(24920)).toEqual(5);
});
});
describe('play', function () { describe('play', function () {
describe('when the caption was not rendered', function () { describe('when the caption was not rendered', function () {
beforeEach(function () { beforeEach(function () {
window.onTouchBasedDevice.andReturn(['iPad']); window.onTouchBasedDevice.andReturn(['iPad']);
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
state.videoCaption.play(); state.videoCaption.play();
}); });
waitsFor(function () {
return state.videoCaption.rendered;
}, 'Captions are not rendered', WAIT_TIMEOUT);
});
it('render the caption', function () { it('render the caption', function () {
runs(function () {
var captionsData; var captionsData;
captionsData = jasmine.stubbedCaption; captionsData = jasmine.stubbedCaption;
...@@ -716,20 +718,28 @@ ...@@ -716,20 +718,28 @@
}); });
}); });
});
it('add a padding element to caption', function () { it('add a padding element to caption', function () {
runs(function () {
expect($('.subtitles li:first')).toBe('.spacing'); expect($('.subtitles li:first')).toBe('.spacing');
expect($('.subtitles li:last')).toBe('.spacing'); expect($('.subtitles li:last')).toBe('.spacing');
}); });
});
it('set rendered to true', function () { it('set rendered to true', function () {
runs(function () {
expect(state.videoCaption.rendered).toBeTruthy(); expect(state.videoCaption.rendered).toBeTruthy();
}); });
});
it('set playing to true', function () { it('set playing to true', function () {
runs(function () {
expect(state.videoCaption.playing).toBeTruthy(); expect(state.videoCaption.playing).toBeTruthy();
}); });
}); });
}); });
});
describe('pause', function () { describe('pause', function () {
beforeEach(function () { beforeEach(function () {
...@@ -745,82 +755,117 @@ ...@@ -745,82 +755,117 @@
describe('updatePlayTime', function () { describe('updatePlayTime', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
}); });
describe('when the video speed is 1.0x', function () { waitsFor(function () {
beforeEach(function () { return state.videoCaption.rendered;
state.videoSpeedControl.currentSpeed = '1.0'; }, 'Captions are not rendered', WAIT_TIMEOUT);
state.videoCaption.updatePlayTime(25.000);
}); });
describe('when the video speed is 1.0x', function () {
it('search the caption based on time', function () { it('search the caption based on time', function () {
runs(function () {
state.videoCaption.updatePlayTime(25.000);
expect(state.videoCaption.currentIndex).toEqual(5); expect(state.videoCaption.currentIndex).toEqual(5);
});
});
describe('when the video speed is not 1.0x', function () { // Flash mode
beforeEach(function () { spyOn(state, 'isFlashMode').andReturn(true);
state.videoSpeedControl.currentSpeed = '0.75'; state.speed = '1.0';
state.videoCaption.updatePlayTime(25.000); state.videoCaption.updatePlayTime(25.000);
expect(state.videoCaption.currentIndex).toEqual(5);
});
});
}); });
describe('when the video speed is not 1.0x', function () {
it('search the caption based on 1.0x speed', function () { it('search the caption based on 1.0x speed', function () {
runs(function () {
state.videoCaption.updatePlayTime(25.000);
expect(state.videoCaption.currentIndex).toEqual(5); expect(state.videoCaption.currentIndex).toEqual(5);
// Flash mode
state.speed = '2.0';
spyOn(state, 'isFlashMode').andReturn(true);
state.videoCaption.updatePlayTime(25.000);
expect(state.videoCaption.currentIndex).toEqual(9);
state.speed = '0.75';
state.videoCaption.updatePlayTime(25.000);
expect(state.videoCaption.currentIndex).toEqual(3);
});
}); });
}); });
describe('when the index is not the same', function () { describe('when the index is not the same', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state.videoCaption.currentIndex = 1; state.videoCaption.currentIndex = 1;
$('.subtitles li[data-index=5]').addClass('current'); $('.subtitles li[data-index=5]').addClass('current');
state.videoCaption.updatePlayTime(25.000); state.videoCaption.updatePlayTime(25.000);
}); });
});
it('deactivate the previous caption', function () { it('deactivate the previous caption', function () {
runs(function () {
expect($('.subtitles li[data-index=1]')) expect($('.subtitles li[data-index=1]'))
.not.toHaveClass('current'); .not.toHaveClass('current');
}); });
});
it('activate new caption', function () { it('activate new caption', function () {
runs(function () {
expect($('.subtitles li[data-index=5]')) expect($('.subtitles li[data-index=5]'))
.toHaveClass('current'); .toHaveClass('current');
}); });
});
it('save new index', function () { it('save new index', function () {
runs(function () {
expect(state.videoCaption.currentIndex).toEqual(5); expect(state.videoCaption.currentIndex).toEqual(5);
}); });
});
// Disabled 11/25/13 due to flakiness in master it('scroll caption to new position', function () {
xit('scroll caption to new position', function () { runs(function () {
expect($.fn.scrollTo).toHaveBeenCalled(); expect($.fn.scrollTo).toHaveBeenCalled();
}); });
}); });
});
describe('when the index is the same', function () { describe('when the index is the same', function () {
beforeEach(function () { it('does not change current subtitle', function () {
runs(function () {
state.videoCaption.currentIndex = 1; state.videoCaption.currentIndex = 1;
$('.subtitles li[data-index=3]').addClass('current'); $('.subtitles li[data-index=3]').addClass('current');
state.videoCaption.updatePlayTime(15.000); state.videoCaption.updatePlayTime(15.000);
});
it('does not change current subtitle', function () {
expect($('.subtitles li[data-index=3]')) expect($('.subtitles li[data-index=3]'))
.toHaveClass('current'); .toHaveClass('current');
}); });
}); });
}); });
});
describe('resize', function () { describe('resize', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
});
waitsFor(function () {
return state.videoCaption.rendered;
}, 'Captions are not rendered', WAIT_TIMEOUT);
runs(function () {
videoControl = state.videoControl; videoControl = state.videoControl;
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
state.videoCaption.resize(); state.videoCaption.resize();
}); });
});
describe('set the height of caption container', function () { describe('set the height of caption container', function () {
it('when CC button is enabled', function () { it('when CC button is enabled', function () {
runs(function () {
var realHeight = parseInt( var realHeight = parseInt(
$('.subtitles').css('maxHeight'), 10 $('.subtitles').css('maxHeight'), 10
), ),
...@@ -830,8 +875,10 @@ ...@@ -830,8 +875,10 @@
// environments: Linux * Mac * FF * Chrome // environments: Linux * Mac * FF * Chrome
expect(realHeight).toBeCloseTo(shouldBeHeight, 2); expect(realHeight).toBeCloseTo(shouldBeHeight, 2);
}); });
});
it('when CC button is disabled ', function () { it('when CC button is disabled ', function () {
runs(function () {
var realHeight, videoWrapperHeight, progressSliderHeight, var realHeight, videoWrapperHeight, progressSliderHeight,
controlHeight, shouldBeHeight; controlHeight, shouldBeHeight;
...@@ -851,8 +898,10 @@ ...@@ -851,8 +898,10 @@
expect(realHeight).toBe(shouldBeHeight); expect(realHeight).toBe(shouldBeHeight);
}); });
}); });
});
it('set the height of caption spacing', function () { it('set the height of caption spacing', function () {
runs(function () {
var firstSpacing, lastSpacing; var firstSpacing, lastSpacing;
firstSpacing = Math.abs(parseInt( firstSpacing = Math.abs(parseInt(
...@@ -867,100 +916,111 @@ ...@@ -867,100 +916,111 @@
expect(lastSpacing - state.videoCaption.bottomSpacingHeight()) expect(lastSpacing - state.videoCaption.bottomSpacingHeight())
.toBeLessThan(1); .toBeLessThan(1);
}); });
});
it('scroll caption to new position', function () { it('scroll caption to new position', function () {
runs(function () {
expect($.fn.scrollTo).toHaveBeenCalled(); expect($.fn.scrollTo).toHaveBeenCalled();
}); });
}); });
});
// Disabled 11/25/13 due to flakiness in master
xdescribe('scrollCaption', function () { xdescribe('scrollCaption', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
}); });
waitsFor(function () {
return state.videoCaption.rendered;
}, 'Captions are not rendered', WAIT_TIMEOUT);
});
describe('when frozen', function () { describe('when frozen', function () {
beforeEach(function () { it('does not scroll the caption', function () {
runs(function () {
state.videoCaption.frozen = true; state.videoCaption.frozen = true;
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
state.videoCaption.scrollCaption(); state.videoCaption.scrollCaption();
});
it('does not scroll the caption', function () {
expect($.fn.scrollTo).not.toHaveBeenCalled(); expect($.fn.scrollTo).not.toHaveBeenCalled();
}); });
}); });
});
describe('when not frozen', function () { describe('when not frozen', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state.videoCaption.frozen = false; state.videoCaption.frozen = false;
}); });
describe('when there is no current caption', function () {
beforeEach(function () {
state.videoCaption.scrollCaption();
}); });
describe('when there is no current caption', function () {
it('does not scroll the caption', function () { it('does not scroll the caption', function () {
runs(function () {
state.videoCaption.scrollCaption();
expect($.fn.scrollTo).not.toHaveBeenCalled(); expect($.fn.scrollTo).not.toHaveBeenCalled();
}); });
}); });
});
describe('when there is a current caption', function () { describe('when there is a current caption', function () {
beforeEach(function () { it('scroll to current caption', function () {
runs(function () {
$('.subtitles li[data-index=1]').addClass('current'); $('.subtitles li[data-index=1]').addClass('current');
state.videoCaption.scrollCaption(); state.videoCaption.scrollCaption();
});
it('scroll to current caption', function () {
expect($.fn.scrollTo).toHaveBeenCalled(); expect($.fn.scrollTo).toHaveBeenCalled();
}); });
}); });
}); });
}); });
});
// Disabled 10/9/13 due to flakiness in master
xdescribe('seekPlayer', function () { xdescribe('seekPlayer', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
}); });
describe('when the video speed is 1.0x', function () { waitsFor(function () {
beforeEach(function () { var duration = state.videoPlayer.duration(),
state.videoSpeedControl.currentSpeed = '1.0'; isRendered = state.videoCaption.rendered;
$('.subtitles li[data-start="14910"]').trigger('click');
return isRendered && duration;
}, 'Captions are not rendered', WAIT_TIMEOUT);
}); });
describe('when the video speed is 1.0x', function () {
it('trigger seek event with the correct time', function () { it('trigger seek event with the correct time', function () {
runs(function () {
state.videoSpeedControl.currentSpeed = '1.0';
$('.subtitles li[data-start="14910"]').trigger('click');
expect(state.videoPlayer.currentTime).toEqual(14.91); expect(state.videoPlayer.currentTime).toEqual(14.91);
}); });
}); });
});
describe('when the video speed is not 1.0x', function () { describe('when the video speed is not 1.0x', function () {
beforeEach(function () { it('trigger seek event with the correct time', function () {
runs(function () {
state.videoSpeedControl.currentSpeed = '0.75'; state.videoSpeedControl.currentSpeed = '0.75';
$('.subtitles li[data-start="14910"]').trigger('click'); $('.subtitles li[data-start="14910"]').trigger('click');
});
it('trigger seek event with the correct time', function () {
expect(state.videoPlayer.currentTime).toEqual(14.91); expect(state.videoPlayer.currentTime).toEqual(14.91);
}); });
}); });
});
describe('when the player type is Flash at speed 0.75x', describe('when the player type is Flash at speed 0.75x',
function () { function () {
it('trigger seek event with the correct time', function () {
beforeEach(function () { runs(function () {
state.videoSpeedControl.currentSpeed = '0.75'; state.videoSpeedControl.currentSpeed = '0.75';
state.currentPlayerMode = 'flash'; state.currentPlayerMode = 'flash';
$('.subtitles li[data-start="14910"]').trigger('click'); $('.subtitles li[data-start="14910"]').trigger('click');
});
it('trigger seek event with the correct time', function () {
expect(state.videoPlayer.currentTime).toEqual(15); expect(state.videoPlayer.currentTime).toEqual(15);
}); });
}); });
}); });
});
describe('toggle', function () { describe('toggle', function () {
beforeEach(function () { beforeEach(function () {
...@@ -998,7 +1058,6 @@ ...@@ -998,7 +1058,6 @@
beforeEach(function () { beforeEach(function () {
state.el.addClass('closed'); state.el.addClass('closed');
state.videoCaption.toggle(jQuery.Event('click')); state.videoCaption.toggle(jQuery.Event('click'));
jasmine.Clock.useMock(); jasmine.Clock.useMock();
}); });
...@@ -1039,43 +1098,61 @@ ...@@ -1039,43 +1098,61 @@
describe('caption accessibility', function () { describe('caption accessibility', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
}); });
waitsFor(function () {
return state.videoCaption.rendered;
}, 'Captions are not rendered', WAIT_TIMEOUT);
});
describe('when getting focus through TAB key', function () { describe('when getting focus through TAB key', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state.videoCaption.isMouseFocus = false; state.videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]').trigger( $('.subtitles li[data-index=0]').trigger(
jQuery.Event('focus') jQuery.Event('focus')
); );
}); });
});
it('shows an outline around the caption', function () { it('shows an outline around the caption', function () {
runs(function () {
expect($('.subtitles li[data-index=0]')) expect($('.subtitles li[data-index=0]'))
.toHaveClass('focused'); .toHaveClass('focused');
}); });
});
it('has automatic scrolling disabled', function () { it('has automatic scrolling disabled', function () {
runs(function () {
expect(state.videoCaption.autoScrolling).toBe(false); expect(state.videoCaption.autoScrolling).toBe(false);
}); });
}); });
});
describe('when loosing focus through TAB key', function () { describe('when loosing focus through TAB key', function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
$('.subtitles li[data-index=0]').trigger( $('.subtitles li[data-index=0]').trigger(
jQuery.Event('blur') jQuery.Event('blur')
); );
}); });
});
it('does not show an outline around the caption', function () { it('does not show an outline around the caption', function () {
runs(function () {
expect($('.subtitles li[data-index=0]')) expect($('.subtitles li[data-index=0]'))
.not.toHaveClass('focused'); .not.toHaveClass('focused');
}); });
});
it('has automatic scrolling enabled', function () { it('has automatic scrolling enabled', function () {
runs(function () {
expect(state.videoCaption.autoScrolling).toBe(true); expect(state.videoCaption.autoScrolling).toBe(true);
}); });
}); });
});
describe( describe(
'when same caption gets the focus through mouse after ' + 'when same caption gets the focus through mouse after ' +
...@@ -1083,22 +1160,28 @@ ...@@ -1083,22 +1160,28 @@
function () { function () {
beforeEach(function () { beforeEach(function () {
runs(function () {
state.videoCaption.isMouseFocus = false; state.videoCaption.isMouseFocus = false;
$('.subtitles li[data-index=0]') $('.subtitles li[data-index=0]')
.trigger(jQuery.Event('focus')); .trigger(jQuery.Event('focus'));
$('.subtitles li[data-index=0]') $('.subtitles li[data-index=0]')
.trigger(jQuery.Event('mousedown')); .trigger(jQuery.Event('mousedown'));
}); });
});
it('does not show an outline around it', function () { it('does not show an outline around it', function () {
runs(function () {
expect($('.subtitles li[data-index=0]')) expect($('.subtitles li[data-index=0]'))
.not.toHaveClass('focused'); .not.toHaveClass('focused');
}); });
});
it('has automatic scrolling enabled', function () { it('has automatic scrolling enabled', function () {
runs(function () {
expect(state.videoCaption.autoScrolling).toBe(true); expect(state.videoCaption.autoScrolling).toBe(true);
}); });
}); });
});
describe( describe(
'when a second caption gets focus through mouse after ' + 'when a second caption gets focus through mouse after ' +
...@@ -1108,6 +1191,7 @@ ...@@ -1108,6 +1191,7 @@
var subDataLiIdx__0, subDataLiIdx__1; var subDataLiIdx__0, subDataLiIdx__1;
beforeEach(function () { beforeEach(function () {
runs(function () {
subDataLiIdx__0 = $('.subtitles li[data-index=0]'); subDataLiIdx__0 = $('.subtitles li[data-index=0]');
subDataLiIdx__1 = $('.subtitles li[data-index=1]'); subDataLiIdx__1 = $('.subtitles li[data-index=1]');
...@@ -1120,20 +1204,27 @@ ...@@ -1120,20 +1204,27 @@
subDataLiIdx__1.trigger(jQuery.Event('mousedown')); subDataLiIdx__1.trigger(jQuery.Event('mousedown'));
}); });
});
it('does not show an outline around the first', function () { it('does not show an outline around the first', function () {
runs(function () {
expect(subDataLiIdx__0).not.toHaveClass('focused'); expect(subDataLiIdx__0).not.toHaveClass('focused');
}); });
});
it('does not show an outline around the second', function () { it('does not show an outline around the second', function () {
runs(function () {
expect(subDataLiIdx__1).not.toHaveClass('focused'); expect(subDataLiIdx__1).not.toHaveClass('focused');
}); });
});
it('has automatic scrolling enabled', function () { it('has automatic scrolling enabled', function () {
runs(function () {
expect(state.videoCaption.autoScrolling).toBe(true); expect(state.videoCaption.autoScrolling).toBe(true);
}); });
}); });
}); });
}); });
});
}).call(this); }).call(this);
(function (define) {
define(
'video/00_async_process.js',
[],
function() {
"use strict";
/**
* Provides convenient way to process big amount of data without UI blocking.
*
* @param {array} list Array to process.
* @param {function} process Calls this function on each item in the list.
* @return {array} Returns a Promise object to observe when all actions of a
certain type bound to the collection, queued or not, have finished.
*/
var AsyncProcess = {
array: function (list, process) {
if (!_.isArray(list)) {
return $.Deferred().reject().promise();
}
if (!_.isFunction(process) || !list.length) {
return $.Deferred().resolve(list).promise();
}
var MAX_DELAY = 50, // maximum amount of time that js code should be allowed to run continuously
dfd = $.Deferred(),
result = [],
index = 0,
len = list.length;
var getCurrentTime = function () {
return (new Date()).getTime();
};
var handler = function () {
var start = getCurrentTime();
do {
result[index] = process(list[index], index);
index++;
} while (index < len && getCurrentTime() - start < MAX_DELAY);
if (index < len) {
setTimeout(handler, 25);
} else {
dfd.resolve(result);
}
};
setTimeout(handler, 25);
return dfd.promise();
}
};
return AsyncProcess;
});
}(RequireJS.define));
(function (define) {
define(
'video/00_sjson.js',
[],
function() {
"use strict";
var Sjson = function (data) {
var sjson = {
start: data.start.concat(),
text: data.text.concat()
},
module = {};
var getter = function (propertyName) {
return function () {
return sjson[propertyName];
};
};
var getStartTimes = getter('start');
var getCaptions = getter('text');
var size = function () {
return sjson.text.length;
};
var search = function (time) {
var start = getStartTimes(),
max = size() - 1,
min = 0,
index;
if (time < start[min]) {
return -1;
}
while (min < max) {
index = Math.ceil((max + min) / 2);
if (time < start[index]) {
max = index - 1;
}
if (time >= start[index]) {
min = index;
}
}
return min;
};
return {
getCaptions: getCaptions,
getStartTimes: getStartTimes,
getSize: size,
search: search
};
};
return Sjson;
});
}(RequireJS.define));
(function (requirejs, require, define) { (function (define) {
// VideoCaption module. // VideoCaption module.
define( define(
'video/09_video_caption.js', 'video/09_video_caption.js',
[], ['video/00_sjson.js', 'video/00_async_process.js'],
function () { function (Sjson, AsyncProcess) {
/** /**
* @desc VideoCaption module exports a function. * @desc VideoCaption module exports a function.
...@@ -21,16 +21,11 @@ function () { ...@@ -21,16 +21,11 @@ function () {
* @returns {undefined} * @returns {undefined}
*/ */
return function (state) { return function (state) {
var dfd = $.Deferred();
state.videoCaption = {}; state.videoCaption = {};
_makeFunctionsPublic(state); _makeFunctionsPublic(state);
state.videoCaption.renderElements(); state.videoCaption.renderElements();
dfd.resolve(); return $.Deferred().resolve().promise();
return dfd.promise();
}; };
// *************************************************************** // ***************************************************************
...@@ -65,10 +60,8 @@ function () { ...@@ -65,10 +60,8 @@ function () {
renderCaption: renderCaption, renderCaption: renderCaption,
renderElements: renderElements, renderElements: renderElements,
renderLanguageMenu: renderLanguageMenu, renderLanguageMenu: renderLanguageMenu,
reRenderCaption: reRenderCaption,
resize: resize, resize: resize,
scrollCaption: scrollCaption, scrollCaption: scrollCaption,
search: search,
seekPlayer: seekPlayer, seekPlayer: seekPlayer,
setSubtitlesHeight: setSubtitlesHeight, setSubtitlesHeight: setSubtitlesHeight,
toggle: toggle, toggle: toggle,
...@@ -133,18 +126,46 @@ function () { ...@@ -133,18 +126,46 @@ function () {
// mousemove, etc.). // mousemove, etc.).
function bindHandlers() { function bindHandlers() {
var self = this, var self = this,
Caption = this.videoCaption; Caption = this.videoCaption,
events = [
'mouseover', 'mouseout', 'mousedown', 'click', 'focus', 'blur',
'keydown'
].join(' ');
Caption.hideSubtitlesEl.on({ Caption.hideSubtitlesEl.on({
'click': Caption.toggle 'click': Caption.toggle
}); });
Caption.subtitlesEl.on({ Caption.subtitlesEl
.on({
mouseenter: Caption.onMouseEnter, mouseenter: Caption.onMouseEnter,
mouseleave: Caption.onMouseLeave, mouseleave: Caption.onMouseLeave,
mousemove: Caption.onMovement, mousemove: Caption.onMovement,
mousewheel: Caption.onMovement, mousewheel: Caption.onMovement,
DOMMouseScroll: Caption.onMovement DOMMouseScroll: Caption.onMovement
})
.on(events, 'li[data-index]', function (event) {
switch (event.type) {
case 'mouseover':
case 'mouseout':
Caption.captionMouseOverOut(event);
break;
case 'mousedown':
Caption.captionMouseDown(event);
break;
case 'click':
Caption.captionClick(event);
break;
case 'focusin':
Caption.captionFocus(event);
break;
case 'focusout':
Caption.captionBlur(event);
break;
case 'keydown':
Caption.captionKeyDown(event);
break;
}
}); });
if (Caption.showLanguageMenu) { if (Caption.showLanguageMenu) {
...@@ -154,12 +175,6 @@ function () { ...@@ -154,12 +175,6 @@ function () {
}); });
} }
this.el.on('speedchange', function () {
if (self.isFlashMode()) {
Caption.fetchCaption();
}
});
if ((this.videoType === 'html5') && (this.config.autohideHtml5)) { if ((this.videoType === 'html5') && (this.config.autohideHtml5)) {
Caption.subtitlesEl.on('scroll', this.videoControl.showControls); Caption.subtitlesEl.on('scroll', this.videoControl.showControls);
} }
...@@ -241,7 +256,7 @@ function () { ...@@ -241,7 +256,7 @@ function () {
if (this.videoType === 'youtube') { if (this.videoType === 'youtube') {
data = { data = {
videoId: this.youtubeId() videoId: this.youtubeId('1.0')
}; };
} }
...@@ -251,13 +266,15 @@ function () { ...@@ -251,13 +266,15 @@ function () {
url: self.config.transcriptTranslationUrl + '/' + language, url: self.config.transcriptTranslationUrl + '/' + language,
notifyOnError: false, notifyOnError: false,
data: data, data: data,
success: function (captions) { success: function (response) {
Caption.captions = captions.text; Caption.sjson = new Sjson(response);
Caption.start = captions.start;
var start = Caption.sjson.getStartTimes(),
captions = Caption.sjson.getCaptions();
if (Caption.loaded) { if (Caption.loaded) {
if (Caption.rendered) { if (Caption.rendered) {
Caption.reRenderCaption(); Caption.renderCaption(start, captions);
Caption.updatePlayTime(self.videoPlayer.currentTime); Caption.updatePlayTime(self.videoPlayer.currentTime);
} }
} else { } else {
...@@ -269,7 +286,7 @@ function () { ...@@ -269,7 +286,7 @@ function () {
) )
); );
} else { } else {
Caption.renderCaption(); Caption.renderCaption(start, captions);
} }
Caption.bindHandlers(); Caption.bindHandlers();
...@@ -334,7 +351,6 @@ function () { ...@@ -334,7 +351,6 @@ function () {
.height(this.videoCaption.bottomSpacingHeight()); .height(this.videoCaption.bottomSpacingHeight());
this.videoCaption.scrollCaption(); this.videoCaption.scrollCaption();
this.videoCaption.setSubtitlesHeight(); this.videoCaption.setSubtitlesHeight();
} }
...@@ -380,90 +396,53 @@ function () { ...@@ -380,90 +396,53 @@ function () {
}); });
} }
function buildCaptions (container, captions, start) { function buildCaptions (container, start, captions) {
var fragment = document.createDocumentFragment(); var process = function(text, index) {
var liEl = $('<li>', {
$.each(captions, function(index, text) {
var liEl = $('<li>');
liEl.html(text);
liEl.attr({
'data-index': index, 'data-index': index,
'data-start': start[index], 'data-start': start[index],
'tabindex': 0 'tabindex': 0
}); }).html(text);
fragment.appendChild(liEl[0]); return liEl[0];
}); };
container.append([fragment]); return AsyncProcess.array(captions, process).done(function (list) {
container.append(list);
});
} }
function renderCaption() { function renderCaption(start, captions) {
var Caption = this.videoCaption, var Caption = this.videoCaption;
events = ['mouseover', 'mouseout', 'mousedown', 'click', 'focus',
'blur', 'keydown'].join(' ');
Caption.setSubtitlesHeight();
buildCaptions(Caption.subtitlesEl, Caption.captions, Caption.start);
Caption.subtitlesEl.on(events, 'li[data-index]', function (event) {
switch (event.type) {
case 'mouseover':
case 'mouseout':
Caption.captionMouseOverOut(event);
break;
case 'mousedown':
Caption.captionMouseDown(event);
break;
case 'click':
Caption.captionClick(event);
break;
case 'focusin':
Caption.captionFocus(event);
break;
case 'focusout':
Caption.captionBlur(event);
break;
case 'keydown':
Caption.captionKeyDown(event);
break;
}
});
var onRender = function () {
Caption.addPaddings();
// Enables or disables automatic scrolling of the captions when the // Enables or disables automatic scrolling of the captions when the
// video is playing. This feature has to be disabled when tabbing // video is playing. This feature has to be disabled when tabbing
// through them as it interferes with that action. Initially, have this // through them as it interferes with that action. Initially, have
// flag enabled as we assume mouse use. Then, if the first caption // this flag enabled as we assume mouse use. Then, if the first
// (through forward tabbing) or the last caption (through backwards // caption (through forward tabbing) or the last caption (through
// tabbing) gets the focus, disable that feature. Re-enable it if tabbing // backwards tabbing) gets the focus, disable that feature.
// then cycles out of the the captions. // Re-enable it if tabbing then cycles out of the the captions.
Caption.autoScrolling = true; Caption.autoScrolling = true;
// Keeps track of where the focus is situated in the array of captions. // Keeps track of where the focus is situated in the array of
// Used to implement the automatic scrolling behavior and decide if the // captions. Used to implement the automatic scrolling behavior and
// outline around a caption has to be hidden or shown on a mouseenter // decide if the outline around a caption has to be hidden or shown
// or mouseleave. Initially, no caption has the focus, set the // on a mouseenter or mouseleave. Initially, no caption has the
// index to -1. // focus, set the index to -1.
Caption.currentCaptionIndex = -1; Caption.currentCaptionIndex = -1;
// Used to track if the focus is coming from a click or tabbing. This // Used to track if the focus is coming from a click or tabbing. This
// has to be known to decide if, when a caption gets the focus, an // has to be known to decide if, when a caption gets the focus, an
// outline has to be drawn (tabbing) or not (mouse click). // outline has to be drawn (tabbing) or not (mouse click).
Caption.isMouseFocus = false; Caption.isMouseFocus = false;
Caption.addPaddings();
Caption.rendered = true; Caption.rendered = true;
} };
function reRenderCaption() {
var Caption = this.videoCaption;
Caption.currentIndex = null;
Caption.rendered = false; Caption.rendered = false;
Caption.subtitlesEl.empty(); Caption.subtitlesEl.empty();
buildCaptions(Caption.subtitlesEl, Caption.captions, Caption.start); Caption.setSubtitlesHeight();
Caption.addPaddings(); buildCaptions(Caption.subtitlesEl, start, captions).done(onRender);
Caption.rendered = true;
} }
function addPaddings() { function addPaddings() {
...@@ -529,7 +508,7 @@ function () { ...@@ -529,7 +508,7 @@ function () {
// off again as it may have been enabled in captionBlur. // off again as it may have been enabled in captionBlur.
if ( if (
captionIndex <= 1 || captionIndex <= 1 ||
captionIndex >= this.videoCaption.captions.length - 2 captionIndex >= this.videoCaption.sjson.getSize() - 2
) { ) {
this.videoCaption.autoScrolling = false; this.videoCaption.autoScrolling = false;
} }
...@@ -547,7 +526,7 @@ function () { ...@@ -547,7 +526,7 @@ function () {
// tabbing back out of the captions or on the last element and tabbing // tabbing back out of the captions or on the last element and tabbing
// forward out of the captions. // forward out of the captions.
if (captionIndex === 0 || if (captionIndex === 0 ||
captionIndex === this.videoCaption.captions.length - 1) { captionIndex === this.videoCaption.sjson.getSize() - 1) {
this.videoCaption.autoScrolling = true; this.videoCaption.autoScrolling = true;
} }
...@@ -579,38 +558,13 @@ function () { ...@@ -579,38 +558,13 @@ function () {
} }
} }
function search(time) {
var index, max, min;
if (this.videoCaption.loaded) {
min = 0;
max = this.videoCaption.start.length - 1;
if (time < this.videoCaption.start[min]) {
return -1;
}
while (min < max) {
index = Math.ceil((max + min) / 2);
if (time < this.videoCaption.start[index]) {
max = index - 1;
}
if (time >= this.videoCaption.start[index]) {
min = index;
}
}
return min;
}
return undefined;
}
function play() { function play() {
if (this.videoCaption.loaded) { if (this.videoCaption.loaded) {
if (!this.videoCaption.rendered) { if (!this.videoCaption.rendered) {
this.videoCaption.renderCaption(); var start = this.videoCaption.sjson.getStartTimes(),
captions = this.videoCaption.sjson.getCaptions();
this.videoCaption.renderCaption(start, captions);
} }
this.videoCaption.playing = true; this.videoCaption.playing = true;
...@@ -627,20 +581,12 @@ function () { ...@@ -627,20 +581,12 @@ function () {
var newIndex; var newIndex;
if (this.videoCaption.loaded) { if (this.videoCaption.loaded) {
// Current mode === 'flash' can only be for YouTube videos. So, we
// don't have to also check for videoType === 'youtube'.
if (this.isFlashMode()) { if (this.isFlashMode()) {
// Total play time changes with speed change. Also there is time = Time.convert(time, this.speed, '1.0');
// a 250 ms delay we have to take into account.
time = Math.round(
Time.convert(time, this.speed, '1.0') * 1000 + 100
);
} else {
// Total play time remains constant when speed changes.
time = Math.round(time * 1000 + 100);
} }
newIndex = this.videoCaption.search(time); time = Math.round(time * 1000 + 100);
newIndex = this.videoCaption.sjson.search(time);
if ( if (
typeof newIndex !== 'undefined' && typeof newIndex !== 'undefined' &&
...@@ -658,39 +604,27 @@ function () { ...@@ -658,39 +604,27 @@ function () {
.addClass('current'); .addClass('current');
this.videoCaption.currentIndex = newIndex; this.videoCaption.currentIndex = newIndex;
this.videoCaption.scrollCaption(); this.videoCaption.scrollCaption();
} }
} }
} }
function seekPlayer(event) { function seekPlayer(event) {
var time; var time = parseInt($(event.target).data('start'), 10);
event.preventDefault();
// Current mode === 'flash' can only be for YouTube videos. So, we
// don't have to also check for videoType === 'youtube'.
if (this.isFlashMode()) { if (this.isFlashMode()) {
// Total play time changes with speed change. Also there is time = Math.round(Time.convert(time, '1.0', this.speed));
// a 250 ms delay we have to take into account.
time = Math.round(
Time.convert(
$(event.target).data('start'), '1.0', this.speed
) / 1000
);
} else {
// Total play time remains constant when speed changes.
time = parseInt($(event.target).data('start'), 10)/1000;
} }
this.trigger( this.trigger(
'videoPlayer.onCaptionSeek', 'videoPlayer.onCaptionSeek',
{ {
'type': 'onCaptionSeek', 'type': 'onCaptionSeek',
'time': time 'time': time/1000
} }
); );
event.preventDefault();
} }
function calculateOffset(element) { function calculateOffset(element) {
...@@ -801,4 +735,4 @@ function () { ...@@ -801,4 +735,4 @@ function () {
} }
}); });
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); }(RequireJS.define));
...@@ -72,6 +72,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule): ...@@ -72,6 +72,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
'js': [ 'js': [
resource_string(module, 'js/src/video/00_video_storage.js'), resource_string(module, 'js/src/video/00_video_storage.js'),
resource_string(module, 'js/src/video/00_resizer.js'), resource_string(module, 'js/src/video/00_resizer.js'),
resource_string(module, 'js/src/video/00_async_process.js'),
resource_string(module, 'js/src/video/00_sjson.js'),
resource_string(module, 'js/src/video/01_initialize.js'), resource_string(module, 'js/src/video/01_initialize.js'),
resource_string(module, 'js/src/video/025_focus_grabber.js'), resource_string(module, 'js/src/video/025_focus_grabber.js'),
resource_string(module, 'js/src/video/02_html5_video.js'), resource_string(module, 'js/src/video/02_html5_video.js'),
......
...@@ -243,6 +243,7 @@ Feature: LMS Video component ...@@ -243,6 +243,7 @@ Feature: LMS Video component
And it has a video in "Youtube" mode: And it has a video in "Youtube" mode:
| transcripts | sub | download_track | | transcripts | sub | download_track |
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true | | {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true |
And I see "Hi, welcome to Edx." text in the captions
Then I can download transcript in "srt" format that has text "Hi, welcome to Edx." Then I can download transcript in "srt" format that has text "Hi, welcome to Edx."
And I select language with code "zh" And I select language with code "zh"
And I see "好 各位同学" text in the captions And I see "好 各位同学" text in the captions
...@@ -256,6 +257,7 @@ Feature: LMS Video component ...@@ -256,6 +257,7 @@ Feature: LMS Video component
And it has a video in "HTML5" mode: And it has a video in "HTML5" mode:
| transcripts | sub | download_track | | transcripts | sub | download_track |
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true | | {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true |
And I see "Hi, welcome to Edx." text in the captions
Then I can download transcript in "srt" format that has text "Hi, welcome to Edx." Then I can download transcript in "srt" format that has text "Hi, welcome to Edx."
And I select language with code "zh" And I select language with code "zh"
And I see "好 各位同学" text in the captions And I see "好 各位同学" text in the captions
...@@ -298,8 +300,10 @@ Feature: LMS Video component ...@@ -298,8 +300,10 @@ Feature: LMS Video component
And a video "D" in "Youtube_HTML5" mode in position "3" of sequential And a video "D" in "Youtube_HTML5" mode in position "3" of sequential
And I open the section with videos And I open the section with videos
Then videos have rendered in "HTML5" mode Then videos have rendered in "HTML5" mode
And I see "Hi, welcome to Edx." text in the captions And I see text in the captions:
And I see "Equal transcripts" text in the captions | text |
| Hi, welcome to Edx. |
| Equal transcripts |
When I open video "C" When I open video "C"
Then the video has rendered in "HTML5" mode Then the video has rendered in "HTML5" mode
And I make sure captions are opened And I make sure captions are opened
......
...@@ -419,7 +419,15 @@ def i_see_menu(_step, menu): ...@@ -419,7 +419,15 @@ def i_see_menu(_step, menu):
@step('I see "([^"]*)" text in the captions$') @step('I see "([^"]*)" text in the captions$')
def check_text_in_the_captions(_step, text): def check_text_in_the_captions(_step, text):
assert world.browser.is_text_present(text.strip()) world.wait_for(lambda _: world.css_text('.subtitles'))
actual_text = world.css_text('.subtitles')
assert (text in actual_text)
@step('I see text in the captions:')
def check_captions(_step):
for index, video in enumerate(_step.hashes):
assert (video.get('text') in world.css_text('.subtitles', index=index))
@step('I select language with code "([^"]*)"$') @step('I select language with code "([^"]*)"$')
...@@ -441,14 +449,14 @@ def select_language(_step, code): ...@@ -441,14 +449,14 @@ def select_language(_step, code):
# Make sure that all ajax requests that affects the display of captions are finished. # Make sure that all ajax requests that affects the display of captions are finished.
# For example, request to get new translation etc. # For example, request to get new translation etc.
world.wait_for_ajax_complete() world.wait_for_ajax_complete()
assert world.css_visible('.subtitles') world.wait_for_visible('.subtitles')
@step('I click video button "([^"]*)"$') @step('I click video button "([^"]*)"$')
def click_button(_step, button): def click_button(_step, button):
world.css_click(VIDEO_BUTTONS[button]) world.css_click(VIDEO_BUTTONS[button])
@step('I see video starts playing from "([^"]*)" position$') @step('I see video starts playing from "([^"]*)" position$')
def start_playing_video_from_n_seconds(_step, position): def start_playing_video_from_n_seconds(_step, position):
world.wait_for( world.wait_for(
...@@ -504,9 +512,7 @@ def video_alignment(_step, transcript_visibility): ...@@ -504,9 +512,7 @@ def video_alignment(_step, transcript_visibility):
height = abs(expected['height'] - real['height']) <= 5 height = abs(expected['height'] - real['height']) <= 5
# Restore initial window size # Restore initial window size
set_window_dimensions( set_window_dimensions(initial['width'], initial['height'])
initial['width'], initial['height']
)
assert all([width, height]) assert all([width, height])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment