Files
edx-platform/lms/djangoapps/courseware/tests/test_navigation.py
Kyle McCormick 151bd13666 Use full names for common.djangoapps imports; warn when using old style (#25477)
* Generate common/djangoapps import shims for LMS
* Generate common/djangoapps import shims for Studio
* Stop appending project root to sys.path
* Stop appending common/djangoapps to sys.path
* Import from common.djangoapps.course_action_state instead of course_action_state
* Import from common.djangoapps.course_modes instead of course_modes
* Import from common.djangoapps.database_fixups instead of database_fixups
* Import from common.djangoapps.edxmako instead of edxmako
* Import from common.djangoapps.entitlements instead of entitlements
* Import from common.djangoapps.pipline_mako instead of pipeline_mako
* Import from common.djangoapps.static_replace instead of static_replace
* Import from common.djangoapps.student instead of student
* Import from common.djangoapps.terrain instead of terrain
* Import from common.djangoapps.third_party_auth instead of third_party_auth
* Import from common.djangoapps.track instead of track
* Import from common.djangoapps.util instead of util
* Import from common.djangoapps.xblock_django instead of xblock_django
* Add empty common/djangoapps/__init__.py to fix pytest collection
* Fix pylint formatting violations
* Exclude import_shims/ directory tree from linting
2020-11-10 07:02:01 -05:00

322 lines
12 KiB
Python

"""
This test file will run through some LMS test scenarios regarding access and navigation of the LMS
"""
import time
from django.conf import settings
from django.test.utils import override_settings
from django.urls import reverse
from mock import patch
from six import text_type
from six.moves import range
from edx_toggles.toggles.testutils import override_waffle_flag
from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
from openedx.features.course_experience import DISABLE_COURSE_OUTLINE_PAGE_FLAG
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
class TestNavigation(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Check that navigation state is saved properly.
"""
STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(TestNavigation, cls).setUpClassAndTestData():
cls.test_course = CourseFactory.create()
cls.test_course_proctored = CourseFactory.create()
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
cls.chapter0 = ItemFactory.create(parent=cls.course,
display_name='Overview')
cls.chapter9 = ItemFactory.create(parent=cls.course,
display_name='factory_chapter')
cls.section0 = ItemFactory.create(parent=cls.chapter0,
display_name='Welcome')
cls.section9 = ItemFactory.create(parent=cls.chapter9,
display_name='factory_section')
cls.unit0 = ItemFactory.create(parent=cls.section0,
display_name='New Unit 0')
cls.chapterchrome = ItemFactory.create(parent=cls.course,
display_name='Chrome')
cls.chromelesssection = ItemFactory.create(parent=cls.chapterchrome,
display_name='chromeless',
chrome='none')
cls.accordionsection = ItemFactory.create(parent=cls.chapterchrome,
display_name='accordion',
chrome='accordion')
cls.tabssection = ItemFactory.create(parent=cls.chapterchrome,
display_name='tabs',
chrome='tabs')
cls.defaultchromesection = ItemFactory.create(
parent=cls.chapterchrome,
display_name='defaultchrome',
)
cls.fullchromesection = ItemFactory.create(parent=cls.chapterchrome,
display_name='fullchrome',
chrome='accordion,tabs')
cls.tabtest = ItemFactory.create(parent=cls.chapterchrome,
display_name='pdf_textbooks_tab',
default_tab='progress')
cls.staff_user = GlobalStaffFactory()
cls.user = UserFactory()
def setUp(self):
super(TestNavigation, self).setUp()
# Create student accounts and activate them.
for i in range(len(self.STUDENT_INFO)):
email, password = self.STUDENT_INFO[i]
username = 'u{0}'.format(i)
self.create_account(username, email, password)
self.activate_user(email)
def assertTabActive(self, tabname, response):
''' Check if the progress tab is active in the tab set '''
for line in response.content.decode('utf-8').split('\n'):
if tabname in line and 'active' in line:
return
raise AssertionError(u"assertTabActive failed: {} not active".format(tabname))
def assertTabInactive(self, tabname, response):
''' Check if the progress tab is active in the tab set '''
for line in response.content.decode('utf-8').split('\n'):
if tabname in line and 'active' in line:
raise AssertionError("assertTabInactive failed: " + tabname + " active")
return
# TODO: LEARNER-71: Do we need to adjust or remove this test?
@override_waffle_flag(DISABLE_COURSE_OUTLINE_PAGE_FLAG, active=True)
def test_chrome_settings(self):
'''
Test settings for disabling and modifying navigation chrome in the courseware:
- Accordion enabled, or disabled
- Navigation tabs enabled, disabled, or redirected
'''
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.course, True)
test_data = (
('tabs', False, True),
('none', False, False),
('accordion', True, False),
('fullchrome', True, True),
)
for (displayname, accordion, tabs) in test_data:
response = self.client.get(reverse('courseware_section', kwargs={
'course_id': text_type(self.course.id),
'chapter': 'Chrome',
'section': displayname,
}))
self.assertEqual('course-tabs' in response.content.decode('utf-8'), tabs)
self.assertEqual('course-navigation' in response.content.decode('utf-8'), accordion)
self.assertTabInactive('progress', response)
self.assertTabActive('courseware', response)
response = self.client.get(reverse('courseware_section', kwargs={
'course_id': text_type(self.course.id),
'chapter': 'Chrome',
'section': 'pdf_textbooks_tab',
}))
self.assertTabActive('progress', response)
self.assertTabInactive('courseware', response)
@override_settings(SESSION_INACTIVITY_TIMEOUT_IN_SECONDS=1)
def test_inactive_session_timeout(self):
"""
Verify that an inactive session times out and redirects to the
login page
"""
email, password = self.STUDENT_INFO[0]
self.login(email, password)
# make sure we can access courseware immediately
resp = self.client.get(reverse('dashboard'))
self.assertEqual(resp.status_code, 200)
# then wait a bit and see if we get timed out
time.sleep(2)
resp = self.client.get(reverse('dashboard'))
# re-request, and we should get a redirect to login page
self.assertRedirects(resp, settings.LOGIN_REDIRECT_URL + '?next=' + reverse('dashboard'))
def test_redirects_first_time(self):
"""
Verify that the first time we click on the courseware tab we are
redirected to the 'Welcome' section.
"""
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.course, True)
self.enroll(self.test_course, True)
resp = self.client.get(reverse('courseware',
kwargs={'course_id': text_type(self.course.id)}))
self.assertRedirects(resp, reverse(
'courseware_section', kwargs={'course_id': text_type(self.course.id),
'chapter': 'Overview',
'section': 'Welcome'}))
def test_redirects_second_time(self):
"""
Verify the accordion remembers we've already visited the Welcome section
and redirects correspondingly.
"""
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.course, True)
self.enroll(self.test_course, True)
section_url = reverse(
'courseware_section',
kwargs={
'course_id': text_type(self.course.id),
'chapter': 'Overview',
'section': 'Welcome',
},
)
self.client.get(section_url)
resp = self.client.get(
reverse('courseware', kwargs={'course_id': text_type(self.course.id)}),
)
self.assertRedirects(resp, section_url)
def test_accordion_state(self):
"""
Verify the accordion remembers which chapter you were last viewing.
"""
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.course, True)
self.enroll(self.test_course, True)
# Now we directly navigate to a section in a chapter other than 'Overview'.
section_url = reverse(
'courseware_section',
kwargs={
'course_id': text_type(self.course.id),
'chapter': 'factory_chapter',
'section': 'factory_section',
}
)
self.assert_request_status_code(200, section_url)
# And now hitting the courseware tab should redirect to 'factory_chapter'
url = reverse(
'courseware',
kwargs={'course_id': text_type(self.course.id)}
)
resp = self.client.get(url)
self.assertRedirects(resp, section_url)
# TODO: LEARNER-71: Do we need to adjust or remove this test?
@override_waffle_flag(DISABLE_COURSE_OUTLINE_PAGE_FLAG, active=True)
def test_incomplete_course(self):
email = self.staff_user.email
password = "test"
self.login(email, password)
self.enroll(self.test_course, True)
test_course_id = text_type(self.test_course.id)
url = reverse(
'courseware',
kwargs={'course_id': test_course_id}
)
response = self.assert_request_status_code(200, url)
self.assertContains(response, "No content has been added to this course")
section = ItemFactory.create(
parent_location=self.test_course.location,
display_name='New Section'
)
url = reverse(
'courseware',
kwargs={'course_id': test_course_id}
)
response = self.assert_request_status_code(200, url)
self.assertNotContains(response, "No content has been added to this course")
self.assertContains(response, "New Section")
subsection = ItemFactory.create(
parent_location=section.location,
display_name='New Subsection',
)
url = reverse(
'courseware',
kwargs={'course_id': test_course_id}
)
response = self.assert_request_status_code(200, url)
self.assertContains(response, "New Subsection")
self.assertNotContains(response, "sequence-nav")
ItemFactory.create(
parent_location=subsection.location,
display_name='New Unit',
)
url = reverse(
'courseware',
kwargs={'course_id': test_course_id}
)
self.assert_request_status_code(302, url)
def test_proctoring_js_includes(self):
"""
Make sure that proctoring JS does not get included on
courseware pages if either the FEATURE flag is turned off
or the course is not proctored enabled
"""
email, password = self.STUDENT_INFO[0]
self.login(email, password)
self.enroll(self.test_course_proctored, True)
test_course_id = text_type(self.test_course_proctored.id)
with patch.dict(settings.FEATURES, {'ENABLE_SPECIAL_EXAMS': False}):
url = reverse(
'courseware',
kwargs={'course_id': test_course_id}
)
resp = self.client.get(url)
self.assertNotContains(resp, '/static/js/lms-proctoring.js')
with patch.dict(settings.FEATURES, {'ENABLE_SPECIAL_EXAMS': True}):
url = reverse(
'courseware',
kwargs={'course_id': test_course_id}
)
resp = self.client.get(url)
self.assertNotContains(resp, '/static/js/lms-proctoring.js')
# now set up a course which is proctored enabled
self.test_course_proctored.enable_proctored_exams = True
self.test_course_proctored.save()
modulestore().update_item(self.test_course_proctored, self.user.id)
resp = self.client.get(url)
self.assertContains(resp, '/static/js/lms-proctoring.js')