Commit cc6e63dd by Renzo Lucioni

Prepare track selection page for new audit track

parent dedd88cc
......@@ -32,7 +32,7 @@ class CourseModeViewTest(ModuleStoreTestCase):
self.client.login(username=self.user.username, password="edx")
@ddt.data(
# is_active?, enrollment_mode, upgrade?, redirect? auto_register?
# is_active?, enrollment_mode, upgrade?, redirect?, auto_register?
(True, 'verified', True, True, False), # User is already verified
(True, 'verified', False, True, False), # User is already verified
(True, 'honor', True, False, False), # User isn't trying to upgrade
......@@ -162,9 +162,9 @@ class CourseModeViewTest(ModuleStoreTestCase):
# Mapping of course modes to the POST parameters sent
# when the user chooses that mode.
POST_PARAMS_FOR_COURSE_MODE = {
'audit': {'audit_mode': True},
'honor': {'honor-code': True},
'verified': {'certificate_mode': True, 'contribution': '1.23'}
'honor': {'honor_mode': True},
'verified': {'verified_mode': True, 'contribution': '1.23'},
'unsupported': {'unsupported_mode': True},
}
# TODO (ECOM-16): Remove the auto-register flag once the AB-test completes
......@@ -172,10 +172,8 @@ class CourseModeViewTest(ModuleStoreTestCase):
@ddt.data(
(False, 'honor', 'dashboard'),
(False, 'verified', 'show_requirements'),
(False, 'audit', 'dashboard'),
(True, 'honor', 'dashboard'),
(True, 'verified', 'show_requirements'),
(True, 'audit', 'dashboard'),
)
@ddt.unpack
def test_choose_mode_redirect(self, auto_register, course_mode, expected_redirect):
......@@ -191,7 +189,7 @@ class CourseModeViewTest(ModuleStoreTestCase):
# Choose the mode (POST request)
choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)])
resp = self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE[course_mode])
response = self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE[course_mode])
# Verify the redirect
if expected_redirect == 'dashboard':
......@@ -204,11 +202,11 @@ class CourseModeViewTest(ModuleStoreTestCase):
else:
self.fail("Must provide a valid redirect URL name")
self.assertRedirects(resp, redirect_url)
self.assertRedirects(response, redirect_url)
def test_remember_donation_for_course(self):
# Create the course modes
for mode in ('audit', 'honor', 'verified'):
for mode in ('honor', 'verified'):
CourseModeFactory(mode_slug=mode, course_id=self.course.id)
# Choose the mode (POST request)
......@@ -223,29 +221,34 @@ class CourseModeViewTest(ModuleStoreTestCase):
expected_amount = decimal.Decimal(self.POST_PARAMS_FOR_COURSE_MODE['verified']['contribution'])
self.assertEqual(actual_amount, expected_amount)
def test_enrollment_skipped_if_autoreg(self):
# TODO (ECOM-16): Remove auto-register booleans once the AB-test completes
@ddt.data(False, True)
def test_successful_honor_enrollment(self, auto_register):
# TODO (ECOM-16): Remove once we complete the auto-reg AB test.
session = self.client.session
session['auto_register'] = True
session.save()
if auto_register:
self.client.session['auto_register'] = True
self.client.session.save()
# Create the course modes
for mode in ('audit', 'honor', 'verified'):
for mode in ('honor', 'verified'):
CourseModeFactory(mode_slug=mode, course_id=self.course.id)
# Now enroll in the course
CourseEnrollmentFactory(
user=self.user,
is_active=True,
mode="honor",
course_id=unicode(self.course.id),
)
# Choose the mode (POST request)
choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)])
self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE['audit'])
self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE['honor'])
# Verify that enrollment mode is still honor
# Verify the enrollment
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
self.assertEqual(mode, "honor")
self.assertEqual(mode, 'honor')
self.assertEqual(is_active, True)
def test_unsupported_enrollment_mode_failure(self):
# Create the supported course modes
for mode in ('honor', 'verified'):
CourseModeFactory(mode_slug=mode, course_id=self.course.id)
# Choose an unsupported mode (POST request)
choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)])
response = self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE['unsupported'])
self.assertEqual(400, response.status_code)
......@@ -21,18 +21,19 @@ from xmodule.modulestore.django import modulestore
class ChooseModeView(View):
"""
View used when the user is asked to pick a mode
"""View used when the user is asked to pick a mode.
When a get request is used, shows the selection page.
When a post request is used, assumes that it is a form submission
from the selection page, parses the response, and then sends user
to the next step in the flow
When a post request is used, assumes that it is a form submission
from the selection page, parses the response, and then sends user
to the next step in the flow.
"""
@method_decorator(login_required)
def get(self, request, course_id, error=None):
""" Displays the course mode choice page
"""Displays the course mode choice page.
Args:
request (`Request`): The Django Request object.
......@@ -121,7 +122,19 @@ class ChooseModeView(View):
@method_decorator(login_required)
def post(self, request, course_id):
""" Takes the form submission from the page and parses it """
"""Takes the form submission from the page and parses it.
Args:
request (`Request`): The Django Request object.
course_id (unicode): The slash-separated course key.
Returns:
Status code 400 when the requested mode is unsupported. When the honor mode
is selected, redirects to the dashboard. When the verified mode is selected,
returns error messages if the indicated contribution amount is invalid or
below the minimum, otherwise redirects to the verification flow.
"""
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
user = request.user
......@@ -134,26 +147,26 @@ class ChooseModeView(View):
upgrade = request.GET.get('upgrade', False)
requested_mode = self.get_requested_mode(request.POST)
requested_mode = self._get_requested_mode(request.POST)
allowed_modes = CourseMode.modes_for_course_dict(course_key)
if requested_mode not in allowed_modes:
return HttpResponseBadRequest(_("Enrollment mode not supported"))
if requested_mode in ("audit", "honor"):
# TODO (ECOM-16): Skip enrollment if we're in the experimental branch
if not request.session.get('auto_register', False):
CourseEnrollment.enroll(user, course_key, requested_mode)
# TODO (ECOM-16): Remove if the experimental variant wins. Functionally,
# it doesn't matter, but it will avoid hitting the database.
if requested_mode == 'honor':
CourseEnrollment.enroll(user, course_key, requested_mode)
return redirect('dashboard')
mode_info = allowed_modes[requested_mode]
if requested_mode == "verified":
if requested_mode == 'verified':
amount = request.POST.get("contribution") or \
request.POST.get("contribution-other-amt") or 0
try:
# validate the amount passed in and force it into two digits
# Validate the amount passed in and force it into two digits
amount_value = decimal.Decimal(amount).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
except decimal.InvalidOperation:
error_msg = _("Invalid amount selected.")
......@@ -172,14 +185,20 @@ class ChooseModeView(View):
reverse('verify_student_show_requirements',
kwargs={'course_id': course_key.to_deprecated_string()}) + "?upgrade={}".format(upgrade))
def get_requested_mode(self, request_dict):
"""
Given the request object of `user_choice`, return the
corresponding course mode slug
def _get_requested_mode(self, request_dict):
"""Get the user's requested mode
Args:
request_dict (`QueryDict`): A dictionary-like object containing all given HTTP POST parameters.
Returns:
The course mode slug corresponding to the choice in the POST parameters,
None if the choice in the POST parameters is missing or is an unsupported mode.
"""
if 'audit_mode' in request_dict:
return 'audit'
if 'certificate_mode' and request_dict.get("honor-code"):
return 'honor'
if 'certificate_mode' in request_dict:
if 'verified_mode' in request_dict:
return 'verified'
if 'honor_mode' in request_dict:
return 'honor'
else:
return None
......@@ -120,50 +120,14 @@ $(document).ready(function() {
%endif
<%include file="_contribution.html" args="suggested_prices=suggested_prices, currency=currency, chosen_price=chosen_price, min_price=min_price"/>
<div class="help-tip is-expandable">
<h5 class="title title-expand" tabindex="0" aria-expanded="false" role="link">
<i class="icon-caret-down expandable-icon"></i>
${_("Why do I have to pay? What if I want a free honor code certificate?")}
</h5>
<div class="copy expandable-area">
<dl class="list-faq">
<dt class="faq-question">${_("Why do I have to pay?")}</dt>
<dd class="faq-answer">
<p>${_("As a not-for-profit, edX uses your contribution to support our mission to provide quality education to everyone around the world, and to improve learning through research. While we have established a minimum fee, we ask that you contribute as much as you can.")}</p>
</dd>
<dt class="faq-question">${_("I'd like to pay more than the minimum. Is my contribution tax deductible?")}</dt>
<dd class="faq-answer">
<p>${_("Please check with your tax advisor to determine whether your contribution is tax deductible.")}</p>
</dd>
% if "honor" in modes:
<dt class="faq-question">${_("What if I can't afford it or don't have the necessary equipment?")}</dt>
<dd class="faq-answer">
<p>${_("If you can't afford the minimum fee or don't meet the requirements, you can audit the course or elect to pursue an honor code certificate at no cost.")}
${_("If you would like to pursue the honor code certificate, please check the honor code certificate box and then click the 'Select Certificate' button to complete your registration.")}</p>
<ul class="list-fields">
<li class="field field-honor-code checkbox">
<input type="checkbox" name="honor-code" id="honor-code">
<label for="honor-code">${_("Select Honor Code Certificate")}</label>
</li>
</ul>
</dd>
% endif
</dl>
</div>
</div>
</div>
<ul class="list-actions">
<li class="action action-select">
%if upgrade:
<input type="submit" name="certificate_mode" value="${_('Upgrade Your Registration')}" />
<input type="submit" name="verified_mode" value="${_('Upgrade Your Registration')}" />
%else:
<input type="submit" name="certificate_mode" value="${_('Select Certificate')}" />
<input type="submit" name="verified_mode" value="${_('Select Certificate')}" />
%endif
</li>
......@@ -192,21 +156,22 @@ $(document).ready(function() {
%if not upgrade:
% if "audit" in modes:
% if "honor" in modes:
<span class="deco-divider">
<span class="copy">${_("or")}</span>
</span>
<div class="register-choice register-choice-audit">
<div class="wrapper-copy">
<span class="deco-ribbon"></span>
<h4 class="title">${_("Audit This Course")}</h4>
<div class="copy">
<p>${_("Sign up to audit this course for free and track your own progress.")}</p>
<p>${_("Audit this course for free and have complete access to all the course material, activities, tests, and forums. If your work is satisfactory and you abide by the Honor Code, you'll receive a personalized Honor Code Certificate to showcase your achievement.")}</p>
</div>
</div>
<ul class="list-actions">
<li class="action action-select">
<input type="submit" name="audit_mode" value="${_('Select Audit')}" />
<input type="submit" name="honor_mode" value="${_('Select Audit')}" />
</li>
</ul>
</div>
......@@ -263,9 +228,9 @@ $(document).ready(function() {
<ul class="list-actions">
<li class="action action-select">
%if upgrade:
<input type="submit" name="certificate_mode" value="${_('Upgrade Your Registration')}" />
<input type="submit" name="verified_mode" value="${_('Upgrade Your Registration')}" />
%else:
<input type="submit" name="certificate_mode" value="${_('Pursue a Verified Certificate')}" />
<input type="submit" name="verified_mode" value="${_('Pursue a Verified Certificate')}" />
%endif
</li>
......@@ -275,23 +240,22 @@ $(document).ready(function() {
% endif
%if not upgrade:
## TODO (ECOM-16): For the duration of the experiment, audit track enrollment is skipped
## to keep the user on the honor track
% if "audit" in modes:
% if "honor" in modes:
<span class="deco-divider">
<span class="copy">${_("or")}</span>
</span>
<div class="register-choice register-choice-audit">
<div class="wrapper-copy">
<span class="deco-ribbon"></span>
<h4 class="title">${_("Audit This Course")}</h4>
<div class="copy">
<p>${_("You can audit this course and still have complete access to the course material. If you complete and pass the course you will automatically earn an Honor Code Certificate free of charge.")}</p>
<p>${_("Audit this course for free and have complete access to all the course material, activities, tests, and forums. If your work is satisfactory and you abide by the Honor Code, you'll receive a personalized Honor Code Certificate to showcase your achievement.")}</p>
</div>
</div>
<ul class="list-actions">
<li class="action action-select">
<input type="submit" name="audit_mode" value="${_('Pursue an Honor Code Certificate')}" />
<input type="submit" name="honor_mode" value="${_('Pursue an Honor Code Certificate')}" />
</li>
</ul>
</div>
......
......@@ -68,13 +68,6 @@ Feature: LMS.Verified certificates
# And I press the payment button
# Then I am at the payment page
Scenario: I can take a verified certificate course for free
Given I am logged in
And the course has an honor mode
When I select the honor code option
Then I should see the course on my dashboard
And a "edx.course.enrollment.activated" server event is emitted
Scenario: The upsell offer is on the dashboard if I am auditing
Given I am logged in
When I select the audit track
......
......@@ -20,13 +20,12 @@ def create_cert_course():
world.scenario_dict['COURSE'] = world.CourseFactory.create(
org=org, number=number, display_name=name)
audit_mode = world.CourseModeFactory.create(
course_id=course_id,
mode_slug='audit',
mode_display_name='audit course',
honor_mode = world.CourseModeFactory.create(
course_id=world.scenario_dict['course_id'],
mode_slug='honor',
mode_display_name='honor mode',
min_price=0,
)
assert isinstance(audit_mode, CourseMode)
)
verfied_mode = world.CourseModeFactory.create(
course_id=course_id,
......@@ -36,7 +35,6 @@ def create_cert_course():
suggested_prices='32,64,128',
currency='usd',
)
assert isinstance(verfied_mode, CourseMode)
def register():
......@@ -48,23 +46,11 @@ def register():
assert world.is_css_present('section.wrapper h3.title')
@step(u'the course has an honor mode')
def the_course_has_an_honor_mode(step):
create_cert_course()
honor_mode = world.CourseModeFactory.create(
course_id=world.scenario_dict['course_id'],
mode_slug='honor',
mode_display_name='honor mode',
min_price=0,
)
assert isinstance(honor_mode, CourseMode)
@step(u'I select the audit track$')
def select_the_audit_track(step):
create_cert_course()
register()
btn_css = 'input[value="Select Audit"]'
btn_css = 'input[name="honor_mode"]'
world.wait(1) # TODO remove this after troubleshooting JZ
world.css_find(btn_css)
world.css_click(btn_css)
......@@ -293,17 +279,3 @@ def see_the_payment_page(step):
def edit_my_name(step):
btn_css = 'a.retake-photos'
world.css_click(btn_css)
@step(u'I select the honor code option$')
def give_a_reason_why_i_cannot_pay(step):
register()
link_css = 'h5 i.expandable-icon'
world.css_click(link_css)
cb_css = 'input#honor-code'
world.css_click(cb_css)
btn_css = 'input[value="Select Certificate"]'
world.css_click(btn_css)
......@@ -1250,12 +1250,24 @@
.register-choice-audit {
border-color: $m-blue-d1;
.deco-ribbon {
position: relative;
top: -($baseline*2.5);
right: -($baseline*0.35);
float: right;
display: block;
width: ($baseline*2.9);
height: ($baseline*4.2);
background: transparent url('../images/honor-ribbon.png') no-repeat 0 0;
}
.wrapper-copy {
width: flex-grid(8,8);
}
.list-actions {
width: flex-grid(8,8);
margin: ($baseline/3) 0;
}
.action-select input {
......@@ -1278,8 +1290,7 @@
}
.list-actions {
margin: ($baseline/2) 0;
border-top: ($baseline/10) solid $m-gray-t1;
margin: ($baseline/3) 0;
}
.action-intro, .action-select {
......
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