@@ -8,7 +8,13 @@ import userEvent from '@testing-library/user-event';
88
99jest . mock ( 'react-router-dom' , ( ) => ( {
1010 useParams : jest . fn ( ) ,
11+ Link : ( { to, children, ...props } : any ) => (
12+ < a href = { to } data-testid = "react-router-link" { ...props } role = "button" >
13+ { children }
14+ </ a >
15+ ) ,
1116} ) ) ;
17+
1218jest . mock ( '@src/data/apiHook' , ( ) => ( {
1319 useCourseInfo : jest . fn ( ) ,
1420} ) ) ;
@@ -126,4 +132,125 @@ describe('InstructorNav', () => {
126132 expect ( tab2Link . closest ( 'a' ) ) . toHaveAttribute ( 'aria-current' , 'page' ) ;
127133 } ) ;
128134 } ) ;
135+
136+ describe ( 'Simplified navigation type detection' , ( ) => {
137+ const courseId = 'course-v1:edX+DemoX+Demo_Course' ;
138+
139+ beforeEach ( ( ) => {
140+ ( useParams as jest . Mock ) . mockReturnValue ( { courseId, tabId : 'tab1' } ) ;
141+ ( useCourseInfo as jest . Mock ) . mockReturnValue ( {
142+ data : { tabs : [ ] } ,
143+ isLoading : false ,
144+ } ) ;
145+ } ) ;
146+
147+ it ( 'uses internal navigation for URLs starting with "/"' , ( ) => {
148+ ( useWidgetProps as jest . Mock ) . mockReturnValue ( [
149+ { tabId : 'tab1' , url : '/instructor-dashboard/course123/course_info' , title : 'Course Info' , sortOrder : 1 } ,
150+ { tabId : 'tab2' , url : '/courses/course123/courseware' , title : 'Courseware' , sortOrder : 2 } ,
151+ { tabId : 'tab3' , url : '/authoring/course123' , title : 'Authoring' , sortOrder : 3 } ,
152+ { tabId : 'tab4' , url : '/admin/dashboard' , title : 'Admin' , sortOrder : 4 } ,
153+ ] ) ;
154+
155+ render ( < InstructorNav /> ) ;
156+
157+ const courseInfoLink = screen . getByText ( 'Course Info' ) ;
158+ const coursewareLink = screen . getByText ( 'Courseware' ) ;
159+ const authoringLink = screen . getByText ( 'Authoring' ) ;
160+ const adminLink = screen . getByText ( 'Admin' ) ;
161+
162+ // All URLs starting with "/" should use React Router Link (internal navigation)
163+ // Our mock Link component has role="button", so we can check for that to confirm it's using our internal navigation
164+ expect ( courseInfoLink ) . toHaveAttribute ( 'role' , 'button' ) ;
165+ expect ( coursewareLink ) . toHaveAttribute ( 'role' , 'button' ) ;
166+ expect ( authoringLink ) . toHaveAttribute ( 'role' , 'button' ) ;
167+ expect ( adminLink ) . toHaveAttribute ( 'role' , 'button' ) ;
168+
169+ // Verify the links are accessible and clickable
170+ expect ( courseInfoLink ) . toBeInTheDocument ( ) ;
171+ expect ( coursewareLink ) . toBeInTheDocument ( ) ;
172+ expect ( authoringLink ) . toBeInTheDocument ( ) ;
173+ expect ( adminLink ) . toBeInTheDocument ( ) ;
174+ } ) ;
175+
176+ it ( 'uses external navigation for URLs NOT starting with "/"' , ( ) => {
177+ ( useWidgetProps as jest . Mock ) . mockReturnValue ( [
178+ { tabId : 'tab1' , url : 'https://external.edx.org/courses' , title : 'External Courses' , sortOrder : 1 } ,
179+ { tabId : 'tab2' , url : 'http://learning.edx.org' , title : 'Learning' , sortOrder : 2 } ,
180+ { tabId : 'tab3' , url : 'course_info' , title : 'Relative Tab' , sortOrder : 3 } ,
181+ { tabId : 'tab4' , url : 'mailto:support@edx.org' , title : 'Contact' , sortOrder : 4 } ,
182+ ] ) ;
183+
184+ render ( < InstructorNav /> ) ;
185+
186+ const externalLink = screen . getByText ( 'External Courses' ) ;
187+ const learningLink = screen . getByText ( 'Learning' ) ;
188+ const relativeLink = screen . getByText ( 'Relative Tab' ) ;
189+ const contactLink = screen . getByText ( 'Contact' ) ;
190+
191+ // All URLs NOT starting with "/" should use anchor tags (external navigation)
192+ // Our mock Link component has role="button", so if it doesn't have that, it's an external link
193+ expect ( externalLink . closest ( 'a' ) ) . not . toHaveAttribute ( 'role' , 'button' ) ;
194+ expect ( learningLink . closest ( 'a' ) ) . not . toHaveAttribute ( 'role' , 'button' ) ;
195+ expect ( relativeLink . closest ( 'a' ) ) . not . toHaveAttribute ( 'role' , 'button' ) ;
196+ expect ( contactLink . closest ( 'a' ) ) . not . toHaveAttribute ( 'role' , 'button' ) ;
197+
198+ // Verify the links are accessible and clickable
199+ expect ( externalLink ) . toBeInTheDocument ( ) ;
200+ expect ( learningLink ) . toBeInTheDocument ( ) ;
201+ expect ( relativeLink ) . toBeInTheDocument ( ) ;
202+ expect ( contactLink ) . toBeInTheDocument ( ) ;
203+ } ) ;
204+
205+ it ( 'handles mixed internal and external URLs in same navigation' , ( ) => {
206+ ( useWidgetProps as jest . Mock ) . mockReturnValue ( [
207+ { tabId : 'tab1' , url : '/internal/path' , title : 'Internal' , sortOrder : 1 } ,
208+ { tabId : 'tab2' , url : 'external-relative' , title : 'External' , sortOrder : 2 } ,
209+ { tabId : 'tab3' , url : '/another/internal' , title : 'Another Internal' , sortOrder : 3 } ,
210+ { tabId : 'tab4' , url : 'https://example.com' , title : 'Absolute External' , sortOrder : 4 } ,
211+ ] ) ;
212+
213+ render ( < InstructorNav /> ) ;
214+
215+ const internalLink = screen . getByText ( 'Internal' ) ;
216+ const externalLink = screen . getByText ( 'External' ) ;
217+ const anotherInternalLink = screen . getByText ( 'Another Internal' ) ;
218+ const absoluteExternalLink = screen . getByText ( 'Absolute External' ) ;
219+
220+ // All links should be present and clickable regardless of navigation type
221+ expect ( internalLink . closest ( 'a' ) ) . toHaveAttribute ( 'role' , 'button' ) ;
222+ expect ( externalLink . closest ( 'a' ) ) . not . toHaveAttribute ( 'role' , 'button' ) ;
223+ expect ( anotherInternalLink . closest ( 'a' ) ) . toHaveAttribute ( 'role' , 'button' ) ;
224+ expect ( absoluteExternalLink . closest ( 'a' ) ) . not . toHaveAttribute ( 'role' , 'button' ) ;
225+
226+ // Verify the links are accessible
227+ expect ( internalLink ) . toBeInTheDocument ( ) ;
228+ expect ( externalLink ) . toBeInTheDocument ( ) ;
229+ expect ( anotherInternalLink ) . toBeInTheDocument ( ) ;
230+ expect ( absoluteExternalLink ) . toBeInTheDocument ( ) ;
231+ } ) ;
232+
233+ it ( 'maintains clearAlerts functionality for both navigation types' , async ( ) => {
234+ ( useWidgetProps as jest . Mock ) . mockReturnValue ( [
235+ { tabId : 'tab1' , url : '/internal' , title : 'Internal Tab' , sortOrder : 1 } ,
236+ { tabId : 'tab2' , url : 'external' , title : 'External Tab' , sortOrder : 2 } ,
237+ ] ) ;
238+
239+ render ( < InstructorNav /> ) ;
240+ const user = userEvent . setup ( ) ;
241+
242+ const internalTab = screen . getByText ( 'Internal Tab' ) ;
243+ const externalTab = screen . getByText ( 'External Tab' ) ;
244+
245+ // Click internal navigation tab
246+ await user . click ( internalTab ) ;
247+ expect ( mockClearAlerts ) . toHaveBeenCalled ( ) ;
248+
249+ mockClearAlerts . mockClear ( ) ;
250+
251+ // Click external navigation tab
252+ await user . click ( externalTab ) ;
253+ expect ( mockClearAlerts ) . toHaveBeenCalled ( ) ;
254+ } ) ;
255+ } ) ;
129256} ) ;
0 commit comments