""" Tests for the recently enrolled messaging within the Dashboard. """ import datetime from django.conf import settings from django.core.urlresolvers import reverse from opaque_keys.edx import locator from pytz import UTC from nose.plugins.attrib import attr import unittest import ddt from shoppingcart.models import DonationConfiguration from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from course_modes.tests.factories import CourseModeFactory from student.models import CourseEnrollment, DashboardConfiguration from student.views import get_course_enrollments, _get_recently_enrolled_courses from common.test.utils import XssTestMixin from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context @attr(shard=3) @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @ddt.ddt class TestRecentEnrollments(ModuleStoreTestCase, XssTestMixin): """ Unit tests for getting the list of courses for a logged in user """ PASSWORD = 'test' def setUp(self): """ Add a student """ super(TestRecentEnrollments, self).setUp() self.student = UserFactory() self.student.set_password(self.PASSWORD) self.student.save() # Old Course old_course_location = locator.CourseLocator('Org0', 'Course0', 'Run0') __, enrollment = self._create_course_and_enrollment(old_course_location) enrollment.created = datetime.datetime(1900, 12, 31, 0, 0, 0, 0) enrollment.save() # New Course course_location = locator.CourseLocator('Org1', 'Course1', 'Run1') self.course, self.enrollment = self._create_course_and_enrollment(course_location) def _create_course_and_enrollment(self, course_location): """ Creates a course and associated enrollment. """ course = CourseFactory.create( org=course_location.org, number=course_location.course, run=course_location.run ) enrollment = CourseEnrollment.enroll(self.student, course.id) return course, enrollment def _configure_message_timeout(self, timeout): """Configure the amount of time the enrollment message will be displayed. """ config = DashboardConfiguration(recent_enrollment_time_delta=timeout) config.save() def test_recently_enrolled_courses(self): """ Test if the function for filtering recent enrollments works appropriately. """ self._configure_message_timeout(60) # get courses through iterating all courses courses_list = list(get_course_enrollments(self.student, None, [])) self.assertEqual(len(courses_list), 2) recent_course_list = _get_recently_enrolled_courses(courses_list) self.assertEqual(len(recent_course_list), 1) def test_zero_second_delta(self): """ Tests that the recent enrollment list is empty if configured to zero seconds. """ self._configure_message_timeout(0) courses_list = list(get_course_enrollments(self.student, None, [])) self.assertEqual(len(courses_list), 2) recent_course_list = _get_recently_enrolled_courses(courses_list) self.assertEqual(len(recent_course_list), 0) def test_enrollments_sorted_most_recent(self): """ Test that the list of newly created courses are properly sorted to show the most recent enrollments first. """ self._configure_message_timeout(600) # Create a number of new enrollments and courses, and force their creation behind # the first enrollment courses = [] for idx, seconds_past in zip(range(2, 6), [5, 10, 15, 20]): course_location = locator.CourseLocator( 'Org{num}'.format(num=idx), 'Course{num}'.format(num=idx), 'Run{num}'.format(num=idx) ) course, enrollment = self._create_course_and_enrollment(course_location) enrollment.created = datetime.datetime.now(UTC) - datetime.timedelta(seconds=seconds_past) enrollment.save() courses.append(course) courses_list = list(get_course_enrollments(self.student, None, [])) self.assertEqual(len(courses_list), 6) recent_course_list = _get_recently_enrolled_courses(courses_list) self.assertEqual(len(recent_course_list), 5) self.assertEqual(recent_course_list[1].course.id, courses[0].id) self.assertEqual(recent_course_list[2].course.id, courses[1].id) self.assertEqual(recent_course_list[3].course.id, courses[2].id) self.assertEqual(recent_course_list[4].course.id, courses[3].id) def test_dashboard_rendering(self): """ Tests that the dashboard renders the recent enrollment messages appropriately. """ self._configure_message_timeout(600) self.client.login(username=self.student.username, password=self.PASSWORD) response = self.client.get(reverse("dashboard")) self.assertContains(response, "Thank you for enrolling in") def test_dashboard_escaped_rendering(self): """ Tests that the dashboard renders the escaped recent enrollment messages appropriately. """ self._configure_message_timeout(600) self.client.login(username=self.student.username, password=self.PASSWORD) # New Course course_location = locator.CourseLocator('TestOrg', 'TestCourse', 'TestRun') xss_content = "" course = CourseFactory.create( org=course_location.org, number=course_location.course, run=course_location.run, display_name=xss_content ) CourseEnrollment.enroll(self.student, course.id) response = self.client.get(reverse("dashboard")) self.assertContains(response, "Thank you for enrolling in") # Check if response is escaped self.assert_no_xss(response, xss_content) @ddt.data( # Register as honor in any course modes with no payment option ([('audit', 0), ('honor', 0)], 'honor', True), ([('honor', 0)], 'honor', True), # Register as honor in any course modes which has payment option ([('honor', 10)], 'honor', False), # This is a paid course ([('audit', 0), ('honor', 0), ('professional', 20)], 'honor', True), ([('audit', 0), ('honor', 0), ('verified', 20)], 'honor', True), ([('audit', 0), ('honor', 0), ('verified', 20), ('professional', 20)], 'honor', True), # Register as audit in any course modes with no payment option ([('audit', 0), ('honor', 0)], 'audit', True), ([('audit', 0)], 'audit', True), ([], 'audit', True), # Register as audit in any course modes which has no payment option ([('audit', 0), ('honor', 0), ('verified', 10)], 'audit', True), # Register as verified in any course modes which has payment option ([('professional', 20)], 'professional', False), ([('verified', 20)], 'verified', False), ([('professional', 20), ('verified', 20)], 'verified', False), ([('audit', 0), ('honor', 0), ('verified', 20)], 'verified', False) ) @ddt.unpack def test_donate_button(self, course_modes, enrollment_mode, show_donate): # Enable the enrollment success message self._configure_message_timeout(10000) # Enable donations DonationConfiguration(enabled=True).save() # Create the course mode(s) for mode, min_price in course_modes: CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=min_price) self.enrollment.mode = enrollment_mode self.enrollment.save() # Check that the donate button is or is not displayed self.client.login(username=self.student.username, password=self.PASSWORD) response = self.client.get(reverse("dashboard")) if show_donate: self.assertContains(response, "donate-container") else: self.assertNotContains(response, "donate-container") def test_donate_button_honor_with_price(self): # Enable the enrollment success message and donations self._configure_message_timeout(10000) DonationConfiguration(enabled=True).save() # Create a white-label course mode # (honor mode with a price set) CourseModeFactory.create(mode_slug="honor", course_id=self.course.id, min_price=100) # Check that the donate button is NOT displayed self.client.login(username=self.student.username, password=self.PASSWORD) response = self.client.get(reverse("dashboard")) self.assertNotContains(response, "donate-container") @ddt.data( (True, False,), (True, True,), (False, False,), (False, True,), ) @ddt.unpack def test_donate_button_with_enabled_site_configuration(self, enable_donation_config, enable_donation_site_config): # Enable the enrollment success message and donations self._configure_message_timeout(10000) # DonationConfiguration has low precedence if 'ENABLE_DONATIONS' is enable in SiteConfiguration DonationConfiguration(enabled=enable_donation_config).save() CourseModeFactory.create(mode_slug="audit", course_id=self.course.id, min_price=0) self.enrollment.mode = "audit" self.enrollment.save() self.client.login(username=self.student.username, password=self.PASSWORD) with with_site_configuration_context(configuration={'ENABLE_DONATIONS': enable_donation_site_config}): response = self.client.get(reverse("dashboard")) if enable_donation_site_config: self.assertContains(response, "donate-container") else: self.assertNotContains(response, "donate-container")