-
-
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
feat: add new 'modern' implementation of Fake Timers #7776
Conversation
f4bbdfc
to
e30e4ac
Compare
e2e/timer-reset-mocks/after-reset-all-mocks/timerAndMock.test.js
Outdated
Show resolved
Hide resolved
09cb24b
to
b52b1ea
Compare
Codecov Report
@@ Coverage Diff @@
## master #7776 +/- ##
==========================================
- Coverage 64.06% 63.96% -0.11%
==========================================
Files 293 293
Lines 12457 12478 +21
Branches 3067 3077 +10
==========================================
Hits 7981 7981
- Misses 3833 3853 +20
- Partials 643 644 +1
Continue to review full report at Codecov.
|
Sounds good! Looking forward to @rubennorte review |
8742aaa
to
0ae51ea
Compare
Just wanted to add that unlike Jest, Lolex implements proper mocking for the Node timers, returning an object with |
Jest also returns |
f793987
to
b34d066
Compare
Rebased this after the great TS migration. I'm in favour of landing this in 24 and flipping the default in 25. While it sucks to ship multiple implementations of the same functionality (fake timers), I think using Lolex is such an improvement that it's worth it. Then people will have a whole major to migrate to the new implementation, and they can do it file by file if they want. Not sure if deprecation warnings are worth it (especially as this is per file, so there'd be a lot of them) @jeysal @mattphillips @thymikee @rubennorte @scotthovestadt thoughts? |
I haven't followed this in detail - is it likely that switching the default will break a lot of users or did it only happen at FB because it's used in a weird/specific/rare way? |
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.
Concerning migration, I like the idea of flipping the default in 25. I also think it'd make sense to warn anyone explicitly opting in to Jest's fake timers (which they're presumably doing because something breaks with Lolex's; maybe they should be strongly encouraged to file bugs about that?). So then the plan goes something like this:
- In 24.
timers
can be any offake
,real
,jest
orlolex
;fake
is equivalent tojest
. No warnings are issued. - In 25,
fake
is equivalent tololex
. Ifjest
is selected, there's a deprecation warning. - In 25 + n,
jest
is removed.
this._maxLoops = maxLoops || 100000; | ||
|
||
this._fakingTime = false; | ||
this._lolex = lolexWithGlobal(global); |
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.
Can there be some way to tell Lolex what to fake? By default it will only fake methods it finds on the global passed in, but in the JSDOM environment queueMicrotask
isn't there, which I'd like to have.
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.
Wouldn't it work for you to just polyfill queueMicrotask
in a setup file, then Lolex would pick it up and fake it?
We discussed this (passing it what to mock) briefly in the fall. IIRC the argument against allow you to pass in args was that you shouldn't be able to opt out of certain methods - that would allow you to write tests in a way that would not reflect at all how it would run in the real world (beyond the fact you're faking time at all).
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 a dummy queueMicrotask
in an environment would suffice, yeah. It'd feel a bit weird because you don't actually need to even attempt to polyfill it - just having that property on the object is enough! But if I let little things like that bother me, coding in JavaScript would drive me insane...
Being hard to use wrong is a good goal. And there's already an escape hatch in manually setting up Lolex in whatever crazy way you want it, so this isn't critical or anything.
Summary of how this is breaking in #5171. I'd wager to say that if your test fails after moving to Lolex, it's because it depends on bugs/broken behavior in Jest (barring the case where we now mock more than before, but that's ok, I think) |
Thanks for chiming in @quasicomputational! I really like your suggestion and I think we should go with that. I'd call the current implementation Related, I'd also like to not call it |
Sounds good. Avoiding exposing the implementation detail in the name is an excellent principle. If I can bikeshed a bit, |
I'm highly interested in this. Does this feature has any ETA? Can I help? |
I'll cry a thousand tears if I'm unable to land one of the 3 Lolex PRs for Jest 25, which is next up (no time estimate, though) |
Just to share with community (thought it was already mentioned in some of comments, but cant remember and without example), anyone can already use lolex as is. I use this setup for fully synchronous testing:
// Tune promises (we use Bluebird with direct scheduler so it can be mocked by lolex...)
global.Promise = require('bluebird')
global.Promise.setScheduler(function(fn) {
setImmediate(fn) // or process.nextTick(fn)
})
import lolex from 'lolex'
const clock = lolex.install({ toFake: Object.keys(lolex.timers) as any })
export { clock }
import 'jest'
import { clock } from 'testing/setupFakeTimers'
// code to test
function example() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true)
}, 1000)
})
}
beforeEach(() => {
clock.setSystemTime(Date.parse('2018-04-03T14:44:00Z')) // Can set mocked time for ex
})
afterEach(() => {
clock.reset()
})
it('example resolves to true after one second', () => {
const cb = jest.fn()
example().then(cb)
clock.tick(500)
expect(cb).not.toHaveBeenCalled()
clock.tick(501)
expect(cb).toHaveBeenCalledWith(true)
}) Notes.
|
Current plan is to land this as default in Jest 25, but allow people to do |
We've entirely switched over to Jest's "modern" fake timers, which landed in jestjs/jest#7776.
We've entirely switched over to Jest's "modern" fake timers, which landed in jestjs/jest#7776.
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
This is an alternative to #5171 and #7300. Difference from them is that in this PR Lolex is added as an opt-in alternative to the current implementation of fake timers, instead of replacing it.
I've added
jest.useFakeTimers('modern');
which will allow people to migrate test file by test file if they want the new system. People can also useuseFakeTimers('legacy')
if they want to keep using the old implementation.The default if nothing is passed to
jest.useFakeTimers
(or justfake
is passed in config) will be the legacy implementation in Jest 26, and we will swap the default in Jest 27. We have no plans to ever remove the legacy implementation.Hopefully this means FB can migrate over over timer and we can remove Jest's implementation.
Fixes #3465
Fixes #5165
Closes #5171
Closes #7300
Test plan
Copied over the refactored unit test from #5171, and added an integration test.