diff --git a/package.json b/package.json index 77affd3f9..d90f051d6 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "i18next-browser-languagedetector": "^7.0.1", "immer": "8.0.2", "jest-fetch-mock": "^3.0.3", - "jest-localstorage-mock": "^2.4.6", "local-web-server": "^4.2.1", "lodash": "^4.17.21", "msw": "^0.49.0", diff --git a/src/common/routes/AppRoutes.tsx b/src/common/routes/AppRoutes.tsx index 9a24cb9d1..6083e4f78 100644 --- a/src/common/routes/AppRoutes.tsx +++ b/src/common/routes/AppRoutes.tsx @@ -6,9 +6,11 @@ import Login from '../../domain/auth/components/Login'; import OidcCallback from '../../domain/auth/components/OidcCallback'; import { LOGIN_CALLBACK_PATH, LOGIN_PATH } from '../../domain/auth/constants'; import LocaleRoutes from './LocaleRoutes'; +import { REDIRECT_PATH_KEY } from './constants'; const AppRoutes: React.FC> = () => { const currentLocale = useLocale(); + const redirectPath = sessionStorage.getItem(REDIRECT_PATH_KEY); return ( @@ -17,7 +19,10 @@ const AppRoutes: React.FC> = () => { {Object.values(LANGUAGES).map((locale) => ( } key={locale} /> ))} - } /> + } + /> ); }; diff --git a/src/common/routes/PrivateRoute.tsx b/src/common/routes/PrivateRoute.tsx index ac2838aa0..21220acac 100644 --- a/src/common/routes/PrivateRoute.tsx +++ b/src/common/routes/PrivateRoute.tsx @@ -1,6 +1,7 @@ -import React from 'react'; -import { Navigate } from 'react-router-dom'; +import React, { useEffect } from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; import useUser from '../../domain/auth/useUser'; +import { REDIRECT_PATH_KEY } from './constants'; type Props = { element: JSX.Element; @@ -8,9 +9,21 @@ type Props = { const PrivateRoute: React.FC> = ({ element }) => { const { data: user } = useUser(); + const isAuthenticated = Boolean(user?.profile); + const location = useLocation(); - if (!user?.profile) { - return ; + useEffect(() => { + if (isAuthenticated) { + sessionStorage.removeItem(REDIRECT_PATH_KEY); + } + }, [isAuthenticated]); + + if (!isAuthenticated) { + // If user tries to access private route when not signed in, + // save URL path to session storage and navigate to login, + // so that user can be redirected to that route after login + sessionStorage.setItem(REDIRECT_PATH_KEY, location.pathname); + return ; } return element; diff --git a/src/common/routes/Routes.test.tsx b/src/common/routes/Routes.test.tsx new file mode 100644 index 000000000..be666dace --- /dev/null +++ b/src/common/routes/Routes.test.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { render, waitFor } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { I18nextProvider } from 'react-i18next'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { InitialEntry } from '@remix-run/router'; +import { User } from 'oidc-client'; +import { Provider } from 'react-redux'; +import AppRoutes from './AppRoutes'; +import { FeatureFlagsProvider } from '../components/featureFlags/FeatureFlagsContext'; +import { GlobalNotificationProvider } from '../components/globalNotification/GlobalNotificationContext'; +import authService from '../../domain/auth/authService'; +import { store } from '../redux/store'; +import i18n from '../../locales/i18n'; +import { REDIRECT_PATH_KEY } from './constants'; + +afterEach(() => { + sessionStorage.clear(); +}); + +const path = '/fi/johtoselvityshakemus'; + +const mockUser: Partial = { + id_token: 'fffff-aaaaaa-11111', + access_token: '.BnutWVN1x7RSAP5bU2a-tXdVPuof_9pBNd_Ozw', + profile: { + iss: '', + sub: '', + aud: '', + exp: 0, + iat: 0, + }, +}; + +function getWrapper(routerInitialEntries?: InitialEntry[]) { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retryDelay: 0, + }, + }, + }); + + return render( + + + + + + + + + + + + + , + ); +} + +test('Should save path to session storage and navigate to login if trying to navigate to private route', async () => { + const login = jest.spyOn(authService, 'login').mockResolvedValue(); + + getWrapper([path]); + + await waitFor(() => expect(login).toHaveBeenCalled()); + expect(window.sessionStorage.getItem(REDIRECT_PATH_KEY)).toBe(path); +}); + +test('Should redirect to path stored in session storage after login if one exists', async () => { + sessionStorage.setItem(REDIRECT_PATH_KEY, path); + jest.spyOn(authService.userManager, 'getUser').mockResolvedValue(mockUser as User); + + getWrapper(); + + await waitFor(() => + expect(window.document.title).toBe('Haitaton - Luo uusi johtoselvityshakemus'), + ); +}); diff --git a/src/common/routes/constants.ts b/src/common/routes/constants.ts new file mode 100644 index 000000000..fe40af578 --- /dev/null +++ b/src/common/routes/constants.ts @@ -0,0 +1 @@ +export const REDIRECT_PATH_KEY = 'redirect-path'; diff --git a/src/domain/auth/authService.test.ts b/src/domain/auth/authService.test.ts index 500014f2e..25d0de0d9 100644 --- a/src/domain/auth/authService.test.ts +++ b/src/domain/auth/authService.test.ts @@ -12,9 +12,6 @@ describe('authService', () => { afterEach(() => { localStorage.clear(); - // eslint-disable-next-line - // @ts-ignore - localStorage.setItem.mockClear(); jest.restoreAllMocks(); }); diff --git a/src/domain/hanke/accessRights/AccessRightsView.test.tsx b/src/domain/hanke/accessRights/AccessRightsView.test.tsx index 6063d1ad0..f9c44ddd7 100644 --- a/src/domain/hanke/accessRights/AccessRightsView.test.tsx +++ b/src/domain/hanke/accessRights/AccessRightsView.test.tsx @@ -7,7 +7,7 @@ import { server } from '../../mocks/test-server'; import usersData from '../../mocks/data/users-data.json'; import { SignedInUser } from '../hankeUsers/hankeUser'; -jest.setTimeout(30000); +jest.setTimeout(40000); afterEach(cleanup); diff --git a/src/setupTests.ts b/src/setupTests.ts index 40f91c1d7..81b85af06 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -4,7 +4,6 @@ // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom'; import '@testing-library/jest-dom/extend-expect'; -import 'jest-localstorage-mock'; import { GlobalWithFetchMock } from 'jest-fetch-mock'; import 'jest-canvas-mock'; import { server } from './domain/mocks/test-server'; diff --git a/yarn.lock b/yarn.lock index f2698708b..cdf967e3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10787,11 +10787,6 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-localstorage-mock@^2.4.6: - version "2.4.6" - resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.6.tgz#ebb9481943bf52e0f8c864ae4858eb791d68149d" - integrity sha512-8n6tuqscEShpvC7vkq3BPabOGGszD1cdLAKTtTtCRqH2bWJIVfpV4aIhrDJstV7xLOjo56wSVZkoMbT7dWLIVg== - jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"