Reputation: 1114
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
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) -
handleOnSubmitMock
function, and then assigned it to screen.getByRole("form", { name: "signup-form" }).onsubmit
GlobalEventHandler. (Not passing the mock function to the Form)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 -
Form
component.onSubmit
handler to be either called or not called depending on the test conditions.Upvotes: 1
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.
Implicit ARIA role - form if the form has an accessible name, otherwise no corresponding role
Upvotes: 1
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
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