refactor(client): remove useless client session state
This commit is contained in:
parent
46ccbf2591
commit
2b853596c3
16 changed files with 61 additions and 211 deletions
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import { render, screen, waitFor } from '../../../../tests/test-utils';
|
||||
import { getTRPCMock, getTRPCMockError } from '../../mocks/getTrpcMock';
|
||||
import { server } from '../../mocks/server';
|
||||
import { render, screen } from '../../../../tests/test-utils';
|
||||
import { Layout } from './Layout';
|
||||
|
||||
const pushFn = jest.fn();
|
||||
|
@ -23,28 +21,4 @@ describe('Test: Layout', () => {
|
|||
|
||||
expect(screen.getByText('test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should correctly set token in localStorage when refreshToken is called', async () => {
|
||||
// Arranger
|
||||
server.use(getTRPCMock({ path: ['auth', 'refreshToken'], type: 'mutation', response: { token: 'fake-token' } }));
|
||||
render(<Layout>test</Layout>);
|
||||
|
||||
// Act
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem('token')).toBe('fake-token');
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove token from local storage and redirect to login page on error', async () => {
|
||||
// Arranger
|
||||
server.use(getTRPCMockError({ path: ['auth', 'refreshToken'], type: 'mutation', message: 'fake-error' }));
|
||||
render(<Layout>test</Layout>);
|
||||
const removeItemSpy = jest.spyOn(localStorage, 'removeItem');
|
||||
|
||||
// Act
|
||||
await waitFor(() => {
|
||||
expect(removeItemSpy).toBeCalledWith('token');
|
||||
});
|
||||
expect(pushFn).toBeCalledWith('/login');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import semver from 'semver';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Header } from '../ui/Header';
|
||||
import styles from './Layout.module.scss';
|
||||
import { useSystemStore } from '../../state/systemStore';
|
||||
import { trpc } from '../../utils/trpc';
|
||||
|
||||
interface IProps {
|
||||
loading?: boolean;
|
||||
breadcrumbs?: { name: string; href: string; current?: boolean }[];
|
||||
children: React.ReactNode;
|
||||
title?: string;
|
||||
|
@ -18,21 +15,6 @@ interface IProps {
|
|||
}
|
||||
|
||||
export const Layout: React.FC<IProps> = ({ children, breadcrumbs, title, actions }) => {
|
||||
const router = useRouter();
|
||||
const { mutate } = trpc.auth.refreshToken.useMutation({
|
||||
onSuccess: (data) => {
|
||||
if (data?.token) localStorage.setItem('token', data.token);
|
||||
},
|
||||
onError: () => {
|
||||
localStorage.removeItem('token');
|
||||
router.push('/login');
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
mutate();
|
||||
}, [mutate]);
|
||||
|
||||
const { version } = useSystemStore();
|
||||
const defaultVersion = '0.0.0';
|
||||
const isLatest = semver.gte(version?.current || defaultVersion, version?.latest || defaultVersion);
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '../../../../../tests/test-utils';
|
||||
import { getTRPCMock } from '../../../mocks/getTrpcMock';
|
||||
import { server } from '../../../mocks/server';
|
||||
import { AuthProvider } from './AuthProvider';
|
||||
|
||||
describe('Test: AuthProvider', () => {
|
||||
it('should render login form if user is not logged in', async () => {
|
||||
// arrange
|
||||
render(
|
||||
<AuthProvider>
|
||||
<div>Should not render</div>
|
||||
</AuthProvider>,
|
||||
);
|
||||
server.use(getTRPCMock({ path: ['auth', 'me'], type: 'query', response: null }));
|
||||
|
||||
// assert
|
||||
await screen.findByText('Login');
|
||||
expect(screen.queryByText('Should not render')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render children if user is logged in', async () => {
|
||||
// arrange
|
||||
render(
|
||||
<AuthProvider>
|
||||
<div>Should render</div>
|
||||
</AuthProvider>,
|
||||
);
|
||||
|
||||
// assert
|
||||
await screen.findByText('Should render');
|
||||
});
|
||||
|
||||
it('should render register form if app is not configured', async () => {
|
||||
// arrange
|
||||
server.use(getTRPCMock({ path: ['auth', 'me'], type: 'query', response: null }));
|
||||
server.use(getTRPCMock({ path: ['auth', 'isConfigured'], type: 'query', response: false }));
|
||||
|
||||
render(
|
||||
<AuthProvider>
|
||||
<div>Should not render</div>
|
||||
</AuthProvider>,
|
||||
);
|
||||
|
||||
// assert
|
||||
await screen.findByText('Register your account');
|
||||
expect(screen.queryByText('Should not render')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -1,29 +0,0 @@
|
|||
import React from 'react';
|
||||
import { LoginContainer } from '../../../modules/Auth/containers/LoginContainer';
|
||||
import { RegisterContainer } from '../../../modules/Auth/containers/RegisterContainer';
|
||||
import { trpc } from '../../../utils/trpc';
|
||||
import { StatusScreen } from '../../StatusScreen';
|
||||
|
||||
interface IProps {
|
||||
children: React.ReactElement;
|
||||
}
|
||||
|
||||
export const AuthProvider: React.FC<IProps> = ({ children }) => {
|
||||
const me = trpc.auth.me.useQuery();
|
||||
const isConfigured = trpc.auth.isConfigured.useQuery();
|
||||
const loading = me.isLoading || isConfigured.isLoading;
|
||||
|
||||
if (loading) {
|
||||
return <StatusScreen title="" subtitle="" />;
|
||||
}
|
||||
|
||||
if (me.data) {
|
||||
return children;
|
||||
}
|
||||
|
||||
if (!isConfigured?.data) {
|
||||
return <RegisterContainer />;
|
||||
}
|
||||
|
||||
return <LoginContainer />;
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export { AuthProvider } from './AuthProvider';
|
|
@ -3,6 +3,19 @@ import { fireEvent, render, renderHook, screen, waitFor } from '../../../../../t
|
|||
import { useUIStore } from '../../../state/uiStore';
|
||||
import { Header } from './Header';
|
||||
|
||||
const pushFn = jest.fn();
|
||||
jest.mock('next/router', () => {
|
||||
const actualRouter = jest.requireActual('next-router-mock');
|
||||
|
||||
return {
|
||||
...actualRouter,
|
||||
useRouter: () => ({
|
||||
...actualRouter.useRouter(),
|
||||
push: pushFn,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Header', () => {
|
||||
it('renders without crashing', () => {
|
||||
render(<Header />);
|
||||
|
@ -46,14 +59,13 @@ describe('Header', () => {
|
|||
expect(result.current.darkMode).toBe(false);
|
||||
});
|
||||
|
||||
it('Should remove the token from local storage on logout', async () => {
|
||||
localStorage.setItem('token', 'token');
|
||||
it('Should redirect to /login after successful logout', async () => {
|
||||
render(<Header />);
|
||||
const logoutButton = screen.getByTestId('logout-button');
|
||||
fireEvent.click(logoutButton as Element);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem('token')).toBeNull();
|
||||
expect(pushFn).toHaveBeenCalledWith('/login');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,6 @@ export const Header: React.FC<IProps> = ({ isUpdateAvailable }) => {
|
|||
const utils = trpc.useContext();
|
||||
const logout = trpc.auth.logout.useMutation({
|
||||
onSuccess: () => {
|
||||
localStorage.removeItem('token');
|
||||
utils.auth.me.invalidate();
|
||||
router.push('/login');
|
||||
},
|
||||
|
|
|
@ -40,22 +40,17 @@ export const handlers = [
|
|||
getTRPCMock({
|
||||
path: ['auth', 'login'],
|
||||
type: 'mutation',
|
||||
response: { token: 'token' },
|
||||
response: {},
|
||||
}),
|
||||
getTRPCMock({
|
||||
path: ['auth', 'logout'],
|
||||
type: 'mutation',
|
||||
response: true,
|
||||
}),
|
||||
getTRPCMock({
|
||||
path: ['auth', 'refreshToken'],
|
||||
type: 'mutation',
|
||||
response: { token: 'token' },
|
||||
}),
|
||||
getTRPCMock({
|
||||
path: ['auth', 'register'],
|
||||
type: 'mutation',
|
||||
response: { token: 'token' },
|
||||
response: true,
|
||||
}),
|
||||
getTRPCMock({
|
||||
path: ['auth', 'me'],
|
||||
|
|
|
@ -37,11 +37,3 @@ export const AppDetailsPage: NextPage<IProps> = ({ appId }) => {
|
|||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
AppDetailsPage.getInitialProps = (ctx) => {
|
||||
const { query } = ctx;
|
||||
|
||||
const appId = String(query.id);
|
||||
|
||||
return { appId };
|
||||
};
|
||||
|
|
|
@ -5,6 +5,19 @@ import { getTRPCMock, getTRPCMockError } from '../../../../mocks/getTrpcMock';
|
|||
import { server } from '../../../../mocks/server';
|
||||
import { LoginContainer } from './LoginContainer';
|
||||
|
||||
const pushFn = jest.fn();
|
||||
jest.mock('next/router', () => {
|
||||
const actualRouter = jest.requireActual('next-router-mock');
|
||||
|
||||
return {
|
||||
...actualRouter,
|
||||
useRouter: () => ({
|
||||
...actualRouter.useRouter(),
|
||||
push: pushFn,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test: LoginContainer', () => {
|
||||
it('should render without error', () => {
|
||||
// Arrange
|
||||
|
@ -38,12 +51,11 @@ describe('Test: LoginContainer', () => {
|
|||
expect(loginButton).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should add token in localStorage on submit', async () => {
|
||||
it('should redirect to / upon successful login', async () => {
|
||||
// Arrange
|
||||
const email = faker.internet.email();
|
||||
const password = faker.internet.password();
|
||||
const token = faker.datatype.uuid();
|
||||
server.use(getTRPCMock({ path: ['auth', 'login'], type: 'mutation', response: { token } }));
|
||||
server.use(getTRPCMock({ path: ['auth', 'login'], type: 'mutation', response: {} }));
|
||||
render(<LoginContainer />);
|
||||
|
||||
// Act
|
||||
|
@ -57,7 +69,7 @@ describe('Test: LoginContainer', () => {
|
|||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem('token')).toEqual(token);
|
||||
expect(pushFn).toHaveBeenCalledWith('/');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -79,8 +91,6 @@ describe('Test: LoginContainer', () => {
|
|||
await waitFor(() => {
|
||||
expect(screen.getByText(/my big error/)).toBeInTheDocument();
|
||||
});
|
||||
const token = localStorage.getItem('token');
|
||||
expect(token).toBeNull();
|
||||
});
|
||||
|
||||
it('should show totp form if totpSessionId is returned', async () => {
|
||||
|
@ -149,14 +159,13 @@ describe('Test: LoginContainer', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should add token in localStorage if totp code is valid', async () => {
|
||||
it('should redirect to / if totp is valid', async () => {
|
||||
// arrange
|
||||
const email = faker.internet.email();
|
||||
const password = faker.internet.password();
|
||||
const totpSessionId = faker.datatype.uuid();
|
||||
const token = faker.datatype.uuid();
|
||||
server.use(getTRPCMock({ path: ['auth', 'login'], type: 'mutation', response: { totpSessionId } }));
|
||||
server.use(getTRPCMock({ path: ['auth', 'verifyTotp'], type: 'mutation', response: { token } }));
|
||||
server.use(getTRPCMock({ path: ['auth', 'verifyTotp'], type: 'mutation', response: true }));
|
||||
render(<LoginContainer />);
|
||||
|
||||
// act
|
||||
|
@ -183,7 +192,7 @@ describe('Test: LoginContainer', () => {
|
|||
|
||||
// assert
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem('token')).toEqual(token);
|
||||
expect(pushFn).toHaveBeenCalledWith('/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useRouter } from 'next/router';
|
||||
import { trpc } from '../../../../utils/trpc';
|
||||
import { AuthFormLayout } from '../../components/AuthFormLayout';
|
||||
import { LoginForm } from '../../components/LoginForm';
|
||||
|
@ -14,14 +14,12 @@ export const LoginContainer: React.FC = () => {
|
|||
const utils = trpc.useContext();
|
||||
const login = trpc.auth.login.useMutation({
|
||||
onError: (e) => {
|
||||
localStorage.removeItem('token');
|
||||
toast.error(`Login failed: ${e.message}`);
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
if (data.totpSessionId) {
|
||||
setTotpSessionId(data.totpSessionId);
|
||||
} else if (data.token) {
|
||||
localStorage.setItem('token', data.token);
|
||||
} else {
|
||||
utils.auth.me.invalidate();
|
||||
router.push('/');
|
||||
}
|
||||
|
@ -32,8 +30,7 @@ export const LoginContainer: React.FC = () => {
|
|||
onError: (e) => {
|
||||
toast.error(`Verification failed: ${e.message}`);
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
localStorage.setItem('token', data.token);
|
||||
onSuccess: () => {
|
||||
utils.auth.me.invalidate();
|
||||
router.push('/');
|
||||
},
|
||||
|
|
|
@ -5,6 +5,19 @@ import { getTRPCMock, getTRPCMockError } from '../../../../mocks/getTrpcMock';
|
|||
import { server } from '../../../../mocks/server';
|
||||
import { RegisterContainer } from './RegisterContainer';
|
||||
|
||||
const pushFn = jest.fn();
|
||||
jest.mock('next/router', () => {
|
||||
const actualRouter = jest.requireActual('next-router-mock');
|
||||
|
||||
return {
|
||||
...actualRouter,
|
||||
useRouter: () => ({
|
||||
...actualRouter.useRouter(),
|
||||
push: pushFn,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test: RegisterContainer', () => {
|
||||
it('should render without error', () => {
|
||||
render(<RegisterContainer />);
|
||||
|
@ -12,13 +25,12 @@ describe('Test: RegisterContainer', () => {
|
|||
expect(screen.getByText('Register')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should add token in localStorage on submit', async () => {
|
||||
it.only('should redirect to / upon successful registration', async () => {
|
||||
// Arrange
|
||||
const email = faker.internet.email();
|
||||
const password = faker.internet.password();
|
||||
const token = faker.datatype.uuid();
|
||||
|
||||
server.use(getTRPCMock({ path: ['auth', 'register'], type: 'mutation', response: { token }, delay: 100 }));
|
||||
server.use(getTRPCMock({ path: ['auth', 'register'], type: 'mutation', response: true, delay: 100 }));
|
||||
render(<RegisterContainer />);
|
||||
|
||||
// Act
|
||||
|
@ -33,7 +45,9 @@ describe('Test: RegisterContainer', () => {
|
|||
fireEvent.click(registerButton);
|
||||
|
||||
// Assert
|
||||
await waitFor(() => expect(localStorage.getItem('token')).toEqual(token));
|
||||
await waitFor(() => {
|
||||
expect(pushFn).toHaveBeenCalledWith('/');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show toast if register mutation fails', async () => {
|
||||
|
|
|
@ -12,11 +12,9 @@ export const RegisterContainer: React.FC = () => {
|
|||
const utils = trpc.useContext();
|
||||
const register = trpc.auth.register.useMutation({
|
||||
onError: (e) => {
|
||||
localStorage.removeItem('token');
|
||||
toast.error(`Registration failed: ${e.message}`);
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
localStorage.setItem('token', data.token);
|
||||
onSuccess: () => {
|
||||
utils.auth.me.invalidate();
|
||||
router.push('/');
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@ jest.mock('next/router', () => {
|
|||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test: LoginPage', () => {
|
||||
it('should render correctly', async () => {
|
||||
render(<LoginPage />);
|
||||
|
|
|
@ -32,28 +32,6 @@ describe('Test: GeneralActions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should log user out if update is successful', async () => {
|
||||
// arrange
|
||||
localStorage.setItem('token', '123');
|
||||
server.use(getTRPCMock({ path: ['system', 'getVersion'], response: { current: '1.0.0', latest: '2.0.0', body: '' } }));
|
||||
server.use(getTRPCMock({ path: ['system', 'update'], response: true }));
|
||||
render(<GeneralActions />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: 'Update to 2.0.0' })).toBeInTheDocument();
|
||||
});
|
||||
const updateButton = screen.getByRole('button', { name: /Update to 2.0.0/i });
|
||||
fireEvent.click(updateButton);
|
||||
|
||||
// act
|
||||
const updateButtonModal = screen.getByRole('button', { name: /Update/i });
|
||||
fireEvent.click(updateButtonModal);
|
||||
|
||||
// assert
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem('token')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show toast if restart mutation fails', async () => {
|
||||
// arrange
|
||||
server.use(getTRPCMockError({ path: ['system', 'restart'], type: 'mutation', status: 500, message: 'Something went wrong' }));
|
||||
|
@ -70,24 +48,4 @@ describe('Test: GeneralActions', () => {
|
|||
expect(screen.getByText(/Something went wrong/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should log user out if restart is successful', async () => {
|
||||
// arrange
|
||||
localStorage.setItem('token', '1234');
|
||||
server.use(getTRPCMock({ path: ['system', 'restart'], response: true }));
|
||||
render(<GeneralActions />);
|
||||
|
||||
// Find button near the top of the page
|
||||
const restartButton = screen.getByRole('button', { name: /Restart/i });
|
||||
|
||||
// act
|
||||
fireEvent.click(restartButton);
|
||||
const restartButtonModal = screen.getByRole('button', { name: /Restart/i });
|
||||
fireEvent.click(restartButtonModal);
|
||||
|
||||
// assert
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem('token')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,7 +27,6 @@ export const GeneralActions = () => {
|
|||
},
|
||||
onSuccess: async () => {
|
||||
setPollStatus(true);
|
||||
localStorage.removeItem('token');
|
||||
},
|
||||
onError: (error) => {
|
||||
updateDisclosure.close();
|
||||
|
@ -42,7 +41,6 @@ export const GeneralActions = () => {
|
|||
},
|
||||
onSuccess: async () => {
|
||||
setPollStatus(true);
|
||||
localStorage.removeItem('token');
|
||||
},
|
||||
onError: (error) => {
|
||||
restartDisclosure.close();
|
||||
|
|
Loading…
Add table
Reference in a new issue