luiquao
luiquao

Reputation: 1114

@testing-library/react test form onSubmit

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?

Upvotes: 4

Views: 16276

Answers (4)

Mehedi
Mehedi

Reputation: 589

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.

Upvotes: 1

blu
blu

Reputation: 372

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

Upvotes: 1

Samerset
Samerset

Reputation: 71

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.

Upvotes: 0

luiquao
luiquao

Reputation: 1114

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

});

Upvotes: 3

Related Questions