Merge pull request #10330 from edx/bderusha/unenroll-certs
Conditionally show unenroll action based on certificate status
This commit is contained in:
@@ -18,6 +18,11 @@ VERIFY_STATUS_APPROVED = "verify_approved"
|
||||
VERIFY_STATUS_MISSED_DEADLINE = "verify_missed_deadline"
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY = "verify_need_to_reverify"
|
||||
|
||||
DISABLE_UNENROLL_CERT_STATES = [
|
||||
'generating',
|
||||
'ready',
|
||||
]
|
||||
|
||||
|
||||
def check_verify_status_by_course(user, course_enrollments):
|
||||
"""
|
||||
|
||||
87
common/djangoapps/student/tests/test_views.py
Normal file
87
common/djangoapps/student/tests/test_views.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Test the student dashboard view.
|
||||
"""
|
||||
import ddt
|
||||
import unittest
|
||||
from mock import patch
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory
|
||||
from student.models import CourseEnrollment
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class TestStudentDashboardUnenrollments(ModuleStoreTestCase):
|
||||
"""
|
||||
Test to ensure that the student dashboard does not show the unenroll button for users with certificates.
|
||||
"""
|
||||
USERNAME = "Bob"
|
||||
EMAIL = "bob@example.com"
|
||||
PASSWORD = "edx"
|
||||
UNENROLL_ELEMENT_ID = "#actions-item-unenroll-0"
|
||||
|
||||
def setUp(self):
|
||||
""" Create a course and user, then log in. """
|
||||
super(TestStudentDashboardUnenrollments, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
|
||||
CourseEnrollmentFactory(course_id=self.course.id, user=self.user)
|
||||
self.cert_status = None
|
||||
self.client.login(username=self.USERNAME, password=self.PASSWORD)
|
||||
|
||||
def mock_cert(self, _user, _course_overview, _course_mode): # pylint: disable=unused-argument
|
||||
""" Return a preset certificate status. """
|
||||
if self.cert_status is not None:
|
||||
return {'status': self.cert_status}
|
||||
else:
|
||||
return {}
|
||||
|
||||
@ddt.data(
|
||||
('notpassing', 1),
|
||||
('restricted', 1),
|
||||
('processing', 1),
|
||||
(None, 1),
|
||||
('generating', 0),
|
||||
('ready', 0),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_unenroll_available(self, cert_status, unenroll_action_count):
|
||||
""" Assert that the unenroll action is shown or not based on the cert status."""
|
||||
self.cert_status = cert_status
|
||||
|
||||
with patch('student.views.cert_info', side_effect=self.mock_cert):
|
||||
response = self.client.get(reverse('dashboard'))
|
||||
|
||||
self.assertEqual(pq(response.content)(self.UNENROLL_ELEMENT_ID).length, unenroll_action_count)
|
||||
|
||||
@ddt.data(
|
||||
('notpassing', 200),
|
||||
('restricted', 200),
|
||||
('processing', 200),
|
||||
(None, 200),
|
||||
('generating', 400),
|
||||
('ready', 400),
|
||||
)
|
||||
@ddt.unpack
|
||||
@patch.object(CourseEnrollment, 'unenroll')
|
||||
def test_unenroll_request(self, cert_status, status_code, course_enrollment):
|
||||
""" Assert that the unenroll method is called or not based on the cert status"""
|
||||
self.cert_status = cert_status
|
||||
|
||||
with patch('student.views.cert_info', side_effect=self.mock_cert):
|
||||
response = self.client.post(
|
||||
reverse('change_enrollment'),
|
||||
{'enrollment_action': 'unenroll', 'course_id': self.course.id}
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
if status_code == 200:
|
||||
course_enrollment.assert_called_with(self.user, self.course.id)
|
||||
else:
|
||||
course_enrollment.assert_not_called()
|
||||
@@ -106,7 +106,8 @@ import third_party_auth
|
||||
from third_party_auth import pipeline, provider
|
||||
from student.helpers import (
|
||||
check_verify_status_by_course,
|
||||
auth_pipeline_urls, get_next_url_for_login_page
|
||||
auth_pipeline_urls, get_next_url_for_login_page,
|
||||
DISABLE_UNENROLL_CERT_STATES,
|
||||
)
|
||||
from student.cookies import set_logged_in_cookies, delete_logged_in_cookies
|
||||
from student.models import anonymous_id_for_user
|
||||
@@ -1014,8 +1015,14 @@ def change_enrollment(request, check_access=True):
|
||||
# Otherwise, there is only one mode available (the default)
|
||||
return HttpResponse()
|
||||
elif action == "unenroll":
|
||||
if not CourseEnrollment.is_enrolled(user, course_id):
|
||||
enrollment = CourseEnrollment.get_enrollment(user, course_id)
|
||||
if not enrollment:
|
||||
return HttpResponseBadRequest(_("You are not enrolled in this course"))
|
||||
|
||||
certicifate_info = cert_info(user, enrollment.course_overview, enrollment.mode)
|
||||
if certicifate_info.get('status') in DISABLE_UNENROLL_CERT_STATES:
|
||||
return HttpResponseBadRequest(_("Your certificate prevents you from unenrolling from this course"))
|
||||
|
||||
CourseEnrollment.unenroll(user, course_id)
|
||||
return HttpResponse()
|
||||
else:
|
||||
|
||||
@@ -14,7 +14,8 @@ from student.helpers import (
|
||||
VERIFY_STATUS_SUBMITTED,
|
||||
VERIFY_STATUS_APPROVED,
|
||||
VERIFY_STATUS_MISSED_DEADLINE,
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY
|
||||
VERIFY_STATUS_NEED_TO_REVERIFY,
|
||||
DISABLE_UNENROLL_CERT_STATES,
|
||||
)
|
||||
%>
|
||||
|
||||
@@ -171,7 +172,8 @@ from student.helpers import (
|
||||
</a>
|
||||
<div class="actions-dropdown" id="actions-dropdown-${dashboard_index}" aria-label="${_('Additional Actions Menu')}">
|
||||
<ul class="actions-dropdown-list" id="actions-dropdown-list-${dashboard_index}" aria-label="${_('Available Actions')}" role="menu">
|
||||
<li class="actions-item" id="actions-item-unenroll-${dashboard_index}">
|
||||
% if cert_status.get('status') not in DISABLE_UNENROLL_CERT_STATES:
|
||||
<li class="actions-item" id="actions-item-unenroll-${dashboard_index}">
|
||||
% if is_paid_course and show_refund_option:
|
||||
## Translators: The course name will be added to the end of this sentence.
|
||||
% if not is_course_blocked:
|
||||
@@ -255,6 +257,7 @@ from student.helpers import (
|
||||
% endif
|
||||
% endif
|
||||
</li>
|
||||
% endif
|
||||
<li class="actions-item" id="actions-item-email-settings-${dashboard_index}">
|
||||
% if show_email_settings:
|
||||
% if not is_course_blocked:
|
||||
|
||||
Reference in New Issue
Block a user