Files
edx-platform/common/test/acceptance/pages/studio/settings_advanced.py
2019-12-30 12:25:38 -05:00

274 lines
9.1 KiB
Python

"""
Course Advanced Settings page
"""
import six
from bok_choy.promise import EmptyPromise
from common.test.acceptance.pages.studio.course_page import CoursePage
from common.test.acceptance.pages.studio.utils import (
get_codemirror_value,
press_the_notification_button,
type_in_codemirror
)
KEY_CSS = '.key h3.title'
UNDO_BUTTON_SELECTOR = ".action-item .action-undo"
MANUAL_BUTTON_SELECTOR = ".action-item .action-cancel"
MODAL_SELECTOR = ".validation-error-modal-content"
ERROR_ITEM_NAME_SELECTOR = ".error-item-title strong"
ERROR_ITEM_CONTENT_SELECTOR = ".error-item-message"
SETTINGS_NAME_SELECTOR = ".is-not-editable"
CONFIRMATION_MESSAGE_SELECTOR = "#alert-confirmation-title"
DEPRECATED_SETTINGS_SELECTOR = ".field-group.course-advanced-policy-list-item.is-deprecated"
DEPRECATED_SETTINGS_BUTTON_SELECTOR = ".deprecated-settings-label"
class AdvancedSettingsPage(CoursePage):
"""
Course Advanced Settings page.
"""
url_path = "settings/advanced"
def is_browser_on_page(self):
def _is_finished_loading():
return len(self.q(css='.course-advanced-policy-list-item')) > 0
EmptyPromise(_is_finished_loading, 'Finished rendering the advanced policy items.').fulfill()
return self.q(css='body.advanced').present
@property
def key_names(self):
"""
Returns a list of key names of all settings.
"""
return self.q(css=KEY_CSS).text
@property
def deprecated_settings_button_text(self):
"""
Returns text for deprecated settings button
"""
return self.q(css=DEPRECATED_SETTINGS_BUTTON_SELECTOR).text[0]
def is_deprecated_setting_visible(self):
"""
Returns true if deprecated settings are visible
"""
return self.q(css=DEPRECATED_SETTINGS_SELECTOR).visible
def toggle_deprecated_settings(self):
"""
Show deprecated Settings
"""
button_text = self.deprecated_settings_button_text
self.q(css=DEPRECATED_SETTINGS_BUTTON_SELECTOR).click()
if button_text == 'Show Deprecated Settings':
self.wait_for_element_presence(DEPRECATED_SETTINGS_SELECTOR, 'Deprecated Settings are present')
else:
self.wait_for_element_absence(DEPRECATED_SETTINGS_SELECTOR, 'Deprecated Settings are not present')
def wait_for_modal_load(self):
"""
Wait for validation response from the server, and make sure that
the validation error modal pops up.
This method should only be called when it is guaranteed that there're
validation errors in the settings changes.
"""
self.wait_for_ajax()
self.wait_for_element_presence(MODAL_SELECTOR, 'Validation Modal is present')
def refresh_and_wait_for_load(self):
"""
Refresh the page and wait for all resources to load.
"""
self.browser.refresh()
self.wait_for_page()
@property
def confirmation_message(self):
"""
Returns the text of confirmation message which appears after saving the settings
"""
self.wait_for_element_visibility(CONFIRMATION_MESSAGE_SELECTOR, 'Confirmation message is visible')
return self.q(css=CONFIRMATION_MESSAGE_SELECTOR).text[0]
def coordinates_for_scrolling(self, coordinates_for):
"""
Get the x and y coordinates of elements
"""
cordinates_dict = self.browser.find_element_by_css_selector(coordinates_for)
location = cordinates_dict.location
for key, val in six.iteritems(location):
if key == 'x':
x_axis = val
elif key == 'y':
y_axis = val
return x_axis, y_axis
def undo_changes_via_modal(self):
"""
Trigger clicking event of the undo changes button in the modal.
Wait for the undoing process to load via ajax call.
Before that Scroll so the button is clickable on all browsers
"""
self.browser.execute_script("window.scrollTo" + str(self.coordinates_for_scrolling(UNDO_BUTTON_SELECTOR)))
self.q(css=UNDO_BUTTON_SELECTOR).click()
self.wait_for_ajax()
def trigger_manual_changes(self):
"""
Trigger click event of the manual changes button in the modal.
No need to wait for any ajax.
Before that Scroll so the button is clickable on all browsers
"""
self.browser.execute_script("window.scrollTo" + str(self.coordinates_for_scrolling(MANUAL_BUTTON_SELECTOR)))
self.q(css=MANUAL_BUTTON_SELECTOR).click()
def is_validation_modal_present(self):
"""
Checks if the validation modal is present.
"""
return self.q(css=MODAL_SELECTOR).present
def get_error_item_names(self):
"""
Returns a list of display names of all invalid settings.
"""
return self.q(css=ERROR_ITEM_NAME_SELECTOR).text
def get_error_item_messages(self):
"""
Returns a list of error messages of all invalid settings.
"""
return self.q(css=ERROR_ITEM_CONTENT_SELECTOR).text
def _get_index_of(self, expected_key):
"""
Returns the index of expected key
"""
for i, element in enumerate(self.q(css=KEY_CSS)):
# Sometimes get stale reference if I hold on to the array of elements
key = self.q(css=KEY_CSS).nth(i).text[0]
if key == expected_key:
return i
return -1
def save(self):
press_the_notification_button(self, "Save")
def cancel(self):
press_the_notification_button(self, "Cancel")
def set(self, key, new_value):
index = self._get_index_of(key)
type_in_codemirror(self, index, new_value)
self.save()
def get(self, key):
index = self._get_index_of(key)
return get_codemirror_value(self, index)
def set_values(self, key_value_map):
"""
Make multiple settings changes and save them.
"""
for key, value in six.iteritems(key_value_map):
index = self._get_index_of(key)
type_in_codemirror(self, index, value)
self.save()
def get_values(self, key_list):
"""
Get a key-value dictionary of all keys in the given list.
"""
result_map = {}
for key in key_list:
index = self._get_index_of(key)
val = get_codemirror_value(self, index)
result_map[key] = val
return result_map
@property
def displayed_settings_names(self):
"""
Returns all settings displayed on the advanced settings page/screen/modal/whatever
We call it 'name', but it's really whatever is embedded in the 'id' element for each field
"""
query = self.q(css=SETTINGS_NAME_SELECTOR)
return query.attrs('id')
@property
def expected_settings_names(self):
"""
Returns a list of settings expected to be displayed on the Advanced Settings screen
Should match the list of settings found in cms/djangoapps/models/settings/course_metadata.py
If a new setting is added to the metadata list, this test will fail and you must update it.
Basically this guards against accidental exposure of a field on the Advanced Settings screen
"""
return [
'advanced_modules',
'allow_anonymous',
'allow_anonymous_to_peers',
'allow_public_wiki_access',
'cert_html_view_overrides',
'cert_name_long',
'cert_name_short',
'certificates_display_behavior',
'course_image',
'banner_image',
'video_thumbnail_image',
'cosmetic_display_price',
'advertised_start',
'announcement',
'display_name',
'is_new',
'issue_badges',
'max_student_enrollments_allowed',
'no_grade',
'display_coursenumber',
'display_organization',
'catalog_visibility',
'days_early_for_beta',
'disable_progress_graph',
'discussion_blackouts',
'discussion_sort_alpha',
'discussion_topics',
'due',
'due_date_display_format',
'edxnotes',
'use_latex_compiler',
'video_speed_optimizations',
'enrollment_domain',
'html_textbooks',
'invitation_only',
'lti_passports',
'matlab_api_key',
'max_attempts',
'mobile_available',
'rerandomize',
'remote_gradebook',
'showanswer',
'show_calculator',
'show_reset_button',
'static_asset_path',
'teams_configuration',
'social_sharing_url',
'video_bumper',
'enable_proctored_exams',
'allow_proctoring_opt_out',
'enable_timed_exams',
'enable_subsection_gating',
'learning_info',
'instructor_info',
'ccx_connector',
'enable_ccx',
]