I'm trying to test a simple form built with react-hook-form and zod.
My form has 2 fields: email and password. It's basiclly the same as the example on the docs, except that I use zod to validate form field instead of passing options into register
function. My test is quite simple as well, just click the button and expect to receive 2 alert
on the DOM. But somehow it fail.
Here is the form component:
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
email: z.string().email(),
password: z.string(),
})
type Input = z.infer<typeof formSchema>
export function SimpleForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Input>({ resolver: zodResolver(formSchema) })
function onSubmit() {}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor='email'>email</label>
<input id='email' {...register('email')} type='email' />
{errors.email && <span role='alert'>{errors.email.message}</span>}
<label htmlFor='password'>password</label>
<input id='password' {...register('password')} type='password' />
{errors.password && <span role='alert'>{errors.password.message}</span>}
<button type='submit'>SUBMIT</button>
</form>
)
}
Here is the test file:
import { SimpleForm } from './simple-form'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import React from 'react'
describe('SimpleForm', () => {
it('should display required error when value is invalid', async () => {
render(<SimpleForm />)
const user = userEvent.setup()
await user.click(screen.getByRole('button'))
expect(await screen.findAllByRole('alert')).toHaveLength(2)
})
})
and the error:
SimpleForm › should display required error when value is invalid
Unable to find role="alert"
Ignored nodes: comments, script, style
<body>
<div>
<form>
<label
for="email"
>
email
</label>
<input
id="email"
name="email"
type="email"
/>
<label
for="password"
>
password
</label>
<input
id="password"
name="password"
type="password"
/>
<button
type="submit"
>
SUBMIT
</button>
</form>
</div>
</body>
12 | await user.click(screen.getByRole('button'))
13 |
> 14 | expect(await screen.findAllByRole('alert')).toHaveLength(2)
| ^
15 | })
16 | })
17 |
I fix this by using waitFor
.
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SimpleForm } from './test-form';
describe('SimpleForm', () => {
it('should display required error when value is invalid', async () => {
render(<SimpleForm />);
const user = userEvent.setup();
await user.click(screen.getByRole('button'));
waitFor(() => {
const alerts = screen.getAllByRole('alert');
return expect(alerts.length).toBe(2);
});
});
});