Commit bd4be00a by polesye

BLD-534: Add acceptance test.

parent 982f24ae
......@@ -4,8 +4,8 @@ Feature: LMS Video component
# 1
Scenario: Video component stores position correctly when page is reloaded
Given the course has a Video component in Youtube mode
Then when I view the video it has rendered in Youtube mode
Given the course has a Video component in "Youtube" mode
When the video has rendered in "Youtube" mode
And I click video button "play"
Then I seek video to "10" seconds
And I click video button "pause"
......@@ -15,8 +15,8 @@ Feature: LMS Video component
# 2
Scenario: Video component is fully rendered in the LMS in HTML5 mode
Given the course has a Video component in HTML5 mode
Then when I view the video it has rendered in HTML5 mode
Given the course has a Video component in "HTML5" mode
When the video has rendered in "HTML5" mode
And all sources are correct
# 3
......@@ -32,30 +32,30 @@ Feature: LMS Video component
# Youtube testing
Scenario: Video component is fully rendered in the LMS in Youtube mode with HTML5 sources
Given youtube server is up and response time is 0.4 seconds
And the course has a Video component in Youtube_HTML5 mode
Then when I view the video it has rendered in Youtube mode
And the course has a Video component in "Youtube_HTML5" mode
When the video has rendered in "Youtube" mode
# 5
Scenario: Video component is not rendered in the LMS in Youtube mode with HTML5 sources
Given youtube server is up and response time is 2 seconds
And the course has a Video component in Youtube_HTML5 mode
Then when I view the video it has rendered in HTML5 mode
And the course has a Video component in "Youtube_HTML5" mode
When the video has rendered in "HTML5" mode
# 6
Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources
Given youtube server is up and response time is 2 seconds
And the course has a Video component in Youtube mode
Then when I view the video it has rendered in Youtube mode
And the course has a Video component in "Youtube" mode
When the video has rendered in "Youtube" mode
# 7
Scenario: Video component is rendered in the LMS in Youtube mode with HTML5 sources that doesn't supported by browser
Given youtube server is up and response time is 2 seconds
And the course has a Video component in Youtube_HTML5_Unsupported_Video mode
Then when I view the video it has rendered in Youtube mode
And the course has a Video component in "Youtube_HTML5_Unsupported_Video" mode
When the video has rendered in "Youtube" mode
# 8
Scenario: Video component is rendered in the LMS in HTML5 mode with HTML5 sources that doesn't supported by browser
Given the course has a Video component in HTML5_Unsupported_Video mode
Given the course has a Video component in "HTML5_Unsupported_Video" mode
Then error message is shown
And error message has correct text
......@@ -136,7 +136,7 @@ Feature: LMS Video component
# 15
Scenario: CC button is hidden if no translations
Given the course has a Video component in Youtube mode
Given the course has a Video component in "Youtube" mode
Then button "CC" is hidden
# 16
......@@ -152,7 +152,7 @@ Feature: LMS Video component
# 17
Scenario: Video is aligned correctly if transcript is hidden in fullscreen mode
Given the course has a Video component in Youtube mode
Given the course has a Video component in "Youtube" mode
And I click video button "fullscreen"
Then I see video aligned correctly without enabled transcript
......@@ -251,3 +251,30 @@ Feature: LMS Video component
| {"zh": "chinese_transcripts.srt"} | true |
And I see "好 各位同学" text in the captions
Then I can download transcript in "srt" format that has text "好 各位同学"
# 25
Scenario: Verify that each video in each sub-section includes a transcript for non-Youtube countries.
Given youtube server is up and response time is 2 seconds
And I am registered for the course "test_course"
And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
And I have a "subs_b7xgknqkQk8.srt.sjson" transcript file in assets
And I have a "chinese_transcripts.srt" transcript file in assets
And it has videos "A, B" in "Youtube_HTML5" mode in position "1" of sequential:
| sub |
| OEoXaMPEzfM |
| b7xgknqkQk8 |
And a video "C" in "Youtube_HTML5" mode in position "2" of sequential:
| transcripts |
| {"zh": "chinese_transcripts.srt"} |
And a video "D" in "Youtube_HTML5" mode in position "3" of sequential
And I open the section with videos
Then videos have rendered in "HTML5" mode
And I see "Hi, welcome to Edx." text in the captions
And I see "Equal transcripts" text in the captions
When I open video "C"
Then the video has rendered in "HTML5" mode
And I make sure captions are opened
And I see "好 各位同学" text in the captions
When I open video "D"
Then the video has rendered in "HTML5" mode
And the video does not show the captions
# -*- coding: utf-8 -*-
# pylint: disable=C0111
from lettuce import world, step
import os
from lettuce import world, step, before
import json
import os
import time
import requests
from common import i_am_registered_for_the_course, section_location, visit_scenario_item
from common import i_am_registered_for_the_course, visit_scenario_item
from django.utils.translation import ugettext as _
from django.conf import settings
from cache_toolbox.core import del_cached_content
......@@ -45,7 +45,10 @@ VIDEO_MENUS = {
}
coursenum = 'test_course'
sequence = {}
@before.each_scenario
def setUp(scenario):
world.video_sequences = {}
class ReuqestHandlerWithSessionId(object):
......@@ -86,28 +89,26 @@ class ReuqestHandlerWithSessionId(object):
return True
return False
def add_video_to_course(course, player_mode, hashes, display_name='Video'):
category = 'video'
def get_metadata(parent_location, player_mode, data, display_name='Video'):
kwargs = {
'parent_location': section_location(course),
'category': category,
'parent_location': parent_location,
'category': 'video',
'display_name': display_name,
'metadata': {},
}
if hashes:
kwargs['metadata'].update(hashes[0])
if data:
conversions = {
'transcripts': json.loads,
'download_track': json.loads,
'download_video': json.loads,
}
for key in kwargs['metadata']:
for key in data:
if key in conversions:
kwargs['metadata'][key] = conversions[key](kwargs['metadata'][key])
data[key] = conversions[key](data[key])
kwargs['metadata'].update(data)
if player_mode == 'html5':
kwargs['metadata'].update({
......@@ -134,19 +135,53 @@ def add_video_to_course(course, player_mode, hashes, display_name='Video'):
'html5_sources': HTML5_SOURCES_INCORRECT
})
return kwargs
def add_videos_to_course(course, player_mode=None, display_names=None, hashes=None):
parent_location = add_vertical_to_course(course)
kwargs = {
'course': course,
'parent_location': parent_location,
'player_mode': player_mode,
'display_name': display_names[0],
}
if hashes:
for index, item_data in enumerate(hashes):
kwargs.update({
'display_name': display_names[index],
'data': item_data,
})
add_video_to_course(**kwargs)
else:
add_video_to_course(**kwargs)
def add_video_to_course(course, parent_location=None, player_mode=None, data=None, display_name='Video'):
if not parent_location:
parent_location = add_vertical_to_course(course)
kwargs = get_metadata(parent_location, player_mode, data, display_name=display_name)
world.scenario_dict['VIDEO'] = world.ItemFactory.create(**kwargs)
world.wait_for_present('.is-initialized')
world.wait_for_invisible('.video-wrapper .spinner')
def _get_sjson_filename(videoId, lang):
if lang == 'en':
return 'subs_{0}.srt.sjson'.format(videoId)
else:
return '{0}_subs_{1}.srt.sjson'.format(lang, videoId)
def add_vertical_to_course(course_num):
world.scenario_dict['LAST_VERTICAL'] = world.ItemFactory.create(
parent_location=world.scenario_dict['SECTION'].location,
category='vertical',
display_name='Test Vertical-{}'.format(len(set(world.video_sequences.values()))),
)
return last_vertical_location(course_num)
def last_vertical_location(course_num):
return world.scenario_dict['LAST_VERTICAL'].location._replace(course=course_num)
def _upload_file(filename, location):
def upload_file(filename, location):
path = os.path.join(TEST_ROOT, 'uploads/', filename)
f = open(os.path.abspath(path))
mime_type = "application/json"
......@@ -159,27 +194,28 @@ def _upload_file(filename, location):
del_cached_content(content.location)
def _navigate_to_an_item_in_a_sequence(number):
def navigate_to_an_item_in_a_sequence(number):
sequence_css = '#sequence-list a[data-element="{0}"]'.format(number)
world.css_click(sequence_css)
def _change_video_speed(speed):
def change_video_speed(speed):
world.browser.execute_script("$('.speeds').addClass('open')")
speed_css = 'li[data-speed="{0}"] a'.format(speed)
world.css_click(speed_css)
def _open_menu(menu):
def open_menu(menu):
world.browser.execute_script("$('{selector}').parent().addClass('open')".format(
selector=VIDEO_MENUS[menu]
))
def _get_all_dimensions():
video = _get_dimensions('.video-player iframe, .video-player video')
wrapper = _get_dimensions('.tc-wrapper')
controls = _get_dimensions('.video-controls')
progress_slider = _get_dimensions('.video-controls > .slider')
def get_all_dimensions():
video = get_dimensions('.video-player iframe, .video-player video')
wrapper = get_dimensions('.tc-wrapper')
controls = get_dimensions('.video-controls')
progress_slider = get_dimensions('.video-controls > .slider')
expected = dict(wrapper)
expected['height'] -= controls['height'] + 0.5 * progress_slider['height']
......@@ -187,30 +223,30 @@ def _get_all_dimensions():
return (video, expected)
def _get_dimensions(selector):
def get_dimensions(selector):
element = world.css_find(selector).first
return element._element.size
def _get_window_dimensions():
def get_window_dimensions():
return world.browser.driver.get_window_size()
def _set_window_dimensions(width, height):
def set_window_dimensions(width, height):
world.browser.driver.set_window_size(width, height)
# Wait 200 ms when JS finish resizing
world.wait(0.2)
def _duration():
def duration():
"""
Total duration of the video, in seconds.
"""
elapsed_time, duration = _video_time()
elapsed_time, duration = video_time()
return duration
def _video_time():
def video_time():
"""
Return a tuple `(elapsed_time, duration)`, each in seconds.
"""
......@@ -221,10 +257,10 @@ def _video_time():
elapsed_str, duration_str = full_time.split(' / ')
# Convert each string to seconds
return (_parse_time_str(elapsed_str), _parse_time_str(duration_str))
return (parse_time_str(elapsed_str), parse_time_str(duration_str))
def _parse_time_str(time_str):
def parse_time_str(time_str):
"""
Parse a string of the form 1:23 into seconds (int).
"""
......@@ -237,23 +273,26 @@ def does_not_autoplay(_step, video_type):
assert(world.css_find('.%s' % video_type)[0]['data-autoplay'] == 'False')
@step('the course has a Video component in (.*) mode(?:\:)?$')
@step('the course has a Video component in "([^"]*)" mode(?:\:)?$')
def view_video(_step, player_mode):
i_am_registered_for_the_course(_step, coursenum)
add_video_to_course(coursenum, player_mode.lower(), _step.hashes)
data = _step.hashes[0] if _step.hashes else None
add_video_to_course(coursenum, player_mode=player_mode.lower(), data=data)
visit_scenario_item('SECTION')
@step('a video in "([^"]*)" mode(?:\:)?$')
def add_video(_step, player_mode):
add_video_to_course(coursenum, player_mode.lower(), _step.hashes)
data = _step.hashes[0] if _step.hashes else None
add_video_to_course(coursenum, player_mode=player_mode.lower(), data=data)
visit_scenario_item('SECTION')
@step('a video "([^"]*)" in "([^"]*)" mode in position "([^"]*)" of sequential(?:\:)?$')
def add_video_in_position(_step, player_id, player_mode, position):
sequence[player_id] = position
add_video_to_course(coursenum, player_mode.lower(), _step.hashes, display_name=player_id)
@step('video(?:s)? "([^"]*)" in "([^"]*)" mode in position "([^"]*)" of sequential(?:\:)?$')
def add_video_in_position(_step, video_ids, player_mode, position):
sequences = {video_id.strip(): position for video_id in video_ids.split(',')}
add_videos_to_course(coursenum, player_mode=player_mode.lower(), display_names=sequences.keys(), hashes=_step.hashes)
world.video_sequences.update(sequences)
@step('I open the section with videos$')
......@@ -262,19 +301,19 @@ def visit_video_section(_step):
@step('I select the "([^"]*)" speed$')
def change_video_speed(_step, speed):
_change_video_speed(speed)
def i_select_video_speed(_step, speed):
change_video_speed(speed)
@step('I select the "([^"]*)" speed on video "([^"]*)"$')
def change_video_speed_on_video(_step, speed, player_id):
_navigate_to_an_item_in_a_sequence(sequence[player_id])
_change_video_speed(speed)
navigate_to_an_item_in_a_sequence(world.video_sequences[player_id])
change_video_speed(speed)
@step('I open video "([^"]*)"$')
def open_video(_step, player_id):
_navigate_to_an_item_in_a_sequence(sequence[player_id])
navigate_to_an_item_in_a_sequence(world.video_sequences[player_id])
@step('video "([^"]*)" should start playing at speed "([^"]*)"$')
......@@ -288,7 +327,7 @@ def set_youtube_response_timeout(_step, time):
world.youtube.config['time_to_response'] = float(time)
@step('when I view the video it has rendered in (.*) mode$')
@step('the video has rendered in "([^"]*)" mode$')
def video_is_rendered(_step, mode):
modes = {
'html5': 'video',
......@@ -299,6 +338,20 @@ def video_is_rendered(_step, mode):
assert world.is_css_present('.speed_link')
@step('videos have rendered in "([^"]*)" mode$')
def videos_are_rendered(_step, mode):
modes = {
'html5': 'video',
'youtube': 'iframe'
}
html_tag = modes[mode.lower()]
actual = len(world.css_find('.video {0}'.format(html_tag)))
expected = len(world.css_find('.xmodule_VideoModule'))
assert actual == expected
assert world.is_css_present('.speed_link')
@step('all sources are correct$')
def all_sources_are_correct(_step):
elements = world.css_find('.video-player video source')
......@@ -333,7 +386,7 @@ def set_captions_visibility_state(_step, captions_state):
@step('I see video menu "([^"]*)" with correct items$')
def i_see_menu(_step, menu):
_open_menu(menu)
open_menu(menu)
menu_items = world.css_find(VIDEO_MENUS[menu] + ' li')
video = world.scenario_dict['VIDEO']
transcripts = dict(video.transcripts)
......@@ -388,7 +441,7 @@ def start_playing_video_from_n_seconds(_step, position):
@step('I see duration "([^"]*)"$')
def i_see_duration(_step, position):
world.wait_for(
func=lambda _: _duration() == _parse_time_str(position),
func=lambda _: duration() == parse_time_str(position),
timeout=5
)
......@@ -402,7 +455,7 @@ def seek_video_to_n_seconds(_step, seconds):
@step('I have a "([^"]*)" transcript file in assets$')
def upload_to_assets(_step, filename):
_upload_file(filename, world.scenario_dict['COURSE'].location)
upload_file(filename, world.scenario_dict['COURSE'].location)
@step('button "([^"]*)" is hidden$')
......@@ -419,20 +472,20 @@ def is_hidden_menu(_step, menu):
def video_alignment(_step, transcript_visibility):
# Width of the video container in css equal 75% of window if transcript enabled
wrapper_width = 75 if transcript_visibility == "with" else 100
initial = _get_window_dimensions()
initial = get_window_dimensions()
_set_window_dimensions(300, 600)
real, expected = _get_all_dimensions()
set_window_dimensions(300, 600)
real, expected = get_all_dimensions()
width = round(100 * real['width']/expected['width']) == wrapper_width
_set_window_dimensions(600, 300)
real, expected = _get_all_dimensions()
set_window_dimensions(600, 300)
real, expected = get_all_dimensions()
height = abs(expected['height'] - real['height']) <= 5
# Restore initial window size
_set_window_dimensions(
set_window_dimensions(
initial['width'], initial['height']
)
......@@ -473,3 +526,12 @@ def select_transcript_format(_step, format):
assert world.css_find(menu_selector + ' .active a')[0]['data-value'] == format
assert world.css_has_text(button_selector, '.' + format, strip=True)
@step('video (.*) show the captions$')
def shows_captions(_step, show_captions):
if 'not' in show_captions or 'n\'t' in show_captions:
assert world.is_css_present('div.video.closed')
else:
assert world.is_css_not_present('div.video.closed')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment