From cebd85e681ac6c7cf30430fa1faa05bd9424fbaa Mon Sep 17 00:00:00 2001 From: Bill Filler Date: Mon, 5 Nov 2018 11:30:49 -0500 Subject: [PATCH] Add expiration banner to progress page In audit mode, add expiration banner to course progress page REVE-6 --- .../tests/test_field_override_performance.py | 57 ++++++++-------- lms/djangoapps/courseware/tests/test_views.py | 66 +++++++++++++++++-- lms/djangoapps/courseware/views/views.py | 2 + 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/lms/djangoapps/ccx/tests/test_field_override_performance.py b/lms/djangoapps/ccx/tests/test_field_override_performance.py index 3b58229b65..3343931283 100644 --- a/lms/djangoapps/ccx/tests/test_field_override_performance.py +++ b/lms/djangoapps/ccx/tests/test_field_override_performance.py @@ -13,6 +13,7 @@ from lms.djangoapps.courseware.field_overrides import OverrideFieldData from courseware.testutils import FieldOverrideTestMixin from courseware.views.views import progress from django.conf import settings +from django.contrib.messages.storage.fallback import FallbackStorage from django.core.cache import caches from django.test.client import RequestFactory from django.test.utils import override_settings @@ -68,6 +69,8 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT self.request = self.request_factory.get("foo") self.request.session = {} self.request.user = self.student + messages = FallbackStorage(self.request) + self.request._messages = messages # pylint: disable=protected-access patcher = mock.patch('edxmako.request_context.get_current_request', return_value=self.request) patcher.start() @@ -237,18 +240,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase): # # of sql queries to default, # # of mongo queries, # ) - ('no_overrides', 1, True, False): (21, 1), - ('no_overrides', 2, True, False): (21, 1), - ('no_overrides', 3, True, False): (21, 1), - ('ccx', 1, True, False): (21, 1), - ('ccx', 2, True, False): (21, 1), - ('ccx', 3, True, False): (21, 1), - ('no_overrides', 1, False, False): (21, 1), - ('no_overrides', 2, False, False): (21, 1), - ('no_overrides', 3, False, False): (21, 1), - ('ccx', 1, False, False): (21, 1), - ('ccx', 2, False, False): (21, 1), - ('ccx', 3, False, False): (21, 1), + ('no_overrides', 1, True, False): (24, 1), + ('no_overrides', 2, True, False): (24, 1), + ('no_overrides', 3, True, False): (24, 1), + ('ccx', 1, True, False): (24, 1), + ('ccx', 2, True, False): (24, 1), + ('ccx', 3, True, False): (24, 1), + ('no_overrides', 1, False, False): (24, 1), + ('no_overrides', 2, False, False): (24, 1), + ('no_overrides', 3, False, False): (24, 1), + ('ccx', 1, False, False): (24, 1), + ('ccx', 2, False, False): (24, 1), + ('ccx', 3, False, False): (24, 1), } @@ -260,19 +263,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase): __test__ = True TEST_DATA = { - ('no_overrides', 1, True, False): (21, 3), - ('no_overrides', 2, True, False): (21, 3), - ('no_overrides', 3, True, False): (21, 3), - ('ccx', 1, True, False): (21, 3), - ('ccx', 2, True, False): (21, 3), - ('ccx', 3, True, False): (21, 3), - ('ccx', 1, True, True): (22, 3), - ('ccx', 2, True, True): (22, 3), - ('ccx', 3, True, True): (22, 3), - ('no_overrides', 1, False, False): (21, 3), - ('no_overrides', 2, False, False): (21, 3), - ('no_overrides', 3, False, False): (21, 3), - ('ccx', 1, False, False): (21, 3), - ('ccx', 2, False, False): (21, 3), - ('ccx', 3, False, False): (21, 3), + ('no_overrides', 1, True, False): (24, 3), + ('no_overrides', 2, True, False): (24, 3), + ('no_overrides', 3, True, False): (24, 3), + ('ccx', 1, True, False): (24, 3), + ('ccx', 2, True, False): (24, 3), + ('ccx', 3, True, False): (24, 3), + ('ccx', 1, True, True): (25, 3), + ('ccx', 2, True, True): (25, 3), + ('ccx', 3, True, True): (25, 3), + ('no_overrides', 1, False, False): (24, 3), + ('no_overrides', 2, False, False): (24, 3), + ('no_overrides', 3, False, False): (24, 3), + ('ccx', 1, False, False): (24, 3), + ('ccx', 2, False, False): (24, 3), + ('ccx', 3, False, False): (24, 3), } diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 2fa5bea93b..c3e905fb3b 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -1438,8 +1438,8 @@ class ProgressPageTests(ProgressPageBaseTests): @override_waffle_flag(CONTENT_TYPE_GATING_FLAG, True) @ddt.data( - (True, 39), - (False, 38) + (True, 40), + (False, 39) ) @ddt.unpack def test_progress_queries_paced_courses(self, self_paced, query_count): @@ -1451,8 +1451,8 @@ class ProgressPageTests(ProgressPageBaseTests): @override_waffle_flag(CONTENT_TYPE_GATING_FLAG, True) @patch.dict(settings.FEATURES, {'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS': False}) @ddt.data( - (False, 46, 29), - (True, 38, 25) + (False, 47, 30), + (True, 39, 26) ) @ddt.unpack def test_progress_queries(self, enable_waffle, initial, subsequent): @@ -1648,6 +1648,64 @@ class ProgressPageTests(ProgressPageBaseTests): u'You are enrolled in the audit track for this course. The audit track does not include a certificate.' ) + @override_waffle_flag(CONTENT_TYPE_GATING_FLAG, True) + @ddt.data( + *itertools.product( + ( + CourseMode.AUDIT, + CourseMode.HONOR, + CourseMode.VERIFIED, + CourseMode.PROFESSIONAL, + CourseMode.NO_ID_PROFESSIONAL_MODE, + CourseMode.CREDIT_MODE + ) + ) + ) + @ddt.unpack + def test_progress_with_course_duration_limits(self, course_mode): + """ + Verify that expired banner message appears on progress page, if learner is enrolled + in audit mode. + """ + user = UserFactory.create() + self.assertTrue(self.client.login(username=user.username, password='test')) + CourseEnrollmentFactory(user=user, course_id=self.course.id, mode=course_mode) + + response = self._get_progress_page() + bannerText = get_expiration_banner_text(user, self.course) + + if course_mode == CourseMode.AUDIT: + self.assertContains(response, bannerText, html=True) + else: + self.assertNotContains(response, bannerText, html=True) + + @override_waffle_flag(CONTENT_TYPE_GATING_FLAG, False) + @ddt.data( + *itertools.product( + ( + CourseMode.AUDIT, + CourseMode.HONOR, + CourseMode.VERIFIED, + CourseMode.PROFESSIONAL, + CourseMode.NO_ID_PROFESSIONAL_MODE, + CourseMode.CREDIT_MODE + ) + ) + ) + @ddt.unpack + def test_progress_without_course_duration_limits(self, course_mode): + """ + Verify that expired banner message never appears on progress page, regardless + of course_mode + """ + user = UserFactory.create() + self.assertTrue(self.client.login(username=user.username, password='test')) + CourseEnrollmentFactory(user=user, course_id=self.course.id, mode=course_mode) + + response = self._get_progress_page() + bannerText = get_expiration_banner_text(user, self.course) + self.assertNotContains(response, bannerText, html=True) + @patch('courseware.views.views.is_course_passed', PropertyMock(return_value=True)) @patch('lms.djangoapps.certificates.api.get_active_web_certificate', PropertyMock(return_value=True)) def test_message_for_honor_mode(self): diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 0eafcb3ddb..0a1cf45d36 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -985,6 +985,8 @@ def _progress(request, course_key, student_id): # checking certificate generation configuration enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user(student, course_key) + register_course_expired_message(request, course) + context = { 'course': course, 'courseware_summary': courseware_summary,