From 1bbfbedb1f4b1c8bb65f6c9249cdcf2bc308bf49 Mon Sep 17 00:00:00 2001 From: Muhammad Ammar Date: Tue, 15 Apr 2014 15:14:28 +0000 Subject: [PATCH] Bok-Choy video tests batch2 --- common/test/acceptance/fixtures/course.py | 4 +- common/test/acceptance/pages/lms/video.py | 234 ++++++++++-- common/test/acceptance/tests/helpers.py | 13 +- .../acceptance/tests/test_video_module.py | 352 ++++++++++++++++-- .../courseware/features/video.feature | 120 +----- 5 files changed, 545 insertions(+), 178 deletions(-) diff --git a/common/test/acceptance/fixtures/course.py b/common/test/acceptance/fixtures/course.py index d81bf59e68..913e882a4e 100644 --- a/common/test/acceptance/fixtures/course.py +++ b/common/test/acceptance/fixtures/course.py @@ -221,7 +221,7 @@ class CourseFixture(StudioApiFixture): """ Add the asset to the list of assets to be uploaded when the install method is called. """ - self._assets.append(asset_name) + self._assets.extend(asset_name) def install(self): """ @@ -312,7 +312,7 @@ class CourseFixture(StudioApiFixture): details = response.json() except ValueError: raise CourseFixtureError( - "Could not decode course details as JSON: '{0}'".format(old_details) + "Could not decode course details as JSON: '{0}'".format(details) ) # Update the old details with our overrides diff --git a/common/test/acceptance/pages/lms/video.py b/common/test/acceptance/pages/lms/video.py index 4e6da4c85e..0974cea65c 100644 --- a/common/test/acceptance/pages/lms/video.py +++ b/common/test/acceptance/pages/lms/video.py @@ -2,11 +2,15 @@ Video player in the courseware. """ +import time +import requests +from selenium.webdriver.common.action_chains import ActionChains from bok_choy.page_object import PageObject from bok_choy.promise import EmptyPromise, Promise from bok_choy.javascript import wait_for_js, js_defined from ...tests.helpers import wait_for_ajax + VIDEO_BUTTONS = { 'CC': '.hide-subtitles', 'volume': '.volume', @@ -18,12 +22,16 @@ VIDEO_BUTTONS = { CSS_CLASS_NAMES = { 'closed_captions': '.closed .subtitles', + 'captions_rendered': '.video.is-captions-rendered', 'captions': '.subtitles', + 'captions_text': '.subtitles > li', 'error_message': '.video .video-player h3', 'video_container': 'div.video', 'video_sources': '.video-player video source', 'video_spinner': '.video-wrapper .spinner', - 'video_xmodule': '.xmodule_VideoModule' + 'video_xmodule': '.xmodule_VideoModule', + 'video_init': '.is-initialized', + 'video_time': 'div.vidtime' } VIDEO_MODES = { @@ -31,6 +39,14 @@ VIDEO_MODES = { 'youtube': 'iframe' } +VIDEO_MENUS = { + 'language': '.lang .menu', + 'speed': '.speed .menu', + 'download_transcript': '.video-tracks .a11y-menu-list', + 'transcript-format': '.video-tracks .a11y-menu-button' +} + + @js_defined('window.Video', 'window.RequireJS.require', 'window.jQuery') class VideoPage(PageObject): @@ -44,21 +60,43 @@ class VideoPage(PageObject): def is_browser_on_page(self): return self.q(css='div{0}'.format(CSS_CLASS_NAMES['video_xmodule'])).present + @wait_for_js + def _wait_for_element(self, element_css_selector, promise_desc): + """ + Wait for element specified by `element_css_selector` is present in DOM. + :param element_css_selector: css selector of the element + :param promise_desc: Description of the Promise, used in log messages. + :return: BrokenPromise: the `Promise` was not satisfied within the time or attempt limits. + """ + + def _is_element_present(): + """ + Check if web-element present in DOM + :return: bool + """ + return self.q(css=element_css_selector).present + + EmptyPromise(_is_element_present, promise_desc, timeout=200).fulfill() + @wait_for_js def wait_for_video_class(self): """ Wait until element with class name `video` appeared in DOM. """ - video_css = '{0}'.format(CSS_CLASS_NAMES['video_container']) - wait_for_ajax(self.browser) - return EmptyPromise(lambda: self.q(css=video_css).present, "Video is initialized").fulfill() + + video_css = '{0}'.format(CSS_CLASS_NAMES['video_container']) + self._wait_for_element(video_css, 'Video is initialized') @wait_for_js def wait_for_video_player_render(self): """ Wait until Video Player Rendered Completely. """ + self.wait_for_video_class() + self._wait_for_element(CSS_CLASS_NAMES['video_init'], 'Video Player Initialized') + self._wait_for_element(CSS_CLASS_NAMES['video_time'], 'Video Player Initialized') + def _is_finished_loading(): """ Check if video loading completed @@ -66,9 +104,9 @@ class VideoPage(PageObject): """ return not self.q(css=CSS_CLASS_NAMES['video_spinner']).visible - self.wait_for_video_class() - return EmptyPromise(_is_finished_loading, 'Finished loading the video', try_limit=10, timeout=60, - try_interval=10).fulfill() + EmptyPromise(_is_finished_loading, 'Finished loading the video', timeout=200).fulfill() + + wait_for_ajax(self.browser) def is_video_rendered(self, mode): """ @@ -88,14 +126,6 @@ class VideoPage(PageObject): return Promise(_is_element_present, 'Video Rendering Failed in {0} mode.'.format(mode)).fulfill() - @property - def all_video_sources(self): - """ - Extract all video source urls on current page. - """ - return self.q(css=CSS_CLASS_NAMES['video_sources']).map( - lambda el: el.get_attribute('src').split('?')[0]).results - @property def is_autoplay_enabled(self): """ @@ -137,6 +167,7 @@ class VideoPage(PageObject): """ Show the video captions. """ + def _is_subtitles_open(): """ Check if subtitles are opened @@ -163,19 +194,168 @@ class VideoPage(PageObject): Extract captions text. :return: str """ - captions_css = CSS_CLASS_NAMES['captions'] + # wait until captions rendered completely + self._wait_for_element(CSS_CLASS_NAMES['captions_rendered'], 'Captions Rendered') - def _captions_text(): - """ - Extract captions text. - :return: tuple - """ - is_present = self.q(css=captions_css).present - result = None + captions_css = CSS_CLASS_NAMES['captions_text'] - if is_present: - result = self.q(css=captions_css).text[0] + subs = self.q(css=captions_css).html - return is_present, result + return ' '.join(subs) - return Promise(_captions_text, 'Captions Text').fulfill() + def set_speed(self, speed): + """ + Change the video play speed. + :param speed: speed value in str + """ + self.browser.execute_script("$('.speeds').addClass('is-opened')") + speed_css = 'li[data-speed="{0}"] a'.format(speed) + + EmptyPromise(lambda: self.q(css='.speeds').visible, 'Video Speed Control Shown').fulfill() + + self.q(css=speed_css).first.click() + + def get_speed(self): + """ + Get current video speed value. + :return: str + """ + speed_css = '.speeds .value' + return self.q(css=speed_css).text[0] + + speed = property(get_speed, set_speed) + + def click_player_button(self, button): + """ + Click on `button`. + :param button: key in VIDEO_BUTTONS dictionary, its value will give us the css selector for `button` + """ + self.q(css=VIDEO_BUTTONS[button]).first.click() + wait_for_ajax(self.browser) + + def _get_element_dimensions(self, selector): + """ + Gets the width and height of element specified by `selector` + :param selector: str, css selector of a web element + :return: dict + """ + element = self.q(css=selector).results[0] + return element.size + + def _get_dimensions(self): + """ + Gets the video player dimensions + :return: tuple + """ + video = self._get_element_dimensions('.video-player iframe, .video-player video') + wrapper = self._get_element_dimensions('.tc-wrapper') + controls = self._get_element_dimensions('.video-controls') + progress_slider = self._get_element_dimensions('.video-controls > .slider') + + expected = dict(wrapper) + expected['height'] -= controls['height'] + 0.5 * progress_slider['height'] + + return video, expected + + def is_aligned(self, is_transcript_visible): + """ + Check if video is aligned properly. + :param is_transcript_visible: bool + :return: bool + """ + # Width of the video container in css equal 75% of window if transcript enabled + wrapper_width = 75 if is_transcript_visible else 100 + initial = self.browser.get_window_size() + + self.browser.set_window_size(300, 600) + + # Wait for browser to resize completely + # Currently there is no other way to wait instead of explicit wait + time.sleep(0.2) + + real, expected = self._get_dimensions() + + width = round(100 * real['width'] / expected['width']) == wrapper_width + + self.browser.set_window_size(600, 300) + + # Wait for browser to resize completely + # Currently there is no other way to wait instead of explicit wait + time.sleep(0.2) + + real, expected = self._get_dimensions() + + height = abs(expected['height'] - real['height']) <= 5 + + # Restore initial window size + self.browser.set_window_size( + initial['width'], initial['height'] + ) + + return all([width, height]) + + def _get_transcript(self, url): + """ + Sends a http get request. + """ + kwargs = dict() + + session_id = [{i['name']: i['value']} for i in self.browser.get_cookies() if i['name'] == u'sessionid'] + if session_id: + kwargs.update({ + 'cookies': session_id[0] + }) + + response = requests.get(url, **kwargs) + return response.status_code < 400, response.headers, response.content + + 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` + :param transcript_format: `srt` or `txt` + :param text_to_search: str + :return: bool + """ + # check if we have a transcript with correct format + assert '.' + transcript_format in self.q(css=VIDEO_MENUS['transcript-format']).text[0] + + formats = { + 'srt': 'application/x-subrip', + 'txt': 'text/plain', + } + + url = self.q(css=VIDEO_BUTTONS['download_transcript']).attrs('href')[0] + result, headers, content = self._get_transcript(url) + + assert result + assert formats[transcript_format] in headers.get('content-type', '') + assert text_to_search in content.decode('utf-8') + + def select_language(self, code): + """ + Select captions for language `code` + :param code: str, two character language code like `en`, `zh` + :return: bool, True for Success, False for Failure or BrokenPromise + """ + wait_for_ajax(self.browser) + + selector = VIDEO_MENUS["language"] + ' li[data-lang-code="{code}"]'.format(code=code) + + # mouse over to CC button + element_to_hover_over = self.q(css=VIDEO_BUTTONS["CC"]).results[0] + hover = ActionChains(self.browser).move_to_element(element_to_hover_over) + hover.perform() + + self.q(css=selector).first.click() + + assert 'is-active' == self.q(css=selector).attrs('class')[0] + assert len(self.q(css=VIDEO_MENUS["language"] + ' li.is-active').results) == 1 + + # Make sure that all ajax requests that affects the display of captions are finished. + # For example, request to get new translation etc. + wait_for_ajax(self.browser) + + EmptyPromise(lambda: self.q(css=CSS_CLASS_NAMES['captions']).visible, 'Subtitles Visible').fulfill() + + # wait until captions rendered completely + self._wait_for_element(CSS_CLASS_NAMES['captions_rendered'], 'Captions Rendered') diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py index 8de5cfa153..12228344a0 100644 --- a/common/test/acceptance/tests/helpers.py +++ b/common/test/acceptance/tests/helpers.py @@ -6,8 +6,13 @@ from bok_choy.web_app_test import WebAppTest from bok_choy.promise import EmptyPromise -def wait_for_ajax(browser): - """ Make sure that all ajax requests are finished. +def wait_for_ajax(browser, try_limit=None, try_interval=0.5, timeout=60): + """ + Make sure that all ajax requests are finished. + :param try_limit (int or None): Number of attempts to make to satisfy the `Promise`. Can be `None` to + disable the limit. + :param try_interval (float): Number of seconds to wait between attempts. + :param timeout (float): Maximum number of seconds to wait for the `Promise` to be satisfied before timing out. :param browser: selenium.webdriver, The Selenium-controlled browser that this page is loaded in. """ def _is_ajax_finished(): @@ -17,7 +22,9 @@ def wait_for_ajax(browser): """ return browser.execute_script("return jQuery.active") == 0 - EmptyPromise(_is_ajax_finished, "Finished waiting for ajax requests.").fulfill() + EmptyPromise(_is_ajax_finished, "Finished waiting for ajax requests.", try_limit=try_limit, + try_interval=try_interval, timeout=timeout).fulfill() + def load_data_str(rel_path): """ diff --git a/common/test/acceptance/tests/test_video_module.py b/common/test/acceptance/tests/test_video_module.py index b2e67f550d..5c30128905 100644 --- a/common/test/acceptance/tests/test_video_module.py +++ b/common/test/acceptance/tests/test_video_module.py @@ -7,6 +7,7 @@ Acceptance tests for Video. from .helpers import UniqueCourseTest from ..pages.lms.video import VideoPage from ..pages.lms.tab_nav import TabNavPage +from ..pages.lms.course_nav import CourseNavPage from ..pages.studio.auto_auth import AutoAuthPage from ..pages.lms.course_info import CourseInfoPage from ..fixtures.course import CourseFixture, XBlockFixtureDesc @@ -23,18 +24,6 @@ HTML5_SOURCES_INCORRECT = [ 'http://localhost:{0}/gizmo.mp99'.format(VIDEO_SOURCE_PORT), ] -HTML5_METADATA = { - 'youtube_id_1_0': '', - 'youtube_id_0_75': '', - 'youtube_id_1_25': '', - 'youtube_id_1_5': '', - 'html5_sources': HTML5_SOURCES -} - -YT_HTML5_METADATA = { - 'html5_sources': HTML5_SOURCES -} - class VideoBaseTest(UniqueCourseTest): """ @@ -50,6 +39,7 @@ class VideoBaseTest(UniqueCourseTest): self.video = VideoPage(self.browser) self.tab_nav = TabNavPage(self.browser) + self.course_nav = CourseNavPage(self.browser) self.course_info_page = CourseInfoPage(self.browser, self.course_id) self.course_fixture = CourseFixture( @@ -57,8 +47,9 @@ class VideoBaseTest(UniqueCourseTest): self.course_info['run'], self.course_info['display_name'] ) - self.metadata = {} - self.assets = None + self.metadata = None + self.assets = [] + self.verticals = None def navigate_to_video(self): """ Prepare the course and get to the video and render it """ @@ -79,16 +70,44 @@ class VideoBaseTest(UniqueCourseTest): if self.assets: self.course_fixture.add_asset(self.assets) - # If you are not sending any metadata then `None` should be send as metadata to XBlockFixtureDesc - # instead of empty dictionary otherwise test will not produce correct results. - _metadata = self.metadata if self.metadata else None + chapter_sequential = XBlockFixtureDesc('sequential', 'Test Section') + chapter_sequential.add_children(*self._add_course_verticals()) + chapter = XBlockFixtureDesc('chapter', 'Test Chapter').add_children(chapter_sequential) + self.course_fixture.add_children(chapter) + self.course_fixture.install() - self.course_fixture.add_children( - XBlockFixtureDesc('chapter', 'Test Chapter').add_children( - XBlockFixtureDesc('sequential', 'Test Section').add_children( - XBlockFixtureDesc('vertical', 'Test Vertical-0').add_children( - XBlockFixtureDesc('video', 'Video', metadata=_metadata) - )))).install() + def _add_course_verticals(self): + """ + Create XBlockFixtureDesc verticals + :return: a list of XBlockFixtureDesc + """ + xblock_verticals = [] + _verticals = self.verticals + + # Video tests require at least one vertical with a single video. + if not _verticals: + _verticals = [[{'display_name': 'Video', 'metadata': self.metadata}]] + + for vertical_index, vertical in enumerate(_verticals): + xblock_verticals.append(self._create_single_vertical(vertical, vertical_index)) + + return xblock_verticals + + def _create_single_vertical(self, vertical, vertical_index): + """ + Create a single course vertical of type XBlockFixtureDesc with category `vertical`. + A single course vertical can contain single or multiple video modules. + :param vertical: vertical data list + :param vertical_index: index for the vertical display name + :return: XBlockFixtureDesc + """ + xblock_course_vertical = XBlockFixtureDesc('vertical', 'Test Vertical-{0}'.format(vertical_index)) + + for video in vertical: + xblock_course_vertical.add_children( + XBlockFixtureDesc('video', video['display_name'], metadata=video.get('metadata'))) + + return xblock_course_vertical def _navigate_to_courseware_video(self): """ Register for the course and navigate to the video unit """ @@ -107,6 +126,56 @@ class VideoBaseTest(UniqueCourseTest): self._navigate_to_courseware_video() self.video.wait_for_video_class() + def metadata_for_mode(self, player_mode, additional_data=None): + """ + Create a dictionary for video player configuration according to `player_mode` + :param player_mode (str): Video player mode + :param additional_data (dict): Optional additional metadata. + :return: dict + """ + metadata = {} + + if player_mode == 'html5': + metadata.update({ + 'youtube_id_1_0': '', + 'youtube_id_0_75': '', + 'youtube_id_1_25': '', + 'youtube_id_1_5': '', + 'html5_sources': HTML5_SOURCES + }) + + if player_mode == 'youtube_html5': + metadata.update({ + 'html5_sources': HTML5_SOURCES, + }) + + if player_mode == 'youtube_html5_unsupported_video': + metadata.update({ + 'html5_sources': HTML5_SOURCES_INCORRECT + }) + + if player_mode == 'html5_unsupported_video': + metadata.update({ + 'youtube_id_1_0': '', + 'youtube_id_0_75': '', + 'youtube_id_1_25': '', + 'youtube_id_1_5': '', + 'html5_sources': HTML5_SOURCES_INCORRECT + }) + + if additional_data: + metadata.update(additional_data) + + return metadata + + def open_video(self, video_display_name): + """ + Navigate to sequential specified by `video_display_name` + :param video_display_name (str): Sequential Title + """ + self.course_nav.go_to_sequential(video_display_name) + self.video.wait_for_video_player_render() + class YouTubeVideoTest(VideoBaseTest): """ Test YouTube Video Player """ @@ -114,7 +183,7 @@ class YouTubeVideoTest(VideoBaseTest): def setUp(self): super(YouTubeVideoTest, self).setUp() - def test_video_component_rendered_in_youtube_without_html5_sources(self): + def test_youtube_video_rendering_wo_html5_sources(self): """ Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources Given the course has a Video component in "Youtube" mode @@ -125,7 +194,7 @@ class YouTubeVideoTest(VideoBaseTest): # Verify that video has rendered in "Youtube" mode self.assertTrue(self.video.is_video_rendered('youtube')) - def test_cc_button_without_english_transcript_youtube_mode(self): + def test_cc_button_wo_english_transcript_youtube(self): """ 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 @@ -133,8 +202,9 @@ class YouTubeVideoTest(VideoBaseTest): And I have uploaded a non-english transcript file to assets Then I see the correct text in the captions """ - self.metadata['transcripts'] = {'zh': 'chinese_transcripts.srt'} - self.assets = 'chinese_transcripts.srt' + data = {'transcripts': {'zh': 'chinese_transcripts.srt'}} + self.metadata = self.metadata_for_mode('youtube', data) + self.assets.append('chinese_transcripts.srt') self.navigate_to_video() self.video.show_captions() @@ -150,14 +220,14 @@ class YouTubeVideoTest(VideoBaseTest): And I have uploaded a .srt.sjson file to assets Then I see the correct english text in the captions """ - self.assets = 'subs_OEoXaMPEzfM.srt.sjson' + self.assets.append('subs_OEoXaMPEzfM.srt.sjson') self.navigate_to_video() self.video.show_captions() # Verify that we see "Hi, welcome to Edx." text in the captions self.assertIn('Hi, welcome to Edx.', self.video.captions_text) - def test_cc_button_hidden_if_no_translations(self): + def test_cc_button_hidden_no_translations(self): """ Scenario: CC button is hidden if no translations Given the course has a Video component in "Youtube" mode @@ -166,22 +236,111 @@ class YouTubeVideoTest(VideoBaseTest): self.navigate_to_video() self.assertFalse(self.video.is_button_shown('CC')) + def test_fullscreen_video_alignment_with_transcript_hidden(self): + """ + Scenario: Video is aligned correctly if transcript is hidden in fullscreen mode + Given the course has a Video component in "Youtube" mode + """ + self.navigate_to_video() + + # click video button "fullscreen" + self.video.click_player_button('fullscreen') + + # check if video aligned correctly without enabled transcript + self.assertTrue(self.video.is_aligned(False)) + + def test_download_button_wo_english_transcript(self): + """ + Scenario: Download button works correctly w/o english transcript in Youtube mode of Video component + Given + I have a "chinese_transcripts.srt" transcript file in assets + And it has a video in "Youtube" mode + """ + data = {'download_track': True, 'transcripts': {'zh': 'chinese_transcripts.srt'}} + self.metadata = self.metadata_for_mode('youtube', additional_data=data) + self.assets.append('chinese_transcripts.srt') + + # go to video + self.navigate_to_video() + + # check if we can download transcript in "srt" format that has text "好 各位同学" + unicode_text = "好 各位同学".decode('utf-8') + self.video.downloaded_transcript_contains_text('srt', unicode_text) + + def test_download_button_two_transcript_languages_youtube(self): + """ + Scenario: Download button works correctly for non-english transcript in Youtube mode of Video component + Given + I have a "chinese_transcripts.srt" transcript file in assets + And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets + And it has a video in "Youtube" mode + """ + self.assets.extend(['chinese_transcripts.srt', 'subs_OEoXaMPEzfM.srt.sjson']) + data = {'download_track': True, 'transcripts': {'zh': 'chinese_transcripts.srt'}, 'sub': 'OEoXaMPEzfM'} + self.metadata = self.metadata_for_mode('youtube', additional_data=data) + + # go to video + self.navigate_to_video() + + # check if "Hi, welcome to Edx." text in the captions + self.assertIn('Hi, welcome to Edx.', self.video.captions_text) + + # check if we can download transcript in "srt" format that has text "Hi, welcome to Edx." + self.video.downloaded_transcript_contains_text('srt', 'Hi, welcome to Edx.') + + # select language with code "zh" + self.video.select_language('zh') + + # check if we see "好 各位同学" text in the captions + unicode_text = "好 各位同学".decode('utf-8') + self.assertIn(unicode_text, self.video.captions_text) + + # check if we can download transcript in "srt" format that has text "好 各位同学" + unicode_text = "好 各位同学".decode('utf-8') + self.video.downloaded_transcript_contains_text('srt', unicode_text) + + def test_fullscreen_video_alignment_on_transcript_toggle(self): + """ + Tests that Video is aligned correctly on transcript toggle in fullscreen mode Given I have a + "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets And it has a video in "Youtube" mode. + """ + self.assets.append('subs_OEoXaMPEzfM.srt.sjson') + data = {'sub': 'OEoXaMPEzfM'} + self.metadata = self.metadata_for_mode('youtube', additional_data=data) + + # go to video + self.navigate_to_video() + + # make sure captions are opened + self.video.show_captions() + + # click video button "fullscreen" + self.video.click_player_button('fullscreen') + + # check if video aligned correctly with enabled transcript + self.assertTrue(self.video.is_aligned(True)) + + # click video button "CC" + self.video.click_player_button('CC') + + # check if video aligned correctly without enabled transcript + self.assertTrue(self.video.is_aligned(False)) + class YouTubeHtml5VideoTest(VideoBaseTest): """ Test YouTube HTML5 Video Player """ def setUp(self): super(YouTubeHtml5VideoTest, self).setUp() - self.metadata = YT_HTML5_METADATA - def test_video_component_rendered_in_youtube_with_unsupported_html5_sources(self): + def test_youtube_video_rendering_with_unsupported_sources(self): """ Scenario: Video component is rendered in the LMS in Youtube mode - with HTML5 sources that doesn't supported by browser + with HTML5 sources that doesn't supported by browser Given the course has a Video component in "Youtube_HTML5_Unsupported_Video" mode Then the video has rendered in "Youtube" mode """ - self.metadata['html5_sources'] = HTML5_SOURCES_INCORRECT + self.metadata = self.metadata_for_mode('youtube_html5_unsupported_video') self.navigate_to_video() # Verify that the video has rendered in "Youtube" mode @@ -193,7 +352,6 @@ class Html5VideoTest(VideoBaseTest): def setUp(self): super(Html5VideoTest, self).setUp() - self.metadata = HTML5_METADATA def test_autoplay_disabled_for_video_component(self): """ @@ -201,19 +359,20 @@ class Html5VideoTest(VideoBaseTest): Given the course has a Video component in "HTML5" mode Then it does not have autoplay enabled """ + self.metadata = self.metadata_for_mode('html5') self.navigate_to_video() # Verify that the video has autoplay mode disabled self.assertFalse(self.video.is_autoplay_enabled) - def test_video_component_rendered_in_html5_with_unsupported_html5_sources(self): + def test_html5_video_rendering_with_unsupported_sources(self): """ - Scenario: Video component is rendered in the LMS in HTML5 mode with HTML5 sources that doesn't supported by browser + Scenario: Video component is rendered in 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 """ - self.metadata['html5_sources'] = HTML5_SOURCES_INCORRECT + self.metadata = self.metadata_for_mode('html5_unsupported_video') self.navigate_to_video_no_render() # Verify that error message is shown @@ -222,3 +381,122 @@ class Html5VideoTest(VideoBaseTest): # Verify that error message has correct text correct_error_message_text = 'ERROR: No playable video sources found!' self.assertIn(correct_error_message_text, self.video.error_message_text) + + def test_download_button_wo_english_transcript(self): + """ + Scenario: Download button works correctly w/o english transcript in HTML5 mode of Video component + Given + I have a "chinese_transcripts.srt" transcript file in assets + And it has a video in "HTML5" mode + """ + data = {'download_track': True, 'transcripts': {'zh': 'chinese_transcripts.srt'}} + self.metadata = self.metadata_for_mode('html5', additional_data=data) + self.assets.append('chinese_transcripts.srt') + + # go to video + self.navigate_to_video() + + # check if we see "好 各位同学" text in the captions + unicode_text = "好 各位同学".decode('utf-8') + self.assertIn(unicode_text, self.video.captions_text) + + # check if we can download transcript in "srt" format that has text "好 各位同学" + unicode_text = "好 各位同学".decode('utf-8') + self.video.downloaded_transcript_contains_text('srt', unicode_text) + + def test_download_button_two_transcript_languages_html5(self): + """ + Scenario: Download button works correctly for non-english transcript in HTML5 mode of Video component + Given + I have a "chinese_transcripts.srt" transcript file in assets + And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets + And it has a video in "HTML5" mode + """ + self.assets.extend(['chinese_transcripts.srt', 'subs_OEoXaMPEzfM.srt.sjson']) + data = {'download_track': True, 'transcripts': {'zh': 'chinese_transcripts.srt'}, 'sub': 'OEoXaMPEzfM'} + self.metadata = self.metadata_for_mode('html5', additional_data=data) + + # go to video + self.navigate_to_video() + + # check if "Hi, welcome to Edx." text in the captions + self.assertIn('Hi, welcome to Edx.', self.video.captions_text) + + # check if we can download transcript in "srt" format that has text "Hi, welcome to Edx." + self.video.downloaded_transcript_contains_text('srt', 'Hi, welcome to Edx.') + + # select language with code "zh" + self.video.select_language('zh') + + # check if we see "好 各位同学" text in the captions + unicode_text = "好 各位同学".decode('utf-8') + + self.assertIn(unicode_text, self.video.captions_text) + + #Then I can download transcript in "srt" format that has text "好 各位同学" + unicode_text = "好 各位同学".decode('utf-8') + self.video.downloaded_transcript_contains_text('srt', unicode_text) + + def test_full_screen_video_alignment_with_transcript_visible(self): + """ + Scenario: Video is aligned correctly if transcript is visible in fullscreen mode + Given + I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets + And it has a video in "HTML5" mode + """ + self.assets.append('subs_OEoXaMPEzfM.srt.sjson') + data = {'sub': 'OEoXaMPEzfM'} + self.metadata = self.metadata_for_mode('html5', additional_data=data) + + # go to video + self.navigate_to_video() + + # make sure captions are opened + self.video.show_captions() + + # click video button "fullscreen" + self.video.click_player_button('fullscreen') + + # check if video aligned correctly with enabled transcript + self.assertTrue(self.video.is_aligned(True)) + + def test_cc_button_with_english_transcript(self): + """ + Scenario: CC button works correctly only w/ english transcript in HTML5 mode of Video component + Given + I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets + And it has a video in "HTML5" mode + """ + self.assets.append('subs_OEoXaMPEzfM.srt.sjson') + data = {'sub': 'OEoXaMPEzfM'} + self.metadata = self.metadata_for_mode('html5', additional_data=data) + + # go to video + self.navigate_to_video() + + # make sure captions are opened + self.video.show_captions() + + # check if we see "Hi, welcome to Edx." text in the captions + self.assertIn("Hi, welcome to Edx.", self.video.captions_text) + + def test_cc_button_wo_english_transcript_html5(self): + """ + Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component + Given + I have a "chinese_transcripts.srt" transcript file in assets + And it has a video in "HTML5" mode + """ + self.assets.append('chinese_transcripts.srt') + data = {'transcripts': {'zh': 'chinese_transcripts.srt'}} + self.metadata = self.metadata_for_mode('html5', additional_data=data) + + # go to video + self.navigate_to_video() + + # make sure captions are opened + self.video.show_captions() + + # check if we see "好 各位同学" text in the captions + unicode_text = "好 各位同学".decode('utf-8') + self.assertIn(unicode_text, self.video.captions_text) diff --git a/lms/djangoapps/courseware/features/video.feature b/lms/djangoapps/courseware/features/video.feature index 2f1e9e1c69..348478a70c 100644 --- a/lms/djangoapps/courseware/features/video.feature +++ b/lms/djangoapps/courseware/features/video.feature @@ -89,56 +89,6 @@ Feature: LMS.Video component And I see "Hi, welcome to Edx." text in the captions # 8 - Scenario: CC button works correctly w/o english transcript in HTML5 mode of Video component - Given I am registered for the course "test_course" - And I have a "chinese_transcripts.srt" transcript file in assets - And it has a video in "HTML5" mode: - | transcripts | - | {"zh": "chinese_transcripts.srt"} | - And I make sure captions are opened - Then I see "好 各位同学" text in the captions - - # 9 - 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 - - # 10 - Scenario: Video is aligned correctly if transcript is visible in fullscreen mode - 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 - And I click video button "fullscreen" - Then I see video aligned correctly with enabled transcript - - # 11 - 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 - - # 12 - Scenario: Video is aligned correctly on transcript toggle in fullscreen mode - 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: - | 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 - - # 13 Scenario: Download Transcript button works correctly in Video component Given I am registered for the course "test_course" And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets @@ -160,7 +110,7 @@ Feature: LMS.Video component When I open video "C" Then menu "download_transcript" doesn't exist - # 14 + # 9 Scenario: Youtube video has correct transcript if fields for other speeds are filled. Given I am registered for the course "test_course" And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets @@ -178,55 +128,7 @@ Feature: LMS.Video component # "1:56" is the duration in the VCR timer before the video plays. And I see duration "1:56" - # 15 - Scenario: Download button works correctly for non-english transcript in Youtube mode of Video component - Given I am registered for the course "test_course" - And I have a "chinese_transcripts.srt" transcript file in assets - And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets - And it has a video in "Youtube" mode: - | transcripts | sub | download_track | - | {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true | - And I see "Hi, welcome to Edx." text in the captions - Then I can download transcript in "srt" format that has text "Hi, welcome to Edx." - And I select language with code "zh" - And I see "好 各位同学" text in the captions - Then I can download transcript in "srt" format that has text "好 各位同学" - - # 16 - Scenario: Download button works correctly for non-english transcript in HTML5 mode of Video component - Given I am registered for the course "test_course" - And I have a "chinese_transcripts.srt" transcript file in assets - And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets - And it has a video in "HTML5" mode: - | transcripts | sub | download_track | - | {"zh": "chinese_transcripts.srt"} | OEoXaMPEzfM | true | - And I see "Hi, welcome to Edx." text in the captions - Then I can download transcript in "srt" format that has text "Hi, welcome to Edx." - And I select language with code "zh" - And I see "好 各位同学" text in the captions - Then I can download transcript in "srt" format that has text "好 各位同学" - - # 17 - Scenario: Download button works correctly w/o english transcript in HTML5 mode of Video component - Given I am registered for the course "test_course" - And I have a "chinese_transcripts.srt" transcript file in assets - And it has a video in "HTML5" mode: - | transcripts | download_track | - | {"zh": "chinese_transcripts.srt"} | true | - And I see "好 各位同学" text in the captions - Then I can download transcript in "srt" format that has text "好 各位同学" - - # 18 - Scenario: Download button works correctly w/o english transcript in Youtube mode of Video component - Given I am registered for the course "test_course" - And I have a "chinese_transcripts.srt" transcript file in assets - And it has a video in "Youtube" mode: - | transcripts | download_track | - | {"zh": "chinese_transcripts.srt"} | true | - And I see "好 各位同学" text in the captions - Then I can download transcript in "srt" format that has text "好 各位同学" - - # 19 + # 10 Scenario: Verify that each video in each sub-section includes a transcript for non-Youtube countries. Given youtube server is up and response time is 2 seconds And I am registered for the course "test_course" @@ -255,7 +157,7 @@ Feature: LMS.Video component Then the video has rendered in "HTML5" mode And the video does not show the captions - # 20 + # 11 Scenario: Start time works for Youtube video Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -264,7 +166,7 @@ Feature: LMS.Video component And I click video button "play" Then I see video slider at "0:10" position - # 21 + # 12 Scenario: End time works for Youtube video Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -274,7 +176,7 @@ Feature: LMS.Video component And I wait "5" seconds Then I see video slider at "0:02" position - # 22 + # 13 Scenario: Youtube video with end-time at 1:00 and the video starts playing at 0:58 Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -286,7 +188,7 @@ Feature: LMS.Video component And I wait "5" seconds Then I see video slider at "1:00" position - # 23 + # 14 Scenario: Start time and end time work together for Youtube video Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -297,7 +199,7 @@ Feature: LMS.Video component And I wait "5" seconds Then I see video slider at "0:12" position - # 24 + # 15 Scenario: Youtube video after pausing at end time video plays to the end from end time Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -312,7 +214,7 @@ Feature: LMS.Video component # The default video length is 00:01:55. Then I see video slider at "1:55" position - # 25 + # 16 Scenario: Youtube video with end-time at 0:32 and start-time at 0:30, the video starts playing from 0:28 Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -324,7 +226,7 @@ Feature: LMS.Video component And I wait "8" seconds Then I see video slider at "0:32" position - # 26 + # 17 Scenario: Youtube video with end-time at 1:00, the video starts playing from 1:52 Given I am registered for the course "test_course" And it has a video in "Youtube" mode: @@ -337,7 +239,7 @@ Feature: LMS.Video component # Video stops at the end. Then I see video slider at "1:55" position - # 27 + # 18 @skip_firefox Scenario: Quality button appears on play Given the course has a Video component in "Youtube" mode @@ -345,7 +247,7 @@ Feature: LMS.Video component And I click video button "play" Then I see video button "quality" is visible - # 28 + # 19 @skip_firefox Scenario: Quality button works correctly Given the course has a Video component in "Youtube" mode