"""Break the signup process out of the base."""
from __future__ import annotations
from time import sleep
from typing import List, Union
from urllib.parse import urlparse
from pypom import Page, Region
from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from pages.accounts.base import AccountsBase
from pages.accounts.profile import Profile
from regions.accounts.fields import Email, FirstName, LastName, Password, Pin
from regions.accounts.social import SocialLogins
from utils.accounts import AccountsException
from utils.email import GmailReader, GuerrillaMail, RestMail
from utils.utilities import Utility, go_to_
ERROR_SELECTOR = ' ~ .errors .invalid-message'
[docs]class ChangeYourEmail(AccountsBase):
"""The email change request page."""
URL_TEMPLATE = '/i/change_your_email'
[docs] class Content(AccountsBase.Content, Email):
"""The email change request pane."""
_email_locator = (
By.CSS_SELECTOR, '#change_signup_email_email')
_information_message_locator = (
By.CSS_SELECTOR, '.info-message')
_send_my_pin_button_locator = (
By.CSS_SELECTOR, '[type=submit]')
_email_error_message_locator = (
By.CSS_SELECTOR, _email_locator[1] + ERROR_SELECTOR)
[docs] def send_my_pin(self) -> Union[ChangeYourEmail, ConfirmEmail]:
"""Click the 'Send my PIN' button.
:return: the change your email page if there was an error or the
email confirmation page
:rtype: :py:class:`~pages.accounts.signup.ChangeYourEmail` or
:py:class:`~pages.accounts.signup.ConfirmEmail`
"""
button = self.find_element(*self._send_my_pin_button_locator)
Utility.click_option(self.driver, element=button)
sleep(0.5)
if self.email_has_error:
return self
return go_to_(
ConfirmEmail(self.driver, base_url=self.page.base_url))
[docs]class CompleteSignup(AccountsBase):
"""The account setup completion page."""
URL_TEMPLATE = '/i/done'
[docs] class Content(AccountsBase.Content):
"""The signup completion pane."""
_email_locator = (
By.CSS_SELECTOR, '.info-message b')
_exit_button_locator = (
By.CSS_SELECTOR, '#exit-icon a')
_finish_button_locator = (
By.CSS_SELECTOR, '[type=submit]')
_information_message_locator = (
By.CSS_SELECTOR, '.info-message')
@property
def information(self) -> str:
"""Return the email information message.
:return: the email information message
:rtype: str
"""
return (self.find_element(*self._information_message_locator)
.get_attribute('textContent'))
@property
def email(self) -> str:
"""Return the email address used during registration.
:return: the email address used during registration
:rtype: str
"""
return self.find_element(*self._email_locator).text
[docs] def exit(self) -> Page:
"""Click the page return 'x' button.
:return: the user profile or the origination page
:rtype: :py:class:`~pypom.Page`
"""
button = self.find_element(*self._exit_button_locator)
Utility.click_option(self.driver, element=button)
sleep(0.5)
if 'profile' in self.current_url:
return go_to_(
Profile(self.driver, base_url=self.page.base_url))
return Page(self.driver)
[docs] def finish(self) -> Page:
"""Click the 'Finish' button.
:return: the account profile or the originating page
:rtype: :py:class:`~pypom.Page`
"""
button = self.find_element(*self._finish_button_locator)
Utility.click_option(self.driver, element=button)
sleep(0.5)
if 'profile' in self.driver.current_url:
return go_to_(
Profile(self.driver, base_url=self.page.base_url))
return Page(self.driver)
[docs]class ConfirmEmail(AccountsBase):
"""The email confirmation page."""
URL_TEMPLATE = '/i/confirmation_form'
[docs] class Content(AccountsBase.Content, Pin):
"""The email confirmation pane."""
_information_message_locator = (
By.CSS_SELECTOR, '.info-message')
_email_locator = (
By.CSS_SELECTOR, '.info-message b')
_edit_your_email_link_locator = (
By.CSS_SELECTOR, '.info-message a')
_pin_locator = (
By.CSS_SELECTOR, '#confirm_pin')
_use_a_different_email_link_locator = (
By.CSS_SELECTOR, '.control-group a')
_confirm_my_account_button_locator = (
By.CSS_SELECTOR, '[type=submit]')
_pin_error_message_locator = (
By.CSS_SELECTOR, _pin_locator[1] + ERROR_SELECTOR)
@property
def email(self) -> str:
"""Return the email address used during registration.
:return: the email address used during registration
:rtype: str
"""
return self.find_element(*self._email_locator).text
@property
def information(self) -> str:
"""Return the email information message.
:return: the email information message
:rtype: str
"""
return (self.find_element(*self._information_message_locator)
.get_attribute('textContent'))
@property
def pin(self) -> str:
"""Return the current PIN value.
:return: the current pin value
:rtype: str
"""
return (self.find_element(*self._pin_locator)
.get_attribute('value'))
@pin.setter
def pin(self, pin: str):
"""Enter the email verification number.
:param str pin: the email verification number
:return: None
"""
field = self.find_element(*self._pin_locator)
Utility.clear_field(self.driver, field=field)
field.send_keys(pin)
[docs] def confirm_my_account(self) -> Union[ConfirmEmail, CompleteSignup]:
"""Click the Continue button.
:return: the email confirmation (second step) if there are errors
or the completion page (third step)
:rtype: :py:class:`~pages.accounts.signup.ConfirmEmail` or
:py:class:`~pages.accounts.signup.CompleteSignup`
"""
current_page = self.page.location
button = self.find_element(
*self._confirm_my_account_button_locator)
Utility.click_option(self.driver, element=button)
sleep(0.5)
if self.driver.current_url == current_page:
raise AccountsException(
self.driver.execute_script(
'return document.querySelector(".invalid-message")'
'.textContent;'))
return go_to_(
CompleteSignup(self.driver, base_url=self.page.base_url))
[docs] def edit_your_email(self) -> ChangeYourEmail:
"""Click the 'edit your email' link.
:return: the change your email page
:rtype: :py:class:`~pages.accounts.signup.ChangeYourEmail`
"""
link = self.find_element(*self._edit_your_email_link_locator)
Utility.click_option(self.driver, element=link)
return go_to_(
ChangeYourEmail(self.driver, base_url=self.page.base_url))
[docs] def sign_up_with_a_different_email(self) -> ChangeYourEmail:
"""Click the 'sign up with a different email' link.
:return: the change your email page
:rtype: :py:class:`~pages.accounts.signup.ChangeYourEmail`
"""
link = self.find_element(*self._use_a_different_email_link_locator)
Utility.click_option(self.driver, element=link)
return go_to_(
ChangeYourEmail(self.driver, base_url=self.page.base_url))
[docs]class Signup(AccountsBase):
"""The Accounts sign up process."""
URL_TEMPLATE = '/i/signup'
[docs] class Content(AccountsBase.Content):
"""The sign up pane."""
DESCRIPTION = ' ~ div'
_educator_signup_button_locator = (
By.CSS_SELECTOR, '[href^="/signup"]')
_student_signup_button_locator = (
By.CSS_SELECTOR, '[href*=student]')
_educator_descrition_locator = (
By.CSS_SELECTOR, _educator_signup_button_locator[1] + DESCRIPTION)
_student_description_locator = (
By.CSS_SELECTOR, _student_signup_button_locator[1] + DESCRIPTION)
@property
def educator_description(self) -> str:
"""Return the educator sign up explanation text.
:return: the explanation why educators should register for an
OpenStax account
:rtype: str
"""
return (self.find_element(*self._educator_descrition_locator)
.get_attribute('textContent'))
@property
def student_description(self) -> str:
"""Return the student sign up explanation text.
:return: the explanation why students should register for an
OpenStax account
:rtype: str
"""
return (self.find_element(*self._student_description_locator)
.get_attribute('textContent'))
[docs] def sign_up_as_an_educator(self) -> EducatorSignup:
"""Click the educator sign up button.
:return: the educator registration process
:rtype: :py:class:`~pages.accounts.signup.EducatorSignup`
"""
button = self.find_element(*self._educator_signup_button_locator)
Utility.click_option(self.driver, element=button)
return go_to_(
EducatorSignup(self.driver, base_url=self.page.base_url))
[docs] def sign_up_as_a_student(self) -> StudentSignup:
"""Click the student sign up button.
:return: the student registration process
:rtype: :py:class:`~pages.accounts.signup.StudentSignup`
"""
button = self.find_element(*self._student_signup_button_locator)
Utility.click_option(self.driver, element=button)
return go_to_(
StudentSignup(self.driver, base_url=self.page.base_url))
[docs]class StudentSignup(AccountsBase):
"""The student sign up process."""
URL_TEMPLATE = '/i/signup/student'
[docs] class Content(AccountsBase.Content,
Email, FirstName, LastName, Password, SocialLogins):
"""The sign up pane."""
_continue_button_locator = (
By.CSS_SELECTOR, '[type=submit]')
_email_locator = (
By.CSS_SELECTOR, '#signup_email')
_first_name_locator = (
By.CSS_SELECTOR, '#signup_first_name')
_last_name_locator = (
By.CSS_SELECTOR, '#signup_last_name')
_newsletter_signup_locator = (
By.CSS_SELECTOR, '#signup_newsletter')
_password_locator = (
By.CSS_SELECTOR, '#signup_password')
_policy_agreement_locator = (
By.CSS_SELECTOR, '#signup_terms_accepted')
_privacy_policy_link_locator = (
By.CSS_SELECTOR, '.terms [href*=terms]:last-child')
_show_password_toggle_locator = (
By.CSS_SELECTOR, '#show-hide-button')
_terms_of_use_link_locator = (
By.CSS_SELECTOR, '.terms [href*=terms]:first-child')
_email_error_message_locator = (
By.CSS_SELECTOR, _email_locator[1] + ERROR_SELECTOR)
_first_name_error_message_locator = (
By.CSS_SELECTOR, _first_name_locator[1] + ERROR_SELECTOR)
_last_name_error_message_locator = (
By.CSS_SELECTOR, _last_name_locator[1] + ERROR_SELECTOR)
_password_error_message_locator = (
By.CSS_SELECTOR, _password_locator[1] + ERROR_SELECTOR)
[docs] def get_errors(self) -> List[str]:
"""Return a list of error messages found on the page.
:return: the list of error messages found on the current page
:rtype: list(str)
"""
errors = []
if self.first_name_has_error:
errors.append(f'First Name: {self.first_name_error}')
if self.last_name_has_error:
errors.append(f'Last Name: {self.last_name_error}')
if self.email_has_error:
errors.append(f'Email: {self.email_error}')
if self.password_has_error:
errors.append(f'Password: {self.password_error}')
return errors
[docs] def i_agree(self) -> StudentSignup:
"""Click the I agree checkbox.
:return: the Accounts student sign up page
:rtype: :py:class:`~pages.accounts.signup.StudentSignup`
"""
checkbox = self.find_element(*self._policy_agreement_locator)
Utility.click_option(self.driver, element=checkbox)
return self.page
[docs] def toggle_newsletter(self) -> StudentSignup:
"""Click the OpenStax newsletter checkbox.
:return: the Accounts student sign up page
:rtype: :py:class:`~pages.accounts.signup.StudentSignup`
"""
checkbox = self.find_element(*self._newsletter_signup_locator)
Utility.click_option(self.driver, element=checkbox)
return self.page
[docs] def toggle_password_display(self) -> StudentSignup:
"""Toggle the password field to show or hide the value.
:return: the Accounts student sign up page
:rtype: :py:class:`~pages.accounts.signup.StudentSignup`
"""
toggle = self.find_element(*self._show_password_toggle_locator)
Utility.click_option(self.driver, element=toggle)
sleep(0.1)
return self.page
def _continue(self) -> Union[StudentSignup, ConfirmEmail]:
"""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 student sign up (first step) if there are errors or
the email confirmation (second step)
:rtype: :py:class:`~pages.accounts.signup.StudentSignup` or
:py:class:`~pages.accounts.signup.ConfirmEmail`
"""
current_page = self.page.location
button = self.find_element(*self._continue_button_locator)
Utility.click_option(self.driver, element=button)
sleep(0.5)
if self.driver.current_url == current_page:
raise AccountsException(
self.driver.execute_script(
'return document.querySelector(".invalid-message")'
'.textContent;'))
return go_to_(
ConfirmEmail(self.driver, base_url=self.page.base_url))
""" *********************************************************************** """
[docs]class SignupOld(AccountsBase):
"""Legacy signup process."""
URL_TEMPLATE = '/signup'
STUDENT = 'Student'
INSTRUCTOR = 'Instructor'
ADMINISTRATOR = 'Administrator'
LIBRARIAN = 'Librarian'
DESIGNER = 'Instructional Designer'
OTHER = 'Other'
GOOGLE = 'google'
GUERRILLA_MAIL = 'guerrilla'
RESTMAIL = 'restmail'
TITLE = 0
FIRST = 1
LAST = 2
SUFFIX = 3
ADOPTED = 'Fully adopted and using it as the primary textbook'
RECOMMENDED = 'Recommending the book – my students buy a different book'
INTEREST = 'Interested in using OpenStax in the future'
NOT_USING = 'Not using OpenStax'
SUBJECTS = [
('accounting', 'Accounting'),
('algebra_and_trigonometry', 'Algebra and Trigonometry'),
('american_government', 'American Government'),
('anatomy_physiology', 'Anatomy and Physiology'),
('astronomy', 'Astronomy'),
('biology', 'Biology'),
('calculus', 'Calculus'),
('chemistry', 'Chemistry'),
('chem_atoms_first', 'Chemistry: Atoms First'),
('college_algebra', 'College Algebra'),
('college_physics_algebra', 'College Physics'),
('concepts_of_bio_non_majors', 'Concepts of Biology'),
('introduction_to_business', 'Introduction to Business'),
('introduction_to_sociology', 'Introduction to Sociology 2e'),
('introductory_statistics', 'Introductory Statistics'),
('microbiology', 'Microbiology'),
('pre_algebra', 'Prealgebra'),
('precalc', 'Precalculus'),
('economics', 'Principles of Economics'),
('macro_econ', 'Principles of Macroeconomics'),
('ap_macro_econ', 'Principles of Macroeconomics for AP® Courses'),
('micro_econ', 'Principles of Microeconomics'),
('ap_micro_econ', 'Principles of Microeconomics for AP® Courses'),
('psychology', 'Psychology'),
('ap_physics', 'The AP Physics Collection'),
('us_history', 'U.S. History'),
('university_physics_calc', 'University Physics'),
('not_listed', 'Not Listed')
]
_next_button_locator = (By.CSS_SELECTOR, '[type=submit]')
_next_page_button_locator = (
By.CSS_SELECTOR, '[data-bind="click:nextPage"]')
_form_submit_locator = (By.CSS_SELECTOR, '[value="Create Account"]')
_error_locator = (By.CSS_SELECTOR, '.alert')
[docs] def subject_list(self, size=1):
"""Return a list of subjects for an elevated signup."""
subjects = len(self.SUBJECTS)
if size > subjects:
size = subjects
book = ''
group = []
while len(group) < size:
book = (self.SUBJECTS[Utility.random(0, subjects - 1)])[1]
if book not in group:
group.append(book)
return group
[docs] def account_signup(self, email,
password=None, _type='Student', provider='restmail',
tutor=False, destination=None,
**kwargs):
r"""Single signup entry point.
Sign up a new user. Social, Random, and Name are mutually exclusive.
:param str email: an accessible e-mail address
:param str password: the user password
:param str _type: (optional) the new user account type using
* :py:data:`SignupOld.STUDENT` (default)
* :py:data:`SignupOld.INSTRUCTOR`
* :py:data:`SignupOld.ADMINISTRATOR`
* :py:data:`SignupOld.LIBRARIAN`
* :py:data:`SignupOld.DESIGNER`
* :py:data:`SignupOld.OTHER`
:param str provider: (optional) the e-mail host, default: ``restmail``
``google``: Google Gmail
``guerrilla``: GuerrillaMail
``restmail``: RestMail API Email
:param bool tutor: (optional) ``True`` if the signup is for OpenStax
Tutor, default: ``False``
:param str destination: a URL destination if not Accounts
default: ``None``
:param \**kwarys: arbitrary keyword arguments, see below
:return: the new user's profile page
:rtype: :py:class:`pages.accounts.profile.Profile`
:Keyword Arguments:
* *email_password* (``str``) --
Webmail login password
* *name* (``list``(``str``)) --
the user's name as [title, first_name, last_name, suffix]
* *news* (``bool``) --
``True`` if the newsletter checkbox should be checked, ``False``
if the checkbox should be cleared
* *phone* (``str``) --
the instructor's telephone number
* *school* (``str``) --
the school name
* *social* (``str``) --
use a social login, either ``facebook`` or ``google``
* *social_login* (``str``) --
the social account login
* *social_password* (``str``) --
the social account password
* *students* (``int``) --
the number of students in the course
* *subjects* (``list``(``str``)) --
a list of interested book subjects
* ``accounting``
* ``algebra_and_trigonometry``
* ``american_government``
* ``anatomy_physiology``
* ``astronomy``
* ``biology``
* ``calculus``
* ``chemistry``
* ``chem_atoms_first``
* ``college_algebra``
* ``college_physics_algebra``
* ``concepts_of_bio_non_majors``
* ``introduction_to_business``
* ``introduction_to_sociology``
* ``introductory_statistics``
* ``microbiology``
* ``pre_algebra``
* ``precalc``
* ``economics``
* ``macro_econ``
* ``ap_macro_econ``
* ``micro_econ``
* ``ap_micro_econ``
* ``psychology``
* ``ap_physics``
* ``us_history``
* ``university_physics_calc``
* ``not_listed``
* *use* (``str``) --
How is the instructor using OpenStax?
* :py:data:`SignupOld.ADOPTED` --
'Fully adopted and using it as the primary textbook'
* :py:data:`SignupOld.RECOMMENDED` --
'Recommending the book - my students buy a different book'
* :py:data:`SignupOld.INTEREST` --
'Interested in using OpenStax in the future'
* :py:data:`SignupOld.NOT_USING` --
'Not using OpenStax'
* *webpage* (``str``) --
the web URL showing the user as a known instructor
"""
# prep the signup help
if 'kwargs' in kwargs:
kwargs = kwargs.get('kwargs')
non_student_role = _type != SignupOld.STUDENT
instructor = _type == SignupOld.INSTRUCTOR
# select user type and email
if not tutor:
self.user_type.role = _type
self.user_type.email = email
self.next()
sleep(0.5)
assert(not self.error), '{0}'.format(self.error)
if non_student_role and not email.endswith('edu'):
self.next()
# verify the pin
not_verified = True
while not_verified:
email_password = None
if 'google' in provider:
# mailer = GoogleBase(self.driver)
pin = (GmailReader(email[0:7])
.read_mail()
.sort_mail()
.latest
.get_pin)
email_password = kwargs.get('email_password')
elif 'guerrilla' in provider:
mailer = GuerrillaMail(self.driver)
elif 'restmail' in provider:
account_name = email[:email.rfind("@")]
mailer = RestMail(account_name)
else:
mailer = _type(self.driver)
email_password = kwargs.get('email_password')
if 'google' not in provider:
pin = self._get_pin(page=mailer,
provider=provider,
return_url=(self.seed_url +
'/verify_email'),
email=email,
password=email_password)
if not pin:
raise ValueError('PIN not found')
self.pin.clear_pin()
self.pin.verify_pin = pin
self.next()
sleep(0.25)
if not self.pin.pin_failure:
not_verified = False
sleep(1.0)
if 'social' not in kwargs:
# set the initial password
self.password.password = password
self.password.confirmation = password
sleep(0.5)
self.next()
assert(not self.error), '{0}'.format(self.error)
self.wait.until(lambda _: 'profile' in self.location)
elif kwargs.get('social') == 'facebook':
# use Facebook
self.password.use_social_login() \
.use_facebook.log_in(kwargs.get('social_login'),
kwargs.get('social_password'))
sleep(3.0)
else:
# use Google
self.password.use_social_login() \
.use_google.log_in(kwargs.get('social_login'),
kwargs.get('social_password'))
sleep(3.0)
# make sure we're actually back on Accounts
self.wait.until(
lambda _: 'accounts' in urlparse(self.selenium.current_url).netloc)
sleep(1.0)
# enter user details in group order
# all users
if 'social' not in kwargs:
self.user.first_name = kwargs.get('name')[SignupOld.FIRST]
self.user.last_name = kwargs.get('name')[SignupOld.LAST]
self.user.suffix = kwargs.get('name')[SignupOld.SUFFIX]
if non_student_role:
self.instructor.phone = kwargs.get('phone')
self.user.school = kwargs.get('school')
if instructor:
self.instructor.students = kwargs.get('students')
if non_student_role:
self.instructor.webpage = kwargs.get('webpage')
# instructor-only
if instructor:
# from utils.accounts import Accounts
self.instructor.using = kwargs.get('use')
'''self.instructor.using = [
Accounts.NOT_USING,
Accounts.ADOPTED
][Utility.random(0, 1)]'''
sleep(0.25)
# self.next((By.CSS_SELECTOR, '[data-bind~="click:nextPage"]'))
'''if not non_student_role:
self.next()'''
# completion
# sleep(1)
subjects_to_select = []
if non_student_role:
for _, name in SignupOld.SUBJECTS:
if name in kwargs.get('subjects', []):
subjects_to_select.append(name)
if subjects_to_select:
self.instructor.select_subjects(subjects_to_select)
'''for subject in subjects_to_select:
book = self.find_element(
By.XPATH,
'//label[text()="{subject}"]/following-sibling::div'
.format(subject=subject))
Utility.click_option(self.driver, element=book)'''
'''if instructor:
if kwargs.get('use') == Accounts.NOT_USING:
self.instructor.students = kwargs.get('students')
else:
for group in self.find_elements(
By.CSS_SELECTOR, '.form-group input[type=number]'):
Utility.scroll_to(self.driver, element=group, shift=-80)
group.send_keys(kwargs.get('students'))
radios = self.find_elements(
By.CSS_SELECTOR,
'.form-group div input[data-bind*="how_using"]')
group = zip(radios[0::2], radios[1::2])
for adopted, recommend in group:
if kwargs.get('use') == Accounts.ADOPTED:
option = adopted
else:
option = recommend
Utility.click_option(self.driver, element=option)
'''
if not kwargs.get('news'):
self.user.toggle_news()
if not tutor:
self.user.agree_to_terms()
sleep(0.25)
self.next()
if non_student_role:
assert(not self.error), '{0}'.format(self.error)
# request e-mail confirmation for an elevated account
if non_student_role:
self.notice.get_confirmation_email()
self.next()
if tutor:
from pages.tutor.enrollment import Terms
return go_to_(
Terms(self.driver,
base_url=destination if destination else None))
# if an alternative destination Page is provided (like from Web), wait
# for it to be loaded
if isinstance(destination, Page):
return go_to_(destination)
from pages.accounts.profile import Profile
return go_to_(Profile(self.driver, base_url=self.base_url))
[docs] def instructor_access(self, role, school_email, phone_number, school,
webpage, students=None, using=None, interests=None,
get_newsletter=True):
"""Request faculty access."""
_apply_role_locator = (By.CSS_SELECTOR, '#apply_role')
_apply_email_locator = (By.CSS_SELECTOR, '#apply_email')
_apply_phone_locator = (By.CSS_SELECTOR, '#apply_phone_number')
_apply_school_locator = (By.CSS_SELECTOR, '#apply_school')
_apply_student_locator = (By.CSS_SELECTOR, '#apply_num_students')
_apply_url_locator = (By.CSS_SELECTOR, '#apply_url')
_apply_using_locator = (By.CSS_SELECTOR, '#apply_using_openstax')
_apply_subject_locators = (By.CSS_SELECTOR, '.subject')
_subject_label_locator = (By.CSS_SELECTOR, 'label')
_subject_checkbox_locator = (By.CSS_SELECTOR, '[type=checkbox]')
_apply_newsletter_locator = (By.CSS_SELECTOR, '#apply_newsletter')
Utility.select(self.driver, _apply_role_locator, role)
_email = self.find_element(*_apply_email_locator)
Utility.scroll_to(self.driver, element=_email)
_email.send_keys(school_email)
_phone = self.find_element(*_apply_phone_locator)
Utility.scroll_to(self.driver, element=_phone)
_phone.send_keys(phone_number)
_school = self.find_element(*_apply_school_locator)
Utility.scroll_to(self.driver, element=_school)
_school.send_keys(school)
if role == self.INSTRUCTOR:
_students = self.find_element(*_apply_student_locator)
Utility.scroll_to(self.driver, element=_students)
_students.send_keys(students)
_webpage = self.find_element(*_apply_url_locator)
Utility.scroll_to(self.driver, element=_webpage)
_webpage.send_keys(webpage)
if role == self.INSTRUCTOR:
Utility.select(self.driver, _apply_using_locator, using)
books = self.find_elements(*_apply_subject_locators)
if not interests:
interests = self.subject_list(Utility.random(1, 5))
for book in books:
if book.find_element(*_subject_label_locator).text in interests:
_book = book.find_element(*_subject_checkbox_locator)
Utility.click_option(self.driver, element=_book)
_news = self.find_element(*_apply_newsletter_locator)
Utility.scroll_to(self.driver, element=_news)
if not get_newsletter:
Utility.click_option(self.driver, element=_news)
self.next()
sleep(1.0)
self.notice.get_confirmation_email()
self.next()
@property
def user_type(self):
"""Fill out the user type."""
return self.UserType(self)
@property
def pin(self):
"""Verify the e-mail pin."""
return self.PinVerification(self)
def _get_pin(self, page, provider, return_url, email=None, password=None):
"""Retrieve a signup pin."""
if 'restmail' in provider:
box = page.wait_for_mail()
return box[-1].pin
else:
page.open()
if 'google' in provider:
page = page.login.go(email, password)
WebDriverWait(page.driver, 60.0).until(
lambda _: page.emails[0].has_pin and page.emails[0].is_new)
sleep(5.0)
pin = page.emails[0].get_pin
page.driver.get(return_url)
sleep(1.0)
return pin
@property
def password(self):
"""Fill out the password fields."""
return self.SetPassword(self)
@property
def social(self):
"""Use a social login."""
return self.SocialLogin(self)
@property
def user(self):
"""Fill out the user data."""
return self.UserFields(self)
@property
def instructor(self):
"""Fill out the instructor verification."""
return self.InstructorVerification(self)
@property
def notice(self):
"""Request notice when instructor access is authorized."""
return self.InstructorNotice(self)
[docs] def next(self, locator=None):
"""Proceed to the next step in the process."""
if not locator:
locator = self._next_button_locator
button = self.find_element(*locator)
Utility.click_option(self.driver, element=button)
return self
@property
def error(self):
"""Return the error message if present."""
try:
return self.find_element(*self._error_locator).text.strip()
except WebDriverException:
return ''
[docs] class UserType(Region):
"""Initial signup pane for type selection."""
_signup_role_locator = (By.ID, 'signup_role')
_signup_email_locator = (By.ID, 'signup_email')
_warning_locator = (By.CLASS_NAME, 'warning')
_sign_in_locator = (By.CSS_SELECTOR, '.sign-in a')
@property
def role(self):
"""Return the role select."""
return self.find_element(*self._signup_role_locator)
@role.setter
def role(self, signup_role):
"""Select a user role."""
Utility.select(self.driver, self._signup_role_locator, signup_role)
return self
@property
def email(self):
"""Return the email input."""
return self.find_element(*self._signup_email_locator)
@email.setter
def email(self, email):
"""Send the e-mail."""
self.email.send_keys(email)
return self
@property
def warning(self):
"""Return the warning text."""
return self.find_element(*self._warning_locator).text
@property
def warning_present(self):
"""Return True if the e-mail warning is displayed."""
return self.find_element(*self._warning_locator).is_displayed()
[docs] def log_in(self):
"""Return to the login screen."""
sign_in = self.find_element(*self._sign_in_locator)
Utility.click_option(self.driver, element=sign_in)
from pages.accounts.home import AccountsHome
return go_to_(
AccountsHome(self.driver, base_url=self.page.base_url))
[docs] class PinVerification(Region):
"""Pin verification."""
_pin_locator = (By.ID, 'pin_pin')
_email_edit_locator = (By.CSS_SELECTOR, '.extra-info a')
_error_locator = (By.CLASS_NAME, 'alert-danger')
@property
def verify_pin(self):
"""Return the pin input."""
return self.find_element(*self._pin_locator)
@verify_pin.setter
def verify_pin(self, pin):
"""Send the verification code."""
self.verify_pin.send_keys(pin)
return self
[docs] def edit_email(self):
"""Return to the user type selection."""
edit_email = self.find_element(*self._email_edit_locator)
Utility.click_option(self.driver, element=edit_email)
return self.UserType(self)
@property
def pin_failure(self):
"""Return True if an error occurs during pin verification."""
try:
WebDriverWait(self.selenium, 1).until(
lambda _: self.find_element(*self._error_locator))
except TimeoutException:
return False
return True
[docs] def clear_pin(self):
"""Clear the pin field for Chrome and Firefox."""
Utility.clear_field(self.selenium, self.verify_pin)
[docs] class SetPassword(Region):
"""Set the user's password."""
_password_locator = (By.ID, 'signup_password')
_password_confirmation_locator = (
By.ID, 'signup_password_confirmation')
_error_locator = (By.CLASS_NAME, 'alert')
_multi_error_locator = (By.CSS_SELECTOR, '.alert li')
_go_to_social_locator = (By.CSS_SELECTOR, '[href$=social]')
@property
def password(self):
"""Return the password field."""
return self.find_element(*self._password_locator)
@password.setter
def password(self, password):
"""Set the password."""
self.find_element(*self._password_locator).send_keys(password)
return self
@property
def confirmation(self):
"""Return the confirmation field."""
return self.find_element(*self._password_confirmation_locator)
@confirmation.setter
def confirmation(self, password):
"""Set the password confirmation."""
self.confirmation.send_keys(password)
return self
@property
def has_error(self):
"""Return True if error messages are displayed."""
return bool(self.find_elements(*self._error_locator))
@property
def get_error(self):
"""Return password error(s)."""
if not self.has_error:
return []
try:
return [el.text for el in
self.find_elements(*self._multi_error_locator)]
except Exception:
return [self.find_element(*self._error_locator).text]
[docs] def use_social_login(self):
"""Go to the social login setup."""
use_social = self.find_element(*self._go_to_social_locator)
Utility.click_option(self.driver, element=use_social)
return SignupOld.SocialLogin(self)
[docs] class UserFields(Region):
"""Standard user fields."""
_first_name_locator = (By.ID, 'profile_first_name')
_last_name_locator = (By.ID, 'profile_last_name')
_school_locator = (By.ID, 'profile_school')
_news_locator = (By.ID, 'profile_newsletter')
_policy_agreement_locator = (By.ID, 'profile_i_agree')
@property
def first_name(self):
"""Return the first name field."""
return self.find_element(*self._first_name_locator)
@first_name.setter
def first_name(self, first):
"""Send the user's first name."""
self.first_name.send_keys(first)
return self
@property
def last_name(self):
"""Return the surname field."""
return self.find_element(*self._last_name_locator)
@last_name.setter
def last_name(self, last):
"""Send the user's surname."""
self.last_name.send_keys(last)
return self
@property
def school(self):
"""Return the school field."""
return self.find_element(*self._school_locator)
@school.setter
def school(self, school):
"""Send the user's school or affiliation."""
self.school.send_keys(school)
return self
[docs] def toggle_news(self):
"""Toggle between receiving and not receiving news."""
news = self.find_element(*self._news_locator)
Utility.click_option(self.driver, element=news)
return self
[docs] def agree_to_terms(self):
"""Accept the Accounts terms of use and the privacy policy."""
accept_terms = self.find_element(*self._policy_agreement_locator)
Utility.click_option(self.driver, element=accept_terms)
return self
[docs] class SocialLogin(Region):
"""Sign up using a social app profile."""
URL_TEMPLATE = '/social'
_facebook_button_locator = (By.ID, 'facebook-login-button')
_google_button_locator = (By.ID, 'google-login-button')
_go_to_password_setup_locator = (By.CSS_SELECTOR, '[href$=password]')
@property
def use_facebook(self):
"""Use Facebook to log in."""
fb_login = self.find_element(*self._facebook_button_locator)
Utility.click_option(self.driver, element=fb_login)
from pages.facebook.home import Facebook
return Facebook(self.driver)
@property
def use_google(self):
"""Use Google to log in."""
g_login = self.find_element(*self._google_button_locator)
Utility.click_option(self.driver, element=g_login)
from pages.google.home import Google
return Google(self.driver)
@property
def use_a_password(self):
"""Use a non-social log in."""
non_social = self.find_element(*self._go_to_password_setup_locator)
Utility.click_option(self.driver, element=non_social)
return SignupOld.SetPassword(self)
[docs] class InstructorVerification(Region):
"""Instructor verification fields."""
_phone_locator = (By.ID, 'profile_phone_number')
_student_number_locator = (By.ID, 'profile_num_students')
_webpage_verification_locator = (By.ID, 'profile_url')
_using_openstax_locator = (By.ID, 'profile_using_openstax')
_subject_option_locator = (By.CLASS_NAME, 'subject')
@property
def phone(self):
"""Return the telephone number field."""
return self.find_element(*self._phone_locator)
@phone.setter
def phone(self, phone_number):
"""Send the verification telephone number."""
self.phone.send_keys(phone_number)
return self
@property
def students(self):
"""Return the student count field."""
return self.find_element(*self._student_number_locator)
@students.setter
def students(self, students):
"""Send the semester course student count."""
self.students.send_keys(str(students))
return self
@property
def webpage(self):
"""Return the webpage verification field."""
return self.find_element(*self._webpage_verification_locator)
@webpage.setter
def webpage(self, webpage):
"""Send the URL showing instructor status."""
self.webpage.send_keys(webpage)
return self
@property
def using(self):
"""Return the instructor's intent field."""
return self.find_element(*self._using_openstax_locator)
@using.setter
def using(self, status):
"""Set the instructor's intent for using OpenStax."""
Utility.select(self.driver, self._using_openstax_locator, status)
return self
# return self.using_openstax(status)
# Use signup_two's <using> setter
[docs] def using_openstax(self, method):
"""Select the current using state."""
from utils.accounts import Accounts
_adopted_locator = (
By.CSS_SELECTOR,
'#profile_using_openstax_confirmed_adoption_won')
_not_using_locator = (
By.CSS_SELECTOR,
'#profile_using_openstax_not_using')
if method == Accounts.ADOPTED:
option = self.find_element(*_adopted_locator)
elif method == Accounts.NOT_USING:
option = self.find_element(*_not_using_locator)
Utility.click_option(self.driver, element=option)
return self.page
@property
def subjects(self):
"""Return a list of book subjects."""
return [self.Subject(self, el) for
el in self.find_elements(*self._subject_option_locator)]
[docs] def select_subjects(self, subject_list):
"""Mark each interested subject."""
for subject in self.subjects:
if subject.title in subject_list:
subject.select()
return self
[docs] class Subject(Region):
"""Book subject."""
_book_title_locator = (By.CSS_SELECTOR, 'label')
_checkbox_locator = (By.CSS_SELECTOR, '[type=checkbox]')
@property
def title(self):
"""Get the book title."""
return self.find_element(*self._book_title_locator).text
[docs] def select(self):
"""Select a book."""
box = self.find_element(*self._checkbox_locator)
Utility.click_option(self.driver, element=box)
return self
[docs] class InstructorNotice(Region):
"""Complete the instructor signup."""
_get_email_confirmation_locator = (By.CSS_SELECTOR, '[type=checkbox]')
[docs] def get_confirmation_email(self):
"""Get an e-mail confirmation when instructor access approved."""
sleep(0.5)
confirm = self.find_element(*self._get_email_confirmation_locator)
Utility.click_option(self.driver, element=confirm)
return self
[docs]class EducatorSignup(SignupOld):
"""The educator sign up process."""