4

In a simple scenario like so

function onSubmit() { e.preventDefault(); /* Some Submit Logic */ }

<form data-testid="form" onSubmit={(e) => onSubmit(e)}>
   <button type="submit" data-testid="submit-button">Submit</button>
</form>

How do I make sure that the form gets submitted when the submit button is clicked?

const { queryByTestId } = render(<LoginForm/>);
const LoginForm = queryByTestId("form")
const SubmitButton = queryByTestId("submit-button")
fireEvent.click(SubmitButton)

???

How do I test if onSubmit() has been called or maybe form has been submitted?

1
  • You can change "onsubmit={(e)=>onSubmit(e)}" to just "onsubmit = {onSubmit}" Commented Nov 10, 2022 at 18:46

4 Answers 4

3

Basically, here is what I "solved" it:

// LoginForm.js

export function LoginForm({ handleSubmit }) {

     const [name, setName] = useState('');

     function handleChange(e) {
         setName(e.target.value)
     }

     return (
         <form data-testid="form" onSubmit={() => handleSubmit({ name })}>
              <input required data-testid="input" type="text" value={name} onChange={(e) => handleChange(e)}/>
              <button type="submit" data-testid="submit-button">Submit</button>
         </form>
     )
}

export default function LoginPage() {

    function handleSubmit(e) {
        // submit stuff
    }

    return <LoginForm handleSubmit={(e) => handleSubmit(e)}/>

}

Now the test's file:

// LoginForm.test.js

import React from 'react';
import { render, fireEvent } from "@testing-library/react";
import LoginPage, { LoginForm } from "./LoginPage";

it("Form can be submited & input field is modifiable", () => {

    const mockSubmit = jest.fn();
    const { debug, queryByTestId } = render(<LoginForm handleSubmit={mockSubmit}/>);

    fireEvent.change(queryByTestId("input"), { target: { value: 'Joe Doe' } }); // invoke handleChange
    fireEvent.submit(queryByTestId("form"));

    expect(mockSubmit).toHaveBeenCalled(); // Test if handleSubmit has been called 
    expect(mockSubmit.mock.calls).toEqual([[{name: 'Joe Doe'}]]); // Test if handleChange works

});
Sign up to request clarification or add additional context in comments.

3 Comments

How would this work if you didnt have handleSubmit prop? In a lot of causes, components dont expect a handleSubmit prop to be passed in - why would they? It seems very counter intuitive to have to pass in a prop for a component that is ONLY used for testing?
Agree with @alexr89 ... requiring component methods to all be passed through props is basically code smell to satisfy tests.
4 years later and sadly there is still no resolution for the problem @alexr89 posed. This is also a problem that I am facing when using @tanstack/react-form where my form submit is fully localised. I'm not going to change my source code just to satisfy tests.
1
getByRole('form', { name: /formname/i })

RTL is meant to move away from using id's. An ideal solution is to name your form which does two things. It allow you to uniquely name it making it useful to screen readers and also gets the browser to assign it a role of form. Without the name, getByRole('form') will do nothing.

MDN: <form> element

Implicit ARIA role - form if the form has an accessible name, otherwise no corresponding role

Comments

1

I used jest and react-testing-library.

I personally don't like the idea of changing the Form component code by passing a mock or spy function just for testing purpose.

For my form, I come up with this idea (find the code samples and full answer here) -

  • basically I created handleOnSubmitMock function, and then assigned it to screen.getByRole("form", { name: "signup-form" }).onsubmit GlobalEventHandler. (Not passing the mock function to the Form)
  • then I checked if expect(handleOnSubmitMock).toHaveBeenCalled() or expect(handleOnSubmitMock).not.toHaveBeenCalled() passes.

You should be able to modify the code for your need from the descriptive code samples in the link, and it should work. The goal is to -

  • render the Form component.
  • get the necessary elements.
  • fire the button click event.
  • expect onSubmit handler to be either called or not called depending on the test conditions.

Comments

0

I'd suggest to use nock to intercept request sending from form and return mocked response after. For example:

nock('https://foo.bar').post('/sign-up', formValues).reply(201);

But I would like to know a better solutions tho.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.