import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import * as React from 'react'; import { useForm } from 'react-hook-form'; import { describe, expect, it, vi } from 'vitest'; import { Form } from './Form'; type TestFormValues = { channel: string; email: string; productUpdates: boolean; role: string; terms: boolean; }; const defaultValues: TestFormValues = { channel: 'email', email: '', productUpdates: false, role: 'designer', terms: false, }; function TestForm({ fieldErrors, onSubmit, withEmailError, }: { fieldErrors?: Partial>; onSubmit: (values: TestFormValues) => void; withEmailError?: boolean; }) { const form = useForm({ defaultValues }); React.useEffect(() => { if (withEmailError) { form.setError('email', { message: 'Enter a valid email address.', type: 'manual', }); } for (const [name, message] of Object.entries(fieldErrors ?? {}) as [ keyof TestFormValues, string, ][]) { form.setError(name, { message, type: 'manual', }); } }, [fieldErrors, form, withEmailError]); return (
Designer Engineer Submit
); } describe('Form', () => { it('registers fields and submits React Hook Form values', async () => { const handleSubmit = vi.fn(); render(); fireEvent.change(screen.getByRole('textbox', { name: 'Email' }), { target: { value: 'ugo@example.com' }, }); fireEvent.click(screen.getByRole('switch', { name: 'Product updates' })); fireEvent.click(screen.getByRole('checkbox', { name: 'Accept terms' })); fireEvent.click(screen.getByRole('radio', { name: 'SMS' })); fireEvent.click(screen.getByRole('button', { name: 'Submit' })); await waitFor(() => { expect(handleSubmit).toHaveBeenCalledWith( { channel: 'sms', email: 'ugo@example.com', productUpdates: true, role: 'designer', terms: true, }, expect.anything(), ); }); }); it('displays field errors from React Hook Form state', async () => { render(); expect( await screen.findByText('Enter a valid email address.'), ).toBeInTheDocument(); expect(screen.getByRole('textbox', { name: 'Email' })).toHaveAttribute( 'aria-invalid', 'true', ); }); it('marks controlled boolean fields invalid from React Hook Form state', async () => { render( , ); expect( await screen.findByText('Choose a product update preference.'), ).toBeInTheDocument(); expect( screen.getByRole('switch', { name: 'Product updates' }), ).toHaveAttribute('aria-invalid', 'true'); expect( screen.getByRole('checkbox', { name: 'Accept terms' }), ).toHaveAttribute('aria-invalid', 'true'); }); });