test_video_module.py 35.9 KB
Newer Older
1 2 3 4 5 6
# -*- coding: utf-8 -*-

"""
Acceptance tests for Video.
"""

7
import json
8
from unittest import skipIf, skip
9
import requests
10 11
from box.test.flaky import flaky
from ..helpers import UniqueCourseTest, is_youtube_available
12 13 14 15 16 17
from ...pages.lms.video.video import VideoPage
from ...pages.lms.tab_nav import TabNavPage
from ...pages.lms.course_nav import CourseNavPage
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.course_info import CourseInfoPage
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
18

19

20
VIDEO_SOURCE_PORT = 8777
21 22
YOUTUBE_STUB_PORT = 9080
YOUTUBE_STUB_URL = 'http://127.0.0.1:{}/'.format(YOUTUBE_STUB_PORT)
23 24

HTML5_SOURCES = [
25 26 27
    'http://localhost:{0}/gizmo.mp4'.format(VIDEO_SOURCE_PORT),
    'http://localhost:{0}/gizmo.webm'.format(VIDEO_SOURCE_PORT),
    'http://localhost:{0}/gizmo.ogv'.format(VIDEO_SOURCE_PORT),
28 29 30
]

HTML5_SOURCES_INCORRECT = [
31
    'http://localhost:{0}/gizmo.mp99'.format(VIDEO_SOURCE_PORT),
32 33 34
]


35 36 37 38 39 40 41
class YouTubeConfigError(Exception):
    """
    Error occurred while configuring YouTube Stub Server.
    """
    pass


42
@flaky
43
@skipIf(is_youtube_available() is False, 'YouTube is not available!')
44 45 46 47 48 49 50 51 52 53 54 55 56 57
class VideoBaseTest(UniqueCourseTest):
    """
    Base class for tests of the Video Player
    Sets up the course and provides helper functions for the Video tests.
    """

    def setUp(self):
        """
        Initialization of pages and course fixture for video tests
        """
        super(VideoBaseTest, self).setUp()

        self.video = VideoPage(self.browser)
        self.tab_nav = TabNavPage(self.browser)
58
        self.course_nav = CourseNavPage(self.browser)
59 60 61 62 63 64 65
        self.course_info_page = CourseInfoPage(self.browser, self.course_id)

        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

66 67 68
        self.metadata = None
        self.assets = []
        self.verticals = None
69 70 71 72
        self.youtube_configuration = {}

        # reset youtube stub server
        self.addCleanup(self._reset_youtube_stub_server)
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

    def navigate_to_video(self):
        """ Prepare the course and get to the video and render it """
        self._install_course_fixture()
        self._navigate_to_courseware_video_and_render()

    def navigate_to_video_no_render(self):
        """
        Prepare the course and get to the video unit
        however do not wait for it to render, because
        the has been an error.
        """
        self._install_course_fixture()
        self._navigate_to_courseware_video_no_render()

    def _install_course_fixture(self):
        """ Install the course fixture that has been defined """
        if self.assets:
            self.course_fixture.add_asset(self.assets)

93 94 95 96 97 98
        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()

99 100 101
        if len(self.youtube_configuration) > 0:
            self._configure_youtube_stub_server(self.youtube_configuration)

102 103 104 105 106 107 108 109 110 111 112 113 114 115
    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))
116

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
        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
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

    def _navigate_to_courseware_video(self):
        """ Register for the course and navigate to the video unit """
        AutoAuthPage(self.browser, course_id=self.course_id).visit()

        self.course_info_page.visit()
        self.tab_nav.go_to_tab('Courseware')

    def _navigate_to_courseware_video_and_render(self):
        """ Wait for the video player to render """
        self._navigate_to_courseware_video()
        self.video.wait_for_video_player_render()

    def _navigate_to_courseware_video_no_render(self):
        """ Wait for the video Xmodule but not for rendering """
        self._navigate_to_courseware_video()
        self.video.wait_for_video_class()

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    def _configure_youtube_stub_server(self, config):
        """
        Allow callers to configure the stub server using the /set_config URL.
        :param config: Configuration dictionary.
        The request should have PUT data, such that:
            Each PUT parameter is the configuration key.
            Each PUT value is a JSON-encoded string value for the configuration.
        :raise YouTubeConfigError:
        """
        youtube_stub_config_url = YOUTUBE_STUB_URL + 'set_config'

        config_data = {param: json.dumps(value) for param, value in config.items()}
        response = requests.put(youtube_stub_config_url, data=config_data)

        if not response.ok:
            raise YouTubeConfigError(
                'YouTube Server Configuration Failed. URL {0}, Configuration Data: {1}, Status was {2}'.format(
                    youtube_stub_config_url, config, response.status_code))

    def _reset_youtube_stub_server(self):
        """
        Reset YouTube Stub Server Configurations using the /del_config URL.
        :raise YouTubeConfigError:
        """
        youtube_stub_config_url = YOUTUBE_STUB_URL + 'del_config'

        response = requests.delete(youtube_stub_config_url)

        if not response.ok:
            raise YouTubeConfigError(
                'YouTube Server Configuration Failed. URL: {0} Status was {1}'.format(
                    youtube_stub_config_url, response.status_code))

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    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

227
    def go_to_sequential_position(self, position):
228 229 230
        """
        Navigate to sequential specified by `video_display_name`
        """
231
        self.course_nav.go_to_sequential_position(position)
232 233
        self.video.wait_for_video_player_render()

234 235 236 237 238 239 240

class YouTubeVideoTest(VideoBaseTest):
    """ Test YouTube Video Player """

    def setUp(self):
        super(YouTubeVideoTest, self).setUp()

241
    def test_youtube_video_rendering_wo_html5_sources(self):
242 243 244 245 246 247 248 249 250 251
        """
        Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources
        Given the course has a Video component in "Youtube" mode
        Then the video has rendered in "Youtube" mode
        """
        self.navigate_to_video()

        # Verify that video has rendered in "Youtube" mode
        self.assertTrue(self.video.is_video_rendered('youtube'))

252
    def test_cc_button_wo_english_transcript(self):
253
        """
254
        Scenario: CC button works correctly w/o english transcript in Youtube mode
255 256 257 258 259
        Given the course has a Video component in "Youtube" mode
        And I have defined a non-english transcript for the video
        And I have uploaded a non-english transcript file to assets
        Then I see the correct text in the captions
        """
260 261 262
        data = {'transcripts': {'zh': 'chinese_transcripts.srt'}}
        self.metadata = self.metadata_for_mode('youtube', data)
        self.assets.append('chinese_transcripts.srt')
263 264 265 266 267
        self.navigate_to_video()
        self.video.show_captions()

        # Verify that we see "好 各位同学" text in the captions
        unicode_text = "好 各位同学".decode('utf-8')
268
        self.assertIn(unicode_text, self.video.captions_text())
269 270 271 272

    def test_cc_button_transcripts_and_sub_fields_empty(self):
        """
        Scenario: CC button works correctly if transcripts and sub fields are empty,
273
            but transcript file exists in assets (Youtube mode of Video component)
274 275 276 277
        Given the course has a Video component in "Youtube" mode
        And I have uploaded a .srt.sjson file to assets
        Then I see the correct english text in the captions
        """
278
        self._install_course_fixture()
279 280
        self.course_fixture.add_asset(['subs_OEoXaMPEzfM.srt.sjson'])
        self.course_fixture._upload_assets()
281
        self._navigate_to_courseware_video_and_render()
282 283 284
        self.video.show_captions()

        # Verify that we see "Hi, welcome to Edx." text in the captions
285
        self.assertIn('Hi, welcome to Edx.', self.video.captions_text())
286

287
    def test_cc_button_hidden_no_translations(self):
288 289 290 291 292 293 294 295
        """
        Scenario: CC button is hidden if no translations
        Given the course has a Video component in "Youtube" mode
        Then the "CC" button is hidden
        """
        self.navigate_to_video()
        self.assertFalse(self.video.is_button_shown('CC'))

296 297
    def test_fullscreen_video_alignment_with_transcript_hidden(self):
        """
298
        Scenario: Video is aligned with transcript hidden in fullscreen mode
299
        Given the course has a Video component in "Youtube" mode
300 301
        When I view the video at fullscreen
        Then the video with the transcript hidden is aligned correctly
302 303 304 305 306 307 308 309 310 311 312
        """
        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):
        """
313 314 315 316 317
        Scenario: Download button works correctly w/o english transcript in YouTube mode
        Given the course has a Video component in "Youtube" mode
        And I have defined a downloadable non-english transcript for the video
        And I have uploaded a non-english transcript file to assets
        Then I can download the transcript in "srt" format
318 319 320 321 322 323 324 325 326 327
        """
        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')
328
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', unicode_text))
329

330
    def test_download_button_two_transcript_languages(self):
331
        """
332 333 334 335 336 337 338 339
        Scenario: Download button works correctly for multiple transcript languages
        Given the course has a Video component in "Youtube" mode
        And I have defined a downloadable non-english transcript for the video
        And I have defined english subtitles for the video
        Then I see the correct english text in the captions
        And the english transcript downloads correctly
        And I see the correct non-english text in the captions
        And the non-english transcript downloads correctly
340 341 342 343 344 345 346 347 348
        """
        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
349
        self.assertIn('Hi, welcome to Edx.', self.video.captions_text())
350 351

        # check if we can download transcript in "srt" format that has text "Hi, welcome to Edx."
352
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', 'Hi, welcome to Edx.'))
353 354

        # select language with code "zh"
355
        self.assertTrue(self.video.select_language('zh'))
356 357 358

        # check if we see "好 各位同学" text in the captions
        unicode_text = "好 各位同学".decode('utf-8')
359
        self.assertIn(unicode_text, self.video.captions_text())
360 361 362

        # check if we can download transcript in "srt" format that has text "好 各位同学"
        unicode_text = "好 各位同学".decode('utf-8')
363
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', unicode_text))
364 365 366

    def test_fullscreen_video_alignment_on_transcript_toggle(self):
        """
367 368 369 370 371 372 373
        Scenario: Video is aligned correctly on transcript toggle in fullscreen mode
        Given the course has a Video component in "Youtube" mode
        And I have uploaded a .srt.sjson file to assets
        And I have defined subtitles for the video
        When I view the video at fullscreen
        Then the video with the transcript enabled is aligned correctly
        And the video with the transcript hidden is aligned correctly
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
        """
        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))

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
    def test_video_rendering_with_default_response_time(self):
        """
        Scenario: Video is rendered in Youtube mode when the YouTube Server responds quickly
        Given the YouTube server response time less than 1.5 seconds
        And the course has a Video component in "Youtube_HTML5" mode
        Then the video has rendered in "Youtube" mode
        """
        # configure youtube server
        self.youtube_configuration['time_to_response'] = 0.4
        self.metadata = self.metadata_for_mode('youtube_html5')

        self.navigate_to_video()

        self.assertTrue(self.video.is_video_rendered('youtube'))

    def test_video_rendering_wo_default_response_time(self):
        """
        Scenario: Video is rendered in HTML5 when the YouTube Server responds slowly
        Given the YouTube server response time is greater than 1.5 seconds
        And the course has a Video component in "Youtube_HTML5" mode
        Then the video has rendered in "HTML5" mode
        """
        # configure youtube server
        self.youtube_configuration['time_to_response'] = 2.0
        self.metadata = self.metadata_for_mode('youtube_html5')

        self.navigate_to_video()

        self.assertTrue(self.video.is_video_rendered('html5'))

    def test_video_with_youtube_blocked(self):
        """
        Scenario: Video is rendered in HTML5 mode when the YouTube API is blocked
        Given the YouTube server response time is greater than 1.5 seconds
        And the YouTube API is blocked
        And the course has a Video component in "Youtube_HTML5" mode
        Then the video has rendered in "HTML5" mode
        """
        # configure youtube server
        self.youtube_configuration.update({
            'time_to_response': 2.0,
            'youtube_api_blocked': True,
        })
        self.metadata = self.metadata_for_mode('youtube_html5')

        self.navigate_to_video()

        self.assertTrue(self.video.is_video_rendered('html5'))

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    def test_download_transcript_button_works_correctly(self):
        """
        Scenario: Download Transcript button works correctly
        Given the course has Video components A and B in "Youtube" mode
        And Video component C in "HTML5" mode
        And I have defined downloadable transcripts for the videos
        Then I can download a transcript for Video A in "srt" format
        And I can download a transcript for Video A in "txt" format
        And I can download a transcript for Video B in "txt" format
        And the Download Transcript menu does not exist for Video C
        """

        data_a = {'sub': 'OEoXaMPEzfM', 'download_track': True}
        youtube_a_metadata = self.metadata_for_mode('youtube', additional_data=data_a)
        self.assets.append('subs_OEoXaMPEzfM.srt.sjson')

        data_b = {'youtube_id_1_0': 'b7xgknqkQk8', 'sub': 'b7xgknqkQk8', 'download_track': True}
        youtube_b_metadata = self.metadata_for_mode('youtube', additional_data=data_b)
        self.assets.append('subs_b7xgknqkQk8.srt.sjson')

        data_c = {'track': 'http://example.org/', 'download_track': True}
        html5_c_metadata = self.metadata_for_mode('html5', additional_data=data_c)

        self.verticals = [
            [{'display_name': 'A', 'metadata': youtube_a_metadata}],
            [{'display_name': 'B', 'metadata': youtube_b_metadata}],
            [{'display_name': 'C', 'metadata': html5_c_metadata}]
        ]

        # open the section with videos (open video "A")
        self.navigate_to_video()

        # check if we can download transcript in "srt" format that has text "00:00:00,270"
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', '00:00:00,270'))

        # select the transcript format "txt"
        self.assertTrue(self.video.select_transcript_format('txt'))

        # check if we can download transcript in "txt" format that has text "Hi, welcome to Edx."
        self.assertTrue(self.video.downloaded_transcript_contains_text('txt', 'Hi, welcome to Edx.'))

        # open video "B"
        self.course_nav.go_to_sequential('B')

        # check if we can download transcript in "txt" format that has text "Equal transcripts"
        self.assertTrue(self.video.downloaded_transcript_contains_text('txt', 'Equal transcripts'))

        # open video "C"
        self.course_nav.go_to_sequential('C')

        # menu "download_transcript" doesn't exist
        self.assertFalse(self.video.is_menu_exist('download_transcript'))

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
    def test_video_language_menu_working(self):
        """
        Scenario: Language menu works correctly in Video component
        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 "zh"
        Then I see "好 各位同学" text in the captions
        And I select language with code "en"
        Then I see "Hi, welcome to Edx." text in the captions
        """
        self.assets.extend(['chinese_transcripts.srt', 'subs_OEoXaMPEzfM.srt.sjson'])
        data = {'transcripts': {"zh": "chinese_transcripts.srt"}, 'sub': 'OEoXaMPEzfM'}
        self.metadata = self.metadata_for_mode('youtube', additional_data=data)

        # go to video
        self.navigate_to_video()

        self.video.hide_captions()

        correct_languages = {'en': 'English', 'zh': 'Chinese'}
        self.assertEqual(self.video.caption_languages(), correct_languages)

        self.video.select_language('zh')

        unicode_text = "好 各位同学".decode('utf-8')
526
        self.assertIn(unicode_text, self.video.captions_text())
527 528

        self.video.select_language('en')
529
        self.assertIn('Hi, welcome to Edx.', self.video.captions_text())
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
    def test_multiple_videos_in_sequentials_load_and_work(self):
        """
        Scenario: Multiple videos in sequentials all load and work, switching between sequentials
        Given it has videos "A,B" in "Youtube" mode in position "1" of sequential
        And videos "E,F" in "Youtube" mode in position "2" of sequential
        """
        self.verticals = [
            [{'display_name': 'A'}, {'display_name': 'B'}], [{'display_name': 'C'}, {'display_name': 'D'}]
        ]

        tab1_video_names = ['A', 'B']
        tab2_video_names = ['C', 'D']

        def execute_video_steps(video_names):
            """
            Execute video steps
            """
            for video_name in video_names:
                self.video.click_player_button('play', video_name)
                self.assertIn(self.video.state(video_name), ['playing', 'buffering'])
                self.video.click_player_button('pause', video_name)

        # go to video
        self.navigate_to_video()

        execute_video_steps(tab1_video_names)

        # go to second sequential position
        self.go_to_sequential_position(2)
        execute_video_steps(tab2_video_names)

        # go back to first sequential position
        # we are again playing tab 1 videos to ensure that switching didn't broke some video functionality.
        self.go_to_sequential_position(1)
        execute_video_steps(tab1_video_names)

    def test_video_component_stores_speed_correctly_for_multiple_videos(self):
        """
        Scenario: Video component stores speed correctly when each video is in separate sequential
        Given I have a video "A" in "Youtube" mode in position "1" of sequential
        And a video "B" in "Youtube" mode in position "2" of sequential
        And a video "C" in "HTML5" mode in position "3" of sequential
        """
        self.verticals = [
            [{'display_name': 'A'}], [{'display_name': 'B'}],
            [{'display_name': 'C', 'metadata': self.metadata_for_mode('html5')}]
        ]

        self.navigate_to_video()

        # select the "2.0" speed on video "A"
        self.course_nav.go_to_sequential('A')
        self.video.set_speed('2.0')

        # select the "0.50" speed on video "B"
        self.course_nav.go_to_sequential('B')
        self.video.set_speed('0.50')

        # open video "C"
        self.course_nav.go_to_sequential('C')

        # check if video "C" should start playing at speed "0.75"
        self.assertEqual(self.video.get_speed(), '0.75x')

        # open video "A"
        self.course_nav.go_to_sequential('A')

        # check if video "A" should start playing at speed "2.0"
        self.assertEqual(self.video.get_speed(), '2.0x')

        # reload the page
        self.video.reload_page()

        # open video "A"
        self.course_nav.go_to_sequential('A')

        # check if video "A" should start playing at speed "2.0"
        self.assertEqual(self.video.get_speed(), '2.0x')

        # select the "1.0" speed on video "A"
        self.video.set_speed('1.0')

        # open video "B"
        self.course_nav.go_to_sequential('B')

        # check if video "B" should start playing at speed "0.50"
        self.assertEqual(self.video.get_speed(), '0.50x')

        # open video "C"
        self.course_nav.go_to_sequential('C')

        # check if video "C" should start playing at speed "1.0"
        self.assertEqual(self.video.get_speed(), '1.0x')

    def test_video_has_correct_transcript(self):
        """
        Scenario: Youtube video has correct transcript if fields for other speeds are filled
        Given it has a video in "Youtube" mode
        And I have uploaded multiple transcripts
        And I make sure captions are opened
        Then I see "Hi, welcome to Edx." text in the captions
        And I select the "1.50" speed
        And I reload the page with video
        Then I see "Hi, welcome to Edx." text in the captions
        And I see duration "1:56"

        """
        self.assets.extend(['subs_OEoXaMPEzfM.srt.sjson', 'subs_b7xgknqkQk8.srt.sjson'])
        data = {'sub': 'OEoXaMPEzfM', 'youtube_id_1_5': 'b7xgknqkQk8'}
        self.metadata = self.metadata_for_mode('youtube', additional_data=data)

        # go to video
        self.navigate_to_video()

        self.video.show_captions()

        self.assertIn('Hi, welcome to Edx.', self.video.captions_text())

        self.video.set_speed('1.50')

        self.video.reload_page()

        self.assertIn('Hi, welcome to Edx.', self.video.captions_text())

        self.assertTrue(self.video.duration(), '1.56')

    def test_video_position_stored_correctly_wo_seek(self):
        """
        Scenario: Video component stores position correctly when page is reloaded
        Given the course has a Video component in "Youtube" mode
        Then the video has rendered in "Youtube" mode
        And I click video button "play""
        Then I wait until video reaches at position "0.05"
        And I click video button "pause"
        And I reload the page with video
        And I click video button "play""
        And I click video button "pause"
        Then video slider should be Equal or Greater than "0:05"

        """
        self.navigate_to_video()

        self.video.click_player_button('play')

        self.video.wait_for_position('0:05')

        self.video.click_player_button('pause')

        self.video.reload_page()

        self.video.click_player_button('play')
        self.video.click_player_button('pause')

        self.assertGreaterEqual(int(self.video.position().split(':')[1]), 5)

686
    @skip("Intermittently fails 03 June 2014")
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    def test_video_position_stored_correctly_with_seek(self):
        """
        Scenario: Video component stores position correctly when page is reloaded
        Given the course has a Video component in "Youtube" mode
        Then the video has rendered in "Youtube" mode
        And I click video button "play""
        And I click video button "pause"
        Then I seek video to "0:10" position
        And I click video button "play""
        And I click video button "pause"
        And I reload the page with video
        Then video slider should be Equal or Greater than "0:10"

        """
        self.navigate_to_video()

        self.video.click_player_button('play')
        self.video.click_player_button('pause')

        self.video.seek('0:10')

        self.video.click_player_button('play')
        self.video.click_player_button('pause')

        self.video.reload_page()

        self.assertGreaterEqual(int(self.video.position().split(':')[1]), 10)

715 716 717 718 719 720 721

class YouTubeHtml5VideoTest(VideoBaseTest):
    """ Test YouTube HTML5 Video Player """

    def setUp(self):
        super(YouTubeHtml5VideoTest, self).setUp()

722
    def test_youtube_video_rendering_with_unsupported_sources(self):
723 724
        """
        Scenario: Video component is rendered in the LMS in Youtube mode
725
            with HTML5 sources that doesn't supported by browser
726 727 728
        Given the course has a Video component in "Youtube_HTML5_Unsupported_Video" mode
        Then the video has rendered in "Youtube" mode
        """
729
        self.metadata = self.metadata_for_mode('youtube_html5_unsupported_video')
730 731 732 733 734 735 736 737 738 739 740 741 742 743
        self.navigate_to_video()

        # Verify that the video has rendered in "Youtube" mode
        self.assertTrue(self.video.is_video_rendered('youtube'))


class Html5VideoTest(VideoBaseTest):
    """ Test HTML5 Video Player """

    def setUp(self):
        super(Html5VideoTest, self).setUp()

    def test_autoplay_disabled_for_video_component(self):
        """
744
        Scenario: Autoplay is disabled by default for a Video component
745
        Given the course has a Video component in "HTML5" mode
746
        When I view the Video component
747 748
        Then it does not have autoplay enabled
        """
749
        self.metadata = self.metadata_for_mode('html5')
750 751 752
        self.navigate_to_video()

        # Verify that the video has autoplay mode disabled
753
        self.assertFalse(self.video.is_autoplay_enabled())
754

755
    def test_html5_video_rendering_with_unsupported_sources(self):
756
        """
757
        Scenario: LMS displays an error message for HTML5 sources that are not supported by browser
758
        Given the course has a Video component in "HTML5_Unsupported_Video" mode
759 760 761
        When I view the Video component
        Then and error message is shown
        And the error message has the correct text
762
        """
763
        self.metadata = self.metadata_for_mode('html5_unsupported_video')
764 765 766
        self.navigate_to_video_no_render()

        # Verify that error message is shown
767
        self.assertTrue(self.video.is_error_message_shown())
768 769

        # Verify that error message has correct text
770
        correct_error_message_text = 'No playable video sources found.'
771
        self.assertIn(correct_error_message_text, self.video.error_message_text())
772

773 774 775
        # Verify that spinner is not shown
        self.assertFalse(self.video.is_spinner_shown())

776 777
    def test_download_button_wo_english_transcript(self):
        """
778 779 780 781 782 783
        Scenario: Download button works correctly w/o english transcript in HTML5 mode
        Given the course has a Video component in "HTML5" mode
        And I have defined a downloadable non-english transcript for the video
        And I have uploaded a non-english transcript file to assets
        Then I see the correct non-english text in the captions
        And the non-english transcript downloads correctly
784 785 786 787 788 789 790 791 792 793
        """
        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')
794
        self.assertIn(unicode_text, self.video.captions_text())
795 796 797

        # check if we can download transcript in "srt" format that has text "好 各位同学"
        unicode_text = "好 各位同学".decode('utf-8')
798
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', unicode_text))
799

800
    def test_download_button_two_transcript_languages(self):
801
        """
802 803 804 805 806 807 808 809
        Scenario: Download button works correctly for multiple transcript languages in HTML5 mode
        Given the course has a Video component in "HTML5" mode
        And I have defined a downloadable non-english transcript for the video
        And I have defined english subtitles for the video
        Then I see the correct english text in the captions
        And the english transcript downloads correctly
        And I see the correct non-english text in the captions
        And the non-english transcript downloads correctly
810 811 812 813 814 815 816 817 818
        """
        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
819
        self.assertIn('Hi, welcome to Edx.', self.video.captions_text())
820 821

        # check if we can download transcript in "srt" format that has text "Hi, welcome to Edx."
822
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', 'Hi, welcome to Edx.'))
823 824

        # select language with code "zh"
825
        self.assertTrue(self.video.select_language('zh'))
826 827 828 829

        # check if we see "好 各位同学" text in the captions
        unicode_text = "好 各位同学".decode('utf-8')

830
        self.assertIn(unicode_text, self.video.captions_text())
831 832 833

        #Then I can download transcript in "srt" format that has text "好 各位同学"
        unicode_text = "好 各位同学".decode('utf-8')
834
        self.assertTrue(self.video.downloaded_transcript_contains_text('srt', unicode_text))
835 836 837

    def test_full_screen_video_alignment_with_transcript_visible(self):
        """
838 839 840 841 842 843 844
        Scenario: Video is aligned correctly with transcript enabled in fullscreen mode
        Given the course has a Video component in "HTML5" mode
        And I have uploaded a .srt.sjson file to assets
        And I have defined subtitles for the video
        When I show the captions
        And I view the video at fullscreen
        Then the video with the transcript enabled is aligned correctly
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
        """
        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):
        """
864 865 866 867 868
        Scenario: CC button works correctly with only english transcript in HTML5 mode
        Given the course has a Video component in "HTML5" mode
        And I have defined english subtitles for the video
        And I have uploaded an english transcript file to assets
        Then I see the correct text in the captions
869 870 871 872 873 874 875 876 877 878 879 880
        """
        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
881
        self.assertIn("Hi, welcome to Edx.", self.video.captions_text())
882

883
    def test_cc_button_wo_english_transcript(self):
884
        """
885 886 887 888 889
        Scenario: CC button works correctly w/o english transcript in HTML5 mode
        Given the course has a Video component in "HTML5" mode
        And I have defined a non-english transcript for the video
        And I have uploaded a non-english transcript file to assets
        Then I see the correct text in the captions
890 891 892 893 894 895 896 897 898 899 900 901 902
        """
        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')
903
        self.assertIn(unicode_text, self.video.captions_text())
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918

    def test_video_rendering(self):
        """
        Scenario: Video component is fully rendered in the LMS in HTML5 mode
        Given the course has a Video component in "HTML5" mode
        Then the video has rendered in "HTML5" mode
        And video sources are correct
        """
        self.metadata = self.metadata_for_mode('html5')

        self.navigate_to_video()

        self.assertTrue(self.video.is_video_rendered('html5'))

        self.assertTrue(all([source in HTML5_SOURCES for source in self.video.sources()]))