Commit c6777a5b by Renzo Lucioni

Bok Choy tests for the split payment and verification flow

Adds mode creation endpoint to course_modes app and ability to bypass media device access prompt when proceeding through verification flow in a testing environment.
parent 27fee620
......@@ -76,7 +76,7 @@ Jonah Stanley <Jonah_Stanley@brown.edu>
Slater Victoroff <slater.r.victoroff@gmail.com>
Peter Fogg <peter.p.fogg@gmail.com>
Bethany LaPenta <lapentab@mit.edu>
Renzo Lucioni <renzo@renzolucioni.com>
Renzo Lucioni <renzo@edx.org>
Felix Sun <felixsun@mit.edu>
Adam Palay <adam@edx.org>
Ian Hoover <ihoover@edx.org>
......
import unittest
import decimal
import ddt
from mock import patch
from django.conf import settings
from django.test.utils import override_settings
from django.core.urlresolvers import reverse
......@@ -9,10 +10,12 @@ from xmodule.modulestore.tests.django_utils import (
ModuleStoreTestCase, mixed_store_config
)
from util.testing import UrlResetMixin
from xmodule.modulestore.tests.factories import CourseFactory
from course_modes.tests.factories import CourseModeFactory
from student.tests.factories import CourseEnrollmentFactory, UserFactory
from student.models import CourseEnrollment
from course_modes.models import CourseMode, Mode
# Since we don't need any XML course fixtures, use a modulestore configuration
......@@ -23,10 +26,10 @@ MODULESTORE_CONFIG = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {}, incl
@ddt.ddt
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class CourseModeViewTest(ModuleStoreTestCase):
class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
@patch.dict(settings.FEATURES, {'MODE_CREATION_FOR_TESTING': True})
def setUp(self):
super(CourseModeViewTest, self).setUp()
super(CourseModeViewTest, self).setUp('course_modes.urls')
self.course = CourseFactory.create()
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
self.client.login(username=self.user.username, password="edx")
......@@ -235,3 +238,65 @@ class CourseModeViewTest(ModuleStoreTestCase):
response = self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE['unsupported'])
self.assertEqual(400, response.status_code)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_default_mode_creation(self):
# Hit the mode creation endpoint with no querystring params, to create an honor mode
url = reverse('create_mode', args=[unicode(self.course.id)])
response = self.client.get(url)
self.assertEquals(response.status_code, 200)
expected_mode = [Mode(u'honor', u'Honor Code Certificate', 0, '', 'usd', None, None)]
course_mode = CourseMode.modes_for_course(self.course.id)
self.assertEquals(course_mode, expected_mode)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.data(
(u'verified', u'Verified Certificate', 10, '10,20,30', 'usd'),
(u'professional', u'Professional Education', 100, '100,200', 'usd'),
)
@ddt.unpack
def test_verified_mode_creation(self, mode_slug, mode_display_name, min_price, suggested_prices, currency):
parameters = {}
parameters['mode_slug'] = mode_slug
parameters['mode_display_name'] = mode_display_name
parameters['min_price'] = min_price
parameters['suggested_prices'] = suggested_prices
parameters['currency'] = currency
url = reverse('create_mode', args=[unicode(self.course.id)])
response = self.client.get(url, parameters)
self.assertEquals(response.status_code, 200)
expected_mode = [Mode(mode_slug, mode_display_name, min_price, suggested_prices, currency, None, None)]
course_mode = CourseMode.modes_for_course(self.course.id)
self.assertEquals(course_mode, expected_mode)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_multiple_mode_creation(self):
# Create an honor mode
base_url = reverse('create_mode', args=[unicode(self.course.id)])
self.client.get(base_url)
# Excluding the currency parameter implicitly tests the mode creation endpoint's ability to
# use default values when parameters are partially missing.
parameters = {}
parameters['mode_slug'] = u'verified'
parameters['mode_display_name'] = u'Verified Certificate'
parameters['min_price'] = 10
parameters['suggested_prices'] = '10,20'
# Create a verified mode
url = reverse('create_mode', args=[unicode(self.course.id)])
response = self.client.get(url, parameters)
honor_mode = Mode(u'honor', u'Honor Code Certificate', 0, '', 'usd', None, None)
verified_mode = Mode(u'verified', u'Verified Certificate', 10, '10,20', 'usd', None, None)
expected_modes = [honor_mode, verified_mode]
course_modes = CourseMode.modes_for_course(self.course.id)
self.assertEquals(course_modes, expected_modes)
......@@ -8,5 +8,11 @@ from course_modes import views
urlpatterns = patterns(
'',
# pylint seems to dislike as_view() calls because it's a `classonlymethod` instead of `classmethod`, so we disable the warning
url(r'^choose/{}/$'.format(settings.COURSE_ID_PATTERN), views.ChooseModeView.as_view(), name="course_modes_choose"), # pylint: disable=no-value-for-parameter
url(r'^choose/{}/$'.format(settings.COURSE_ID_PATTERN), views.ChooseModeView.as_view(), name='course_modes_choose'), # pylint: disable=no-value-for-parameter
)
# Enable verified mode creation
if settings.FEATURES.get('MODE_CREATION_FOR_TESTING'):
urlpatterns += (
url(r'^create_mode/{}/$'.format(settings.COURSE_ID_PATTERN), 'course_modes.views.create_mode', name='create_mode'),
)
......@@ -5,7 +5,7 @@ Views for the course_mode module
import decimal
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import HttpResponseBadRequest
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect
from django.views.generic.base import View
from django.utils.translation import ugettext as _
......@@ -226,3 +226,48 @@ class ChooseModeView(View):
return 'honor'
else:
return None
def create_mode(request, course_id):
"""Add a mode to the course corresponding to the given course ID.
Only available when settings.FEATURES['MODE_CREATION_FOR_TESTING'] is True.
Attempts to use the following querystring parameters from the request:
`mode_slug` (str): The mode to add, either 'honor', 'verified', or 'professional'
`mode_display_name` (str): Describes the new course mode
`min_price` (int): The minimum price a user must pay to enroll in the new course mode
`suggested_prices` (str): Comma-separated prices to suggest to the user.
`currency` (str): The currency in which to list prices.
By default, this endpoint will create an 'honor' mode for the given course with display name
'Honor Code', a minimum price of 0, no suggested prices, and using USD as the currency.
Args:
request (`Request`): The Django Request object.
course_id (unicode): The slash-separated course key.
Returns:
Response
"""
PARAMETERS = {
'mode_slug': u'honor',
'mode_display_name': u'Honor Code Certificate',
'min_price': 0,
'suggested_prices': u'',
'currency': u'usd',
}
# Try pulling querystring parameters out of the request
for parameter, default in PARAMETERS.iteritems():
PARAMETERS[parameter] = request.GET.get(parameter, default)
# Attempt to create the new mode for the given course
course_key = CourseKey.from_string(course_id)
CourseMode.objects.get_or_create(course_id=course_key, **PARAMETERS)
# Return a success message and a 200 response
return HttpResponse("Mode '{mode_slug}' created for course with ID '{course_id}'.".format(
mode_slug=PARAMETERS['mode_slug'],
course_id=course_id
))
"""Mode creation page (used to add modes to courses during testing)."""
import re
import urllib
from bok_choy.page_object import PageObject
from . import BASE_URL
class ModeCreationPage(PageObject):
"""The mode creation page.
When allowed by the Django settings file, visiting this page allows modes to be
created for an existing course.
"""
def __init__(self, browser, course_id, mode_slug=None, mode_display_name=None, min_price=None, suggested_prices=None, currency=None):
"""The mode creation page is an endpoint for HTTP GET requests.
By default, it will create an 'honor' mode for the given course with display name
'Honor Code', a minimum price of 0, no suggested prices, and using USD as the currency.
Arguments:
browser (Browser): The browser instance.
course_id (unicode): The ID of the course for which modes are to be created.
Keyword Arguments:
mode_slug (str): The mode to add, either 'honor', 'verified', or 'professional'
mode_display_name (str): Describes the new course mode
min_price (int): The minimum price a user must pay to enroll in the new course mode
suggested_prices (str): Comma-separated prices to suggest to the user.
currency (str): The currency in which to list prices.
"""
super(ModeCreationPage, self).__init__(browser)
self.course_id = course_id
self._parameters = {}
if mode_slug is not None:
self._parameters['mode_slug'] = mode_slug
if mode_display_name is not None:
self._parameters['mode_display_name'] = mode_display_name
if min_price is not None:
self._parameters['min_price'] = min_price
if suggested_prices is not None:
self._parameters['suggested_prices'] = suggested_prices
if currency is not None:
self._parameters['currency'] = currency
@property
def url(self):
"""Construct the mode creation URL."""
url = '{base}/course_modes/create_mode/{course_id}'.format(
base=BASE_URL,
course_id=self.course_id
)
query_string = urllib.urlencode(self._parameters)
if query_string:
url += '?' + query_string
return url
def is_browser_on_page(self):
message = self.q(css='BODY').text[0]
match = re.search(r'Mode ([^$]+) created for course with ID ([^$]+).$', message)
return True if match else False
......@@ -13,8 +13,32 @@ class DashboardPage(PageObject):
Student dashboard, where the student can view
courses she/he has registered for.
"""
def __init__(self, browser, separate_verified=False):
"""Initialize the page.
url = BASE_URL + "/dashboard"
Arguments:
browser (Browser): The browser instance.
Keyword Arguments:
separate_verified (Boolean): Whether to use the split payment and
verification flow.
"""
super(DashboardPage, self).__init__(browser)
if separate_verified:
self._querystring = "?separate-verified=1"
else:
self._querystring = "?disable-separate-verified=1"
@property
def url(self):
"""Return the URL corresponding to the dashboard."""
url = "{base}/dashboard{querystring}".format(
base=BASE_URL,
querystring=self._querystring
)
return url
def is_browser_on_page(self):
return self.q(css='section.my-courses').present
......@@ -44,6 +68,60 @@ class DashboardPage(PageObject):
return self.q(css='section.info > hgroup > h3 > a').map(_get_course_name).results
def get_enrollment_mode(self, course_name):
"""Get the enrollment mode for a given course on the dashboard.
Arguments:
course_name (str): The name of the course whose mode should be retrieved.
Returns:
String, indicating the enrollment mode for the course corresponding to
the provided course name.
Raises:
Exception, if no course with the provided name is found on the dashboard.
"""
# Filter elements by course name, only returning the relevant course item
course_listing = self.q(css=".course").filter(lambda el: course_name in el.text).results
if course_listing:
# There should only be one course listing for the provided course name.
# Since 'ENABLE_VERIFIED_CERTIFICATES' is true in the Bok Choy settings, we
# can expect two classes to be present on <article> elements, one being 'course'
# and the other being the enrollment mode.
enrollment_mode = course_listing[0].get_attribute('class').split('course ')[1]
else:
raise Exception("No course named {} was found on the dashboard".format(course_name))
return enrollment_mode
def upgrade_enrollment(self, course_name, upgrade_page):
"""Interact with the upgrade button for the course with the provided name.
Arguments:
course_name (str): The name of the course whose mode should be checked.
upgrade_page (PageObject): The page to wait on after clicking the upgrade button. Importing
the definition of PaymentAndVerificationFlow results in a circular dependency.
Raises:
Exception, if no enrollment corresponding to the provided course name appears
on the dashboard.
"""
# Filter elements by course name, only returning the relevant course item
course_listing = self.q(css=".course").filter(lambda el: course_name in el.text).results
if course_listing:
# There should only be one course listing corresponding to the provided course name.
el = course_listing[0]
# Expand the upsell copy and click the upgrade button
el.find_element_by_css_selector('.message-upsell').click()
el.find_element_by_css_selector('#upgrade-to-verified').click()
upgrade_page.wait_for_page()
else:
raise Exception("No enrollment for {} is visible on the dashboard.".format(course_name))
def view_course(self, course_id):
"""
Go to the course with `course_id` (e.g. edx/Open_DemoX/edx_demo_course)
......
"""Payment and verification pages"""
import re
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
class PaymentAndVerificationFlow(PageObject):
"""Interact with the split payment and verification flow.
These pages are currently hidden behind the feature flag
`SEPARATE_VERIFICATION_FROM_PAYMENT`, which is enabled in
the Bok Choy settings.
When enabled, the flow can be accessed at the following URLs:
`/verify_student/start-flow/{course}/`
`/verify_student/upgrade/{course}/`
`/verify_student/verify-now/{course}/`
`/verify_student/verify-later/{course}/`
`/verify_student/payment-confirmation/{course}/`
Users can reach the flow when attempting to enroll in a course's verified
mode, either directly from the track selection page, or by upgrading from
the honor mode. Users can also reach the flow when attempting to complete
a deferred verification, or when attempting to view a receipt corresponding
to an earlier payment.
"""
def __init__(self, browser, course_id, entry_point='start-flow'):
"""Initialize the page.
Arguments:
browser (Browser): The browser instance.
course_id (unicode): The course in which the user is enrolling.
Keyword Arguments:
entry_point (str): Where to begin the flow; must be one of 'start-flow',
'upgrade', 'verify-now', verify-later', or 'payment-confirmation'.
Raises:
ValueError
"""
super(PaymentAndVerificationFlow, self).__init__(browser)
self._course_id = course_id
if entry_point not in ['start-flow', 'upgrade', 'verify-now', 'verify-later', 'payment-confirmation']:
raise ValueError(
"Entry point must be either 'start-flow', 'upgrade', 'verify-now', 'verify-later', or 'payment-confirmation'."
)
self._entry_point = entry_point
@property
def url(self):
"""Return the URL corresponding to the initial position in the flow."""
url = "{base}/verify_student/{entry_point}/{course}".format(
base=BASE_URL,
entry_point=self._entry_point,
course=self._course_id
)
return url
def is_browser_on_page(self):
"""Check if a step in the payment and verification flow has loaded."""
return (
self.q(css="div .make-payment-step").is_present() or
self.q(css="div .payment-confirmation-step").is_present() or
self.q(css="div .face-photo-step").is_present() or
self.q(css="div .id-photo-step").is_present() or
self.q(css="div .review-photos-step").is_present() or
self.q(css="div .enrollment-confirmation-step").is_present()
)
def indicate_contribution(self):
"""Interact with the radio buttons appearing on the first page of the upgrade flow."""
self.q(css=".contribution-option > input").first.click()
def proceed_to_payment(self):
"""Interact with the payment button."""
self.q(css="#pay_button").click()
FakePaymentPage(self.browser, self._course_id).wait_for_page()
def immediate_verification(self):
"""Interact with the immediate verification button."""
self.q(css="#verify_now_button").click()
PaymentAndVerificationFlow(self.browser, self._course_id, entry_point='verify-now').wait_for_page()
def defer_verification(self):
"""Interact with the link allowing the user to defer their verification."""
self.q(css="#verify_later_button").click()
DashboardPage(self.browser).wait_for_page()
def webcam_capture(self):
"""Interact with a webcam capture button."""
self.q(css="#webcam_capture_button").click()
def _check_func():
next_step_button_classes = self.q(css="#next_step_button").attrs('class')
next_step_button_enabled = 'is-disabled' not in next_step_button_classes
return (next_step_button_enabled, next_step_button_classes)
# Check that the #next_step_button is enabled before returning control to the caller
Promise(_check_func, "The 'Next Step' button is enabled.").fulfill()
def next_verification_step(self, next_page_object):
"""Interact with the 'Next' step button found in the verification flow."""
self.q(css="#next_step_button").click()
next_page_object.wait_for_page()
def go_to_dashboard(self):
"""Interact with the link to the dashboard appearing on the enrollment confirmation page."""
if self.q(css="div .enrollment-confirmation-step").is_present():
self.q(css=".action-primary").click()
else:
raise Exception("The dashboard can only be accessed from the enrollment confirmation.")
DashboardPage(self.browser, separate_verified=True).wait_for_page()
class FakePaymentPage(PageObject):
"""Interact with the fake payment endpoint.
This page is hidden behind the feature flag `ENABLE_PAYMENT_FAKE`,
which is enabled in the Bok Choy env settings.
Configuring this payment endpoint also requires configuring the Bok Choy
auth settings with the following:
"CC_PROCESSOR_NAME": "CyberSource2",
"CC_PROCESSOR": {
"CyberSource2": {
"SECRET_KEY": <string>,
"ACCESS_KEY": <string>,
"PROFILE_ID": "edx",
"PURCHASE_ENDPOINT": "/shoppingcart/payment_fake"
}
}
"""
def __init__(self, browser, course_id):
"""Initialize the page.
Arguments:
browser (Browser): The browser instance.
course_id (unicode): The course in which the user is enrolling.
"""
super(FakePaymentPage, self).__init__(browser)
self._course_id = course_id
url = BASE_URL + "/shoppingcart/payment_fake/"
def is_browser_on_page(self):
"""Check if a step in the payment and verification flow has loaded."""
message = self.q(css='BODY').text[0]
match = re.search('Payment page', message)
return True if match else False
def submit_payment(self):
"""Interact with the payment submission button."""
self.q(css="input[value='Submit']").click()
return PaymentAndVerificationFlow(self.browser, self._course_id, entry_point='payment-confirmation').wait_for_page()
"""Track selection page"""
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
from .pay_and_verify import PaymentAndVerificationFlow
class TrackSelectionPage(PageObject):
"""Interact with the track selection page.
This page can be accessed at `/course_modes/choose/{course_id}/`.
"""
def __init__(self, browser, course_id, separate_verified=False):
"""Initialize the page.
Arguments:
browser (Browser): The browser instance.
course_id (unicode): The course in which the user is enrolling.
Keyword Arguments:
separate_verified (Boolean): Whether to use the split payment and
verification flow when enrolling as verified.
"""
super(TrackSelectionPage, self).__init__(browser)
self._course_id = course_id
self._separate_verified = separate_verified
if self._separate_verified:
self._querystring = "?separate-verified=1"
else:
self._querystring = "?disable-separate-verified=1"
@property
def url(self):
"""Return the URL corresponding to the track selection page."""
url = "{base}/course_modes/choose/{course_id}{querystring}".format(
base=BASE_URL,
course_id=self._course_id,
querystring=self._querystring
)
return url
def is_browser_on_page(self):
"""Check if the track selection page has loaded."""
return self.q(css=".wrapper-register-choose").is_present()
def enroll(self, mode="honor"):
"""Interact with one of the enrollment buttons on the page.
Keyword Arguments:
mode (str): Can be "honor" or "verified"
Raises:
ValueError
"""
if mode == "honor":
self.q(css="input[name='honor_mode']").click()
return DashboardPage(self.browser, separate_verified=self._separate_verified).wait_for_page()
elif mode == "verified":
# Check the first contribution option, then click the enroll button
self.q(css=".contribution-option > input").first.click()
self.q(css="input[name='verified_mode']").click()
return PaymentAndVerificationFlow(self.browser, self._course_id).wait_for_page()
else:
raise ValueError("Mode must be either 'honor' or 'verified'.")
......@@ -16,6 +16,7 @@ from ..helpers import (
select_option_by_value,
)
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.create_mode import ModeCreationPage
from ...pages.common.logout import LogoutPage
from ...pages.lms.find_courses import FindCoursesPage
from ...pages.lms.course_about import CourseAboutPage
......@@ -28,6 +29,8 @@ 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 ...pages.lms.track_selection import TrackSelectionPage
from ...pages.lms.pay_and_verify import PaymentAndVerificationFlow, FakePaymentPage
from ...pages.studio.settings import SettingsPage
from ...fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc
......@@ -237,6 +240,128 @@ class RegisterFromCombinedPageTest(UniqueCourseTest):
self.assertEqual(self.register_page.current_form, "login")
@attr('shard_1')
class PayAndVerifyTest(UniqueCourseTest):
"""Test that we can proceed through the payment and verification flow."""
def setUp(self):
"""Initialize the test.
Create the necessary page objects, create a test course and configure its modes,
create a user and log them in.
"""
super(PayAndVerifyTest, self).setUp()
self.track_selection_page = TrackSelectionPage(self.browser, self.course_id, separate_verified=True)
self.payment_and_verification_flow = PaymentAndVerificationFlow(self.browser, self.course_id)
self.immediate_verification_page = PaymentAndVerificationFlow(self.browser, self.course_id, entry_point='verify-now')
self.upgrade_page = PaymentAndVerificationFlow(self.browser, self.course_id, entry_point='upgrade')
self.fake_payment_page = FakePaymentPage(self.browser, self.course_id)
self.dashboard_page = DashboardPage(self.browser, separate_verified=True)
# Create a course
CourseFixture(
self.course_info['org'],
self.course_info['number'],
self.course_info['run'],
self.course_info['display_name']
).install()
# Add an honor mode to the course
ModeCreationPage(self.browser, self.course_id).visit()
# Add a verified mode to the course
ModeCreationPage(self.browser, self.course_id, mode_slug=u'verified', mode_display_name=u'Verified Certificate', min_price=10, suggested_prices='10,20').visit()
def test_immediate_verification_enrollment(self):
# Create a user and log them in
AutoAuthPage(self.browser).visit()
# Navigate to the track selection page with the appropriate GET parameter in the URL
self.track_selection_page.visit()
# Enter the payment and verification flow by choosing to enroll as verified
self.track_selection_page.enroll('verified')
# Proceed to the fake payment page
self.payment_and_verification_flow.proceed_to_payment()
# Submit payment
self.fake_payment_page.submit_payment()
# Proceed to verification
self.payment_and_verification_flow.immediate_verification()
# Take face photo and proceed to the ID photo step
self.payment_and_verification_flow.webcam_capture()
self.payment_and_verification_flow.next_verification_step(self.immediate_verification_page)
# Take ID photo and proceed to the review photos step
self.payment_and_verification_flow.webcam_capture()
self.payment_and_verification_flow.next_verification_step(self.immediate_verification_page)
# Submit photos and proceed to the enrollment confirmation step
self.payment_and_verification_flow.next_verification_step(self.immediate_verification_page)
# Navigate to the dashboard with the appropriate GET parameter in the URL
self.dashboard_page.visit()
# Expect that we're enrolled as verified in the course
enrollment_mode = self.dashboard_page.get_enrollment_mode(self.course_info["display_name"])
self.assertEqual(enrollment_mode, 'verified')
def test_deferred_verification_enrollment(self):
# Create a user and log them in
AutoAuthPage(self.browser).visit()
# Navigate to the track selection page with the appropriate GET parameter in the URL
self.track_selection_page.visit()
# Enter the payment and verification flow by choosing to enroll as verified
self.track_selection_page.enroll('verified')
# Proceed to the fake payment page
self.payment_and_verification_flow.proceed_to_payment()
# Submit payment
self.fake_payment_page.submit_payment()
# Navigate to the dashboard with the appropriate GET parameter in the URL
self.dashboard_page.visit()
# Expect that we're enrolled as verified in the course
enrollment_mode = self.dashboard_page.get_enrollment_mode(self.course_info["display_name"])
self.assertEqual(enrollment_mode, 'verified')
def test_enrollment_upgrade(self):
# Create a user, log them in, and enroll them in the honor mode
AutoAuthPage(self.browser, course_id=self.course_id).visit()
# Navigate to the dashboard with the appropriate GET parameter in the URL
self.dashboard_page.visit()
# Expect that we're enrolled as honor in the course
enrollment_mode = self.dashboard_page.get_enrollment_mode(self.course_info["display_name"])
self.assertEqual(enrollment_mode, 'honor')
# Click the upsell button on the dashboard
self.dashboard_page.upgrade_enrollment(self.course_info["display_name"], self.upgrade_page)
# Select the first contribution option appearing on the page
self.upgrade_page.indicate_contribution()
# Proceed to the fake payment page
self.upgrade_page.proceed_to_payment()
# Submit payment
self.fake_payment_page.submit_payment()
# Navigate to the dashboard with the appropriate GET parameter in the URL
self.dashboard_page.visit()
# Expect that we're enrolled as verified in the course
enrollment_mode = self.dashboard_page.get_enrollment_mode(self.course_info["display_name"])
self.assertEqual(enrollment_mode, 'verified')
class LanguageTest(WebAppTest):
"""
Tests that the change language functionality on the dashboard works
......
......@@ -2,6 +2,15 @@
"ANALYTICS_API_KEY": "",
"AWS_ACCESS_KEY_ID": "",
"AWS_SECRET_ACCESS_KEY": "",
"CC_PROCESSOR_NAME": "CyberSource2",
"CC_PROCESSOR": {
"CyberSource2": {
"SECRET_KEY": "abcd123",
"ACCESS_KEY": "abcd123",
"PROFILE_ID": "edx",
"PURCHASE_ENDPOINT": "/shoppingcart/payment_fake"
}
},
"CELERY_BROKER_PASSWORD": "celery",
"CELERY_BROKER_USER": "celery",
"CONTENTSTORE": {
......
......@@ -65,15 +65,21 @@
"FEATURES": {
"AUTH_USE_OPENID_PROVIDER": true,
"CERTIFICATES_ENABLED": true,
"MULTIPLE_ENROLLMENT_ROLES": true,
"ENABLE_PAYMENT_FAKE": true,
"ENABLE_VERIFIED_CERTIFICATES": true,
"ENABLE_DISCUSSION_SERVICE": true,
"ENABLE_INSTRUCTOR_ANALYTICS": true,
"ENABLE_S3_GRADE_DOWNLOADS": true,
"ENABLE_THIRD_PARTY_AUTH": true,
"ENABLE_COMBINED_LOGIN_REGISTRATION": true,
"SEPARATE_VERIFICATION_FROM_PAYMENT": true,
"PREVIEW_LMS_BASE": "localhost:8003",
"SUBDOMAIN_BRANDING": false,
"SUBDOMAIN_COURSE_LISTINGS": false,
"ALLOW_AUTOMATED_SIGNUPS": true
"ALLOW_AUTOMATED_SIGNUPS": true,
"MODE_CREATION_FOR_TESTING": true,
"AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING": true
},
"FEEDBACK_SUBMISSION_EMAIL": "",
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
......
......@@ -312,7 +312,7 @@ FEATURES = {
# Show the mobile app links in the footer
'ENABLE_FOOTER_MOBILE_APP_LINKS': False,
# let students save and manage their annotations
# Let students save and manage their annotations
'ENABLE_EDXNOTES': False,
# Milestones application flag
......@@ -320,6 +320,9 @@ FEATURES = {
# Prerequisite courses feature flag
'ENABLE_PREREQUISITE_COURSES': False,
# For easily adding modes to courses during acceptance testing
'MODE_CREATION_FOR_TESTING': False,
}
# Ignore static asset files on import which match this pattern
......
......@@ -70,6 +70,12 @@ var edx = edx || {};
platformName: el.data('platform-name'),
requirements: el.data('requirements')
},
'face-photo-step': {
platformName: el.data('platform-name')
},
'id-photo-step': {
platformName: el.data('platform-name')
},
'review-photos-step': {
fullName: el.data('full-name'),
platformName: el.data('platform-name')
......@@ -79,12 +85,6 @@ var edx = edx || {};
courseStartDate: el.data('course-start-date'),
coursewareUrl: el.data('courseware-url'),
platformName: el.data('platform-name')
},
'face-photo-step': {
platformName: el.data('platform-name')
},
'id-photo-step': {
platformName: el.data('platform-name')
}
}
}).render();
......
......@@ -25,7 +25,14 @@
// Start the capture
this.getUserMediaFunc()(
{ video: true },
{
video: true,
// Specify the `fake` constraint if we detect we are running in a test
// environment. In Chrome, this will do nothing, but in Firefox, it will
// instruct the browser to use a fake video device.
fake: window.location.hostname === 'localhost'
},
_.bind( this.getUserMediaCallback, this ),
_.bind( this.handleVideoFailure, this )
);
......
......@@ -25,7 +25,7 @@ git+https://github.com/mitocw/django-cas.git@60a5b8e5a62e63e0d5d224a87f0b489201a
-e git+https://github.com/edx/codejail.git@2b095e820ff752a108653bb39d518b122f7154db#egg=codejail
-e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool
-e git+https://github.com/edx/event-tracking.git@0.1.0#egg=event-tracking
-e git+https://github.com/edx/bok-choy.git@4a259e3548a19e41cc39433caf68ea58d10a27ba#egg=bok_choy
-e git+https://github.com/edx/bok-choy.git@9fd05383dc434e0275c9874fd50ad14e519448bc#egg=bok_choy
-e git+https://github.com/edx-solutions/django-splash.git@7579d052afcf474ece1239153cffe1c89935bc4f#egg=django-splash
-e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock
-e git+https://github.com/edx/edx-ora2.git@release-2015-01-05T12.24#egg=edx-ora2
......
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