I have a header component that renders a form component:
import { useWeatherContext } from "../lib/Context/WeatherContext";
import HeaderForm from "./HeaderForm";
const Header = () => {
const { setWeather } = useWeatherContext();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const target = e.target as typeof e.target & {
cityZip: { value: string };
};
setWeather({ location: target.cityZip.value });
target.cityZip.value = "";
};
return (
<header>
<h2>Weathur</h2>
<HeaderForm handleSubmit={handleSubmit} />
</header>
);
};
export default Header;
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
const HeaderForm = ({
handleSubmit,
}: {
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
}) => {
return (
<form
data-testid="city-form"
autoComplete="off"
id="search-form"
onSubmit={(e) => {
handleSubmit(e);
}}
>
<input
data-testid="city-input"
type="text"
name="cityZip"
placeholder="City or Zip..."
aria-label="City or Zip Code Input"
required
onInvalid={(e) => {
const target = e.target as HTMLInputElement;
target.setCustomValidity("Please enter a valid city or zip code.");
}}
onInput={(e) => {
const target = e.target as HTMLInputElement;
target.setCustomValidity("");
}}
/>
<button
data-testid="city-form-button"
type="submit"
aria-label="Submit Button for Search"
>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</button>
</form>
);
};
export default HeaderForm;
Previously, I had this form within the header, but then extracted it because I could not figure out how to test the handleSubmit() function. So after extracting the form, and passing handleSubmit as a prop, my test looks like this (and passes):
import { screen, fireEvent, waitFor, render } from "@testing-library/react";
import { WeatherContextProvider } from "../src/lib/Context/WeatherContext";
import { ErrorContextProvider } from "../src/lib/Context/ErrorContext";
import { LoadingContextProvider } from "../src/lib/Context/LoadingContext";
import HeaderForm from "../src/components/HeaderForm";
const handleSubmitMock = vi.fn();
const renderElements = () => {
render(
<WeatherContextProvider>
<ErrorContextProvider>
<LoadingContextProvider>
<HeaderForm handleSubmit={handleSubmitMock} />
</LoadingContextProvider>
</ErrorContextProvider>
</WeatherContextProvider>
);
};
describe("Header", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should trigger handleSubmit when form is submitted", async () => {
renderElements();
const input = screen.getByTestId("city-input");
const button = screen.getByTestId("city-form-button");
fireEvent.change(input, { target: { value: "11102" } });
fireEvent.click(button);
await waitFor(() => {
expect(handleSubmitMock).toHaveBeenCalledTimes(1);
});
});
it("should not allow a blank input to be submitted", async () => {
renderElements();
const input = screen.getByTestId("city-input");
const button = screen.getByTestId("city-form-button");
fireEvent.change(input, { target: { value: "" } });
fireEvent.click(button);
await waitFor(() => {
expect(handleSubmitMock).not.toHaveBeenCalledTimes(1);
});
});
});
I'm confused why I needed to extract my form in the first place. Is there a way to access handleSubmit within Header()? I am using Vitest and React Testing Library for this, along with React, Vite, and Typescript.
I appreciate any help, if more code is necessary, here is the github repo!