Commit 58eaaf29 by Anton Stupak

Merge pull request #2813 from edx/anton/fix-transcript-in-full-screen-mode

Fix video positioning in full view mode.
parents c34fef6c fe8e6026
...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Blades: Fix bug when transcript cutting off view in full view mode. BLD-852.
Blades: Show start time or starting position on slider and VCR. BLD-823. Blades: Show start time or starting position on slider and VCR. BLD-823.
Common: Upgraded CodeMirror to 3.21.0 with an accessibility patch applied. Common: Upgraded CodeMirror to 3.21.0 with an accessibility patch applied.
......
...@@ -256,6 +256,11 @@ div.video { ...@@ -256,6 +256,11 @@ div.video {
margin: 0 lh() 0 0; margin: 0 lh() 0 0;
padding: 0; padding: 0;
@media (max-width: 1120px) {
margin-right: lh(.5);
font-size: em(14);
}
li { li {
float: left; float: left;
margin-bottom: 0; margin-bottom: 0;
...@@ -292,11 +297,13 @@ div.video { ...@@ -292,11 +297,13 @@ div.video {
} }
div.vidtime { div.vidtime {
padding-left: lh(.75);
font-weight: bold; font-weight: bold;
line-height: 46px; //height of play pause buttons line-height: 46px; //height of play pause buttons
padding-left: lh(.75);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
padding-left: lh(.75);
@media (max-width: 1120px) {
padding-left: lh(.5);
}
} }
} }
} }
...@@ -389,8 +396,8 @@ div.video { ...@@ -389,8 +396,8 @@ div.video {
.menu{ .menu{
width: 131px; width: 131px;
@media (max-width: 1024px) { @media (max-width: 1120px) {
width: 101px; width: 80px;
} }
} }
...@@ -403,9 +410,9 @@ div.video { ...@@ -403,9 +410,9 @@ div.video {
min-width: 116px; min-width: 116px;
text-indent: 0; text-indent: 0;
@media (max-width: 1024px) { @media (max-width: 1120px) {
min-width: 0; min-width: 0;
width: 86px; width: 60px;
} }
h3 { h3 {
...@@ -418,7 +425,7 @@ div.video { ...@@ -418,7 +425,7 @@ div.video {
text-transform: uppercase; text-transform: uppercase;
color: #999; color: #999;
@media (max-width: 1024px) { @media (max-width: 1120px) {
display: none; display: none;
} }
} }
...@@ -429,7 +436,7 @@ div.video { ...@@ -429,7 +436,7 @@ div.video {
margin-bottom: 0; margin-bottom: 0;
padding: 0 lh(.5) 0 0; padding: 0 lh(.5) 0 0;
@media (max-width: 1024px) { @media (max-width: 1120px) {
padding: 0 lh(.5) 0 lh(.5); padding: 0 lh(.5) 0 lh(.5);
} }
...@@ -676,9 +683,10 @@ div.video { ...@@ -676,9 +683,10 @@ div.video {
vertical-align: middle; vertical-align: middle;
&.closed { &.closed {
ol.subtitles { div.tc-wrapper {
right: -(flex-grid(4)); article.video-wrapper {
width: auto; width: 100%;
}
} }
} }
...@@ -698,17 +706,16 @@ div.video { ...@@ -698,17 +706,16 @@ div.video {
div.tc-wrapper { div.tc-wrapper {
@include clearfix; @include clearfix;
display: table;
width: 100%; width: 100%;
height: 100%; height: 100%;
position: static; position: static;
article.video-wrapper { article.video-wrapper {
width: 100%; height: 100%;
display: table-cell; width: 75%;
vertical-align: middle; vertical-align: middle;
float: none; margin-right: 0;
object, iframe, video{ object, iframe, video{
position: absolute; position: absolute;
...@@ -727,16 +734,12 @@ div.video { ...@@ -727,16 +734,12 @@ div.video {
} }
ol.subtitles { ol.subtitles {
@include box-sizing(border-box);
@include transition(none); @include transition(none);
background: rgba(#000, .8); background: #000;
bottom: 0;
height: 100%; height: 100%;
max-height: 460px; width: 25%;
max-width: flex-grid(3);
padding: lh(); padding: lh();
position: fixed;
right: 0;
top: 0;
visibility: visible; visibility: visible;
li { li {
......
...@@ -240,12 +240,19 @@ ...@@ -240,12 +240,19 @@
'setParams', 'setParams',
'setMode' 'setMode'
], ],
obj = {}; obj = {},
delta = {
add: jasmine.createSpy().andReturn(obj),
substract: jasmine.createSpy().andReturn(obj),
reset: jasmine.createSpy().andReturn(obj)
};
$.each(methods, function (index, method) { $.each(methods, function (index, method) {
obj[method] = jasmine.createSpy(method).andReturn(obj); obj[method] = jasmine.createSpy(method).andReturn(obj);
}); });
obj.delta = delta;
return obj; return obj;
}()); }());
......
...@@ -18,7 +18,7 @@ function (Resizer) { ...@@ -18,7 +18,7 @@ function (Resizer) {
'</div>', '</div>',
'</div>' '</div>'
].join(''), ].join(''),
config, container, element, originalConsoleLog; config, container, element;
beforeEach(function () { beforeEach(function () {
setFixtures(html); setFixtures(html);
...@@ -30,14 +30,9 @@ function (Resizer) { ...@@ -30,14 +30,9 @@ function (Resizer) {
element: element element: element
}; };
originalConsoleLog = window.console.log;
spyOn(console, 'log'); spyOn(console, 'log');
}); });
afterEach(function () {
window.console.log = originalConsoleLog;
});
it('When Initialize without required parameters, log message is shown', it('When Initialize without required parameters, log message is shown',
function () { function () {
new Resizer({ }); new Resizer({ });
...@@ -134,7 +129,7 @@ function (Resizer) { ...@@ -134,7 +129,7 @@ function (Resizer) {
expect(spiesList[0].calls.length).toEqual(1); expect(spiesList[0].calls.length).toEqual(1);
}); });
it('All callbacks are removed', function () { it('all callbacks are removed', function () {
$.each(spiesList, function (index, spy) { $.each(spiesList, function (index, spy) {
resizer.callbacks.add(spy); resizer.callbacks.add(spy);
}); });
...@@ -147,7 +142,7 @@ function (Resizer) { ...@@ -147,7 +142,7 @@ function (Resizer) {
}); });
}); });
it('Specific callback is removed', function () { it('specific callback is removed', function () {
$.each(spiesList, function (index, spy) { $.each(spiesList, function (index, spy) {
resizer.callbacks.add(spy); resizer.callbacks.add(spy);
}); });
...@@ -176,9 +171,86 @@ function (Resizer) { ...@@ -176,9 +171,86 @@ function (Resizer) {
}); });
}); });
}); });
describe('Delta', function () {
var resizer;
beforeEach(function () {
resizer = new Resizer(config);
});
it('adding delta align correctly by height', function () {
var delta = 100,
expectedHeight = container.height() + delta,
realHeight;
resizer
.delta.add(delta, 'height')
.setMode('height');
realHeight = element.height();
expect(realHeight).toBe(expectedHeight);
});
it('adding delta align correctly by width', function () {
var delta = 100,
expectedWidth = container.width() + delta,
realWidth;
resizer
.delta.add(delta, 'width')
.setMode('width');
realWidth = element.width();
expect(realWidth).toBe(expectedWidth);
});
it('substract delta align correctly by height', function () {
var delta = 100,
expectedHeight = container.height() - delta,
realHeight;
resizer
.delta.substract(delta, 'height')
.setMode('height');
realHeight = element.height();
expect(realHeight).toBe(expectedHeight);
});
it('substract delta align correctly by width', function () {
var delta = 100,
expectedWidth = container.width() - delta,
realWidth;
resizer
.delta.substract(delta, 'width')
.setMode('width');
realWidth = element.width();
expect(realWidth).toBe(expectedWidth);
});
it('reset delta', function () {
var delta = 100,
expectedWidth = container.width(),
realWidth;
resizer
.delta.substract(delta, 'width')
.delta.reset()
.setMode('width');
realWidth = element.width();
expect(realWidth).toBe(expectedWidth);
});
});
}); });
}); });
......
...@@ -106,13 +106,6 @@ ...@@ -106,13 +106,6 @@
}); });
}); });
it('bind window resize event', function () {
state = jasmine.initializePlayer();
expect($(window)).toHandleWith(
'resize', state.videoCaption.resize
);
});
it('bind the hide caption button', function () { it('bind the hide caption button', function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
expect($('.hide-subtitles')).toHandleWith( expect($('.hide-subtitles')).toHandleWith(
......
...@@ -549,6 +549,17 @@ ...@@ -549,6 +549,17 @@
}); });
}); });
it('Controls height is actual on switch to fullscreen', function () {
spyOn($.fn, 'height').andCallFake(function (val) {
return _.isUndefined(val) ? 100: this;
});
state = jasmine.initializePlayer();
$(state.el).trigger('fullscreen');
expect(state.videoControl.height).toBe(150);
});
describe('play', function () { describe('play', function () {
beforeEach(function () { beforeEach(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
......
...@@ -711,6 +711,7 @@ function (VideoPlayer) { ...@@ -711,6 +711,7 @@ function (VideoPlayer) {
state.videoEl = $('video, iframe'); state.videoEl = $('video, iframe');
spyOn(state.videoCaption, 'resize').andCallThrough(); spyOn(state.videoCaption, 'resize').andCallThrough();
spyOn($.fn, 'trigger').andCallThrough();
state.videoControl.toggleFullScreen(jQuery.Event('click')); state.videoControl.toggleFullScreen(jQuery.Event('click'));
}); });
...@@ -725,7 +726,8 @@ function (VideoPlayer) { ...@@ -725,7 +726,8 @@ function (VideoPlayer) {
it('tell VideoCaption to resize', function () { it('tell VideoCaption to resize', function () {
expect(state.videoCaption.resize).toHaveBeenCalled(); expect(state.videoCaption.resize).toHaveBeenCalled();
expect(state.resizer.setMode).toHaveBeenCalled(); expect(state.resizer.setMode).toHaveBeenCalledWith('both');
expect(state.resizer.delta.substract).toHaveBeenCalled();
}); });
}); });
...@@ -758,6 +760,7 @@ function (VideoPlayer) { ...@@ -758,6 +760,7 @@ function (VideoPlayer) {
expect(state.videoCaption.resize).toHaveBeenCalled(); expect(state.videoCaption.resize).toHaveBeenCalled();
expect(state.resizer.setMode) expect(state.resizer.setMode)
.toHaveBeenCalledWith('width'); .toHaveBeenCalledWith('width');
expect(state.resizer.delta.reset).toHaveBeenCalled();
}); });
}); });
}); });
......
...@@ -13,20 +13,24 @@ function () { ...@@ -13,20 +13,24 @@ function () {
elementRatio: null elementRatio: null
}, },
callbacksList = [], callbacksList = [],
delta = {
height: 0,
width: 0
},
module = {}, module = {},
mode = null, mode = null,
config; config;
var initialize = function (params) { var initialize = function (params) {
if (config) { if (!config) {
config = $.extend(true, config, params); config = defaults;
} else {
config = $.extend(true, {}, defaults, params);
} }
config = $.extend(true, {}, config, params);
if (!config.element) { if (!config.element) {
console.log( console.log(
'[Video info]: Required parameter `element` is not passed.' 'Required parameter `element` is not passed.'
); );
} }
...@@ -35,8 +39,8 @@ function () { ...@@ -35,8 +39,8 @@ function () {
var getData = function () { var getData = function () {
var container = $(config.container), var container = $(config.container),
containerWidth = container.width(), containerWidth = container.width() + delta.width,
containerHeight = container.height(), containerHeight = container.height() + delta.height,
containerRatio = config.containerRatio, containerRatio = config.containerRatio,
element = $(config.element), element = $(config.element),
...@@ -74,7 +78,6 @@ function () { ...@@ -74,7 +78,6 @@ function () {
default: default:
if (data.containerRatio >= data.elementRatio) { if (data.containerRatio >= data.elementRatio) {
alignByHeightOnly(); alignByHeightOnly();
} else { } else {
alignByWidthOnly(); alignByWidthOnly();
} }
...@@ -142,7 +145,7 @@ function () { ...@@ -142,7 +145,7 @@ function () {
addCallback(decorator); addCallback(decorator);
} else { } else {
console.error('[Video info]: TypeError: Argument is not a function.'); console.error('TypeError: Argument is not a function.');
} }
return module; return module;
...@@ -168,6 +171,29 @@ function () { ...@@ -168,6 +171,29 @@ function () {
} }
}; };
var cleanDelta = function () {
delta['height'] = 0;
delta['width'] = 0;
return module;
};
var addDelta = function (value, side) {
if (_.isNumber(value) && _.isNumber(delta[side])) {
delta[side] += value;
}
return module;
};
var substractDelta = function (value, side) {
if (_.isNumber(value) && _.isNumber(delta[side])) {
delta[side] -= value;
}
return module;
};
initialize.apply(module, arguments); initialize.apply(module, arguments);
return $.extend(true, module, { return $.extend(true, module, {
...@@ -181,6 +207,11 @@ function () { ...@@ -181,6 +207,11 @@ function () {
once: addOnceCallback, once: addOnceCallback,
remove: removeCallback, remove: removeCallback,
removeAll: removeCallbacks removeAll: removeCallbacks
},
delta: {
add: addDelta,
substract: substractDelta,
reset: cleanDelta
} }
}); });
}; };
......
...@@ -221,7 +221,7 @@ function (HTML5Video, Resizer) { ...@@ -221,7 +221,7 @@ function (HTML5Video, Resizer) {
state.resizer = new Resizer({ state.resizer = new Resizer({
element: state.videoEl, element: state.videoEl,
elementRatio: videoWidth/videoHeight, elementRatio: videoWidth/videoHeight,
container: state.videoEl.parent() container: state.container
}) })
.callbacks.once(function() { .callbacks.once(function() {
state.trigger('videoCaption.resize', null); state.trigger('videoCaption.resize', null);
...@@ -235,7 +235,11 @@ function (HTML5Video, Resizer) { ...@@ -235,7 +235,11 @@ function (HTML5Video, Resizer) {
}); });
} }
$(window).bind('resize', _.debounce(state.resizer.align, 100)); $(window).on('resize', _.debounce(function () {
state.trigger('videoControl.updateControlsHeight', null);
state.trigger('videoCaption.resize', null);
state.resizer.align();
}, 100));
} }
// function _restartUsingFlash(state) // function _restartUsingFlash(state)
......
...@@ -40,6 +40,7 @@ function () { ...@@ -40,6 +40,7 @@ function () {
showPlayPlaceholder: showPlayPlaceholder, showPlayPlaceholder: showPlayPlaceholder,
toggleFullScreen: toggleFullScreen, toggleFullScreen: toggleFullScreen,
togglePlayback: togglePlayback, togglePlayback: togglePlayback,
updateControlsHeight: updateControlsHeight,
updateVcrVidTime: updateVcrVidTime updateVcrVidTime: updateVcrVidTime
}; };
...@@ -83,6 +84,8 @@ function () { ...@@ -83,6 +84,8 @@ function () {
'role': 'slider', 'role': 'slider',
'title': gettext('Video slider') 'title': gettext('Video slider')
}); });
state.videoControl.updateControlsHeight();
} }
// function _bindHandlers(state) // function _bindHandlers(state)
...@@ -91,6 +94,23 @@ function () { ...@@ -91,6 +94,23 @@ function () {
function _bindHandlers(state) { function _bindHandlers(state) {
state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback); state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback);
state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen); state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreen);
state.el.on('fullscreen', function (event, isFullScreen) {
var height = state.videoControl.updateControlsHeight();
if (isFullScreen) {
state.resizer
.delta
.substract(height, 'height')
.setMode('both');
} else {
state.resizer
.delta
.reset()
.setMode('width');
}
});
$(document).on('keyup', state.videoControl.exitFullScreen); $(document).on('keyup', state.videoControl.exitFullScreen);
if ((state.videoType === 'html5') && (state.config.autohideHtml5)) { if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
...@@ -110,12 +130,22 @@ function () { ...@@ -110,12 +130,22 @@ function () {
}); });
} }
} }
function _getControlsHeight(control) {
return control.el.height() + 0.5 * control.sliderEl.height();
}
// *************************************************************** // ***************************************************************
// Public functions start here. // Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object. // These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
// The magic private function that makes them available and sets up their context is makeFunctionsPublic(). // The magic private function that makes them available and sets up their context is makeFunctionsPublic().
// *************************************************************** // ***************************************************************
function updateControlsHeight () {
this.videoControl.height = _getControlsHeight(this.videoControl);
return this.videoControl.height;
}
function show() { function show() {
this.videoControl.el.removeClass('is-hidden'); this.videoControl.el.removeClass('is-hidden');
this.el.trigger('controls:show', arguments); this.el.trigger('controls:show', arguments);
...@@ -234,13 +264,6 @@ function () { ...@@ -234,13 +264,6 @@ function () {
this.videoControl.fullScreenState = this.isFullScreen = false; this.videoControl.fullScreenState = this.isFullScreen = false;
fullScreenClassNameEl.removeClass('video-fullscreen'); fullScreenClassNameEl.removeClass('video-fullscreen');
text = gettext('Fill browser'); text = gettext('Fill browser');
this.resizer
.setParams({
container: this.videoEl.parent()
})
.setMode('width');
win.scrollTop(this.scrollPos); win.scrollTop(this.scrollPos);
} else { } else {
this.scrollPos = win.scrollTop(); this.scrollPos = win.scrollTop();
...@@ -248,13 +271,6 @@ function () { ...@@ -248,13 +271,6 @@ function () {
this.videoControl.fullScreenState = this.isFullScreen = true; this.videoControl.fullScreenState = this.isFullScreen = true;
fullScreenClassNameEl.addClass('video-fullscreen'); fullScreenClassNameEl.addClass('video-fullscreen');
text = gettext('Exit full browser'); text = gettext('Exit full browser');
this.resizer
.setParams({
container: window
})
.setMode('both');
} }
this.videoControl.fullScreenEl this.videoControl.fullScreenEl
...@@ -262,6 +278,7 @@ function () { ...@@ -262,6 +278,7 @@ function () {
.text(text); .text(text);
this.trigger('videoCaption.resize', null); this.trigger('videoCaption.resize', null);
this.el.trigger('fullscreen', [this.isFullScreen]);
} }
function exitFullScreen(event) { function exitFullScreen(event) {
......
...@@ -135,7 +135,6 @@ function () { ...@@ -135,7 +135,6 @@ function () {
var self = this, var self = this,
Caption = this.videoCaption; Caption = this.videoCaption;
$(window).bind('resize', Caption.resize);
Caption.hideSubtitlesEl.on({ Caption.hideSubtitlesEl.on({
'click': Caption.toggle 'click': Caption.toggle
}); });
...@@ -754,8 +753,12 @@ function () { ...@@ -754,8 +753,12 @@ function () {
}); });
} }
if (this.resizer && !this.isFullScreen) { if (this.resizer) {
this.resizer.alignByWidthOnly(); if (this.isFullScreen) {
this.resizer.setMode('both');
} else {
this.resizer.alignByWidthOnly();
}
} }
this.videoCaption.setSubtitlesHeight(); this.videoCaption.setSubtitlesHeight();
...@@ -769,17 +772,8 @@ function () { ...@@ -769,17 +772,8 @@ function () {
} }
function captionHeight() { function captionHeight() {
var paddingTop;
if (this.isFullScreen) { if (this.isFullScreen) {
paddingTop = parseInt( return this.container.height() - this.videoControl.height;
this.videoCaption.subtitlesEl.css('padding-top'), 10
);
return $(window).height() -
this.videoControl.el.height() -
0.5 * this.videoControl.sliderEl.height() -
2 * paddingTop;
} else { } else {
return this.container.height(); return this.container.height();
} }
......
...@@ -83,7 +83,7 @@ Feature: LMS Video component ...@@ -83,7 +83,7 @@ Feature: LMS Video component
Scenario: Language menu works correctly in Video component Scenario: Language menu works correctly in Video component
Given the course has a Video component in Youtube mode: Given the course has a Video component in Youtube mode:
| transcripts | sub | | transcripts | sub |
| {"zh": "OEoXaMPEzfM"} | OEoXaMPEzfM | | {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM |
And I make sure captions are closed And I make sure captions are closed
And I see video menu "language" with correct items And I see video menu "language" with correct items
And I select language with code "zh" And I select language with code "zh"
...@@ -95,7 +95,7 @@ Feature: LMS Video component ...@@ -95,7 +95,7 @@ Feature: LMS Video component
Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component
Given the course has a Video component in HTML5 mode: Given the course has a Video component in HTML5 mode:
| transcripts | | transcripts |
| {"zh": "OEoXaMPEzfM"} | | {"zh": "chinese_transcripts.srt"} |
And I make sure captions are opened And I make sure captions are opened
Then I see "好 各位同学" text in the captions Then I see "好 各位同学" text in the captions
...@@ -113,7 +113,7 @@ Feature: LMS Video component ...@@ -113,7 +113,7 @@ Feature: LMS Video component
Scenario: CC button works correctly w/o english transcript in Youtube mode of Video component Scenario: CC button works correctly w/o english transcript in Youtube mode of Video component
Given the course has a Video component in Youtube mode: Given the course has a Video component in Youtube mode:
| transcripts | | transcripts |
| {"zh": "OEoXaMPEzfM"} | | {"zh": "chinese_transcripts.srt"} |
And I make sure captions are opened And I make sure captions are opened
Then I see "好 各位同学" text in the captions Then I see "好 各位同学" text in the captions
...@@ -129,3 +129,29 @@ Feature: LMS Video component ...@@ -129,3 +129,29 @@ Feature: LMS Video component
Scenario: CC button is hidden if no translations Scenario: CC button is hidden if no translations
Given the course has a Video component in Youtube mode Given the course has a Video component in Youtube mode
Then button "CC" is hidden Then button "CC" is hidden
# 16
Scenario: Video is aligned correctly if transcript is visible in fullscreen mode
Given the course has a Video component in HTML5 mode:
| sub |
| OEoXaMPEzfM |
And I make sure captions are opened
And I click video button "fullscreen"
Then I see video aligned correctly with enabled transcript
# 17
Scenario: Video is aligned correctly if transcript is hidden in fullscreen mode
Given the course has a Video component in Youtube mode
And I click video button "fullscreen"
Then I see video aligned correctly without enabled transcript
# 18
Scenario: Video is aligned correctly on transcript toggle in fullscreen mode
Given the course has a Video component in Youtube mode:
| sub |
| OEoXaMPEzfM |
And I make sure captions are opened
And I click video button "fullscreen"
Then I see video aligned correctly with enabled transcript
And I click video button "CC"
Then I see video aligned correctly without enabled transcript
...@@ -21,27 +21,24 @@ HTML5_SOURCES = [ ...@@ -21,27 +21,24 @@ HTML5_SOURCES = [
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.webm', 'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.webm',
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.ogv', 'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.ogv',
] ]
HTML5_SOURCES_INCORRECT = [ HTML5_SOURCES_INCORRECT = [
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.mp99', 'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.mp99',
] ]
VIDEO_BUTTONS = { VIDEO_BUTTONS = {
'CC': '.hide-subtitles', 'CC': '.hide-subtitles',
'volume': '.volume', 'volume': '.volume',
'play': '.video_control.play', 'play': '.video_control.play',
'pause': '.video_control.pause', 'pause': '.video_control.pause',
'fullscreen': '.add-fullscreen',
} }
VIDEO_MENUS = { VIDEO_MENUS = {
'language': '.lang .menu', 'language': '.lang .menu',
'speed': '.speed .menu', 'speed': '.speed .menu',
} }
VIDEO_BUTTONS = {
'CC': '.hide-subtitles',
'volume': '.volume',
'play': '.video_control.play',
'pause': '.video_control.pause',
}
coursenum = 'test_course' coursenum = 'test_course'
sequence = {} sequence = {}
...@@ -83,23 +80,22 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'): ...@@ -83,23 +80,22 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'):
if hashes: if hashes:
kwargs['metadata'].update(hashes[0]) kwargs['metadata'].update(hashes[0])
course_location = world.scenario_dict['COURSE'].location
if 'sub' in kwargs['metadata']:
filename = _get_sjson_filename(kwargs['metadata']['sub'], 'en')
_upload_file(filename, course_location)
if 'transcripts' in kwargs['metadata']: if 'transcripts' in kwargs['metadata']:
kwargs['metadata']['transcripts'] = json.loads(kwargs['metadata']['transcripts']) kwargs['metadata']['transcripts'] = json.loads(kwargs['metadata']['transcripts'])
course_location = world.scenario_dict['COURSE'].location
if 'sub' in kwargs['metadata']:
filename = _get_transcript_filename(kwargs['metadata']['sub'], 'en')
_upload_file(filename, course_location)
for lang, videoId in kwargs['metadata']['transcripts'].items(): for lang, filename in kwargs['metadata']['transcripts'].items():
filename = _get_transcript_filename(videoId, lang)
_upload_file(filename, course_location) _upload_file(filename, course_location)
world.scenario_dict['VIDEO'] = world.ItemFactory.create(**kwargs) world.scenario_dict['VIDEO'] = world.ItemFactory.create(**kwargs)
def _get_transcript_filename(videoId, lang): def _get_sjson_filename(videoId, lang):
if lang == 'en': if lang == 'en':
return 'subs_{0}.srt.sjson'.format(videoId) return 'subs_{0}.srt.sjson'.format(videoId)
else: else:
...@@ -120,7 +116,7 @@ def _upload_file(filename, location): ...@@ -120,7 +116,7 @@ def _upload_file(filename, location):
def _navigate_to_an_item_in_a_sequence(number): def _navigate_to_an_item_in_a_sequence(number):
sequence_css = 'a[data-element="{0}"]'.format(number) sequence_css = '#sequence-list a[data-element="{0}"]'.format(number)
world.css_click(sequence_css) world.css_click(sequence_css)
...@@ -136,6 +132,33 @@ def _open_menu(menu): ...@@ -136,6 +132,33 @@ def _open_menu(menu):
)) ))
def _get_all_dimensions():
video = _get_dimensions('.video-player iframe, .video-player video')
wrapper = _get_dimensions('.tc-wrapper')
controls = _get_dimensions('.video-controls')
progress_slider = _get_dimensions('.video-controls > .slider')
expected = dict(wrapper)
expected['height'] -= controls['height'] + 0.5 * progress_slider['height']
return (video, expected)
def _get_dimensions(selector):
element = world.css_find(selector).first
return element._element.size
def _get_window_dimensions():
return world.browser.driver.get_window_size()
def _set_window_dimensions(width, height):
world.browser.driver.set_window_size(width, height)
# Wait 200 ms when JS finish resizing
world.wait(0.2)
@step('when I view the (.*) it does not have autoplay enabled$') @step('when I view the (.*) it does not have autoplay enabled$')
def does_not_autoplay(_step, video_type): def does_not_autoplay(_step, video_type):
assert(world.css_find('.%s' % video_type)[0]['data-autoplay'] == 'False') assert(world.css_find('.%s' % video_type)[0]['data-autoplay'] == 'False')
...@@ -143,10 +166,7 @@ def does_not_autoplay(_step, video_type): ...@@ -143,10 +166,7 @@ def does_not_autoplay(_step, video_type):
@step('the course has a Video component in (.*) mode(?:\:)?$') @step('the course has a Video component in (.*) mode(?:\:)?$')
def view_video(_step, player_mode): def view_video(_step, player_mode):
i_am_registered_for_the_course(_step, coursenum) i_am_registered_for_the_course(_step, coursenum)
# Make sure we have a video
add_video_to_course(coursenum, player_mode.lower(), _step.hashes) add_video_to_course(coursenum, player_mode.lower(), _step.hashes)
visit_scenario_item('SECTION') visit_scenario_item('SECTION')
...@@ -203,7 +223,7 @@ def video_is_rendered(_step, mode): ...@@ -203,7 +223,7 @@ def video_is_rendered(_step, mode):
@step('all sources are correct$') @step('all sources are correct$')
def all_sources_are_correct(_step): def all_sources_are_correct(_step):
elements = world.css_find('.video video source') elements = world.css_find('.video-player video source')
sources = [source['src'].split('?')[0] for source in elements] sources = [source['src'].split('?')[0] for source in elements]
assert set(sources) == set(HTML5_SOURCES) assert set(sources) == set(HTML5_SOURCES)
...@@ -273,15 +293,8 @@ def select_language(_step, code): ...@@ -273,15 +293,8 @@ def select_language(_step, code):
world.wait_for_ajax_complete() world.wait_for_ajax_complete()
@step('I click on video button "([^"]*)"$')
def click_button(_step, button):
world.css_find(VIDEO_BUTTONS[button]).click()
@step('I click video button "([^"]*)"$') @step('I click video button "([^"]*)"$')
def click_button_video(_step, button_type): def click_button(_step, button):
world.wait_for_ajax_complete()
button = button_type.strip()
world.css_click(VIDEO_BUTTONS[button]) world.css_click(VIDEO_BUTTONS[button])
...@@ -309,3 +322,26 @@ def upload_to_assets(_step, filename): ...@@ -309,3 +322,26 @@ def upload_to_assets(_step, filename):
def is_hidden_button(_step, button): def is_hidden_button(_step, button):
assert not world.css_visible(VIDEO_BUTTONS[button]) assert not world.css_visible(VIDEO_BUTTONS[button])
@step('I see video aligned correctly (with(?:out)?) enabled transcript$')
def video_alignment(_step, transcript_visibility):
# Width of the video container in css equal 75% of window if transcript enabled
wrapper_width = 75 if transcript_visibility == "with" else 100
initial = _get_window_dimensions()
_set_window_dimensions(300, 600)
real, expected = _get_all_dimensions()
width = round(100 * real['width']/expected['width']) == wrapper_width
_set_window_dimensions(600, 300)
real, expected = _get_all_dimensions()
height = abs(expected['height'] - real['height']) <= 5
# Restore initial window size
_set_window_dimensions(
initial['width'], initial['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