chore: remove IDV references for proctoring and student/instructor display (#30146)

Places where IDV is referenced for blocking proctoring or displaying the IDV status to students and instructors should be removed. This includes the use of the ENABLE_INTEGRITY_SIGNATURE setting, which was used in part to deprecate IDV in those specified places. Other areas where ENABLE_INTEGRITY_SIGNATURE is used (such as for showing/hiding the integrity signature, for the verification deadline, or for certs) shall be left as is due to deprecation concerns.
This commit is contained in:
alangsto
2022-04-04 08:11:40 -07:00
committed by GitHub
parent 3abd1c5d9f
commit 681c2f1d6c
18 changed files with 51 additions and 780 deletions

View File

@@ -10,7 +10,6 @@ from requests.exceptions import ConnectionError, Timeout # pylint: disable=rede
from slumber.exceptions import SlumberBaseException
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.student.helpers import VERIFY_STATUS_APPROVED, VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED # lint-amnesty, pylint: disable=line-too-long
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
DISPLAY_VERIFIED = "verified"
@@ -21,7 +20,7 @@ DISPLAY_PROFESSIONAL = "professional"
LOGGER = logging.getLogger(__name__)
def enrollment_mode_display(mode, verification_status, course_id):
def enrollment_mode_display(mode, course_id):
""" Select appropriate display strings and CSS classes.
Uses mode and verification status to select appropriate display strings and CSS classes
@@ -38,19 +37,13 @@ def enrollment_mode_display(mode, verification_status, course_id):
image_alt = ''
enrollment_title = ''
enrollment_value = ''
display_mode = _enrollment_mode_display(mode, verification_status, course_id)
display_mode = _enrollment_mode_display(mode, course_id)
if display_mode == DISPLAY_VERIFIED:
if settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE') or verification_status == VERIFY_STATUS_APPROVED:
enrollment_title = _("You're enrolled as a verified student")
enrollment_value = _("Verified")
show_image = True
image_alt = _("ID Verified Ribbon/Badge")
elif verification_status in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED]:
enrollment_title = _("Your verification is pending")
enrollment_value = _("Verified: Pending Verification")
show_image = True
image_alt = _("ID verification pending")
enrollment_title = _("You're enrolled as a verified student")
enrollment_value = _("Verified")
show_image = True
image_alt = _("ID Verified Ribbon/Badge")
elif display_mode == DISPLAY_HONOR:
enrollment_title = _("You're enrolled as an honor code student")
enrollment_value = _("Honor Code")
@@ -67,7 +60,7 @@ def enrollment_mode_display(mode, verification_status, course_id):
}
def _enrollment_mode_display(enrollment_mode, verification_status, course_id):
def _enrollment_mode_display(enrollment_mode, course_id):
"""Checking enrollment mode and status and returns the display mode
Args:
enrollment_mode (str): enrollment mode.
@@ -79,15 +72,9 @@ def _enrollment_mode_display(enrollment_mode, verification_status, course_id):
course_mode_slugs = [mode.slug for mode in CourseMode.modes_for_course(course_id)]
if enrollment_mode == CourseMode.VERIFIED:
if (
settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE')
or verification_status in [VERIFY_STATUS_NEED_TO_VERIFY, VERIFY_STATUS_SUBMITTED, VERIFY_STATUS_APPROVED]
):
display_mode = DISPLAY_VERIFIED
elif DISPLAY_HONOR in course_mode_slugs:
display_mode = DISPLAY_VERIFIED
if DISPLAY_HONOR in course_mode_slugs:
display_mode = DISPLAY_HONOR
else:
display_mode = DISPLAY_AUDIT
elif enrollment_mode in [CourseMode.PROFESSIONAL, CourseMode.NO_ID_PROFESSIONAL_MODE]:
display_mode = DISPLAY_PROFESSIONAL
else:

View File

@@ -11,7 +11,6 @@ from datetime import timedelta
from unittest.mock import patch
import ddt
from django.conf import settings
from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings
from django.utils.timezone import now
@@ -342,49 +341,15 @@ class CourseModeModelTest(TestCase):
assert exc.messages == ['Professional education modes are not allowed to have expiration_datetime set.']
@ddt.data(
("verified", "verify_need_to_verify", True),
("verified", "verify_submitted", True),
("verified", "verify_approved", True),
("verified", 'dummy', True),
("verified", None, True),
('honor', None, True),
('honor', 'dummy', True),
('audit', None, True),
('professional', None, True),
('no-id-professional', None, True),
('no-id-professional', 'dummy', True),
("verified", "verify_need_to_verify", False),
("verified", "verify_submitted", False),
("verified", "verify_approved", False),
("verified", 'dummy', False),
("verified", None, False),
('honor', None, False),
('honor', 'dummy', False),
('audit', None, False),
('professional', None, False),
('no-id-professional', None, False),
('no-id-professional', 'dummy', False)
"verified",
"honor",
"audit",
"professional",
"no-id-professional",
)
@ddt.unpack
def test_enrollment_mode_display(self, mode, verification_status, enable_integrity_signature):
with patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature):
if mode == "verified":
assert enrollment_mode_display(mode, verification_status, self.course_key) ==\
self._enrollment_display_modes_dicts(mode, verification_status, enable_integrity_signature)
assert enrollment_mode_display(mode, verification_status, self.course_key) ==\
self._enrollment_display_modes_dicts(mode, verification_status, enable_integrity_signature)
assert enrollment_mode_display(mode, verification_status, self.course_key) ==\
self._enrollment_display_modes_dicts(mode, verification_status, enable_integrity_signature)
elif mode == "honor":
assert enrollment_mode_display(mode, verification_status, self.course_key) ==\
self._enrollment_display_modes_dicts(mode, mode, enable_integrity_signature)
elif mode == "audit":
assert enrollment_mode_display(mode, verification_status, self.course_key) ==\
self._enrollment_display_modes_dicts(mode, mode, enable_integrity_signature)
elif mode == "professional":
assert enrollment_mode_display(mode, verification_status, self.course_key) ==\
self._enrollment_display_modes_dicts(mode, mode, enable_integrity_signature)
def test_enrollment_mode_display(self, mode):
assert enrollment_mode_display(mode, self.course_key) == \
self._enrollment_display_modes_dicts(mode)
@ddt.data(
(['honor', 'verified', 'credit'], ['honor', 'verified']),
@@ -408,30 +373,23 @@ class CourseModeModelTest(TestCase):
all_modes = CourseMode.modes_for_course_dict(self.course_key, only_selectable=False)
self.assertCountEqual(list(all_modes.keys()), available_modes)
def _enrollment_display_modes_dicts(self, mode, dict_type, enable_flag):
def _enrollment_display_modes_dicts(self, mode):
"""
Helper function to generate the enrollment display mode dict.
"""
dict_keys = ['enrollment_title', 'enrollment_value', 'show_image', 'image_alt', 'display_mode']
display_values = {
"verify_need_to_verify": ["Your verification is pending", "Verified: Pending Verification", True,
'ID verification pending', 'verified'],
"verify_approved": ["You're enrolled as a verified student", "Verified", True, 'ID Verified Ribbon/Badge',
'verified'],
"verify_none": ["", "", False, '', 'audit'],
"verified": ["You're enrolled as a verified student", "Verified", True, 'ID Verified Ribbon/Badge',
'verified'],
"honor": ["You're enrolled as an honor code student", "Honor Code", False, '', 'honor'],
"audit": ["", "", False, '', 'audit'],
"professional": ["You're enrolled as a professional education student", "Professional Ed", False, '',
'professional']
'professional'],
"no-id-professional": ["You're enrolled as a professional education student", "Professional Ed", False, '',
'professional'],
}
if mode == 'verified' and enable_flag:
return dict(list(zip(dict_keys, display_values.get('verify_approved'))))
elif dict_type in ['verify_need_to_verify', 'verify_submitted']:
return dict(list(zip(dict_keys, display_values.get('verify_need_to_verify'))))
elif dict_type is None or dict_type == 'dummy':
return dict(list(zip(dict_keys, display_values.get('verify_none'))))
else:
return dict(list(zip(dict_keys, display_values.get(dict_type))))
return dict(list(zip(dict_keys, display_values.get(mode))))
def test_expiration_datetime_explicitly_set(self):
""" Verify that setting the expiration_date property sets the explicit flag. """

View File

@@ -29,7 +29,6 @@ from common.djangoapps.student.views import (
)
from common.djangoapps.third_party_auth.views import inactive_user_view
from common.djangoapps.util.testing import EventTestMixin
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.ace_common.tests.mixins import EmailTemplateTagMixin
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
@@ -237,7 +236,6 @@ class ProctoringRequirementsEmailTests(EmailTemplateTagMixin, ModuleStoreTestCas
send_proctoring_requirements_email(context)
self._assert_email()
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_INTEGRITY_SIGNATURE': True})
def test_send_proctoring_requirements_email_honor(self):
self.course = CourseFactory(
display_name='honor code on course',
@@ -259,13 +257,10 @@ class ProctoringRequirementsEmailTests(EmailTemplateTagMixin, ModuleStoreTestCas
assert message.subject == f"Proctoring requirements for {self.course.display_name}"
appears, does_not_appear = self._get_fragments()
appears = self._get_fragments()
for fragment in appears:
self.assertIn(fragment, text)
self.assertIn(fragment, html)
for fragment in does_not_appear:
self.assertNotIn(fragment, text)
self.assertNotIn(fragment, html)
def _get_fragments(self):
"""
@@ -273,7 +268,6 @@ class ProctoringRequirementsEmailTests(EmailTemplateTagMixin, ModuleStoreTestCas
"""
course_module = modulestore().get_course(self.course.id)
proctoring_provider = capwords(course_module.proctoring_provider.replace('_', ' '))
id_verification_url = IDVerificationService.get_verify_location()
fragments = [
(
"You are enrolled in {} at {}. This course contains proctored exams.".format(
@@ -292,14 +286,7 @@ class ProctoringRequirementsEmailTests(EmailTemplateTagMixin, ModuleStoreTestCas
),
settings.PROCTORING_SETTINGS.get('LINK_URLS', {}).get('faq', ''),
]
idv_fragments = [
escape("Before taking a graded proctored exam, you must have approved ID verification photos."),
id_verification_url,
]
if not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE'):
fragments.extend(idv_fragments)
return (fragments, [])
return (fragments, idv_fragments)
return fragments
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', "Test only valid in LMS")

View File

@@ -1,469 +0,0 @@
"""Tests for per-course verification status on the dashboard. """
import unittest
from datetime import datetime, timedelta
from unittest.mock import patch
import ddt
import six
from django.conf import settings
from django.test import override_settings
from django.urls import reverse
from django.utils.timezone import now
from pytz import UTC
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.helpers import (
VERIFY_STATUS_APPROVED,
VERIFY_STATUS_MISSED_DEADLINE,
VERIFY_STATUS_NEED_TO_REVERIFY,
VERIFY_STATUS_NEED_TO_VERIFY,
VERIFY_STATUS_RESUBMITTED,
VERIFY_STATUS_SUBMITTED
)
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
@override_settings(PLATFORM_NAME='edX')
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.ddt
class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
"""Tests for per-course verification status on the dashboard. """
PAST = 'past'
FUTURE = 'future'
DATES = {
PAST: datetime.now(UTC) - timedelta(days=5),
FUTURE: datetime.now(UTC) + timedelta(days=5),
None: None,
}
URLCONF_MODULES = ['lms.djangoapps.verify_student.urls']
def setUp(self):
# Invoke UrlResetMixin
super().setUp()
self.user = UserFactory(password="edx")
self.course = CourseFactory.create()
success = self.client.login(username=self.user.username, password="edx")
assert success, 'Did not log in successfully'
self.dashboard_url = reverse('dashboard')
def test_enrolled_as_non_verified(self):
self._setup_mode_and_enrollment(None, "audit")
# Expect that the course appears on the dashboard
# without any verification messaging
self._assert_course_verification_status(None)
def test_no_verified_mode_available(self):
# Enroll the student in a verified mode, but don't
# create any verified course mode.
# This won't happen unless someone deletes a course mode,
# but if so, make sure we handle it gracefully.
CourseEnrollmentFactory(
course_id=self.course.id,
user=self.user,
mode="verified"
)
# Continue to show the student as needing to verify.
# The student is enrolled as verified, so we might as well let them
# complete verification. We'd need to change their enrollment mode
# anyway to ensure that the student is issued the correct kind of certificate.
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_VERIFY)
def test_need_to_verify_no_expiration(self):
self._setup_mode_and_enrollment(None, "verified")
# Since the student has not submitted a photo verification,
# the student should see a "need to verify" message
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_VERIFY)
# Start the photo verification process, but do not submit
# Since we haven't submitted the verification, we should still
# see the "need to verify" message
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_VERIFY)
# Upload images, but don't submit to the verification service
# We should still need to verify
attempt.mark_ready()
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_VERIFY)
def test_need_to_verify_expiration(self):
self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
response = self.client.get(self.dashboard_url)
self.assertContains(response, self.BANNER_ALT_MESSAGES[VERIFY_STATUS_NEED_TO_VERIFY])
self.assertContains(response, "You only have 4 days left to verify for this course.")
@ddt.data(None, FUTURE)
def test_waiting_approval(self, expiration):
self._setup_mode_and_enrollment(self.DATES[expiration], "verified")
# The student has submitted a photo verification
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
# Now the student should see a "verification submitted" message
self._assert_course_verification_status(VERIFY_STATUS_SUBMITTED)
@ddt.data(None, FUTURE)
def test_fully_verified(self, expiration):
self._setup_mode_and_enrollment(self.DATES[expiration], "verified")
# The student has an approved verification
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
# Expect that the successfully verified message is shown
self._assert_course_verification_status(VERIFY_STATUS_APPROVED)
# Check that the "verification good until" date is displayed
response = self.client.get(self.dashboard_url)
self.assertContains(response, attempt.expiration_datetime.strftime("%m/%d/%Y"))
@patch("lms.djangoapps.verify_student.services.is_verification_expiring_soon")
def test_verify_resubmit_button_on_dashboard(self, mock_expiry):
mock_expiry.return_value = True
SoftwareSecurePhotoVerification.objects.create(
user=self.user,
status='approved',
expiration_date=now() + timedelta(days=1)
)
response = self.client.get(self.dashboard_url)
self.assertContains(response, "Resubmit Verification")
mock_expiry.return_value = False
response = self.client.get(self.dashboard_url)
self.assertNotContains(response, "Resubmit Verification")
def test_missed_verification_deadline(self):
# Expiration date in the past
self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
# The student does NOT have an approved verification
# so the status should show that the student missed the deadline.
self._assert_course_verification_status(VERIFY_STATUS_MISSED_DEADLINE)
def test_missed_verification_deadline_verification_was_expired(self):
# Expiration date in the past
self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
# Create a verification, but the expiration date of the verification
# occurred before the deadline.
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
attempt.expiration_date = self.DATES[self.PAST] - timedelta(days=900)
attempt.save()
# The student didn't have an approved verification at the deadline,
# so we should show that the student missed the deadline.
self._assert_course_verification_status(VERIFY_STATUS_MISSED_DEADLINE)
def test_missed_verification_deadline_but_later_verified(self):
# Expiration date in the past
self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
# Successfully verify, but after the deadline has already passed
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
attempt.expiration_date = self.DATES[self.PAST] - timedelta(days=900)
attempt.save()
# The student didn't have an approved verification at the deadline,
# so we should show that the student missed the deadline.
self._assert_course_verification_status(VERIFY_STATUS_MISSED_DEADLINE)
def test_verification_denied(self):
# Expiration date in the future
self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
# Create a verification with the specified status
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.deny("Not valid!")
# Since this is not a status we handle, don't display any
# messaging relating to verification
self._assert_course_verification_status(None)
def test_verification_error(self):
# Expiration date in the future
self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
# Create a verification with the specified status
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.status = "must_retry"
attempt.system_error("Error!")
# Since this is not a status we handle, don't display any
# messaging relating to verification
self._assert_course_verification_status(None)
@override_settings(VERIFY_STUDENT={"DAYS_GOOD_FOR": 5, "EXPIRING_SOON_WINDOW": 10})
def test_verification_will_expire_by_deadline(self):
# Expiration date in the future
self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
# Create a verification attempt that:
# 1) Is current (submitted in the last year)
# 2) Will expire by the deadline for the course
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
attempt.save()
# Verify that learner can submit photos if verification is set to expire soon.
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_REVERIFY)
@override_settings(VERIFY_STUDENT={"DAYS_GOOD_FOR": 5, "EXPIRING_SOON_WINDOW": 10})
def test_reverification_submitted_with_current_approved_verificaiton(self):
# Expiration date in the future
self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
# Create a verification attempt that is approved but expiring soon
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
attempt.save()
# Verify that learner can submit photos if verification is set to expire soon.
self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_REVERIFY)
# Submit photos for reverification
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
# Expect that learner has submitted photos for reverfication and their
# previous verification is set to expired soon.
self._assert_course_verification_status(VERIFY_STATUS_RESUBMITTED)
def test_verification_occurred_after_deadline(self):
# Expiration date in the past
self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
# The deadline has passed, and we've asked the student
# to reverify (through the support team).
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
# Expect that the user's displayed enrollment mode is verified.
self._assert_course_verification_status(VERIFY_STATUS_APPROVED)
def test_with_two_verifications(self):
# checking if a user has two verification and but most recent verification course deadline is expired
self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
# The student has an approved verification
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
# Making created at to previous date to differentiate with 2nd attempt.
attempt.created_at = datetime.now(UTC) - timedelta(days=1)
attempt.save()
# Expect that the successfully verified message is shown
self._assert_course_verification_status(VERIFY_STATUS_APPROVED)
# Check that the "verification good until" date is displayed
response = self.client.get(self.dashboard_url)
self.assertContains(response, attempt.expiration_datetime.strftime("%m/%d/%Y"))
# Adding another verification with different course.
# Its created_at is greater than course deadline.
course2 = CourseFactory.create()
CourseModeFactory.create(
course_id=course2.id,
mode_slug="verified",
expiration_datetime=self.DATES[self.PAST]
)
CourseEnrollmentFactory(
course_id=course2.id,
user=self.user,
mode="verified"
)
# The student has an approved verification
attempt2 = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt2.mark_ready()
attempt2.submit()
attempt2.approve()
attempt2.save()
# Mark the attemp2 as approved so its date will appear on dasboard.
self._assert_course_verification_status(VERIFY_STATUS_APPROVED)
response2 = self.client.get(self.dashboard_url)
self.assertContains(response2, attempt2.expiration_datetime.strftime("%m/%d/%Y"))
self.assertContains(response2, attempt2.expiration_datetime.strftime("%m/%d/%Y"), count=2)
@patch.dict(settings.FEATURES, {'ENABLE_INTEGRITY_SIGNATURE': True})
@ddt.data(
None,
'past',
'future'
)
def test_verify_message_idv_disabled(self, deadline_key):
if deadline_key:
self._setup_mode_and_enrollment(self.DATES[deadline_key], "verified")
else:
self._setup_mode_and_enrollment(None, "verified")
self._assert_course_verification_status(None, "verified")
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
self._assert_course_verification_status(None, "verified")
attempt.mark_ready()
self._assert_course_verification_status(None, "verified")
attempt.submit()
self._assert_course_verification_status(None, "verified")
attempt.approve()
self._assert_course_verification_status(None, "verified")
attempt.expiration_date = self.DATES[self.PAST] - timedelta(days=900)
attempt.save()
self._assert_course_verification_status(None, "verified")
@ddt.data(True, False)
def test_integrity_disables_sidebar(self, enable_integrity_signature):
self._setup_mode_and_enrollment(None, "verified")
#no sidebar when no IDV yet
response = self.client.get(self.dashboard_url)
self.assertNotContains(response, "profile-sidebar")
# The student has an approved verification
attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
attempt.mark_ready()
attempt.submit()
attempt.approve()
# sidebar only appears after IDV if integrity signature setting is not on
with patch.dict(settings.FEATURES, {'ENABLE_INTEGRITY_SIGNATURE': enable_integrity_signature}):
response = self.client.get(self.dashboard_url)
if enable_integrity_signature:
self.assertNotContains(response, "profile-sidebar")
else:
self.assertContains(response, "profile-sidebar")
def _setup_mode_and_enrollment(self, deadline, enrollment_mode):
"""Create a course mode and enrollment.
Arguments:
deadline (datetime): The deadline for submitting your verification.
enrollment_mode (str): The mode of the enrollment.
"""
CourseModeFactory.create(
course_id=self.course.id,
mode_slug="verified",
expiration_datetime=deadline
)
CourseEnrollmentFactory(
course_id=self.course.id,
user=self.user,
mode=enrollment_mode
)
VerificationDeadline.set_deadline(self.course.id, deadline)
BANNER_ALT_MESSAGES = {
VERIFY_STATUS_NEED_TO_VERIFY: "ID verification pending",
VERIFY_STATUS_SUBMITTED: "ID verification pending",
VERIFY_STATUS_APPROVED: "ID Verified Ribbon/Badge",
}
NOTIFICATION_MESSAGES = {
VERIFY_STATUS_NEED_TO_VERIFY: [
"You still need to verify for this course.",
"Verification not yet complete"
],
VERIFY_STATUS_SUBMITTED: ["You have submitted your verification information."],
VERIFY_STATUS_RESUBMITTED: ["You have submitted your reverification information."],
VERIFY_STATUS_APPROVED: ["You have successfully verified your ID with edX"],
VERIFY_STATUS_NEED_TO_REVERIFY: ["Your current verification will expire soon."]
}
MODE_CLASSES = {
None: "audit",
VERIFY_STATUS_NEED_TO_VERIFY: "verified",
VERIFY_STATUS_SUBMITTED: "verified",
VERIFY_STATUS_APPROVED: "verified",
VERIFY_STATUS_MISSED_DEADLINE: "audit",
VERIFY_STATUS_NEED_TO_REVERIFY: "audit",
VERIFY_STATUS_RESUBMITTED: "audit"
}
def _assert_course_verification_status(self, status, enrollment_mode=None):
"""Check whether the specified verification status is shown on the dashboard.
Arguments:
status (str): One of the verification status constants.
If None, check that *none* of the statuses are displayed.
Raises:
AssertionError
"""
response = self.client.get(self.dashboard_url)
# Sanity check: verify that the course is on the page
self.assertContains(response, str(self.course.id))
# Verify that the correct banner is rendered on the dashboard
alt_text = self.BANNER_ALT_MESSAGES.get(status)
if alt_text:
self.assertContains(response, alt_text)
mode = enrollment_mode if enrollment_mode else self.MODE_CLASSES[status]
# Verify that the correct banner color is rendered
self.assertContains(
response,
f"<article class=\"course {mode}\""
)
# Verify that the correct copy is rendered on the dashboard
if status is not None:
if status in self.NOTIFICATION_MESSAGES:
# Different states might have different messaging
# so in some cases we check several possibilities
# and fail if none of these are found.
found_msg = False
for message in self.NOTIFICATION_MESSAGES[status]:
if six.b(message) in response.content:
found_msg = True
break
fail_msg = "Could not find any of these messages: {expected}".format(
expected=self.NOTIFICATION_MESSAGES[status]
)
assert found_msg, fail_msg
else:
# Combine all possible messages into a single list
all_messages = []
for msg_group in self.NOTIFICATION_MESSAGES.values():
all_messages.extend(msg_group)
# Verify that none of the messages are displayed
for msg in all_messages:
self.assertNotContains(response, msg)

View File

@@ -748,11 +748,6 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem
else:
redirect_message = ''
valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired']
display_sidebar_on_dashboard = not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE') and \
verification_status['status'] in valid_verification_statuses and \
verification_status['should_display']
# Filter out any course enrollment course cards that are associated with fulfilled entitlements
for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]:
course_enrollments = [
@@ -804,7 +799,6 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem
'show_dashboard_tabs': True,
'disable_courseware_js': True,
'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard,
'display_sidebar_on_dashboard': display_sidebar_on_dashboard,
'display_sidebar_account_activation_message': not(user.is_active or hide_dashboard_courses_until_activated),
'display_dashboard_courses': (user.is_active or not hide_dashboard_courses_until_activated),
'empty_dashboard_message': empty_dashboard_message,