def test_resolve_course_enrollments(self): """ Test that the CourseEnrollmentsScopeResolver actually returns all enrollments """ test_user_1 = UserFactory.create(password='******') CourseEnrollmentFactory(user=test_user_1, course_id=self.course.id) test_user_2 = UserFactory.create(password='******') CourseEnrollmentFactory(user=test_user_2, course_id=self.course.id) test_user_3 = UserFactory.create(password='******') enrollment = CourseEnrollmentFactory(user=test_user_3, course_id=self.course.id) # unenroll #3 enrollment.is_active = False enrollment.save() resolver = CourseEnrollmentsScopeResolver() user_ids = resolver.resolve('course_enrollments', {'course_id': self.course.id}, None) # should have first two, but the third should not be present self.assertTrue(test_user_1.id in user_ids) self.assertTrue(test_user_2.id in user_ids) self.assertFalse(test_user_3.id in user_ids)
def test_namespace_scope(self): """ Make sure that we handle resolving namespaces correctly """ test_user_1 = UserFactory.create(password='******', email='*****@*****.**', first_name='user', last_name='one') CourseEnrollmentFactory(user=test_user_1, course_id=self.course.id) test_user_2 = UserFactory.create(password='******', email='*****@*****.**', first_name='John', last_name='Smith') CourseEnrollmentFactory(user=test_user_2, course_id=self.course.id) test_user_3 = UserFactory.create(password='******') enrollment = CourseEnrollmentFactory(user=test_user_3, course_id=self.course.id) # unenroll #3 enrollment.is_active = False enrollment.save() resolver = NamespaceEnrollmentsScopeResolver() users = resolver.resolve( 'namespace_scope', { 'namespace': self.course.id, 'fields': { 'id': True, 'email': True, 'first_name': True, 'last_name': True, } }, None) _users = [user for user in users] self.assertEqual(len(_users), 2) self.assertIn('id', _users[0]) self.assertIn('email', _users[0]) self.assertIn('first_name', _users[0]) self.assertIn('last_name', _users[0]) self.assertEquals(_users[0]['id'], test_user_1.id) self.assertEquals(_users[0]['email'], test_user_1.email) self.assertEquals(_users[0]['first_name'], test_user_1.first_name) self.assertEquals(_users[0]['last_name'], test_user_1.last_name) self.assertIn('id', _users[1]) self.assertIn('email', _users[1]) self.assertIn('first_name', _users[1]) self.assertIn('last_name', _users[1]) self.assertEquals(_users[1]['id'], test_user_2.id) self.assertEquals(_users[1]['email'], test_user_2.email) self.assertEquals(_users[1]['first_name'], test_user_2.first_name) self.assertEquals(_users[1]['last_name'], test_user_2.last_name)
def test_resolve_course_enrollments(self): """ Test that the CourseEnrollmentsScopeResolver actually returns all enrollments """ test_user_1 = UserFactory.create(password='******') CourseEnrollmentFactory(user=test_user_1, course_id=self.course.id) test_user_2 = UserFactory.create(password='******') CourseEnrollmentFactory(user=test_user_2, course_id=self.course.id) test_user_3 = UserFactory.create(password='******') enrollment = CourseEnrollmentFactory(user=test_user_3, course_id=self.course.id) # unenroll #3 enrollment.is_active = False enrollment.save() resolver = CourseEnrollmentsScopeResolver() user_ids = resolver.resolve('course_enrollments', {'course_id': self.course.id}, None) # should have first two, but the third should not be present self.assertTrue(test_user_1.id in user_ids) self.assertTrue(test_user_2.id in user_ids) self.assertFalse(test_user_3.id in user_ids)
def test_enrollment_end(self, experiment_end, enrollment_created, expected_bucket): if enrollment_created: enrollment = CourseEnrollmentFactory(user=self.user, course_id='a/b/c') enrollment.created = parser.parse(enrollment_created).replace(tzinfo=pytz.UTC) enrollment.save() if experiment_end: ExperimentKeyValueFactory(experiment_id=0, key='enrollment_end', value=experiment_end) self.assertEqual(self.get_bucket(), expected_bucket)
class ResetSelfPacedScheduleTests(SharedModuleStoreTestCase): def create_schedule(self, offset=0): self.config = ScheduleConfigFactory(create_schedules=True) site_patch = patch('openedx.core.djangoapps.schedules.signals.get_current_site', return_value=self.config.site) self.addCleanup(site_patch.stop) site_patch.start() start = datetime.datetime.now(utc) - datetime.timedelta(days=100) self.course = CourseFactory.create(start=start, self_paced=True) self.enrollment = CourseEnrollmentFactory( course_id=self.course.id, mode=CourseMode.AUDIT, ) self.enrollment.created = start + datetime.timedelta(days=offset) self.enrollment.save() self.schedule = self.enrollment.schedule self.schedule.start_date = self.enrollment.created self.schedule.save() self.user = self.enrollment.user def test_reset_to_now(self): self.create_schedule() original_start = self.schedule.start_date with self.assertNumQueries(3): reset_self_paced_schedule(self.user, self.course.id, use_availability_date=False) self.schedule.refresh_from_db() self.assertGreater(self.schedule.start_date, original_start) @ddt.data( (-1, 0), # enrolled before course started (will reset to start date) (1, 1), # enrolled after course started (will reset to enroll date) ) @ddt.unpack def test_reset_to_start_date(self, offset, expected_offset): self.create_schedule(offset=offset) expected_start = self.course.start + datetime.timedelta(days=expected_offset) with self.assertNumQueries(3): reset_self_paced_schedule(self.user, self.course.id, use_availability_date=True) self.schedule.refresh_from_db() self.assertEqual(self.schedule.start_date.replace(microsecond=0), expected_start.replace(microsecond=0)) def test_safe_without_schedule(self): """ Just ensure that we don't raise exceptions or create any schedules """ self.create_schedule() self.schedule.delete() reset_self_paced_schedule(self.user, self.course.id, use_availability_date=False) reset_self_paced_schedule(self.user, self.course.id, use_availability_date=True) self.assertEqual(Schedule.objects.count(), 0)
class CertificatesApiTestCase(TestCase): def setUp(self): super(CertificatesApiTestCase, self).setUp() self.course = CourseOverviewFactory.create( start=datetime(2017, 1, 1, tzinfo=pytz.UTC), end=datetime(2017, 1, 31, tzinfo=pytz.UTC), certificate_available_date=None) self.user = UserFactory.create() self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, is_active=True, mode='audit', ) self.certificate = MockGeneratedCertificate(user=self.user, course_id=self.course.id) @ddt.data(True, False) def test_auto_certificate_generation_enabled(self, feature_enabled): with configure_waffle_namespace(feature_enabled): self.assertEqual(feature_enabled, api.auto_certificate_generation_enabled()) @ddt.data( (True, True, False), # feature enabled and self-paced should return False (True, False, True), # feature enabled and instructor-paced should return True (False, True, False), # feature not enabled and self-paced should return False (False, False, False ), # feature not enabled and instructor-paced should return False ) @ddt.unpack def test_can_show_certificate_available_date_field(self, feature_enabled, is_self_paced, expected_value): self.course.self_paced = is_self_paced with configure_waffle_namespace(feature_enabled): self.assertEqual( expected_value, api.can_show_certificate_available_date_field(self.course)) @ddt.data((CourseMode.VERIFIED, CertificateStatuses.downloadable, True), (CourseMode.VERIFIED, CertificateStatuses.notpassing, False), (CourseMode.AUDIT, CertificateStatuses.downloadable, False)) @ddt.unpack def test_is_certificate_valid(self, enrollment_mode, certificate_status, expected_value): self.enrollment.mode = enrollment_mode self.enrollment.save() self.certificate.mode = CourseMode.VERIFIED self.certificate.status = certificate_status self.assertEqual(expected_value, api.is_certificate_valid(self.certificate))
class CertificatesApiTestCase(TestCase): def setUp(self): super(CertificatesApiTestCase, self).setUp() self.course = CourseOverviewFactory.create( start=datetime(2017, 1, 1, tzinfo=pytz.UTC), end=datetime(2017, 1, 31, tzinfo=pytz.UTC), certificate_available_date=None ) self.user = UserFactory.create() self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, is_active=True, mode='audit', ) self.certificate = MockGeneratedCertificate( user=self.user, course_id=self.course.id ) @ddt.data(True, False) def test_auto_certificate_generation_enabled(self, feature_enabled): with configure_waffle_namespace(feature_enabled): self.assertEqual(feature_enabled, api.auto_certificate_generation_enabled()) @ddt.data( (True, True, False), # feature enabled and self-paced should return False (True, False, True), # feature enabled and instructor-paced should return True (False, True, False), # feature not enabled and self-paced should return False (False, False, False), # feature not enabled and instructor-paced should return False ) @ddt.unpack def test_can_show_certificate_available_date_field( self, feature_enabled, is_self_paced, expected_value ): self.course.self_paced = is_self_paced with configure_waffle_namespace(feature_enabled): self.assertEqual(expected_value, api.can_show_certificate_available_date_field(self.course)) @ddt.data( (CourseMode.VERIFIED, CertificateStatuses.downloadable, True), (CourseMode.VERIFIED, CertificateStatuses.notpassing, False), (CourseMode.AUDIT, CertificateStatuses.downloadable, False) ) @ddt.unpack def test_is_certificate_valid(self, enrollment_mode, certificate_status, expected_value): self.enrollment.mode = enrollment_mode self.enrollment.save() self.certificate.mode = CourseMode.VERIFIED self.certificate.status = certificate_status self.assertEqual(expected_value, api.is_certificate_valid(self.certificate))
def test_celebration_created(self): """ Test that we make celebration objects when enrollments are created """ self.assertEqual(CourseEnrollmentCelebration.objects.count(), 0) # Test initial creation upon an enrollment being made enrollment = CourseEnrollmentFactory() self.assertEqual(CourseEnrollmentCelebration.objects.count(), 1) celebration = CourseEnrollmentCelebration.objects.get(enrollment=enrollment, celebrate_first_section=True) # Test nothing changes if we update that enrollment celebration.celebrate_first_section = False celebration.save() enrollment.mode = 'test-mode' enrollment.save() self.assertEqual(CourseEnrollmentCelebration.objects.count(), 1) CourseEnrollmentCelebration.objects.get(enrollment=enrollment, celebrate_first_section=False)
def test_namespace_scope(self): """ Make sure that we handle resolving namespaces correctly """ test_user_1 = UserFactory.create( password='******', email='*****@*****.**', first_name='user', last_name='one' ) CourseEnrollmentFactory(user=test_user_1, course_id=self.course.id) test_user_2 = UserFactory.create( password='******', email='*****@*****.**', first_name='John', last_name='Smith' ) CourseEnrollmentFactory(user=test_user_2, course_id=self.course.id) test_user_3 = UserFactory.create(password='******') enrollment = CourseEnrollmentFactory(user=test_user_3, course_id=self.course.id) # unenroll #3 enrollment.is_active = False enrollment.save() resolver = NamespaceEnrollmentsScopeResolver() users = resolver.resolve( 'namespace_scope', { 'namespace': self.course.id, 'fields': { 'id': True, 'email': True, 'first_name': True, 'last_name': True, } }, None ) _users = [user for user in users] self.assertEqual(len(_users), 2) self.assertIn('id', _users[0]) self.assertIn('email', _users[0]) self.assertIn('first_name', _users[0]) self.assertIn('last_name', _users[0]) self.assertEquals(_users[0]['id'], test_user_1.id) self.assertEquals(_users[0]['email'], test_user_1.email) self.assertEquals(_users[0]['first_name'], test_user_1.first_name) self.assertEquals(_users[0]['last_name'], test_user_1.last_name) self.assertIn('id', _users[1]) self.assertIn('email', _users[1]) self.assertIn('first_name', _users[1]) self.assertIn('last_name', _users[1]) self.assertEquals(_users[1]['id'], test_user_2.id) self.assertEquals(_users[1]['email'], test_user_2.email) self.assertEquals(_users[1]['first_name'], test_user_2.first_name) self.assertEquals(_users[1]['last_name'], test_user_2.last_name)
class VerifiedUpgradeToolTest(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): super(VerifiedUpgradeToolTest, cls).setUpClass() cls.now = datetime.datetime.now(pytz.UTC) cls.course = CourseFactory.create( org='edX', number='test', display_name='Test Course', self_paced=True, start=cls.now - datetime.timedelta(days=30), ) cls.course_overview = CourseOverview.get_from_id(cls.course.id) @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def setUp(self): super(VerifiedUpgradeToolTest, self).setUp() self.course_verified_mode = CourseModeFactory( course_id=self.course.id, mode_slug=CourseMode.VERIFIED, expiration_datetime=self.now + datetime.timedelta(days=30), ) patcher = patch( 'openedx.core.djangoapps.schedules.signals.get_current_site') mock_get_current_site = patcher.start() self.addCleanup(patcher.stop) mock_get_current_site.return_value = SiteFactory.create() DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) self.request = RequestFactory().request() crum.set_current_request(self.request) self.addCleanup(crum.set_current_request, None) self.enrollment = CourseEnrollmentFactory( course_id=self.course.id, mode=CourseMode.AUDIT, course=self.course_overview, ) self.request.user = self.enrollment.user def test_tool_visible(self): self.assertTrue(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_no_enrollment_exists(self): self.enrollment.delete() request = RequestFactory().request() request.user = UserFactory() self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_using_deadline_from_course_mode(self): DynamicUpgradeDeadlineConfiguration.objects.create(enabled=False) self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_enrollment_is_inactive(self): self.enrollment.is_active = False self.enrollment.save() self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_already_verified(self): self.enrollment.mode = CourseMode.VERIFIED self.enrollment.save() self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_no_verified_track(self): self.course_verified_mode.delete() self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_course_deadline_has_passed(self): self.course_verified_mode.expiration_datetime = self.now - datetime.timedelta( days=1) self.course_verified_mode.save() self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id)) def test_not_visible_when_course_mode_has_no_deadline(self): self.course_verified_mode.expiration_datetime = None self.course_verified_mode.save() self.assertFalse(VerifiedUpgradeTool().is_enabled( self.request, self.course.id))
class CreditCourseDashboardTest(ModuleStoreTestCase): """ Tests for credit courses on the student dashboard. """ USERNAME = "******" PASSWORD = "******" PROVIDER_ID = "hogwarts" PROVIDER_NAME = "Hogwarts School of Witchcraft and Wizardry" PROVIDER_STATUS_URL = "http://credit.example.com/status" def setUp(self): """Create a course and an enrollment. """ super(CreditCourseDashboardTest, self).setUp() # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Create a course and configure it as a credit course self.course = CourseFactory() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # pylint: disable=no-member # Configure a credit provider CreditProvider.objects.create( provider_id=self.PROVIDER_ID, display_name=self.PROVIDER_NAME, provider_status_url=self.PROVIDER_STATUS_URL, enable_integration=True, ) # Configure a single credit requirement (minimum passing grade) credit_api.set_credit_requirements( self.course.id, # pylint: disable=no-member [{ "namespace": "grade", "name": "grade", "display_name": "Final Grade", "criteria": { "min_grade": 0.8 } }]) # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, # pylint: disable=no-member mode="verified") def test_not_eligible_for_credit(self): # The user is not yet eligible for credit, so no additional information should be displayed on the dashboard. response = self._load_dashboard() self.assertNotContains(response, "credit-eligibility-msg") self.assertNotContains(response, "purchase-credit-btn") def test_eligible_for_credit(self): # Simulate that the user has completed the only requirement in the course # so the user is eligible for credit. self._make_eligible() # The user should have the option to purchase credit response = self._load_dashboard() self.assertContains(response, "credit-eligibility-msg") self.assertContains(response, "purchase-credit-btn") # Move the eligibility deadline so it's within 30 days eligibility = CreditEligibility.objects.get(username=self.USERNAME) eligibility.deadline = datetime.datetime.now( pytz.UTC) + datetime.timedelta(days=29) eligibility.save() # The user should still have the option to purchase credit, # but there should also be a message urging the user to purchase soon. response = self._load_dashboard() self.assertContains(response, "credit-eligibility-msg") self.assertContains(response, "purchase-credit-btn") self.assertContains(response, "You have completed this course and are eligible") def test_purchased_credit(self): # Simulate that the user has purchased credit, but has not # yet initiated a request to the credit provider self._make_eligible() self._purchase_credit() response = self._load_dashboard() self.assertContains(response, "credit-request-not-started-msg") def test_purchased_credit_and_request_pending(self): # Simulate that the user has purchased credit and initiated a request, # but we haven't yet heard back from the credit provider. self._make_eligible() self._purchase_credit() self._initiate_request() # Expect that the user's status is "pending" response = self._load_dashboard() self.assertContains(response, "credit-request-pending-msg") def test_purchased_credit_and_request_approved(self): # Simulate that the user has purchased credit and initiated a request, # and had that request approved by the credit provider self._make_eligible() self._purchase_credit() request_uuid = self._initiate_request() self._set_request_status(request_uuid, "approved") # Expect that the user's status is "approved" response = self._load_dashboard() self.assertContains(response, "credit-request-approved-msg") def test_purchased_credit_and_request_rejected(self): # Simulate that the user has purchased credit and initiated a request, # and had that request rejected by the credit provider self._make_eligible() self._purchase_credit() request_uuid = self._initiate_request() self._set_request_status(request_uuid, "rejected") # Expect that the user's status is "approved" response = self._load_dashboard() self.assertContains(response, "credit-request-rejected-msg") def test_credit_status_error(self): # Simulate an error condition: the user has a credit enrollment # but no enrollment attribute indicating which provider the user # purchased credit from. self._make_eligible() self._purchase_credit() CourseEnrollmentAttribute.objects.all().delete() # Expect an error message response = self._load_dashboard() self.assertContains(response, "credit-error-msg") def _load_dashboard(self): """Load the student dashboard and return the HttpResponse. """ return self.client.get(reverse("dashboard")) def _make_eligible(self): """Make the user eligible for credit in the course. """ credit_api.set_credit_requirement_status( self.user, self.course.id, # pylint: disable=no-member "grade", "grade", status="satisfied", reason={"final_grade": 0.95}) def _purchase_credit(self): """Purchase credit from a provider in the course. """ self.enrollment.mode = "credit" self.enrollment.save() # pylint: disable=no-member CourseEnrollmentAttribute.objects.create( enrollment=self.enrollment, namespace="credit", name="provider_id", value=self.PROVIDER_ID, ) def _initiate_request(self): """Initiate a request for credit from a provider. """ request = credit_api.create_credit_request( self.course.id, # pylint: disable=no-member self.PROVIDER_ID, self.USERNAME) return request["parameters"]["request_uuid"] def _set_request_status(self, uuid, status): """Set the status of a request for credit, simulating the notification from the provider. """ credit_api.update_credit_request_status(uuid, self.PROVIDER_ID, status) @ddt.data(([ u'Arizona State University' ], 'You are now eligible for credit from Arizona State University'), ( [u'Arizona State University', u'Hogwarts School of Witchcraft'], 'You are now eligible for credit from Arizona State University and Hogwarts School of Witchcraft' ), ([ u'Arizona State University', u'Hogwarts School of Witchcraft and Wizardry', u'Charter Oak' ], 'You are now eligible for credit from Arizona State University, Hogwarts School' ' of Witchcraft and Wizardry, and Charter Oak'), ([], 'You have completed this course and are eligible'), (None, 'You have completed this course and are eligible')) @ddt.unpack def test_eligible_for_credit_with_providers_names(self, providers_list, credit_string): """Verify the message on dashboard with different number of providers.""" # Simulate that the user has completed the only requirement in the course # so the user is eligible for credit. self._make_eligible() # The user should have the option to purchase credit with patch('student.views.get_credit_provider_display_names' ) as mock_method: mock_method.return_value = providers_list response = self._load_dashboard() self.assertContains(response, "credit-eligibility-msg") self.assertContains(response, "purchase-credit-btn") self.assertContains(response, credit_string)
class ProgressPageCreditRequirementsTest(ModuleStoreTestCase): """ Tests for credit requirement display on the progress page. """ USERNAME = "******" PASSWORD = "******" USER_FULL_NAME = "Bob" MIN_GRADE_REQ_DISPLAY = "Final Grade Credit Requirement" VERIFICATION_REQ_DISPLAY = "Midterm Exam Credit Requirement" def setUp(self): super(ProgressPageCreditRequirementsTest, self).setUp() # Create a course and configure it as a credit course self.course = CourseFactory.create() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # Configure credit requirements (passing grade and in-course reverification) credit_api.set_credit_requirements( self.course.id, [ { "namespace": "grade", "name": "grade", "display_name": self.MIN_GRADE_REQ_DISPLAY, "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "display_name": self.VERIFICATION_REQ_DISPLAY, "criteria": {} } ] ) # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) self.user.profile.name = self.USER_FULL_NAME self.user.profile.save() result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, mode="verified" ) def test_credit_requirements_maybe_eligible(self): # The user hasn't satisfied any of the credit requirements yet, but she # also hasn't failed any. response = self._get_progress_page() # Expect that the requirements are displayed self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY) self.assertContains(response, self.VERIFICATION_REQ_DISPLAY) self.assertContains(response, "Upcoming") self.assertContains( response, "{}, you have not yet met the requirements for credit".format(self.USER_FULL_NAME) ) def test_credit_requirements_eligible(self): # Mark the user as eligible for all requirements credit_api.set_credit_requirement_status( self.user.username, self.course.id, "grade", "grade", status="satisfied", reason={"final_grade": 0.95} ) credit_api.set_credit_requirement_status( self.user.username, self.course.id, "reverification", "midterm", status="satisfied", reason={} ) # Check the progress page display response = self._get_progress_page() self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY) self.assertContains(response, self.VERIFICATION_REQ_DISPLAY) self.assertContains( response, "{}, you have met the requirements for credit in this course.".format(self.USER_FULL_NAME) ) self.assertContains(response, "Completed {date}".format(date=self._now_formatted_date())) self.assertContains(response, "95%") def test_credit_requirements_not_eligible(self): # Mark the user as having failed both requirements credit_api.set_credit_requirement_status( self.user.username, self.course.id, "reverification", "midterm", status="failed", reason={} ) # Check the progress page display response = self._get_progress_page() self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY) self.assertContains(response, self.VERIFICATION_REQ_DISPLAY) self.assertContains( response, "{}, you are no longer eligible for credit in this course.".format(self.USER_FULL_NAME) ) self.assertContains(response, "Verification Failed") @ddt.data( (CourseMode.VERIFIED, True), (CourseMode.CREDIT_MODE, True), (CourseMode.HONOR, False), (CourseMode.AUDIT, False), (CourseMode.PROFESSIONAL, False), (CourseMode.NO_ID_PROFESSIONAL_MODE, False) ) @ddt.unpack def test_credit_requirements_on_progress_page(self, enrollment_mode, is_requirement_displayed): """Test the progress table is only displayed to the verified and credit students.""" self.enrollment.mode = enrollment_mode self.enrollment.save() # pylint: disable=no-member response = self._get_progress_page() # Verify the requirements are shown only if the user is in a credit-eligible mode. classes = ('credit-eligibility', 'eligibility-heading') method = self.assertContains if is_requirement_displayed else self.assertNotContains for _class in classes: method(response, _class) def _get_progress_page(self): """Load the progress page for the course the user is enrolled in. """ url = reverse("progress", kwargs={"course_id": unicode(self.course.id)}) return self.client.get(url) def _now_formatted_date(self): """Retrieve the formatted current date. """ return get_time_display( datetime.datetime.now(UTC), DEFAULT_SHORT_DATE_FORMAT, settings.TIME_ZONE )
class CreditCourseDashboardTest(ModuleStoreTestCase): """ Tests for credit courses on the student dashboard. """ USERNAME = "******" PASSWORD = "******" PROVIDER_ID = "hogwarts" PROVIDER_NAME = "Hogwarts School of Witchcraft and Wizardry" PROVIDER_STATUS_URL = "http://credit.example.com/status" def setUp(self): """Create a course and an enrollment. """ super(CreditCourseDashboardTest, self).setUp() # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Create a course and configure it as a credit course self.course = CourseFactory() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # pylint: disable=no-member # Configure a credit provider CreditProvider.objects.create( provider_id=self.PROVIDER_ID, display_name=self.PROVIDER_NAME, provider_status_url=self.PROVIDER_STATUS_URL, enable_integration=True, ) # Configure a single credit requirement (minimum passing grade) credit_api.set_credit_requirements( self.course.id, # pylint: disable=no-member [ { "namespace": "grade", "name": "grade", "display_name": "Final Grade", "criteria": { "min_grade": 0.8 } } ] ) # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, # pylint: disable=no-member mode="verified" ) def test_not_eligible_for_credit(self): # The user is not yet eligible for credit, so no additional information should be displayed on the dashboard. response = self._load_dashboard() self.assertNotContains(response, "credit") def test_eligible_for_credit(self): # Simulate that the user has completed the only requirement in the course # so the user is eligible for credit. self._make_eligible() # The user should have the option to purchase credit response = self._load_dashboard() self.assertContains(response, "credit-eligibility-msg") self.assertContains(response, "purchase-credit-btn") # Move the eligibility deadline so it's within 30 days eligibility = CreditEligibility.objects.get(username=self.USERNAME) eligibility.deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=29) eligibility.save() # The user should still have the option to purchase credit, # but there should also be a message urging the user to purchase soon. response = self._load_dashboard() self.assertContains(response, "credit-eligibility-msg") self.assertContains(response, "purchase-credit-btn") self.assertContains(response, "purchase credit for this course expires") def test_purchased_credit(self): # Simulate that the user has purchased credit, but has not # yet initiated a request to the credit provider self._make_eligible() self._purchase_credit() # Expect that the user's status is "pending" response = self._load_dashboard() self.assertContains(response, "credit-request-pending-msg") def test_purchased_credit_and_request_pending(self): # Simulate that the user has purchased credit and initiated a request, # but we haven't yet heard back from the credit provider. self._make_eligible() self._purchase_credit() self._initiate_request() # Expect that the user's status is "pending" response = self._load_dashboard() self.assertContains(response, "credit-request-pending-msg") def test_purchased_credit_and_request_approved(self): # Simulate that the user has purchased credit and initiated a request, # and had that request approved by the credit provider self._make_eligible() self._purchase_credit() request_uuid = self._initiate_request() self._set_request_status(request_uuid, "approved") # Expect that the user's status is "approved" response = self._load_dashboard() self.assertContains(response, "credit-request-approved-msg") def test_purchased_credit_and_request_rejected(self): # Simulate that the user has purchased credit and initiated a request, # and had that request rejected by the credit provider self._make_eligible() self._purchase_credit() request_uuid = self._initiate_request() self._set_request_status(request_uuid, "rejected") # Expect that the user's status is "approved" response = self._load_dashboard() self.assertContains(response, "credit-request-rejected-msg") def test_credit_status_error(self): # Simulate an error condition: the user has a credit enrollment # but no enrollment attribute indicating which provider the user # purchased credit from. self._make_eligible() self._purchase_credit() CourseEnrollmentAttribute.objects.all().delete() # Expect an error message response = self._load_dashboard() self.assertContains(response, "credit-error-msg") def _load_dashboard(self): """Load the student dashboard and return the HttpResponse. """ return self.client.get(reverse("dashboard")) def _make_eligible(self): """Make the user eligible for credit in the course. """ credit_api.set_credit_requirement_status( self.USERNAME, self.course.id, # pylint: disable=no-member "grade", "grade", status="satisfied", reason={ "final_grade": 0.95 } ) def _purchase_credit(self): """Purchase credit from a provider in the course. """ self.enrollment.mode = "credit" self.enrollment.save() # pylint: disable=no-member CourseEnrollmentAttribute.objects.create( enrollment=self.enrollment, namespace="credit", name="provider_id", value=self.PROVIDER_ID, ) def _initiate_request(self): """Initiate a request for credit from a provider. """ request = credit_api.create_credit_request( self.course.id, # pylint: disable=no-member self.PROVIDER_ID, self.USERNAME ) return request["parameters"]["request_uuid"] def _set_request_status(self, uuid, status): """Set the status of a request for credit, simulating the notification from the provider. """ credit_api.update_credit_request_status(uuid, self.PROVIDER_ID, status)
class ProgressPageCreditRequirementsTest(ModuleStoreTestCase): """ Tests for credit requirement display on the progress page. """ USERNAME = "******" PASSWORD = "******" USER_FULL_NAME = "Bob" MIN_GRADE_REQ_DISPLAY = "Final Grade Credit Requirement" VERIFICATION_REQ_DISPLAY = "Midterm Exam Credit Requirement" def setUp(self): super(ProgressPageCreditRequirementsTest, self).setUp() # Create a course and configure it as a credit course self.course = CourseFactory.create() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # Configure credit requirements (passing grade and in-course reverification) credit_api.set_credit_requirements( self.course.id, [{ "namespace": "grade", "name": "grade", "display_name": self.MIN_GRADE_REQ_DISPLAY, "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "display_name": self.VERIFICATION_REQ_DISPLAY, "criteria": {} }]) # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) self.user.profile.name = self.USER_FULL_NAME self.user.profile.save() result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory(user=self.user, course_id=self.course.id, mode="verified") def test_credit_requirements_maybe_eligible(self): # The user hasn't satisfied any of the credit requirements yet, but she # also hasn't failed any. response = self._get_progress_page() # Expect that the requirements are displayed self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY) self.assertContains(response, self.VERIFICATION_REQ_DISPLAY) self.assertContains(response, "Upcoming") self.assertContains( response, "{}, you have not yet met the requirements for credit".format( self.USER_FULL_NAME)) def test_credit_requirements_eligible(self): # Mark the user as eligible for all requirements credit_api.set_credit_requirement_status(self.user.username, self.course.id, "grade", "grade", status="satisfied", reason={"final_grade": 0.95}) credit_api.set_credit_requirement_status(self.user.username, self.course.id, "reverification", "midterm", status="satisfied", reason={}) # Check the progress page display response = self._get_progress_page() self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY) self.assertContains(response, self.VERIFICATION_REQ_DISPLAY) self.assertContains( response, "{}, you have met the requirements for credit in this course.". format(self.USER_FULL_NAME)) self.assertContains( response, "Completed {date}".format(date=self._now_formatted_date())) self.assertContains(response, "95%") def test_credit_requirements_not_eligible(self): # Mark the user as having failed both requirements credit_api.set_credit_requirement_status(self.user.username, self.course.id, "reverification", "midterm", status="failed", reason={}) # Check the progress page display response = self._get_progress_page() self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY) self.assertContains(response, self.VERIFICATION_REQ_DISPLAY) self.assertContains( response, "{}, you are no longer eligible for credit in this course.".format( self.USER_FULL_NAME)) self.assertContains(response, "Verification Failed") @ddt.data((CourseMode.VERIFIED, True), (CourseMode.CREDIT_MODE, True), (CourseMode.HONOR, False), (CourseMode.AUDIT, False), (CourseMode.PROFESSIONAL, False), (CourseMode.NO_ID_PROFESSIONAL_MODE, False)) @ddt.unpack def test_credit_requirements_on_progress_page(self, enrollment_mode, is_requirement_displayed): """Test the progress table is only displayed to the verified and credit students.""" self.enrollment.mode = enrollment_mode self.enrollment.save() # pylint: disable=no-member response = self._get_progress_page() # Verify the requirements are shown only if the user is in a credit-eligible mode. classes = ('credit-eligibility', 'eligibility-heading') method = self.assertContains if is_requirement_displayed else self.assertNotContains for _class in classes: method(response, _class) def _get_progress_page(self): """Load the progress page for the course the user is enrolled in. """ url = reverse("progress", kwargs={"course_id": unicode(self.course.id)}) return self.client.get(url) def _now_formatted_date(self): """Retrieve the formatted current date. """ return get_time_display(datetime.datetime.now(UTC), DEFAULT_SHORT_DATE_FORMAT, settings.TIME_ZONE)
class VerifiedUpgradeToolTest(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): super(VerifiedUpgradeToolTest, cls).setUpClass() cls.now = datetime.datetime.now(pytz.UTC) cls.course = CourseFactory.create( org='edX', number='test', display_name='Test Course', self_paced=True, start=cls.now - datetime.timedelta(days=30), ) cls.course_overview = CourseOverview.get_from_id(cls.course.id) @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) def setUp(self): super(VerifiedUpgradeToolTest, self).setUp() self.course_verified_mode = CourseModeFactory( course_id=self.course.id, mode_slug=CourseMode.VERIFIED, expiration_datetime=self.now + datetime.timedelta(days=30), ) patcher = patch('openedx.core.djangoapps.schedules.signals.get_current_site') mock_get_current_site = patcher.start() self.addCleanup(patcher.stop) mock_get_current_site.return_value = SiteFactory.create() DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) self.enrollment = CourseEnrollmentFactory( course_id=self.course.id, mode=CourseMode.AUDIT, course=self.course_overview, ) self.request = RequestFactory().request() self.request.user = self.enrollment.user crum.set_current_request(self.request) def test_tool_visible(self): self.assertTrue(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_no_enrollment_exists(self): self.enrollment.delete() request = RequestFactory().request() request.user = UserFactory() self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_using_deadline_from_course_mode(self): DynamicUpgradeDeadlineConfiguration.objects.create(enabled=False) self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_enrollment_is_inactive(self): self.enrollment.is_active = False self.enrollment.save() self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_already_verified(self): self.enrollment.mode = CourseMode.VERIFIED self.enrollment.save() self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_no_verified_track(self): self.course_verified_mode.delete() self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_course_deadline_has_passed(self): self.course_verified_mode.expiration_datetime = self.now - datetime.timedelta(days=1) self.course_verified_mode.save() self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) def test_not_visible_when_course_mode_has_no_deadline(self): self.course_verified_mode.expiration_datetime = None self.course_verified_mode.save() self.assertFalse(VerifiedUpgradeTool().is_enabled(self.request, self.course.id))