I read that act / waitFor is to wait until the DOM is updated. I've also read that Render is synchronous. I assume that I don't need to wrap a Render with act/waitFor as the DOM will always be updated by the time I do a click or a test ? Is this correct ?
Next, I don't understand why I need act / waitFor ? Can't I just do a await ? For Test A, I use user.click and I saw the online sample code uses await. It seems to work well on my laptop.
However in Test B, when I see some sample codes for fireEvent.click, they use waitFor. I tried using await, it flag me "await has not effect on this type of expression". But "await waitFor()" works. Why is this ?
Thanks very much in advance !
Test A
it('user.click', async () => {
render(
<BrowserRouter>
<Notes />
</BrowserRouter>
);
const checkBox = screen.getByRole('checkbox');
await user.click(checkBox);
const prev = screen.getByRole('button', { name: 'Prev' });
expect(prev).toBeEnabled();
});
Test B
it('fireEvent.click', async () => {
render(
<BrowserRouter>
<Notes />
</BrowserRouter>
);
const checkBox = screen.getByRole('checkbox');
await fireEvent.click(checkBox);
const prev = screen.getByRole('button', { name: 'Prev' });
expect(prev).toBeEnabled();
});
I tried using await, it flag me "await has not effect on this type of expression". But "await waitFor()" works. Why is this ?
await
is syntactic sugar for attaching callbacks to a promise; the rest of your asynchronous function will continue once the promise has resolved or rejected.
So, for example, it doesn't make sense to write something like await 3
, because 3
isn't a promise; there isn't anything to wait for. (It's actually legal to write await 3
, but it doesn't really accomplish anything; you could just write 3
.)
So the reason that await user.click(...)
and await waitFor(...)
are both fine, whereas await fireEvent.click(...)
is not, is that user.click
and waitFor
both return promises, whereas fireEvent.click
does not. fireEvent.click
just dispatches the event and returns synchronously. (Actually I'm not sure that user.click
does anything inherently asynchronous, either — it doesn't seem to depend on any APIs like fetch
— but presumably the Testing Library maintainers want to keep their options open in case there comes to be a real need for user.click
to support something that has to be asynchronous. But waitFor
genuinely has to be asynchronous no matter what, because its entire purpose is to poll a callback every so often until it succeeds.)
- I read that act / waitFor is to wait until the DOM is updated. I've also read that Render is synchronous. I assume that I don't need to wrap a Render with act/waitFor as the DOM will always be updated by the time I do a click or a test ? Is this correct ?
So, there are two kinds of "asynchronous":
act
to work around it. Specifically: if you wrap those things in act(() => { ... })
, then instead of enqueuing microtasks, React will instead put those tasks in a special queue that act
owns, and act
will execute those tasks before returning. So the end result is that the tasks are still asynchronous from the standpoint of your React component (for example, setState
returns immediately without actually changing anything), but it's now synchronous from the standpoint of your test code (all those updates happen before act
returns).act
for those, but rather, you need to use await
to wait for them to complete.setState
(which is very common — updating the UI with information fetched asynchronously), then await
isn't enough, because setState
(intentionally) doesn't return a promise that you could wait for, it just enqueues some changes behind the scenes. For that case, you need to write await act(async () => { ... })
. (act
has special logic to make this work; it detects that the callback returned a promise, and it ensures that all of React's asynchronous logic is executed before its own promise resolves.)Now that you understand that, this question is simple to answer: you don't need to use act
with render
, because render
actually calls act
internally for you; and you don't need to use await
with render
, because rendering doesn't involve any truly asynchronous APIs. So you can just use render
directly.