88
99import pytz
1010from django .urls import reverse
11+ from edx_toggles .toggles .testutils import override_waffle_flag
1112
1213from common .djangoapps .student .tests .factories import (
1314 BetaTesterFactory ,
2122)
2223from lms .djangoapps .courseware .access import has_access
2324from lms .djangoapps .courseware .tests .helpers import CourseAccessTestMixin , LoginEnrollmentTestCase
25+ from lms .djangoapps .instructor .toggles import LEGACY_INSTRUCTOR_DASHBOARD
2426from openedx .features .enterprise_support .tests .mixins .enterprise import EnterpriseTestConsentRequired
2527from xmodule .modulestore .django import modulestore
2628from 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
359545class TestBetatesterAccess (ModuleStoreTestCase , CourseAccessTestMixin ):
360546 """
0 commit comments