@@ -4,6 +4,8 @@ import * as util from "util";
44// Grab the setTimeout from global before jest overwrites it with useFakeTimers
55const setTimeoutBypassingFakes = global . setTimeout ;
66
7+ type Timeout = ReturnType < typeof setTimeout > ;
8+
79/**
810 * Wait a number of milliseconds of _real time_ (not mocked time); useful for
911 * allowing the runloop or external systems to advance.
@@ -13,8 +15,8 @@ const setTimeoutBypassingFakes = global.setTimeout;
1315export const sleep = (
1416 ms : number ,
1517 unref = false ,
16- ) : Promise < void > & { timeout : NodeJS . Timeout } => {
17- let timeout ! : NodeJS . Timeout ;
18+ ) : Promise < void > & { timeout : Timeout } => {
19+ let timeout ! : Timeout ;
1820 const promise = new Promise < void > ( ( resolve ) => {
1921 timeout = setTimeoutBypassingFakes ( resolve , ms ) ;
2022 } ) ;
@@ -74,6 +76,15 @@ async function aFewRunLoops(count = 5) {
7476 }
7577}
7678
79+ const OriginalDate = global . Date ;
80+
81+ /**
82+ * Returns the real-world current timestamp (unaffected by fake timers).
83+ */
84+ function realNow ( ) {
85+ return OriginalDate . now ( ) ;
86+ }
87+
7788/**
7889 * Sets up fake timers and Date implementation within your Jest test file. You
7990 * should call this at the top of the file (**not** within a test or an
@@ -89,106 +100,48 @@ async function aFewRunLoops(count = 5) {
89100 * offset.
90101 */
91102export function setupFakeTimers ( ) {
92- jest . useFakeTimers ( ) ;
93-
94- const OriginalDate = global . Date ;
95-
96- /** The offset, in milliseconds, to apply to results from `Date.now()` */
97- let offset = 0 ;
98-
99- function fakeNow ( ) {
100- return OriginalDate . now ( ) + offset ;
101- }
102-
103- /**
104- * A copy of `Date`, but overrides `new Date()` and `Date.now()` to return a
105- * date/timestamp factoring in `setTime()` calls.
106- */
107- const FakeDate : typeof Date = function ( ...args : any [ ] ) {
108- if ( args . length === 0 ) {
109- // `new Date()` becomes `new Date(fakeNow())`
110- return new OriginalDate ( fakeNow ( ) ) ;
111- } else if ( args . length === 1 ) {
112- // As before
113- return new OriginalDate ( args [ 0 ] ) ;
114- } else {
115- // As before
116- return new OriginalDate (
117- args [ 0 ] ,
118- args [ 1 ] ,
119- args [ 2 ] ,
120- args [ 3 ] ,
121- args [ 4 ] ,
122- args [ 5 ] ,
123- args [ 6 ] ,
124- ) ;
125- }
126- } as any ;
127-
128- // Copy static methods of Date, overriding Date.now()
129- FakeDate . now = ( ) => fakeNow ( ) ;
130- FakeDate . parse = Date . parse ;
131- FakeDate . UTC = Date . UTC ;
103+ beforeEach ( ( ) => void jest . useFakeTimers ( ) ) ;
104+ afterEach ( ( ) => void jest . useRealTimers ( ) ) ;
132105
133106 /**
134- * Sets the `offset` such that a call to `Date.now()` (or `new Date()`) would
135- * return this timestamp if called immediately (but time continues to
107+ * Sets the fake time such that a call to `Date.now()` (or `new Date()`)
108+ * would return this timestamp if called immediately (but time continues to
136109 * progress as expected after this). Also advances the timers by the
137- * difference from the previous `offset` , if positive. Setting time backwards
138- * is allowed (like setting back the system clock on a computer), but will
139- * not advance (or undo the advancement of) any timers.
110+ * difference from the previous time , if positive. Setting time backwards is
111+ * allowed (like setting back the system clock on a computer), but will not
112+ * advance (or undo the advancement of) any timers.
140113 *
141114 * Since advancing the time a few hours might not run all the intermediary
142115 * code quite right, we actually step it up by a configurable increment
143116 * (defaults to one minute) at a time. Setting time backwards (no matter how
144117 * far back) is done all at once.
145118 *
146- * @param timestamp - the target timestamp
119+ * @param targetTimestamp - the target timestamp
147120 * @param increment - the maximum we should advance time by at once in order to step towards `timestamp`
148121 */
149- async function setTime ( timestamp : number , increment = MINUTE ) {
122+ async function setTime ( targetTimestamp : number , increment = MINUTE ) {
150123 assert . strictEqual (
151- typeof timestamp ,
124+ typeof targetTimestamp ,
152125 "number" ,
153126 `Expected \`setTime\` to be passed a number of milliseconds, instead received '${ util . inspect (
154- timestamp ,
127+ targetTimestamp ,
155128 ) } '`,
156129 ) ;
157- const finalOffset = timestamp - OriginalDate . now ( ) ;
158- const advancement = finalOffset - offset ;
159- if ( advancement < 0 ) {
160- offset = finalOffset ;
130+
131+ if ( targetTimestamp < Date . now ( ) ) {
132+ jest . setSystemTime ( targetTimestamp ) ;
161133 } else {
162- let previousOffset = offset ;
163- while ( previousOffset + increment < finalOffset ) {
164- offset = previousOffset + increment ;
165- previousOffset = offset ;
134+ while ( Date . now ( ) + increment < targetTimestamp ) {
166135 jest . advanceTimersByTime ( increment ) ;
167136 await aFewRunLoops ( ) ;
168137 }
169- if ( previousOffset < finalOffset ) {
170- offset = finalOffset ;
171- jest . advanceTimersByTime ( finalOffset - previousOffset ) ;
138+ if ( Date . now ( ) < targetTimestamp ) {
139+ jest . advanceTimersByTime ( targetTimestamp - Date . now ( ) ) ;
172140 await aFewRunLoops ( ) ;
173141 }
174142 }
175143 }
176144
177- beforeEach ( ( ) => {
178- offset = 0 ;
179- global . Date = FakeDate ;
180- } ) ;
181- afterEach ( ( ) => {
182- global . Date = OriginalDate ;
183- } ) ;
184-
185- /**
186- * Returns the real-world current timestamp (unaffected by fake timers).
187- */
188- function realNow ( ) {
189- return OriginalDate . now ( ) ;
190- }
191-
192145 // In future we may add other methods such as `setTimeWithoutAdvancingTimers`
193146 // to emulate the system clock changing without real time elapsing.
194147 return { setTime, realNow } ;
0 commit comments