Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STCOR-875 sync to master #1533

Merged
merged 4 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions src/components/Logout/Logout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Redirect } from 'react-router';
import { FormattedMessage } from 'react-intl';

Expand All @@ -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.
Expand All @@ -35,8 +33,4 @@ const Logout = ({ history }) => {
return didLogout ? <Redirect to="/" /> : <FormattedMessage id="stripes-core.logoutPending" />;
};

Logout.propTypes = {
history: PropTypes.object,
};

export default Logout;
17 changes: 8 additions & 9 deletions src/components/LogoutTimeout/LogoutTimeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -52,16 +52,15 @@ const LogoutTimeout = () => {
[]
);

if (!didLogout) {
return <LoadingView />;
}

const handleClick = (_e) => {
removeUnauthorizedPathFromSession();
};

const previousPath = getUnauthorizedPathFromSession();
const redirectTo = previousPath ?? '/';
const redirectTo = getUnauthorizedPathFromSession() || '/';

if (!didLogout) {
return <LoadingView />;
}

return (
<main>
Expand Down
43 changes: 34 additions & 9 deletions src/components/LogoutTimeout/LogoutTimeout.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ 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');
jest.mock('react-router', () => ({
Redirect: () => <div>Redirect</div>,
}));

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 () => {
Expand All @@ -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();
Expand All @@ -31,16 +38,34 @@ describe('LogoutTimeout', () => {
render(<LogoutTimeout />);

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(<LogoutTimeout />);
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(<LogoutTimeout />);

expect(logout).toHaveBeenCalled();
screen.getByText('stripes-core.rtr.idleSession.sessionExpiredSoSad');

render(<LogoutTimeout />);
screen.getByText('Redirect');
await user.click(screen.getByRole('button'));
expect(getUnauthorizedPathFromSession()).toBeFalsy();
});
});
});
30 changes: 10 additions & 20 deletions src/components/SessionEventContainer/SessionEventContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -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();
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -292,7 +283,6 @@ const SessionEventContainer = ({ history, queryClient }) => {

SessionEventContainer.propTypes = {
history: PropTypes.object,
queryClient: PropTypes.object,
};

export default SessionEventContainer;
26 changes: 5 additions & 21 deletions src/components/SessionEventContainer/SessionEventContainer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import SessionEventContainer, {
thisWindowRtrIstTimeout,
} from './SessionEventContainer';
import {
logout,
setUnauthorizedPathToSession,
SESSION_NAME,
} from '../../loginServices';
Expand Down Expand Up @@ -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');
});
Expand All @@ -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');
});

Expand All @@ -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');
});

Expand All @@ -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');
});
});

Expand Down
Loading