Test fixes to prepare for firefox upgrade on jenkins

This commit is contained in:
Stuart Young
2018-01-25 13:15:44 -05:00
committed by Michael Youngstrom
parent 81899bdaee
commit 53fe34615e
27 changed files with 158 additions and 50 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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):

View File

@@ -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):
"""

View File

@@ -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")

View File

@@ -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):

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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.")

View File

@@ -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)

View File

@@ -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:

View File

@@ -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):
"""

View File

@@ -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.

View File

@@ -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 \

View File

@@ -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

View File

@@ -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