-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add advanceTimers
option
#907
Merged
ph-fritsche
merged 4 commits into
testing-library:main
from
CreativeTechGuy:pr/advance-timers
Apr 11, 2022
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,11 @@ | ||
export function wait(time?: number) { | ||
return new Promise<void>(resolve => setTimeout(() => resolve(), time)) | ||
import type {Options} from '../../options' | ||
|
||
export function wait( | ||
time: number, | ||
advanceTimers: Exclude<Options['advanceTimers'], undefined>, | ||
) { | ||
CreativeTechGuy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return new Promise<void>(resolve => { | ||
setTimeout(() => resolve(), time) | ||
advanceTimers(time) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import {wait} from '#src/utils/misc/wait' | ||
|
||
test('advances timers when set', async () => { | ||
jest.useFakeTimers() | ||
jest.setTimeout(50) | ||
// If this wasn't advancing fake timers, we'd timeout and fail the test | ||
await wait(10000, jest.advanceTimersByTime) | ||
jest.useRealTimers() | ||
}) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should return
Promise<void>
. The resultingawait
on this enables the user to let third-party code run after the timers were advanced and before we continue interacting. Otherwise asynchronous code during the timeout might not be executed until after the next interaction which might result in hard to debug issues.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The place where this
advanceTimers
function would be used is already inside of a promise and resolving the promise is already from asetTimeout
. Can you give an example of some code which would needadvanceTimers
to return a promise?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you ever seen code like that in a real project? That's gnarly!
In the
wait
function, the promise thatadvanceTimers
returns is not awaited though (nor would it make sense to be). Which is confusing to a user who passes a promise since if they pass:It would appear as if the timers would wait additional time for that work to be completed before continuing. But because the
advanceTimersByTime
call happens first, then it wouldn't wait and the rest of the test would continue while the promise was still pending.I think it'd be best to avoid a footgun like this. In the event that someone does want to write a crazy function here, and wants to use
await
, they can always do:Which has the benefit of being clear that the caller (this library) doesn't care about your promise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code above just demonstrates the problem in a reproducible manner.
In real-world scenarios this usually isn't that obvious, the pushes to the event loop can be implemented in nested/imported modules and/or can involve event-driven APIs.
^ This would not allow the asynchronous code after any
await
to run before we continue with the next interaction that is supposed to happen "later".The problem is that fake timers create a situation that normally doesn't exist: Microtasks being queued and not executed before picking the next "macrotask". This is because the next "macrotask" is actually queued synchronously while the microtask still is queued through the event loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I don't understand why anyone would ever do this. But sure, I can make it support promises too if someone wants to do this.