From 81a5a7b9623b8346b01a5404c19ca2d9b1c7a897 Mon Sep 17 00:00:00 2001 From: Muhammad Ammar Date: Wed, 2 Jul 2014 06:00:44 +0000 Subject: [PATCH] CMS Video Handout Tests --- .../features/video_handout.feature | 71 ------- common/test/acceptance/fixtures/course.py | 7 +- common/test/acceptance/pages/studio/unit.py | 39 ++++ .../acceptance/pages/studio/video/__init__.py | 0 .../acceptance/pages/studio/video/video.py | 142 +++++++++++++ .../tests/video/test_studio_video_module.py | 117 +++++++++++ .../tests/video/test_video_handout.py | 191 ++++++++++++++++++ common/test/data/uploads/.gitignore | 1 - common/test/data/uploads/asset.html | 10 + 9 files changed, 503 insertions(+), 75 deletions(-) delete mode 100644 cms/djangoapps/contentstore/features/video_handout.feature create mode 100644 common/test/acceptance/pages/studio/video/__init__.py create mode 100644 common/test/acceptance/pages/studio/video/video.py create mode 100644 common/test/acceptance/tests/video/test_studio_video_module.py create mode 100644 common/test/acceptance/tests/video/test_video_handout.py create mode 100644 common/test/data/uploads/asset.html diff --git a/cms/djangoapps/contentstore/features/video_handout.feature b/cms/djangoapps/contentstore/features/video_handout.feature deleted file mode 100644 index dba4c71f26..0000000000 --- a/cms/djangoapps/contentstore/features/video_handout.feature +++ /dev/null @@ -1,71 +0,0 @@ -@shard_3 @requires_stub_youtube -Feature: CMS Video Component Handout - As a course author, I want to be able to create video handout - - # 1 - Scenario: Handout uploading works correctly - Given I have created a Video component with handout file "textbook.pdf" - And I save changes - Then I can see video button "handout" - And I can download handout file with mime type "application/pdf" - - # 2 - Scenario: Handout downloading works correctly w/ preliminary saving - Given I have created a Video component with handout file "textbook.pdf" - And I save changes - And I edit the component - And I open tab "Advanced" - And I can download handout file in editor with mime type "application/pdf" - - # 3 - Scenario: Handout downloading works correctly w/o preliminary saving - Given I have created a Video component with handout file "textbook.pdf" - And I can download handout file in editor with mime type "application/pdf" - - # 4 - Scenario: Handout clearing works correctly w/ preliminary saving - Given I have created a Video component with handout file "textbook.pdf" - And I save changes - And I can download handout file with mime type "application/pdf" - And I edit the component - And I open tab "Advanced" - And I clear handout - And I save changes - Then I do not see video button "handout" - - # 5 - Scenario: Handout clearing works correctly w/o preliminary saving - Given I have created a Video component with handout file "asset.html" - And I clear handout - And I save changes - Then I do not see video button "handout" - - # 6 - Scenario: User can easy replace the handout by another one w/ preliminary saving - Given I have created a Video component with handout file "asset.html" - And I save changes - Then I can see video button "handout" - And I can download handout file with mime type "text/html" - And I edit the component - And I open tab "Advanced" - And I replace handout file by "textbook.pdf" - And I save changes - Then I can see video button "handout" - And I can download handout file with mime type "application/pdf" - - # 7 - Scenario: User can easy replace the handout by another one w/o preliminary saving - Given I have created a Video component with handout file "asset.html" - And I replace handout file by "textbook.pdf" - And I save changes - Then I can see video button "handout" - And I can download handout file with mime type "application/pdf" - - # 8 - Scenario: Upload file "A" -> Remove it -> Upload file "B" - Given I have created a Video component with handout file "asset.html" - And I clear handout - And I upload handout file "textbook.pdf" - And I save changes - Then I can see video button "handout" - And I can download handout file with mime type "application/pdf" diff --git a/common/test/acceptance/fixtures/course.py b/common/test/acceptance/fixtures/course.py index 5d14c2d27c..87f7b11840 100644 --- a/common/test/acceptance/fixtures/course.py +++ b/common/test/acceptance/fixtures/course.py @@ -2,6 +2,7 @@ Fixture to create a course and course components (XBlocks). """ +import mimetypes import json import re import datetime @@ -407,10 +408,10 @@ class CourseFixture(StudioApiFixture): test_dir = path(__file__).abspath().dirname().dirname().dirname() for asset_name in self._assets: - srt_path = test_dir + '/data/uploads/' + asset_name + asset_file_path = test_dir + '/data/uploads/' + asset_name - asset_file = open(srt_path) - files = {'file': (asset_name, asset_file)} + asset_file = open(asset_file_path) + files = {'file': (asset_name, asset_file, mimetypes.guess_type(asset_file_path)[0])} headers = { 'Accept': 'application/json', diff --git a/common/test/acceptance/pages/studio/unit.py b/common/test/acceptance/pages/studio/unit.py index 7eca47dfd3..998cd9b990 100644 --- a/common/test/acceptance/pages/studio/unit.py +++ b/common/test/acceptance/pages/studio/unit.py @@ -62,6 +62,22 @@ class UnitPage(PageObject): 'Wait for draft mode to be activated' ).fulfill() + def set_unit_visibility(self, visibility): + """ + Set unit visibility state + + Arguments: + visibility (str): private or public + + """ + self.q(css='select[name="visibility-select"] option[value="{}"]'.format(visibility)).first.click() + self.wait_for_ajax() + + +COMPONENT_BUTTONS = { + 'advanced_tab': '.editor-tabs li.inner_tab_wrap:nth-child(2) > a', + 'save_settings': '.action-save', +} class Component(PageObject): """ @@ -125,3 +141,26 @@ class Component(PageObject): an initialized :class:`.ContainerPage` for that xblock. """ return ContainerPage(self.browser, self.locator).visit() + + def _click_button(self, button_name): + """ + Click on a button as specified by `button_name` + + Arguments: + button_name (str): button name + + """ + self.q(css=COMPONENT_BUTTONS[button_name]).first.click() + self.wait_for_ajax() + + def open_advanced_tab(self): + """ + Click on Advanced Tab. + """ + self._click_button('advanced_tab') + + def save_settings(self): + """ + Click on settings Save button. + """ + self._click_button('save_settings') diff --git a/common/test/acceptance/pages/studio/video/__init__.py b/common/test/acceptance/pages/studio/video/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/test/acceptance/pages/studio/video/video.py b/common/test/acceptance/pages/studio/video/video.py new file mode 100644 index 0000000000..e59400588f --- /dev/null +++ b/common/test/acceptance/pages/studio/video/video.py @@ -0,0 +1,142 @@ +""" +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 + +CLASS_SELECTORS = { + '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', +} + +BUTTON_SELECTORS = { + 'handout_download': '.video-handout.video-download-button a', + 'handout_download_editor': '.wrapper-comp-setting.file-uploader .download-action', + 'upload_handout': '.upload-action', + 'handout_submit': '.action-upload', + 'handout_clear': '.wrapper-comp-setting.file-uploader .setting-clear', +} + + +@js_defined('window.Video', 'window.RequireJS.require', 'window.jQuery', 'window.XModule', 'window.XBlock', + 'window.MathJax.isReady') +class VidoComponentPage(PageObject): + """ + CMS Video Component Page + """ + + url = None + + @wait_for_js + def is_browser_on_page(self): + return self.q(css='div{0}'.format(CLASS_SELECTORS['video_xmodule'])).present + + def _wait_for(self, check_func, desc, result=False, timeout=200): + """ + Calls the method provided as an argument until the Promise satisfied or BrokenPromise + + Arguments: + check_func (callable): Promise function to be fulfilled. + desc (str): Description of the Promise, used in log messages. + result (bool): Indicates whether we need result from Promise or not + timeout (float): Maximum number of seconds to wait for the Promise to be satisfied before timing out. + + """ + if result: + return Promise(check_func, desc, timeout=timeout).fulfill() + else: + return EmptyPromise(check_func, desc, timeout=timeout).fulfill() + + def wait_for_video_component_render(self): + """ + 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') + + def click_button(self, button_name): + """ + Click on a button as specified by `button_name` + + Arguments: + button_name (str): button name + + """ + self.q(css=BUTTON_SELECTORS[button_name]).first.click() + self.wait_for_ajax() + + def upload_handout(self, handout_filename): + """ + Upload a handout file to assets + + Arguments: + handout_filename (str): handout file name + + """ + handout_path = os.sep.join(__file__.split(os.sep)[:-5]) + '/data/uploads/' + handout_filename + + self.click_button('upload_handout') + + self.q(css=CLASS_SELECTORS['attach_handout']).results[0].send_keys(handout_path) + + self.click_button('handout_submit') + + # confirm upload completion + self._wait_for(lambda: not self.q(css=CLASS_SELECTORS['upload_dialog']).present, 'Upload Handout Completed') + + def clear_handout(self): + """ + Clear handout from settings + """ + self.click_button('handout_clear') + + def _get_handout(self, url): + """ + Download handout at `url` + """ + kwargs = dict() + + session_id = [{i['name']: i['value']} for i in self.browser.get_cookies() if i['name'] == u'sessionid'] + if session_id: + kwargs.update({ + 'cookies': session_id[0] + }) + + response = requests.get(url, **kwargs) + return response.status_code < 400, response.headers + + def download_handout(self, mime_type, is_editor=False): + """ + Download handout with mime type specified by `mime_type` + + Arguments: + mime_type (str): mime type of handout file + + Returns: + tuple: Handout download result. + + """ + selector = BUTTON_SELECTORS['handout_download_editor'] if is_editor else BUTTON_SELECTORS['handout_download'] + + handout_url = self.q(css=selector).attrs('href')[0] + result, headers = self._get_handout(handout_url) + + return result, headers['content-type'] == mime_type + + @property + def is_handout_button_visible(self): + """ + 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 diff --git a/common/test/acceptance/tests/video/test_studio_video_module.py b/common/test/acceptance/tests/video/test_studio_video_module.py new file mode 100644 index 0000000000..1ba3eed446 --- /dev/null +++ b/common/test/acceptance/tests/video/test_studio_video_module.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +""" +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 ...fixtures.course import CourseFixture, XBlockFixtureDesc +from ..helpers import UniqueCourseTest, is_youtube_available + + +@skipIf(is_youtube_available() is False, 'YouTube is not available!') +class CMSVideoBaseTest(UniqueCourseTest): + """ + CMS Video Module Base Test Class + """ + + def setUp(self): + """ + Initialization of pages and course fixture for tests + """ + super(CMSVideoBaseTest, self).setUp() + + self.video = VidoComponentPage(self.browser) + + # This will be initialized later + self.unit_page = None + + self.outline = CourseOutlinePage( + self.browser, + self.course_info['org'], + self.course_info['number'], + self.course_info['run'] + ) + + self.course_fixture = CourseFixture( + self.course_info['org'], self.course_info['number'], + self.course_info['run'], self.course_info['display_name'] + ) + + def _install_course_fixture(self): + """ + Prepare for tests by creating a course with a section, subsection, and unit. + Performs the following: + Create a course with a section, subsection, and unit + Create a user and make that user a course author + Log the user into studio + """ + + # 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'), + ) + ) + ) + ).install() + + # Auto login and register the course + AutoAuthPage( + self.browser, + staff=False, + username=self.course_fixture.user.get('username'), + email=self.course_fixture.user.get('email'), + password=self.course_fixture.user.get('password') + ).visit() + + def _navigate_to_course_unit_page(self): + """ + Open the course from the dashboard and expand the section and subsection and click on the Unit link + The end result is the page where the user is editing the newly created unit + """ + # Visit Course Outline page + self.outline.visit() + + # Visit Unit page + self.unit_page = self.outline.section('Test Section').subsection('Test Subsection').toggle_expand().unit( + 'Test Unit').go_to() + + self.video.wait_for_video_component_render() + + def navigate_to_course_unit(self): + """ + Install the course with required components and navigate to course unit page + """ + self._install_course_fixture() + self._navigate_to_course_unit_page() + + def edit_component(self): + """ + Make component editable and open components Edit Dialog. + + Arguments: + handout_filename (str): handout file name to be uploaded + save_settings (bool): save settings or not + + """ + self.unit_page.set_unit_visibility('private') + self.unit_page.components[0].edit() + + def open_advanced_tab(self): + """ + Open components advanced tab. + """ + self.unit_page.components[0].open_advanced_tab() + + def save_unit_settings(self): + """ + Save component settings. + """ + self.unit_page.components[0].save_settings() + diff --git a/common/test/acceptance/tests/video/test_video_handout.py b/common/test/acceptance/tests/video/test_video_handout.py new file mode 100644 index 0000000000..506aea9eb7 --- /dev/null +++ b/common/test/acceptance/tests/video/test_video_handout.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- + +""" +Acceptance tests for CMS Video Handout. +""" + +from .test_studio_video_module import CMSVideoBaseTest + + +class VideoHandoutTest(CMSVideoBaseTest): + """ + CMS Video Handout Test Class + """ + + def setUp(self): + super(VideoHandoutTest, self).setUp() + + def _create_course_unit_with_handout(self, handout_filename, save_settings=True): + """ + Create a course with unit and also upload handout + + Arguments: + handout_filename (str): handout file name to be uploaded + save_settings (bool): save settings or not + + """ + self.navigate_to_course_unit() + + self.edit_component() + + self.open_advanced_tab() + + self.video.upload_handout(handout_filename) + + if save_settings: + self.save_unit_settings() + + def test_handout_uploads_correctly(self): + """ + Scenario: Handout uploading works correctly + Given I have created a Video component with handout file "textbook.pdf" + Then I can see video button "handout" + And I can download handout file with mime type "application/pdf" + """ + self._create_course_unit_with_handout('textbook.pdf') + + self.assertTrue(self.video.is_handout_button_visible) + + self.assertEqual(self.video.download_handout('application/pdf'), (True, True)) + + def test_handout_download_works_with_save(self): + """ + Scenario: Handout downloading works correctly w/ preliminary saving + Given I have created a Video component with handout file "textbook.pdf" + And I save changes + And I edit the component + And I open tab "Advanced" + And I can download handout file in editor with mime type "application/pdf" + """ + self._create_course_unit_with_handout('textbook.pdf') + + self.edit_component() + + self.open_advanced_tab() + + self.assertEqual(self.video.download_handout('application/pdf', is_editor=True), (True, True)) + + def test_handout_download_works_wo_save(self): + """ + Scenario: Handout downloading works correctly w/o preliminary saving + Given I have created a Video component with handout file "textbook.pdf" + And I can download handout file in editor with mime type "application/pdf" + """ + self._create_course_unit_with_handout('textbook.pdf', save_settings=False) + + self.assertEqual(self.video.download_handout('application/pdf', is_editor=True), (True, True)) + + def test_handout_clearing_works_w_save(self): + """ + Scenario: Handout clearing works correctly w/ preliminary saving + Given I have created a Video component with handout file "textbook.pdf" + And I save changes + And I can download handout file with mime type "application/pdf" + And I edit the component + And I open tab "Advanced" + And I clear handout + And I save changes + Then I do not see video button "handout" + """ + self._create_course_unit_with_handout('textbook.pdf') + + self.assertEqual(self.video.download_handout('application/pdf'), (True, True)) + + self.edit_component() + + self.open_advanced_tab() + + self.video.clear_handout() + + self.save_unit_settings() + + self.assertFalse(self.video.is_handout_button_visible) + + def test_handout_clearing_works_wo_save(self): + """ + Scenario: Handout clearing works correctly w/o preliminary saving + Given I have created a Video component with handout file "asset.html" + And I clear handout + And I save changes + Then I do not see video button "handout" + """ + self._create_course_unit_with_handout('asset.html', save_settings=False) + + self.video.clear_handout() + + self.save_unit_settings() + + self.assertFalse(self.video.is_handout_button_visible) + + def test_handout_replace_w_save(self): + """ + Scenario: User can easy replace the handout by another one w/ preliminary saving + Given I have created a Video component with handout file "asset.html" + And I save changes + Then I can see video button "handout" + And I can download handout file with mime type "text/html" + And I edit the component + And I open tab "Advanced" + And I replace handout file by "textbook.pdf" + And I save changes + Then I can see video button "handout" + And I can download handout file with mime type "application/pdf" + """ + self._create_course_unit_with_handout('asset.html') + + self.assertTrue(self.video.is_handout_button_visible) + + self.assertEqual(self.video.download_handout('text/html'), (True, True)) + + self.edit_component() + + self.open_advanced_tab() + + self.video.upload_handout('textbook.pdf') + + self.save_unit_settings() + + self.assertTrue(self.video.is_handout_button_visible) + + self.assertEqual(self.video.download_handout('application/pdf'), (True, True)) + + def test_handout_replace_wo_save(self): + """ + Scenario: User can easy replace the handout by another one w/o preliminary saving + Given I have created a Video component with handout file "asset.html" + And I replace handout file by "textbook.pdf" + And I save changes + Then I can see video button "handout" + And I can download handout file with mime type "application/pdf" + """ + self._create_course_unit_with_handout('asset.html', save_settings=False) + + self.video.upload_handout('textbook.pdf') + + self.save_unit_settings() + + self.assertTrue(self.video.is_handout_button_visible) + + self.assertEqual(self.video.download_handout('application/pdf'), (True, True)) + + def test_handout_upload_and_clear_works(self): + """ + Scenario: Upload file "A" -> Remove it -> Upload file "B" + Given I have created a Video component with handout file "asset.html" + And I clear handout + And I upload handout file "textbook.pdf" + And I save changes + Then I can see video button "handout" + And I can download handout file with mime type "application/pdf" + """ + self._create_course_unit_with_handout('asset.html', save_settings=False) + + self.video.clear_handout() + + self.video.upload_handout('textbook.pdf') + + self.save_unit_settings() + + self.assertTrue(self.video.is_handout_button_visible) + + self.assertEqual(self.video.download_handout('application/pdf'), (True, True)) diff --git a/common/test/data/uploads/.gitignore b/common/test/data/uploads/.gitignore index a85ef7b7f3..b02def2d0f 100644 --- a/common/test/data/uploads/.gitignore +++ b/common/test/data/uploads/.gitignore @@ -1,3 +1,2 @@ test test2 -asset.html diff --git a/common/test/data/uploads/asset.html b/common/test/data/uploads/asset.html new file mode 100644 index 0000000000..7898439363 --- /dev/null +++ b/common/test/data/uploads/asset.html @@ -0,0 +1,10 @@ + + + + ASSET + + +
i am a dummy asset file
+ + + \ No newline at end of file