test_course_info.py 10.4 KB
Newer Older
1 2 3
"""
Test the course_info xblock
"""
4
import mock
5
from nose.plugins.attrib import attr
6
from pyquery import PyQuery as pq
7
from urllib import urlencode
Ned Batchelder committed
8

9
from ccx_keys.locator import CCXLocator
10
from django.conf import settings
Ned Batchelder committed
11
from django.core.urlresolvers import reverse
12
from django.test.utils import override_settings
13
from opaque_keys.edx.locations import SlashSeparatedCourseKey
14

15
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
16
from util.date_utils import strftime_localized
17 18 19 20 21
from xmodule.modulestore.tests.django_utils import (
    ModuleStoreTestCase,
    SharedModuleStoreTestCase,
    TEST_DATA_SPLIT_MODULESTORE
)
Ned Batchelder committed
22
from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_CLOSED_MODULESTORE
23
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls
24
from student.models import CourseEnrollment
25
from student.tests.factories import AdminFactory
26

27 28
from .helpers import LoginEnrollmentTestCase

29 30
from lms.djangoapps.ccx.tests.factories import CcxFactory

31

32
@attr('shard_1')
33
class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
34 35 36
    """
    Tests for the Course Info page
    """
37
    def setUp(self):
38
        super(CourseInfoTestCase, self).setUp()
39 40 41 42 43 44
        self.course = CourseFactory.create()
        self.page = ItemFactory.create(
            category="course_info", parent_location=self.course.location,
            data="OOGIE BLOOGIE", display_name="updates"
        )

45
    def test_logged_in_unenrolled(self):
46
        self.setup_user()
47
        url = reverse('info', args=[self.course.id.to_deprecated_string()])
48 49 50
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertIn("OOGIE BLOOGIE", resp.content)
51 52 53 54 55 56 57
        self.assertIn("You are not currently enrolled in this course", resp.content)

    def test_logged_in_enrolled(self):
        self.enroll(self.course)
        url = reverse('info', args=[self.course.id.to_deprecated_string()])
        resp = self.client.get(url)
        self.assertNotIn("You are not currently enrolled in this course", resp.content)
58 59

    def test_anonymous_user(self):
60
        url = reverse('info', args=[self.course.id.to_deprecated_string()])
61 62 63
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertNotIn("OOGIE BLOOGIE", resp.content)
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78
    def test_logged_in_not_enrolled(self):
        self.setup_user()
        url = reverse('info', args=[self.course.id.to_deprecated_string()])
        self.client.get(url)

        # Check whether the user has been enrolled in the course.
        # There was a bug in which users would be automatically enrolled
        # with is_active=False (same as if they enrolled and immediately unenrolled).
        # This verifies that the user doesn't have *any* enrollment record.
        enrollment_exists = CourseEnrollment.objects.filter(
            user=self.user, course_id=self.course.id
        ).exists()
        self.assertFalse(enrollment_exists)

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    @mock.patch.dict(settings.FEATURES, {'DISABLE_START_DATES': False})
    def test_non_live_course(self):
        """Ensure that a user accessing a non-live course sees a redirect to
        the student dashboard, not a 404.
        """
        self.setup_user()
        self.enroll(self.course)
        url = reverse('info', args=[unicode(self.course.id)])
        response = self.client.get(url)
        start_date = strftime_localized(self.course.start, 'SHORT_DATE')
        self.assertRedirects(response, '{0}?{1}'.format(reverse('dashboard'), urlencode({'notlive': start_date})))

    def test_nonexistent_course(self):
        self.setup_user()
        url = reverse('info', args=['not/a/course'])
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

97
    def test_last_accessed_courseware_not_shown(self):
98 99 100 101
        """
        Test that the last accessed courseware link is not shown if there
        is no course content.
        """
102 103 104
        SelfPacedConfiguration(enable_course_home_improvements=True).save()
        url = reverse('info', args=(unicode(self.course.id),))
        response = self.client.get(url)
105 106
        content = pq(response.content)
        self.assertEqual(content('.page-header-secondary a').length, 0)
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

    def test_last_accessed_shown(self):
        SelfPacedConfiguration(enable_course_home_improvements=True).save()
        chapter = ItemFactory.create(
            category="chapter", parent_location=self.course.location
        )
        section = ItemFactory.create(
            category='section', parent_location=chapter.location
        )
        section_url = reverse(
            'courseware_section',
            kwargs={
                'section': section.url_name,
                'chapter': chapter.url_name,
                'course_id': self.course.id
            }
        )
        self.client.get(section_url)
        info_url = reverse('info', args=(unicode(self.course.id),))
        info_page_response = self.client.get(info_url)
127 128
        content = pq(info_page_response.content)
        self.assertEqual(content('.page-header-secondary .last-accessed-link').attr('href'), section_url)
129

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
    def test_info_title(self):
        """
        Test the info page on a course without any display_* settings against
        one that does.
        """
        url = reverse('info', args=(unicode(self.course.id),))
        response = self.client.get(url)
        content = pq(response.content)
        expected_title = "Welcome to {org}'s {course_name}!".format(
            org=self.course.display_org_with_default,
            course_name=self.course.display_number_with_default
        )
        display_course = CourseFactory.create(
            org="HogwartZ",
            number="Potions_3",
            display_organization="HogwartsX",
            display_coursenumber="Potions",
            display_name="Introduction_to_Potions"
        )
        display_url = reverse('info', args=(unicode(display_course.id),))
        display_response = self.client.get(display_url)
        display_content = pq(display_response.content)
        expected_display_title = "Welcome to {org}'s {course_name}!".format(
            org=display_course.display_org_with_default,
            course_name=display_course.display_number_with_default
        )
        self.assertIn(
            expected_title,
            content('h1.page-title').contents()
        )
        self.assertIn(
            expected_display_title,
            display_content('h1.page-title').contents()
        )
        self.assertIn(
            display_course.display_name_with_default,
            display_content('h2.page-subtitle').contents()
        )

Adam Palay committed
169

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
class CourseInfoTestCaseCCX(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
    """
    Test for unenrolled student tries to access ccx.
    Note: Only CCX coach can enroll a student in CCX. In sum self-registration not allowed.
    """

    MODULESTORE = TEST_DATA_SPLIT_MODULESTORE

    @classmethod
    def setUpClass(cls):
        super(CourseInfoTestCaseCCX, cls).setUpClass()
        cls.course = CourseFactory.create()

    def setUp(self):
        super(CourseInfoTestCaseCCX, self).setUp()

        # Create ccx coach account
        self.coach = coach = AdminFactory.create(password="test")
        self.client.login(username=coach.username, password="test")

    def test_redirect_to_dashboard_unenrolled_ccx(self):
        """
        Assert that when unenroll student tries to access ccx do not allow him self-register.
        Redirect him to his student dashboard
        """
        # create ccx
        ccx = CcxFactory(course_id=self.course.id, coach=self.coach)
        ccx_locator = CCXLocator.from_course_locator(self.course.id, unicode(ccx.id))

        self.setup_user()
        url = reverse('info', args=[ccx_locator])
        response = self.client.get(url)
        expected = reverse('dashboard')
        self.assertRedirects(response, expected, status_code=302, target_status_code=200)


206
@attr('shard_1')
Adam Palay committed
207
class CourseInfoTestCaseXML(LoginEnrollmentTestCase, ModuleStoreTestCase):
208 209 210
    """
    Tests for the Course Info page for an XML course
    """
211 212
    MODULESTORE = TEST_DATA_MIXED_CLOSED_MODULESTORE

Adam Palay committed
213 214 215
    # The following XML test course (which lives at common/test/data/2014)
    # is closed; we're testing that a course info page still appears when
    # the course is already closed
216
    xml_course_key = SlashSeparatedCourseKey('edX', 'detached_pages', '2014')
Adam Palay committed
217 218 219 220 221

    # this text appears in that course's course info page
    # common/test/data/2014/info/updates.html
    xml_data = "course info 463139"

222 223 224
    @mock.patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
    def test_logged_in_xml(self):
        self.setup_user()
225
        url = reverse('info', args=[self.xml_course_key.to_deprecated_string()])
226 227 228 229 230 231
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertIn(self.xml_data, resp.content)

    @mock.patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
    def test_anonymous_user_xml(self):
232
        url = reverse('info', args=[self.xml_course_key.to_deprecated_string()])
233 234 235
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertNotIn(self.xml_data, resp.content)
236 237 238 239 240 241 242 243 244 245


@attr('shard_1')
@override_settings(FEATURES=dict(settings.FEATURES, EMBARGO=False))
class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
    """
    Tests for the info page of self-paced courses.
    """

    def setUp(self):
246
        SelfPacedConfiguration(enabled=True).save()
247
        super(SelfPacedCourseInfoTestCase, self).setUp()
248
        self.instructor_paced_course = CourseFactory.create(self_paced=False)
249 250 251 252 253 254 255 256 257 258 259 260 261 262
        self.self_paced_course = CourseFactory.create(self_paced=True)
        self.setup_user()

    def fetch_course_info_with_queries(self, course, sql_queries, mongo_queries):
        """
        Fetch the given course's info page, asserting the number of SQL
        and Mongo queries.
        """
        url = reverse('info', args=[unicode(course.id)])
        with self.assertNumQueries(sql_queries):
            with check_mongo_calls(mongo_queries):
                resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)

263
    def test_num_queries_instructor_paced(self):
264
        self.fetch_course_info_with_queries(self.instructor_paced_course, 19, 4)
265 266

    def test_num_queries_self_paced(self):
267
        self.fetch_course_info_with_queries(self.self_paced_course, 19, 4)