React testing-library Be sure to await previous act() calls before making a new one
Asked Answered
C

3

10

I write a test for a seletion and I get this warning. In my test I'm waiting for the end of the act. Why I get this error?

Warning: You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one.

test('Selection should be have the correct number of options', async () => {
  const leftClick = { button: 0 };
  const { options } = makeSUT();
  const selection = screen.getByLabelText('MultiSelection');

  // open all option
  act(() => {
    userEvent.click(selection, leftClick);
  });
  // await wait();

  options.forEach(async (option, index) => {
    if (index === 0) {
      expect((await screen.findAllByText(option.label)).length).toEqual(1);
    } else {
      expect((await screen.findAllByText(option.label)).length).toEqual(1);
    }
  });
});

Thank you

Cardoso answered 30/1, 2022 at 13:9 Comment(1)
The first item wrapped in act needs an await. RTL methods are wrapped in act so your assertion is using an act call before the first one has resolved.Rewire
N
11

userEvent utility API methods should not be wrapped in act(). It is recommended not to do so here. Instead, you can just await the method call. After you have performed an action, you can use waitFor to wait for the Component state to update and run your assertions. To simplify your logic, I would replace findBy with a waitFor and getBy so you're not having to async your forEach().

You should ensure you are setting up userEvent correctly (see here)

The following should resolve your issue:

test('Selection should be have the correct number of options', async () => {
    const user = userEvent.setup(); // Make sure to setup correctly.
    const leftClick = { button: 0 };
    const { options } = makeSUT();
    const selection = screen.getByLabelText('MultiSelection');
    
    // Wait for the userEvent to click:
    await user.click(selection, leftClick);
    waitFor(() => {
        options.forEach((option, index) => {
            if (index === 0) {
                expect((screen.getAllByText(option.label)).length).toEqual(1);
            } else {
                expect((screen.getAllByText(option.label)).length).toEqual(1);
            }
        });
    });
});
Newborn answered 15/6, 2022 at 2:58 Comment(4)
Any idea why await works? Doesn't seem to be any mention of it in the docs.Westernmost
I just checked out the docs I linked, and I can see that the example they use has await user.click(element). They might not have done it in some other examples because perhaps they weren't using an async test.Newborn
@Westernmost I believe all RTL methods are wrapped in act. The waitFor is probably not necessary as you are waiting on that state update to complete before you are asserting.Rewire
@Rewire I found inconsistencies in external testing CI pipelines when I did not use the waitFor before asserting when using RTL APIs like type or keyboard.Newborn
M
0

You shouldn't wrap userEvent.click in act, you just need to waitFor the effect of the click to be visible.

I made a couple of small changes that leave us with a very readable test:

  • Left clicking is the default, so we don't need to specify that.
  • getByText returns the one element that matches the text, so you don't need to get all of the elements and check that there's only one.
  • We're not using the index argument in forEach so we can drop that too.
test('Selection should be have the correct number of options', async () => {
  const { options } = makeSUT();

  const selection = screen.getByLabelText('MultiSelection');
  userEvent.click(selection);

  await waitFor(() => {
    options.forEach((option) => screen.getByText(option.label);
  });
});
Medawar answered 9/3, 2023 at 0:16 Comment(0)
B
0

I don't get the warning when I use a for of loop. Try something like below


    let index = 0;
    for await (const option of options) {
      if (index === 0) {                
         expect((screen.getAllByText(option.label)).length).toEqual(1);
      } else {
         expect((screen.getAllByText(option.label)).length).toEqual(1);
      }
      index +=1;
    }
Beiderbecke answered 15/1 at 10:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.