I am writing a test for checking if removeItem is called successfully on logout. The test passes but I get an error that "the LoginForm inside a test was not wrapped in act(...)".
Here is the LoginForm component code:
const LoginForm = () => {
const [loggedIn, setLoggedIn] = useState(false);
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<LoginData>();
const onSubmit: SubmitHandler<LoginData> = async (data) => {
try {
const response = await loginUser(data);
if (response.status === 200) {
localStorage.setItem("accessToken", response.data.accessToken);
setLoggedIn(true);
toast.success("login successful");
reset();
}
} catch (error: any) {
toast.error(error.response.data.error);
reset();
}
};
const logoutUser = () => {
try {
localStorage.removeItem("accessToken");
setLoggedIn(false);
toast.success("logout successful");
} catch (error) {
toast.error("Could not logout");
}
};
return (
<main>
<ToastContainer />
<form data-testid="login-form" onSubmit={handleSubmit(onSubmit)}>
<h1>User Login:</h1>
<div className="form__input">
<input
type="text"
id="email"
placeholder="Email Address"
data-testid="email"
{...register("email", { required: "Email address is required" })}
/>
{errors.email && (
<span className="errorMsg" role="alert">
{errors.email.message}
</span>
)}
</div>
<div className="form__input">
<input
type="password"
id="password"
placeholder="Password"
data-testid="password"
{...register("password", { required: "Password is required" })}
/>
{errors.password && (
<span className="errorMsg" role="alert">
{errors.password.message}
</span>
)}
</div>
{loggedIn === false ? (
<button className="submitBtn" type="submit">
Login
</button>
) : (
<button
data-testid="logoutBtn"
className="submitBtn"
onClick={logoutUser}
>
Logout
</button>
)}
</form>
</main>
);
};
I believe its a problem with async functions and have tried wrapping the expect statement in await waitFor(())=>{} but the problem persists.
Here is the test code:
it("should remove accessToken from localStorage on logout", async () => {
render(<Login />);
const email = await screen.findByRole("textbox");
const password = await screen.findByPlaceholderText("Password");
const loginBtn = await screen.findByRole("button", {
name: /login/i,
});
fireEvent.change(email, { target: { value: "email" } });
fireEvent.change(password, { target: { value: "password" } });
fireEvent.click(loginBtn);
mockedAxios.post.mockImplementation(() =>
Promise.resolve({
status: 200,
data: { accessToken: "eYagkaogk...", refreshToken: "eyAagga..." },
})
);
const removeItem = jest.spyOn(Storage.prototype, "removeItem");
const logoutBtn = await screen.findByRole("button", {
name: /logout/i,
});
fireEvent.click(logoutBtn);
await waitFor(() => {
expect(removeItem).toHaveBeenCalledWith("accessToken");
});
});