Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
936bd4d5
Commit
936bd4d5
authored
Aug 11, 2014
by
Muhammad Ammar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bok-Choy CMS Video Tests
parent
619f72f3
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
398 additions
and
162 deletions
+398
-162
cms/djangoapps/contentstore/features/video.feature
+2
-78
common/djangoapps/terrain/stubs/youtube.py
+4
-1
common/test/acceptance/pages/lms/video/video.py
+21
-18
common/test/acceptance/pages/studio/video/video.py
+104
-10
common/test/acceptance/tests/helpers.py
+75
-0
common/test/acceptance/tests/video/test_studio_video_module.py
+189
-8
common/test/acceptance/tests/video/test_video_module.py
+3
-47
No files found.
cms/djangoapps/contentstore/features/video.feature
View file @
936bd4d5
...
@@ -2,83 +2,7 @@
...
@@ -2,83 +2,7 @@
Feature
:
CMS Video Component
Feature
:
CMS Video Component
As a course author, I want to be able to view my created videos in Studio
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
# 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
# Disabled 2/19/14 after intermittent failures in master
#Scenario: Check that position is stored on page refresh, position within start-end range
#Scenario: Check that position is stored on page refresh, position within start-end range
# Given I have created a Video component with subtitles
# Given I have created a Video component with subtitles
...
@@ -96,7 +20,7 @@ Feature: CMS Video Component
...
@@ -96,7 +20,7 @@ Feature: CMS Video Component
# And I click video button "play"
# And I click video button "play"
# Then I see video starts playing from "0:16" position
# Then I see video starts playing from "0:16" position
#
12
#
3
# Disabled 2/18/14 after intermittent failures in master
# Disabled 2/18/14 after intermittent failures in master
# Scenario: Check that position is stored on page refresh, position before start-end range
# Scenario: Check that position is stored on page refresh, position before start-end range
# Given I have created a Video component with subtitles
# Given I have created a Video component with subtitles
...
@@ -114,7 +38,7 @@ Feature: CMS Video Component
...
@@ -114,7 +38,7 @@ Feature: CMS Video Component
# And I click video button "play"
# And I click video button "play"
# Then I see video starts playing from "0:12" position
# Then I see video starts playing from "0:12" position
#
13
#
4
# Disabled 2/18/14 after intermittent failures in master
# Disabled 2/18/14 after intermittent failures in master
# Scenario: Check that position is stored on page refresh, position after start-end range
# Scenario: Check that position is stored on page refresh, position after start-end range
# Given I have created a Video component with subtitles
# Given I have created a Video component with subtitles
...
...
common/djangoapps/terrain/stubs/youtube.py
View file @
936bd4d5
...
@@ -55,7 +55,10 @@ class StubYouTubeHandler(StubHttpRequestHandler):
...
@@ -55,7 +55,10 @@ class StubYouTubeHandler(StubHttpRequestHandler):
"Youtube provider received GET request to path {}"
.
format
(
self
.
path
)
"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
:
if
't__eq_exist'
in
self
.
path
:
status_message
=
""
.
join
([
status_message
=
""
.
join
([
...
...
common/test/acceptance/pages/lms/video/video.py
View file @
936bd4d5
...
@@ -122,20 +122,24 @@ class VideoPage(PageObject):
...
@@ -122,20 +122,24 @@ class VideoPage(PageObject):
else
:
else
:
return
'.vert.vert-0'
return
'.vert.vert-0'
def
get_element_selector
(
self
,
class_name
):
def
get_element_selector
(
self
,
class_name
,
vertical
=
True
):
"""
"""
Construct unique element selector.
Construct unique element selector.
Arguments:
Arguments:
class_name (str): css class name for an element.
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:
Returns:
str: Element Selector.
str: Element Selector.
"""
"""
return
'{vertical} {video_element}'
.
format
(
if
vertical
:
vertical
=
self
.
get_video_vertical_selector
(
self
.
current_video_display_name
),
return
'{vertical} {video_element}'
.
format
(
video_element
=
class_name
)
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
):
def
use_video
(
self
,
video_display_name
):
"""
"""
...
@@ -253,6 +257,17 @@ class VideoPage(PageObject):
...
@@ -253,6 +257,17 @@ class VideoPage(PageObject):
"""
"""
self
.
_captions_visibility
(
False
)
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
@wait_for_js
def
_captions_visibility
(
self
,
captions_new_state
):
def
_captions_visibility
(
self
,
captions_new_state
):
"""
"""
...
@@ -265,28 +280,16 @@ class VideoPage(PageObject):
...
@@ -265,28 +280,16 @@ class VideoPage(PageObject):
states
=
{
True
:
'Shown'
,
False
:
'Hidden'
}
states
=
{
True
:
'Shown'
,
False
:
'Hidden'
}
state
=
states
[
captions_new_state
]
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
# Make sure that the CC button is there
EmptyPromise
(
lambda
:
self
.
is_button_shown
(
'CC'
),
EmptyPromise
(
lambda
:
self
.
is_button_shown
(
'CC'
),
"CC button is shown"
)
.
fulfill
()
"CC button is shown"
)
.
fulfill
()
# toggle captions visibility state if needed
# toggle captions visibility state if needed
if
_captions_current_stat
e
()
!=
captions_new_state
:
if
self
.
is_captions_visibl
e
()
!=
captions_new_state
:
self
.
click_player_button
(
'CC'
)
self
.
click_player_button
(
'CC'
)
# Verify that captions state is toggled/changed
# Verify that captions state is toggled/changed
EmptyPromise
(
lambda
:
_captions_current_stat
e
()
==
captions_new_state
,
EmptyPromise
(
lambda
:
self
.
is_captions_visibl
e
()
==
captions_new_state
,
"Captions are {state}"
.
format
(
state
=
state
))
.
fulfill
()
"Captions are {state}"
.
format
(
state
=
state
))
.
fulfill
()
@property
@property
...
...
common/test/acceptance/pages/studio/video/video.py
View file @
936bd4d5
...
@@ -4,20 +4,27 @@ CMS Video
...
@@ -4,20 +4,27 @@ CMS Video
import
os
import
os
import
requests
import
requests
from
bok_choy.page_object
import
PageObject
from
bok_choy.promise
import
EmptyPromise
,
Promise
from
bok_choy.promise
import
EmptyPromise
,
Promise
from
bok_choy.javascript
import
wait_for_js
,
js_defined
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
=
{
CLASS_SELECTORS
=
{
'video_container'
:
'div.video'
,
'video_init'
:
'.is-initialized'
,
'video_init'
:
'.is-initialized'
,
'video_xmodule'
:
'.xmodule_VideoModule'
,
'video_xmodule'
:
'.xmodule_VideoModule'
,
'video_spinner'
:
'.video-wrapper .spinner'
,
'video_spinner'
:
'.video-wrapper .spinner'
,
'video_controls'
:
'section.video-controls'
,
'video_controls'
:
'section.video-controls'
,
'attach_handout'
:
'.upload-dialog > input[type="file"]'
,
'attach_handout'
:
'.upload-dialog > input[type="file"]'
,
'upload_dialog'
:
'.wrapper-modal-window-assetupload'
,
'upload_dialog'
:
'.wrapper-modal-window-assetupload'
,
'xblock'
:
'.add-xblock-component'
,
'slider_range'
:
'.slider-range'
,
}
}
BUTTON_SELECTORS
=
{
BUTTON_SELECTORS
=
{
'create_video'
:
'a[data-category="video"]'
,
'handout_download'
:
'.video-handout.video-download-button a'
,
'handout_download'
:
'.video-handout.video-download-button a'
,
'handout_download_editor'
:
'.wrapper-comp-setting.file-uploader .download-action'
,
'handout_download_editor'
:
'.wrapper-comp-setting.file-uploader .download-action'
,
'upload_handout'
:
'.upload-action'
,
'upload_handout'
:
'.upload-action'
,
...
@@ -28,7 +35,7 @@ BUTTON_SELECTORS = {
...
@@ -28,7 +35,7 @@ BUTTON_SELECTORS = {
@js_defined
(
'window.Video'
,
'window.RequireJS.require'
,
'window.jQuery'
,
'window.XModule'
,
'window.XBlock'
,
@js_defined
(
'window.Video'
,
'window.RequireJS.require'
,
'window.jQuery'
,
'window.XModule'
,
'window.XBlock'
,
'window.MathJax.isReady'
)
'window.MathJax.isReady'
)
class
Vid
oComponentPage
(
PageObject
):
class
Vid
eoComponentPage
(
VideoPage
):
"""
"""
CMS Video Component Page
CMS Video Component Page
"""
"""
...
@@ -37,7 +44,11 @@ class VidoComponentPage(PageObject):
...
@@ -37,7 +44,11 @@ class VidoComponentPage(PageObject):
@wait_for_js
@wait_for_js
def
is_browser_on_page
(
self
):
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
):
def
_wait_for
(
self
,
check_func
,
desc
,
result
=
False
,
timeout
=
30
):
"""
"""
...
@@ -59,9 +70,10 @@ class VidoComponentPage(PageObject):
...
@@ -59,9 +70,10 @@ class VidoComponentPage(PageObject):
"""
"""
Wait until video component rendered completely
Wait until video component rendered completely
"""
"""
self
.
_wait_for
(
lambda
:
self
.
q
(
css
=
CLASS_SELECTORS
[
'video_init'
])
.
present
,
'Video Player Initialized'
)
if
not
YouTubeStubConfig
.
get_configuration
()
.
get
(
'youtube_api_blocked'
):
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_init'
])
.
present
,
'Video Player Initialized'
)
self
.
_wait_for
(
lambda
:
self
.
q
(
css
=
CLASS_SELECTORS
[
'video_controls'
])
.
visible
,
'Player Controls are Visible'
)
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
):
def
click_button
(
self
,
button_name
):
"""
"""
...
@@ -74,6 +86,17 @@ class VidoComponentPage(PageObject):
...
@@ -74,6 +86,17 @@ class VidoComponentPage(PageObject):
self
.
q
(
css
=
BUTTON_SELECTORS
[
button_name
])
.
first
.
click
()
self
.
q
(
css
=
BUTTON_SELECTORS
[
button_name
])
.
first
.
click
()
self
.
wait_for_ajax
()
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
):
def
upload_handout
(
self
,
handout_filename
):
"""
"""
Upload a handout file to assets
Upload a handout file to assets
...
@@ -82,7 +105,7 @@ class VidoComponentPage(PageObject):
...
@@ -82,7 +105,7 @@ class VidoComponentPage(PageObject):
handout_filename (str): handout file name
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'
)
self
.
click_button
(
'upload_handout'
)
...
@@ -137,6 +160,77 @@ class VidoComponentPage(PageObject):
...
@@ -137,6 +160,77 @@ class VidoComponentPage(PageObject):
"""
"""
Check if handout download button is visible
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'
])
.
visible
return
self
.
q
(
css
=
BUTTON_SELECTORS
[
'handout_download'
])
.
present
and
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
common/test/acceptance/tests/helpers.py
View file @
936bd4d5
"""
"""
Test helper functions and base classes.
Test helper functions and base classes.
"""
"""
import
json
import
unittest
import
unittest
import
functools
import
functools
import
requests
import
requests
...
@@ -194,3 +195,77 @@ class UniqueCourseTest(WebAppTest):
...
@@ -194,3 +195,77 @@ class UniqueCourseTest(WebAppTest):
self
.
course_info
[
'number'
],
self
.
course_info
[
'number'
],
self
.
course_info
[
'run'
]
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
{}
common/test/acceptance/tests/video/test_studio_video_module.py
View file @
936bd4d5
...
@@ -7,9 +7,9 @@ Acceptance tests for CMS Video Module.
...
@@ -7,9 +7,9 @@ Acceptance tests for CMS Video Module.
from
unittest
import
skipIf
from
unittest
import
skipIf
from
...pages.studio.auto_auth
import
AutoAuthPage
from
...pages.studio.auto_auth
import
AutoAuthPage
from
...pages.studio.overview
import
CourseOutlinePage
from
...pages.studio.overview
import
CourseOutlinePage
from
...pages.studio.video.video
import
VidoComponentPage
from
...pages.studio.video.video
import
Vid
e
oComponentPage
from
...fixtures.course
import
CourseFixture
,
XBlockFixtureDesc
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!'
)
@skipIf
(
is_youtube_available
()
is
False
,
'YouTube is not available!'
)
...
@@ -24,7 +24,7 @@ class CMSVideoBaseTest(UniqueCourseTest):
...
@@ -24,7 +24,7 @@ class CMSVideoBaseTest(UniqueCourseTest):
"""
"""
super
(
CMSVideoBaseTest
,
self
)
.
setUp
()
super
(
CMSVideoBaseTest
,
self
)
.
setUp
()
self
.
video
=
VidoComponentPage
(
self
.
browser
)
self
.
video
=
Vid
e
oComponentPage
(
self
.
browser
)
# This will be initialized later
# This will be initialized later
self
.
unit_page
=
None
self
.
unit_page
=
None
...
@@ -41,6 +41,8 @@ class CMSVideoBaseTest(UniqueCourseTest):
...
@@ -41,6 +41,8 @@ class CMSVideoBaseTest(UniqueCourseTest):
self
.
course_info
[
'run'
],
self
.
course_info
[
'display_name'
]
self
.
course_info
[
'run'
],
self
.
course_info
[
'display_name'
]
)
)
self
.
assets
=
[]
def
_install_course_fixture
(
self
):
def
_install_course_fixture
(
self
):
"""
"""
Prepare for tests by creating a course with a section, subsection, and unit.
Prepare for tests by creating a course with a section, subsection, and unit.
...
@@ -49,13 +51,15 @@ class CMSVideoBaseTest(UniqueCourseTest):
...
@@ -49,13 +51,15 @@ class CMSVideoBaseTest(UniqueCourseTest):
Create a user and make that user a course author
Create a user and make that user a course author
Log the user into studio
Log the user into studio
"""
"""
if
self
.
assets
:
self
.
course_fixture
.
add_asset
(
self
.
assets
)
# Create course with Video component
# Create course with Video component
self
.
course_fixture
.
add_children
(
self
.
course_fixture
.
add_children
(
XBlockFixtureDesc
(
'chapter'
,
'Test Section'
)
.
add_children
(
XBlockFixtureDesc
(
'chapter'
,
'Test Section'
)
.
add_children
(
XBlockFixtureDesc
(
'sequential'
,
'Test Subsection'
)
.
add_children
(
XBlockFixtureDesc
(
'sequential'
,
'Test Subsection'
)
.
add_children
(
XBlockFixtureDesc
(
"vertical"
,
"Test Unit"
)
.
add_children
(
XBlockFixtureDesc
(
'vertical'
,
'Test Unit'
)
.
add_children
(
XBlockFixtureDesc
(
'video'
,
'Video'
)
,
XBlockFixtureDesc
(
'video'
,
'Video'
)
)
)
)
)
)
)
...
@@ -95,17 +99,194 @@ class CMSVideoBaseTest(UniqueCourseTest):
...
@@ -95,17 +99,194 @@ class CMSVideoBaseTest(UniqueCourseTest):
"""
"""
Open component Edit Dialog for first component on page.
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
):
def
open_advanced_tab
(
self
):
"""
"""
Open components advanced tab.
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
):
def
save_unit_settings
(
self
):
"""
"""
Save component settings.
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
)
common/test/acceptance/tests/video/test_video_module.py
View file @
936bd4d5
...
@@ -4,10 +4,8 @@
...
@@ -4,10 +4,8 @@
Acceptance tests for Video.
Acceptance tests for Video.
"""
"""
import
json
from
unittest
import
skipIf
,
skip
from
unittest
import
skipIf
,
skip
import
requests
from
..helpers
import
UniqueCourseTest
,
is_youtube_available
,
YouTubeStubConfig
from
..helpers
import
UniqueCourseTest
,
is_youtube_available
from
...pages.lms.video.video
import
VideoPage
from
...pages.lms.video.video
import
VideoPage
from
...pages.lms.tab_nav
import
TabNavPage
from
...pages.lms.tab_nav
import
TabNavPage
from
...pages.lms.course_nav
import
CourseNavPage
from
...pages.lms.course_nav
import
CourseNavPage
...
@@ -18,8 +16,6 @@ from ..helpers import skip_if_browser
...
@@ -18,8 +16,6 @@ from ..helpers import skip_if_browser
VIDEO_SOURCE_PORT
=
8777
VIDEO_SOURCE_PORT
=
8777
YOUTUBE_STUB_PORT
=
9080
YOUTUBE_STUB_URL
=
'http://127.0.0.1:{}/'
.
format
(
YOUTUBE_STUB_PORT
)
HTML5_SOURCES
=
[
HTML5_SOURCES
=
[
'http://localhost:{0}/gizmo.mp4'
.
format
(
VIDEO_SOURCE_PORT
),
'http://localhost:{0}/gizmo.mp4'
.
format
(
VIDEO_SOURCE_PORT
),
...
@@ -32,13 +28,6 @@ HTML5_SOURCES_INCORRECT = [
...
@@ -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!'
)
@skipIf
(
is_youtube_available
()
is
False
,
'YouTube is not available!'
)
class
VideoBaseTest
(
UniqueCourseTest
):
class
VideoBaseTest
(
UniqueCourseTest
):
"""
"""
...
@@ -68,7 +57,7 @@ class VideoBaseTest(UniqueCourseTest):
...
@@ -68,7 +57,7 @@ class VideoBaseTest(UniqueCourseTest):
self
.
youtube_configuration
=
{}
self
.
youtube_configuration
=
{}
# reset youtube stub server
# reset youtube stub server
self
.
addCleanup
(
self
.
_reset_youtube_stub_server
)
self
.
addCleanup
(
YouTubeStubConfig
.
reset
)
def
navigate_to_video
(
self
):
def
navigate_to_video
(
self
):
""" Prepare the course and get to the video and render it """
""" Prepare the course and get to the video and render it """
...
@@ -96,7 +85,7 @@ class VideoBaseTest(UniqueCourseTest):
...
@@ -96,7 +85,7 @@ class VideoBaseTest(UniqueCourseTest):
self
.
course_fixture
.
install
()
self
.
course_fixture
.
install
()
if
len
(
self
.
youtube_configuration
)
>
0
:
if
len
(
self
.
youtube_configuration
)
>
0
:
self
.
_configure_youtube_stub_server
(
self
.
youtube_configuration
)
YouTubeStubConfig
.
configure
(
self
.
youtube_configuration
)
def
_add_course_verticals
(
self
):
def
_add_course_verticals
(
self
):
"""
"""
...
@@ -148,39 +137,6 @@ class VideoBaseTest(UniqueCourseTest):
...
@@ -148,39 +137,6 @@ class VideoBaseTest(UniqueCourseTest):
self
.
_navigate_to_courseware_video
()
self
.
_navigate_to_courseware_video
()
self
.
video
.
wait_for_video_class
()
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
):
def
metadata_for_mode
(
self
,
player_mode
,
additional_data
=
None
):
"""
"""
Create a dictionary for video player configuration according to `player_mode`
Create a dictionary for video player configuration according to `player_mode`
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment