msw icon indicating copy to clipboard operation
msw copied to clipboard

Fake timers with long delay fail

Open leepowelldev opened this issue 3 years ago • 1 comments

Prerequisites

Environment check

  • [X] I'm using the latest msw version
  • [X] I'm using Node.js version 14 or higher

Node.js version

19.1.0

Reproduction repository

https://github.com/leepowelldev/msw-delay-bug

Reproduction steps

npm test

Current behavior

Currently, when using fake timers in jest, adding a long delay will result in the response not being resolved. Very short delays, seem to work, but I think this is more by luck.

The repo includes three tests, no delay, short delay and a long delay... the long delay fails. I believe this is down to this line https://github.com/mswjs/msw/blob/7380011de47460212065ca41be1b5a1c60ed1b98/src/node/SetupServerApi.ts#L91 using a real timer for the setTimeout. This causes jest's fake timers and the real timer to get out of sync.

Changing that line to use the faked global timeout (just for the delay!) allows us to advance any timers and have the promise resolve as expected.

if (response.delay) {
  await new Promise((resolve) => {
    globalThis.setTimeout(resolve, response.delay)
  })
}

Obviously I don't have deep knoledge of this library (thats for BTW!) so am a bit uncertain of the concequenses of making this change. It may mean users of fake timers need to advance the timers when using a delay - but I would sort of expect this behaviour as it's consistent with other timers.

Happy to create a PR if needed.

Expected behavior

All tests to pass regardless of the delay duration.

leepowelldev avatar Dec 23 '22 10:12 leepowelldev

Update - I did figure out a workaround is to use real timers to wait for the delay to complete... not ideal, but seems to work around the issue.

function wait(ms: number) {
  return new Promise((resolve) => {
    jest.requireActual('timers').setTimeout(resolve, ms);
  });
}

Then in my test, wait for the delay duration to complete with real timers.

await act(() => wait(1000));

leepowellnbs avatar Dec 30 '22 20:12 leepowellnbs