I am trying to write tests for an authentication form I created. I have the need to mock the return value of a custom hook which returns the login function and the state showing if the login function is running and waiting for a response. I have to mock the useAuth() hook in the top of the component. I've never done testing before so I feel a bit lost!
Here is my Form:
export const LoginForm = () => {
const { login, isLoggingIn } = useAuth(); // here's the function I want to mock
const { values, onChange }: any = useForm({});
const [error, setError] = useState(null);
const handleLogin = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
await login(values);
} catch (err: any) {
setError(err.message);
}
};
return (
<Box
sx={{
py: (t) => t.spacing(8),
px: (t) => t.spacing(8),
background: '#FFF',
borderRadius: 2,
marginTop: 0,
position: 'relative',
zIndex: 1,
}}
>
<form data-testid='login-form' onSubmit={handleLogin}>
<FormTextField
placeholder='your email'
name='email'
data-testid='login-email'
onChangeFn={onChange}
loading={isLoggingIn}
fullWidth={true}
id='email'
label='email'
type='text'
autoFocus={true}
/>
<FormTextField
placeholder='password'
name='password'
data-testid='login-password'
onChangeFn={onChange}
loading={isLoggingIn}
fullWidth={true}
id='password'
label='password'
type='password'
autoFocus={false}
sx={{
marginBottom: 2,
}}
/>
<Typography
variant='subtitle1'
sx={{
marginY: (t) => t.spacing(3.7),
textAlign: 'center',
}}
>
Forget password? <BlueLink to='#/hello' text='Reset here' />
</Typography>
<FormButton
text='Submit'
data-testid='submit-login'
variant='contained'
disabled={false}
fullWidth
type='submit'
sx={{
color: 'white',
}}
/>
<div data-testid='error-login'>
{error && (
<>
<hr />
{error}
</>
)}
</div>
</form>
<Hidden mdDown>
<img
style={{
position: 'absolute',
right: -50,
bottom: -50,
}}
src={SquigglesOne}
alt=''
/>
</Hidden>
</Box>
);
};
export default LoginForm;
Here's how I'm trying to mock it:
jest.mock('../../../helpers/auth', () => ({
...jest.requireActual('../../../helpers/auth'),
useAuth: jest.fn(() => ({
login: () => true,
})),
}));
When I run my tests I get an error:
Error: Uncaught [TypeError: Cannot destructure property 'login' of '(0 , _auth.useAuth)(...)' as it is undefined.] But when I remove the mock my test runs successfully so I hope I'm close. Here's my full test:
import {
fireEvent,
render /* fireEvent */,
within,
} from '@testing-library/react';
import { AuthProvider } from 'helpers/auth';
import { ReactQueryProvider } from 'helpers/react-query';
import { MemoryRouter } from 'react-router';
import LoginForm from './index';
jest.mock('../../../helpers/auth', () => ({
...jest.requireActual('../../../helpers/auth'),
useAuth: jest.fn(() => ({
login: () => true,
})),
}));
const setup = () => {
const loginRender = render(
<MemoryRouter>
<ReactQueryProvider>
<AuthProvider>
<LoginForm />
</AuthProvider>
</ReactQueryProvider>
</MemoryRouter>
);
const emailbox = loginRender.getByTestId('login-email');
const passwordBox = loginRender.getByTestId('login-password');
const emailField = within(emailbox).getByPlaceholderText('your email');
const passwordField = within(passwordBox).getByPlaceholderText('password');
const form = loginRender.getByTestId('login-form');
return {
emailField,
passwordField,
form,
...loginRender,
};
};
test('Input should be in document', async () => {
const { emailField, passwordField } = setup();
expect(emailField).toBeInTheDocument();
expect(passwordField).toBeInTheDocument();
});
test('Inputs should accept email and password strings', async () => {
const { emailField, passwordField } = setup();
emailField.focus();
await fireEvent.change(emailField, {
target: { value: 'atlanteavila@gmail.com' },
});
expect(emailField.value).toEqual('atlanteavila@gmail.com');
passwordField.focus();
await fireEvent.change(passwordField, {
target: { value: 'supersecret' },
});
expect(passwordField.value).toEqual('supersecret');
await fireEvent.keyDown(form, { key: 'Enter' });
});
test('Form should be submitted', async () => {
const { emailField, passwordField, form } = setup();
emailField.focus();
await fireEvent.change(emailField, {
target: { value: 'atlanteavila@gmail.com' },
});
expect(emailField.value).toEqual('atlanteavila@gmail.com');
passwordField.focus();
await fireEvent.change(passwordField, {
target: { value: 'supersecret' },
});
await fireEvent.keyDown(form, { key: 'Enter' });
expect(passwordField.value).toEqual('supersecret');
});