diff --git a/src/courseware/course/Course.test.jsx b/src/courseware/course/Course.test.jsx index 62d3084cf6..d2c60d8fbe 100644 --- a/src/courseware/course/Course.test.jsx +++ b/src/courseware/course/Course.test.jsx @@ -15,6 +15,10 @@ import { executeThunk } from '../../utils'; import * as thunks from '../data/thunks'; jest.mock('@edx/frontend-platform/analytics'); +jest.mock('@edx/frontend-lib-special-exams/dist/data/thunks.js', () => ({ + ...jest.requireActual('@edx/frontend-lib-special-exams/dist/data/thunks.js'), + checkExamEntry: () => jest.fn(), +})); const recordFirstSectionCelebration = jest.fn(); // eslint-disable-next-line no-import-assign diff --git a/src/courseware/course/CourseBreadcrumbs.test.jsx b/src/courseware/course/CourseBreadcrumbs.test.jsx index 614538df1e..f51ead34c3 100644 --- a/src/courseware/course/CourseBreadcrumbs.test.jsx +++ b/src/courseware/course/CourseBreadcrumbs.test.jsx @@ -121,7 +121,7 @@ describe('CourseBreadcrumbs', () => { sequenceId="block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions" isStaff /> - + , , ); it('renders course breadcrumbs as expected', async () => { diff --git a/src/courseware/course/course-exit/CourseExit.jsx b/src/courseware/course/course-exit/CourseExit.jsx index a54cec45e3..7f74204776 100644 --- a/src/courseware/course/course-exit/CourseExit.jsx +++ b/src/courseware/course/course-exit/CourseExit.jsx @@ -4,7 +4,7 @@ import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Button } from '@edx/paragon'; import { useSelector } from 'react-redux'; -import { useNavigate } from 'react-router-dom'; +import { Navigate } from 'react-router-dom'; import CourseCelebration from './CourseCelebration'; import CourseInProgress from './CourseInProgress'; @@ -16,7 +16,6 @@ import { unsubscribeFromGoalReminders } from './data/thunks'; import { useModel } from '../../../generic/model-store'; const CourseExit = ({ intl }) => { - const navigate = useNavigate(); const { courseId } = useSelector(state => state.courseware); const { certificateData, @@ -58,14 +57,10 @@ const CourseExit = ({ intl }) => { body = (); } else if (mode === COURSE_EXIT_MODES.celebration) { body = (); + } else { + return (); } - useEffect(() => { - if ((mode === COURSE_EXIT_MODES.disabled) || (!(mode in COURSE_EXIT_MODES))) { - navigate(`/course/${courseId}`); - } - }, []); - return ( <>
diff --git a/src/courseware/course/sequence/Sequence.test.jsx b/src/courseware/course/sequence/Sequence.test.jsx index 4dc0291aad..dea0649d83 100644 --- a/src/courseware/course/sequence/Sequence.test.jsx +++ b/src/courseware/course/sequence/Sequence.test.jsx @@ -11,6 +11,10 @@ import Sequence from './Sequence'; import { fetchSequenceFailure } from '../../data/slice'; jest.mock('@edx/frontend-platform/analytics'); +jest.mock('@edx/frontend-lib-special-exams/dist/data/thunks.js', () => ({ + ...jest.requireActual('@edx/frontend-lib-special-exams/dist/data/thunks.js'), + checkExamEntry: () => jest.fn(), +})); describe('Sequence', () => { let mockData; diff --git a/src/courseware/course/sequence/content-lock/ContentLock.jsx b/src/courseware/course/sequence/content-lock/ContentLock.jsx index dec64e7878..319dcdb70a 100644 --- a/src/courseware/course/sequence/content-lock/ContentLock.jsx +++ b/src/courseware/course/sequence/content-lock/ContentLock.jsx @@ -1,11 +1,11 @@ import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; +import { useNavigate } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faLock } from '@fortawesome/free-solid-svg-icons'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Button } from '@edx/paragon'; -import { useNavigate } from 'react-router-dom'; import messages from './messages'; const ContentLock = ({ diff --git a/src/courseware/course/sequence/content-lock/ContentLock.test.jsx b/src/courseware/course/sequence/content-lock/ContentLock.test.jsx index c53523c4c3..c2ab9d3dac 100644 --- a/src/courseware/course/sequence/content-lock/ContentLock.test.jsx +++ b/src/courseware/course/sequence/content-lock/ContentLock.test.jsx @@ -4,11 +4,11 @@ import { } from '../../../../setupTest'; import ContentLock from './ContentLock'; -const mockedNavigator = jest.fn(); +const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), - useNavigate: () => mockedNavigator, + useNavigate: () => mockNavigate, })); describe('Content Lock', () => { @@ -43,6 +43,6 @@ describe('Content Lock', () => { render(, { wrapWithRouter: true }); fireEvent.click(screen.getByRole('button')); - expect(mockedNavigator).toHaveBeenCalledWith(`/course/${mockData.courseId}/${mockData.prereqId}`); + expect(mockNavigate).toHaveBeenCalledWith(`/course/${mockData.courseId}/${mockData.prereqId}`); }); }); diff --git a/src/courseware/course/sequence/honor-code/HonorCode.test.jsx b/src/courseware/course/sequence/honor-code/HonorCode.test.jsx index 1ce7ec54a3..d0c38bde4d 100644 --- a/src/courseware/course/sequence/honor-code/HonorCode.test.jsx +++ b/src/courseware/course/sequence/honor-code/HonorCode.test.jsx @@ -9,12 +9,12 @@ import { } from '../../../../setupTest'; import HonorCode from './HonorCode'; -const mockedNavigator = jest.fn(); +const mockNavigate = jest.fn(); initializeMockApp(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), - useNavigate: () => mockedNavigator, + useNavigate: () => mockNavigate, })); describe('Honor Code', () => { @@ -41,7 +41,7 @@ describe('Honor Code', () => { render(, { wrapWithRouter: true }); const cancelButton = screen.getByText('Cancel'); fireEvent.click(cancelButton); - expect(mockedNavigator).toHaveBeenCalledWith(`/course/${mockData.courseId}/home`); + expect(mockNavigate).toHaveBeenCalledWith(`/course/${mockData.courseId}/home`); }); it('calls to save integrity_signature when agreeing', async () => { diff --git a/src/decode-page-route/index.jsx b/src/decode-page-route/index.jsx index be588b23c8..eff47fa3fd 100644 --- a/src/decode-page-route/index.jsx +++ b/src/decode-page-route/index.jsx @@ -4,6 +4,7 @@ import React from 'react'; import { generatePath, useMatch, Navigate, } from 'react-router-dom'; + import { DECODE_ROUTES } from '../constants'; const ROUTES = [].concat( diff --git a/src/generic/CourseAccessErrorPage.jsx b/src/generic/CourseAccessErrorPage.jsx index 210d2b3a48..8eff1f7aec 100644 --- a/src/generic/CourseAccessErrorPage.jsx +++ b/src/generic/CourseAccessErrorPage.jsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { LearningHeader as Header } from '@edx/frontend-component-header'; import Footer from '@edx/frontend-component-footer'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useParams, Navigate } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import useActiveEnterpriseAlert from '../alerts/active-enteprise-alert'; @@ -13,7 +13,6 @@ import messages from '../tab-page/messages'; const CourseAccessErrorPage = ({ intl }) => { const { courseId } = useParams(); - const navigate = useNavigate(); const dispatch = useDispatch(); const activeEnterpriseAlert = useActiveEnterpriseAlert(courseId); @@ -38,7 +37,7 @@ const CourseAccessErrorPage = ({ intl }) => { ); } if (courseStatus === LOADED) { - navigate(`/redirect/home/${courseId}`, { replace: true }); + return ; } return ( <> diff --git a/src/generic/CourseAccessErrorPage.test.jsx b/src/generic/CourseAccessErrorPage.test.jsx index 7812246cef..340e5d07b9 100644 --- a/src/generic/CourseAccessErrorPage.test.jsx +++ b/src/generic/CourseAccessErrorPage.test.jsx @@ -5,7 +5,7 @@ import { initializeTestStore, render, screen } from '../setupTest'; import CourseAccessErrorPage from './CourseAccessErrorPage'; const mockDispatch = jest.fn(); -const mockedNavigator = jest.fn(); +const mockNavigate = jest.fn(); let mockCourseStatus; jest.mock('react-redux', () => ({ @@ -18,7 +18,7 @@ jest.mock('./PageLoading', () => function () { }); jest.mock('react-router-dom', () => ({ ...(jest.requireActual('react-router-dom')), - useNavigate: () => mockedNavigator, + useNavigate: () => mockNavigate, })); describe('CourseAccessErrorPage', () => { @@ -40,7 +40,7 @@ describe('CourseAccessErrorPage', () => { { wrapWithRouter: true }, ); expect(screen.getByTestId('page-loading')).toBeInTheDocument(); - expect(history.location.pathname).toBe(accessDeniedUrl); + expect(window.location.pathname).toBe(accessDeniedUrl); }); it('Redirect user to homepage if user has access', () => { @@ -51,7 +51,7 @@ describe('CourseAccessErrorPage', () => { , { wrapWithRouter: true }, ); - expect(mockedNavigator).toHaveBeenCalledWith('/redirect/home/course-v1:edX+DemoX+Demo_Course', { replace: true }); + expect(window.location.pathname).toBe('/redirect/home/course-v1:edX+DemoX+Demo_Course'); }); it('For access denied it should render access denied page', () => { @@ -64,6 +64,6 @@ describe('CourseAccessErrorPage', () => { { wrapWithRouter: true }, ); expect(screen.getByTestId('access-denied-main')).toBeInTheDocument(); - expect(history.location.pathname).toBe(accessDeniedUrl); + expect(window.location.pathname).toBe(accessDeniedUrl); }); }); diff --git a/src/generic/path-fixes/PathFixesProvider.jsx b/src/generic/path-fixes/PathFixesProvider.jsx index 021ed15c08..83e9552c99 100644 --- a/src/generic/path-fixes/PathFixesProvider.jsx +++ b/src/generic/path-fixes/PathFixesProvider.jsx @@ -1,8 +1,7 @@ -import { useLocation, useNavigate } from 'react-router-dom'; +import { Navigate, useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; -import { useEffect } from 'react'; /** * We have seen evidence of learners hitting MFE pages with spaces instead of plus signs (which are used commonly @@ -14,27 +13,24 @@ import { useEffect } from 'react'; */ const PathFixesProvider = ({ children }) => { const location = useLocation(); - const navigate = useNavigate(); - - useEffect(() => { - // We only check for spaces. That's not the only kind of character that is escaped in URLs, but it would always be - // present for our cases, and I believe it's the only one we use normally. - if (location.pathname.includes(' ') || location.pathname.includes('%20')) { - const newLocation = { - ...location, - pathname: (location.pathname.replaceAll(' ', '+')).replaceAll('%20', '+'), - }; - - sendTrackEvent('edx.ui.lms.path_fixed', { - new_path: newLocation.pathname, - old_path: location.pathname, - referrer: document.referrer, - search: location.search, - }); - - navigate(newLocation); - } - }, [location.pathname]); + + // We only check for spaces. That's not the only kind of character that is escaped in URLs, but it would always be + // present for our cases, and I believe it's the only one we use normally. + if (location.pathname.includes(' ') || location.pathname.includes('%20')) { + const newLocation = { + ...location, + pathname: (location.pathname.replaceAll(' ', '+')).replaceAll('%20', '+'), + }; + + sendTrackEvent('edx.ui.lms.path_fixed', { + new_path: newLocation.pathname, + old_path: location.pathname, + referrer: document.referrer, + search: location.search, + }); + + return (); + } return children; // pass through };