Merge pull request #2792 from edx/anton/fix-cc-button
Anton/fix cc button
This commit is contained in:
@@ -75,32 +75,8 @@
|
||||
expect(state.el).toBe('#video_id');
|
||||
});
|
||||
|
||||
it('parse the videos if subtitles exist', function () {
|
||||
var sub = 'Z5KLxerq05Y';
|
||||
|
||||
expect(state.videos).toEqual({
|
||||
'0.75': sub,
|
||||
'1.0': sub,
|
||||
'1.25': sub,
|
||||
'1.50': sub
|
||||
});
|
||||
});
|
||||
|
||||
it(
|
||||
'parse the videos if subtitles do not exist',
|
||||
function ()
|
||||
{
|
||||
var sub = '';
|
||||
|
||||
$('#example').find('.video').data('sub', '');
|
||||
state = new window.Video('#example');
|
||||
|
||||
expect(state.videos).toEqual({
|
||||
'0.75': sub,
|
||||
'1.0': sub,
|
||||
'1.25': sub,
|
||||
'1.50': sub
|
||||
});
|
||||
it('doesn\'t have `videos` dictionary', function () {
|
||||
expect(state.videos).toBeUndefined();
|
||||
});
|
||||
|
||||
it('parse Html5 sources', function () {
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
|
||||
.andReturn(null);
|
||||
|
||||
state = jasmine.initializePlayer();
|
||||
videoControl = state.videoControl;
|
||||
$.fn.scrollTo.reset();
|
||||
});
|
||||
|
||||
@@ -29,18 +27,20 @@
|
||||
describe('always', function () {
|
||||
beforeEach(function () {
|
||||
spyOn($, 'ajaxWithPrefix').andCallThrough();
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
it('create the caption element', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
expect($('.video')).toContain('ol.subtitles');
|
||||
});
|
||||
|
||||
it('add caption control to video player', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
expect($('.video')).toContain('a.hide-subtitles');
|
||||
});
|
||||
|
||||
it('add ARIA attributes to caption control', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
var captionControl = $('a.hide-subtitles');
|
||||
expect(captionControl).toHaveAttrs({
|
||||
'role': 'button',
|
||||
@@ -49,7 +49,11 @@
|
||||
});
|
||||
});
|
||||
|
||||
it('fetch the caption', function () {
|
||||
it('fetch the caption in HTML5 mode', function () {
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayer();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
if (state.videoCaption.loaded === true) {
|
||||
return true;
|
||||
@@ -62,29 +66,62 @@
|
||||
expect($.ajaxWithPrefix).toHaveBeenCalledWith({
|
||||
url: '/transcript/translation',
|
||||
notifyOnError: false,
|
||||
data: {
|
||||
videoId: 'Z5KLxerq05Y',
|
||||
language: 'en'
|
||||
},
|
||||
data: jasmine.any(Object),
|
||||
success: jasmine.any(Function),
|
||||
error: jasmine.any(Function)
|
||||
});
|
||||
expect($.ajaxWithPrefix.mostRecentCall.args[0].data)
|
||||
.toEqual({
|
||||
language: 'en'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('fetch the caption in Youtube mode', function () {
|
||||
runs(function () {
|
||||
state = jasmine.initializePlayerYouTube();
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
if (state.videoCaption.loaded === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, 'Expect captions to be loaded.', WAIT_TIMEOUT);
|
||||
|
||||
runs(function () {
|
||||
expect($.ajaxWithPrefix).toHaveBeenCalledWith({
|
||||
url: '/transcript/translation',
|
||||
notifyOnError: false,
|
||||
data: jasmine.any(Object),
|
||||
success: jasmine.any(Function),
|
||||
error: jasmine.any(Function)
|
||||
});
|
||||
expect($.ajaxWithPrefix.mostRecentCall.args[0].data)
|
||||
.toEqual({
|
||||
language: 'en',
|
||||
videoId: 'abcdefghijkl'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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(
|
||||
'click', state.videoCaption.toggle
|
||||
);
|
||||
});
|
||||
|
||||
it('bind the mouse movement', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
expect($('.subtitles')).toHandleWith(
|
||||
'mouseover', state.videoCaption.onMouseEnter
|
||||
);
|
||||
@@ -103,8 +140,9 @@
|
||||
});
|
||||
|
||||
it('bind the scroll', function () {
|
||||
expect($('.subtitles'))
|
||||
.toHandleWith('scroll', state.videoControl.showControls);
|
||||
state = jasmine.initializePlayer();
|
||||
expect($('.subtitles'))
|
||||
.toHandleWith('scroll', state.videoControl.showControls);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -284,7 +322,8 @@
|
||||
describe('when no captions file was specified', function () {
|
||||
beforeEach(function () {
|
||||
state = jasmine.initializePlayer('video_all.html', {
|
||||
'sub': ''
|
||||
'sub': '',
|
||||
'transcriptLanguages': {},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -395,6 +434,8 @@
|
||||
});
|
||||
|
||||
it('reRenderCaption', function () {
|
||||
state = jasmine.initializePlayer();
|
||||
|
||||
var Caption = state.videoCaption,
|
||||
li;
|
||||
|
||||
@@ -426,14 +467,6 @@
|
||||
spyOn(state, 'youtubeId').andReturn('Z5KLxerq05Y');
|
||||
});
|
||||
|
||||
it('do not fetch captions, if 1.0 speed is absent', function () {
|
||||
state.youtubeId.andReturn(void(0));
|
||||
Caption.fetchCaption();
|
||||
|
||||
expect($.ajaxWithPrefix).not.toHaveBeenCalled();
|
||||
expect(Caption.hideCaptions).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('show caption on language change', function () {
|
||||
Caption.loaded = true;
|
||||
Caption.fetchCaption();
|
||||
|
||||
@@ -45,7 +45,6 @@ function (VideoPlayer) {
|
||||
|
||||
it('create video caption', function () {
|
||||
expect(state.videoCaption).toBeDefined();
|
||||
expect(state.youtubeId('1.0')).toEqual('Z5KLxerq05Y');
|
||||
expect(state.speed).toEqual('1.50');
|
||||
expect(state.config.transcriptTranslationUrl)
|
||||
.toEqual('/transcript/translation');
|
||||
|
||||
@@ -202,12 +202,6 @@ function (VideoPlayer, VideoStorage) {
|
||||
);
|
||||
|
||||
state.speeds = ['0.75', '1.0', '1.25', '1.50'];
|
||||
state.videos = {
|
||||
'0.75': state.config.sub,
|
||||
'1.0': state.config.sub,
|
||||
'1.25': state.config.sub,
|
||||
'1.50': state.config.sub
|
||||
};
|
||||
|
||||
// We must have at least one non-YouTube video source available.
|
||||
// Otherwise, return a negative.
|
||||
|
||||
@@ -461,7 +461,7 @@ function (HTML5Video, Resizer) {
|
||||
this.videoPlayer.log(
|
||||
'pause_video',
|
||||
{
|
||||
'currentTime': this.videoPlayer.currentTime
|
||||
currentTime: this.videoPlayer.currentTime
|
||||
}
|
||||
);
|
||||
|
||||
@@ -482,7 +482,7 @@ function (HTML5Video, Resizer) {
|
||||
this.videoPlayer.log(
|
||||
'play_video',
|
||||
{
|
||||
'currentTime': this.videoPlayer.currentTime
|
||||
currentTime: this.videoPlayer.currentTime
|
||||
}
|
||||
);
|
||||
|
||||
@@ -863,8 +863,7 @@ function (HTML5Video, Resizer) {
|
||||
|
||||
// Default parameters that always get logged.
|
||||
logInfo = {
|
||||
'id': this.id,
|
||||
'code': this.youtubeId()
|
||||
id: this.id
|
||||
};
|
||||
|
||||
// If extra parameters were passed to the log.
|
||||
|
||||
@@ -226,14 +226,10 @@ function () {
|
||||
*/
|
||||
function fetchCaption() {
|
||||
var self = this,
|
||||
Caption = self.videoCaption;
|
||||
// Check whether the captions file was specified. This is the point
|
||||
// where we either stop with the caption panel (so that a white empty
|
||||
// panel to the right of the video will not be shown), or carry on
|
||||
// further.
|
||||
if (!this.youtubeId('1.0')) {
|
||||
return false;
|
||||
}
|
||||
Caption = self.videoCaption,
|
||||
data = {
|
||||
language: this.getCurrentLanguage()
|
||||
};
|
||||
|
||||
if (Caption.loaded) {
|
||||
Caption.hideCaptions(false);
|
||||
@@ -245,15 +241,16 @@ function () {
|
||||
Caption.fetchXHR.abort();
|
||||
}
|
||||
|
||||
if (this.videoType === 'youtube') {
|
||||
data.videoId = this.youtubeId();
|
||||
}
|
||||
|
||||
// Fetch the captions file. If no file was specified, or if an error
|
||||
// occurred, then we hide the captions panel, and the "CC" button
|
||||
Caption.fetchXHR = $.ajaxWithPrefix({
|
||||
url: self.config.transcriptTranslationUrl,
|
||||
notifyOnError: false,
|
||||
data: {
|
||||
videoId: this.youtubeId(),
|
||||
language: this.getCurrentLanguage()
|
||||
},
|
||||
data: data,
|
||||
success: function (captions) {
|
||||
Caption.captions = captions.text;
|
||||
Caption.start = captions.start;
|
||||
|
||||
@@ -10,7 +10,6 @@ in-browser HTML5 video method (when in HTML5 mode).
|
||||
in XML.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
from operator import itemgetter
|
||||
@@ -329,7 +328,7 @@ class VideoModule(VideoFields, XModule):
|
||||
`available_translations`: returns list of languages, for which SRT files exist. For 'en' check if SJSON exists.
|
||||
"""
|
||||
if dispatch == 'translation':
|
||||
if 'language' not in request.GET or 'videoId' not in request.GET:
|
||||
if 'language' not in request.GET:
|
||||
log.info("Invalid /transcript GET parameters.")
|
||||
return Response(status=400)
|
||||
|
||||
@@ -341,7 +340,7 @@ class VideoModule(VideoFields, XModule):
|
||||
self.transcript_language = lang
|
||||
|
||||
try:
|
||||
transcript = self.translation(request.GET.get('videoId'))
|
||||
transcript = self.translation(request.GET.get('videoId', None))
|
||||
except (TranscriptException, NotFoundError) as ex:
|
||||
log.info(ex.message)
|
||||
response = Response(status=404)
|
||||
@@ -390,26 +389,30 @@ class VideoModule(VideoFields, XModule):
|
||||
|
||||
return response
|
||||
|
||||
def translation(self, subs_id):
|
||||
def translation(self, youtube_id):
|
||||
"""
|
||||
This is called to get transcript file for specific language.
|
||||
|
||||
subs_id: str: must be on of: self.sub or one of youtube_ids.
|
||||
youtube_id: str: must be one of youtube_ids or None if HTML video
|
||||
|
||||
Logic flow:
|
||||
|
||||
If english -> give back `sub` subtitles:
|
||||
Return what we have in contentstore for given subs_id,
|
||||
We should not regenerate needed transcripts, if, for example, they present for youtube 1.0 speed,
|
||||
and we need for other speeds. Such generation should be done in transcripts workflow.
|
||||
If non-english:
|
||||
a) extract subs_id from srt file name
|
||||
if non-youtube:
|
||||
b) try to find sjson by subs_id and return if sucessful
|
||||
c) otherwise generate sjson from srt and return it.
|
||||
if youtube:
|
||||
b) try to find sjson by subs_id and return if sucessful
|
||||
c) generate sjson from srt for all youtube speeds
|
||||
If youtube_id doesn't exist, we have a video in HTML5 mode. Otherwise,
|
||||
video video in Youtube or Flash modes.
|
||||
|
||||
if youtube:
|
||||
If english -> give back youtube_id subtitles:
|
||||
Return what we have in contentstore for given youtube_id.
|
||||
If non-english:
|
||||
a) extract youtube_id from srt file name.
|
||||
b) try to find sjson by youtube_id and return if successful.
|
||||
c) generate sjson from srt for all youtube speeds.
|
||||
if non-youtube:
|
||||
If english -> give back `sub` subtitles:
|
||||
Return what we have in contentstore for given subs_if that is stored in self.sub.
|
||||
If non-english:
|
||||
a) try to find previously generated sjson.
|
||||
b) otherwise generate sjson from srt and return it.
|
||||
|
||||
Filenames naming:
|
||||
en: subs_videoid.srt.sjson
|
||||
@@ -418,28 +421,36 @@ class VideoModule(VideoFields, XModule):
|
||||
Raises:
|
||||
NotFoundError if for 'en' subtitles no asset is uploaded.
|
||||
"""
|
||||
if self.transcript_language == 'en':
|
||||
return asset(self.location, subs_id).data
|
||||
|
||||
if not self.youtube_id_1_0: # Non-youtube (HTML5) case:
|
||||
return get_or_create_sjson(self)
|
||||
if youtube_id:
|
||||
# Youtube case:
|
||||
if self.transcript_language == 'en':
|
||||
return asset(self.location, youtube_id).data
|
||||
|
||||
youtube_ids = youtube_speed_dict(self)
|
||||
assert youtube_id in youtube_ids
|
||||
|
||||
try:
|
||||
sjson_transcript = asset(self.location, youtube_id, self.transcript_language).data
|
||||
except (NotFoundError):
|
||||
log.info("Can't find content in storage for %s transcript: generating.", youtube_id)
|
||||
generate_sjson_for_all_speeds(
|
||||
self,
|
||||
self.transcripts[self.transcript_language],
|
||||
{speed: youtube_id for youtube_id, speed in youtube_ids.iteritems()},
|
||||
self.transcript_language
|
||||
)
|
||||
sjson_transcript = asset(self.location, youtube_id, self.transcript_language).data
|
||||
|
||||
return sjson_transcript
|
||||
else:
|
||||
# HTML5 case
|
||||
if self.transcript_language == 'en':
|
||||
return asset(self.location, self.sub).data
|
||||
else:
|
||||
return get_or_create_sjson(self)
|
||||
|
||||
# Youtube case:
|
||||
youtube_ids = youtube_speed_dict(self)
|
||||
assert subs_id in youtube_ids
|
||||
|
||||
try:
|
||||
sjson_transcript = asset(self.location, subs_id, self.transcript_language).data
|
||||
except (NotFoundError):
|
||||
log.info("Can't find content in storage for %s transcript: generating.", subs_id)
|
||||
generate_sjson_for_all_speeds(
|
||||
self,
|
||||
self.transcripts[self.transcript_language],
|
||||
{speed: subs_id for subs_id, speed in youtube_ids.iteritems()},
|
||||
self.transcript_language
|
||||
)
|
||||
sjson_transcript = asset(self.location, subs_id, self.transcript_language).data
|
||||
return sjson_transcript
|
||||
|
||||
|
||||
class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Feature: LMS Video component
|
||||
As a student, I want to view course videos in LMS
|
||||
|
||||
# 0
|
||||
# 1
|
||||
Scenario: Video component stores position correctly when page is reloaded
|
||||
Given the course has a Video component in Youtube mode
|
||||
Then when I view the video it has rendered in Youtube mode
|
||||
@@ -13,51 +13,51 @@ Feature: LMS Video component
|
||||
And I click video button "play"
|
||||
Then I see video starts playing from "0:10" position
|
||||
|
||||
# 1
|
||||
# 2
|
||||
Scenario: Video component is fully rendered in the LMS in HTML5 mode
|
||||
Given the course has a Video component in HTML5 mode
|
||||
Then when I view the video it has rendered in HTML5 mode
|
||||
And all sources are correct
|
||||
|
||||
# 2
|
||||
# 3
|
||||
# Firefox doesn't have HTML5 (only mp4 - fix here)
|
||||
@skip_firefox
|
||||
Scenario: Autoplay is disabled in LMS for a Video component
|
||||
Given the course has a Video component in HTML5 mode
|
||||
Then when I view the video it does not have autoplay enabled
|
||||
|
||||
# 3
|
||||
# 4
|
||||
# Youtube testing
|
||||
Scenario: Video component is fully rendered in the LMS in Youtube mode with HTML5 sources
|
||||
Given youtube server is up and response time is 0.4 seconds
|
||||
And the course has a Video component in Youtube_HTML5 mode
|
||||
Then when I view the video it has rendered in Youtube mode
|
||||
|
||||
# 4
|
||||
# 5
|
||||
Scenario: Video component is not rendered in the LMS in Youtube mode with HTML5 sources
|
||||
Given youtube server is up and response time is 2 seconds
|
||||
And the course has a Video component in Youtube_HTML5 mode
|
||||
Then when I view the video it has rendered in HTML5 mode
|
||||
|
||||
# 5
|
||||
# 6
|
||||
Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources
|
||||
Given youtube server is up and response time is 2 seconds
|
||||
And the course has a Video component in Youtube mode
|
||||
Then when I view the video it has rendered in Youtube mode
|
||||
|
||||
# 6
|
||||
# 7
|
||||
Scenario: Video component is rendered in the LMS in Youtube mode with HTML5 sources that doesn't supported by browser
|
||||
Given youtube server is up and response time is 2 seconds
|
||||
And the course has a Video component in Youtube_HTML5_Unsupported_Video mode
|
||||
Then when I view the video it has rendered in Youtube mode
|
||||
|
||||
# 7
|
||||
# 8
|
||||
Scenario: Video component is rendered in the LMS in HTML5 mode with HTML5 sources that doesn't supported by browser
|
||||
Given the course has a Video component in HTML5_Unsupported_Video mode
|
||||
Then error message is shown
|
||||
And error message has correct text
|
||||
|
||||
# 8
|
||||
# 9
|
||||
Scenario: Video component stores speed correctly when each video is in separate sequence
|
||||
Given I am registered for the course "test_course"
|
||||
And it has a video "A" in "Youtube" mode in position "1" of sequential
|
||||
@@ -79,8 +79,8 @@ Feature: LMS Video component
|
||||
When I open video "C"
|
||||
Then video "C" should start playing at speed "1.0"
|
||||
|
||||
# 9
|
||||
Scenario: Language menu in Video component works correctly
|
||||
# 10
|
||||
Scenario: Language menu works correctly in Video component
|
||||
Given the course has a Video component in Youtube mode:
|
||||
| transcripts | sub |
|
||||
| {"zh": "OEoXaMPEzfM"} | OEoXaMPEzfM |
|
||||
@@ -90,3 +90,42 @@ Feature: LMS Video component
|
||||
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
|
||||
|
||||
# 11
|
||||
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"} |
|
||||
And I make sure captions are opened
|
||||
Then I see "好 各位同学" text in the captions
|
||||
|
||||
# 12
|
||||
Scenario: CC button works correctly only w/ english transcript in HTML5 mode of Video component
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "HTML5" mode:
|
||||
| sub |
|
||||
| OEoXaMPEzfM |
|
||||
And I make sure captions are opened
|
||||
Then I see "Hi, welcome to Edx." text in the captions
|
||||
|
||||
# 13
|
||||
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"} |
|
||||
And I make sure captions are opened
|
||||
Then I see "好 各位同学" text in the captions
|
||||
|
||||
# 14
|
||||
Scenario: CC button works correctly if transcripts and sub fields are empty, but transcript file exists is assets (Youtube mode of Video component)
|
||||
Given I am registered for the course "test_course"
|
||||
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
|
||||
And it has a video in "Youtube" mode
|
||||
And I make sure captions are opened
|
||||
Then I see "Hi, welcome to Edx." text in the captions
|
||||
|
||||
# 15
|
||||
Scenario: CC button is hidden if no translations
|
||||
Given the course has a Video component in Youtube mode
|
||||
Then button "CC" is hidden
|
||||
|
||||
@@ -9,7 +9,6 @@ from django.conf import settings
|
||||
from cache_toolbox.core import del_cached_content
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
import os
|
||||
from functools import partial
|
||||
from xmodule.contentstore.django import contentstore
|
||||
TEST_ROOT = settings.COMMON_TEST_DATA_ROOT
|
||||
LANGUAGES = settings.ALL_LANGUAGES
|
||||
@@ -46,48 +45,6 @@ VIDEO_BUTTONS = {
|
||||
coursenum = 'test_course'
|
||||
sequence = {}
|
||||
|
||||
@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')
|
||||
|
||||
|
||||
@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')
|
||||
|
||||
|
||||
@step('a video "([^"]*)" in "([^"]*)" mode in position "([^"]*)" of sequential(?:\:)?$')
|
||||
def add_video(_step, player_id, player_mode, position):
|
||||
sequence[player_id] = position
|
||||
add_video_to_course(coursenum, player_mode.lower(), _step.hashes, display_name=player_id)
|
||||
|
||||
|
||||
@step('I open the section with videos$')
|
||||
def visit_video_section(_step):
|
||||
visit_scenario_item('SECTION')
|
||||
|
||||
|
||||
@step('I select the "([^"]*)" speed on video "([^"]*)"$')
|
||||
def change_video_speed(_step, speed, player_id):
|
||||
_navigate_to_an_item_in_a_sequence(sequence[player_id])
|
||||
_change_video_speed(speed)
|
||||
|
||||
|
||||
@step('I open video "([^"]*)"$')
|
||||
def open_video(_step, player_id):
|
||||
_navigate_to_an_item_in_a_sequence(sequence[player_id])
|
||||
|
||||
|
||||
@step('video "([^"]*)" should start playing at speed "([^"]*)"$')
|
||||
def check_video_speed(_step, player_id, speed):
|
||||
speed_css = '.speeds p.active'
|
||||
assert world.css_has_text(speed_css, '{0}x'.format(speed))
|
||||
|
||||
|
||||
def add_video_to_course(course, player_mode, hashes, display_name='Video'):
|
||||
category = 'video'
|
||||
@@ -129,16 +86,105 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'):
|
||||
|
||||
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']:
|
||||
_upload_file(kwargs['metadata']['sub'], 'en', world.scenario_dict['COURSE'].location)
|
||||
filename = _get_transcript_filename(kwargs['metadata']['sub'], 'en')
|
||||
_upload_file(filename, course_location)
|
||||
|
||||
for lang, videoId in kwargs['metadata']['transcripts'].items():
|
||||
_upload_file(videoId, lang, world.scenario_dict['COURSE'].location)
|
||||
filename = _get_transcript_filename(videoId, lang)
|
||||
_upload_file(filename, course_location)
|
||||
|
||||
world.scenario_dict['VIDEO'] = world.ItemFactory.create(**kwargs)
|
||||
|
||||
|
||||
def _get_transcript_filename(videoId, lang):
|
||||
if lang == 'en':
|
||||
return 'subs_{0}.srt.sjson'.format(videoId)
|
||||
else:
|
||||
return '{0}_subs_{1}.srt.sjson'.format(lang, videoId)
|
||||
|
||||
|
||||
def _upload_file(filename, location):
|
||||
path = os.path.join(TEST_ROOT, 'uploads/', filename)
|
||||
f = open(os.path.abspath(path))
|
||||
mime_type = "application/json"
|
||||
|
||||
content_location = StaticContent.compute_location(
|
||||
location.org, location.course, filename
|
||||
)
|
||||
content = StaticContent(content_location, filename, mime_type, f.read())
|
||||
contentstore().save(content)
|
||||
del_cached_content(content.location)
|
||||
|
||||
|
||||
def _navigate_to_an_item_in_a_sequence(number):
|
||||
sequence_css = 'a[data-element="{0}"]'.format(number)
|
||||
world.css_click(sequence_css)
|
||||
|
||||
|
||||
def _change_video_speed(speed):
|
||||
world.browser.execute_script("$('.speeds').addClass('open')")
|
||||
speed_css = 'li[data-speed="{0}"] a'.format(speed)
|
||||
world.css_click(speed_css)
|
||||
|
||||
|
||||
def _open_menu(menu):
|
||||
world.browser.execute_script("$('{selector}').parent().addClass('open')".format(
|
||||
selector=VIDEO_MENUS[menu]
|
||||
))
|
||||
|
||||
|
||||
@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')
|
||||
|
||||
|
||||
@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')
|
||||
|
||||
|
||||
@step('a video in "([^"]*)" mode(?:\:)?$')
|
||||
def add_video(_step, player_mode):
|
||||
add_video_to_course(coursenum, player_mode.lower(), _step.hashes)
|
||||
visit_scenario_item('SECTION')
|
||||
|
||||
|
||||
@step('a video "([^"]*)" in "([^"]*)" mode in position "([^"]*)" of sequential(?:\:)?$')
|
||||
def add_video_in_position(_step, player_id, player_mode, position):
|
||||
sequence[player_id] = position
|
||||
add_video_to_course(coursenum, player_mode.lower(), _step.hashes, display_name=player_id)
|
||||
|
||||
|
||||
@step('I open the section with videos$')
|
||||
def visit_video_section(_step):
|
||||
visit_scenario_item('SECTION')
|
||||
|
||||
|
||||
@step('I select the "([^"]*)" speed on video "([^"]*)"$')
|
||||
def change_video_speed(_step, speed, player_id):
|
||||
_navigate_to_an_item_in_a_sequence(sequence[player_id])
|
||||
_change_video_speed(speed)
|
||||
|
||||
|
||||
@step('I open video "([^"]*)"$')
|
||||
def open_video(_step, player_id):
|
||||
_navigate_to_an_item_in_a_sequence(sequence[player_id])
|
||||
|
||||
|
||||
@step('video "([^"]*)" should start playing at speed "([^"]*)"$')
|
||||
def check_video_speed(_step, player_id, speed):
|
||||
speed_css = '.speeds p.active'
|
||||
assert world.css_has_text(speed_css, '{0}x'.format(speed))
|
||||
|
||||
|
||||
@step('youtube server is up and response time is (.*) seconds$')
|
||||
def set_youtube_response_timeout(_step, time):
|
||||
world.youtube.config['time_to_response'] = float(time)
|
||||
@@ -232,47 +278,6 @@ def click_button(_step, button):
|
||||
world.css_find(VIDEO_BUTTONS[button]).click()
|
||||
|
||||
|
||||
def _upload_file(videoId, lang, location):
|
||||
if lang == 'en':
|
||||
filename = 'subs_{0}.srt.sjson'.format(videoId)
|
||||
else:
|
||||
filename = '{0}_subs_{1}.srt.sjson'.format(lang, videoId)
|
||||
|
||||
path = os.path.join(TEST_ROOT, 'uploads/', filename)
|
||||
f = open(os.path.abspath(path))
|
||||
mime_type = "application/json"
|
||||
|
||||
content_location = StaticContent.compute_location(
|
||||
location.org, location.course, filename
|
||||
)
|
||||
|
||||
sc_partial = partial(StaticContent, content_location, filename, mime_type)
|
||||
content = sc_partial(f.read())
|
||||
|
||||
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(
|
||||
content,
|
||||
tempfile_path=None
|
||||
)
|
||||
del_cached_content(thumbnail_location)
|
||||
|
||||
if thumbnail_content is not None:
|
||||
content.thumbnail_location = thumbnail_location
|
||||
|
||||
contentstore().save(content)
|
||||
del_cached_content(content.location)
|
||||
|
||||
|
||||
def _navigate_to_an_item_in_a_sequence(number):
|
||||
sequence_css = 'a[data-element="{0}"]'.format(number)
|
||||
world.css_click(sequence_css)
|
||||
|
||||
|
||||
def _change_video_speed(speed):
|
||||
world.browser.execute_script("$('.speeds').addClass('open')")
|
||||
speed_css = 'li[data-speed="{0}"] a'.format(speed)
|
||||
world.css_click(speed_css)
|
||||
|
||||
|
||||
@step('I click video button "([^"]*)"$')
|
||||
def click_button_video(_step, button_type):
|
||||
world.wait_for_ajax_complete()
|
||||
@@ -295,7 +300,12 @@ def seek_video_to_n_seconds(_step, seconds):
|
||||
world.browser.execute_script(jsCode)
|
||||
|
||||
|
||||
def _open_menu(menu):
|
||||
world.browser.execute_script("$('{selector}').parent().addClass('open')".format(
|
||||
selector=VIDEO_MENUS[menu]
|
||||
))
|
||||
@step('I have a "([^"]*)" transcript file in assets$')
|
||||
def upload_to_assets(_step, filename):
|
||||
_upload_file(filename, world.scenario_dict['COURSE'].location)
|
||||
|
||||
|
||||
@step('button "([^"]*)" is hidden$')
|
||||
def is_hidden_button(_step, button):
|
||||
assert not world.css_visible(VIDEO_BUTTONS[button])
|
||||
|
||||
|
||||
@@ -189,17 +189,22 @@ class TestVideoTranscriptTranslation(TestVideo):
|
||||
# Tests for `translation` dispatch:
|
||||
|
||||
def test_translation_fails(self):
|
||||
# No videoId
|
||||
request = Request.blank('/translation?language=ru')
|
||||
# No language
|
||||
request = Request.blank('/translation')
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertEqual(response.status, '400 Bad Request')
|
||||
|
||||
# No videoId - HTML5 video with language that is not in available languages
|
||||
request = Request.blank('/translation?language=ru')
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertEqual(response.status, '404 Not Found')
|
||||
|
||||
# Language is not in available languages
|
||||
request = Request.blank('/translation?language=ru&videoId=12345')
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertEqual(response.status, '404 Not Found')
|
||||
|
||||
def test_translaton_en_success(self):
|
||||
def test_translaton_en_youtube_success(self):
|
||||
subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
|
||||
good_sjson = _create_file(json.dumps(subs))
|
||||
_upload_sjson_file(good_sjson, self.item_descriptor.location)
|
||||
@@ -210,25 +215,7 @@ class TestVideoTranscriptTranslation(TestVideo):
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertDictEqual(json.loads(response.body), subs)
|
||||
|
||||
def test_translaton_non_en_non_youtube_success(self):
|
||||
subs = {
|
||||
u'end': [100],
|
||||
u'start': [12],
|
||||
u'text': [
|
||||
u'\u041f\u0440\u0438\u0432\u0456\u0442, edX \u0432\u0456\u0442\u0430\u0454 \u0432\u0430\u0441.'
|
||||
]
|
||||
}
|
||||
self.non_en_file.seek(0)
|
||||
_upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
|
||||
subs_id = _get_subs_id(self.non_en_file.name)
|
||||
|
||||
# manually clean youtube_id_1_0, as it has default value
|
||||
self.item.youtube_id_1_0 = ""
|
||||
request = Request.blank('/translation?language=uk&videoId={}'.format(subs_id))
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertDictEqual(json.loads(response.body), subs)
|
||||
|
||||
def test_translation_non_en_youtube(self):
|
||||
def test_translation_non_en_youtube_success(self):
|
||||
subs = {
|
||||
u'end': [100],
|
||||
u'start': [12],
|
||||
@@ -270,6 +257,34 @@ class TestVideoTranscriptTranslation(TestVideo):
|
||||
}
|
||||
self.assertDictEqual(json.loads(response.body), calculated_1_5)
|
||||
|
||||
def test_translaton_en_html5_success(self):
|
||||
subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
|
||||
good_sjson = _create_file(json.dumps(subs))
|
||||
_upload_sjson_file(good_sjson, self.item_descriptor.location)
|
||||
subs_id = _get_subs_id(good_sjson.name)
|
||||
|
||||
self.item.sub = subs_id
|
||||
request = Request.blank('/translation?language=en')
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertDictEqual(json.loads(response.body), subs)
|
||||
|
||||
def test_translaton_non_en_html5_success(self):
|
||||
subs = {
|
||||
u'end': [100],
|
||||
u'start': [12],
|
||||
u'text': [
|
||||
u'\u041f\u0440\u0438\u0432\u0456\u0442, edX \u0432\u0456\u0442\u0430\u0454 \u0432\u0430\u0441.'
|
||||
]
|
||||
}
|
||||
self.non_en_file.seek(0)
|
||||
_upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
|
||||
|
||||
# manually clean youtube_id_1_0, as it has default value
|
||||
self.item.youtube_id_1_0 = ""
|
||||
request = Request.blank('/translation?language=uk')
|
||||
response = self.item.transcript(request=request, dispatch='translation')
|
||||
self.assertDictEqual(json.loads(response.body), subs)
|
||||
|
||||
|
||||
class TestVideoTranscriptsDownload(TestVideo):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user