Skip to content

Instantly share code, notes, and snippets.

@pratik14
Created September 17, 2019 03:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pratik14/0d2287d3dd4dc8ee81e0da2e38c107f9 to your computer and use it in GitHub Desktop.
Save pratik14/0d2287d3dd4dc8ee81e0da2e38c107f9 to your computer and use it in GitHub Desktop.
Testing react Formik forms with react-testing-library
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
const App = () => (
<div>
<Formik
initialValues={{ email: "", date: "" }}
validate={values => {
let errors = {};
if (!values.date) {
errors.date = "Required";
}
if (!values.email) {
errors.email = "Required";
}
return errors;
}}
>
{({ setFieldValue, values }) => (
<Form>
<label htmlFor="email">Email</label>
<Field id="email" type="email" name="email" />
<ErrorMessage data-testid="emailError" name="email" component="div" />
<br />
<DatePicker
selected={values.date}
onChange={event => {
setFieldValue("date", event);
}}
/>
<ErrorMessage data-testid="dateError" name="date" component="div" />
<br />
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
export default App;
import React from "react";
import "@testing-library/jest-dom/extend-expect";
import { render, fireEvent, wait } from "@testing-library/react";
import App from "./App";
it("should show validation on blur", async () => {
const { getByLabelText, getByTestId } = render(<App />);
const input = getByLabelText("Email");
fireEvent.blur(input);
await wait(() => {
expect(getByTestId("emailError")).not.toBe(null);
expect(getByTestId("emailError")).toHaveTextContent("Required");
});
});
it("should show date validation on click on submit", async () => {
const { getByText, getByTestId } = render(<App />);
const button = getByText("Submit");
fireEvent.click(button);
await wait(() => {
expect(getByTestId("dateError")).toHaveTextContent("Required");
});
});
jest.mock("react-datepicker", () => props => (
<input
data-testid="mockedDateField"
onChange={() => {
console.log(props.onChange);
props.onChange("asdfasd");
}}
/>
));
test("should remove date error id we select date", async () => {
const { getByText, getByTestId, queryByTestId } = render(<App />);
const button = getByText("Submit");
fireEvent.click(button);
const mockedDateField = getByTestId("mockedDateField");
fireEvent.change(mockedDateField, { target: { value: new Date() } });
await wait(() => {
expect(queryByTestId("dateError")).toBe(null);
});
});
@huxaiphaer
Copy link

Nice, though I need more help how can I test a component with a formik component e.g :

const CountrySelector = ({
  name,
  type,
  textFieldProps = undefined,
  withContext = true,
  countriesListProp = [],
  validateAttached = false,
  ...props
}: Props) => {
  const {
    touched,
    errors,
    setFieldValue,
    setFieldTouched,
    validateForm,
  } = useFormikContext<FormikValues>();
  const { countryList } = useCustomerCreatorState();
  const [countriesList, setCountriesList] = useState<CountryType[]>([]);

  useEffect(() => {
    if (withContext) setCountriesList(countryList);
    else setCountriesList(countriesListProp);
    countriesList.sort((a: CountryType, b: CountryType) => a.country.localeCompare(b.country));
  }, [withContext, countryList, countriesListProp, countriesList]);

  const handleGetOptionLabel = useCallback(
    (option: CountryType) => (option ? getOptionLabel[type](option) : ''),
    [type],
  );

  const handleGetOptionSelected = useCallback(
    (option: CountryType, value: CountryType) =>
      option && value ? option.code === value.code : null,
    [],
  );

  const handleGetOptionRender = useCallback(
    (option: CountryType) => getOptionRender[type](option),
    [type],
  );

  const handleOnBlurCountry = useCallback(() => {
    setFieldTouched(name);
  }, [setFieldTouched, name]);

  const handleOnOpenCountryList = useCallback(() => {
    setTimeout(() => {
      const optionEl = document.querySelector(`[data-name="${getOptionScroller[type]}"]`);
      optionEl?.scrollIntoView();
    }, 1);
  }, [type]);

  const handleOnChangeCountry = useCallback(
    (_: React.ChangeEvent<HTMLInputElement>, value: CountryType) => {
      setFieldTouched(name);
      setFieldValue(`${name}`, value);
      if (validateAttached) {
        setTimeout(() => {
          validateForm();
        }, 0);
      }
    },
    [setFieldValue, setFieldTouched, name, validateAttached, validateForm],
  );

  const handleRenderInput = useCallback(
    (params: AutocompleteRenderInputParams) => (
      <TextField
        {...params}
        {...textFieldProps}
        error={_get(touched, name) && !!_get(errors, name)}
        helperText={_get(touched, name) && _get(errors, name)}
        variant="outlined"
      />
    ),
    [errors, touched, name, textFieldProps],
  );

  return (
    <>
      {/* Todo: Field is used (instead of FastField) since the data is loaded asynchronously when the context does not exist. In a future we can use a different context for the creation of a customer profile. */}
      <Field
        name={name}
        onOpen={handleOnOpenCountryList}
        component={Autocomplete}
        options={countriesList}
        getOptionLabel={handleGetOptionLabel}
        getOptionSelected={handleGetOptionSelected}
        disableClearable
        renderOption={handleGetOptionRender}
        renderInput={handleRenderInput}
        onChange={handleOnChangeCountry}
        onBlur={handleOnBlurCountry}
        size="small"
        blurOnSelect
        clearOnBlur
        {...props}
        MenuProps={configMenuProps}
      />
    </>
  );
};

export default memo(CountrySelector);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment