MUI Autocomplete tests not working properly

26 views Asked by At

I am trying to write a test for a formik form to get submitted after I have filled the form correctly. I am using Material UI auto complete and Text Field for this. I have created a dynamic component for both Textfield and Autocomplete field. Now the issue I am facing is that I am unable to access the value options from the dropdown.

Here is my test file

import React from "react";
import {
  render,
  fireEvent,
  screen,
  waitFor,
  within,
  act,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Step1 from "../Step1";

describe("Step1 Component", () => {
  const handleNext = jest.fn();
  // Mock handleNext function

  // Mock useFormik hook
  jest.mock("formik", () => ({
    useFormik: jest.fn(() => ({
      handleSubmit: jest.fn(),
      isValid: true, // Mock the isValid property to return true
      values: {}, // You may need to add values if your form relies on them
    })),
  }));

  beforeEach(() => {
    render(<Step1 handleNext={handleNext} />);
  });

  test("form renders without crashing", () => {
    const formElement = screen.getByTestId("step1-form");
    expect(formElement).toBeInTheDocument();
  });

  test("form renders validations", () => {
    const nextButton = screen.getByTestId("next-button");

    fireEvent.click(nextButton);
    const validation1 = screen.getByText("First name is required");
    const validation2 = screen.getByText("Last name is required");
    expect(validation1).toBeInTheDocument();
    expect(validation2).toBeInTheDocument();
  });

  test("rendering and submitting a basic Formik form", async () => {
    const user = userEvent.setup();

    await user.type(
      screen.getByRole("textbox", { name: "First Name" }),
      "John"
    );
    await user.type(screen.getByRole("textbox", { name: "Last Name" }), "Dee");

    const selectLabel1 = "Pakistan";
    const selectLabel2 = "Master";

    const dropdown1 = screen.getByRole("combobox", { name: "Country" });
    userEvent.click(dropdown1);
    // Locate the corresponding popup (`listbox`) of options.
    const optionsPopupEl = await screen.findByRole("option", {
      name: selectLabel1,
    });

    const dropdown2 = screen.getByRole("combobox", { name: "Degree" });
    userEvent.click(dropdown2);
    // Locate the corresponding popup (`listbox`) of options.
    const optionsPopupEl2 = await screen.findByRole("option", {
      name: selectLabel2,
    });

    console.log(optionsPopupEl2, "OPT");

    // Click an option in the popup.
    userEvent.click(within(optionsPopupEl).findByText("Pakistan"));
    fireEvent.change(dropdown1, { target: { value: "Pakistan" } });

    // userEvent.click(within(optionsPopupEl2).findByText("Master"));

    // userEvent.click(optionsPopupEl);
    // userEvent.click(optionsPopupEl2);

    await user.click(screen.getByTestId("next-button"));

    // Assert that handleNextMock has been called
    await waitFor(() => expect(handleNext).toHaveBeenCalled());
  });


  
});

This is the form I am performing the tests on

import React from "react";

import { Box, Grid } from "@mui/material";
// import Grid from "@mui/material/Unstable_Grid2/Grid2";

import { useFormik } from "formik";

import { InputField, AutoCompleteField } from "../../../../components/fields";
import FormHeader from "../FormHeader/FormHeader";

import { useValidationSchema, useInitialValues } from "./utils";

const Step1 = (props) => {
  const {
    loading,
    submitLoading,
    stepsArray,
    activeStep,
    handleBack,
    handleNext,
  } = props;

  const formik = useFormik({
    initialValues: useInitialValues(),

    validationSchema: useValidationSchema(),

    enableReinitialize: true,

    validateOnMount: true,

    onSubmit: (values) => {
      console.log(values, "submit");
    },
  });

  const handleNextClick = () => {
    formik.handleSubmit();
    if (formik?.isValid) {
      handleNext();
    }
  };

  const handleBackClick = () => {
    handleBack();
  };

  const countryOptions = [
    { label: "Pakistan", value: "Pakistan" },
    { label: "India", value: "India" },
    { label: "England", value: "England" },
    { label: "USA", value: "USA" },
  ];
  const degreeOptions = [
    { label: "Bachelor", value: "Bachelor" },
    { label: "Master", value: "Master" },
    { label: "PHD", value: "PHD" },
  ];

  return (
    <Box sx={{ background: "white", p: 3 }}>
      <FormHeader
        loading={loading}
        handleBack={handleBackClick}
        handleNext={handleNextClick}
        activeStep={activeStep}
        title={"Step 1"}
        steps={stepsArray}
      />
      <Box sx={{ p: 2, pt: 3, pointerEvents: submitLoading ? "none" : "auto" }}>
        <form data-testid="step1-form">
          <Grid container spacing={2}>
            <Grid xs={6}>
              <InputField
                formik={formik}
                name="firstName"
                type="text"
                loading={loading}
                required
                label="First Name"
              />
            </Grid>

            <Grid xs={6}>
              <InputField
                formik={formik}
                name="lastName"
                type="text"
                loading={loading}
                required
                label="Last Name"
              />
            </Grid>

            <Grid xs={6}>
              <AutoCompleteField
                data-testid="country"
                name="Country"
                formik={formik}
                loading={loading}
                label="Country"
                options={countryOptions}
                required
              />
            </Grid>

            <Grid xs={6}>
              <AutoCompleteField
                data-testid="degree"
                name="Degree"
                formik={formik}
                loading={loading}
                label="Degree"
                options={degreeOptions}
                required
              />
            </Grid>
          </Grid>
        </form>
      </Box>
    </Box>
  );
};

export default React.memo(Step1);

Here is the Custom Autocomplete I have created

import React from "react";

// import {
//   Autocomplete,
//   TextField,
//   FormControl,
//   Skeleton,
// } from "src/shared/material";
import {
  Autocomplete,
  TextField,
  FormControl,
  Skeleton,
  Box,
  Typography,
} from "@mui/material";

import { useState } from "react";

import { useEffect } from "react";

const AutoCompleteField = ({
  formik,
  name,
  label,
  options,
  required,
  loading,
}) => {
  const [defaultValue, setDefaultValue] = useState("Select a Country");

  useEffect(() => {
    if ((name && !defaultValue) || formik?.values[name] === "")
      setDefaultValue(formik.values[name]);
  }, [formik, name]);

  return (
    <>
      {loading ? (
        <Skeleton variant="rounded" width={"100%"} height={55} />
      ) : (
        <FormControl
          key={name}
          fullWidth
          error={Boolean(formik?.touched[name] && formik?.errors[name])}
          sx={{ pointerEvents: "auto" }}
        >
          <Autocomplete
            data-testid="dropdown-field"
            id={name}
            error={Boolean(formik?.touched?.[name] && formik?.errors?.[name])}
            helperText={formik?.touched?.[name] && formik?.errors?.[name]}
            options={options}
            getOptionLabel={(option) => option.label} // Specify how to extract the label
            onChange={(e, val) => {
              formik?.setFieldValue(name, val ? val.value : ""); // Adjust for the case when value is null
            }}
            renderOption={(props, option) => (
              <Box
                display="flex"
                component="li"
                flexDirection="row"
                alignItems="center"
                {...props}
              >
                <Typography variant="body2">{option.label}</Typography>
              </Box>
            )}
            renderInput={(params) => (
              <TextField
                {...params}
                name={name}
                label={label}
                variant="outlined"
                fullWidth
                inputProps={{
                  ...params.inputProps,
                  autoComplete: "new-password", // disable autocomplete and autofill
                }}
              />
            )}
          />
        </FormControl>
      )}
    </>
  );
};

export default React.memo(AutoCompleteField);

I have tried different solutions but nothing has worked so far.

0

There are 0 answers