Commit f67b2e1f by Clinton Blackburn

Added acceptance tests for new user enrollment

These acceptance tests will help identify issues with registration+enrollment before they reach production.

ECOM-2158
parent 2bd5b4e2
import logging import logging
import uuid import uuid
import requests
from ecommerce_api_client.client import EcommerceApiClient from ecommerce_api_client.client import EcommerceApiClient
import requests
from acceptance_tests.api import EnrollmentApiClient from acceptance_tests.api import EnrollmentApiClient
from acceptance_tests.config import (ENABLE_LMS_AUTO_AUTH, APP_SERVER_URL, LMS_PASSWORD, LMS_EMAIL, LMS_URL, from acceptance_tests.config import (ENABLE_LMS_AUTO_AUTH, APP_SERVER_URL, LMS_PASSWORD, LMS_EMAIL, LMS_URL,
BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD, ECOMMERCE_API_SERVER_URL, BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD, ECOMMERCE_API_SERVER_URL,
LMS_USERNAME, ECOMMERCE_API_TOKEN) LMS_USERNAME, ECOMMERCE_API_TOKEN)
from acceptance_tests.pages import LMSLoginPage, LMSDashboardPage from acceptance_tests.pages import LMSLoginPage, LMSDashboardPage, LMSRegistrationPage
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class LoginMixin(object):
def setUp(self):
super(LoginMixin, self).setUp()
self.lms_login_page = LMSLoginPage(self.browser)
def login(self):
self.login_with_lms()
def login_with_lms(self, email=None, password=None, course_id=None):
""" Visit LMS and login. """
email = email or LMS_EMAIL
password = password or LMS_PASSWORD
# Note: We use Selenium directly here (as opposed to bok-choy) to avoid issues with promises being broken.
self.lms_login_page.browser.get(self.lms_login_page.url(course_id)) # pylint: disable=not-callable
self.lms_login_page.login(email, password)
class LogoutMixin(object):
def logout(self):
url = '{}/accounts/logout/'.format(APP_SERVER_URL)
self.browser.get(url)
class LmsUserMixin(object): class LmsUserMixin(object):
password = 'edx' password = 'edx'
...@@ -46,10 +22,14 @@ class LmsUserMixin(object): ...@@ -46,10 +22,14 @@ class LmsUserMixin(object):
return LMS_USERNAME, LMS_PASSWORD, LMS_EMAIL return LMS_USERNAME, LMS_PASSWORD, LMS_EMAIL
def create_lms_user(self, username=None, password=None, email=None): def generate_user_credentials(self, username_prefix):
username = username or ('auto_auth_' + uuid.uuid4().hex[0:20]) username = username_prefix + uuid.uuid4().hex[0:20]
password = password or 'edx' password = self.password
email = email or '{}@example.com'.format(username) email = '{}@example.com'.format(username)
return username, email, password
def create_lms_user(self):
username, email, password = self.generate_user_credentials(username_prefix='auto_auth_')
url = '{host}/auto_auth?no_login=true&username={username}&password={password}&email={email}'.format( url = '{host}/auto_auth?no_login=true&username={username}&password={password}&email={email}'.format(
host=LMS_URL, username=username, password=password, email=email) host=LMS_URL, username=username, password=password, email=email)
...@@ -63,6 +43,40 @@ class LmsUserMixin(object): ...@@ -63,6 +43,40 @@ class LmsUserMixin(object):
return username, password, email return username, password, email
class LogistrationMixin(LmsUserMixin):
def setUp(self):
super(LogistrationMixin, self).setUp()
self.lms_login_page = LMSLoginPage(self.browser)
self.lms_registration_page = LMSRegistrationPage(self.browser)
def login(self):
self.login_with_lms()
def login_with_lms(self, email=None, password=None, course_id=None):
""" Visit LMS and login. """
email = email or LMS_EMAIL
password = password or LMS_PASSWORD
# Note: We use Selenium directly here (as opposed to bok-choy) to avoid issues with promises being broken.
self.lms_login_page.browser.get(self.lms_login_page.url(course_id)) # pylint: disable=not-callable
self.lms_login_page.login(email, password)
def register_via_ui(self, course_id=None):
""" Creates a new account via the normal user interface. """
username, email, password = self.generate_user_credentials(username_prefix='otto_acceptance_')
url = self.lms_registration_page.url(course_id) # pylint: disable=not-callable
self.lms_registration_page.browser.get(url)
self.lms_registration_page.register_and_login(username, username, email, password)
return username, email, password
class LogoutMixin(object):
def logout(self):
url = '{}/accounts/logout/'.format(APP_SERVER_URL)
self.browser.get(url)
class EnrollmentApiMixin(object): class EnrollmentApiMixin(object):
def setUp(self): def setUp(self):
super(EnrollmentApiMixin, self).setUp() super(EnrollmentApiMixin, self).setUp()
......
...@@ -39,6 +39,9 @@ class LMSPage(PageObject): # pylint: disable=abstract-method ...@@ -39,6 +39,9 @@ class LMSPage(PageObject): # pylint: disable=abstract-method
return url return url
def _is_browser_on_lms_dashboard(self):
return lambda: self.browser.title.startswith('Dashboard')
class LMSLoginPage(LMSPage): class LMSLoginPage(LMSPage):
def url(self, course_id=None): # pylint: disable=arguments-differ def url(self, course_id=None): # pylint: disable=arguments-differ
...@@ -51,10 +54,7 @@ class LMSLoginPage(LMSPage): ...@@ -51,10 +54,7 @@ class LMSLoginPage(LMSPage):
return url return url
def is_browser_on_page(self): def is_browser_on_page(self):
return self.browser.title.startswith('Sign in') return self.q(css='form#login').visible
def _is_browser_on_lms_dashboard(self):
return lambda: self.browser.title.startswith('Dashboard')
def login(self, username, password): def login(self, username, password):
self.q(css='input#login-email').fill(username) self.q(css='input#login-email').fill(username)
...@@ -65,6 +65,31 @@ class LMSLoginPage(LMSPage): ...@@ -65,6 +65,31 @@ class LMSLoginPage(LMSPage):
EmptyPromise(self._is_browser_on_lms_dashboard(), "LMS login redirected to dashboard").fulfill() EmptyPromise(self._is_browser_on_lms_dashboard(), "LMS login redirected to dashboard").fulfill()
class LMSRegistrationPage(LMSPage):
def url(self, course_id=None): # pylint: disable=arguments-differ
url = self._build_url('register')
if course_id:
params = {'enrollment_action': 'enroll', 'course_id': course_id}
url = '{0}?{1}'.format(url, urllib.urlencode(params))
return url
def is_browser_on_page(self):
return self.q(css='form#register').visible
def register_and_login(self, username, name, email, password):
self.q(css='input#register-username').fill(username)
self.q(css='input#register-name').fill(name)
self.q(css='input#register-email').fill(email)
self.q(css='input#register-password').fill(password)
self.q(css='input#register-honor_code').click()
self.q(css='button.register-button').click()
# Wait for LMS to redirect to the dashboard
EmptyPromise(self._is_browser_on_lms_dashboard(), "LMS login redirected to dashboard").fulfill()
class LMSCourseModePage(LMSPage): class LMSCourseModePage(LMSPage):
def is_browser_on_page(self): def is_browser_on_page(self):
return self.browser.title.lower().startswith('enroll in') return self.browser.title.lower().startswith('enroll in')
......
...@@ -3,12 +3,12 @@ from unittest import skipUnless ...@@ -3,12 +3,12 @@ from unittest import skipUnless
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from acceptance_tests.config import ENABLE_OAUTH_TESTS from acceptance_tests.config import ENABLE_OAUTH_TESTS
from acceptance_tests.mixins import LoginMixin from acceptance_tests.mixins import LogistrationMixin
from acceptance_tests.pages import DashboardHomePage from acceptance_tests.pages import DashboardHomePage
@skipUnless(ENABLE_OAUTH_TESTS, 'OAuth tests are not enabled.') @skipUnless(ENABLE_OAUTH_TESTS, 'OAuth tests are not enabled.')
class OAuth2FlowTests(LoginMixin, WebAppTest): class OAuth2FlowTests(LogistrationMixin, WebAppTest):
def setUp(self): def setUp(self):
""" """
Instantiate the page objects. Instantiate the page objects.
......
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from acceptance_tests.config import COURSE_ID from acceptance_tests.config import COURSE_ID
from acceptance_tests.mixins import LoginMixin, EcommerceApiMixin, EnrollmentApiMixin, LmsUserMixin, UnenrollmentMixin from acceptance_tests.mixins import LogistrationMixin, EcommerceApiMixin, EnrollmentApiMixin, UnenrollmentMixin
class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LmsUserMixin, LoginMixin, class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin, WebAppTest):
WebAppTest):
def setUp(self): def setUp(self):
super(LoginEnrollmentTests, self).setUp() super(LoginEnrollmentTests, self).setUp()
self.course_id = COURSE_ID self.course_id = COURSE_ID
...@@ -18,3 +17,9 @@ class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMi ...@@ -18,3 +17,9 @@ class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMi
self.login_with_lms(self.email, self.password, self.course_id) self.login_with_lms(self.email, self.password, self.course_id)
self.assert_order_created_and_completed() self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id) self.assert_user_enrolled(self.username, self.course_id)
def test_honor_enrollment_and_registration(self):
""" Verifies that a user can register and enroll in a course via the login page. """
username, __, __ = self.register_via_ui(self.course_id)
self.assert_order_created_and_completed()
self.assert_user_enrolled(username, self.course_id)
...@@ -7,13 +7,13 @@ from selenium.webdriver.support.select import Select ...@@ -7,13 +7,13 @@ from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from acceptance_tests.config import VERIFIED_COURSE_ID, HTTPS_RECEIPT_PAGE, PAYPAL_PASSWORD, PAYPAL_EMAIL from acceptance_tests.config import VERIFIED_COURSE_ID, HTTPS_RECEIPT_PAGE, PAYPAL_PASSWORD, PAYPAL_EMAIL
from acceptance_tests.mixins import LoginMixin, EnrollmentApiMixin, EcommerceApiMixin, LmsUserMixin, UnenrollmentMixin from acceptance_tests.mixins import LogistrationMixin, EnrollmentApiMixin, EcommerceApiMixin, UnenrollmentMixin
from acceptance_tests.pages import LMSCourseModePage from acceptance_tests.pages import LMSCourseModePage
@ddt.ddt @ddt.ddt
class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LmsUserMixin, class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin,
LoginMixin, WebAppTest): WebAppTest):
def setUp(self): def setUp(self):
super(VerifiedCertificatePaymentTests, self).setUp() super(VerifiedCertificatePaymentTests, self).setUp()
self.course_id = VERIFIED_COURSE_ID self.course_id = VERIFIED_COURSE_ID
......
...@@ -2,12 +2,12 @@ from unittest import skip ...@@ -2,12 +2,12 @@ from unittest import skip
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from acceptance_tests.mixins import LoginMixin, LmsUserMixin, EnrollmentApiMixin from acceptance_tests.mixins import LogistrationMixin, EnrollmentApiMixin
from acceptance_tests.pages import LMSCourseModePage from acceptance_tests.pages import LMSCourseModePage
@skip('Prof. Ed. tests should be run on an as-needed basis.') @skip('Prof. Ed. tests should be run on an as-needed basis.')
class ProfessionalEducationEnrollmentTests(EnrollmentApiMixin, LmsUserMixin, LoginMixin, WebAppTest): class ProfessionalEducationEnrollmentTests(EnrollmentApiMixin, LogistrationMixin, WebAppTest):
def test_payment_required(self): def test_payment_required(self):
""" Verify payment is required before enrolling in a professional education course. """ """ Verify payment is required before enrolling in a professional education course. """
......
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