Commit b883d527 by Chris Rodriguez

Adding closed captions (not draggable)

parent dcc86a3b
...@@ -283,7 +283,7 @@ require.config({ ...@@ -283,7 +283,7 @@ require.config({
"osda":{ "osda":{
exports: "osda", exports: "osda",
deps: ["annotator", "annotator-harvardx", "video.dev", "vjs.youtube", "rangeslider", "share-annotator", "richText-annotator", "reply-annotator", "tags-annotator", "flagging-annotator", "grouping-annotator", "diacritic-annotator", "openseadragon", "jquery-Watch", "catch", "handlebars", "URI"] deps: ["annotator", "annotator-harvardx", "video.dev", "vjs.youtube", "rangeslider", "share-annotator", "richText-annotator", "reply-annotator", "tags-annotator", "flagging-annotator", "grouping-annotator", "diacritic-annotator", "openseadragon", "jquery-Watch", "catch", "handlebars", "URI"]
}, }
// end of annotation tool files // end of annotation tool files
} }
}); });
...@@ -247,6 +247,22 @@ html:not('.afontgarde') .icon-fallback-img { ...@@ -247,6 +247,22 @@ html:not('.afontgarde') .icon-fallback-img {
} }
} }
.closed-captions {
position: absolute;
width: 85%;
left: 5%;
top: 70%;
text-align: center;
}
.closed-captions.is-visible {
max-height: ($baseline * 3);
border-radius: ($baseline / 5);
padding: 8px ($baseline / 2) 8px ($baseline * 1.5);
background: rgba(0, 0, 0, .75);
color: $yellow;
}
.video-player { .video-player {
overflow: hidden; overflow: hidden;
min-height: 300px; min-height: 300px;
...@@ -701,39 +717,44 @@ html:not('.afontgarde') .icon-fallback-img { ...@@ -701,39 +717,44 @@ html:not('.afontgarde') .icon-fallback-img {
.subtitles { .subtitles {
@include float(left); @include float(left);
overflow: auto; overflow: auto;
margin: 0;
max-height: 460px; max-height: 460px;
width: flex-grid(3, 9); width: flex-grid(3, 9);
padding: 0; padding: 0;
font-size: 14px; font-size: 14px;
list-style: none;
visibility: visible; visibility: visible;
li { .subtitles-menu {
@extend %ui-fake-link; height: 100%;
margin-bottom: 8px; margin: 0;
border: 0;
padding: 0; padding: 0;
color: #0074b5; // AA compliant list-style: none;
line-height: lh();
&.current { li {
color: #333; @extend %ui-fake-link;
font-weight: 700; margin-bottom: 8px;
} border: 0;
padding: 0;
color: #0074b5; // AA compliant
line-height: lh();
&.focused { &.current {
outline: #000 dotted thin; color: #333;
outline-offset: -1px; font-weight: 700;
} }
&:hover, &.focused {
&:focus { outline: #000 dotted thin;
text-decoration: underline; outline-offset: -1px;
} }
&:hover,
&:focus {
text-decoration: underline;
}
&:empty { &:empty {
margin-bottom: 0; margin-bottom: 0;
}
} }
} }
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<div id="id"></div> <div id="id"></div>
</section> </section>
<div class="video-player-post"></div> <div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden"> <section class="video-controls is-hidden">
<div class="slider"></div> <div class="slider"></div>
<div> <div>
......
...@@ -48,6 +48,12 @@ ...@@ -48,6 +48,12 @@
}); });
}); });
it('adds the captioning control to the video player', function() {
state = jasmine.initializePlayer();
expect($('.video')).toContain('.toggle-captions');
expect($('.video')).toContain('.closed-captions');
});
it('fetch the transcript in HTML5 mode', function () { it('fetch the transcript in HTML5 mode', function () {
runs(function () { runs(function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
...@@ -122,16 +128,16 @@ ...@@ -122,16 +128,16 @@
it('bind the mouse movement', function () { it('bind the mouse movement', function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
expect($('.subtitles')).toHandle('mouseover'); expect($('.subtitles-menu')).toHandle('mouseover');
expect($('.subtitles')).toHandle('mouseout'); expect($('.subtitles-menu')).toHandle('mouseout');
expect($('.subtitles')).toHandle('mousemove'); expect($('.subtitles-menu')).toHandle('mousemove');
expect($('.subtitles')).toHandle('mousewheel'); expect($('.subtitles-menu')).toHandle('mousewheel');
expect($('.subtitles')).toHandle('DOMMouseScroll'); expect($('.subtitles-menu')).toHandle('DOMMouseScroll');
}); });
it('bind the scroll', function () { it('bind the scroll', function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
expect($('.subtitles')) expect($('.subtitles-menu'))
.toHandleWith('scroll', state.videoControl.showControls); .toHandleWith('scroll', state.videoControl.showControls);
}); });
...@@ -158,14 +164,61 @@ ...@@ -158,14 +164,61 @@
}); });
}); });
describe('renderCaptions', function() {
describe('is rendered', function() {
var KEY = $.ui.keyCode,
keyPressEvent = function(key) {
return $.Event('keydown', { keyCode: key });
};
it('toggles the captions on control click', function() {
state = jasmine.initializePlayer();
$('.toggle-captions').click();
expect($('.toggle-captions')).toHaveClass('is-active');
expect($('.closed-captions')).toHaveClass('is-visible');
$('.toggle-captions').click();
expect($('.toggle-captions')).not.toHaveClass('is-active');
expect($('.closed-captions')).not.toHaveClass('is-visible');
});
it('toggles the captions on keypress ENTER', function() {
state = jasmine.initializePlayer();
$('.toggle-captions').focus().trigger(keyPressEvent(KEY.ENTER));
expect($('.toggle-captions')).toHaveClass('is-active');
expect($('.closed-captions')).toHaveClass('is-visible');
$('.toggle-captions').focus().trigger(keyPressEvent(KEY.ENTER));
expect($('.toggle-captions')).not.toHaveClass('is-active');
expect($('.closed-captions')).not.toHaveClass('is-visible');
});
it('toggles the captions on keypress SPACE', function() {
state = jasmine.initializePlayer();
$('.toggle-captions').focus().trigger(keyPressEvent(KEY.SPACE));
expect($('.toggle-captions')).toHaveClass('is-active');
expect($('.closed-captions')).toHaveClass('is-visible');
$('.toggle-captions').focus().trigger(keyPressEvent(KEY.SPACE));
expect($('.toggle-captions')).not.toHaveClass('is-active');
expect($('.closed-captions')).not.toHaveClass('is-visible');
});
});
});
describe('renderLanguageMenu', function () { describe('renderLanguageMenu', function () {
describe('is rendered', function () { describe('is rendered', function () {
var KEY = $.ui.keyCode, var KEY = $.ui.keyCode,
keyPressEvent = function(key) { keyPressEvent = function(key) {
return $.Event('keydown', { keyCode: key }); return $.Event('keydown', { keyCode: key });
}; };
it('if languages more than 1', function () { it('if languages more than 1', function () {
state = jasmine.initializePlayer(); state = jasmine.initializePlayer();
...@@ -364,9 +417,8 @@ ...@@ -364,9 +417,8 @@
}); });
it('show explanation message', function () { it('show explanation message', function () {
expect($('.subtitles li')).toHaveHtml( expect($('.subtitles-menu li')).toHaveHtml(
'Caption will be displayed when you start playing ' + 'Transcript will be displayed when you start playing the video.'
'the video.'
); );
}); });
...@@ -444,7 +496,7 @@ ...@@ -444,7 +496,7 @@
runs(function () { runs(function () {
$(window).trigger(jQuery.Event('mousemove')); $(window).trigger(jQuery.Event('mousemove'));
jasmine.Clock.tick(state.config.captionsFreezeTime); jasmine.Clock.tick(state.config.captionsFreezeTime);
$('.subtitles').trigger(jQuery.Event('mouseenter')); $('.subtitles-menu').trigger(jQuery.Event('mouseenter'));
jasmine.Clock.tick(state.config.captionsFreezeTime); jasmine.Clock.tick(state.config.captionsFreezeTime);
}); });
}); });
...@@ -459,7 +511,7 @@ ...@@ -459,7 +511,7 @@
describe('when the cursor is moving', function () { describe('when the cursor is moving', function () {
it('reset the freezing timeout', function () { it('reset the freezing timeout', function () {
runs(function () { runs(function () {
$('.subtitles').trigger(jQuery.Event('mousemove')); $('.subtitles-menu').trigger(jQuery.Event('mousemove'));
expect(window.clearTimeout).toHaveBeenCalled(); expect(window.clearTimeout).toHaveBeenCalled();
}); });
}); });
...@@ -468,7 +520,7 @@ ...@@ -468,7 +520,7 @@
describe('when the mouse is scrolling', function () { describe('when the mouse is scrolling', function () {
it('reset the freezing timeout', function () { it('reset the freezing timeout', function () {
runs(function () { runs(function () {
$('.subtitles').trigger(jQuery.Event('mousewheel')); $('.subtitles-menu').trigger(jQuery.Event('mousewheel'));
expect(window.clearTimeout).toHaveBeenCalled(); expect(window.clearTimeout).toHaveBeenCalled();
}); });
}); });
...@@ -486,7 +538,7 @@ ...@@ -486,7 +538,7 @@
describe('always', function () { describe('always', function () {
beforeEach(function () { beforeEach(function () {
$('.subtitles').trigger(jQuery.Event('mouseout')); $('.subtitles-menu').trigger(jQuery.Event('mouseout'));
}); });
it('reset the freezing timeout', function () { it('reset the freezing timeout', function () {
...@@ -501,9 +553,9 @@ ...@@ -501,9 +553,9 @@
describe('when the player is playing', function () { describe('when the player is playing', function () {
beforeEach(function () { beforeEach(function () {
state.videoCaption.playing = true; state.videoCaption.playing = true;
$('.subtitles li[data-index]:first') $('.subtitles-menu li[data-index]:first')
.addClass('current'); .addClass('current');
$('.subtitles').trigger(jQuery.Event('mouseout')); $('.subtitles-menu').trigger(jQuery.Event('mouseout'));
}); });
it('scroll the transcript', function () { it('scroll the transcript', function () {
...@@ -514,7 +566,7 @@ ...@@ -514,7 +566,7 @@
describe('when the player is not playing', function () { describe('when the player is not playing', function () {
beforeEach(function () { beforeEach(function () {
state.videoCaption.playing = false; state.videoCaption.playing = false;
$('.subtitles').trigger(jQuery.Event('mouseout')); $('.subtitles-menu').trigger(jQuery.Event('mouseout'));
}); });
it('does not scroll the transcript', function () { it('does not scroll the transcript', function () {
......
...@@ -92,7 +92,8 @@ function (VideoPlayer) { ...@@ -92,7 +92,8 @@ function (VideoPlayer) {
showinfo: 0, showinfo: 0,
enablejsapi: 1, enablejsapi: 1,
modestbranding: 1, modestbranding: 1,
html5: 1 html5: 1,
cc_load_policy: 0
}, },
videoId: 'cogebirgzzM', videoId: 'cogebirgzzM',
events: events events: events
...@@ -118,7 +119,8 @@ function (VideoPlayer) { ...@@ -118,7 +119,8 @@ function (VideoPlayer) {
rel: 0, rel: 0,
showinfo: 0, showinfo: 0,
enablejsapi: 1, enablejsapi: 1,
modestbranding: 1 modestbranding: 1,
cc_load_policy: 0
}, },
videoId: 'abcdefghijkl', videoId: 'abcdefghijkl',
events: jasmine.any(Object) events: jasmine.any(Object)
......
...@@ -139,7 +139,8 @@ function (HTML5Video, Resizer) { ...@@ -139,7 +139,8 @@ function (HTML5Video, Resizer) {
rel: 0, rel: 0,
showinfo: 0, showinfo: 0,
enablejsapi: 1, enablejsapi: 1,
modestbranding: 1 modestbranding: 1,
cc_load_policy: 0
}; };
if (!state.isFlashMode()) { if (!state.isFlashMode()) {
......
...@@ -16,6 +16,7 @@ log = logging.getLogger('VideoPage') ...@@ -16,6 +16,7 @@ log = logging.getLogger('VideoPage')
VIDEO_BUTTONS = { VIDEO_BUTTONS = {
'transcript': '.lang', 'transcript': '.lang',
'transcript_button': '.toggle-transcript', 'transcript_button': '.toggle-transcript',
'cc_button': '.toggle-captions',
'volume': '.volume', 'volume': '.volume',
'play': '.video_control.play', 'play': '.video_control.play',
'pause': '.video_control.pause', 'pause': '.video_control.pause',
...@@ -28,10 +29,12 @@ VIDEO_BUTTONS = { ...@@ -28,10 +29,12 @@ VIDEO_BUTTONS = {
} }
CSS_CLASS_NAMES = { CSS_CLASS_NAMES = {
'closed_captions': '.video.closed', 'captions_closed': '.video.closed',
'captions_rendered': '.video.is-captions-rendered', 'captions_rendered': '.video.is-captions-rendered',
'captions': '.subtitles', 'captions': '.subtitles',
'captions_text': '.subtitles > li', 'captions_text': '.subtitles li',
'captions_text_getter': '.subtitles li[role="link"][data-index="1"]',
'closed_captions': '.closed-captions',
'error_message': '.video .video-player h3', 'error_message': '.video .video-player h3',
'video_container': '.video', 'video_container': '.video',
'video_sources': '.video-player video source', 'video_sources': '.video-player video source',
...@@ -293,6 +296,18 @@ class VideoPage(PageObject): ...@@ -293,6 +296,18 @@ class VideoPage(PageObject):
""" """
self._captions_visibility(False) self._captions_visibility(False)
def show_closed_captions(self):
"""
Make closed captions visible.
"""
self._closed_captions_visibility(True)
def hide_closed_captions(self):
"""
Make closed captions invisible.
"""
self._closed_captions_visibility(False)
def is_captions_visible(self): def is_captions_visible(self):
""" """
Get current visibility sate of captions. Get current visibility sate of captions.
...@@ -302,8 +317,20 @@ class VideoPage(PageObject): ...@@ -302,8 +317,20 @@ class VideoPage(PageObject):
""" """
self.wait_for_ajax() self.wait_for_ajax()
caption_state_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions']) caption_state_selector = self.get_element_selector(CSS_CLASS_NAMES['captions'])
return not self.q(css=caption_state_selector).present return self.q(css=caption_state_selector).visible
def is_closed_captions_visible(self):
"""
Get current visibility sate of closed captions.
Returns:
bool: True means captions are visible, False means captions are not visible
"""
self.wait_for_ajax()
closed_caption_state_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
return self.q(css=closed_caption_state_selector).visible
@wait_for_js @wait_for_js
def _captions_visibility(self, captions_new_state): def _captions_visibility(self, captions_new_state):
...@@ -327,7 +354,24 @@ class VideoPage(PageObject): ...@@ -327,7 +354,24 @@ class VideoPage(PageObject):
# Verify that captions state is toggled/changed # Verify that captions state is toggled/changed
EmptyPromise(lambda: self.is_captions_visible() == captions_new_state, EmptyPromise(lambda: self.is_captions_visible() == captions_new_state,
"Captions are {state}".format(state=state)).fulfill() "Transcripts are {state}".format(state=state)).fulfill()
@wait_for_js
def _closed_captions_visibility(self, closed_captions_new_state):
"""
Set the video closed captioning visibility state.
Arguments:
closed_captions_new_state (bool): True means show closed captioning
"""
states = {True: 'shown', False: 'hidden'}
state = states[closed_captions_new_state]
self.click_player_button('cc_button')
# Make sure that the captions are visible
EmptyPromise(lambda: self.is_closed_captions_visible() == closed_captions_new_state,
"Closed captions are {state}".format(state=state)).fulfill()
@property @property
def captions_text(self): def captions_text(self):
...@@ -346,6 +390,31 @@ class VideoPage(PageObject): ...@@ -346,6 +390,31 @@ class VideoPage(PageObject):
return ' '.join(subs) return ' '.join(subs)
@property @property
def closed_captions_text(self):
"""
Extract closed captioning text.
Returns:
str: closed captions Text.
"""
self.wait_for_closed_captions()
closed_captions_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
subs = self.q(css=closed_captions_selector).html
return ' '.join(subs)
def click_first_line_in_transcript(self):
"""
Clicks a line in the transcript updating the current caption.
"""
self.wait_for_captions()
captions_selector = self.q(css=CSS_CLASS_NAMES['captions_text_getter'])
captions_selector.click()
@property
def speed(self): def speed(self):
""" """
Get current video speed value. Get current video speed value.
...@@ -494,6 +563,12 @@ class VideoPage(PageObject): ...@@ -494,6 +563,12 @@ class VideoPage(PageObject):
response = requests.get(url, **kwargs) response = requests.get(url, **kwargs)
return response.status_code < 400, response.headers, response.content return response.status_code < 400, response.headers, response.content
def get_cookie(self, cookie_name):
"""
Searches for and returns `cookie_name`
"""
return self.browser.get_cookie(cookie_name)
def downloaded_transcript_contains_text(self, transcript_format, text_to_search): def downloaded_transcript_contains_text(self, transcript_format, text_to_search):
""" """
Download the transcript in format `transcript_format` and check that it contains the text `text_to_search` Download the transcript in format `transcript_format` and check that it contains the text `text_to_search`
...@@ -837,6 +912,20 @@ class VideoPage(PageObject): ...@@ -837,6 +912,20 @@ class VideoPage(PageObject):
captions_rendered_selector = self.get_element_selector(CSS_CLASS_NAMES['captions_rendered']) captions_rendered_selector = self.get_element_selector(CSS_CLASS_NAMES['captions_rendered'])
self.wait_for_element_presence(captions_rendered_selector, 'Captions Rendered') self.wait_for_element_presence(captions_rendered_selector, 'Captions Rendered')
def wait_for_closed_captions(self):
"""
Wait until closed captions are rendered completely.
"""
cc_rendered_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
self.wait_for_element_visibility(cc_rendered_selector, 'Closed captions rendered')
def wait_for_closed_captions_to_be_hidden(self):
"""
Waits for the closed captions to be turned off completely.
"""
cc_rendered_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
self.wait_for_element_invisibility(cc_rendered_selector, 'Closed captions hidden')
def _parse_time_str(time_str): def _parse_time_str(time_str):
""" """
......
...@@ -13,11 +13,11 @@ from selenium.webdriver.common.keys import Keys ...@@ -13,11 +13,11 @@ from selenium.webdriver.common.keys import Keys
CLASS_SELECTORS = { CLASS_SELECTORS = {
'video_container': 'div.video', 'video_container': '.video',
'video_init': '.is-initialized', 'video_init': '.is-initialized',
'video_xmodule': '.xmodule_VideoModule', 'video_xmodule': '.xmodule_VideoModule',
'video_spinner': '.video-wrapper .spinner', 'video_spinner': '.video-wrapper .spinner',
'video_controls': 'section.video-controls', 'video_controls': '.video-controls',
'attach_asset': '.upload-dialog > input[type="file"]', 'attach_asset': '.upload-dialog > input[type="file"]',
'upload_dialog': '.wrapper-modal-window-assetupload', 'upload_dialog': '.wrapper-modal-window-assetupload',
'xblock': '.add-xblock-component', 'xblock': '.add-xblock-component',
...@@ -264,7 +264,7 @@ class VideoComponentPage(VideoPage): ...@@ -264,7 +264,7 @@ class VideoComponentPage(VideoPage):
line_number (int): caption line number line_number (int): caption line number
""" """
caption_line_selector = ".subtitles > li[data-index='{index}']".format(index=line_number - 1) caption_line_selector = ".subtitles li[data-index='{index}']".format(index=line_number - 1)
self.q(css=caption_line_selector).results[0].send_keys(Keys.ENTER) self.q(css=caption_line_selector).results[0].send_keys(Keys.ENTER)
def is_caption_line_focused(self, line_number): def is_caption_line_focused(self, line_number):
...@@ -275,7 +275,7 @@ class VideoComponentPage(VideoPage): ...@@ -275,7 +275,7 @@ class VideoComponentPage(VideoPage):
line_number (int): caption line number line_number (int): caption line number
""" """
caption_line_selector = ".subtitles > li[data-index='{index}']".format(index=line_number - 1) caption_line_selector = ".subtitles li[data-index='{index}']".format(index=line_number - 1)
attributes = self.q(css=caption_line_selector).attrs('class') attributes = self.q(css=caption_line_selector).attrs('class')
return 'focused' in attributes return 'focused' in attributes
...@@ -504,7 +504,7 @@ class VideoComponentPage(VideoPage): ...@@ -504,7 +504,7 @@ class VideoComponentPage(VideoPage):
As all the captions lines are exactly same so only getting partial lines will work. As all the captions lines are exactly same so only getting partial lines will work.
""" """
self.wait_for_captions() self.wait_for_captions()
selector = '.subtitles > li:nth-child({})' selector = '.subtitles li:nth-child({})'
return ' '.join([self.q(css=selector.format(i)).text[0] for i in range(1, 6)]) return ' '.join([self.q(css=selector.format(i)).text[0] for i in range(1, 6)])
def set_url_field(self, url, field_number): def set_url_field(self, url, field_number):
......
...@@ -142,7 +142,7 @@ class VideoEditorTest(CMSVideoBaseTest): ...@@ -142,7 +142,7 @@ class VideoEditorTest(CMSVideoBaseTest):
self.open_advanced_tab() self.open_advanced_tab()
self.video.upload_translation('1mb_transcripts.srt', 'uk') self.video.upload_translation('1mb_transcripts.srt', 'uk')
self.save_unit_settings() self.save_unit_settings()
self.assertTrue(self.video.is_captions_visible()) self.video.wait_for(self.video.is_captions_visible, 'Captions are visible', timeout=10)
unicode_text = "Привіт, edX вітає вас.".decode('utf-8') unicode_text = "Привіт, edX вітає вас.".decode('utf-8')
self.assertIn(unicode_text, self.video.captions_lines()) self.assertIn(unicode_text, self.video.captions_lines())
......
...@@ -255,7 +255,6 @@ class CMSVideoTest(CMSVideoBaseTest): ...@@ -255,7 +255,6 @@ class CMSVideoTest(CMSVideoBaseTest):
Then when I view the video it does show the captions Then when I view the video it does show the captions
""" """
self._create_course_unit(subtitles=True) self._create_course_unit(subtitles=True)
self.assertTrue(self.video.is_captions_visible()) self.assertTrue(self.video.is_captions_visible())
def test_captions_toggling(self): def test_captions_toggling(self):
......
...@@ -212,9 +212,9 @@ class YouTubeVideoTest(VideoBaseTest): ...@@ -212,9 +212,9 @@ class YouTubeVideoTest(VideoBaseTest):
# Verify that video has rendered in "Youtube" mode # Verify that video has rendered in "Youtube" mode
self.assertTrue(self.video.is_video_rendered('youtube')) self.assertTrue(self.video.is_video_rendered('youtube'))
def test_cc_button_wo_english_transcript(self): def test_transcript_button_wo_english_transcript(self):
""" """
Scenario: CC button works correctly w/o english transcript in Youtube mode Scenario: Transcript button works correctly w/o english transcript in Youtube mode
Given the course has a Video component in "Youtube" mode Given the course has a Video component in "Youtube" mode
And I have defined a non-english transcript for the video And I have defined a non-english transcript for the video
And I have uploaded a non-english transcript file to assets And I have uploaded a non-english transcript file to assets
...@@ -226,13 +226,38 @@ class YouTubeVideoTest(VideoBaseTest): ...@@ -226,13 +226,38 @@ class YouTubeVideoTest(VideoBaseTest):
self.navigate_to_video() self.navigate_to_video()
self.video.show_captions() self.video.show_captions()
# Verify that we see "好 各位同学" text in the captions # Verify that we see "好 各位同学" text in the transcript
unicode_text = "好 各位同学".decode('utf-8') unicode_text = "好 各位同学".decode('utf-8')
self.assertIn(unicode_text, self.video.captions_text) self.assertIn(unicode_text, self.video.captions_text)
def test_cc_button_transcripts_and_sub_fields_empty(self): def test_cc_button(self):
"""
Scenario: CC button works correctly with transcript in YouTube mode
Given the course has a video component in "Youtube" mode
And I have defined a transcript for the video
Then I see the closed captioning element over the video
""" """
Scenario: CC button works correctly if transcripts and sub fields are empty, data = {'transcripts': {'zh': 'chinese_transcripts.srt'}}
self.metadata = self.metadata_for_mode('youtube', data)
self.assets.append('chinese_transcripts.srt')
self.navigate_to_video()
# Show captions and make sure they're visible and cookie is set
self.video.show_closed_captions()
self.video.wait_for_closed_captions()
self.assertTrue(self.video.is_closed_captions_visible)
self.video.reload_page()
self.assertTrue(self.video.is_closed_captions_visible)
# Hide captions and make sure they're hidden and cookie is unset
self.video.hide_closed_captions()
self.video.wait_for_closed_captions_to_be_hidden()
self.video.reload_page()
self.video.wait_for_closed_captions_to_be_hidden()
def test_transcript_button_transcripts_and_sub_fields_empty(self):
"""
Scenario: Transcript button works correctly if transcripts and sub fields are empty,
but transcript file exists in assets (Youtube mode of Video component) but transcript file exists in assets (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
And I have uploaded a .srt.sjson file to assets And I have uploaded a .srt.sjson file to assets
...@@ -247,11 +272,11 @@ class YouTubeVideoTest(VideoBaseTest): ...@@ -247,11 +272,11 @@ class YouTubeVideoTest(VideoBaseTest):
# Verify that we see "Welcome to edX." text in the captions # Verify that we see "Welcome to edX." text in the captions
self.assertIn('Welcome to edX.', self.video.captions_text) self.assertIn('Welcome to edX.', self.video.captions_text)
def test_cc_button_hidden_no_translations(self): def test_transcript_button_hidden_no_translations(self):
""" """
Scenario: CC button is hidden if no translations Scenario: Transcript 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 the "CC" button is hidden Then the "Transcript" button is hidden
""" """
self.navigate_to_video() self.navigate_to_video()
self.assertFalse(self.video.is_button_shown('transcript_button')) self.assertFalse(self.video.is_button_shown('transcript_button'))
...@@ -522,6 +547,16 @@ class YouTubeVideoTest(VideoBaseTest): ...@@ -522,6 +547,16 @@ class YouTubeVideoTest(VideoBaseTest):
timeout=5 timeout=5
) )
def _verify_closed_caption_text(self, text):
"""
Scenario: returns True if the captions are visible, False is else
"""
self.video.wait_for(
lambda: (text in self.video.closed_captions_text),
u'Closed captions contain "{}" text'.format(text),
timeout=5
)
def test_video_language_menu_working(self): def test_video_language_menu_working(self):
""" """
Scenario: Language menu works correctly in Video component Scenario: Language menu works correctly in Video component
...@@ -554,6 +589,43 @@ class YouTubeVideoTest(VideoBaseTest): ...@@ -554,6 +589,43 @@ class YouTubeVideoTest(VideoBaseTest):
self.video.select_language('en') self.video.select_language('en')
self._verify_caption_text('Welcome to edX.') self._verify_caption_text('Welcome to edX.')
def test_video_language_menu_working_closed_captions(self):
"""
Scenario: Language menu works correctly in Video component, checks closed captions
Given the course has a Video component in "Youtube" mode
And I have defined multiple language transcripts for the videos
And I make sure captions are closed
And I see video menu "language" with correct items
And I select language with code "en"
Then I see "Welcome to edX." text in the closed captions
And I select language with code "zh"
Then I see "我们今天要讲的题目是" text in the closed captions
"""
self.assets.extend(['chinese_transcripts.srt', 'subs_3_yD_cEKoCk.srt.sjson'])
data = {'transcripts': {"zh": "chinese_transcripts.srt"}, 'sub': '3_yD_cEKoCk'}
self.metadata = self.metadata_for_mode('youtube', additional_data=data)
# go to video
self.navigate_to_video()
self.video.show_closed_captions()
correct_languages = {'en': 'English', 'zh': 'Chinese'}
self.assertEqual(self.video.caption_languages, correct_languages)
# we start the video, then pause it to activate the transcript
self.video.click_player_button('play')
self.video.wait_for_position('0:01')
self.video.click_player_button('pause')
self.video.select_language('en')
self.video.click_first_line_in_transcript()
self._verify_closed_caption_text('Welcome to edX.')
self.video.select_language('zh')
unicode_text = "我们今天要讲的题目是".decode('utf-8')
self.video.click_first_line_in_transcript()
self._verify_closed_caption_text(unicode_text)
def test_multiple_videos_in_sequentials_load_and_work(self): def test_multiple_videos_in_sequentials_load_and_work(self):
""" """
Scenario: Multiple videos in sequentials all load and work, switching between sequentials Scenario: Multiple videos in sequentials all load and work, switching between sequentials
......
...@@ -1284,7 +1284,7 @@ main_vendor_js = base_vendor_js + [ ...@@ -1284,7 +1284,7 @@ main_vendor_js = base_vendor_js + [
'js/vendor/jquery.ba-bbq.min.js', 'js/vendor/jquery.ba-bbq.min.js',
'js/vendor/afontgarde/modernizr.fontface-generatedcontent.js', 'js/vendor/afontgarde/modernizr.fontface-generatedcontent.js',
'js/vendor/afontgarde/afontgarde.js', 'js/vendor/afontgarde/afontgarde.js',
'js/vendor/afontgarde/edx-icons.js', 'js/vendor/afontgarde/edx-icons.js'
] ]
# Common files used by both RequireJS code and non-RequireJS code # Common files used by both RequireJS code and non-RequireJS code
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
<h3 class="hidden">${_('No playable video sources found.')}</h3> <h3 class="hidden">${_('No playable video sources found.')}</h3>
</section> </section>
<div class="video-player-post"></div> <div class="video-player-post"></div>
<div class="closed-captions"></div>
<section class="video-controls is-hidden"> <section class="video-controls is-hidden">
<div> <div>
<div class="vcr"><div class="vidtime">0:00 / 0:00</div></div> <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
......
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