Skip to content

Commit 08e5fef

Browse files
holaontiverosVAIBHAVPANT07
authored andcommitted
feat: added redirect to new instructor dash MFE
Enables by default a redirection from the old instructor dashboard views to the new instructor dashboard front end app. Course author, staff or anyone with the instructor dashboard access will now see by default the new frontendapp A waffle flag is introduced that will enable the legacy dashboard LEGACY_INSTRUCTOR_DASHBOARD that will be deprecated in Willow.
1 parent fe6b0cd commit 08e5fef

11 files changed

Lines changed: 284 additions & 9 deletions

File tree

lms/djangoapps/bulk_email/tests/test_course_optout.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@
1313
from edx_ace.message import Message
1414
from edx_ace.policy import PolicyResult
1515
from edx_ace.recipient import Recipient
16+
from edx_toggles.toggles.testutils import override_waffle_flag
1617

1718
from common.djangoapps.student.models import CourseEnrollment
1819
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
1920
from lms.djangoapps.bulk_email.api import get_unsubscribed_link
2021
from lms.djangoapps.bulk_email.models import BulkEmailFlag
2122
from lms.djangoapps.bulk_email.policies import CourseEmailOptout
23+
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
2224
from xmodule.modulestore.tests.django_utils import (
2325
ModuleStoreTestCase, # lint-amnesty, pylint: disable=wrong-import-order
2426
)
2527
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
2628

2729

2830
@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
31+
# Tests for legacy views. When DEPR-38432 is picked up, these tests will require the following changes:
32+
# Either remove or leave the specific parts that reference the legacy instructor dashboard,
33+
# and remove the override_waffle_flag for LEGACY_INSTRUCTOR_DASHBOARD.
34+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
2935
class TestOptoutCourseEmails(ModuleStoreTestCase):
3036
"""
3137
Test that optouts are referenced in sending course email.

lms/djangoapps/bulk_email/tests/test_email.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from django.test.utils import override_settings
1717
from django.urls import reverse
1818
from django.utils.translation import get_language
19+
from edx_toggles.toggles.testutils import override_waffle_flag
1920
from markupsafe import escape
2021

2122
from common.djangoapps.course_modes.models import CourseMode
@@ -29,6 +30,7 @@
2930
)
3031
from lms.djangoapps.bulk_email.messages import ACEEmail
3132
from lms.djangoapps.bulk_email.tasks import _get_course_email_context, _get_source_address
33+
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
3234
from lms.djangoapps.instructor_task.subtasks import update_subtask_status
3335
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
3436
from openedx.core.djangoapps.course_groups.models import CourseCohort
@@ -62,6 +64,10 @@ def mock_update_subtask_status(entry_id, current_task_id, new_subtask_status):
6264
return mock_update_subtask_status
6365

6466

67+
# Tests for legacy views. When DEPR-38432 is picked up, these tests will require the following changes:
68+
# Either remove or leave the specific parts that reference the legacy instructor dashboard,
69+
# and remove the override_waffle_flag for LEGACY_INSTRUCTOR_DASHBOARD.
70+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
6571
class EmailSendFromDashboardTestCase(SharedModuleStoreTestCase):
6672
"""
6773
Test that emails send correctly.

lms/djangoapps/bulk_email/tests/test_signals.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,25 @@
99
from django.core import mail
1010
from django.core.management import call_command
1111
from django.urls import reverse
12+
from edx_toggles.toggles.testutils import override_waffle_flag
1213
from opaque_keys.edx.keys import CourseKey
1314

1415
from common.djangoapps.student.models import CourseEnrollment
1516
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
1617
from lms.djangoapps.bulk_email.models import BulkEmailFlag, Optout
1718
from lms.djangoapps.bulk_email.signals import force_optout_all
19+
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
1820
from xmodule.modulestore.tests.django_utils import (
1921
ModuleStoreTestCase, # lint-amnesty, pylint: disable=wrong-import-order
2022
)
2123
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
2224

2325

2426
@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
27+
# Tests for legacy views. When DEPR-38432 is picked up, these tests will require the following changes:
28+
# Either remove or leave the specific parts that reference the legacy instructor dashboard,
29+
# and remove the override_waffle_flag for LEGACY_INSTRUCTOR_DASHBOARD.
30+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
2531
class TestOptoutCourseEmailsBySignal(ModuleStoreTestCase):
2632
"""
2733
Tests that the force_optout_all signal receiver opts the user out of course emails

lms/djangoapps/courseware/tests/test_view_authentication.py

Lines changed: 193 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import pytz
1010
from django.urls import reverse
11+
from edx_toggles.toggles.testutils import override_waffle_flag
1112

1213
from common.djangoapps.student.tests.factories import (
1314
BetaTesterFactory,
@@ -21,6 +22,7 @@
2122
)
2223
from lms.djangoapps.courseware.access import has_access
2324
from lms.djangoapps.courseware.tests.helpers import CourseAccessTestMixin, LoginEnrollmentTestCase
25+
from lms.djangoapps.instructor.toggles import LEGACY_INSTRUCTOR_DASHBOARD
2426
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
2527
from xmodule.modulestore.django import modulestore
2628
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -92,7 +94,11 @@ def _check_staff(self, course):
9294
for index in range(len(course.textbooks))
9395
])
9496
for url in urls:
95-
self.assert_request_status_code(200, url)
97+
# Instructor dashboard returns 302 (MFE redirect) by default
98+
if 'instructor' in url:
99+
self.assert_request_status_code(302, url)
100+
else:
101+
self.assert_request_status_code(200, url)
96102

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

118+
def _check_staff_legacy(self, course):
119+
"""
120+
Check that access is right for staff in course with legacy instructor dashboard enabled.
121+
"""
122+
names = ['about_course', 'instructor_dashboard', 'progress']
123+
urls = self._reverse_urls(names, course)
124+
urls.extend([
125+
reverse('book', kwargs={'course_id': str(course.id),
126+
'book_index': index})
127+
for index in range(len(course.textbooks))
128+
])
129+
for url in urls:
130+
# With legacy flag enabled, all URLs return 200 (instructor dashboard skips MFE redirect)
131+
self.assert_request_status_code(200, url)
132+
133+
# The student progress tab behavior is affected by legacy flag in normal scenarios
134+
url = reverse(
135+
'student_progress',
136+
kwargs={
137+
'course_id': str(course.id),
138+
'student_id': self.enrolled_user.id,
139+
}
140+
)
141+
self.assert_request_status_code(200, url)
142+
143+
def _check_staff_legacy_dark_launch(self, course):
144+
"""
145+
Check staff access during dark launch with legacy instructor dashboard enabled.
146+
In dark launch scenarios, student progress URL still returns 302 even with legacy flag.
147+
"""
148+
names = ['about_course', 'instructor_dashboard', 'progress']
149+
urls = self._reverse_urls(names, course)
150+
urls.extend([
151+
reverse('book', kwargs={'course_id': str(course.id),
152+
'book_index': index})
153+
for index in range(len(course.textbooks))
154+
])
155+
for url in urls:
156+
# With legacy flag enabled, all URLs return 200 (instructor dashboard skips MFE redirect)
157+
self.assert_request_status_code(200, url)
158+
159+
# In dark launch scenarios, student progress URL still returns 302 even with legacy flag
160+
# because course access restrictions take precedence
161+
url = reverse(
162+
'student_progress',
163+
kwargs={
164+
'course_id': str(course.id),
165+
'student_id': self.enrolled_user.id,
166+
}
167+
)
168+
self.assert_request_status_code(302, url)
169+
112170
def login(self, user): # lint-amnesty, pylint: disable=arguments-differ
113171
return super().login(user.email, self.TEST_PASSWORD)
114172

@@ -186,7 +244,7 @@ def test_staff_course_access(self):
186244

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

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

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

205263
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
206264
self.assert_request_status_code(404, url)
@@ -212,10 +270,10 @@ def test_org_staff_access(self):
212270
"""
213271
self.login(self.org_staff_user)
214272
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
215-
self.assert_request_status_code(200, url)
273+
self.assert_request_status_code(302, url)
216274

217275
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
218-
self.assert_request_status_code(200, url)
276+
self.assert_request_status_code(302, url)
219277

220278
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
221279
self.assert_request_status_code(404, url)
@@ -227,10 +285,10 @@ def test_org_instructor_access(self):
227285
"""
228286
self.login(self.org_instructor_user)
229287
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
230-
self.assert_request_status_code(200, url)
288+
self.assert_request_status_code(302, url)
231289

232290
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
233-
self.assert_request_status_code(200, url)
291+
self.assert_request_status_code(302, url)
234292

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

244302
# and now should be able to load both
303+
urls = [reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)}),
304+
reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})]
305+
306+
for url in urls:
307+
self.assert_request_status_code(302, url)
308+
309+
# Legacy instructor dashboard tests (with waffle flag enabled, expect 200 responses)
310+
311+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
312+
def test_staff_course_access_legacy(self):
313+
"""
314+
Verify staff can load the legacy instructor dashboard (expects 200 response).
315+
"""
316+
self.login(self.staff_user)
317+
318+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
319+
self.assert_request_status_code(200, url)
320+
321+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
322+
self.assert_request_status_code(404, url)
323+
324+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
325+
def test_instructor_course_access_legacy(self):
326+
"""
327+
Verify instructor can load the legacy instructor dashboard (expects 200 response).
328+
"""
329+
self.login(self.instructor_user)
330+
331+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
332+
self.assert_request_status_code(200, url)
333+
334+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
335+
self.assert_request_status_code(404, url)
336+
337+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
338+
def test_org_staff_access_legacy(self):
339+
"""
340+
Verify org staff can load the legacy instructor dashboard (expects 200 response).
341+
"""
342+
self.login(self.org_staff_user)
343+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
344+
self.assert_request_status_code(200, url)
345+
346+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
347+
self.assert_request_status_code(200, url)
348+
349+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
350+
self.assert_request_status_code(404, url)
351+
352+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
353+
def test_org_instructor_access_legacy(self):
354+
"""
355+
Verify org instructor can load the legacy instructor dashboard (expects 200 response).
356+
"""
357+
self.login(self.org_instructor_user)
358+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)})
359+
self.assert_request_status_code(200, url)
360+
361+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})
362+
self.assert_request_status_code(200, url)
363+
364+
url = reverse('instructor_dashboard', kwargs={'course_id': str(self.other_org_course.id)})
365+
self.assert_request_status_code(404, url)
366+
367+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
368+
def test_global_staff_access_legacy(self):
369+
"""
370+
Verify the global staff user can access the legacy instructor dashboard (expects 200 response).
371+
"""
372+
self.login(self.global_staff_user)
373+
245374
urls = [reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)}),
246375
reverse('instructor_dashboard', kwargs={'course_id': str(self.test_course.id)})]
247376

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

380+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
381+
def test_staff_method_legacy(self):
382+
"""
383+
Test the _check_staff_legacy helper method with legacy flag enabled (expects 200 response).
384+
"""
385+
self.login(self.staff_user)
386+
self.enroll(self.course, True)
387+
388+
# Test the _check_staff_legacy method which includes instructor dashboard checks
389+
self._check_staff_legacy(self.course)
390+
251391
@patch.dict('lms.djangoapps.courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
252392
def test_dark_launch_enrolled_student(self):
253393
"""
@@ -355,6 +495,52 @@ def test_enrollment_period(self):
355495
self.login(self.global_staff_user)
356496
assert self.enroll(self.course)
357497

498+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
499+
@patch.dict('lms.djangoapps.courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
500+
def test_dark_launch_instructor_legacy(self):
501+
"""
502+
Make sure that before course start instructors can access the
503+
page for their course with legacy instructor dashboard enabled.
504+
"""
505+
now = datetime.datetime.now(pytz.UTC)
506+
tomorrow = now + datetime.timedelta(days=1)
507+
self.course.start = tomorrow
508+
self.test_course.start = tomorrow
509+
self.course = self.update_course(self.course, self.user.id)
510+
self.test_course = self.update_course(self.test_course, self.user.id)
511+
512+
self.login(self.instructor_user)
513+
# Enroll in the classes---can't see courseware otherwise.
514+
self.enroll(self.course, True)
515+
self.enroll(self.test_course, True)
516+
517+
# should now be able to get to everything for self.course
518+
self._check_staff_legacy_dark_launch(self.course)
519+
self._check_non_staff_dark(self.test_course)
520+
521+
@override_waffle_flag(LEGACY_INSTRUCTOR_DASHBOARD, active=True)
522+
@patch.dict('lms.djangoapps.courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
523+
def test_dark_launch_global_staff_legacy(self):
524+
"""
525+
Make sure that before course start staff can access
526+
course pages with legacy instructor dashboard enabled.
527+
"""
528+
now = datetime.datetime.now(pytz.UTC)
529+
tomorrow = now + datetime.timedelta(days=1)
530+
531+
self.course.start = tomorrow
532+
self.test_course.start = tomorrow
533+
self.course = self.update_course(self.course, self.user.id)
534+
self.test_course = self.update_course(self.test_course, self.user.id)
535+
536+
self.login(self.global_staff_user)
537+
self.enroll(self.course, True)
538+
self.enroll(self.test_course, True)
539+
540+
# and now should be able to load both
541+
self._check_staff_legacy_dark_launch(self.course)
542+
self._check_staff_legacy_dark_launch(self.test_course)
543+
358544

359545
class TestBetatesterAccess(ModuleStoreTestCase, CourseAccessTestMixin):
360546
"""

0 commit comments

Comments
 (0)