Description
I use the following technique to temporarily override the global setTimeout
to always use a delay of zero. This makes all my transitions instant and my test suite runs nice and fast.
I have the following helper function:
function useZeroTimers(cb) {
return new Promise((resolve, reject) => {
let setTimeout = globalThis.setTimeout;
globalThis.setTimeout = cb => setTimeout(cb, 0);
Promise.resolve(cb())
.then(() => {
globalThis.setTimeout = setTimeout;
resolve();
})
.catch(error => {
globalThis.setTimeout = setTimeout;
reject(error);
});
});
}
And I use it in my tests like this:
it('should do something', async function() {
await useZeroTimers(async () => {
// Any calls to setTimeout within this scope will use the
// temporarily-overridden setTimeout with a delay of zero.
doSomething();
await waitFor(() => {
expect(something).to.have.been.called;
});
});
// outside the scope, setTimeout is magically restored
});
It's a nice technique that I think is simple and elegant and has worked well for me for a long time.
After upgrading react-testing-library to v12.0.0, this technique no longer works. All my tests that use my useZeroTimers
helper are broken.
I noticed the following in the release notes for v8.0.0:
The timeout in waitFor(callback, { interval, timeout } ) now uses the same clock as interval. Previously timeout was always using the real clock while interval was using the global clock which could've been mocked out. For the old behavior I'd recommend waitFor(callback, { interval, timeout: Number.POSITIVE_INFINITY }) and rely on your test runner to timeout considering real timers.
I'm not entirely sure what's going on, but I think that as my useZeroTimers
technique modifies the global setTimeout
, it is not compatible with v8.0.0. Is there any workaround that can be suggested for this? If not, could I suggest an option to tell waitFor
to behave as it did before v8.0.0?