-
-
Notifications
You must be signed in to change notification settings - Fork 642
Commit
At present, it's not possible (or at least not easy) to test any code which relies on `Date.now()` for timing purposes. Timer functions such as `setInterval()` are mockable with Jest's existing mock-timer functionality, but its mock timers do not include a mock time. [0] Fortunately, there is work underway to have Jest v25^H^H26 use the Lolex time-mocking library for its fake timer implementation, which will do exactly that! Unfortunately, we need that capability now, rather than in the indefinite future, so we'll jump the gun a bit. To ease conversion of tests once Lolex is available natively, we use a small shim taken from the code on Jest's current master branch which implements Jest's timer-control functions (not projected to change) in terms of Lolex. Tests for this shim -- which may also serve, in part, as examples for Zulip contributors who would like to use Lolex -- are included. (Additionally, inform Jest that files present in src/__tests__/aux are not supposed to contain executable tests.) [0] S2E1, 1967-09-15. GitHub-PR: #3886
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// flow-typed signature: 314c6282bca14b5a4e072a34d665ef62 | ||
// flow-typed version: <<STUB>>/lolex_v5.1.1/flow_v0.92.0 | ||
|
||
/** | ||
* This is an autogenerated libdef stub for: | ||
* | ||
* 'lolex' | ||
* | ||
* Fill this stub out by replacing all the `any` types. | ||
* | ||
* Once filled out, we encourage you to share your work with the | ||
* community by sending a pull request to: | ||
* https://github.com/flowtype/flow-typed | ||
*/ | ||
|
||
declare module 'lolex' { | ||
declare module.exports: any; | ||
} | ||
|
||
/** | ||
* We include stubs for each file inside this npm package in case you need to | ||
* require those files directly. Feel free to delete any files that aren't | ||
* needed. | ||
*/ | ||
declare module 'lolex/lolex' { | ||
declare module.exports: any; | ||
} | ||
|
||
declare module 'lolex/src/lolex-src' { | ||
declare module.exports: any; | ||
} | ||
|
||
// Filename aliases | ||
declare module 'lolex/lolex.js' { | ||
declare module.exports: $Exports<'lolex/lolex'>; | ||
} | ||
declare module 'lolex/src/lolex-src.js' { | ||
declare module.exports: $Exports<'lolex/src/lolex-src'>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// @flow strict-local | ||
|
||
import LolexModule from 'lolex'; | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
chrisbobbe
Contributor
|
||
|
||
/* | ||
* At present (Jest v24.9.0), Jest does not override Date.now() when using a | ||
* fake timer implementation. This means that any timer-based code relying on | ||
* Date.now() for throttling (etc.) will be very confused. | ||
* | ||
* The good news is that Jest is very close to using Lolex internally -- see, | ||
* e.g., https://github.com/facebook/jest/pull/7776 -- at which point that | ||
* behavior will be available to us via Jest. The bad news, alas, is that it's | ||
* not there yet. | ||
* | ||
* For now, we borrow slices of Jest's planned Lolex-based timer implementation. | ||
*/ | ||
|
||
/** | ||
* A Lolex-backed implementation of certain relevant Jest functions. | ||
* | ||
* Carved from the more-complete, not-yet-NPM-available implementation at: | ||
* https://github.com/facebook/jest/blob/9279a3a97/packages/jest-fake-timers/src/FakeTimersLolex.ts | ||
* | ||
* Instantiating one of these will switch Jest over to using Lolex's | ||
* `Date.now()` replacement. Calling `.dispose()` on that instantiation will | ||
* remove that. (Behavior in the presence of multiple Lolex instances is not | ||
* defined. Don't do that.) | ||
* | ||
* Users of this class are recommended to use Jest's setup and teardown | ||
* functions, perhaps as follows: | ||
* | ||
* ``` | ||
* describe('description', () => { | ||
* const lolex: Lolex = new Lolex(); | ||
* | ||
* afterEach(() => { lolex.clearAllTimers(); }); | ||
* afterAll(() => { lolex.dispose(); }); | ||
* | ||
* // ...tests... | ||
* }); | ||
* ``` | ||
*/ | ||
export class Lolex { | ||
/** The installed Lolex clock object. (Name also taken from Jest's | ||
implementation, for simplicity's sake. */ | ||
_clock; | ||
|
||
constructor() { | ||
this._clock = LolexModule.install(); | ||
} | ||
|
||
clearAllTimers(): void { | ||
this._clock.reset(); | ||
} | ||
|
||
getTimerCount(): number { | ||
return this._clock.countTimers(); | ||
} | ||
|
||
runOnlyPendingTimers(): void { | ||
this._clock.runToLast(); | ||
} | ||
|
||
advanceTimersByTime(msToRun: number): void { | ||
this._clock.tick(msToRun); | ||
} | ||
|
||
setSystemTime(now?: number): void { | ||
this._clock.setSystemTime(now); | ||
} | ||
|
||
dispose(): void { | ||
this._clock.uninstall(); | ||
} | ||
|
||
/** | ||
* Convenience function; not part of jest-lolex interface. | ||
* | ||
* Per Lolex's implementation, adjusts both the clock and the relative | ||
* timestamps of all timers. This can be used to simulate an environment in | ||
* which timers are entirely stopped while their hosting process is inactive. | ||
*/ | ||
unsafeAdvanceOnlyTime(ms: number) { | ||
this.setSystemTime(Date.now() + ms); | ||
} | ||
} |
I'm seeing a Flow error in this commit; it looks like it wasn't caught by CI because CI tests only run on the tip of a branch, and this was one of a selection of non-tip commits merged from #3886.