I'm trying write integration tests the onSubmit
function of a Formik form using Jest and React Testing Library. Currently, I have a LoginForm
component which functional right now, but I'm having trouble testing it. I'm referencing the example for Formik
in React Testing Library's
docs and it looks like they're using a mock function to test the onSubmit
function.
https://testing-library.com/docs/example-react-formik/
It looks like onSubmit
is passed in via props to the component, but my component isn't doing that right now and I'm wondering if there's a way to test my onSubmit
function as it's currently written? Should I update my code to be functional with the tests? Any direction will be appreciated.
import React from "react";
import { Field, Form, Formik } from "formik";
import { loginService } from "../../services/login/loginService";
import { yupValidationSchema } from "./yupValidationSchema";
const LoginForm = () => {
const [state, setState] = React.useState({
loginFailed: false, // Boolean represented as true when user is successfully authenticated,
// false when there's an auth error, token has been removed or user has been logged out
});
const handleSubmit = async (values: { username: string; password: string }) => {
const loginSuccess = await loginService(values.username, values.password);
if (loginSuccess) {
window.history.replaceState("#/login", "", "#/");
window.location.reload();
} else {
setState({
...state,
loginFailed: true,
});
}
};
return (
<Formik
initialValues={{ username: "", password: "" }}
validationSchema={yupValidationSchema}
onSubmit={handleSubmit}
>
{({ errors, touched }) => (
<Form>
<Field data-testid={"username"} name="username" type="text" placeholder="Username" />
{touched.username && errors.username && <div>{errors.username}</div>}
<Field
data-testid={"password"}
name="password"
type="password"
placeholder="Password"
/>{" "}
<br />
{touched.password && errors.password && <div>{errors.password}</div>}
<button data-testid={"button"} name="submitButton" type="submit">
SUBMIT
</button>
{state.loginFailed && <div data-testid={"loginError"}>Unsuccessful Login</div>}
</Form>
)}
</Formik>
);
};
export default LoginForm;
describe("Login Component", () => {
const handleSubmit = jest.fn(); // where do I pass this?
test("Test Submission of login form" , async () => {
act(() => {
render(
<LoginForm /> // do I pass props in here?
);
});
const username = screen.getByPlaceholderText(/Username/i);
const password = screen.getByPlaceholderText(/Password/i);
const submitButton = screen.getByText("SUBMIT");
await waitFor(() => {
fireEvent.change(username, {
target: {
value: "testUser",
},
});
});
await waitFor(() => {
fireEvent.change(password, {
target: {
value: "testPassword",
},
});
});
await waitFor(() => {
fireEvent.click(submitButton);
});
await waitFor(() => {
expect(handleSubmit).toHaveBeenCalledWith({ // how do I test the handle submit?
username: "testUser",
password: "testPassword",
})
});
});
});
You can test if the component behaves as expected. So looking at the code of handleSubmit
method (see comments):
const loginSuccess = await loginService(values.username, values.password); // mock this and check if this is called with expected parameters
if (loginSuccess) { // mock the loginService to return truthy value and check if the redirection is made
window.history.replaceState("#/login", "", "#/");
window.location.reload();
} else { // mock the loginService to return falsy value and check for element with error message: "Unsuccessful Login"
setState({
...state,
loginFailed: true,
});
}
With that test you should also achieve nice coverage :)