From 936bd4d5ad78271888dcfcef8efe4d433d44ac1e Mon Sep 17 00:00:00 2001 From: Muhammad Ammar Date: Mon, 11 Aug 2014 07:50:02 +0000 Subject: [PATCH] Bok-Choy CMS Video Tests --- .../contentstore/features/video.feature | 80 +------ common/djangoapps/terrain/stubs/youtube.py | 5 +- .../test/acceptance/pages/lms/video/video.py | 39 ++-- .../acceptance/pages/studio/video/video.py | 114 +++++++++- common/test/acceptance/tests/helpers.py | 75 +++++++ .../tests/video/test_studio_video_module.py | 197 +++++++++++++++++- .../tests/video/test_video_module.py | 50 +---- 7 files changed, 398 insertions(+), 162 deletions(-) diff --git a/cms/djangoapps/contentstore/features/video.feature b/cms/djangoapps/contentstore/features/video.feature index f81c4bb712..5992c2540c 100644 --- a/cms/djangoapps/contentstore/features/video.feature +++ b/cms/djangoapps/contentstore/features/video.feature @@ -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 diff --git a/common/djangoapps/terrain/stubs/youtube.py b/common/djangoapps/terrain/stubs/youtube.py index 2bd29f79ad..0f4f7a894d 100644 --- a/common/djangoapps/terrain/stubs/youtube.py +++ b/common/djangoapps/terrain/stubs/youtube.py @@ -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([ diff --git a/common/test/acceptance/pages/lms/video/video.py b/common/test/acceptance/pages/lms/video/video.py index 5fa35fdae0..1f69e1125b 100644 --- a/common/test/acceptance/pages/lms/video/video.py +++ b/common/test/acceptance/pages/lms/video/video.py @@ -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 diff --git a/common/test/acceptance/pages/studio/video/video.py b/common/test/acceptance/pages/studio/video/video.py index 01a3e81299..43e6729cf7 100644 --- a/common/test/acceptance/pages/studio/video/video.py +++ b/common/test/acceptance/pages/studio/video/video.py @@ -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 diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py index 93421ee980..c6f74700f1 100644 --- a/common/test/acceptance/tests/helpers.py +++ b/common/test/acceptance/tests/helpers.py @@ -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 {} diff --git a/common/test/acceptance/tests/video/test_studio_video_module.py b/common/test/acceptance/tests/video/test_studio_video_module.py index 1988f17253..e7515b1f2c 100644 --- a/common/test/acceptance/tests/video/test_studio_video_module.py +++ b/common/test/acceptance/tests/video/test_studio_video_module.py @@ -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) diff --git a/common/test/acceptance/tests/video/test_video_module.py b/common/test/acceptance/tests/video/test_video_module.py index 140a1f94fe..635c8b57d3 100644 --- a/common/test/acceptance/tests/video/test_video_module.py +++ b/common/test/acceptance/tests/video/test_video_module.py @@ -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`