diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py index 72320ebca8..74e3b4b1b5 100644 --- a/common/djangoapps/course_modes/tests/test_views.py +++ b/common/djangoapps/course_modes/tests/test_views.py @@ -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) diff --git a/common/djangoapps/course_modes/views.py b/common/djangoapps/course_modes/views.py index 249aff9568..79e12a452e 100644 --- a/common/djangoapps/course_modes/views.py +++ b/common/djangoapps/course_modes/views.py @@ -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): + 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. + """ - Given the request object of `user_choice`, return the - corresponding course mode slug - """ - 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 diff --git a/common/templates/course_modes/choose.html b/common/templates/course_modes/choose.html index 0d4c4993f6..1d28a4d972 100644 --- a/common/templates/course_modes/choose.html +++ b/common/templates/course_modes/choose.html @@ -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"/> - -