React Testing Library - Impossible to detect an onSubmit call

78 Views Asked by At

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.

0

There are 0 best solutions below