def rename_section(self, name=None): """Rename a current course section or period. .. note:: If the name matches an existing section (case-sensitive), an HTML 422 error will be thrown by Tutor; server errors are not handled by the automation code. If name is not provided, open and return the Rename Section modal. If it is provided, rename the current section or period and return the course roster. :param str name: (optional) the section or period name :return: the Rename Section modal if name is not provided or the course roster if name is provided :rtype: :py:class:`RenameSection` or :py:class:`CourseRoster` """ link = self.find_element(*self._rename_section_link_locator) Utility.click_option(self.driver, element=link) sleep(0.5) dialog_root = self.driver.execute_script(GET_ROOT.format('dialog')) modal = RenameSection(self.page, dialog_root) if not name: return modal Utility.clear_field(self.driver, field=modal.section_name) modal.section_name = name return modal.rename()
def add(self) -> Page: """Add a new log in method to the account. :return: the password setup page, the Facebook log in page or the Google log in page :rtype: :py:class:`~pages.accounts.reset.SetPassword`, :py:class:`~pages.facebook.home.Facebook`, or :py:class:`~pages.google.home.Google` """ provider = self.name.lower() base_url = None if 'password' in provider: from pages.accounts.reset import SetPassword Destination = SetPassword base_url = Utility.parent_page(self).base_url elif 'facebook' in provider: from pages.facebook.home import Facebook Destination = Facebook else: from pages.google.home import Google Destination = Google button = self.find_element(*self._add_button_locator) Utility.click_option(self.driver, element=button) return go_to_(Destination(self.driver, base_url=base_url))
def search_for(self, topic: str) \ -> List[Profile.Console.PopupConsole.Users.Result]: r"""Use the pop up console to search for a given string. :param str topic: the search string :return: the list of users matching the search string :rtype: list(:py:class:`~pages.accounts.profile. \ Profile.Console.PopupConsole. \ Users.Result`) """ results_list = self.find_element( *self._results_list_root_locator) self.find_element(*self._search_bar_locator) \ .send_keys(topic) search_button = self.find_element( *self._search_button_locator) Utility.click_option(self.driver, element=search_button) try: self.wait.until( lambda _: Utility.has_children(results_list)) except TimeoutException: raise AccountsException('search not completed') return [ self.Result(self, line) for line in self.find_elements( *self._search_result_row_locator) ]
def learn_more(self): """Click the 'Learn More' button.""" Utility.click_option( self.driver, element=self.find_element(*self._how_it_works_locator)) sleep(0.75) return self.page
def _selection_helper(self, locator, destination): """Menu option helper for duplicated actions.""" if not self.is_open: self.open() target = self.find_element(*locator) Utility.click_option(self.driver, element=target) return go_to_(destination(self.driver))
def view_errata(self): """View the errata item in detail.""" Utility.click_option(self.driver, element=self.errata) return go_to_( ErrataDetail(self.driver, base_url=self.base_url, _id=self.errata_id))
def toggle(self): """Open or close a nav menu.""" Utility.scroll_top(self.driver) menu = self.find_element(*self._toggle_locator) Utility.click_option(self.driver, element=menu) sleep(0.15) return self
def i_agree(self) -> Union[BuyAccess, StudentCourse]: """Click on the 'I agree' button. After clicking on the I agree button, one of two destinations are possible: 1. the student course page with the product purchase modal open for paid courses #. the student course page without a modal open for existing students in a free course :return: the course page with the product purchase modal displayed :rtype: :py:class:`~pages.tutor.enrollment.BuyAccess` or :py:class:`~pages.tutor.course.StudentCourse` """ button = self.find_element(*self._i_agree_button_locator) Utility.click_option(self.driver, element=button) sleep(1.25) course = StudentCourse(self.driver, base_url=self.page.base_url) dialog_root = self.driver.execute_script(GET_ROOT.format('document')) if (dialog_root and 'pay-now-or-later' in dialog_root.get_attribute('class')): return BuyAccess(course, dialog_root) return go_to_(course)
def purchase(self) -> Union[PurchaseConfirmation]: """Click on the 'Purchase' Tutor button. :return: the purchase confirmation modal or the error message for a failed transaction :rtype: :py:class:`~pages.tutor.enrollment.PurchaseConfirmation` :raises: :py:class:`~utils.tutor.TutorException` if error messages are found """ purchase = self.find_element(*self._base_iframe_locator) self.driver.switch_to.frame(purchase) button = self.wait.until( expect.presence_of_element_located(self._purchase_button_locator)) sleep(0.25) is_not_chrome = not Utility.is_browser(self.driver, 'chrome') Utility.click_option(self.driver, element=button, force_js_click=is_not_chrome) sleep(1) self.driver.switch_to.default_content() errors = self.error_messages if errors: raise TutorException(errors) return PurchaseConfirmation(self.page)
def view_tutor_data(self): """View user information on Tutor.""" if 'Tutor' in self.app: user = self.find_element(*self._view_app_user_info_locator) Utility.click_option(self.driver, element=user) from pages.tutor.admin.users import Details as TutorDetails return go_to_(TutorDetails(self.driver))
def go_to_student_learning(self): """Follow the improving student learning link.""" learning = self.find_element(*self._tutor_marketing_link_locator) Utility.click_option(self.driver, element=learning) from pages.web.tutor import TutorMarketing return go_to_( TutorMarketing(self.driver, base_url=self.page.base_url))
def copy_this_course(self): """Clone the selected course. :return: the course cloning wizard :rtype: :py:class:`~pages.tutor.new_course.CloneCourse` """ assert(self.is_teacher), \ "Only verified instructors may clone a course" assert(not self.is_preview), \ "Preview courses may not be cloned" if Utility.is_browser(self.driver, 'safari'): button = self.find_element(By.CSS_SELECTOR, '.btn-sm') Utility.click_option(self.driver, element=button) else: from selenium.webdriver.common.action_chains import \ ActionChains Utility.scroll_to(self.driver, element=self.root, shift=-80) ActionChains(self.driver) \ .move_to_element(self.course_brand) \ .pause(1) \ .move_to_element( self.find_element(By.CSS_SELECTOR, '.btn-sm')) \ .pause(1) \ .click() \ .perform() from pages.tutor.new_course import CloneCourse return go_to_(CloneCourse(self.driver, self.page.page.base_url))
def click(self): """Go to the banner link destination. Return a 'Destination' so the function will fail if a new banner link is added. """ destination = self.destination parent = self.page while not isinstance(parent, Page): parent = parent.page base_url = parent.base_url if destination.endswith(Web.SUBJECTS): from pages.web.subjects import Subjects as Destination elif destination.endswith(Web.ABOUT): from pages.web.about import AboutUs as Destination elif destination.endswith(Web.SE_APP): from pages.web.se_app import StudyEdge as Destination elif destination.endswith(Web.GLOBAL_REACH): from pages.web.reach import GlobalReach as Destination elif destination.endswith(Web.ONLINE_RESOURCES): from pages.web.online_resources \ import OnlineResources as Destination else: raise WebException( 'Unknown destination: {0}' .format(self.destination.split('/')[-1])) Utility.click_option(self.driver, element=self.root) return go_to_(Destination(self.driver, base_url=base_url))
def student_id(self, _id=None): """Set the student's identification number. :param str _id: (optional) the student's new ID number; if _id is '' or None, the field will be cleared :return: the course roster :rtype: :py:class:`CourseRoster` """ from selenium.webdriver.common.keys import Keys edit_button = self.find_element(*self._edit_student_id_locator) Utility.click_option(self.driver, element=edit_button) sleep(0.25) id_field = self.find_element( *self._student_id_input_box_locator) Utility.clear_field(self.driver, field=id_field) if _id: edit_button = self.find_element( *self._edit_student_id_locator) Utility.click_option(self.driver, element=edit_button) sleep(0.25) id_field = self.find_element( *self._student_id_input_box_locator) id_field.send_keys(_id) id_field.send_keys(Keys.TAB) return self.page.page
def _add_assignment(self, assignment_type): """Click on an add assignment button. An internal helper function to select a new assignment. :param str assignment_type: the type of assignment to add :return: an assignment creation page :rtype: :py:class:`~pages.tutor.assignment.AddExternal` or :py:class:`~pages.tutor.assignment.AddEvent` or :py:class:`~pages.tutor.assignment.AddHomework` or :py:class:`~pages.tutor.assignment.AddReading` :raises :py:class:`~utils.tutor.TutorException`: if the assignment_type does not match a known assignment type :noindex: """ if assignment_type == Tutor.EXTERNAL: locator = self._add_external_locator from pages.tutor.assignment import External as Assignment elif assignment_type == Tutor.EVENT: locator = self._add_event_locator from pages.tutor.assignment import Event as Assignment elif assignment_type == Tutor.HOMEWORK: locator = self._add_homework_locator from pages.tutor.assignment import Homework as Assignment elif assignment_type == Tutor.READING: locator = self._add_reading_locator from pages.tutor.assignment import Reading as Assignment else: raise TutorException('"{0}" is not a known assignment type.' .format(assignment_type)) button = self.find_element(*locator) Utility.click_option(self.driver, element=button) return go_to_(Assignment(self.driver, self.page.base_url))
def select_assignment(self, name: str, _type: str = None): """Click on an assignment. :param str name: the assignment's name :param str _type: (optional) the assignment's type :return: the assignment task page(s) :rtype: :py:class:`~pages.tutor.task.Assignment` """ for assignment in self.find_elements(*self._assignment_link_locator): if name in assignment.get_attribute('textContent'): description = assignment.get_attribute('class') if _type and _type not in description: continue if Tutor.EVENT in description: from pages.tutor.task import Event as Destination elif Tutor.EXTERNAL in description: from pages.tutor.task import EXTERNAL as Destination elif Tutor.HOMEWORK in description: from pages.tutor.task import Homework as Destination elif Tutor.READING in description: from pages.tutor.task import Reading as Destination else: raise TutorException( f'Unknown assignment type in "{description}"') Utility.click_option(self.driver, element=assignment) sleep(1) return go_to_(Destination(self.driver, self.base_url)) raise TutorException(f'"{name}" not found in the currently ' + 'available assignments')
def _remove_user(self, student=True, return_tooltip=False): """Remove the user from the course. :param bool student: (optional) use the Drop Student tooltip if ``True`` and the Remove Instructor tooltip if ``False`` :param bool return_tooltip: (optional) don't drop the user; click the 'Remove' or 'Drop' button to open the tooltip and return the pop up :return: None or the open tooltip :rtype: NoneType or :py:class:`Tooltip` """ locator = self._remove_student_user_locator if student \ else self._remove_instructor_user_locator button = self.find_element(*locator) Utility.click_option(self.driver, element=button) tooltip_root = self.driver.execute_script(GET_ROOT.format('tooltip')) if student: popup = DropStudent(self, tooltip_root) if return_tooltip: return popup popup.drop() else: popup = RemoveInstructor(self, tooltip_root) if return_tooltip: return popup popup.remove()
def select(self) -> None: """Click on the course option. :return: None """ Utility.click_option(self.driver, element=self.root)
def _continue(self, previous: Page = None, base_url: str = None, **kwargs) \ -> Page: """Click the Continue button. :param Page previous: (optional) the Page object for the initial page that sent the log in request :param str base_url: (optional) the base URL for the previous Page :param kwargs: (optional) additional keyword arguments for the Page :return: the log in page if there is an error, the profile page if remaining on Accounts, the terms of use or privacy policy if a new policy is available, or the previous page if logging on to another OpenStax resource (like Tutor or OpenStax.org) :rtype: :py:class:`~pypom.Page` """ current_page = self.page.location button = self.find_element(*self._continue_button_locator) Utility.click_option(self.driver, element=button) sleep(0.75) if self.driver.current_url == current_page: raise AccountsException( self.driver.execute_script( 'return document.querySelector(".invalid-message")' '.textContent;')) if previous: return go_to_(previous(self.driver, base_url, **kwargs)) source = self.driver.page_source policies = 'Terms of Use' in source or 'Privacy policy' in source if 'accounts' in self.page.location and policies: from pages.accounts.legal import AcceptTerms return go_to_(AcceptTerms(self.driver, self.page.base_url)) return go_to_(Profile(self.driver, self.page.base_url))
def view_course_settings(self): """Click on the 'Course settings' link if there are no students.""" if self.no_students: link = self.find_element(*self._course_settings_locator) Utility.click_option(self.driver, element=link) from pages.tutor.settings import CourseSettings return go_to_(CourseSettings(self.driver, self.page.base_url))
def submit(self): """Click the Submit form button.""" button = self.find_element(*self._submit_button_locator) Utility.scroll_to(self.driver, element=button, shift=-80) Utility.click_option(self.driver, element=button) sleep(1.0) return self
def back(self): """Click the Back button.""" button = self.find_element(*self._back_button_locator) Utility.scroll_to(self.driver, element=button, shift=-80) Utility.click_option(self.driver, element=button) sleep(1.0) return self
def go_back(self, destination=WebBase, url=None): """Click the student GO BACK button.""" back_button = self.find_element(*self._go_back_button_locator) Utility.click_option(self.driver, element=back_button) if url: return go_to_(destination(self.driver, url=url)) return go_to_(destination(self.driver))
def send(self): """Submit the contact message.""" button = self.find_element(*self._send_locator) Utility.click_option(self.driver, element=button) issues = self.errors if issues: return issues return go_to_(ContactConfirmation(self.driver, self.page.base_url))
def select(self) -> None: """Select the answer response. :return: None """ Utility.click_option(self.driver, element=self.root) self.wait.until(lambda _: (not self.page.is_displayed()))
def back(self) -> None: """Go to the previous step in the wizard. :return: None """ button = self.find_element(*self._back_button_locator) Utility.click_option(self.driver, element=button)
def select(self) -> None: """Click on the book option. :return: None """ book = self.find_element(*self._book_content_locator) Utility.click_option(self.driver, element=book)
def cancel(self) -> None: """Cancel the course creation wizard. :return: None """ button = self.find_element(*self._cancel_button_locator) Utility.click_option(self.driver, element=button)
def select(self) -> None: """Click on the term option. :return: None """ button = self.find_element(*self._option_locator) Utility.click_option(self.driver, element=button)
def view_newer_releases(self): """View newer OpenStax press releases.""" if not self.viewing_fewer_releases: switch = self.find_element(*self._newer_releases_locator) if 'presentation' not in switch.get_attribute('role'): # there is another pagination group of older or newer links Utility.click_option(self.driver, element=switch) return self