'React testing library, how to open a Collapse in Ant Design

I want to write a unit test that opens an antd. Collapse <Panel />

But no matter what combination of fireEvent or userEvent mouse actions I try, I cannot get React testing library to properly "click" on this antD component the same way a real user does.

Example code:

import { Collapse } from 'antd'
const { Panel } = Collapse

const Example = () => (
<Collapse>
  <Panel header="test" data-testid="testid">hello_world</Panel>
</Collapse>
)

and in my unit test:

I have tried the following combinations to open the Panel with the header "test"

const panel = screen.getByText('test')
userEvent.click(panel)

const result = screen.getByText('hello_world') // FAILED and never finds hello_world
const panel = screen.getByText('test')
userEvent.click(panel)

const result = await screen.findByText('hello_world') // FAILED and never finds hello_world
const panel = screen.getByText('test')
fireEvent.click(panel)

const result = await screen.findByText('hello_world') // FAILED and never finds hello_world
const panel = screen.getByText('test')
fireEvent.mouseDown(panel)

const result = await screen.findByText('hello_world') // FAILED and never finds hello_world
const panel = screen.getByText('test')
await act(async () => {
  fireEvent.mouseDown(panel)
})

const result = await screen.findByText('hello_world') // FAILED and never finds hello_world
const panel = screen.getByText('test')
await act(async () => {
  fireEvent.mouseDown(panel)
})

await waait(500) // using waait library, to force time to pass to see if it was that...

const result = await screen.findByText('hello_world') // FAILED and never finds hello_world
const panel = screen.getByTestId('testid')
fireEvent.click(panel) // FAILED, never finds anything with data-testid="testid"
// I guess antd did not bother passing the test-id to the component.

const result = await screen.findByText('hello_world')

This is what the HTML looks like:

                    <div
                      class="ant-collapse ant-collapse-icon-position-left css-14dabdk"
                    >
                      <div
                        class="ant-collapse-item"
                      >
                        <div
                          aria-expanded="false"
                          class="ant-collapse-header"
                          role="button"
                          tabindex="0"
                        >
                          test
                        </div>
                      </div>

I did also try to use querySelector on container to target .ant-collapse-header, ant-collapse-item or .ant-collapse with userEvent.click and fireEvent.mouseDown. But those didn't work either.

Why is this thing so difficult to open in a test 🤯.

Does anyone know how to open and antD Collapse component?



Solution 1:[1]

Using user-event, you can try:

await userEvent.click(screen.getByText(/test/i))

Or click the button, like:

await userEvent.click(screen.getByRole("button"))

Then you can use findByText like:

expect(await screen.findByText(/hello_world/i)).toBeInTheDocument();

(Working example)

Solution 2:[2]

I've created a simple example to simulate your issue.

What i did to get it working:

  1. instead of screen, i use getByText, queryByText and findByText direct from the return of render, like const { getByText, queryByText } = render(<Example />);

* but you can use screen, not problem at all.

  1. Your function test should be async, like:
it("test", async () => { ... })

or

test("test", async () => { ... })
  1. To test the success of render 'Hello world', you can use getByText with waitFor or findByText, like below:

Code:

describe("Test PANEL", () => {
  it("should PASS WITH QUERYBY - hello_world RENDERED after CLICKED", async () => {
    const { getByText, queryByText } = render(<Example />);

    const panel = getByText("test");

    fireEvent.click(panel);

    await waitFor(() => {
      const result = queryByText("hello_world");

      expect(result).toBeInTheDocument();
    });
  });

  it("should PASS WITH FINDBY - hello_world RENDERED after CLICKED", async () => {
    const { getByText, findByText } = render(<Example />);

    const panel = getByText("test");

    fireEvent.click(panel);

    const result = await findByText("hello_world");

    expect(result).toBeInTheDocument();
  });
});

If you want to check if 'hello_world' is not rendered yet, i recommend you use queryBy, like this:

describe("Test PANEL", () => {
  it("should PASS - hello_world NOT redenred before CLICKED", async () => {
    const { getByText, queryByText } = render(<Example />);

    const panel = getByText("test");

    expect(panel).toBeInTheDocument();

    await waitFor(() => {
      const result = queryByText("hello_world");

      expect(result).not.toBeInTheDocument();
    });
  });

That´s because queryBy returns null if it doesnt find any Element, different than findBy and getBy -> both throws an error, as you can see in this link.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2