from bok_choy.page_object import PageObject from selenium.webdriver.common.keys import Keys from common.test.acceptance.pages.common.utils import click_css from common.test.acceptance.tests.helpers import select_option_by_text, get_selected_option_text from selenium.webdriver.support.ui import Select 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 Tracks" @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, "Current visible 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()