Closed mc0 closed 2 years ago
Thanks for raising this.
I found myself chasing what goes wrong with my tests and it was really hard!
Ended up using sinon's fake-timers with config.
So now I'm actually scared of using jest.useFakeTimers()
since it mocks everything which makes it really hard to find what's wrong.
Personally I prefer adding config to the waitFor
to allow users to opt-in on this event loop manipulation.
Duplicate of https://github.com/testing-library/dom-testing-library/issues/988
If msw
is still ignoring the clock the test wants to run in, then there's little we can do.
@testing-library/dom
version: 8.10.1Relevant code or config:
See reproduction
What you did:
With modern fake timers in Jest: Attempting to use latest Jest, MSW, and react-testing-library to run a test that fetches data from an API and validates a component.
What happened:
Responses are delayed until after the test runs as MSW uses NodeJS timers[0] and dom-testing-library blocks the event loop while using waitFor.
0: https://github.com/mswjs/msw/blob/6decc6015e59c1b1aa3706c9bda467e2e3750c79/rollup.config.ts#L134
Reproduction:
https://codesandbox.io/s/react-testing-library-msw-demo-c7nd2
May need to be forked to run
Problem description:
Advancement of time via dom-testing-library is super handy, but has compatibility problems with other libraries.
Suggested solution:
We need some way of engaging with the event loop. This is tricky since there is no supported way of getting the original timer functions from Jest.
Option 1: Config
To properly support waitFor when using fake timers, the code must provide the original setTimeout/clearTimeout via configuration.
If configured: Use
setTimeout(..., 0)
, use advanceTimerByTime to progress the fake timers, and do our checks until the timeout occurs using the fake timer.Option 2: postMessage for browser,
timers
for NodeJSThe fake timers don't overwrite postMessage, and this is an entrypoint to the JS event loop. In the browser, we can add a listener for a
message
and then use this to advance the timer and do our checks. (Edit: It's possible handling this in a browser isn't necessary as there are fewer ways around the fake timers, but I left this in for completeness.)For NodeJS: Use
timers.setTimeout(..., 0)
, use advanceTimerByTime to progress the fake timers, and do our checks until the timeout occurs using the fake timer.Option 3: Disable and enable for original timer functions
The first time waitFor is ran while fake timers are enabled, the fake timers can be disabled, the functions can be stored, and then re-enabled.
i.e.
jest.runOnlyPendingTimers()
jest.useRealTimers()
jest.useFakeTimers(modern|legacy)