Merge pull request #3128 from edx/anton/transcript-generation
Update captions logic on front-end.
This commit is contained in:
@@ -201,7 +201,9 @@ def upload_file(_step, file_name):
|
||||
|
||||
@step('I see "([^"]*)" text in the captions')
|
||||
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 "([^"]*)"$')
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
(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));
|
||||
35
common/lib/xmodule/xmodule/js/spec/video/sjson_spec.js
Normal file
35
common/lib/xmodule/xmodule/js/spec/video/sjson_spec.js
Normal file
@@ -0,0 +1,35 @@
|
||||
(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 @@
|
||||
});
|
||||
expect($.ajaxWithPrefix.mostRecentCall.args[0].data)
|
||||
.toEqual({
|
||||
videoId: 'abcdefghijkl'
|
||||
videoId: 'cogebirgzzM'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -237,73 +237,94 @@
|
||||
|
||||
describe('when on a non touch-based device', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
it('render the caption', function () {
|
||||
var captionsData;
|
||||
runs(function () {
|
||||
var captionsData;
|
||||
|
||||
captionsData = jasmine.stubbedCaption;
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
captionsData = jasmine.stubbedCaption;
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
|
||||
expect($(link)).toHaveData('index', index);
|
||||
expect($(link)).toHaveData(
|
||||
'start', captionsData.start[index]
|
||||
);
|
||||
expect($(link)).toHaveAttr('tabindex', 0);
|
||||
expect($(link)).toHaveText(captionsData.text[index]);
|
||||
expect($(link)).toHaveData('index', index);
|
||||
expect($(link)).toHaveData(
|
||||
'start', captionsData.start[index]
|
||||
);
|
||||
expect($(link)).toHaveAttr('tabindex', 0);
|
||||
expect($(link)).toHaveText(captionsData.text[index]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('add a padding element to caption', function () {
|
||||
expect($('.subtitles li:first').hasClass('spacing'))
|
||||
.toBe(true);
|
||||
expect($('.subtitles li:last').hasClass('spacing'))
|
||||
.toBe(true);
|
||||
runs(function () {
|
||||
expect($('.subtitles li:first').hasClass('spacing'))
|
||||
.toBe(true);
|
||||
expect($('.subtitles li:last').hasClass('spacing'))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('bind all the caption link', function () {
|
||||
var handlerList = ['captionMouseOverOut', 'captionClick',
|
||||
'captionMouseDown', 'captionFocus', 'captionBlur',
|
||||
'captionKeyDown'
|
||||
];
|
||||
runs(function () {
|
||||
var handlerList = ['captionMouseOverOut', 'captionClick',
|
||||
'captionMouseDown', 'captionFocus', 'captionBlur',
|
||||
'captionKeyDown'
|
||||
];
|
||||
|
||||
$.each(handlerList, function(index, handler) {
|
||||
spyOn(state.videoCaption, handler);
|
||||
});
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
$.each(handlerList, function(index, handler) {
|
||||
spyOn(state.videoCaption, handler);
|
||||
});
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
|
||||
|
||||
$(link).trigger('mouseover');
|
||||
expect(state.videoCaption.captionMouseOverOut).toHaveBeenCalled();
|
||||
$(link).trigger('mouseover');
|
||||
expect(state.videoCaption.captionMouseOverOut).toHaveBeenCalled();
|
||||
|
||||
state.videoCaption.captionMouseOverOut.reset();
|
||||
$(link).trigger('mouseout');
|
||||
expect(state.videoCaption.captionMouseOverOut).toHaveBeenCalled();
|
||||
state.videoCaption.captionMouseOverOut.reset();
|
||||
$(link).trigger('mouseout');
|
||||
expect(state.videoCaption.captionMouseOverOut).toHaveBeenCalled();
|
||||
|
||||
$(this).click();
|
||||
expect(state.videoCaption.captionClick).toHaveBeenCalled();
|
||||
$(this).click();
|
||||
expect(state.videoCaption.captionClick).toHaveBeenCalled();
|
||||
|
||||
$(this).trigger('mousedown');
|
||||
expect(state.videoCaption.captionMouseDown).toHaveBeenCalled();
|
||||
$(this).trigger('mousedown');
|
||||
expect(state.videoCaption.captionMouseDown).toHaveBeenCalled();
|
||||
|
||||
$(this).trigger('focus');
|
||||
expect(state.videoCaption.captionFocus).toHaveBeenCalled();
|
||||
$(this).trigger('focus');
|
||||
expect(state.videoCaption.captionFocus).toHaveBeenCalled();
|
||||
|
||||
$(this).trigger('blur');
|
||||
expect(state.videoCaption.captionBlur).toHaveBeenCalled();
|
||||
$(this).trigger('blur');
|
||||
expect(state.videoCaption.captionBlur).toHaveBeenCalled();
|
||||
|
||||
$(this).trigger('keydown');
|
||||
expect(state.videoCaption.captionKeyDown).toHaveBeenCalled();
|
||||
$(this).trigger('keydown');
|
||||
expect(state.videoCaption.captionKeyDown).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('set rendered to true', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
expect(state.videoCaption.rendered).toBeTruthy();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
|
||||
runs(function () {
|
||||
expect(state.videoCaption.rendered).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -345,49 +366,58 @@
|
||||
beforeEach(function () {
|
||||
jasmine.Clock.useMock();
|
||||
spyOn(window, 'clearTimeout');
|
||||
state = jasmine.initializePlayer();
|
||||
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
jasmine.Clock.tick(50);
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
describe('when cursor is outside of the caption box', function () {
|
||||
beforeEach(function () {
|
||||
$(window).trigger(jQuery.Event('mousemove'));
|
||||
jasmine.Clock.tick(state.config.captionsFreezeTime);
|
||||
});
|
||||
|
||||
it('does not set freezing timeout', function () {
|
||||
expect(state.videoCaption.frozen).toBeFalsy();
|
||||
runs(function () {
|
||||
expect(state.videoCaption.frozen).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cursor is in the caption box', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(state.videoCaption, 'onMouseLeave');
|
||||
$('.subtitles').trigger(jQuery.Event('mouseenter'));
|
||||
jasmine.Clock.tick(state.config.captionsFreezeTime);
|
||||
runs(function () {
|
||||
$(window).trigger(jQuery.Event('mousemove'));
|
||||
jasmine.Clock.tick(state.config.captionsFreezeTime);
|
||||
$('.subtitles').trigger(jQuery.Event('mouseenter'));
|
||||
jasmine.Clock.tick(state.config.captionsFreezeTime);
|
||||
});
|
||||
});
|
||||
|
||||
it('set the freezing timeout', function () {
|
||||
expect(state.videoCaption.frozen).not.toBeFalsy();
|
||||
expect(state.videoCaption.onMouseLeave).toHaveBeenCalled();
|
||||
runs(function () {
|
||||
expect(state.videoCaption.frozen).not.toBeFalsy();
|
||||
expect(state.videoCaption.onMouseLeave).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the cursor is moving', function () {
|
||||
beforeEach(function () {
|
||||
$('.subtitles').trigger(jQuery.Event('mousemove'));
|
||||
});
|
||||
|
||||
it('reset the freezing timeout', function () {
|
||||
expect(window.clearTimeout).toHaveBeenCalled();
|
||||
runs(function () {
|
||||
$('.subtitles').trigger(jQuery.Event('mousemove'));
|
||||
expect(window.clearTimeout).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the mouse is scrolling', function () {
|
||||
beforeEach(function () {
|
||||
$('.subtitles').trigger(jQuery.Event('mousewheel'));
|
||||
});
|
||||
|
||||
it('reset the freezing timeout', function () {
|
||||
expect(window.clearTimeout).toHaveBeenCalled();
|
||||
runs(function () {
|
||||
$('.subtitles').trigger(jQuery.Event('mousewheel'));
|
||||
expect(window.clearTimeout).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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 () {
|
||||
var Caption, msg;
|
||||
|
||||
@@ -467,7 +478,6 @@
|
||||
state = jasmine.initializePlayer();
|
||||
Caption = state.videoCaption;
|
||||
spyOn($, 'ajaxWithPrefix').andCallThrough();
|
||||
spyOn(Caption, 'reRenderCaption');
|
||||
spyOn(Caption, 'renderCaption');
|
||||
spyOn(Caption, 'bindHandlers');
|
||||
spyOn(Caption, 'updatePlayTime');
|
||||
@@ -511,7 +521,6 @@
|
||||
expect(Caption.bindHandlers).toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.updatePlayTime).not.toHaveBeenCalled();
|
||||
expect(Caption.reRenderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.loaded).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -527,7 +536,6 @@
|
||||
expect(Caption.bindHandlers).not.toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.updatePlayTime).not.toHaveBeenCalled();
|
||||
expect(Caption.reRenderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.loaded).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -539,9 +547,8 @@
|
||||
|
||||
expect($.ajaxWithPrefix).toHaveBeenCalled();
|
||||
expect(Caption.bindHandlers).not.toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).toHaveBeenCalled();
|
||||
expect(Caption.updatePlayTime).toHaveBeenCalled();
|
||||
expect(Caption.reRenderCaption).toHaveBeenCalled();
|
||||
expect(Caption.loaded).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -553,19 +560,18 @@
|
||||
expect(Caption.bindHandlers).toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).toHaveBeenCalled();
|
||||
expect(Caption.updatePlayTime).not.toHaveBeenCalled();
|
||||
expect(Caption.reRenderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.loaded).toBeTruthy();
|
||||
});
|
||||
|
||||
it('on success: re-rendered correct', function () {
|
||||
Caption.loaded = true;
|
||||
Caption.rendered = true;
|
||||
Caption.fetchCaption();
|
||||
|
||||
expect($.ajaxWithPrefix).toHaveBeenCalled();
|
||||
expect(Caption.bindHandlers).not.toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).not.toHaveBeenCalled();
|
||||
expect(Caption.renderCaption).toHaveBeenCalled();
|
||||
expect(Caption.updatePlayTime).toHaveBeenCalled();
|
||||
expect(Caption.reRenderCaption).toHaveBeenCalled();
|
||||
expect(Caption.loaded).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -680,53 +686,57 @@
|
||||
});
|
||||
});
|
||||
|
||||
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('when the caption was not rendered', function () {
|
||||
beforeEach(function () {
|
||||
window.onTouchBasedDevice.andReturn(['iPad']);
|
||||
state = jasmine.initializePlayer();
|
||||
state.videoCaption.play();
|
||||
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
state.videoCaption.play();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
it('render the caption', function () {
|
||||
var captionsData;
|
||||
runs(function () {
|
||||
var captionsData;
|
||||
|
||||
captionsData = jasmine.stubbedCaption;
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
captionsData = jasmine.stubbedCaption;
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
|
||||
expect($(link)).toHaveData('index', index);
|
||||
expect($(link)).toHaveData(
|
||||
'start', captionsData.start[index]
|
||||
);
|
||||
expect($(link)).toHaveAttr('tabindex', 0);
|
||||
expect($(link)).toHaveText(captionsData.text[index]);
|
||||
expect($(link)).toHaveData('index', index);
|
||||
expect($(link)).toHaveData(
|
||||
'start', captionsData.start[index]
|
||||
);
|
||||
expect($(link)).toHaveAttr('tabindex', 0);
|
||||
expect($(link)).toHaveText(captionsData.text[index]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('add a padding element to caption', function () {
|
||||
expect($('.subtitles li:first')).toBe('.spacing');
|
||||
expect($('.subtitles li:last')).toBe('.spacing');
|
||||
runs(function () {
|
||||
expect($('.subtitles li:first')).toBe('.spacing');
|
||||
expect($('.subtitles li:last')).toBe('.spacing');
|
||||
});
|
||||
});
|
||||
|
||||
it('set rendered to true', function () {
|
||||
expect(state.videoCaption.rendered).toBeTruthy();
|
||||
runs(function () {
|
||||
expect(state.videoCaption.rendered).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('set playing to true', function () {
|
||||
expect(state.videoCaption.playing).toBeTruthy();
|
||||
runs(function () {
|
||||
expect(state.videoCaption.playing).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -745,219 +755,269 @@
|
||||
|
||||
describe('updatePlayTime', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
describe('when the video speed is 1.0x', function () {
|
||||
beforeEach(function () {
|
||||
state.videoSpeedControl.currentSpeed = '1.0';
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
});
|
||||
|
||||
it('search the caption based on time', function () {
|
||||
expect(state.videoCaption.currentIndex).toEqual(5);
|
||||
runs(function () {
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
expect(state.videoCaption.currentIndex).toEqual(5);
|
||||
|
||||
// Flash mode
|
||||
spyOn(state, 'isFlashMode').andReturn(true);
|
||||
state.speed = '1.0';
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
expect(state.videoCaption.currentIndex).toEqual(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the video speed is not 1.0x', function () {
|
||||
beforeEach(function () {
|
||||
state.videoSpeedControl.currentSpeed = '0.75';
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
});
|
||||
|
||||
it('search the caption based on 1.0x speed', function () {
|
||||
expect(state.videoCaption.currentIndex).toEqual(5);
|
||||
runs(function () {
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
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 () {
|
||||
beforeEach(function () {
|
||||
state.videoCaption.currentIndex = 1;
|
||||
$('.subtitles li[data-index=5]').addClass('current');
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
runs(function () {
|
||||
state.videoCaption.currentIndex = 1;
|
||||
$('.subtitles li[data-index=5]').addClass('current');
|
||||
state.videoCaption.updatePlayTime(25.000);
|
||||
});
|
||||
});
|
||||
|
||||
it('deactivate the previous caption', function () {
|
||||
expect($('.subtitles li[data-index=1]'))
|
||||
.not.toHaveClass('current');
|
||||
runs(function () {
|
||||
expect($('.subtitles li[data-index=1]'))
|
||||
.not.toHaveClass('current');
|
||||
});
|
||||
});
|
||||
|
||||
it('activate new caption', function () {
|
||||
expect($('.subtitles li[data-index=5]'))
|
||||
.toHaveClass('current');
|
||||
runs(function () {
|
||||
expect($('.subtitles li[data-index=5]'))
|
||||
.toHaveClass('current');
|
||||
});
|
||||
});
|
||||
|
||||
it('save new index', function () {
|
||||
expect(state.videoCaption.currentIndex).toEqual(5);
|
||||
runs(function () {
|
||||
expect(state.videoCaption.currentIndex).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
// Disabled 11/25/13 due to flakiness in master
|
||||
xit('scroll caption to new position', function () {
|
||||
expect($.fn.scrollTo).toHaveBeenCalled();
|
||||
it('scroll caption to new position', function () {
|
||||
runs(function () {
|
||||
expect($.fn.scrollTo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the index is the same', function () {
|
||||
beforeEach(function () {
|
||||
state.videoCaption.currentIndex = 1;
|
||||
$('.subtitles li[data-index=3]').addClass('current');
|
||||
state.videoCaption.updatePlayTime(15.000);
|
||||
});
|
||||
|
||||
it('does not change current subtitle', function () {
|
||||
expect($('.subtitles li[data-index=3]'))
|
||||
.toHaveClass('current');
|
||||
runs(function () {
|
||||
state.videoCaption.currentIndex = 1;
|
||||
$('.subtitles li[data-index=3]').addClass('current');
|
||||
state.videoCaption.updatePlayTime(15.000);
|
||||
expect($('.subtitles li[data-index=3]'))
|
||||
.toHaveClass('current');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resize', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
videoControl = state.videoControl;
|
||||
$('.subtitles li[data-index=1]').addClass('current');
|
||||
state.videoCaption.resize();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
|
||||
runs(function () {
|
||||
videoControl = state.videoControl;
|
||||
$('.subtitles li[data-index=1]').addClass('current');
|
||||
state.videoCaption.resize();
|
||||
});
|
||||
});
|
||||
|
||||
describe('set the height of caption container', function () {
|
||||
it('when CC button is enabled', function () {
|
||||
var realHeight = parseInt(
|
||||
$('.subtitles').css('maxHeight'), 10
|
||||
),
|
||||
shouldBeHeight = $('.video-wrapper').height();
|
||||
runs(function () {
|
||||
var realHeight = parseInt(
|
||||
$('.subtitles').css('maxHeight'), 10
|
||||
),
|
||||
shouldBeHeight = $('.video-wrapper').height();
|
||||
|
||||
// Because of some problems with rounding on different
|
||||
// environments: Linux * Mac * FF * Chrome
|
||||
expect(realHeight).toBeCloseTo(shouldBeHeight, 2);
|
||||
// Because of some problems with rounding on different
|
||||
// environments: Linux * Mac * FF * Chrome
|
||||
expect(realHeight).toBeCloseTo(shouldBeHeight, 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('when CC button is disabled ', function () {
|
||||
var realHeight, videoWrapperHeight, progressSliderHeight,
|
||||
controlHeight, shouldBeHeight;
|
||||
runs(function () {
|
||||
var realHeight, videoWrapperHeight, progressSliderHeight,
|
||||
controlHeight, shouldBeHeight;
|
||||
|
||||
state.captionsHidden = true;
|
||||
state.videoCaption.setSubtitlesHeight();
|
||||
state.captionsHidden = true;
|
||||
state.videoCaption.setSubtitlesHeight();
|
||||
|
||||
realHeight = parseInt(
|
||||
$('.subtitles').css('maxHeight'), 10
|
||||
);
|
||||
videoWrapperHeight = $('.video-wrapper').height();
|
||||
progressSliderHeight = videoControl.sliderEl.height();
|
||||
controlHeight = videoControl.el.height();
|
||||
shouldBeHeight = videoWrapperHeight -
|
||||
0.5 * progressSliderHeight -
|
||||
controlHeight;
|
||||
realHeight = parseInt(
|
||||
$('.subtitles').css('maxHeight'), 10
|
||||
);
|
||||
videoWrapperHeight = $('.video-wrapper').height();
|
||||
progressSliderHeight = videoControl.sliderEl.height();
|
||||
controlHeight = videoControl.el.height();
|
||||
shouldBeHeight = videoWrapperHeight -
|
||||
0.5 * progressSliderHeight -
|
||||
controlHeight;
|
||||
|
||||
expect(realHeight).toBe(shouldBeHeight);
|
||||
expect(realHeight).toBe(shouldBeHeight);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('set the height of caption spacing', function () {
|
||||
var firstSpacing, lastSpacing;
|
||||
runs(function () {
|
||||
var firstSpacing, lastSpacing;
|
||||
|
||||
firstSpacing = Math.abs(parseInt(
|
||||
$('.subtitles .spacing:first').css('height'), 10
|
||||
));
|
||||
lastSpacing = Math.abs(parseInt(
|
||||
$('.subtitles .spacing:last').css('height'), 10
|
||||
));
|
||||
firstSpacing = Math.abs(parseInt(
|
||||
$('.subtitles .spacing:first').css('height'), 10
|
||||
));
|
||||
lastSpacing = Math.abs(parseInt(
|
||||
$('.subtitles .spacing:last').css('height'), 10
|
||||
));
|
||||
|
||||
expect(firstSpacing - state.videoCaption.topSpacingHeight())
|
||||
.toBeLessThan(1);
|
||||
expect(lastSpacing - state.videoCaption.bottomSpacingHeight())
|
||||
.toBeLessThan(1);
|
||||
expect(firstSpacing - state.videoCaption.topSpacingHeight())
|
||||
.toBeLessThan(1);
|
||||
expect(lastSpacing - state.videoCaption.bottomSpacingHeight())
|
||||
.toBeLessThan(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('scroll caption to new position', function () {
|
||||
expect($.fn.scrollTo).toHaveBeenCalled();
|
||||
runs(function () {
|
||||
expect($.fn.scrollTo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Disabled 11/25/13 due to flakiness in master
|
||||
xdescribe('scrollCaption', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
describe('when frozen', function () {
|
||||
beforeEach(function () {
|
||||
state.videoCaption.frozen = true;
|
||||
$('.subtitles li[data-index=1]').addClass('current');
|
||||
state.videoCaption.scrollCaption();
|
||||
});
|
||||
|
||||
it('does not scroll the caption', function () {
|
||||
expect($.fn.scrollTo).not.toHaveBeenCalled();
|
||||
runs(function () {
|
||||
state.videoCaption.frozen = true;
|
||||
$('.subtitles li[data-index=1]').addClass('current');
|
||||
state.videoCaption.scrollCaption();
|
||||
expect($.fn.scrollTo).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when not frozen', function () {
|
||||
beforeEach(function () {
|
||||
state.videoCaption.frozen = false;
|
||||
runs(function () {
|
||||
state.videoCaption.frozen = false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is no current caption', function () {
|
||||
beforeEach(function () {
|
||||
state.videoCaption.scrollCaption();
|
||||
});
|
||||
|
||||
it('does not scroll the caption', function () {
|
||||
expect($.fn.scrollTo).not.toHaveBeenCalled();
|
||||
runs(function () {
|
||||
state.videoCaption.scrollCaption();
|
||||
expect($.fn.scrollTo).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a current caption', function () {
|
||||
beforeEach(function () {
|
||||
$('.subtitles li[data-index=1]').addClass('current');
|
||||
state.videoCaption.scrollCaption();
|
||||
});
|
||||
|
||||
it('scroll to current caption', function () {
|
||||
expect($.fn.scrollTo).toHaveBeenCalled();
|
||||
runs(function () {
|
||||
$('.subtitles li[data-index=1]').addClass('current');
|
||||
state.videoCaption.scrollCaption();
|
||||
expect($.fn.scrollTo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Disabled 10/9/13 due to flakiness in master
|
||||
xdescribe('seekPlayer', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
var duration = state.videoPlayer.duration(),
|
||||
isRendered = state.videoCaption.rendered;
|
||||
|
||||
return isRendered && duration;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
describe('when the video speed is 1.0x', function () {
|
||||
beforeEach(function () {
|
||||
state.videoSpeedControl.currentSpeed = '1.0';
|
||||
$('.subtitles li[data-start="14910"]').trigger('click');
|
||||
});
|
||||
|
||||
it('trigger seek event with the correct time', function () {
|
||||
expect(state.videoPlayer.currentTime).toEqual(14.91);
|
||||
runs(function () {
|
||||
state.videoSpeedControl.currentSpeed = '1.0';
|
||||
$('.subtitles li[data-start="14910"]').trigger('click');
|
||||
expect(state.videoPlayer.currentTime).toEqual(14.91);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the video speed is not 1.0x', function () {
|
||||
beforeEach(function () {
|
||||
state.videoSpeedControl.currentSpeed = '0.75';
|
||||
$('.subtitles li[data-start="14910"]').trigger('click');
|
||||
});
|
||||
|
||||
it('trigger seek event with the correct time', function () {
|
||||
expect(state.videoPlayer.currentTime).toEqual(14.91);
|
||||
runs(function () {
|
||||
state.videoSpeedControl.currentSpeed = '0.75';
|
||||
$('.subtitles li[data-start="14910"]').trigger('click');
|
||||
expect(state.videoPlayer.currentTime).toEqual(14.91);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the player type is Flash at speed 0.75x',
|
||||
function () {
|
||||
|
||||
beforeEach(function () {
|
||||
state.videoSpeedControl.currentSpeed = '0.75';
|
||||
state.currentPlayerMode = 'flash';
|
||||
$('.subtitles li[data-start="14910"]').trigger('click');
|
||||
});
|
||||
|
||||
it('trigger seek event with the correct time', function () {
|
||||
expect(state.videoPlayer.currentTime).toEqual(15);
|
||||
runs(function () {
|
||||
state.videoSpeedControl.currentSpeed = '0.75';
|
||||
state.currentPlayerMode = 'flash';
|
||||
$('.subtitles li[data-start="14910"]').trigger('click');
|
||||
expect(state.videoPlayer.currentTime).toEqual(15);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -998,7 +1058,6 @@
|
||||
beforeEach(function () {
|
||||
state.el.addClass('closed');
|
||||
state.videoCaption.toggle(jQuery.Event('click'));
|
||||
|
||||
jasmine.Clock.useMock();
|
||||
});
|
||||
|
||||
@@ -1039,41 +1098,59 @@
|
||||
|
||||
describe('caption accessibility', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
});
|
||||
|
||||
describe('when getting focus through TAB key', function () {
|
||||
beforeEach(function () {
|
||||
state.videoCaption.isMouseFocus = false;
|
||||
$('.subtitles li[data-index=0]').trigger(
|
||||
jQuery.Event('focus')
|
||||
);
|
||||
runs(function () {
|
||||
state.videoCaption.isMouseFocus = false;
|
||||
$('.subtitles li[data-index=0]').trigger(
|
||||
jQuery.Event('focus')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows an outline around the caption', function () {
|
||||
expect($('.subtitles li[data-index=0]'))
|
||||
.toHaveClass('focused');
|
||||
runs(function () {
|
||||
expect($('.subtitles li[data-index=0]'))
|
||||
.toHaveClass('focused');
|
||||
});
|
||||
});
|
||||
|
||||
it('has automatic scrolling disabled', function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(false);
|
||||
runs(function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when loosing focus through TAB key', function () {
|
||||
beforeEach(function () {
|
||||
$('.subtitles li[data-index=0]').trigger(
|
||||
jQuery.Event('blur')
|
||||
);
|
||||
runs(function () {
|
||||
$('.subtitles li[data-index=0]').trigger(
|
||||
jQuery.Event('blur')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show an outline around the caption', function () {
|
||||
expect($('.subtitles li[data-index=0]'))
|
||||
.not.toHaveClass('focused');
|
||||
runs(function () {
|
||||
expect($('.subtitles li[data-index=0]'))
|
||||
.not.toHaveClass('focused');
|
||||
});
|
||||
});
|
||||
|
||||
it('has automatic scrolling enabled', function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(true);
|
||||
runs(function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1083,20 +1160,26 @@
|
||||
function () {
|
||||
|
||||
beforeEach(function () {
|
||||
state.videoCaption.isMouseFocus = false;
|
||||
$('.subtitles li[data-index=0]')
|
||||
.trigger(jQuery.Event('focus'));
|
||||
$('.subtitles li[data-index=0]')
|
||||
.trigger(jQuery.Event('mousedown'));
|
||||
runs(function () {
|
||||
state.videoCaption.isMouseFocus = false;
|
||||
$('.subtitles li[data-index=0]')
|
||||
.trigger(jQuery.Event('focus'));
|
||||
$('.subtitles li[data-index=0]')
|
||||
.trigger(jQuery.Event('mousedown'));
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show an outline around it', function () {
|
||||
expect($('.subtitles li[data-index=0]'))
|
||||
.not.toHaveClass('focused');
|
||||
runs(function () {
|
||||
expect($('.subtitles li[data-index=0]'))
|
||||
.not.toHaveClass('focused');
|
||||
});
|
||||
});
|
||||
|
||||
it('has automatic scrolling enabled', function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(true);
|
||||
runs(function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1108,29 +1191,37 @@
|
||||
var subDataLiIdx__0, subDataLiIdx__1;
|
||||
|
||||
beforeEach(function () {
|
||||
subDataLiIdx__0 = $('.subtitles li[data-index=0]');
|
||||
subDataLiIdx__1 = $('.subtitles li[data-index=1]');
|
||||
runs(function () {
|
||||
subDataLiIdx__0 = $('.subtitles li[data-index=0]');
|
||||
subDataLiIdx__1 = $('.subtitles li[data-index=1]');
|
||||
|
||||
state.videoCaption.isMouseFocus = false;
|
||||
state.videoCaption.isMouseFocus = false;
|
||||
|
||||
subDataLiIdx__0.trigger(jQuery.Event('focus'));
|
||||
subDataLiIdx__0.trigger(jQuery.Event('blur'));
|
||||
subDataLiIdx__0.trigger(jQuery.Event('focus'));
|
||||
subDataLiIdx__0.trigger(jQuery.Event('blur'));
|
||||
|
||||
state.videoCaption.isMouseFocus = true;
|
||||
state.videoCaption.isMouseFocus = true;
|
||||
|
||||
subDataLiIdx__1.trigger(jQuery.Event('mousedown'));
|
||||
subDataLiIdx__1.trigger(jQuery.Event('mousedown'));
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show an outline around the first', function () {
|
||||
expect(subDataLiIdx__0).not.toHaveClass('focused');
|
||||
runs(function () {
|
||||
expect(subDataLiIdx__0).not.toHaveClass('focused');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show an outline around the second', function () {
|
||||
expect(subDataLiIdx__1).not.toHaveClass('focused');
|
||||
runs(function () {
|
||||
expect(subDataLiIdx__1).not.toHaveClass('focused');
|
||||
});
|
||||
});
|
||||
|
||||
it('has automatic scrolling enabled', function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(true);
|
||||
runs(function () {
|
||||
expect(state.videoCaption.autoScrolling).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
59
common/lib/xmodule/xmodule/js/src/video/00_async_process.js
Normal file
59
common/lib/xmodule/xmodule/js/src/video/00_async_process.js
Normal file
@@ -0,0 +1,59 @@
|
||||
(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));
|
||||
|
||||
65
common/lib/xmodule/xmodule/js/src/video/00_sjson.js
Normal file
65
common/lib/xmodule/xmodule/js/src/video/00_sjson.js
Normal file
@@ -0,0 +1,65 @@
|
||||
(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));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
(function (requirejs, require, define) {
|
||||
(function (define) {
|
||||
|
||||
// VideoCaption module.
|
||||
define(
|
||||
'video/09_video_caption.js',
|
||||
[],
|
||||
function () {
|
||||
['video/00_sjson.js', 'video/00_async_process.js'],
|
||||
function (Sjson, AsyncProcess) {
|
||||
|
||||
/**
|
||||
* @desc VideoCaption module exports a function.
|
||||
@@ -21,16 +21,11 @@ function () {
|
||||
* @returns {undefined}
|
||||
*/
|
||||
return function (state) {
|
||||
var dfd = $.Deferred();
|
||||
|
||||
state.videoCaption = {};
|
||||
|
||||
_makeFunctionsPublic(state);
|
||||
|
||||
state.videoCaption.renderElements();
|
||||
|
||||
dfd.resolve();
|
||||
return dfd.promise();
|
||||
return $.Deferred().resolve().promise();
|
||||
};
|
||||
|
||||
// ***************************************************************
|
||||
@@ -65,10 +60,8 @@ function () {
|
||||
renderCaption: renderCaption,
|
||||
renderElements: renderElements,
|
||||
renderLanguageMenu: renderLanguageMenu,
|
||||
reRenderCaption: reRenderCaption,
|
||||
resize: resize,
|
||||
scrollCaption: scrollCaption,
|
||||
search: search,
|
||||
seekPlayer: seekPlayer,
|
||||
setSubtitlesHeight: setSubtitlesHeight,
|
||||
toggle: toggle,
|
||||
@@ -133,19 +126,47 @@ function () {
|
||||
// mousemove, etc.).
|
||||
function bindHandlers() {
|
||||
var self = this,
|
||||
Caption = this.videoCaption;
|
||||
Caption = this.videoCaption,
|
||||
events = [
|
||||
'mouseover', 'mouseout', 'mousedown', 'click', 'focus', 'blur',
|
||||
'keydown'
|
||||
].join(' ');
|
||||
|
||||
Caption.hideSubtitlesEl.on({
|
||||
'click': Caption.toggle
|
||||
});
|
||||
|
||||
Caption.subtitlesEl.on({
|
||||
mouseenter: Caption.onMouseEnter,
|
||||
mouseleave: Caption.onMouseLeave,
|
||||
mousemove: Caption.onMovement,
|
||||
mousewheel: Caption.onMovement,
|
||||
DOMMouseScroll: Caption.onMovement
|
||||
});
|
||||
Caption.subtitlesEl
|
||||
.on({
|
||||
mouseenter: Caption.onMouseEnter,
|
||||
mouseleave: Caption.onMouseLeave,
|
||||
mousemove: Caption.onMovement,
|
||||
mousewheel: 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) {
|
||||
Caption.container.on({
|
||||
@@ -154,12 +175,6 @@ function () {
|
||||
});
|
||||
}
|
||||
|
||||
this.el.on('speedchange', function () {
|
||||
if (self.isFlashMode()) {
|
||||
Caption.fetchCaption();
|
||||
}
|
||||
});
|
||||
|
||||
if ((this.videoType === 'html5') && (this.config.autohideHtml5)) {
|
||||
Caption.subtitlesEl.on('scroll', this.videoControl.showControls);
|
||||
}
|
||||
@@ -241,7 +256,7 @@ function () {
|
||||
|
||||
if (this.videoType === 'youtube') {
|
||||
data = {
|
||||
videoId: this.youtubeId()
|
||||
videoId: this.youtubeId('1.0')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -251,13 +266,15 @@ function () {
|
||||
url: self.config.transcriptTranslationUrl + '/' + language,
|
||||
notifyOnError: false,
|
||||
data: data,
|
||||
success: function (captions) {
|
||||
Caption.captions = captions.text;
|
||||
Caption.start = captions.start;
|
||||
success: function (response) {
|
||||
Caption.sjson = new Sjson(response);
|
||||
|
||||
var start = Caption.sjson.getStartTimes(),
|
||||
captions = Caption.sjson.getCaptions();
|
||||
|
||||
if (Caption.loaded) {
|
||||
if (Caption.rendered) {
|
||||
Caption.reRenderCaption();
|
||||
Caption.renderCaption(start, captions);
|
||||
Caption.updatePlayTime(self.videoPlayer.currentTime);
|
||||
}
|
||||
} else {
|
||||
@@ -269,7 +286,7 @@ function () {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
Caption.renderCaption();
|
||||
Caption.renderCaption(start, captions);
|
||||
}
|
||||
|
||||
Caption.bindHandlers();
|
||||
@@ -334,7 +351,6 @@ function () {
|
||||
.height(this.videoCaption.bottomSpacingHeight());
|
||||
|
||||
this.videoCaption.scrollCaption();
|
||||
|
||||
this.videoCaption.setSubtitlesHeight();
|
||||
}
|
||||
|
||||
@@ -380,90 +396,53 @@ function () {
|
||||
});
|
||||
}
|
||||
|
||||
function buildCaptions (container, captions, start) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
function buildCaptions (container, start, captions) {
|
||||
var process = function(text, index) {
|
||||
var liEl = $('<li>', {
|
||||
'data-index': index,
|
||||
'data-start': start[index],
|
||||
'tabindex': 0
|
||||
}).html(text);
|
||||
|
||||
$.each(captions, function(index, text) {
|
||||
var liEl = $('<li>');
|
||||
return liEl[0];
|
||||
};
|
||||
|
||||
liEl.html(text);
|
||||
|
||||
liEl.attr({
|
||||
'data-index': index,
|
||||
'data-start': start[index],
|
||||
'tabindex': 0
|
||||
});
|
||||
|
||||
fragment.appendChild(liEl[0]);
|
||||
return AsyncProcess.array(captions, process).done(function (list) {
|
||||
container.append(list);
|
||||
});
|
||||
|
||||
container.append([fragment]);
|
||||
}
|
||||
|
||||
function renderCaption() {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
// Enables or disables automatic scrolling of the captions when the
|
||||
// video is playing. This feature has to be disabled when tabbing
|
||||
// through them as it interferes with that action. Initially, have this
|
||||
// flag enabled as we assume mouse use. Then, if the first caption
|
||||
// (through forward tabbing) or the last caption (through backwards
|
||||
// tabbing) gets the focus, disable that feature. Re-enable it if tabbing
|
||||
// then cycles out of the the captions.
|
||||
Caption.autoScrolling = true;
|
||||
// Keeps track of where the focus is situated in the array of captions.
|
||||
// Used to implement the automatic scrolling behavior and decide if the
|
||||
// outline around a caption has to be hidden or shown on a mouseenter
|
||||
// or mouseleave. Initially, no caption has the focus, set the
|
||||
// index to -1.
|
||||
Caption.currentCaptionIndex = -1;
|
||||
// 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
|
||||
// outline has to be drawn (tabbing) or not (mouse click).
|
||||
Caption.isMouseFocus = false;
|
||||
Caption.addPaddings();
|
||||
Caption.rendered = true;
|
||||
}
|
||||
|
||||
function reRenderCaption() {
|
||||
function renderCaption(start, captions) {
|
||||
var Caption = this.videoCaption;
|
||||
|
||||
Caption.currentIndex = null;
|
||||
var onRender = function () {
|
||||
Caption.addPaddings();
|
||||
// Enables or disables automatic scrolling of the captions when the
|
||||
// video is playing. This feature has to be disabled when tabbing
|
||||
// through them as it interferes with that action. Initially, have
|
||||
// this flag enabled as we assume mouse use. Then, if the first
|
||||
// caption (through forward tabbing) or the last caption (through
|
||||
// backwards tabbing) gets the focus, disable that feature.
|
||||
// Re-enable it if tabbing then cycles out of the the captions.
|
||||
Caption.autoScrolling = true;
|
||||
// Keeps track of where the focus is situated in the array of
|
||||
// captions. Used to implement the automatic scrolling behavior and
|
||||
// decide if the outline around a caption has to be hidden or shown
|
||||
// on a mouseenter or mouseleave. Initially, no caption has the
|
||||
// focus, set the index to -1.
|
||||
Caption.currentCaptionIndex = -1;
|
||||
// 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
|
||||
// outline has to be drawn (tabbing) or not (mouse click).
|
||||
Caption.isMouseFocus = false;
|
||||
Caption.rendered = true;
|
||||
};
|
||||
|
||||
|
||||
Caption.rendered = false;
|
||||
Caption.subtitlesEl.empty();
|
||||
buildCaptions(Caption.subtitlesEl, Caption.captions, Caption.start);
|
||||
Caption.addPaddings();
|
||||
Caption.rendered = true;
|
||||
Caption.setSubtitlesHeight();
|
||||
buildCaptions(Caption.subtitlesEl, start, captions).done(onRender);
|
||||
}
|
||||
|
||||
function addPaddings() {
|
||||
@@ -529,7 +508,7 @@ function () {
|
||||
// off again as it may have been enabled in captionBlur.
|
||||
if (
|
||||
captionIndex <= 1 ||
|
||||
captionIndex >= this.videoCaption.captions.length - 2
|
||||
captionIndex >= this.videoCaption.sjson.getSize() - 2
|
||||
) {
|
||||
this.videoCaption.autoScrolling = false;
|
||||
}
|
||||
@@ -547,7 +526,7 @@ function () {
|
||||
// tabbing back out of the captions or on the last element and tabbing
|
||||
// forward out of the captions.
|
||||
if (captionIndex === 0 ||
|
||||
captionIndex === this.videoCaption.captions.length - 1) {
|
||||
captionIndex === this.videoCaption.sjson.getSize() - 1) {
|
||||
|
||||
this.videoCaption.autoScrolling = true;
|
||||
}
|
||||
@@ -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() {
|
||||
if (this.videoCaption.loaded) {
|
||||
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;
|
||||
@@ -627,20 +581,12 @@ function () {
|
||||
var newIndex;
|
||||
|
||||
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()) {
|
||||
// Total play time changes with speed change. Also there is
|
||||
// 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);
|
||||
time = Time.convert(time, this.speed, '1.0');
|
||||
}
|
||||
|
||||
newIndex = this.videoCaption.search(time);
|
||||
time = Math.round(time * 1000 + 100);
|
||||
newIndex = this.videoCaption.sjson.search(time);
|
||||
|
||||
if (
|
||||
typeof newIndex !== 'undefined' &&
|
||||
@@ -658,39 +604,27 @@ function () {
|
||||
.addClass('current');
|
||||
|
||||
this.videoCaption.currentIndex = newIndex;
|
||||
|
||||
this.videoCaption.scrollCaption();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
// Total play time changes with speed change. Also there is
|
||||
// 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;
|
||||
time = Math.round(Time.convert(time, '1.0', this.speed));
|
||||
}
|
||||
|
||||
this.trigger(
|
||||
'videoPlayer.onCaptionSeek',
|
||||
{
|
||||
'type': 'onCaptionSeek',
|
||||
'time': time
|
||||
'time': time/1000
|
||||
}
|
||||
);
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function calculateOffset(element) {
|
||||
@@ -801,4 +735,4 @@ function () {
|
||||
}
|
||||
});
|
||||
|
||||
}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
|
||||
}(RequireJS.define));
|
||||
|
||||
@@ -72,6 +72,8 @@ class VideoModule(VideoFields, VideoStudentViewHandlers, XModule):
|
||||
'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_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/025_focus_grabber.js'),
|
||||
resource_string(module, 'js/src/video/02_html5_video.js'),
|
||||
|
||||
@@ -111,18 +111,18 @@ Feature: LMS Video component
|
||||
|
||||
# 11
|
||||
Scenario: Language menu works correctly in Video component
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "chinese_transcripts.srt" transcript file in assets
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "Youtube" mode:
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "chinese_transcripts.srt" transcript file in assets
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "Youtube" mode:
|
||||
| transcripts | sub |
|
||||
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM |
|
||||
And I make sure captions are closed
|
||||
And I see video menu "language" with correct items
|
||||
And I select language with code "zh"
|
||||
Then I see "好 各位同学" text in the captions
|
||||
And I select language with code "en"
|
||||
And I see "Hi, welcome to Edx." text in the captions
|
||||
And I make sure captions are closed
|
||||
And I see video menu "language" with correct items
|
||||
And I select language with code "zh"
|
||||
Then I see "好 各位同学" text in the captions
|
||||
And I select language with code "en"
|
||||
And I see "Hi, welcome to Edx." text in the captions
|
||||
|
||||
# 12
|
||||
Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component
|
||||
@@ -237,29 +237,31 @@ Feature: LMS Video component
|
||||
|
||||
# 21
|
||||
Scenario: Download button works correctly for non-english transcript in Youtube mode of Video component
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "chinese_transcripts.srt" transcript file in assets
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "Youtube" mode:
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "chinese_transcripts.srt" transcript file in assets
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "Youtube" mode:
|
||||
| transcripts | sub | download_track |
|
||||
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true |
|
||||
Then I can download transcript in "srt" format that has text "Hi, welcome to Edx."
|
||||
And I select language with code "zh"
|
||||
And I see "好 各位同学" text in the captions
|
||||
Then I can download transcript in "srt" format that has text "好 各位同学"
|
||||
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."
|
||||
And I select language with code "zh"
|
||||
And I see "好 各位同学" text in the captions
|
||||
Then I can download transcript in "srt" format that has text "好 各位同学"
|
||||
|
||||
# 22
|
||||
Scenario: Download button works correctly for non-english transcript in HTML5 mode of Video component
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "chinese_transcripts.srt" transcript file in assets
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "HTML5" mode:
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "chinese_transcripts.srt" transcript file in assets
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "HTML5" mode:
|
||||
| transcripts | sub | download_track |
|
||||
| {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true |
|
||||
Then I can download transcript in "srt" format that has text "Hi, welcome to Edx."
|
||||
And I select language with code "zh"
|
||||
And I see "好 各位同学" text in the captions
|
||||
Then I can download transcript in "srt" format that has text "好 各位同学"
|
||||
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."
|
||||
And I select language with code "zh"
|
||||
And I see "好 各位同学" text in the captions
|
||||
Then I can download transcript in "srt" format that has text "好 各位同学"
|
||||
|
||||
# 23
|
||||
Scenario: Download button works correctly w/o english transcript in HTML5 mode of Video component
|
||||
@@ -298,8 +300,10 @@ Feature: LMS Video component
|
||||
And a video "D" in "Youtube_HTML5" mode in position "3" of sequential
|
||||
And I open the section with videos
|
||||
Then videos have rendered in "HTML5" mode
|
||||
And I see "Hi, welcome to Edx." text in the captions
|
||||
And I see "Equal transcripts" text in the captions
|
||||
And I see text in the captions:
|
||||
| text |
|
||||
| Hi, welcome to Edx. |
|
||||
| Equal transcripts |
|
||||
When I open video "C"
|
||||
Then the video has rendered in "HTML5" mode
|
||||
And I make sure captions are opened
|
||||
|
||||
@@ -240,25 +240,25 @@ def set_window_dimensions(width, height):
|
||||
|
||||
|
||||
def duration():
|
||||
"""
|
||||
Total duration of the video, in seconds.
|
||||
"""
|
||||
elapsed_time, duration = video_time()
|
||||
return duration
|
||||
"""
|
||||
Total duration of the video, in seconds.
|
||||
"""
|
||||
elapsed_time, duration = video_time()
|
||||
return duration
|
||||
|
||||
|
||||
def video_time():
|
||||
"""
|
||||
Return a tuple `(elapsed_time, duration)`, each in seconds.
|
||||
"""
|
||||
# The full time has the form "0:32 / 3:14"
|
||||
full_time = world.css_text('div.vidtime')
|
||||
"""
|
||||
Return a tuple `(elapsed_time, duration)`, each in seconds.
|
||||
"""
|
||||
# The full time has the form "0:32 / 3:14"
|
||||
full_time = world.css_text('div.vidtime')
|
||||
|
||||
# Split the time at the " / ", to get ["0:32", "3:14"]
|
||||
elapsed_str, duration_str = full_time.split(' / ')
|
||||
# Split the time at the " / ", to get ["0:32", "3:14"]
|
||||
elapsed_str, duration_str = full_time.split(' / ')
|
||||
|
||||
# Convert each string to seconds
|
||||
return (parse_time_str(elapsed_str), parse_time_str(duration_str))
|
||||
# Convert each string to seconds
|
||||
return (parse_time_str(elapsed_str), parse_time_str(duration_str))
|
||||
|
||||
|
||||
def parse_time_str(time_str):
|
||||
@@ -316,13 +316,13 @@ def visit_video_section(_step):
|
||||
|
||||
@step('I select the "([^"]*)" speed$')
|
||||
def i_select_video_speed(_step, speed):
|
||||
change_video_speed(speed)
|
||||
change_video_speed(speed)
|
||||
|
||||
|
||||
@step('I select the "([^"]*)" speed on video "([^"]*)"$')
|
||||
def change_video_speed_on_video(_step, speed, player_id):
|
||||
navigate_to_an_item_in_a_sequence(world.video_sequences[player_id])
|
||||
change_video_speed(speed)
|
||||
navigate_to_an_item_in_a_sequence(world.video_sequences[player_id])
|
||||
change_video_speed(speed)
|
||||
|
||||
|
||||
@step('I open video "([^"]*)"$')
|
||||
@@ -419,7 +419,15 @@ def i_see_menu(_step, menu):
|
||||
|
||||
@step('I see "([^"]*)" text in the captions$')
|
||||
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 "([^"]*)"$')
|
||||
@@ -441,14 +449,14 @@ def select_language(_step, code):
|
||||
# Make sure that all ajax requests that affects the display of captions are finished.
|
||||
# For example, request to get new translation etc.
|
||||
world.wait_for_ajax_complete()
|
||||
assert world.css_visible('.subtitles')
|
||||
|
||||
world.wait_for_visible('.subtitles')
|
||||
|
||||
|
||||
@step('I click video button "([^"]*)"$')
|
||||
def click_button(_step, button):
|
||||
world.css_click(VIDEO_BUTTONS[button])
|
||||
|
||||
|
||||
@step('I see video starts playing from "([^"]*)" position$')
|
||||
def start_playing_video_from_n_seconds(_step, position):
|
||||
world.wait_for(
|
||||
@@ -504,9 +512,7 @@ def video_alignment(_step, transcript_visibility):
|
||||
height = abs(expected['height'] - real['height']) <= 5
|
||||
|
||||
# Restore initial window size
|
||||
set_window_dimensions(
|
||||
initial['width'], initial['height']
|
||||
)
|
||||
set_window_dimensions(initial['width'], initial['height'])
|
||||
|
||||
assert all([width, height])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user