From 53fe34615ee39e23f7cb3302fd6a13104f89dde7 Mon Sep 17 00:00:00 2001 From: Stuart Young Date: Thu, 25 Jan 2018 13:15:44 -0500 Subject: [PATCH] Test fixes to prepare for firefox upgrade on jenkins --- cms/envs/acceptance.py | 7 +-- cms/envs/bok_choy.py | 7 +-- .../test/acceptance/pages/common/__init__.py | 2 +- .../pages/lms/instructor_dashboard.py | 16 +++---- .../acceptance/pages/lms/learner_profile.py | 2 - .../test/acceptance/pages/lms/video/video.py | 13 ++++++ .../test/acceptance/pages/studio/container.py | 4 +- .../acceptance/pages/studio/import_export.py | 2 +- common/test/acceptance/pages/studio/index.py | 15 +++++-- .../test/acceptance/pages/studio/overview.py | 12 +++-- .../test/acceptance/pages/studio/settings.py | 11 +++-- common/test/acceptance/pages/studio/utils.py | 8 ++-- .../acceptance/pages/studio/video/video.py | 42 ++++++++++++++++++ .../tests/discussion/test_discussion.py | 1 - common/test/acceptance/tests/helpers.py | 21 ++++++++- common/test/acceptance/tests/lms/test_lms.py | 4 +- .../acceptance/tests/lms/test_lms_help.py | 14 ++++-- .../tests/studio/test_import_export.py | 2 + .../tests/studio/test_studio_rerun.py | 1 + .../test_studio_settings_certificates.py | 3 +- .../tests/video/test_studio_video_editor.py | 3 +- .../{uploads => imports}/bad_course.tar.gz | Bin .../{uploads => imports}/bad_library.tar.gz | Bin common/test/data/imports/funny_cat_video.mp4 | 2 + lms/envs/acceptance.py | 7 +-- lms/envs/bok_choy.py | 8 ++-- requirements/edx/base.txt | 1 + 27 files changed, 158 insertions(+), 50 deletions(-) rename common/test/data/{uploads => imports}/bad_course.tar.gz (100%) rename common/test/data/{uploads => imports}/bad_library.tar.gz (100%) create mode 100644 common/test/data/imports/funny_cat_video.mp4 diff --git a/cms/envs/acceptance.py b/cms/envs/acceptance.py index 4cabd08403..afcbfee46c 100644 --- a/cms/envs/acceptance.py +++ b/cms/envs/acceptance.py @@ -136,9 +136,10 @@ except ImportError: pass # Point the URL used to test YouTube availability to our stub YouTube server -YOUTUBE['API'] = "http://127.0.0.1:{0}/get_youtube_api/".format(YOUTUBE_PORT) -YOUTUBE['METADATA_URL'] = "http://127.0.0.1:{0}/test_youtube/".format(YOUTUBE_PORT) -YOUTUBE['TEXT_API']['url'] = "127.0.0.1:{0}/test_transcripts_youtube/".format(YOUTUBE_PORT) +YOUTUBE_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1') +YOUTUBE['API'] = "http://{0}:{1}/get_youtube_api/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['METADATA_URL'] = "http://{0}:{1}/test_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['TEXT_API']['url'] = "{0}:{1}/test_transcripts_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) YOUTUBE['TEST_TIMEOUT'] = 1500 # Generate a random UUID so that different runs of acceptance tests don't break each other diff --git a/cms/envs/bok_choy.py b/cms/envs/bok_choy.py index 895dec3b6e..cd681ed13b 100644 --- a/cms/envs/bok_choy.py +++ b/cms/envs/bok_choy.py @@ -119,9 +119,10 @@ FEATURES['ENABLE_SPECIAL_EXAMS'] = True # Point the URL used to test YouTube availability to our stub YouTube server YOUTUBE_PORT = 9080 YOUTUBE['TEST_TIMEOUT'] = 5000 -YOUTUBE['API'] = "http://127.0.0.1:{0}/get_youtube_api/".format(YOUTUBE_PORT) -YOUTUBE['METADATA_URL'] = "http://127.0.0.1:{0}/test_youtube/".format(YOUTUBE_PORT) -YOUTUBE['TEXT_API']['url'] = "127.0.0.1:{0}/test_transcripts_youtube/".format(YOUTUBE_PORT) +YOUTUBE_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1') +YOUTUBE['API'] = "http://{0}:{1}/get_youtube_api/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['METADATA_URL'] = "http://{0}:{1}/test_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['TEXT_API']['url'] = "{0}:{1}/test_transcripts_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) FEATURES['ENABLE_COURSEWARE_INDEX'] = True FEATURES['ENABLE_LIBRARY_INDEX'] = True diff --git a/common/test/acceptance/pages/common/__init__.py b/common/test/acceptance/pages/common/__init__.py index 264cba811d..c00b0da0ec 100644 --- a/common/test/acceptance/pages/common/__init__.py +++ b/common/test/acceptance/pages/common/__init__.py @@ -2,7 +2,7 @@ import os HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost') CMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', 8031) -LMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', 8003) +LMS_PORT = os.environ.get('BOK_CHOY_LMS_PORT', 8003) # Get the URL of the instance under test BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, LMS_PORT)) diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py index f7475184a4..91e7ef30ce 100644 --- a/common/test/acceptance/pages/lms/instructor_dashboard.py +++ b/common/test/acceptance/pages/lms/instructor_dashboard.py @@ -21,11 +21,11 @@ class InstructorDashboardPage(CoursePage): def is_browser_on_page(self): return self.q(css='div.instructor-dashboard-wrapper-2').present - def click_help(self): + def get_help_element(self): """ - Clicks the general Help button in the header. + Returns the general Help button in the header. """ - self.q(css='.help-link').first.click() + return self.q(css='.help-link').first def select_membership(self): """ @@ -272,16 +272,14 @@ class CohortManagementSection(PageObject): select_content_group_button_css = '.cohort-management-details-association-course input.radio-yes' assignment_type_buttons_css = '.cohort-management-assignment-type-settings input' - def get_cohort_help_element_and_click_help(self): + def get_cohort_help_element(self): """ - Clicks help link and returns it. Specifically, clicks 'What does it mean' + Returns the help element ('What does it mean') Returns: help_element (WebElement): help link element """ - help_element = self.q(css=self.cohort_help_css).results[0] - help_element.click() - return help_element + return self.q(css=self.cohort_help_css).results[0] def is_browser_on_page(self): """ @@ -559,6 +557,8 @@ class CohortManagementSection(PageObject): Returns whether or not the radio button is in the selected state after the click. """ radio_button = self.q(css=self._bounded_selector(self.select_content_group_button_css)).results[0] + if not radio_button.is_enabled(): + return False radio_button.click() return radio_button.is_selected() diff --git a/common/test/acceptance/pages/lms/learner_profile.py b/common/test/acceptance/pages/lms/learner_profile.py index 3b20ca47d5..d8fcdc7e70 100644 --- a/common/test/acceptance/pages/lms/learner_profile.py +++ b/common/test/acceptance/pages/lms/learner_profile.py @@ -281,9 +281,7 @@ class LearnerProfilePage(FieldsMixin, PageObject): self.browser.execute_script('$(".upload-button-input").css("opacity",1);') self.wait_for_element_visibility('.upload-button-input', "upload button is visible") - self.browser.execute_script('$(".upload-submit").show();') - self.q(css='.upload-submit').first.click() self.q(css='.upload-button-input').results[0].send_keys(file_path) self.wait_for_ajax() diff --git a/common/test/acceptance/pages/lms/video/video.py b/common/test/acceptance/pages/lms/video/video.py index db6ca969bd..c3249cb7e0 100644 --- a/common/test/acceptance/pages/lms/video/video.py +++ b/common/test/acceptance/pages/lms/video/video.py @@ -93,6 +93,18 @@ class VideoPage(PageObject): video_selector = '{0}'.format(CSS_CLASS_NAMES['video_container']) self.wait_for_element_presence(video_selector, 'Video is initialized') + def scroll_to_button(self, button_name, index=0): + """ + Scroll to a button specified by `button_name` + + Arguments: + button_name (str): button name + index (int): query index + + """ + element = self.q(css=VIDEO_BUTTONS[button_name])[index] + self.browser.execute_script("arguments[0].scrollIntoView();", element) + @wait_for_js def wait_for_video_player_render(self, autoplay=False): """ @@ -468,6 +480,7 @@ class VideoPage(PageObject): """ # mouse over to video speed button + self.scroll_to_button('speed') speed_menu_selector = self.get_element_selector(VIDEO_BUTTONS['speed']) element_to_hover_over = self.q(css=speed_menu_selector).results[0] hover = ActionChains(self.browser).move_to_element(element_to_hover_over) diff --git a/common/test/acceptance/pages/studio/container.py b/common/test/acceptance/pages/studio/container.py index 047d422344..f2669f71b8 100644 --- a/common/test/acceptance/pages/studio/container.py +++ b/common/test/acceptance/pages/studio/container.py @@ -8,6 +8,7 @@ from bok_choy.promise import EmptyPromise, Promise from common.test.acceptance.pages.common.utils import click_css, confirm_prompt from common.test.acceptance.pages.studio import BASE_URL from common.test.acceptance.pages.studio.utils import HelpMixin, type_in_codemirror +from common.test.acceptance.tests.helpers import click_and_wait_for_window class ContainerPage(PageObject, HelpMixin): @@ -177,6 +178,7 @@ class ContainerPage(PageObject, HelpMixin): """ Returns the link for publishing a unit. """ + self.scroll_to_element('.action-publish') return self.q(css='.action-publish').first def publish(self): @@ -224,7 +226,7 @@ class ContainerPage(PageObject, HelpMixin): Switches the browser to the newly opened LMS window. """ - self.q(css='.button-view').first.click() + click_and_wait_for_window(self, self.q(css='.button-view').first) self._switch_to_lms() def verify_publish_title(self, expected_title): diff --git a/common/test/acceptance/pages/studio/import_export.py b/common/test/acceptance/pages/studio/import_export.py index 6fd98a7d07..bc249347dc 100644 --- a/common/test/acceptance/pages/studio/import_export.py +++ b/common/test/acceptance/pages/studio/import_export.py @@ -291,7 +291,7 @@ class ImportMixin(ImportExportMixin): self.q(css='input[type="file"]')[0].send_keys(asset_file_path) # Some of the tests need these lines to pass so don't remove them. self._wait_for_button() - click_css(self, '.submit-button', require_notification=False) + click_css(self, '.submit-button', require_notification=True) def is_upload_finished(self): """ diff --git a/common/test/acceptance/pages/studio/index.py b/common/test/acceptance/pages/studio/index.py index 7a645e4710..d22d00c671 100644 --- a/common/test/acceptance/pages/studio/index.py +++ b/common/test/acceptance/pages/studio/index.py @@ -83,6 +83,13 @@ class DashboardPage(PageObject, HelpMixin): # Clicking on course with run will trigger an ajax event self.wait_for_ajax() + def scroll_to_course(self, course_key): + """ + Scroll down to the course element + """ + element = '[data-course-key*="{}"]'.format(course_key) + self.scroll_to_element(element) + def has_new_library_button(self): """ (bool) is the "New Library" button present? @@ -239,16 +246,18 @@ class DashboardPage(PageObject, HelpMixin): Click the tab to display the available libraries, and return detail of them. """ # Workaround Selenium/Firefox bug: `.text` property is broken on invisible elements - self.q(css='#course-index-tabs .libraries-tab a').click() + library_tab_css = '#course-index-tabs .libraries-tab' + self.wait_for_element_presence(library_tab_css, "Libraries tab") + self.q(css=library_tab_css).click() if self.q(css='.list-notices.libraries-tab').present: # No libraries are available. - self.wait_for_element_visibility('.libraries-tab .new-library-button', "Switch to library tab") + self.wait_for_element_presence('.libraries-tab .new-library-button', "new library tab") return [] div2info = lambda element: { 'name': element.find_element_by_css_selector('.course-title').text, + 'link_element': element.find_element_by_css_selector('.course-title'), 'org': element.find_element_by_css_selector('.course-org .value').text, 'number': element.find_element_by_css_selector('.course-num .value').text, - 'link_element': element.find_element_by_css_selector('a.library-link'), 'url': element.find_element_by_css_selector('a.library-link').get_attribute('href'), } self.wait_for_element_visibility('.libraries li.course-item', "Switch to library tab") diff --git a/common/test/acceptance/pages/studio/overview.py b/common/test/acceptance/pages/studio/overview.py index 576fd68423..db2be17699 100644 --- a/common/test/acceptance/pages/studio/overview.py +++ b/common/test/acceptance/pages/studio/overview.py @@ -290,13 +290,17 @@ class CourseOutlineContainer(CourseOutlineItem): self.wait_for_element_presence( self._bounded_selector(self.ADD_BUTTON_SELECTOR), 'Toggle control is present' ) - add_button = self.q(css=self._bounded_selector(self.ADD_BUTTON_SELECTOR)).first.results + css_element = self._bounded_selector(self.ADD_BUTTON_SELECTOR) + add_button = self.q(css=css_element).first.results # pylint: disable=no-member + self.scroll_to_element(css_element) # pylint: disable=no-member return add_button and add_button[0].is_displayed() currently_expanded = subsection_expanded() # Need to click slightly off-center in order for the click to be recognized. - ele = self.browser.find_element_by_css_selector(self._bounded_selector('.ui-toggle-expansion .fa')) + css_element = self._bounded_selector('.ui-toggle-expansion .fa') + self.scroll_to_element(css_element) # pylint: disable=no-member + ele = self.browser.find_element_by_css_selector(css_element) # pylint: disable=no-member ActionChains(self.browser).move_to_element_with_offset(ele, 8, 8).click().perform() # pylint: disable=no-member self.wait_for_element_presence(self._bounded_selector(self.ADD_BUTTON_SELECTOR), 'Subsection is expanded') @@ -314,7 +318,9 @@ class CourseOutlineContainer(CourseOutlineItem): """ Return whether this outline item is currently collapsed. """ - return "is-collapsed" in self.q(css=self._bounded_selector('')).first.attrs("class")[0] # pylint: disable=no-member + css_element = self._bounded_selector('') + self.scroll_to_element(css_element) # pylint: disable=no-member + return "is-collapsed" in self.q(css=css_element).first.attrs("class")[0] # pylint: disable=no-member class CourseOutlineChild(PageObject, CourseOutlineItem): diff --git a/common/test/acceptance/pages/studio/settings.py b/common/test/acceptance/pages/studio/settings.py index 8a6e164239..468924d0f6 100644 --- a/common/test/acceptance/pages/studio/settings.py +++ b/common/test/acceptance/pages/studio/settings.py @@ -260,18 +260,21 @@ class SettingsPage(CoursePage): Set the entrance exam requirement via the checkbox. """ checkbox = self.entrance_exam_field + # Wait for license section to load before interacting with the checkbox to avoid race condition + self.wait_for_element_presence('div.wrapper-license', 'License section present') selected = checkbox.is_selected() + self.scroll_to_element('#entrance-exam-enabled') if required and not selected: checkbox.click() - self.wait_for_element_visibility( + self.wait_for_element_presence( '#entrance-exam-minimum-score-pct', - 'Entrance exam minimum score percent is visible' + 'Entrance exam minimum score percent is present' ) if not required and selected: checkbox.click() - self.wait_for_element_invisibility( + self.wait_for_element_absence( '#entrance-exam-minimum-score-pct', - 'Entrance exam minimum score percent is invisible' + 'Entrance exam minimum score percent is absent' ) def save_changes(self, wait_for_confirmation=True): diff --git a/common/test/acceptance/pages/studio/utils.py b/common/test/acceptance/pages/studio/utils.py index 90c694a3e0..0d8f9bfe29 100644 --- a/common/test/acceptance/pages/studio/utils.py +++ b/common/test/acceptance/pages/studio/utils.py @@ -7,6 +7,7 @@ from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from common.test.acceptance.pages.common.utils import click_css, sync_on_notification +from common.test.acceptance.tests.helpers import click_and_wait_for_window NAV_HELP_NOT_SIGNED_IN_CSS = '.nav-item.nav-not-signedin-help a' NAV_HELP_CSS = '.nav-item.nav-account-help a' @@ -288,8 +289,9 @@ class HelpMixin(object): else: element_css = NAV_HELP_NOT_SIGNED_IN_CSS - self.q(css=element_css).first.click() - return self.q(css=element_css).results[0] + help_element = self.q(css=element_css).results[0] + click_and_wait_for_window(self, help_element) + return help_element def get_side_bar_help_element_and_click_help(self, as_list_item=False, index=-1): """ @@ -313,5 +315,5 @@ class HelpMixin(object): element_css = SIDE_BAR_HELP_CSS help_element = self.q(css=element_css).results[index] - help_element.click() + click_and_wait_for_window(self, help_element) return help_element diff --git a/common/test/acceptance/pages/studio/video/video.py b/common/test/acceptance/pages/studio/video/video.py index 1b6f75d8a8..2ba230dfcf 100644 --- a/common/test/acceptance/pages/studio/video/video.py +++ b/common/test/acceptance/pages/studio/video/video.py @@ -50,6 +50,10 @@ BUTTON_SELECTORS = { 'collapse_link': '.collapse-action.collapse-setting', } +DROP_DOWN_SELECTORS = { + 'transcript_language': '.wrapper-translations-settings .list-settings .list-settings-item select' +} + DISPLAY_NAME = "Component Display Name" DEFAULT_SETTINGS = [ @@ -160,11 +164,48 @@ class VideoComponentPage(VideoPage): index (int): query index """ + self.scroll_to_button(button_name, index) self.q(css=BUTTON_SELECTORS[button_name]).nth(index).click() if require_notification: sync_on_notification(self) self.wait_for_ajax() + def scroll_to_button(self, button_name, index=0): + """ + Scroll to a button specified by `button_name` + + Arguments: + button_name (str): button name + index (int): query index + + """ + element = self.q(css=BUTTON_SELECTORS[button_name])[index] + self.browser.execute_script("arguments[0].scrollIntoView();", element) + + def get_drop_down_items(self, drop_down_name, index=0): + """ + Get the items from a drop down list specified by `drop_down_name` + + Arguments: + drop_down_name (str): name of the drop down list + index (int): query index + + """ + drop_downs = self.q(css=DROP_DOWN_SELECTORS[drop_down_name]) + return drop_downs[index].find_elements_by_tag_name("option") + + def is_language_disabled(self, lang_code): + """ + Determine whether or not a lanuage is disabled in a drop down + + Arguments: + lang_code (str): two letter language code + + """ + language_options = self.get_drop_down_items('transcript_language', index=1) + language = [l for l in language_options if l.get_attribute('value') == lang_code][0] + return language.get_attribute("disabled") + @staticmethod def file_path(filename): """ @@ -197,6 +238,7 @@ class VideoComponentPage(VideoPage): """ asset_file_path = self.file_path(asset_filename) + self.scroll_to_button('upload_asset') self.click_button('upload_asset', index) self.q(css=CLASS_SELECTORS['attach_asset']).results[0].send_keys(asset_file_path) # Only srt format transcript files can be uploaded, If an error diff --git a/common/test/acceptance/tests/discussion/test_discussion.py b/common/test/acceptance/tests/discussion/test_discussion.py index 5f962a45fd..f5d9c025d7 100644 --- a/common/test/acceptance/tests/discussion/test_discussion.py +++ b/common/test/acceptance/tests/discussion/test_discussion.py @@ -991,7 +991,6 @@ class DiscussionEditorPreviewTest(UniqueCourseTest): 'Text line 2 \n' '$$e[n]=d_2$$' ) - self.assertEqual(self.page.get_new_post_preview_text(), 'Text line 1\nText line 2') def test_mathjax_not_rendered_after_post_cancel(self): diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py index 92e6db4a91..5642bfe181 100644 --- a/common/test/acceptance/tests/helpers.py +++ b/common/test/acceptance/tests/helpers.py @@ -427,6 +427,7 @@ def assert_opened_help_link_is_correct(test, url): url (str): url to verify. """ test.browser.switch_to_window(test.browser.window_handles[-1]) + WebDriverWait(test.browser, 10).until(lambda driver: driver.current_url != "about:blank") # Assert that url in the browser is the same. test.assertEqual(url, test.browser.current_url) # Check that the URL loads. Can't do this in the browser because it might @@ -788,8 +789,9 @@ class YouTubeStubConfig(object): Configure YouTube Stub Server. """ + YOUTUBE_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1') PORT = 9080 - URL = 'http://127.0.0.1:{}/'.format(PORT) + URL = 'http://{}:{}/'.format(YOUTUBE_HOSTNAME, PORT) @classmethod def configure(cls, config): @@ -850,6 +852,23 @@ class YouTubeStubConfig(object): return {} +def click_and_wait_for_window(page, element): + """ + To avoid a race condition, click an element that launces a new window, and + wait for that window to launch. + To check this, make sure the number of window_handles increases by one. + + Arguments: + page (PageObject): Page object to perform method on + element (WebElement): Clickable element that triggers the new window to open + """ + num_windows = len(page.browser.window_handles) + element.click() + WebDriverWait(page.browser, 10).until( + lambda driver: len(driver.window_handles) > num_windows + ) + + def create_user_partition_json(partition_id, name, description, groups, scheme="random"): """ Helper method to create user partition JSON. If scheme is not supplied, "random" is used. diff --git a/common/test/acceptance/tests/lms/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py index 4abe017a8a..1b711ab0da 100644 --- a/common/test/acceptance/tests/lms/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -1010,7 +1010,7 @@ class EntranceExamTest(UniqueCourseTest): # visit course settings page and set/enabled entrance exam for that course. self.settings_page.visit() - self.settings_page.entrance_exam_field.click() + self.settings_page.require_entrance_exam() self.settings_page.save_changes() # Logout and login as a student. @@ -1047,7 +1047,7 @@ class EntranceExamTest(UniqueCourseTest): # visit course settings page and set/enabled entrance exam for that course. self.settings_page.visit() - self.settings_page.entrance_exam_field.click() + self.settings_page.require_entrance_exam() self.settings_page.save_changes() # Logout and login as a student. diff --git a/common/test/acceptance/tests/lms/test_lms_help.py b/common/test/acceptance/tests/lms/test_lms_help.py index c21a429c65..b2befb9f20 100644 --- a/common/test/acceptance/tests/lms/test_lms_help.py +++ b/common/test/acceptance/tests/lms/test_lms_help.py @@ -5,9 +5,13 @@ Test Help links in LMS from common.test.acceptance.fixtures.course import CourseFixture from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage from common.test.acceptance.tests.discussion.helpers import CohortTestMixin -from common.test.acceptance.tests.helpers import assert_opened_help_link_is_correct, url_for_help from common.test.acceptance.tests.lms.test_lms_instructor_dashboard import BaseInstructorDashboardTest from common.test.acceptance.tests.studio.base_studio_test import ContainerBase +from common.test.acceptance.tests.helpers import ( + assert_opened_help_link_is_correct, + url_for_help, + click_and_wait_for_window +) class TestCohortHelp(ContainerBase, CohortTestMixin): @@ -27,8 +31,9 @@ class TestCohortHelp(ContainerBase, CohortTestMixin): Arguments: href (str): Help url """ - actual_link = self.cohort_management.get_cohort_help_element_and_click_help() - self.assertEqual(actual_link.text, "What does this mean?") + help_element = self.cohort_management.get_cohort_help_element() + self.assertEqual(help_element.text, "What does this mean?") + click_and_wait_for_window(self, help_element) assert_opened_help_link_is_correct(self, href) def test_manual_cohort_help(self): @@ -92,5 +97,6 @@ class InstructorDashboardHelp(BaseInstructorDashboardTest): Then I see help about the instructor dashboard in a new tab """ href = url_for_help('course_author', '/CA_instructor_dash_help.html') - self.instructor_dashboard_page.click_help() + help_element = self.instructor_dashboard_page.get_help_element() + click_and_wait_for_window(self, help_element) assert_opened_help_link_is_correct(self, href) diff --git a/common/test/acceptance/tests/studio/test_import_export.py b/common/test/acceptance/tests/studio/test_import_export.py index ee08755a50..52c6bcc22e 100644 --- a/common/test/acceptance/tests/studio/test_import_export.py +++ b/common/test/acceptance/tests/studio/test_import_export.py @@ -218,6 +218,7 @@ class ImportTestMixin(object): A button will appear that contains the URL to the library or course's main page """ self.import_page.upload_tarball(self.tarball_name) + self.import_page.wait_for_upload() self.assertEqual(self.import_page.finished_target_url(), self.landing_page.url) def test_bad_filename_error(self): @@ -242,6 +243,7 @@ class ImportTestMixin(object): self.assertFalse(self.import_page.is_task_list_showing(), "Task list shown too early.") self.import_page.wait_for_tasks() self.import_page.upload_tarball(self.tarball_name) + self.import_page.wait_for_upload() self.import_page.wait_for_tasks(completed=True) self.assertTrue(self.import_page.is_task_list_showing(), "Task list did not display.") diff --git a/common/test/acceptance/tests/studio/test_studio_rerun.py b/common/test/acceptance/tests/studio/test_studio_rerun.py index 8088e65356..bcff7f1d4e 100644 --- a/common/test/acceptance/tests/studio/test_studio_rerun.py +++ b/common/test/acceptance/tests/studio/test_studio_rerun.py @@ -70,6 +70,7 @@ class CourseRerunTest(StudioCourseTest): updated_course_info = course_info[0] + "+" + course_info[1] + "+" + course_info[2] self.dashboard_page.visit() + self.dashboard_page.scroll_to_course(course_info[1]) self.dashboard_page.create_rerun(updated_course_info) rerun_page = CourseRerunPage(self.browser, *course_info) diff --git a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py index 3925dbaf22..93c174d28c 100644 --- a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py +++ b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py @@ -2,7 +2,6 @@ Acceptance tests for Studio's Setting pages """ import re -import uuid from nose.plugins.attrib import attr @@ -73,7 +72,7 @@ class CertificatesTest(StudioCourseTest): certificate.signatories[idx].name = signatory['name'] certificate.signatories[idx].title = signatory['title'] certificate.signatories[idx].organization = signatory['organization'] - certificate.signatories[idx].upload_signature_image('Signature-{}.png'.format(uuid.uuid4().hex[:4])) + certificate.signatories[idx].upload_signature_image('Signature-{}.png'.format(idx)) added_signatories += 1 if len(signatories) > added_signatories: diff --git a/common/test/acceptance/tests/video/test_studio_video_editor.py b/common/test/acceptance/tests/video/test_studio_video_editor.py index 17fd98159a..73c2d126b5 100644 --- a/common/test/acceptance/tests/video/test_studio_video_editor.py +++ b/common/test/acceptance/tests/video/test_studio_video_editor.py @@ -478,8 +478,7 @@ class VideoEditorTest(CMSVideoBaseTest): self.video.click_button('translation_add') self.video.select_translation_language('zh') self.video.click_button('translation_add') - self.video.select_translation_language('zh') - self.assertEqual(self.video.translations(), [u'zh', u'']) + self.assertTrue(self.video.is_language_disabled('zh')) def test_table_of_contents(self): """ diff --git a/common/test/data/uploads/bad_course.tar.gz b/common/test/data/imports/bad_course.tar.gz similarity index 100% rename from common/test/data/uploads/bad_course.tar.gz rename to common/test/data/imports/bad_course.tar.gz diff --git a/common/test/data/uploads/bad_library.tar.gz b/common/test/data/imports/bad_library.tar.gz similarity index 100% rename from common/test/data/uploads/bad_library.tar.gz rename to common/test/data/imports/bad_library.tar.gz diff --git a/common/test/data/imports/funny_cat_video.mp4 b/common/test/data/imports/funny_cat_video.mp4 new file mode 100644 index 0000000000..f8aac2e992 --- /dev/null +++ b/common/test/data/imports/funny_cat_video.mp4 @@ -0,0 +1,2 @@ +This is not an mp4 file. It is used in tests that upload tarballs to make +sure that other extensions are not accepted. diff --git a/lms/envs/acceptance.py b/lms/envs/acceptance.py index ee7e4d423a..9fede5c749 100644 --- a/lms/envs/acceptance.py +++ b/lms/envs/acceptance.py @@ -188,9 +188,10 @@ XQUEUE_INTERFACE = { } # Point the URL used to test YouTube availability to our stub YouTube server -YOUTUBE['API'] = "http://127.0.0.1:{0}/get_youtube_api/".format(YOUTUBE_PORT) -YOUTUBE['METADATA_URL'] = "http://127.0.0.1:{0}/test_youtube/".format(YOUTUBE_PORT) -YOUTUBE['TEXT_API']['url'] = "127.0.0.1:{0}/test_transcripts_youtube/".format(YOUTUBE_PORT) +YOUTUBE_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1') +YOUTUBE['API'] = "http://{0}:{1}/get_youtube_api/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['METADATA_URL'] = "http://{0}:{1}/test_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['TEXT_API']['url'] = "{0}:{1}/test_transcripts_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) YOUTUBE['TEST_TIMEOUT'] = 1500 if FEATURES.get('ENABLE_COURSEWARE_SEARCH') or \ diff --git a/lms/envs/bok_choy.py b/lms/envs/bok_choy.py index 0ba4095a5c..115a023c75 100644 --- a/lms/envs/bok_choy.py +++ b/lms/envs/bok_choy.py @@ -157,12 +157,14 @@ FEATURES['ENTRANCE_EXAMS'] = True FEATURES['ENABLE_SPECIAL_EXAMS'] = True + +YOUTUBE_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1') # Point the URL used to test YouTube availability to our stub YouTube server YOUTUBE_PORT = 9080 YOUTUBE['TEST_TIMEOUT'] = 5000 -YOUTUBE['API'] = "http://127.0.0.1:{0}/get_youtube_api/".format(YOUTUBE_PORT) -YOUTUBE['METADATA_URL'] = "http://127.0.0.1:{0}/test_youtube/".format(YOUTUBE_PORT) -YOUTUBE['TEXT_API']['url'] = "127.0.0.1:{0}/test_transcripts_youtube/".format(YOUTUBE_PORT) +YOUTUBE['API'] = "http://{0}:{1}/get_youtube_api/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['METADATA_URL'] = "http://{0}:{1}/test_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) +YOUTUBE['TEXT_API']['url'] = "{0}:{1}/test_transcripts_youtube/".format(YOUTUBE_HOSTNAME, YOUTUBE_PORT) ############################# SECURITY SETTINGS ################################ # Default to advanced security in common.py, so tests can reset here to use diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 0f36dbeca7..c4df241654 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -170,6 +170,7 @@ sqlparse>=0.2.0,<0.3.0 # Used for testing before_after==0.1.3 bok-choy==0.7.1 + chrono==1.0.2 ddt==0.8.0 django-crum==0.7.2