from bok_choy.page_object import PageObject from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from common.test.acceptance.pages.common.utils import click_css from common.test.acceptance.tests.helpers import get_selected_option_text, select_option_by_text class BaseComponentEditorView(PageObject): """ A base :class:`.PageObject` for the component and visibility editors. This class assumes that the editor is our default editor as displayed for xmodules. """ BODY_SELECTOR = '.xblock-editor' def __init__(self, browser, locator): """ Args: browser (selenium.webdriver): The Selenium-controlled browser that this page is loaded in. locator (str): The locator that identifies which xblock this :class:`.xblock-editor` relates to. """ super(BaseComponentEditorView, self).__init__(browser) self.locator = locator def is_browser_on_page(self): return self.q(css='{}[data-locator="{}"]'.format(self.BODY_SELECTOR, self.locator)).present def _bounded_selector(self, selector): """ Return `selector`, but limited to this particular `ComponentEditorView` context """ return '{}[data-locator="{}"] {}'.format( self.BODY_SELECTOR, self.locator, selector ) def url(self): """ Returns None because this is not directly accessible via URL. """ return None def save(self): """ Clicks save button. """ click_css(self, 'a.action-save') def cancel(self): """ Clicks cancel button. """ click_css(self, 'a.action-cancel', require_notification=False) class ComponentEditorView(BaseComponentEditorView): """ A :class:`.PageObject` representing the rendered view of a component editor. """ def get_setting_element(self, label): """ Returns the index of the setting entry with given label (display name) within the Settings modal. """ settings_button = self.q(css='.edit-xblock-modal .editor-modes .settings-button') if settings_button.is_present(): settings_button.click() setting_labels = self.q(css=self._bounded_selector('.metadata_edit .wrapper-comp-setting .setting-label')) for index, setting in enumerate(setting_labels): if setting.text == label: return self.q(css=self._bounded_selector('.metadata_edit div.wrapper-comp-setting .setting-input'))[index] return None def set_field_value_and_save(self, label, value): """ Sets the text field with given label (display name) to the specified value, and presses Save. """ elem = self.get_setting_element(label) # Clear the current value, set the new one, then # Tab to move to the next field (so change event is triggered). elem.clear() elem.send_keys(value) elem.send_keys(Keys.TAB) self.save() def set_select_value_and_save(self, label, value): """ Sets the select with given label (display name) to the specified value, and presses Save. """ elem = self.get_setting_element(label) select = Select(elem) select.select_by_value(value) self.save() def get_selected_option_text(self, label): """ Returns the text of the first selected option for the select with given label (display name). """ elem = self.get_setting_element(label) if elem: select = Select(elem) return select.first_selected_option.text else: return None class ComponentVisibilityEditorView(BaseComponentEditorView): """ A :class:`.PageObject` representing the rendered view of a component visibility editor. """ OPTION_SELECTOR = '.partition-group-control .field' ALL_LEARNERS_AND_STAFF = 'All Learners and Staff' CONTENT_GROUP_PARTITION = 'Content Groups' ENROLLMENT_TRACK_PARTITION = "Enrollment Track Groups" @property def all_group_options(self): """ Return all partition groups. """ return self.q(css=self._bounded_selector(self.OPTION_SELECTOR)).results @property def current_groups_message(self): """ This returns the message shown at the top of the visibility dialog about the current visibility state (at the time that the dialog was opened). For example, "Access is restricted to: All Learners and Staff". """ return self.q(css=self._bounded_selector('.visibility-header'))[0].text @property def selected_partition_scheme(self): """ Return the selected partition scheme (or "All Learners and Staff" if no partitioning is selected). """ selector = self.q(css=self._bounded_selector('.partition-visibility select')) return get_selected_option_text(selector) def select_partition_scheme(self, partition_name): """ Sets the selected partition scheme to the one with the matching name. """ selector = self.q(css=self._bounded_selector('.partition-visibility select')) select_option_by_text(selector, partition_name, focus_out=True) @property def selected_groups(self): """ Return all selected partition groups. If none are selected, returns an empty array. """ results = [] for option in self.all_group_options: checkbox = option.find_element_by_css_selector('input') if checkbox.is_selected(): results.append(option) return results def select_group(self, group_name, save=True): """ Select the first group which has a label matching `group_name`. Arguments: group_name (str): The name of the group. save (boolean): Whether the "save" button should be clicked afterwards. Returns: bool: Whether a group with the provided name was found and clicked. """ for option in self.all_group_options: if group_name in option.text: checkbox = option.find_element_by_css_selector('input') checkbox.click() if save: self.save() return True return False def select_groups_in_partition_scheme(self, partition_name, group_names): """ Select groups in the provided partition scheme. The "save" button is clicked afterwards. """ self.select_partition_scheme(partition_name) for label in group_names: self.select_group(label, save=False) self.save()