Bok-Choy CMS Video Tests
This commit is contained in:
@@ -2,83 +2,7 @@
|
||||
Feature: CMS Video Component
|
||||
As a course author, I want to be able to view my created videos in Studio
|
||||
|
||||
# 1
|
||||
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"
|
||||
|
||||
# 2
|
||||
Scenario: YouTube stub server can block YouTube API
|
||||
Given youtube stub server blocks YouTube API
|
||||
And I have created a Video component
|
||||
And I wait for "3" seconds
|
||||
Then I do not see video button "play"
|
||||
|
||||
# 3
|
||||
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
|
||||
|
||||
# 4
|
||||
Scenario: Creating a video takes a single click
|
||||
Given I have clicked the new unit button
|
||||
Then creating a video takes a single click
|
||||
|
||||
# 5
|
||||
# Sauce Labs cannot delete cookies
|
||||
@skip_sauce
|
||||
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
|
||||
|
||||
# 6
|
||||
# Sauce Labs cannot delete cookies
|
||||
@skip_sauce
|
||||
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
|
||||
|
||||
# 7
|
||||
# Sauce Labs cannot delete cookies
|
||||
@skip_sauce
|
||||
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
|
||||
|
||||
# 8
|
||||
Scenario: Video data is shown correctly
|
||||
Given I have created a video with only XML data
|
||||
And I reload the page
|
||||
Then the correct Youtube video is shown
|
||||
|
||||
# 9
|
||||
# Disabled 11/26 due to flakiness in master.
|
||||
# Enabled back on 11/29.
|
||||
Scenario: When enter key is pressed on a caption shows an outline around it
|
||||
Given I have created a Video component with subtitles
|
||||
And Make sure captions are opened
|
||||
Then I focus on caption line with data-index "0"
|
||||
Then I press "enter" button on caption line with data-index "0"
|
||||
And I see caption line with data-index "0" has class "focused"
|
||||
|
||||
# 10
|
||||
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
|
||||
|
||||
# 11
|
||||
# Disabled 2/19/14 after intermittent failures in master
|
||||
#Scenario: Check that position is stored on page refresh, position within start-end range
|
||||
# Given I have created a Video component with subtitles
|
||||
@@ -96,7 +20,7 @@ Feature: CMS Video Component
|
||||
# And I click video button "play"
|
||||
# Then I see video starts playing from "0:16" position
|
||||
|
||||
# 12
|
||||
# 3
|
||||
# Disabled 2/18/14 after intermittent failures in master
|
||||
# Scenario: Check that position is stored on page refresh, position before start-end range
|
||||
# Given I have created a Video component with subtitles
|
||||
@@ -114,7 +38,7 @@ Feature: CMS Video Component
|
||||
# And I click video button "play"
|
||||
# Then I see video starts playing from "0:12" position
|
||||
|
||||
# 13
|
||||
# 4
|
||||
# Disabled 2/18/14 after intermittent failures in master
|
||||
# Scenario: Check that position is stored on page refresh, position after start-end range
|
||||
# Given I have created a Video component with subtitles
|
||||
|
||||
@@ -55,7 +55,10 @@ class StubYouTubeHandler(StubHttpRequestHandler):
|
||||
"Youtube provider received GET request to path {}".format(self.path)
|
||||
)
|
||||
|
||||
if 'test_transcripts_youtube' in self.path:
|
||||
if 'get_config' in self.path:
|
||||
self.send_json_response(self.server.config)
|
||||
|
||||
elif 'test_transcripts_youtube' in self.path:
|
||||
|
||||
if 't__eq_exist' in self.path:
|
||||
status_message = "".join([
|
||||
|
||||
@@ -122,20 +122,24 @@ class VideoPage(PageObject):
|
||||
else:
|
||||
return '.vert.vert-0'
|
||||
|
||||
def get_element_selector(self, class_name):
|
||||
def get_element_selector(self, class_name, vertical=True):
|
||||
"""
|
||||
Construct unique element selector.
|
||||
|
||||
Arguments:
|
||||
class_name (str): css class name for an element.
|
||||
vertical (bool): do we need vertical css selector or not. vertical css selector is not present in Studio
|
||||
|
||||
Returns:
|
||||
str: Element Selector.
|
||||
|
||||
"""
|
||||
return '{vertical} {video_element}'.format(
|
||||
vertical=self.get_video_vertical_selector(self.current_video_display_name),
|
||||
video_element=class_name)
|
||||
if vertical:
|
||||
return '{vertical} {video_element}'.format(
|
||||
vertical=self.get_video_vertical_selector(self.current_video_display_name),
|
||||
video_element=class_name)
|
||||
else:
|
||||
return class_name
|
||||
|
||||
def use_video(self, video_display_name):
|
||||
"""
|
||||
@@ -253,6 +257,17 @@ class VideoPage(PageObject):
|
||||
"""
|
||||
self._captions_visibility(False)
|
||||
|
||||
def is_captions_visible(self):
|
||||
"""
|
||||
Get current visibility sate of captions.
|
||||
|
||||
Returns:
|
||||
bool: True means captions are visible, False means captions are not visible
|
||||
|
||||
"""
|
||||
caption_state_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
|
||||
return not self.q(css=caption_state_selector).present
|
||||
|
||||
@wait_for_js
|
||||
def _captions_visibility(self, captions_new_state):
|
||||
"""
|
||||
@@ -265,28 +280,16 @@ class VideoPage(PageObject):
|
||||
states = {True: 'Shown', False: 'Hidden'}
|
||||
state = states[captions_new_state]
|
||||
|
||||
caption_state_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
|
||||
|
||||
def _captions_current_state():
|
||||
"""
|
||||
Get current visibility sate of captions.
|
||||
|
||||
Returns:
|
||||
bool: True means captions are visible, False means captions are not visible
|
||||
|
||||
"""
|
||||
return not self.q(css=caption_state_selector).present
|
||||
|
||||
# Make sure that the CC button is there
|
||||
EmptyPromise(lambda: self.is_button_shown('CC'),
|
||||
"CC button is shown").fulfill()
|
||||
|
||||
# toggle captions visibility state if needed
|
||||
if _captions_current_state() != captions_new_state:
|
||||
if self.is_captions_visible() != captions_new_state:
|
||||
self.click_player_button('CC')
|
||||
|
||||
# Verify that captions state is toggled/changed
|
||||
EmptyPromise(lambda: _captions_current_state() == captions_new_state,
|
||||
EmptyPromise(lambda: self.is_captions_visible() == captions_new_state,
|
||||
"Captions are {state}".format(state=state)).fulfill()
|
||||
|
||||
@property
|
||||
|
||||
@@ -4,20 +4,27 @@ CMS Video
|
||||
|
||||
import os
|
||||
import requests
|
||||
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 YouTubeStubConfig
|
||||
from ...lms.video.video import VideoPage
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
|
||||
CLASS_SELECTORS = {
|
||||
'video_container': 'div.video',
|
||||
'video_init': '.is-initialized',
|
||||
'video_xmodule': '.xmodule_VideoModule',
|
||||
'video_spinner': '.video-wrapper .spinner',
|
||||
'video_controls': 'section.video-controls',
|
||||
'attach_handout': '.upload-dialog > input[type="file"]',
|
||||
'upload_dialog': '.wrapper-modal-window-assetupload',
|
||||
'xblock': '.add-xblock-component',
|
||||
'slider_range': '.slider-range',
|
||||
}
|
||||
|
||||
BUTTON_SELECTORS = {
|
||||
'create_video': 'a[data-category="video"]',
|
||||
'handout_download': '.video-handout.video-download-button a',
|
||||
'handout_download_editor': '.wrapper-comp-setting.file-uploader .download-action',
|
||||
'upload_handout': '.upload-action',
|
||||
@@ -28,7 +35,7 @@ BUTTON_SELECTORS = {
|
||||
|
||||
@js_defined('window.Video', 'window.RequireJS.require', 'window.jQuery', 'window.XModule', 'window.XBlock',
|
||||
'window.MathJax.isReady')
|
||||
class VidoComponentPage(PageObject):
|
||||
class VideoComponentPage(VideoPage):
|
||||
"""
|
||||
CMS Video Component Page
|
||||
"""
|
||||
@@ -37,7 +44,11 @@ class VidoComponentPage(PageObject):
|
||||
|
||||
@wait_for_js
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='div{0}'.format(CLASS_SELECTORS['video_xmodule'])).present
|
||||
return self.q(css='div{0}'.format(CLASS_SELECTORS['video_xmodule'])).present or self.q(
|
||||
css='div{0}'.format(CLASS_SELECTORS['xblock'])).present
|
||||
|
||||
def get_element_selector(self, class_name, vertical=False):
|
||||
return super(VideoComponentPage, self).get_element_selector(class_name, vertical=vertical)
|
||||
|
||||
def _wait_for(self, check_func, desc, result=False, timeout=30):
|
||||
"""
|
||||
@@ -59,9 +70,10 @@ class VidoComponentPage(PageObject):
|
||||
"""
|
||||
Wait until video component rendered completely
|
||||
"""
|
||||
self._wait_for(lambda: self.q(css=CLASS_SELECTORS['video_init']).present, 'Video Player Initialized')
|
||||
self._wait_for(lambda: not self.q(css=CLASS_SELECTORS['video_spinner']).visible, 'Video Buffering Completed')
|
||||
self._wait_for(lambda: self.q(css=CLASS_SELECTORS['video_controls']).visible, 'Player Controls are Visible')
|
||||
if not YouTubeStubConfig.get_configuration().get('youtube_api_blocked'):
|
||||
self._wait_for(lambda: self.q(css=CLASS_SELECTORS['video_init']).present, 'Video Player Initialized')
|
||||
self._wait_for(lambda: not self.q(css=CLASS_SELECTORS['video_spinner']).visible, 'Video Buffering Completed')
|
||||
self._wait_for(lambda: self.q(css=CLASS_SELECTORS['video_controls']).visible, 'Player Controls are Visible')
|
||||
|
||||
def click_button(self, button_name):
|
||||
"""
|
||||
@@ -74,6 +86,17 @@ class VidoComponentPage(PageObject):
|
||||
self.q(css=BUTTON_SELECTORS[button_name]).first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
@staticmethod
|
||||
def file_path(filename):
|
||||
"""
|
||||
Construct file path to be uploaded to assets.
|
||||
|
||||
Arguments:
|
||||
filename (str): asset filename
|
||||
|
||||
"""
|
||||
return os.sep.join(__file__.split(os.sep)[:-5]) + '/data/uploads/' + filename
|
||||
|
||||
def upload_handout(self, handout_filename):
|
||||
"""
|
||||
Upload a handout file to assets
|
||||
@@ -82,7 +105,7 @@ class VidoComponentPage(PageObject):
|
||||
handout_filename (str): handout file name
|
||||
|
||||
"""
|
||||
handout_path = os.sep.join(__file__.split(os.sep)[:-5]) + '/data/uploads/' + handout_filename
|
||||
handout_path = self.file_path(handout_filename)
|
||||
|
||||
self.click_button('upload_handout')
|
||||
|
||||
@@ -137,6 +160,77 @@ class VidoComponentPage(PageObject):
|
||||
"""
|
||||
Check if handout download button is visible
|
||||
"""
|
||||
# TODO! Remove .present below after bok-choy is updated to latest commit, Only .visible is enough
|
||||
return self.q(css=BUTTON_SELECTORS['handout_download']).present and self.q(
|
||||
css=BUTTON_SELECTORS['handout_download']).visible
|
||||
return self.q(css=BUTTON_SELECTORS['handout_download']).visible
|
||||
|
||||
def create_video(self):
|
||||
"""
|
||||
Create a Video Component by clicking on Video button and wait for rendering to complete.
|
||||
"""
|
||||
# Create video
|
||||
self.click_button('create_video')
|
||||
self.wait_for_video_component_render()
|
||||
|
||||
def xblocks(self):
|
||||
"""
|
||||
Tells the total number of video xblocks present on current unit page.
|
||||
|
||||
Returns:
|
||||
(int): total video xblocks
|
||||
|
||||
"""
|
||||
return len(self.q(css='.xblock-header').filter(
|
||||
lambda el: 'xblock-header-video' in el.get_attribute('class')).results)
|
||||
|
||||
def focus_caption_line(self, line_number):
|
||||
"""
|
||||
Focus a caption line as specified by `line_number`
|
||||
|
||||
Arguments:
|
||||
line_number (int): caption line number
|
||||
|
||||
"""
|
||||
caption_line_selector = ".subtitles > li[data-index='{index}']".format(index=line_number - 1)
|
||||
self.q(css=caption_line_selector).results[0].send_keys(Keys.ENTER)
|
||||
|
||||
def is_caption_line_focused(self, line_number):
|
||||
"""
|
||||
Check if a caption line focused
|
||||
|
||||
Arguments:
|
||||
line_number (int): caption line number
|
||||
|
||||
"""
|
||||
caption_line_selector = ".subtitles > li[data-index='{index}']".format(index=line_number - 1)
|
||||
attributes = self.q(css=caption_line_selector).attrs('class')
|
||||
|
||||
return 'focused' in attributes
|
||||
|
||||
def set_settings_field_value(self, field, value):
|
||||
"""
|
||||
In Advanced Tab set `field` with `value`
|
||||
|
||||
Arguments:
|
||||
field (str): field name
|
||||
value (str): field value
|
||||
|
||||
"""
|
||||
query = '.wrapper-comp-setting > label:nth-child(1)'
|
||||
field_id = ''
|
||||
|
||||
for index, _ in enumerate(self.q(css=query)):
|
||||
if field in self.q(css=query).nth(index).text[0]:
|
||||
field_id = self.q(css=query).nth(index).attrs('for')[0]
|
||||
break
|
||||
|
||||
self.q(css='#{}'.format(field_id)).fill(value)
|
||||
|
||||
@property
|
||||
def is_slider_range_visible(self):
|
||||
"""
|
||||
Check if slider range visible.
|
||||
|
||||
Returns:
|
||||
bool: slider range is visible or not
|
||||
|
||||
"""
|
||||
return self.q(css=CLASS_SELECTORS['slider_range']).visible
|
||||
|
||||
@@ -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