Commit ffee7517 by Will Daly

Add Selenium tests for the combined login/registration form.

parent d0c06dda
......@@ -3,7 +3,7 @@ Course about page (with registration button)
"""
from .course_page import CoursePage
from .register import RegisterPage
from .login_and_register import RegisterPage
class CourseAboutPage(CoursePage):
......
"""
Registration page (create a new account)
"""
"""Login and Registration pages """
from bok_choy.page_object import PageObject
from urllib import urlencode
from bok_choy.page_object import PageObject, unguarded
from bok_choy.promise import Promise, EmptyPromise
from . import BASE_URL
from .dashboard import DashboardPage
......@@ -48,6 +48,7 @@ class RegisterPage(PageObject):
self.q(css='input#name').fill(full_name)
self.q(css='input#tos-yes').first.click()
self.q(css='input#honorcode-yes').first.click()
self.q(css="#country option[value='US']").first.click()
def submit(self):
"""
......@@ -59,3 +60,164 @@ class RegisterPage(PageObject):
dashboard = DashboardPage(self.browser)
dashboard.wait_for_page()
return dashboard
class CombinedLoginAndRegisterPage(PageObject):
"""Interact with combined login and registration page.
This page is currently hidden behind the feature flag
`ENABLE_COMBINED_LOGIN_REGISTRATION`, which is enabled
in the bok choy settings.
When enabled, the new page is available from either
`/account/login` or `/account/register`.
Users can reach this page while attempting to enroll
in a course, in which case users will be auto-enrolled
when they successfully authenticate (unless the course
has been paywalled).
"""
def __init__(self, browser, start_page="register", course_id=None):
"""Initialize the page.
Arguments:
browser (Browser): The browser instance.
Keyword Args:
start_page (str): Whether to start on the login or register page.
course_id (unicode): If provided, load the page as if the user
is trying to enroll in a course.
"""
super(CombinedLoginAndRegisterPage, self).__init__(browser)
self._course_id = course_id
if start_page not in ["register", "login"]:
raise ValueError("Start page must be either 'register' or 'login'")
self._start_page = start_page
@property
def url(self):
"""Return the URL for the combined login/registration page. """
url = "{base}/account/{login_or_register}".format(
base=BASE_URL,
login_or_register=self._start_page
)
# These are the parameters that would be included if the user
# were trying to enroll in a course.
if self._course_id is not None:
url += "?{params}".format(
params=urlencode({
"course_id": self._course_id,
"enrollment_action": "enroll"
})
)
return url
def is_browser_on_page(self):
"""Check whether the combined login/registration page has loaded. """
return (
self.q(css="#register-option").is_present() and
self.q(css="#login-option").is_present() and
self.current_form is not None
)
def toggle_form(self):
"""Toggle between the login and registration forms. """
old_form = self.current_form
# Toggle the form
self.q(css=".form-toggle:not(:checked)").click()
# Wait for the form to change before returning
EmptyPromise(
lambda: self.current_form != old_form,
"Finish toggling to the other form"
).fulfill()
def register(self, email="", password="", username="", full_name="", country="", terms_of_service=False):
"""Fills in and submits the registration form.
Requires that the "register" form is visible.
This does NOT wait for the next page to load,
so the caller should wait for the next page
(or errors if that's the expected behavior.)
Keyword Arguments:
email (unicode): The user's email address.
password (unicode): The user's password.
username (unicode): The user's username.
full_name (unicode): The user's full name.
country (unicode): Two-character country code.
terms_of_service (boolean): If True, agree to the terms of service and honor code.
"""
# Fill in the form
self.q(css="#register-email").fill(email)
self.q(css="#register-password").fill(password)
self.q(css="#register-username").fill(username)
self.q(css="#register-name").fill(full_name)
if country:
self.q(css="#register-country option[value='{country}']".format(country=country)).click()
if (terms_of_service):
self.q(css="#register-honor_code").click()
# Submit it
self.q(css=".register-button").click()
def login(self, email="", password="", remember_me=True):
"""Fills in and submits the login form.
Requires that the "login" form is visible.
This does NOT wait for the next page to load,
so the caller should wait for the next page
(or errors if that's the expected behavior).
Keyword Arguments:
email (unicode): The user's email address.
password (unicode): The user's password.
remember_me (boolean): If True, check the "remember me" box.
"""
# Fill in the form
self.q(css="#login-email").fill(email)
self.q(css="#login-password").fill(password)
if remember_me:
self.q(css="#login-remember").click()
# Submit it
self.q(css=".login-button").click()
@property
@unguarded
def current_form(self):
"""Return the form that is currently visible to the user.
Returns:
Either "register", "login", or "password-reset" if a valid
form is loaded.
If we can't find any of these forms on the page, return None.
"""
if self.q(css=".register-button").visible:
return "register"
elif self.q(css=".login-button").visible:
return "login"
elif self.q(css=".js-reset").visible:
return "password-reset"
@property
def errors(self):
"""Return a list of errors displayed to the user. """
return self.q(css=".submission-error li").text
def wait_for_errors(self):
"""Wait for errors to be visible, then return them. """
def _check_func():
errors = self.errors
return (bool(errors), errors)
return Promise(_check_func, "Errors are visible").fulfill()
......@@ -5,10 +5,12 @@ End-to-end tests for the LMS.
from textwrap import dedent
from unittest import skip
from nose.plugins.attrib import attr
from bok_choy.web_app_test import WebAppTest
from ..helpers import UniqueCourseTest, load_data_str
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.common.logout import LogoutPage
from ...pages.lms.find_courses import FindCoursesPage
from ...pages.lms.course_about import CourseAboutPage
from ...pages.lms.course_info import CourseInfoPage
......@@ -19,6 +21,7 @@ from ...pages.lms.dashboard import DashboardPage
from ...pages.lms.problem import ProblemPage
from ...pages.lms.video.video import VideoPage
from ...pages.lms.courseware import CoursewarePage
from ...pages.lms.login_and_register import CombinedLoginAndRegisterPage
from ...fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc
......@@ -64,6 +67,138 @@ class RegistrationTest(UniqueCourseTest):
self.assertIn(self.course_info['display_name'], course_names)
@attr('shard_1')
class LoginFromCombinedPageTest(UniqueCourseTest):
"""Test that we can log in using the combined login/registration page. """
def setUp(self):
"""Initialize the page objects and create a test course. """
super(LoginFromCombinedPageTest, self).setUp()
self.login_page = CombinedLoginAndRegisterPage(
self.browser,
start_page="login",
course_id=self.course_id
)
self.dashboard_page = DashboardPage(self.browser)
# Create a course to enroll in
CourseFixture(
self.course_info['org'], self.course_info['number'],
self.course_info['run'], self.course_info['display_name']
).install()
def test_login_success(self):
# Create a user account
email, password = self._create_unique_user()
# Navigate to the login page and try to log in
self.login_page.visit().login(email=email, password=password)
# Expect that we reach the dashboard and we're auto-enrolled in the course
course_names = self.dashboard_page.wait_for_page().available_courses
self.assertIn(self.course_info["display_name"], course_names)
def test_login_failure(self):
# Navigate to the login page
self.login_page.visit()
# User account does not exist
self.login_page.login(email="nobody@nowhere.com", password="password")
# Verify that an error is displayed
self.assertIn("Email or password is incorrect.", self.login_page.wait_for_errors())
def test_toggle_to_register_form(self):
self.login_page.visit().toggle_form()
self.assertEqual(self.login_page.current_form, "register")
def _create_unique_user(self):
username = "test_{uuid}".format(uuid=self.unique_id[0:6])
email = "{user}@example.com".format(user=username)
password = "password"
# Create the user (automatically logs us in)
AutoAuthPage(
self.browser,
username=username,
email=email,
password=password
).visit()
# Log out
LogoutPage(self.browser).visit()
return (email, password)
@attr('shard_1')
class RegisterFromCombinedPageTest(UniqueCourseTest):
"""Test that we can register a new user from the combined login/registration page. """
def setUp(self):
"""Initialize the page objects and create a test course. """
super(RegisterFromCombinedPageTest, self).setUp()
self.register_page = CombinedLoginAndRegisterPage(
self.browser,
start_page="register",
course_id=self.course_id
)
self.dashboard_page = DashboardPage(self.browser)
# Create a course to enroll in
CourseFixture(
self.course_info['org'], self.course_info['number'],
self.course_info['run'], self.course_info['display_name']
).install()
def test_register_success(self):
# Navigate to the registration page
self.register_page.visit()
# Fill in the form and submit it
username = "test_{uuid}".format(uuid=self.unique_id[0:6])
email = "{user}@example.com".format(user=username)
self.register_page.register(
email=email,
password="password",
username=username,
full_name="Test User",
country="US",
terms_of_service=True
)
# Expect that we reach the dashboard and we're auto-enrolled in the course
course_names = self.dashboard_page.wait_for_page().available_courses
self.assertIn(self.course_info["display_name"], course_names)
def test_register_failure(self):
# Navigate to the registration page
self.register_page.visit()
# Enter a blank for the username field, which is required
# Don't agree to the terms of service / honor code.
# Don't specify a country code, which is required.
username = "test_{uuid}".format(uuid=self.unique_id[0:6])
email = "{user}@example.com".format(user=username)
self.register_page.register(
email=email,
password="password",
username="",
full_name="Test User",
terms_of_service=False
)
# Verify that the expected errors are displayed.
errors = self.register_page.wait_for_errors()
self.assertIn(u'The Username field cannot be empty.', errors)
self.assertIn(u'You must agree to the edX Terms of Service and Honor Code.', errors)
self.assertIn(u'The Country field cannot be empty.', errors)
def test_toggle_to_login_form(self):
self.register_page.visit().toggle_form()
self.assertEqual(self.register_page.current_form, "login")
class LanguageTest(WebAppTest):
"""
Tests that the change language functionality on the dashboard works
......
......@@ -69,6 +69,7 @@
"ENABLE_INSTRUCTOR_ANALYTICS": true,
"ENABLE_S3_GRADE_DOWNLOADS": true,
"ENABLE_THIRD_PARTY_AUTH": true,
"ENABLE_COMBINED_LOGIN_REGISTRATION": true,
"PREVIEW_LMS_BASE": "localhost:8003",
"SUBDOMAIN_BRANDING": false,
"SUBDOMAIN_COURSE_LISTINGS": false
......@@ -82,6 +83,17 @@
"MEDIA_URL": "",
"MKTG_URL_LINK_MAP": {},
"PLATFORM_NAME": "edX",
"REGISTRATION_EXTRA_FIELDS": {
"level_of_education": "optional",
"gender": "optional",
"year_of_birth": "optional",
"mailing_address": "optional",
"goals": "optional",
"honor_code": "required",
"terms_of_service": "hidden",
"city": "hidden",
"country": "required"
},
"SEGMENT_IO_LMS": true,
"SERVER_EMAIL": "devops@example.com",
"SESSION_COOKIE_DOMAIN": null,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment