Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lms/djangoapps/bulk_email/tests/test_course_optout.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,25 @@
from edx_ace.message import Message
from edx_ace.policy import PolicyResult
from edx_ace.recipient import Recipient
from edx_toggles.toggles.testutils import override_waffle_flag

from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
from lms.djangoapps.bulk_email.api import get_unsubscribed_link
from lms.djangoapps.bulk_email.models import BulkEmailFlag
from lms.djangoapps.bulk_email.policies import CourseEmailOptout
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
from xmodule.modulestore.tests.django_utils import (
ModuleStoreTestCase, # lint-amnesty, pylint: disable=wrong-import-order
)
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order


@patch('lms.djangoapps.bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message', autospec=True)) # lint-amnesty, pylint: disable=line-too-long
# Tests for legacy views. When DEPR-38432 is picked up, these tests will require the following changes:
# Either remove or leave the specific parts that reference the legacy instructor dashboard,
# and remove the override_waffle_flag for LEGACY_INSTRUCTOR_DASHBOARD.
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
class TestOptoutCourseEmails(ModuleStoreTestCase):
"""
Test that optouts are referenced in sending course email.
Expand Down
6 changes: 6 additions & 0 deletions lms/djangoapps/bulk_email/tests/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from django.test.utils import override_settings
from django.urls import reverse
from django.utils.translation import get_language
from edx_toggles.toggles.testutils import override_waffle_flag
from markupsafe import escape

from common.djangoapps.course_modes.models import CourseMode
Expand All @@ -29,6 +30,7 @@
)
from lms.djangoapps.bulk_email.messages import ACEEmail
from lms.djangoapps.bulk_email.tasks import _get_course_email_context, _get_source_address
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
from lms.djangoapps.instructor_task.subtasks import update_subtask_status
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
from openedx.core.djangoapps.course_groups.models import CourseCohort
Expand Down Expand Up @@ -62,6 +64,10 @@ def mock_update_subtask_status(entry_id, current_task_id, new_subtask_status):
return mock_update_subtask_status


# Tests for legacy views. When DEPR-38432 is picked up, these tests will require the following changes:
# Either remove or leave the specific parts that reference the legacy instructor dashboard,
# and remove the override_waffle_flag for LEGACY_INSTRUCTOR_DASHBOARD.
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
class EmailSendFromDashboardTestCase(SharedModuleStoreTestCase):
"""
Test that emails send correctly.
Expand Down
6 changes: 6 additions & 0 deletions lms/djangoapps/bulk_email/tests/test_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,25 @@
from django.core import mail
from django.core.management import call_command
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.keys import CourseKey

from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
from lms.djangoapps.bulk_email.models import BulkEmailFlag, Optout
from lms.djangoapps.bulk_email.signals import force_optout_all
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
from xmodule.modulestore.tests.django_utils import (
ModuleStoreTestCase, # lint-amnesty, pylint: disable=wrong-import-order
)
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order


@patch('lms.djangoapps.bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message', autospec=True)) # lint-amnesty, pylint: disable=line-too-long
# Tests for legacy views. When DEPR-38432 is picked up, these tests will require the following changes:
# Either remove or leave the specific parts that reference the legacy instructor dashboard,
# and remove the override_waffle_flag for LEGACY_INSTRUCTOR_DASHBOARD.
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
class TestOptoutCourseEmailsBySignal(ModuleStoreTestCase):
"""
Tests that the force_optout_all signal receiver opts the user out of course emails
Expand Down
200 changes: 193 additions & 7 deletions lms/djangoapps/courseware/tests/test_view_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import pytz
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag

from common.djangoapps.student.tests.factories import (
BetaTesterFactory,
Expand All @@ -21,6 +22,7 @@
)
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.tests.helpers import CourseAccessTestMixin, LoginEnrollmentTestCase
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
Expand Down Expand Up @@ -92,7 +94,11 @@ def _check_staff(self, course):
for index in range(len(course.textbooks))
])
for url in urls:
self.assert_request_status_code(200, url)
# Instructor dashboard returns 302 (MFE redirect) by default
if 'instructor' in url:
self.assert_request_status_code(302, url)
else:
self.assert_request_status_code(200, url)

# The student progress tab is not accessible to a student
# before launch, so the instructor view-as-student feature
Expand All @@ -109,6 +115,58 @@ def _check_staff(self, course):
)
self.assert_request_status_code(302, url)

def _check_staff_legacy(self, course):
"""
Check that access is right for staff in course with legacy instructor dashboard enabled.
"""
names = ['about_course', 'instructor_dashboard', 'progress']
urls = self._reverse_urls(names, course)
urls.extend([
reverse('book', kwargs={'course_id': str(course.id),
'book_index': index})
for index in range(len(course.textbooks))
])
for url in urls:
# With legacy flag enabled, all URLs return 200 (instructor dashboard skips MFE redirect)
self.assert_request_status_code(200, url)

# The student progress tab behavior is affected by legacy flag in normal scenarios
url = reverse(
'student_progress',
kwargs={
'course_id': str(course.id),
'student_id': self.enrolled_user.id,
}
)
self.assert_request_status_code(200, url)

def _check_staff_legacy_dark_launch(self, course):
"""
Check staff access during dark launch with legacy instructor dashboard enabled.
In dark launch scenarios, student progress URL still returns 302 even with legacy flag.
"""
names = ['about_course', 'instructor_dashboard', 'progress']
urls = self._reverse_urls(names, course)
urls.extend([
reverse('book', kwargs={'course_id': str(course.id),
'book_index': index})
for index in range(len(course.textbooks))
])
for url in urls:
# With legacy flag enabled, all URLs return 200 (instructor dashboard skips MFE redirect)
self.assert_request_status_code(200, url)

# In dark launch scenarios, student progress URL still returns 302 even with legacy flag
# because course access restrictions take precedence
url = reverse(
'student_progress',
kwargs={
'course_id': str(course.id),
'student_id': self.enrolled_user.id,
}
)
self.assert_request_status_code(302, url)

def login(self, user): # lint-amnesty, pylint: disable=arguments-differ
return super().login(user.email, self.TEST_PASSWORD)

Expand Down Expand Up @@ -186,7 +244,7 @@ def test_staff_course_access(self):

# Now should be able to get to self.course, but not self.test_course
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)
self.assert_request_status_code(302, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(404, url)
Expand All @@ -200,7 +258,7 @@ def test_instructor_course_access(self):

# Now should be able to get to self.course, but not self.test_course
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)
self.assert_request_status_code(302, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(404, url)
Expand All @@ -212,10 +270,10 @@ def test_org_staff_access(self):
"""
self.login(self.org_staff_user)
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)
self.assert_request_status_code(302, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(200, url)
self.assert_request_status_code(302, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
self.assert_request_status_code(404, url)
Expand All @@ -227,10 +285,10 @@ def test_org_instructor_access(self):
"""
self.login(self.org_instructor_user)
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)
self.assert_request_status_code(302, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(200, url)
self.assert_request_status_code(302, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
self.assert_request_status_code(404, url)
Expand All @@ -242,12 +300,94 @@ def test_global_staff_access(self):
self.login(self.global_staff_user)

# and now should be able to load both
urls = [reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)}),
reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})]

for url in urls:
self.assert_request_status_code(302, url)

# Legacy instructor dashboard tests (with waffle flag enabled, expect 200 responses)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
def test_staff_course_access_legacy(self):
"""
Verify staff can load the legacy instructor dashboard (expects 200 response).
"""
self.login(self.staff_user)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(404, url)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
def test_instructor_course_access_legacy(self):
"""
Verify instructor can load the legacy instructor dashboard (expects 200 response).
"""
self.login(self.instructor_user)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(404, url)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
def test_org_staff_access_legacy(self):
"""
Verify org staff can load the legacy instructor dashboard (expects 200 response).
"""
self.login(self.org_staff_user)
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(200, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
self.assert_request_status_code(404, url)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
def test_org_instructor_access_legacy(self):
"""
Verify org instructor can load the legacy instructor dashboard (expects 200 response).
"""
self.login(self.org_instructor_user)
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
self.assert_request_status_code(200, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
self.assert_request_status_code(200, url)

url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
self.assert_request_status_code(404, url)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
def test_global_staff_access_legacy(self):
"""
Verify the global staff user can access the legacy instructor dashboard (expects 200 response).
"""
self.login(self.global_staff_user)

urls = [reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)}),
reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})]

for url in urls:
self.assert_request_status_code(200, url)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
def test_staff_method_legacy(self):
"""
Test the _check_staff_legacy helper method with legacy flag enabled (expects 200 response).
"""
self.login(self.staff_user)
self.enroll(self.course, True)

# Test the _check_staff_legacy method which includes instructor dashboard checks
self._check_staff_legacy(self.course)

@patch.dict('lms.djangoapps.courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_dark_launch_enrolled_student(self):
"""
Expand Down Expand Up @@ -355,6 +495,52 @@ def test_enrollment_period(self):
self.login(self.global_staff_user)
assert self.enroll(self.course)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
@patch.dict('lms.djangoapps.courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_dark_launch_instructor_legacy(self):
"""
Make sure that before course start instructors can access the
page for their course with legacy instructor dashboard enabled.
"""
now = datetime.datetime.now(pytz.UTC)
tomorrow = now + datetime.timedelta(days=1)
self.course.start = tomorrow
self.test_course.start = tomorrow
self.course = self.update_course(self.course, self.user.id)
self.test_course = self.update_course(self.test_course, self.user.id)

self.login(self.instructor_user)
# Enroll in the classes---can't see courseware otherwise.
self.enroll(self.course, True)
self.enroll(self.test_course, True)

# should now be able to get to everything for self.course
self._check_staff_legacy_dark_launch(self.course)
self._check_non_staff_dark(self.test_course)

@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
@patch.dict('lms.djangoapps.courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_dark_launch_global_staff_legacy(self):
"""
Make sure that before course start staff can access
course pages with legacy instructor dashboard enabled.
"""
now = datetime.datetime.now(pytz.UTC)
tomorrow = now + datetime.timedelta(days=1)

self.course.start = tomorrow
self.test_course.start = tomorrow
self.course = self.update_course(self.course, self.user.id)
self.test_course = self.update_course(self.test_course, self.user.id)

self.login(self.global_staff_user)
self.enroll(self.course, True)
self.enroll(self.test_course, True)

# and now should be able to load both
self._check_staff_legacy_dark_launch(self.course)
self._check_staff_legacy_dark_launch(self.test_course)


class TestBetatesterAccess(ModuleStoreTestCase, CourseAccessTestMixin):
"""
Expand Down
Loading
Loading