fireEvent is calling Found multiple elements by: data-testid error in react-testing-library
Asked Answered
H

5

12

I'm calling a function by finding the button with the data-testid with "show_more_button"

<OurSecondaryButton test={"show_more_button"} onClick={(e) => showComments(e)} component="span" color="secondary">
    View {min !== -1 && min !== -2 ? min : 0} More Comments
</OurSecondaryButton>

showComments

const showComments = (e) => {
  e.preventDefault();
  if (inc + 2 && inc <= the_comments) {
     setShowMore(inc + 2);
     setShowLessFlag(true);
  } else {
     setShowMore(the_comments);
  }
};

which renders this

const showMoreComments = () => {
    return filterComments.map((comment, i) => (
        <div data-testid="comment-show-more" key={i}>
            <CommentListContainer ref={ref} comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
        </div>
    ));
};

and upon executing fireEvent the function gets called which is good but, im getting the error:

TestingLibraryElementError: Found multiple elements by: [data-testid="comment-show-more"]

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

There is only one data-testid with "comment-show-more", i doubled checked, this function must be getting triggered multiple times in the same test i guess, Im not sure..

CommentList.test.tsx

   it("should fire show more action", () => {
      const { getByTestId } = render(<CommentList {...props} />);
      const showMore = getByTestId("show_more_button");
      fireEvent.click(showMore);
      expect(getByTestId("comment-show-more").firstChild).toBeTruthy();
   });

CommentList.test.tsx (full code)

import "@testing-library/jest-dom";
import React, { Ref } from "react";
import CommentList from "./CommentList";
import { render, getByText, queryByText, getAllByTestId, fireEvent } from "@testing-library/react";
import { Provider } from "react-redux";
import { store } from "../../../store";

const props = {
    user: {},
    postId: null,
    userId: null,
    currentUser: {},
    ref: {
        current: undefined,
    },
    comments: [
        {
            author: { username: "barnowl", gravatar: "https://api.adorable.io/avatars/400/bf1eed82fbe37add91cb4192e4d14de6.png", bio: null },
            comment_body: "fsfsfsfsfs",
            createdAt: "2020-05-27T14:32:01.682Z",
            gifUrl: "",
            id: 520,
            postId: 28,
            updatedAt: "2020-05-27T14:32:01.682Z",
            userId: 9,
        },
        {
            author: { username: "barnowl", gravatar: "https://api.adorable.io/avatars/400/bf1eed82fbe37add91cb4192e4d14de6.png", bio: null },
            comment_body: "fsfsfsfsfs",
            createdAt: "2020-05-27T14:32:01.682Z",
            gifUrl: "",
            id: 519,
            postId: 27,
            updatedAt: "2020-05-27T14:32:01.682Z",
            userId: 10,
        },
        {
            author: { username: "barnowl2", gravatar: "https://api.adorable.io/avatars/400/bf1eed82fbe37add91cb4192e4d14de6.png", bio: null },
            comment_body: "fsfsfsfsfs",
            createdAt: "2020-05-27T14:32:01.682Z",
            gifUrl: "",
            id: 518,
            postId: 28,
            updatedAt: "2020-05-27T14:32:01.682Z",
            userId: 11,
        },
    ],
    deleteComment: jest.fn(),
};
describe("Should render <CommentList/>", () => {
    it("should render <CommentList/>", () => {
        const commentList = render(<CommentList {...props} />);
        expect(commentList).toBeTruthy();
    });

    it("should render first comment", () => {
        const { getByTestId } = render(<CommentList {...props} />);
        const commentList = getByTestId("comment-list-div");
        expect(commentList.firstChild).toBeTruthy();
    });

    it("should render second child", () => {
        const { getByTestId } = render(<CommentList {...props} />);
        const commentList = getByTestId("comment-list-div");
        expect(commentList.lastChild).toBeTruthy();
    });

    it("should check comments", () => {
        const rtl = render(<CommentList {...props} />);

        expect(rtl.getByTestId("comment-list-div")).toBeTruthy();
        expect(rtl.getByTestId("comment-list-div")).toBeTruthy();

        expect(rtl.getByTestId("comment-list-div").querySelectorAll(".comment").length).toEqual(2);
    });
    // it("should match snapshot", () => {
    //     const rtl = render(<CommentList {...props} />);
    //     expect(rtl).toMatchSnapshot();
    // });

    it("should check more comments", () => {
        const { queryByTestId } = render(<CommentList {...props} />);
        const commentList = queryByTestId("comment-show-more");
        expect(commentList).toBeNull();
    });

    it("should fire show more action", () => {
        const { getByTestId } = render(<CommentList {...props} />);
        const showMore = getByTestId("show_more_button");
        fireEvent.click(showMore);
        expect(getByTestId("comment-show-more").firstChild).toBeTruthy();
    });
});
Herodias answered 29/5, 2020 at 14:23 Comment(0)
L
10
  • Try to clean up the DOM after each test

import { cleanup } from '@testing-library/react'
// Other import and mock props
describe("Should render <CommentList/>", () => {
    afterEach(cleanup)
    // your test
}

Note: You have filterComments.map so make sure filterComments have one item.

Licking answered 29/5, 2020 at 14:44 Comment(7)
thank you for the answer, what i ended up doing was just doing expect(getAllByTestId("comment-show-more")).toBeTruthy(); which got the tests passing. This answer is still helpful, as i do need to clean up the dom after each test.Herodias
what do you think about the edit ? i got the test passing with this approach.Herodias
That's ok but I recommend that you should need to know how many comments you rendered in CommentList then get all of them by testId any check if the are correct number.Licking
ok in this case i rendered 3 comments as per the props indicates, so i should do something like getAllByTestId.ToEqual(3) something like that ?Herodias
expect(getAllByTestId(/comment-show-more/i).length).toEqual(3); this also got the test passing.Herodias
@Herodias Yeah, that's better.Licking
you should not cleanup manuallyLoopy
C
10

use

getAllByTestId

example:

await waitFor(() => userEvent.click(screen.getAllByTestId('serviceCard')[0]));
Cuthbertson answered 7/3, 2022 at 18:37 Comment(0)
S
2

It can be solved by use getAllByTestId.

it("should fire show more action", () => {
        const { getAllByTestId, getByTestId } = render(<CommentList {...props} />);
        const showMore = getAllByTestId("show_more_button")[0];
    
        fireEvent.click(showMore);
        expect(getByTestId("comment-show-more").firstChild).toBeTruthy();
    });
Semimonthly answered 4/1, 2022 at 19:22 Comment(0)
F
0

Kinda late but this may be helpful for somebody:

I can see that you are using a iterator that might return multiple children, if you want to solve differently, add a literals key for each child when defining your data-testid tag:

const showMoreComments = () => {
    return filterComments.map((comment, i) => (
        <div data-testid={`comment-show-more-test-key-${i}`} key={i}>
            <CommentListContainer ref={ref} comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
        </div>
    ));
};
Farfetched answered 7/12, 2022 at 13:27 Comment(0)
S
0

I encountered a similar issue where Vitest wouldn’t clean the DOM between each render. To resolve this, I added { test: { globals: true } } to my vitest.config.ts. Hope this helps someone!

Steelworker answered 18/8, 2024 at 19:13 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.