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();
});
awaitis 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, because3isn't a promise; there isn't anything to wait for. (It's actually legal to writeawait 3, but it doesn't really accomplish anything; you could just write3.)So the reason that
await user.click(...)andawait waitFor(...)are both fine, whereasawait fireEvent.click(...)is not, is thatuser.clickandwaitForboth return promises, whereasfireEvent.clickdoes not.fireEvent.clickjust dispatches the event and returns synchronously. (Actually I'm not sure thatuser.clickdoes anything inherently asynchronous, either — it doesn't seem to depend on any APIs likefetch— but presumably the Testing Library maintainers want to keep their options open in case there comes to be a real need foruser.clickto support something that has to be asynchronous. ButwaitForgenuinely has to be asynchronous no matter what, because its entire purpose is to poll a callback every so often until it succeeds.)So, there are two kinds of "asynchronous":
actto work around it. Specifically: if you wrap those things inact(() => { ... }), then instead of enqueuing microtasks, React will instead put those tasks in a special queue thatactowns, andactwill 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,setStatereturns immediately without actually changing anything), but it's now synchronous from the standpoint of your test code (all those updates happen beforeactreturns).actfor those, but rather, you need to useawaitto wait for them to complete.setState(which is very common — updating the UI with information fetched asynchronously), thenawaitisn't enough, becausesetState(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 writeawait act(async () => { ... }). (acthas 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
actwithrender, becauserenderactually callsactinternally for you; and you don't need to useawaitwithrender, because rendering doesn't involve any truly asynchronous APIs. So you can just userenderdirectly.