1.) Making YouTube mock server serve YouTube API. 2.) Adding ability to block YouTube API from YouTube mock server. 3.) Adding acceptance tests that check YouTube mock server for availability of YouTube API, and for it's ability to block YouTube API. Part of BLD-939.
256 lines
8.1 KiB
Python
256 lines
8.1 KiB
Python
# pylint: disable=C0111
|
|
|
|
from lettuce import world, step
|
|
from xmodule.modulestore import Location
|
|
from contentstore.utils import get_modulestore
|
|
from selenium.webdriver.common.keys import Keys
|
|
|
|
VIDEO_BUTTONS = {
|
|
'CC': '.hide-subtitles',
|
|
'volume': '.volume',
|
|
'play': '.video_control.play',
|
|
'pause': '.video_control.pause',
|
|
}
|
|
|
|
SELECTORS = {
|
|
'spinner': '.video-wrapper .spinner',
|
|
'controls': 'section.video-controls',
|
|
}
|
|
|
|
# We should wait 300 ms for event handler invocation + 200ms for safety.
|
|
DELAY = 0.5
|
|
|
|
|
|
@step('youtube stub server (.*) YouTube API')
|
|
def configure_youtube_api(_step, action):
|
|
action=action.strip()
|
|
if action == 'proxies':
|
|
world.youtube.config['youtube_api_blocked'] = False
|
|
elif action == 'blocks':
|
|
world.youtube.config['youtube_api_blocked'] = True
|
|
else:
|
|
raise ValueError('Parameter `action` should be one of "proxies" or "blocks".')
|
|
|
|
|
|
@step('We explicitly wait for YouTube API to not load$')
|
|
def wait_for_youtube_api_fail(_step):
|
|
world.wait(3)
|
|
|
|
|
|
@step('I have created a Video component$')
|
|
def i_created_a_video_component(_step):
|
|
world.create_course_with_unit()
|
|
world.create_component_instance(
|
|
step=_step,
|
|
category='video',
|
|
)
|
|
|
|
world.wait_for_xmodule()
|
|
world.disable_jquery_animations()
|
|
|
|
world.wait_for_present('.is-initialized')
|
|
world.wait(DELAY)
|
|
world.wait_for_invisible(SELECTORS['spinner'])
|
|
|
|
|
|
@step('I have created a Video component with subtitles$')
|
|
def i_created_a_video_with_subs(_step):
|
|
_step.given('I have created a Video component with subtitles "OEoXaMPEzfM"')
|
|
|
|
|
|
@step('I have created a Video component with subtitles "([^"]*)"$')
|
|
def i_created_a_video_with_subs_with_name(_step, sub_id):
|
|
_step.given('I have created a Video component')
|
|
|
|
# Store the current URL so we can return here
|
|
video_url = world.browser.url
|
|
|
|
# Upload subtitles for the video using the upload interface
|
|
_step.given('I have uploaded subtitles "{}"'.format(sub_id))
|
|
|
|
# Return to the video
|
|
world.visit(video_url)
|
|
|
|
world.wait_for_xmodule()
|
|
|
|
# update .sub filed with proper subs name (which mimics real Studio/XML behavior)
|
|
# this is needed only for that videos which are created in acceptance tests.
|
|
_step.given('I edit the component')
|
|
world.wait_for_ajax_complete()
|
|
_step.given('I save changes')
|
|
|
|
world.disable_jquery_animations()
|
|
|
|
world.wait_for_present('.is-initialized')
|
|
world.wait_for_invisible(SELECTORS['spinner'])
|
|
|
|
|
|
@step('I have uploaded subtitles "([^"]*)"$')
|
|
def i_have_uploaded_subtitles(_step, sub_id):
|
|
_step.given('I go to the files and uploads page')
|
|
_step.given('I upload the test file "subs_{}.srt.sjson"'.format(sub_id.strip()))
|
|
|
|
|
|
@step('when I view the (.*) it does not have autoplay enabled$')
|
|
def does_not_autoplay(_step, video_type):
|
|
world.wait(DELAY)
|
|
world.wait_for_ajax_complete()
|
|
actual = world.css_find('.%s' % video_type)[0]['data-autoplay']
|
|
expected = [u'False', u'false', False]
|
|
assert actual in expected
|
|
assert world.css_has_class('.video_control', 'play')
|
|
|
|
|
|
@step('creating a video takes a single click$')
|
|
def video_takes_a_single_click(_step):
|
|
component_css = '.xmodule_VideoModule'
|
|
assert world.is_css_not_present(component_css)
|
|
|
|
world.css_click("a[data-category='video']")
|
|
assert world.is_css_present(component_css)
|
|
|
|
|
|
@step('I edit the component$')
|
|
def i_edit_the_component(_step):
|
|
world.edit_component()
|
|
|
|
|
|
@step('I have (hidden|toggled) captions$')
|
|
def hide_or_show_captions(step, shown):
|
|
button_css = 'a.hide-subtitles'
|
|
if shown == 'hidden':
|
|
world.css_click(button_css)
|
|
if shown == 'toggled':
|
|
world.css_click(button_css)
|
|
# When we click the first time, a tooltip shows up. We want to
|
|
# click the button rather than the tooltip, so move the mouse
|
|
# away to make it disappear.
|
|
button = world.css_find(button_css)
|
|
# mouse_out is not implemented on firefox with selenium
|
|
if not world.is_firefox:
|
|
button.mouse_out()
|
|
world.css_click(button_css)
|
|
|
|
|
|
@step('I have created a video with only XML data$')
|
|
def xml_only_video(step):
|
|
# Create a new video *without* metadata. This requires a certain
|
|
# amount of rummaging to make sure all the correct data is present
|
|
step.given('I have clicked the new unit button')
|
|
|
|
# Wait for the new unit to be created and to load the page
|
|
world.wait(1)
|
|
|
|
location = world.scenario_dict['COURSE'].location
|
|
store = get_modulestore(location)
|
|
|
|
parent_location = store.get_items(Location(category='vertical', revision='draft'))[0].location
|
|
|
|
youtube_id = 'ABCDEFG'
|
|
world.scenario_dict['YOUTUBE_ID'] = youtube_id
|
|
|
|
# Create a new Video component, but ensure that it doesn't have
|
|
# metadata. This allows us to test that we are correctly parsing
|
|
# out XML
|
|
world.ItemFactory.create(
|
|
parent_location=parent_location,
|
|
category='video',
|
|
data='<video youtube="1.00:%s"></video>' % youtube_id
|
|
)
|
|
|
|
|
|
@step('The correct Youtube video is shown$')
|
|
def the_youtube_video_is_shown(_step):
|
|
ele = world.css_find('.video').first
|
|
assert ele['data-streams'].split(':')[1] == world.scenario_dict['YOUTUBE_ID']
|
|
|
|
|
|
@step('Make sure captions are (.+)$')
|
|
def set_captions_visibility_state(_step, captions_state):
|
|
SELECTOR = '.closed .subtitles'
|
|
world.wait_for_visible('.hide-subtitles')
|
|
if captions_state == 'closed':
|
|
if not world.is_css_present(SELECTOR):
|
|
world.css_find('.hide-subtitles').click()
|
|
else:
|
|
if world.is_css_present(SELECTOR):
|
|
world.css_find('.hide-subtitles').click()
|
|
|
|
|
|
@step('I hover over button "([^"]*)"$')
|
|
def hover_over_button(_step, button):
|
|
world.css_find(VIDEO_BUTTONS[button.strip()]).mouse_over()
|
|
|
|
|
|
@step('Captions (?:are|become) "([^"]*)"$')
|
|
def check_captions_visibility_state(_step, visibility_state):
|
|
if visibility_state == 'visible':
|
|
assert world.css_visible('.subtitles')
|
|
else:
|
|
assert not world.css_visible('.subtitles')
|
|
|
|
|
|
def find_caption_line_by_data_index(index):
|
|
SELECTOR = ".subtitles > li[data-index='{index}']".format(index=index)
|
|
return world.css_find(SELECTOR).first
|
|
|
|
|
|
@step('I focus on caption line with data-index "([^"]*)"$')
|
|
def focus_on_caption_line(_step, index):
|
|
find_caption_line_by_data_index(int(index.strip()))._element.send_keys(Keys.TAB)
|
|
|
|
|
|
@step('I press "enter" button on caption line with data-index "([^"]*)"$')
|
|
def click_on_the_caption(_step, index):
|
|
find_caption_line_by_data_index(int(index.strip()))._element.send_keys(Keys.ENTER)
|
|
|
|
|
|
@step('I see caption line with data-index "([^"]*)" has class "([^"]*)"$')
|
|
def caption_line_has_class(_step, index, className):
|
|
SELECTOR = ".subtitles > li[data-index='{index}']".format(index=int(index.strip()))
|
|
assert world.css_has_class(SELECTOR, className.strip())
|
|
|
|
|
|
@step('I see a range on slider$')
|
|
def see_a_range_slider_with_proper_range(_step):
|
|
world.wait_for_visible(VIDEO_BUTTONS['pause'])
|
|
|
|
assert world.css_visible(".slider-range")
|
|
|
|
|
|
@step('I (.*) see video button "([^"]*)"$')
|
|
def do_not_see_or_not_button_video(_step, action, button_type):
|
|
world.wait(DELAY)
|
|
world.wait_for_ajax_complete()
|
|
action=action.strip()
|
|
button = button_type.strip()
|
|
if action == 'do not':
|
|
assert not world.is_css_present(VIDEO_BUTTONS[button])
|
|
elif action == 'can':
|
|
assert world.css_visible(VIDEO_BUTTONS[button])
|
|
else:
|
|
raise ValueError('Parameter `action` should be one of "do not" or "can".')
|
|
|
|
|
|
@step('I click video button "([^"]*)"$')
|
|
def click_button_video(_step, button_type):
|
|
world.wait(DELAY)
|
|
world.wait_for_ajax_complete()
|
|
button = button_type.strip()
|
|
world.css_click(VIDEO_BUTTONS[button])
|
|
|
|
|
|
@step('I seek video to "([^"]*)" seconds$')
|
|
def seek_video_to_n_seconds(_step, seconds):
|
|
time = float(seconds.strip())
|
|
jsCode = "$('.video').data('video-player-state').videoPlayer.onSlideSeek({{time: {0:f}}})".format(time)
|
|
world.browser.execute_script(jsCode)
|
|
|
|
|
|
@step('I see video starts playing from "([^"]*)" position$')
|
|
def start_playing_video_from_n_seconds(_step, position):
|
|
world.wait_for(
|
|
func=lambda _: world.css_html('.vidtime')[:4] == position.strip(),
|
|
timeout=5
|
|
)
|