Search code examples
reactjsenzymereact-testing-library

How to use test-id in Material UI Textfield


I'm trying to test a material ui text field using react-testing-library.

The issue im facing is that in order to test the material ui textField i would have to use this property method

screen.getByLabelText()

which works, however i do not want to display the label on the UI, i want the label to remain hidden, as im already using Material UI <FormLabel>.

I tried using inputProps and passing data-testId on the element, using the getByTestId() method. but i get this error

TestingLibraryElementError: Found multiple elements by: [data-testid="bio"]

(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).

editForm.test.tsx

import "@testing-library/jest-dom";
import React from "react";
import { createMount } from "@material-ui/core/test-utils";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import EditProfileForm from "./editForm";
import { render as testRender, fireEvent, screen, getByText } from "@testing-library/react";
const props = {
    handleBio: jest.fn(),
};
describe("<EditProfileForm/>", () => {
    let wrapper;
    let mount;
    beforeEach(() => {
        mount = createMount();
        wrapper = mount(<EditProfileForm {...props} />);
    });

    it("should render <EditProfileForm/>", () => {
        expect(wrapper).toHaveLength(1);
    });

    it("calls handleBio on bio TextField change", () => {
        const input = screen.getByLabelText("bio");

        fireEvent.change(input, { target: { value: "new value" } });

        expect(props.handleBio).toHaveBeenCalledTimes(1);
    });
});

editForm.tsx

import Button from "@material-ui/core/Button";
import FormGroup from "@material-ui/core/FormGroup";
import FormLabel from "@material-ui/core/FormLabel";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import React from "react";
const EditProfileForm = (props: any) => (
    <form onSubmit={props.onSubmit}>
        <Typography variant="h5">Edit Profile</Typography>
        <FormGroup style={{ padding: "30px 0px" }}>
            <FormLabel style={{ display: "block" }}>Bio</FormLabel>
            <TextField
                id="outlined-name"
                style={{
                    width: "100%",
                }}
                name="bio"
                label="bio"
                multiline={true}
                rows="3"
                defaultValue={props.bio}
                onChange={props.handleBio}
                margin="normal"
                variant="outlined"
            />

        </FormGroup>
        <Button className="subBtn" variant="outlined" color="primary" type="submit">
            Submit
        </Button>
    </form>
);

export default EditProfileForm;

Solution

  • I was able to resolve this issue by first moving the test function after beforeEach been called.

    so it will now be

    import "@testing-library/jest-dom";
    import React from "react";
    import { createMount } from "@material-ui/core/test-utils";
    import Button from "@material-ui/core/Button";
    import Typography from "@material-ui/core/Typography";
    import EditProfileForm from "./editForm";
    import { render as testRender, fireEvent, screen, getByText } from "@testing-library/react";
    const props = {
        handleChange: jest.fn(),
        onSubmit: jest.fn(),
        bio: "test",
        gravatar: "https://i.pravatar.cc/150?img=3",
        handleBio: jest.fn(),
        handleGravatar: jest.fn(),
    };
    describe("<EditProfileForm/>", () => {
        let wrapper;
        let mount;
        beforeEach(() => {
            mount = createMount();
            wrapper = mount(<EditProfileForm {...props} />);
        });
        // must be called first
        it("calls handleBio on bio TextField change", () => {
            const input = screen.getByTestId("bio");
    
            fireEvent.change(input, { target: { value: "new value" } });
    
            expect(props.handleBio).toHaveBeenCalledTimes(1);
        });
    
        it("should render <EditProfileForm/>", () => {
            expect(wrapper).toHaveLength(1);
        });
    
        it("should check header title ", () => {
            expect(wrapper.find(Typography).at(0)).toHaveLength(1);
            expect(
                wrapper
                    .find(Typography)
                    .at(0)
                    .text(),
            ).toContain("Edit Profile");
        });
    
        it("should test bio prop", () => {
            expect(wrapper.props().bio).toContain("test");
        });
    
        it("should test gravtar prop", () => {
            const link = "https://i.pravatar.cc/150?img=3";
            expect(wrapper.props().gravatar).toContain(link);
        });
    
        it("should test handleChange props", () => {
            const title = "Test";
            expect(
                wrapper.props().handleChange({
                    target: {
                        value: title,
                    },
                }),
            );
            expect(props.handleChange).toHaveBeenCalled();
        });
    
        it("should test onSubmit prop", () => {
            // console.log(wrapper.find(TextField).debug());
            const submit = jest.fn();
            wrapper.simulate("submit", { submit });
            expect(props.onSubmit).toBeCalled();
        });
    
        it("should test button click", () => {
            const button = wrapper.find(Button);
            button.simulate("click");
            expect(props.onSubmit).toBeCalled();
        });
    });
    

    And then passing data-testid as an input prop on text field like this

    <TextField
        id="outlined-name"
        className="bio-test"
        style={{
            width: "100%",
        }}
        name="bio"
        inputProps={{
            "data-testid": "bio",
        }}
        multiline={true}
        rows="3"
        defaultValue={props.bio}
        onChange={props.handleBio}
        margin="normal"
        variant="outlined"
    />