Merge pull request #2813 from edx/anton/fix-transcript-in-full-screen-mode
Fix video positioning in full view mode.
This commit is contained in:
@@ -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
|
||||
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.
|
||||
|
||||
Common: Upgraded CodeMirror to 3.21.0 with an accessibility patch applied.
|
||||
|
||||
@@ -256,6 +256,11 @@ div.video {
|
||||
margin: 0 lh() 0 0;
|
||||
padding: 0;
|
||||
|
||||
@media (max-width: 1120px) {
|
||||
margin-right: lh(.5);
|
||||
font-size: em(14);
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
margin-bottom: 0;
|
||||
@@ -292,11 +297,13 @@ div.video {
|
||||
}
|
||||
|
||||
div.vidtime {
|
||||
padding-left: lh(.75);
|
||||
font-weight: bold;
|
||||
line-height: 46px; //height of play pause buttons
|
||||
padding-left: lh(.75);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
padding-left: lh(.75);
|
||||
@media (max-width: 1120px) {
|
||||
padding-left: lh(.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,8 +396,8 @@ div.video {
|
||||
.menu{
|
||||
width: 131px;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
width: 101px;
|
||||
@media (max-width: 1120px) {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,9 +410,9 @@ div.video {
|
||||
min-width: 116px;
|
||||
text-indent: 0;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
@media (max-width: 1120px) {
|
||||
min-width: 0;
|
||||
width: 86px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@@ -418,7 +425,7 @@ div.video {
|
||||
text-transform: uppercase;
|
||||
color: #999;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
@media (max-width: 1120px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -429,7 +436,7 @@ div.video {
|
||||
margin-bottom: 0;
|
||||
padding: 0 lh(.5) 0 0;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
@media (max-width: 1120px) {
|
||||
padding: 0 lh(.5) 0 lh(.5);
|
||||
}
|
||||
|
||||
@@ -676,9 +683,10 @@ div.video {
|
||||
vertical-align: middle;
|
||||
|
||||
&.closed {
|
||||
ol.subtitles {
|
||||
right: -(flex-grid(4));
|
||||
width: auto;
|
||||
div.tc-wrapper {
|
||||
article.video-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,17 +706,16 @@ div.video {
|
||||
|
||||
div.tc-wrapper {
|
||||
@include clearfix;
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
position: static;
|
||||
|
||||
article.video-wrapper {
|
||||
width: 100%;
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
vertical-align: middle;
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
|
||||
object, iframe, video{
|
||||
position: absolute;
|
||||
@@ -727,16 +734,12 @@ div.video {
|
||||
}
|
||||
|
||||
ol.subtitles {
|
||||
@include box-sizing(border-box);
|
||||
@include transition(none);
|
||||
background: rgba(#000, .8);
|
||||
bottom: 0;
|
||||
background: #000;
|
||||
height: 100%;
|
||||
max-height: 460px;
|
||||
max-width: flex-grid(3);
|
||||
width: 25%;
|
||||
padding: lh();
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
visibility: visible;
|
||||
|
||||
li {
|
||||
|
||||
@@ -240,12 +240,19 @@
|
||||
'setParams',
|
||||
'setMode'
|
||||
],
|
||||
obj = {};
|
||||
obj = {},
|
||||
delta = {
|
||||
add: jasmine.createSpy().andReturn(obj),
|
||||
substract: jasmine.createSpy().andReturn(obj),
|
||||
reset: jasmine.createSpy().andReturn(obj)
|
||||
};
|
||||
|
||||
$.each(methods, function (index, method) {
|
||||
obj[method] = jasmine.createSpy(method).andReturn(obj);
|
||||
});
|
||||
|
||||
obj.delta = delta;
|
||||
|
||||
return obj;
|
||||
}());
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ function (Resizer) {
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join(''),
|
||||
config, container, element, originalConsoleLog;
|
||||
config, container, element;
|
||||
|
||||
beforeEach(function () {
|
||||
setFixtures(html);
|
||||
@@ -30,14 +30,9 @@ function (Resizer) {
|
||||
element: element
|
||||
};
|
||||
|
||||
originalConsoleLog = window.console.log;
|
||||
spyOn(console, 'log');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.console.log = originalConsoleLog;
|
||||
});
|
||||
|
||||
it('When Initialize without required parameters, log message is shown',
|
||||
function () {
|
||||
new Resizer({ });
|
||||
@@ -134,7 +129,7 @@ function (Resizer) {
|
||||
expect(spiesList[0].calls.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('All callbacks are removed', function () {
|
||||
it('all callbacks are removed', function () {
|
||||
$.each(spiesList, function (index, spy) {
|
||||
resizer.callbacks.add(spy);
|
||||
});
|
||||
@@ -147,7 +142,7 @@ function (Resizer) {
|
||||
});
|
||||
});
|
||||
|
||||
it('Specific callback is removed', function () {
|
||||
it('specific callback is removed', function () {
|
||||
$.each(spiesList, function (index, spy) {
|
||||
resizer.callbacks.add(spy);
|
||||
});
|
||||
@@ -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 @@
|
||||
});
|
||||
});
|
||||
|
||||
it('bind window resize event', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
expect($(window)).toHandleWith(
|
||||
'resize', state.videoCaption.resize
|
||||
);
|
||||
});
|
||||
|
||||
it('bind the hide caption button', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
expect($('.hide-subtitles')).toHandleWith(
|
||||
|
||||
@@ -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 () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
|
||||
@@ -711,6 +711,7 @@ function (VideoPlayer) {
|
||||
state.videoEl = $('video, iframe');
|
||||
|
||||
spyOn(state.videoCaption, 'resize').andCallThrough();
|
||||
spyOn($.fn, 'trigger').andCallThrough();
|
||||
state.videoControl.toggleFullScreen(jQuery.Event('click'));
|
||||
});
|
||||
|
||||
@@ -725,7 +726,8 @@ function (VideoPlayer) {
|
||||
|
||||
it('tell VideoCaption to resize', function () {
|
||||
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) {
|
||||
expect(state.videoCaption.resize).toHaveBeenCalled();
|
||||
expect(state.resizer.setMode)
|
||||
.toHaveBeenCalledWith('width');
|
||||
expect(state.resizer.delta.reset).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,20 +13,24 @@ function () {
|
||||
elementRatio: null
|
||||
},
|
||||
callbacksList = [],
|
||||
delta = {
|
||||
height: 0,
|
||||
width: 0
|
||||
},
|
||||
module = {},
|
||||
mode = null,
|
||||
config;
|
||||
|
||||
var initialize = function (params) {
|
||||
if (config) {
|
||||
config = $.extend(true, config, params);
|
||||
} else {
|
||||
config = $.extend(true, {}, defaults, params);
|
||||
if (!config) {
|
||||
config = defaults;
|
||||
}
|
||||
|
||||
config = $.extend(true, {}, config, params);
|
||||
|
||||
if (!config.element) {
|
||||
console.log(
|
||||
'[Video info]: Required parameter `element` is not passed.'
|
||||
'Required parameter `element` is not passed.'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,8 +39,8 @@ function () {
|
||||
|
||||
var getData = function () {
|
||||
var container = $(config.container),
|
||||
containerWidth = container.width(),
|
||||
containerHeight = container.height(),
|
||||
containerWidth = container.width() + delta.width,
|
||||
containerHeight = container.height() + delta.height,
|
||||
containerRatio = config.containerRatio,
|
||||
|
||||
element = $(config.element),
|
||||
@@ -74,7 +78,6 @@ function () {
|
||||
default:
|
||||
if (data.containerRatio >= data.elementRatio) {
|
||||
alignByHeightOnly();
|
||||
|
||||
} else {
|
||||
alignByWidthOnly();
|
||||
}
|
||||
@@ -142,7 +145,7 @@ function () {
|
||||
|
||||
addCallback(decorator);
|
||||
} else {
|
||||
console.error('[Video info]: TypeError: Argument is not a function.');
|
||||
console.error('TypeError: Argument is not a function.');
|
||||
}
|
||||
|
||||
return module;
|
||||
@@ -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);
|
||||
|
||||
return $.extend(true, module, {
|
||||
@@ -181,6 +207,11 @@ function () {
|
||||
once: addOnceCallback,
|
||||
remove: removeCallback,
|
||||
removeAll: removeCallbacks
|
||||
},
|
||||
delta: {
|
||||
add: addDelta,
|
||||
substract: substractDelta,
|
||||
reset: cleanDelta
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -221,7 +221,7 @@ function (HTML5Video, Resizer) {
|
||||
state.resizer = new Resizer({
|
||||
element: state.videoEl,
|
||||
elementRatio: videoWidth/videoHeight,
|
||||
container: state.videoEl.parent()
|
||||
container: state.container
|
||||
})
|
||||
.callbacks.once(function() {
|
||||
state.trigger('videoCaption.resize', null);
|
||||
@@ -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)
|
||||
|
||||
@@ -40,6 +40,7 @@ function () {
|
||||
showPlayPlaceholder: showPlayPlaceholder,
|
||||
toggleFullScreen: toggleFullScreen,
|
||||
togglePlayback: togglePlayback,
|
||||
updateControlsHeight: updateControlsHeight,
|
||||
updateVcrVidTime: updateVcrVidTime
|
||||
};
|
||||
|
||||
@@ -83,6 +84,8 @@ function () {
|
||||
'role': 'slider',
|
||||
'title': gettext('Video slider')
|
||||
});
|
||||
|
||||
state.videoControl.updateControlsHeight();
|
||||
}
|
||||
|
||||
// function _bindHandlers(state)
|
||||
@@ -91,6 +94,23 @@ function () {
|
||||
function _bindHandlers(state) {
|
||||
state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback);
|
||||
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);
|
||||
|
||||
if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
|
||||
@@ -110,12 +130,22 @@ function () {
|
||||
});
|
||||
}
|
||||
}
|
||||
function _getControlsHeight(control) {
|
||||
return control.el.height() + 0.5 * control.sliderEl.height();
|
||||
}
|
||||
|
||||
// ***************************************************************
|
||||
// Public functions start here.
|
||||
// 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().
|
||||
// ***************************************************************
|
||||
|
||||
function updateControlsHeight () {
|
||||
this.videoControl.height = _getControlsHeight(this.videoControl);
|
||||
|
||||
return this.videoControl.height;
|
||||
}
|
||||
|
||||
function show() {
|
||||
this.videoControl.el.removeClass('is-hidden');
|
||||
this.el.trigger('controls:show', arguments);
|
||||
@@ -234,13 +264,6 @@ function () {
|
||||
this.videoControl.fullScreenState = this.isFullScreen = false;
|
||||
fullScreenClassNameEl.removeClass('video-fullscreen');
|
||||
text = gettext('Fill browser');
|
||||
|
||||
this.resizer
|
||||
.setParams({
|
||||
container: this.videoEl.parent()
|
||||
})
|
||||
.setMode('width');
|
||||
|
||||
win.scrollTop(this.scrollPos);
|
||||
} else {
|
||||
this.scrollPos = win.scrollTop();
|
||||
@@ -248,13 +271,6 @@ function () {
|
||||
this.videoControl.fullScreenState = this.isFullScreen = true;
|
||||
fullScreenClassNameEl.addClass('video-fullscreen');
|
||||
text = gettext('Exit full browser');
|
||||
|
||||
this.resizer
|
||||
.setParams({
|
||||
container: window
|
||||
})
|
||||
.setMode('both');
|
||||
|
||||
}
|
||||
|
||||
this.videoControl.fullScreenEl
|
||||
@@ -262,6 +278,7 @@ function () {
|
||||
.text(text);
|
||||
|
||||
this.trigger('videoCaption.resize', null);
|
||||
this.el.trigger('fullscreen', [this.isFullScreen]);
|
||||
}
|
||||
|
||||
function exitFullScreen(event) {
|
||||
|
||||
@@ -135,7 +135,6 @@ function () {
|
||||
var self = this,
|
||||
Caption = this.videoCaption;
|
||||
|
||||
$(window).bind('resize', Caption.resize);
|
||||
Caption.hideSubtitlesEl.on({
|
||||
'click': Caption.toggle
|
||||
});
|
||||
@@ -754,8 +753,12 @@ function () {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.resizer && !this.isFullScreen) {
|
||||
this.resizer.alignByWidthOnly();
|
||||
if (this.resizer) {
|
||||
if (this.isFullScreen) {
|
||||
this.resizer.setMode('both');
|
||||
} else {
|
||||
this.resizer.alignByWidthOnly();
|
||||
}
|
||||
}
|
||||
|
||||
this.videoCaption.setSubtitlesHeight();
|
||||
@@ -769,17 +772,8 @@ function () {
|
||||
}
|
||||
|
||||
function captionHeight() {
|
||||
var paddingTop;
|
||||
|
||||
if (this.isFullScreen) {
|
||||
paddingTop = parseInt(
|
||||
this.videoCaption.subtitlesEl.css('padding-top'), 10
|
||||
);
|
||||
|
||||
return $(window).height() -
|
||||
this.videoControl.el.height() -
|
||||
0.5 * this.videoControl.sliderEl.height() -
|
||||
2 * paddingTop;
|
||||
return this.container.height() - this.videoControl.height;
|
||||
} else {
|
||||
return this.container.height();
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ Feature: LMS Video component
|
||||
Scenario: Language menu works correctly in Video component
|
||||
Given the course has a Video component in Youtube mode:
|
||||
| transcripts | sub |
|
||||
| {"zh": "OEoXaMPEzfM"} | OEoXaMPEzfM |
|
||||
| {"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"
|
||||
@@ -95,7 +95,7 @@ Feature: LMS 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:
|
||||
| transcripts |
|
||||
| {"zh": "OEoXaMPEzfM"} |
|
||||
| {"zh": "chinese_transcripts.srt"} |
|
||||
And I make sure captions are opened
|
||||
Then I see "好 各位同学" text in the captions
|
||||
|
||||
@@ -113,7 +113,7 @@ Feature: LMS 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:
|
||||
| transcripts |
|
||||
| {"zh": "OEoXaMPEzfM"} |
|
||||
| {"zh": "chinese_transcripts.srt"} |
|
||||
And I make sure captions are opened
|
||||
Then I see "好 各位同学" text in the captions
|
||||
|
||||
@@ -129,3 +129,29 @@ Feature: LMS Video component
|
||||
Scenario: CC button is hidden if no translations
|
||||
Given the course has a Video component in Youtube mode
|
||||
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,25 +21,22 @@ 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.ogv',
|
||||
]
|
||||
|
||||
HTML5_SOURCES_INCORRECT = [
|
||||
'https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.mp99',
|
||||
]
|
||||
VIDEO_BUTTONS = {
|
||||
'CC': '.hide-subtitles',
|
||||
'volume': '.volume',
|
||||
'play': '.video_control.play',
|
||||
'pause': '.video_control.pause',
|
||||
}
|
||||
VIDEO_MENUS = {
|
||||
'language': '.lang .menu',
|
||||
'speed': '.speed .menu',
|
||||
}
|
||||
|
||||
VIDEO_BUTTONS = {
|
||||
'CC': '.hide-subtitles',
|
||||
'volume': '.volume',
|
||||
'play': '.video_control.play',
|
||||
'pause': '.video_control.pause',
|
||||
'fullscreen': '.add-fullscreen',
|
||||
}
|
||||
|
||||
VIDEO_MENUS = {
|
||||
'language': '.lang .menu',
|
||||
'speed': '.speed .menu',
|
||||
}
|
||||
|
||||
coursenum = 'test_course'
|
||||
@@ -83,23 +80,22 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'):
|
||||
|
||||
if hashes:
|
||||
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']:
|
||||
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():
|
||||
filename = _get_transcript_filename(videoId, lang)
|
||||
for lang, filename in kwargs['metadata']['transcripts'].items():
|
||||
_upload_file(filename, course_location)
|
||||
|
||||
world.scenario_dict['VIDEO'] = world.ItemFactory.create(**kwargs)
|
||||
|
||||
|
||||
def _get_transcript_filename(videoId, lang):
|
||||
def _get_sjson_filename(videoId, lang):
|
||||
if lang == 'en':
|
||||
return 'subs_{0}.srt.sjson'.format(videoId)
|
||||
else:
|
||||
@@ -120,7 +116,7 @@ def _upload_file(filename, location):
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -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$')
|
||||
def does_not_autoplay(_step, video_type):
|
||||
assert(world.css_find('.%s' % video_type)[0]['data-autoplay'] == 'False')
|
||||
@@ -143,10 +166,7 @@ def does_not_autoplay(_step, video_type):
|
||||
|
||||
@step('the course has a Video component in (.*) mode(?:\:)?$')
|
||||
def view_video(_step, player_mode):
|
||||
|
||||
i_am_registered_for_the_course(_step, coursenum)
|
||||
|
||||
# Make sure we have a video
|
||||
add_video_to_course(coursenum, player_mode.lower(), _step.hashes)
|
||||
visit_scenario_item('SECTION')
|
||||
|
||||
@@ -203,7 +223,7 @@ def video_is_rendered(_step, mode):
|
||||
|
||||
@step('all sources are correct$')
|
||||
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]
|
||||
|
||||
assert set(sources) == set(HTML5_SOURCES)
|
||||
@@ -273,15 +293,8 @@ def select_language(_step, code):
|
||||
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 "([^"]*)"$')
|
||||
def click_button_video(_step, button_type):
|
||||
world.wait_for_ajax_complete()
|
||||
button = button_type.strip()
|
||||
def click_button(_step, button):
|
||||
world.css_click(VIDEO_BUTTONS[button])
|
||||
|
||||
|
||||
@@ -309,3 +322,26 @@ def upload_to_assets(_step, filename):
|
||||
def is_hidden_button(_step, 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])
|
||||
|
||||
Reference in New Issue
Block a user