Skip to content

Commit

Permalink
Add LoginContinue tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dauglyon committed Aug 6, 2024
1 parent ab62bb0 commit 5c2e7db
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 10 deletions.
11 changes: 3 additions & 8 deletions src/features/login/LogIn.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,7 @@ describe('Login', () => {
});

it('redirect if logged in', () => {
const redirectSpy = jest.fn();
const Redirect = () => {
redirectSpy();
return <></>;
};

const Narratives = jest.fn(() => <></>);
render(
<Provider
store={createTestStore({
Expand All @@ -131,13 +126,13 @@ describe('Login', () => {
<MemoryRouter initialEntries={[LOGIN_ROUTE]}>
<Routes>
<Route path={LOGIN_ROUTE} element={<LogIn />} />
<Route path={'/narratives'} element={<Redirect />} />
<Route path={'/narratives'} Component={Narratives} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>
);
expect(redirectSpy).toBeCalled();
expect(Narratives).toBeCalled();
});

it('redirect if logged in with nextRequest', () => {
Expand Down
255 changes: 255 additions & 0 deletions src/features/login/LogInContinue.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import { ThemeProvider } from '@mui/material';
import { render, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { createTestStore } from '../../app/store';
import { theme } from '../../theme';
import { LogInContinue } from './LogInContinue';
import fetchMock from 'jest-fetch-mock';
import { toast } from 'react-hot-toast';
import { noOp } from '../common';

jest.mock('react-hot-toast', () => ({
toast: jest.fn(),
}));

describe('Login Continue', () => {
beforeEach(() => {
fetchMock.enableMocks();
fetchMock.resetMocks();
});

it('renders and logs in', async () => {
// getLoginChoice
fetchMock.mockResponseOnce(
JSON.stringify({
login: [
{
id: 'foouserid',
policyids: [
{
agreedon: 0,
id: 'foopolicy',
},
],
},
],
})
);
// postLoginPick
fetchMock.mockResponseOnce(
JSON.stringify({
token: {
token: 'foobartoken',
},
})
);
// authFromToken
fetchMock.mockResponseOnce(
JSON.stringify({
user: 'someusername',
})
);

const store = createTestStore();
const Narratives = jest.fn(() => <></>);
const { container } = render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<MemoryRouter initialEntries={['/login/continue']}>
<Routes>
<Route path={'/login/continue'} element={<LogInContinue />} />
<Route path={'/narratives'} Component={Narratives} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>
);
await waitFor(() => expect(container).toHaveTextContent('Logging in'));
await waitFor(() => {
expect(store.getState().auth.initialized).toBe(true);
expect(store.getState().auth.token).toBe('FOOBARTOKEN');
expect(store.getState().auth.username).toBe('someusername');
expect(Narratives).toHaveBeenCalled();
});
});

it('renders and logs in with redirect (nextRequest)', async () => {
// getLoginChoice
fetchMock.mockResponseOnce(
JSON.stringify({
login: [
{
id: 'foouserid',
policyids: [
{
agreedon: 0,
id: 'foopolicy',
},
],
},
],
})
);
// postLoginPick
fetchMock.mockResponseOnce(
JSON.stringify({
redirecturl:
'http://localhost/login/continue?state=%7B%22nextRequest%22%3A%22%7B%5C%22pathname%5C%22%3A%5C%22%2FsomeRedirect%5C%22%7D%22%2C%22origin%22%3A%22http%3A%2F%2Flocalhost%22%7D',
token: {
token: 'foobartoken',
},
})
);
// authFromToken
fetchMock.mockResponseOnce(
JSON.stringify({
user: 'someusername',
})
);

const store = createTestStore();
const SomeRedirect = jest.fn(() => <></>);
const { container } = render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<MemoryRouter initialEntries={['/login/continue']}>
<Routes>
<Route path={'/login/continue'} element={<LogInContinue />} />
<Route path={'/someRedirect'} Component={SomeRedirect} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>
);
await waitFor(() => expect(container).toHaveTextContent('Logging in'));
await waitFor(() => {
expect(store.getState().auth.initialized).toBe(true);
expect(store.getState().auth.token).toBe('FOOBARTOKEN');
expect(store.getState().auth.username).toBe('someusername');
expect(SomeRedirect).toHaveBeenCalled();
});
});

it('getLoginChoice fails gracefully', async () => {
const consoleError = jest.spyOn(console, 'error');
consoleError.mockImplementation(noOp);
// getLoginChoice
fetchMock.mockResponseOnce('', { status: 500 });
const Login = jest.fn(() => <></>);
const store = createTestStore();
render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<MemoryRouter initialEntries={['/login/continue']}>
<Routes>
<Route path={'/login/continue'} element={<LogInContinue />} />
<Route path={'/login'} Component={Login} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>
);
await waitFor(() => {
expect(store.getState().auth.initialized).toBe(false);
expect(toast).toHaveBeenCalled();
expect(consoleError).toHaveBeenCalled();
expect(Login).toHaveBeenCalled();
});
});

it('postLoginPick fails gracefully', async () => {
const consoleError = jest.spyOn(console, 'error');
consoleError.mockImplementation(noOp);
// getLoginChoice
fetchMock.mockResponseOnce(
JSON.stringify({
login: [
{
id: 'foouserid',
policyids: [
{
agreedon: 0,
id: 'foopolicy',
},
],
},
],
})
);
// postLoginPick
fetchMock.mockResponseOnce('', { status: 500 });

const Login = jest.fn(() => <></>);
const store = createTestStore();
render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<MemoryRouter initialEntries={['/login/continue']}>
<Routes>
<Route path={'/login/continue'} element={<LogInContinue />} />
<Route path={'/login'} Component={Login} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>
);
await waitFor(() => {
expect(store.getState().auth.initialized).toBe(false);
expect(toast).toHaveBeenCalled();
expect(consoleError).toHaveBeenCalled();
expect(Login).toHaveBeenCalled();
});
});

it('authFromToken fails gracefully', async () => {
const consoleError = jest.spyOn(console, 'error');
consoleError.mockImplementation(noOp);
// getLoginChoice
fetchMock.mockResponseOnce(
JSON.stringify({
login: [
{
id: 'foouserid',
policyids: [
{
agreedon: 0,
id: 'foopolicy',
},
],
},
],
})
);
// postLoginPick
fetchMock.mockResponseOnce(
JSON.stringify({
token: {
token: 'foobartoken',
},
})
);
// authFromToken
fetchMock.mockResponseOnce('', { status: 500 });
const Login = jest.fn(() => <></>);
const store = createTestStore();
render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<MemoryRouter initialEntries={['/login/continue']}>
<Routes>
<Route path={'/login/continue'} element={<LogInContinue />} />
<Route path={'/login'} Component={Login} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>
);
await waitFor(() => {
expect(store.getState().auth.initialized).toBe(false);
expect(toast).toHaveBeenCalled();
expect(consoleError).toHaveBeenCalled();
expect(Login).toHaveBeenCalled();
});
});
});
7 changes: 5 additions & 2 deletions src/features/login/LogInContinue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const LogInContinue: FC = () => {
const choiceData = choiceResult.data;

// if/when postLoginPick has a result, update app auth state using that token
useTryAuthFromToken(pickResult.data?.token.token);
const tokenResult = useTryAuthFromToken(pickResult.data?.token.token);

// wrap choiceData handling in an effect so we only trigger the pick call once
useEffect(() => {
Expand All @@ -65,14 +65,15 @@ export const LogInContinue: FC = () => {

useEffect(() => {
// Monitor error state, return to login
if (!pickResult.isError && !choiceResult.isError) {
if (!pickResult.isError && !choiceResult.isError && !tokenResult.isError) {
return;
} else {
// eslint-disable-next-line no-console
console.error({
'login error(s)': {
pick: pickResult.error,
choice: choiceResult.error,
token: tokenResult.error,
},
});
toast('An error occured during login, please try again.');
Expand All @@ -84,6 +85,8 @@ export const LogInContinue: FC = () => {
navigate,
pickResult.error,
pickResult.isError,
tokenResult.error,
tokenResult.isError,
]);

return (
Expand Down

0 comments on commit 5c2e7db

Please sign in to comment.