Bok-Choy CMS Video Tests
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Test helper functions and base classes.
|
||||
"""
|
||||
import json
|
||||
import unittest
|
||||
import functools
|
||||
import requests
|
||||
@@ -194,3 +195,77 @@ class UniqueCourseTest(WebAppTest):
|
||||
self.course_info['number'],
|
||||
self.course_info['run']
|
||||
])
|
||||
|
||||
|
||||
class YouTubeConfigError(Exception):
|
||||
"""
|
||||
Error occurred while configuring YouTube Stub Server.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class YouTubeStubConfig(object):
|
||||
"""
|
||||
Configure YouTube Stub Server.
|
||||
"""
|
||||
|
||||
PORT = 9080
|
||||
URL = 'http://127.0.0.1:{}/'.format(PORT)
|
||||
|
||||
@classmethod
|
||||
def configure(cls, config):
|
||||
"""
|
||||
Allow callers to configure the stub server using the /set_config URL.
|
||||
|
||||
Arguments:
|
||||
config (dict): Configuration dictionary.
|
||||
|
||||
Raises:
|
||||
YouTubeConfigError
|
||||
|
||||
"""
|
||||
youtube_stub_config_url = cls.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))
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
"""
|
||||
Reset YouTube Stub Server Configurations using the /del_config URL.
|
||||
|
||||
Raises:
|
||||
YouTubeConfigError
|
||||
|
||||
"""
|
||||
youtube_stub_config_url = cls.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))
|
||||
|
||||
@classmethod
|
||||
def get_configuration(cls):
|
||||
"""
|
||||
Allow callers to get current stub server configuration.
|
||||
|
||||
Returns:
|
||||
dict
|
||||
|
||||
"""
|
||||
youtube_stub_config_url = cls.URL + 'get_config'
|
||||
|
||||
response = requests.get(youtube_stub_config_url)
|
||||
|
||||
if response.ok:
|
||||
return json.loads(response.content)
|
||||
else:
|
||||
return {}
|
||||
|
||||
@@ -7,9 +7,9 @@ Acceptance tests for CMS Video Module.
|
||||
from unittest import skipIf
|
||||
from ...pages.studio.auto_auth import AutoAuthPage
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.studio.video.video import VidoComponentPage
|
||||
from ...pages.studio.video.video import VideoComponentPage
|
||||
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from ..helpers import UniqueCourseTest, is_youtube_available
|
||||
from ..helpers import UniqueCourseTest, is_youtube_available, YouTubeStubConfig
|
||||
|
||||
|
||||
@skipIf(is_youtube_available() is False, 'YouTube is not available!')
|
||||
@@ -24,7 +24,7 @@ class CMSVideoBaseTest(UniqueCourseTest):
|
||||
"""
|
||||
super(CMSVideoBaseTest, self).setUp()
|
||||
|
||||
self.video = VidoComponentPage(self.browser)
|
||||
self.video = VideoComponentPage(self.browser)
|
||||
|
||||
# This will be initialized later
|
||||
self.unit_page = None
|
||||
@@ -41,6 +41,8 @@ class CMSVideoBaseTest(UniqueCourseTest):
|
||||
self.course_info['run'], self.course_info['display_name']
|
||||
)
|
||||
|
||||
self.assets = []
|
||||
|
||||
def _install_course_fixture(self):
|
||||
"""
|
||||
Prepare for tests by creating a course with a section, subsection, and unit.
|
||||
@@ -49,13 +51,15 @@ class CMSVideoBaseTest(UniqueCourseTest):
|
||||
Create a user and make that user a course author
|
||||
Log the user into studio
|
||||
"""
|
||||
if self.assets:
|
||||
self.course_fixture.add_asset(self.assets)
|
||||
|
||||
# Create course with Video component
|
||||
self.course_fixture.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
|
||||
XBlockFixtureDesc("vertical", "Test Unit").add_children(
|
||||
XBlockFixtureDesc('video', 'Video'),
|
||||
XBlockFixtureDesc('vertical', 'Test Unit').add_children(
|
||||
XBlockFixtureDesc('video', 'Video')
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -95,17 +99,194 @@ class CMSVideoBaseTest(UniqueCourseTest):
|
||||
"""
|
||||
Open component Edit Dialog for first component on page.
|
||||
"""
|
||||
self.unit_page.xblocks[0].edit()
|
||||
# The 0th entry is the unit page itself.
|
||||
self.unit_page.xblocks[1].edit()
|
||||
|
||||
def open_advanced_tab(self):
|
||||
"""
|
||||
Open components advanced tab.
|
||||
"""
|
||||
self.unit_page.xblocks[0].open_advanced_tab()
|
||||
# The 0th entry is the unit page itself.
|
||||
self.unit_page.xblocks[1].open_advanced_tab()
|
||||
|
||||
def save_unit_settings(self):
|
||||
"""
|
||||
Save component settings.
|
||||
"""
|
||||
self.unit_page.xblocks[0].save_settings()
|
||||
# The 0th entry is the unit page itself.
|
||||
self.unit_page.xblocks[1].save_settings()
|
||||
|
||||
|
||||
class CMSVideoTest(CMSVideoBaseTest):
|
||||
"""
|
||||
CMS Video Test Class
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(CMSVideoTest, self).setUp()
|
||||
|
||||
self.addCleanup(YouTubeStubConfig.reset)
|
||||
|
||||
def _create_course_unit(self, youtube_stub_config=None, subtitles=False):
|
||||
"""
|
||||
Create a Studio Video Course Unit and Navigate to it.
|
||||
|
||||
Arguments:
|
||||
youtube_stub_config (dict)
|
||||
subtitles (bool)
|
||||
|
||||
"""
|
||||
if youtube_stub_config:
|
||||
YouTubeStubConfig.configure(youtube_stub_config)
|
||||
|
||||
if subtitles:
|
||||
self.assets.append('subs_OEoXaMPEzfM.srt.sjson')
|
||||
|
||||
self.navigate_to_course_unit()
|
||||
|
||||
def _create_video(self):
|
||||
"""
|
||||
Create Xblock Video Component.
|
||||
"""
|
||||
self.video.create_video()
|
||||
|
||||
video_xblocks = self.video.xblocks()
|
||||
|
||||
# Total video xblock components count should be equals to 2
|
||||
# Why 2? One video component is created by default for each test. Please see
|
||||
# test_studio_video_module.py:CMSVideoTest._create_course_unit
|
||||
# And we are creating second video component here.
|
||||
self.assertTrue(video_xblocks == 2)
|
||||
|
||||
def test_youtube_stub_proxy(self):
|
||||
"""
|
||||
Scenario: YouTube stub server proxies YouTube API correctly
|
||||
Given youtube stub server proxies YouTube API
|
||||
And I have created a Video component
|
||||
Then I can see video button "play"
|
||||
And I click video button "play"
|
||||
Then I can see video button "pause"
|
||||
"""
|
||||
self._create_course_unit(youtube_stub_config={'youtube_api_blocked': False})
|
||||
|
||||
self.assertTrue(self.video.is_button_shown('play'))
|
||||
self.video.click_player_button('play')
|
||||
self.assertTrue(self.video.is_button_shown('pause'))
|
||||
|
||||
def test_youtube_stub_blocks_youtube_api(self):
|
||||
"""
|
||||
Scenario: YouTube stub server can block YouTube API
|
||||
Given youtube stub server blocks YouTube API
|
||||
And I have created a Video component
|
||||
Then I do not see video button "play"
|
||||
"""
|
||||
self._create_course_unit(youtube_stub_config={'youtube_api_blocked': True})
|
||||
|
||||
self.assertFalse(self.video.is_button_shown('play'))
|
||||
|
||||
def test_autoplay_is_disabled(self):
|
||||
"""
|
||||
Scenario: Autoplay is disabled in Studio
|
||||
Given I have created a Video component
|
||||
Then when I view the video it does not have autoplay enabled
|
||||
"""
|
||||
self._create_course_unit()
|
||||
|
||||
self.assertFalse(self.video.is_autoplay_enabled)
|
||||
|
||||
def test_video_creation_takes_single_click(self):
|
||||
"""
|
||||
Scenario: Creating a video takes a single click
|
||||
And creating a video takes a single click
|
||||
"""
|
||||
self._create_course_unit()
|
||||
|
||||
# This will create a video by doing a single click and then ensure that video is created
|
||||
self._create_video()
|
||||
|
||||
def test_captions_hidden_correctly(self):
|
||||
"""
|
||||
Scenario: Captions are hidden correctly
|
||||
Given I have created a Video component with subtitles
|
||||
And I have hidden captions
|
||||
Then when I view the video it does not show the captions
|
||||
"""
|
||||
self._create_course_unit(subtitles=True)
|
||||
|
||||
self.video.hide_captions()
|
||||
|
||||
self.assertFalse(self.video.is_captions_visible())
|
||||
|
||||
def test_captions_shown_correctly(self):
|
||||
"""
|
||||
Scenario: Captions are shown correctly
|
||||
Given I have created a Video component with subtitles
|
||||
Then when I view the video it does show the captions
|
||||
"""
|
||||
self._create_course_unit(subtitles=True)
|
||||
|
||||
self.assertTrue(self.video.is_captions_visible())
|
||||
|
||||
def test_captions_toggling(self):
|
||||
"""
|
||||
Scenario: Captions are toggled correctly
|
||||
Given I have created a Video component with subtitles
|
||||
And I have toggled captions
|
||||
Then when I view the video it does show the captions
|
||||
"""
|
||||
self._create_course_unit(subtitles=True)
|
||||
|
||||
self.video.click_player_button('CC')
|
||||
|
||||
self.assertFalse(self.video.is_captions_visible())
|
||||
|
||||
self.video.click_player_button('CC')
|
||||
|
||||
self.assertTrue(self.video.is_captions_visible())
|
||||
|
||||
def test_caption_line_focus(self):
|
||||
"""
|
||||
Scenario: When enter key is pressed on a caption, an outline shows around it
|
||||
Given I have created a Video component with subtitles
|
||||
And Make sure captions are opened
|
||||
Then I focus on first caption line
|
||||
And I see first caption line has focused
|
||||
"""
|
||||
self._create_course_unit(subtitles=True)
|
||||
|
||||
self.video.show_captions()
|
||||
|
||||
self.video.focus_caption_line(1)
|
||||
|
||||
self.assertTrue(self.video.is_caption_line_focused(1))
|
||||
|
||||
def test_slider_range_works(self):
|
||||
"""
|
||||
Scenario: When start and end times are specified, a range on slider is shown
|
||||
Given I have created a Video component with subtitles
|
||||
And Make sure captions are closed
|
||||
And I edit the component
|
||||
And I open tab "Advanced"
|
||||
And I set value "00:00:12" to the field "Video Start Time"
|
||||
And I set value "00:00:24" to the field "Video Stop Time"
|
||||
And I save changes
|
||||
And I click video button "play"
|
||||
Then I see a range on slider
|
||||
"""
|
||||
self._create_course_unit(subtitles=True)
|
||||
|
||||
self.video.hide_captions()
|
||||
|
||||
self.edit_component()
|
||||
|
||||
self.open_advanced_tab()
|
||||
|
||||
self.video.set_settings_field_value('Video Start Time', '00:00:12')
|
||||
|
||||
self.video.set_settings_field_value('Video Stop Time', '00:00:24')
|
||||
|
||||
self.save_unit_settings()
|
||||
|
||||
self.video.click_player_button('play')
|
||||
|
||||
self.assertTrue(self.video.is_slider_range_visible)
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
Acceptance tests for Video.
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest import skipIf, skip
|
||||
import requests
|
||||
from ..helpers import UniqueCourseTest, is_youtube_available
|
||||
from ..helpers import UniqueCourseTest, is_youtube_available, YouTubeStubConfig
|
||||
from ...pages.lms.video.video import VideoPage
|
||||
from ...pages.lms.tab_nav import TabNavPage
|
||||
from ...pages.lms.course_nav import CourseNavPage
|
||||
@@ -18,8 +16,6 @@ from ..helpers import skip_if_browser
|
||||
|
||||
|
||||
VIDEO_SOURCE_PORT = 8777
|
||||
YOUTUBE_STUB_PORT = 9080
|
||||
YOUTUBE_STUB_URL = 'http://127.0.0.1:{}/'.format(YOUTUBE_STUB_PORT)
|
||||
|
||||
HTML5_SOURCES = [
|
||||
'http://localhost:{0}/gizmo.mp4'.format(VIDEO_SOURCE_PORT),
|
||||
@@ -32,13 +28,6 @@ HTML5_SOURCES_INCORRECT = [
|
||||
]
|
||||
|
||||
|
||||
class YouTubeConfigError(Exception):
|
||||
"""
|
||||
Error occurred while configuring YouTube Stub Server.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@skipIf(is_youtube_available() is False, 'YouTube is not available!')
|
||||
class VideoBaseTest(UniqueCourseTest):
|
||||
"""
|
||||
@@ -68,7 +57,7 @@ class VideoBaseTest(UniqueCourseTest):
|
||||
self.youtube_configuration = {}
|
||||
|
||||
# reset youtube stub server
|
||||
self.addCleanup(self._reset_youtube_stub_server)
|
||||
self.addCleanup(YouTubeStubConfig.reset)
|
||||
|
||||
def navigate_to_video(self):
|
||||
""" Prepare the course and get to the video and render it """
|
||||
@@ -96,7 +85,7 @@ class VideoBaseTest(UniqueCourseTest):
|
||||
self.course_fixture.install()
|
||||
|
||||
if len(self.youtube_configuration) > 0:
|
||||
self._configure_youtube_stub_server(self.youtube_configuration)
|
||||
YouTubeStubConfig.configure(self.youtube_configuration)
|
||||
|
||||
def _add_course_verticals(self):
|
||||
"""
|
||||
@@ -148,39 +137,6 @@ class VideoBaseTest(UniqueCourseTest):
|
||||
self._navigate_to_courseware_video()
|
||||
self.video.wait_for_video_class()
|
||||
|
||||
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))
|
||||
|
||||
def metadata_for_mode(self, player_mode, additional_data=None):
|
||||
"""
|
||||
Create a dictionary for video player configuration according to `player_mode`
|
||||
|
||||
Reference in New Issue
Block a user