I feel like it is an issue due to the use of React hook form and its handleSubmit method, but still can't figure it out at this point.
Here is a SimpleForm component I am currently writing a test procedure for, using React Testing Library & vitest :
'use client';
import React from 'react';
import { useForm } from 'react-hook-form';
import { Button } from '../../button';
import { Input } from '../../input';
import { cn } from '@utils/cn';
import { formVariants } from '../style';
import type { FieldValues } from 'react-hook-form';
import type { SimpleFormProps } from '@/types';
/**
*
* @param inputs array containing all the fiels to process
* @param onSubmit
* @param defaultValues
* @param width max-width of the form
* @param gap gap between the inputs
* @param backgroundColor general setting to set a default value
* @param labelPosition
* @param inputVariant
* @param children to pass extra buttons in the submit button zone
* @returns a simple form component that process all the inputs with React Hook Form
*/
export const SimpleForm = <T extends FieldValues>({
inputs,
onSubmit,
defaultValues,
width = 'md',
gap = 'lg',
backgroundColor: globalBackgroundColor,
labelPosition: globalLabelPosition,
inputVariant: globalInputVariant,
children,
}: SimpleFormProps<T>) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<T>({ defaultValues });
return (
<form
onSubmit={handleSubmit(onSubmit)}
className={cn(formVariants({ width, gap }))}
aria-label="form"
>
{inputs.map(
({
label,
backgroundColor: customBackgroundColor,
labelPosition: customlabelPosition,
inputVariant: customInputVariant,
...props
}) => (
<Input
key={label}
label={label}
register={register}
errors={errors}
backgroundColor={customBackgroundColor ?? globalBackgroundColor}
labelPosition={customlabelPosition ?? globalLabelPosition}
inputVariant={customInputVariant ?? globalInputVariant}
{...props}
/>
)
)}
<div>
<Button
type="submit"
backgroundColor={globalBackgroundColor}
className="mt-12"
>
Submit
</Button>
{children}
</div>
</form>
);
};
The test procedure I'm writing :
import React from 'react';
import '@testing-library/jest-dom';
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, test, vi, afterEach, beforeEach } from 'vitest';
import type { Mock } from 'vitest';
import { SimpleForm } from '.';
import { createFakeInputs, fillRequiredFakeInputs } from '@/test/utils';
import type { InputGenericType } from '@/types';
type InputOption = Omit<InputGenericType<any>, 'errors' | 'register'>;
describe('Simple forms', () => {
let fakeInputs: InputOption[];
const mockedHandleOnSubmit: Mock = vi.fn((data) => {
console.log('Je suis appelée !');
console.log(data);
});
beforeEach(() => {
fakeInputs = createFakeInputs();
});
afterEach(() => {
cleanup();
});
test('creates a form with random inputs and a submit button', () => {
render(<SimpleForm inputs={fakeInputs} onSubmit={mockedHandleOnSubmit} />);
screen.getByRole('form');
for (const input of fakeInputs) {
screen.getByText(input.label);
}
screen.getByRole('button', { name: 'Submit' });
});
test('Submit button sends ', () => {
render(<SimpleForm inputs={fakeInputs} onSubmit={mockedHandleOnSubmit} />);
// const form = screen.getByRole('form');
fillRequiredFakeInputs(fakeInputs);
const submitButton = screen.getByRole('button', { name: 'Submit' });
console.log('Before fireEvent.click(submitButton)');
fireEvent.click(submitButton);
// fireEvent.submit(form);
console.log('After fireEvent.click(submitButton)');
expect(mockedHandleOnSubmit).toHaveBeenCalledOnce();
});
});
And here is the test report :
stdout | app/components/atomic/form/simple-form/simple-form.test.tsx > Simple forms > Submit button sends
Before fireEvent.click(submitButton)
After fireEvent.click(submitButton)
Je suis appelée !
{
neque: 'unde omnis minima autem fuga ad',
beatae: 'labore enim qui corrupti accusamus quo',
delectus: 'occaecati consequuntur et modi laborum fugiat',
minima: 'dicta unde laborum repellat maiores aspernatur',
praesentium: undefined
}
❯ app/components/atomic/form/simple-form/simple-form.test.tsx (2)
❯ Simple forms (2)
✓ creates a form with random inputs and a submit button
× Submit button sends
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
FAIL app/components/atomic/form/simple-form/simple-form.test.tsx > Simple forms > Submit button sends
AssertionError: expected "spy" to be called once, but got 0 times
mockedHandleOnSubmit is called, but undetected by vitest's spy.
Stack : NextJS: latest React: latest testing-library/react: 14.0.0
I tried to use a submission variant firing onSubmit directly from the form, with the exact same outcome. I tried a promise based approach as well, without positive outcome.