Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Testing Library - using 'await wait()' after fireEvent

I'm trying to use Testing Library to check for DOM Elements after a fireEvent.click. I know I need to wait after the fireEvent, but am not sure why simply using await doesn't work? Below is the same test written two ways -- the first one fails, the second passes. I don't understand why the first one fails...am very grateful for any insights!

p.s. -- I know wait is deprecated and waitFor is preferred, however due to some constraints I can not update the version at this time :(

FAILING TEST

// This test fails with the following error and warning:
// Error: Unable to find an element by: [data-test="name_wrapper"]
// Warning: An update to OnlinePaymentModule inside a test was not wrapped in act(...).

  it('this is a failing test...why', async () => {
      const { getByText, getByTestId } = render(<Modal {...props} />);
      const button = getByText('open modal');

      fireEvent.click(button);

        const nameWrapper =  await getByTestId('name_wrapper');
        expect(
          nameWrapper.getElementsByTagName('output')[0].textContent
        ).toBe('Jon Doe');

        const numberWrapper = await getByTestId('number_wrapper');
        expect(
          numberWrapper.getElementsByTagName('output')[0].textContent
        ).toBe('123456');
        
    });
PASSING TEST -- Why does this pass but first one fails?

// This test passes with no warnings

  it('this is a passing test...why', async () => {
      const { getByText, getByTestId } = render(<Modal {...props} />);
      const button = getByText('open modal');

      fireEvent.click(button);
      
      await wait(() => {
        const nameWrapper = getByTestId('name_wrapper');
        expect(
          nameWrapper.getElementsByTagName('output')[0].textContent
        ).toBe('Jon Doe');

        const numberWrapper = getByTestId('number_wrapper');
        expect(
          numberWrapper.getElementsByTagName('output')[0].textContent
        ).toBe('123456');
      })  
    });
like image 796
LydiaHendriks Avatar asked Jul 23 '20 20:07

LydiaHendriks


People also ask

What is wait in react testing library?

When waitFor is invoked, if the condition is not met it will retry the callback again at a 50ms interval until the text appears or the call times out. The default timeout value for waitFor is one second. If this timeout value is hit, RTL will finally throw an error and the test will fail.

How do you wait in RTL?

wait (now waitFor ) is a utility from RTL that will make execution of the test wait until the callback does not throw an error. You do not need await with findByTestId, do you? @wuarmin you do need await . Otherwise, the code on the next line will execute, before the promise has resolved.

How do you use waitFor in react test library?

In order to use this function, we need to import it from @testing-library/react . import { waitFor } from '@testing-library/react'; As with the other async functions, the waitFor() function returns a Promise, so we have to preface its call with the await keyword.


1 Answers

5 months later I'm coming back to answer my question (I've learned a lot since posting this question lol)....

First of all, since it is 5 months later I want to underscore that it is better to use the userEvent library instead of fireEvent if possible.

I also would be remiss to not call out that there are a lot of antipatterns in the code ...You should only ever make one assertion in waitFor. You should avoid using getByTestId in favor of more accessible alternatives.

And finally the reason the first test was failing is that you can not use wait with getBy*. getBy is not async and will not wait. This would have been the better solution:

fireEvent.click(button);
const nameWrapper =  await findByTestId('name_wrapper');

Then the test would have waited on the nameWrapper element to be available.

The second test passed because getBy is wrapped in RTL's async utility, wait (wait is now deprecated in favor of waitFor). That is essentially what findBy does under the hood -- findBy is the async version of getBy.

When I posted the question I didn't fully understand that await is a Javascript key word (and just syntactical sugar to make code wait on a promise to resolve). wait (now waitFor) is a utility from RTL that will make execution of the test wait until the callback does not throw an error.

like image 82
LydiaHendriks Avatar answered Oct 12 '22 05:10

LydiaHendriks