diff --git a/src/components/Logout/Logout.js b/src/components/Logout/Logout.js index c8361a16..3d582c73 100644 --- a/src/components/Logout/Logout.js +++ b/src/components/Logout/Logout.js @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; import { Redirect } from 'react-router'; import { FormattedMessage } from 'react-intl'; @@ -13,16 +12,15 @@ import { getLocale, logout } from '../../loginServices'; * This corresponds to the '/logout' route, allowing that route to be directly * accessible rather than only accessible through the menu action. * - * @param {object} history */ -const Logout = ({ history }) => { +const Logout = () => { const stripes = useStripes(); const [didLogout, setDidLogout] = useState(false); useEffect( () => { getLocale(stripes.okapi.url, stripes.store, stripes.okapi.tenant) - .then(logout(stripes.okapi.url, stripes.store, history)) + .then(logout(stripes.okapi.url, stripes.store)) .then(setDidLogout(true)); }, // no dependencies because we only want to start the logout process once. @@ -35,8 +33,4 @@ const Logout = ({ history }) => { return didLogout ? : ; }; -Logout.propTypes = { - history: PropTypes.object, -}; - export default Logout; diff --git a/src/components/LogoutTimeout/LogoutTimeout.js b/src/components/LogoutTimeout/LogoutTimeout.js index 1945ec28..557a32c0 100644 --- a/src/components/LogoutTimeout/LogoutTimeout.js +++ b/src/components/LogoutTimeout/LogoutTimeout.js @@ -12,14 +12,14 @@ import { import OrganizationLogo from '../OrganizationLogo'; import { useStripes } from '../../StripesContext'; -import { logout } from '../../loginServices'; - -import styles from './LogoutTimeout.css'; import { getUnauthorizedPathFromSession, + logout, removeUnauthorizedPathFromSession, } from '../../loginServices'; +import styles from './LogoutTimeout.css'; + /** * LogoutTimeout * Show a "sorry, your session timed out message"; if the session is still @@ -52,16 +52,15 @@ const LogoutTimeout = () => { [] ); - if (!didLogout) { - return ; - } - const handleClick = (_e) => { removeUnauthorizedPathFromSession(); }; - const previousPath = getUnauthorizedPathFromSession(); - const redirectTo = previousPath ?? '/'; + const redirectTo = getUnauthorizedPathFromSession() || '/'; + + if (!didLogout) { + return ; + } return (
diff --git a/src/components/LogoutTimeout/LogoutTimeout.test.js b/src/components/LogoutTimeout/LogoutTimeout.test.js index 532c5c4e..cb8606ea 100644 --- a/src/components/LogoutTimeout/LogoutTimeout.test.js +++ b/src/components/LogoutTimeout/LogoutTimeout.test.js @@ -3,7 +3,7 @@ import { userEvent } from '@folio/jest-config-stripes/testing-library/user-event import LogoutTimeout from './LogoutTimeout'; import { useStripes } from '../../StripesContext'; -import { getUnauthorizedPathFromSession, setUnauthorizedPathToSession } from '../../loginServices'; +import { getUnauthorizedPathFromSession, logout, setUnauthorizedPathToSession } from '../../loginServices'; jest.mock('../OrganizationLogo'); jest.mock('../../StripesContext'); @@ -11,6 +11,13 @@ jest.mock('react-router', () => ({ Redirect: () =>
Redirect
, })); +jest.mock('../../loginServices', () => ({ + logout: jest.fn(() => Promise.resolve()), + getUnauthorizedPathFromSession: jest.fn(), + removeUnauthorizedPathFromSession: jest.fn(), + setUnauthorizedPathToSession: jest.fn(), +})); + describe('LogoutTimeout', () => { describe('if not authenticated', () => { it('renders a timeout message', async () => { @@ -21,7 +28,7 @@ describe('LogoutTimeout', () => { screen.getByText('stripes-core.rtr.idleSession.sessionExpiredSoSad'); }); - it('clears previous path from storage after clicking', async () => { + it('clears previous path from storage after clicking "log in again"', async () => { const previousPath = '/monkey?bagel'; setUnauthorizedPathToSession(previousPath); const user = userEvent.setup(); @@ -31,16 +38,34 @@ describe('LogoutTimeout', () => { render(); await user.click(screen.getByRole('button')); - - expect(getUnauthorizedPathFromSession()).toBe(null); + expect(getUnauthorizedPathFromSession()).toBeFalsy(); }); }); - it('if authenticated, renders a redirect', async () => { - const mockUseStripes = useStripes; - mockUseStripes.mockReturnValue({ okapi: { isAuthenticated: true } }); + describe('if not authenticated', () => { + it('calls logout then renders a timeout message', async () => { + const mockUseStripes = useStripes; + mockUseStripes.mockReturnValue({ okapi: { isAuthenticated: true } }); + + render(); + expect(logout).toHaveBeenCalled(); + screen.getByText('stripes-core.rtr.idleSession.sessionExpiredSoSad'); + }); + + it('clears previous path from storage after clicking "log in again"', async () => { + const previousPath = '/monkey?bagel'; + setUnauthorizedPathToSession(previousPath); + const user = userEvent.setup(); + const mockUseStripes = useStripes; + mockUseStripes.mockReturnValue({ okapi: { isAuthenticated: true } }); + + render(); + + expect(logout).toHaveBeenCalled(); + screen.getByText('stripes-core.rtr.idleSession.sessionExpiredSoSad'); - render(); - screen.getByText('Redirect'); + await user.click(screen.getByRole('button')); + expect(getUnauthorizedPathFromSession()).toBeFalsy(); + }); }); }); diff --git a/src/components/SessionEventContainer/SessionEventContainer.js b/src/components/SessionEventContainer/SessionEventContainer.js index d5e200fe..af19c588 100644 --- a/src/components/SessionEventContainer/SessionEventContainer.js +++ b/src/components/SessionEventContainer/SessionEventContainer.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import createInactivityTimer from 'inactivity-timer'; import ms from 'ms'; -import { logout, SESSION_NAME, setUnauthorizedPathToSession } from '../../loginServices'; +import { SESSION_NAME, setUnauthorizedPathToSession } from '../../loginServices'; import KeepWorkingModal from './KeepWorkingModal'; import { useStripes } from '../../StripesContext'; import { @@ -22,26 +22,20 @@ import FixedLengthSessionWarning from './FixedLengthSessionWarning'; // // RTR error in this window: logout -export const thisWindowRtrError = (_e, stripes, history, queryClient) => { +export const thisWindowRtrError = (_e, stripes, history) => { console.warn('rtr error; logging out'); // eslint-disable-line no-console setUnauthorizedPathToSession(); - return logout(stripes.okapi.url, stripes.store, queryClient) - .then(() => { - history.push('/logout-timeout'); - }); + history.push('/logout-timeout'); }; // idle session timeout in this window: logout export const thisWindowRtrIstTimeout = (_e, stripes, history) => { stripes.logger.log('rtr', 'idle session timeout; logging out'); setUnauthorizedPathToSession(); - return logout(stripes.okapi.url, stripes.store, queryClient) - .then(() => { - history.push('/logout-timeout'); - }); + history.push('/logout-timeout'); }; -// fixed-length session warning in this window +// fixed-length session warning in this window: logout export const thisWindowRtrFlsWarning = (_e, stripes, setIsFlsVisible) => { stripes.logger.log('rtr', 'fixed-length session warning'); setIsFlsVisible(true); @@ -58,14 +52,11 @@ export const thisWindowRtrFlsTimeout = (_e, stripes, history) => { // logout if it was a timeout event or if SESSION_NAME is being // removed from localStorage, an indicator that logout is in-progress // in another window and so must occur here as well -export const otherWindowStorage = (e, stripes, history, queryClient) => { +export const otherWindowStorage = (e, stripes, history) => { if (e.key === RTR_TIMEOUT_EVENT) { stripes.logger.log('rtr', 'idle session timeout; logging out'); setUnauthorizedPathToSession(); - return logout(stripes.okapi.url, stripes.store, queryClient) - .then(() => { - history.push('/logout-timeout'); - }); + history.push('/logout-timeout'); } else if (!localStorage.getItem(SESSION_NAME)) { stripes.logger.log('rtr', 'external localstorage change; logging out'); setUnauthorizedPathToSession(); @@ -138,7 +129,7 @@ export const thisWindowActivity = (_e, stripes, timers, broadcastChannel) => { * @param {object} history * @returns KeepWorkingModal or null */ -const SessionEventContainer = ({ history, queryClient }) => { +const SessionEventContainer = ({ history }) => { // is the "keep working?" modal visible? const [isVisible, setIsVisible] = useState(false); @@ -220,13 +211,13 @@ const SessionEventContainer = ({ history, queryClient }) => { timers.current = { showModalIT, logoutIT }; // RTR error in this window: logout - channels.window[RTR_ERROR_EVENT] = (e) => thisWindowRtrError(e, stripes, history, queryClient); + channels.window[RTR_ERROR_EVENT] = (e) => thisWindowRtrError(e, stripes, history); // idle session timeout in this window: logout channels.window[RTR_TIMEOUT_EVENT] = (e) => thisWindowRtrIstTimeout(e, stripes, history); // localstorage change in another window: logout? - channels.window.storage = (e) => otherWindowStorage(e, stripes, history, queryClient); + channels.window.storage = (e) => otherWindowStorage(e, stripes, history); // activity in another window: send keep-alive to idle-timers. channels.bc.message = (message) => otherWindowActivity(message, stripes, timers, setIsVisible); @@ -292,7 +283,6 @@ const SessionEventContainer = ({ history, queryClient }) => { SessionEventContainer.propTypes = { history: PropTypes.object, - queryClient: PropTypes.object, }; export default SessionEventContainer; diff --git a/src/components/SessionEventContainer/SessionEventContainer.test.js b/src/components/SessionEventContainer/SessionEventContainer.test.js index 0001dad7..ddca458c 100644 --- a/src/components/SessionEventContainer/SessionEventContainer.test.js +++ b/src/components/SessionEventContainer/SessionEventContainer.test.js @@ -10,7 +10,6 @@ import SessionEventContainer, { thisWindowRtrIstTimeout, } from './SessionEventContainer'; import { - logout, setUnauthorizedPathToSession, SESSION_NAME, } from '../../loginServices'; @@ -73,14 +72,11 @@ describe('SessionEventContainer', () => { describe('SessionEventContainer event listeners', () => { it('thisWindowRtrError', async () => { const history = { push: jest.fn() }; - const logoutMock = logout; - logoutMock.mockReturnValue(Promise.resolve()); const setUnauthorizedPathToSessionMock = setUnauthorizedPathToSession; setUnauthorizedPathToSessionMock.mockReturnValue(null); - await thisWindowRtrError(null, { okapi: { url: 'http' } }, history); - expect(logout).toHaveBeenCalled(); + thisWindowRtrError(null, { okapi: { url: 'http' } }, history); expect(setUnauthorizedPathToSession).toHaveBeenCalled(); expect(history.push).toHaveBeenCalledWith('/logout-timeout'); }); @@ -98,12 +94,7 @@ describe('SessionEventContainer event listeners', () => { const history = { push: jest.fn() }; - const setUnauthorizedPathToSessionMock = setUnauthorizedPathToSession; - setUnauthorizedPathToSessionMock.mockReturnValue(null); - - await thisWindowRtrIstTimeout(null, s, history); - expect(logout).toHaveBeenCalled(); - expect(setUnauthorizedPathToSession).toHaveBeenCalled(); + thisWindowRtrIstTimeout(null, s, history); expect(history.push).toHaveBeenCalledWith('/logout-timeout'); }); @@ -124,13 +115,8 @@ describe('SessionEventContainer event listeners', () => { } }; const history = { push: jest.fn() }; - const qc = {}; - const setUnauthorizedPathToSessionMock = setUnauthorizedPathToSession; - setUnauthorizedPathToSessionMock.mockReturnValue(null); - await otherWindowStorage(e, s, history, qc); - expect(logout).toHaveBeenCalledWith(s.okapi.url, s.store, qc); - expect(setUnauthorizedPathToSession).toHaveBeenCalled(); + otherWindowStorage(e, s, history); expect(history.push).toHaveBeenCalledWith('/logout-timeout'); }); @@ -146,11 +132,9 @@ describe('SessionEventContainer event listeners', () => { } }; const history = { push: jest.fn() }; - const qc = {}; - await otherWindowStorage(e, s, history, qc); - expect(logout).toHaveBeenCalledWith(s.okapi.url, s.store, qc); - expect(history.push).toHaveBeenCalledWith('/'); + otherWindowStorage(e, s, history); + expect(history.push).toHaveBeenCalledWith('/logout'); }); });