-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
(jest-fake-timers): Add now()
API to get the fake clock time
#13244
Conversation
CI is my bad - will fix as soon as I'm home from birthday gift shopping |
docs/JestObjectAPI.md
Outdated
@@ -799,6 +799,10 @@ This means, if any timers have been scheduled (but have not yet executed), they | |||
|
|||
Returns the number of fake timers still left to run. | |||
|
|||
### `jest.now()` | |||
|
|||
Returns the time in ms of the current fake clock. |
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.
Add a note this is equivalent to Date.now()
for non-legacy timers?
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 guess it could be useful for modern timers too if doNotFake: ['Date', 'performance']
is set?
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.
Indeed, that’s a good point. I was also wondering what does it do if called without faking time? Would it be worth mentioning that?
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.
Yeah good point - currently, it'll pass through to now()
in the default (modern) fake timers implementation, which will try to use this._clock.now
, which will throw as this._clock
is only initialised by useFakeTimers()
. Not good!
Other APIs in that file wrap calls in _checkTimers()
which prints a warning if fake timers aren't active, and then usually return some dummy value.
So I guess two options:
- Return the real
Date.now()
(and document that behaviour) - Warn on the console and return some dummy value, probably
0
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.
Perhaps 1? Since jest.now()
is equivalent to mocked Date.now()
, then for me it makes sense to return the real Date.now()
also in case if time is not faked.
Co-authored-by: Tom Mrazauskas <tom@mrazauskas.de>
No rush! 😅 |
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.
great stuff, thanks!
It's relatively new that EDIT: Ah, I see you point it out as well 😀 |
Thanks for merging, but I was looking to sort out the issue @mrazauskas pointed out above re undefined behaviour when real timers are used. I'll follow up in another PR with that - planning to fall back to real |
Ah sorry, thought it was ready |
No worries, I should've been clearer. You're very quick! 😅 |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Summary
Currently, users of legacy fake timers have no way to mock other time-related APIs in a way that keeps a consistent clock with the Jest mocked APIs.
Exposing the fake time allows easy mocking of APIs like
Date.now()
,performance.now()
, etc.Use case
For example, React Native's
TimingAnimation
uses a combination of time elapsed according toDate.now()
to interpolate state within each update, andrequestAnimationFrame()
to set a timer for the next update.The legacy timers API in Jest 27+ mocks
requestAnimationFrame
(taking ~16ms of fake clock time per frame), butDate.now()
is not mocked, which means thatadvanceTimersToTime
runs some number of animation frames, but doesn't advanceDate.now()
time by the corresponding amount.jest.runAllTimers()
"works", but only because it actually ends up running for the real time of the animation, processing as many frames as the CPU allows.What would be useful is a way to mock APIs like
Date.now()
orperformance.now()
in a manner consistent with the internal fake clock, but Jest doesn't expose the internal clock, so we're left guessing about whatDate.now()
should return after an unknown number of timers/frames have run.(
requestAnimationFrame
does actually pass the current high-res clock time to its callback, but for "reasons"(?)TimingAnimation
doesn't use it - in any case that doesn't really help a test author, and we'd still need to mock a start time.)Before
About the best we can do at the moment is to advance frame by frame:
(Alternatives include a mock implementation of
Date.now()
that "knows about" the number of frames processed, eg by assuming it's called once per frame, which is also not great!)After
Test Plan
I've added tests for modern and legacy timers, and also tested the "After" above with React Native animation.