88
99'use strict' ;
1010
11- Zone . __load_patch ( 'jest' , ( context : any , Zone : ZoneType ) => {
11+ Zone . __load_patch ( 'jest' , ( context : any , Zone : ZoneType , api : _ZonePrivate ) => {
1212 if ( typeof jest === 'undefined' || jest [ '__zone_patch__' ] ) {
1313 return ;
1414 }
1515
1616 jest [ '__zone_patch__' ] = true ;
1717
18-
19- if ( typeof Zone === 'undefined' ) {
20- throw new Error ( 'Missing Zone.js' ) ;
21- }
22-
2318 const ProxyZoneSpec = ( Zone as any ) [ 'ProxyZoneSpec' ] ;
2419 const SyncTestZoneSpec = ( Zone as any ) [ 'SyncTestZoneSpec' ] ;
2520
@@ -29,7 +24,8 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => {
2924
3025 const rootZone = Zone . current ;
3126 const syncZone = rootZone . fork ( new SyncTestZoneSpec ( 'jest.describe' ) ) ;
32- const proxyZone = rootZone . fork ( new ProxyZoneSpec ( ) ) ;
27+ const proxyZoneSpec = new ProxyZoneSpec ( ) ;
28+ const proxyZone = rootZone . fork ( proxyZoneSpec ) ;
3329
3430 function wrapDescribeFactoryInZone ( originalJestFn : Function ) {
3531 return function ( this : unknown , ...tableArgs : any [ ] ) {
@@ -65,11 +61,20 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => {
6561 * execute in a ProxyZone zone.
6662 * This will run in the `proxyZone`.
6763 */
68- function wrapTestInZone ( testBody : Function ) : Function {
64+ function wrapTestInZone ( testBody : Function , isTestFunc = false ) : Function {
6965 if ( typeof testBody !== 'function' ) {
7066 return testBody ;
7167 }
7268 const wrappedFunc = function ( ) {
69+ if ( ( Zone as any ) [ api . symbol ( 'useFakeTimersCalled' ) ] === true && testBody &&
70+ ! ( testBody as any ) . isFakeAsync ) {
71+ // jest.useFakeTimers is called, run into fakeAsyncTest automatically.
72+ const fakeAsyncModule = ( Zone as any ) [ Zone . __symbol__ ( 'fakeAsyncTest' ) ] ;
73+ if ( fakeAsyncModule && typeof fakeAsyncModule . fakeAsync === 'function' ) {
74+ testBody = fakeAsyncModule . fakeAsync ( testBody ) ;
75+ }
76+ }
77+ proxyZoneSpec . isTestFunc = isTestFunc ;
7378 return proxyZone . run ( testBody , null , arguments as any ) ;
7479 } ;
7580 // Update the length of wrappedFunc to be the same as the length of the testBody
@@ -102,7 +107,7 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => {
102107 }
103108 context [ Zone . __symbol__ ( methodName ) ] = originalJestFn ;
104109 context [ methodName ] = function ( this : unknown , ...args : any [ ] ) {
105- args [ 1 ] = wrapTestInZone ( args [ 1 ] ) ;
110+ args [ 1 ] = wrapTestInZone ( args [ 1 ] , true ) ;
106111 return originalJestFn . apply ( this , args ) ;
107112 } ;
108113 context [ methodName ] . each = wrapTestFactoryInZone ( ( originalJestFn as any ) . each ) ;
@@ -125,4 +130,165 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => {
125130 return originalJestFn . apply ( this , args ) ;
126131 } ;
127132 } ) ;
133+
134+ ( Zone as any ) . patchJestObject = function patchJestObject ( Timer : any , isModern = false ) {
135+ // check whether currently the test is inside fakeAsync()
136+ function isPatchingFakeTimer ( ) {
137+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
138+ return ! ! fakeAsyncZoneSpec ;
139+ }
140+
141+ // check whether the current function is inside `test/it` or other methods
142+ // such as `describe/beforeEach`
143+ function isInTestFunc ( ) {
144+ const proxyZoneSpec = Zone . current . get ( 'ProxyZoneSpec' ) ;
145+ return proxyZoneSpec && proxyZoneSpec . isTestFunc ;
146+ }
147+
148+ if ( Timer [ api . symbol ( 'fakeTimers' ) ] ) {
149+ return ;
150+ }
151+
152+ Timer [ api . symbol ( 'fakeTimers' ) ] = true ;
153+ // patch jest fakeTimer internal method to make sure no console.warn print out
154+ api . patchMethod ( Timer , '_checkFakeTimers' , delegate => {
155+ return function ( self : any , args : any [ ] ) {
156+ if ( isPatchingFakeTimer ( ) ) {
157+ return true ;
158+ } else {
159+ return delegate . apply ( self , args ) ;
160+ }
161+ }
162+ } ) ;
163+
164+ // patch useFakeTimers(), set useFakeTimersCalled flag, and make test auto run into fakeAsync
165+ api . patchMethod ( Timer , 'useFakeTimers' , delegate => {
166+ return function ( self : any , args : any [ ] ) {
167+ ( Zone as any ) [ api . symbol ( 'useFakeTimersCalled' ) ] = true ;
168+ if ( isModern || isInTestFunc ( ) ) {
169+ return delegate . apply ( self , args ) ;
170+ }
171+ return self ;
172+ }
173+ } ) ;
174+
175+ // patch useRealTimers(), unset useFakeTimers flag
176+ api . patchMethod ( Timer , 'useRealTimers' , delegate => {
177+ return function ( self : any , args : any [ ] ) {
178+ ( Zone as any ) [ api . symbol ( 'useFakeTimersCalled' ) ] = false ;
179+ if ( isModern || isInTestFunc ( ) ) {
180+ return delegate . apply ( self , args ) ;
181+ }
182+ return self ;
183+ }
184+ } ) ;
185+
186+ // patch setSystemTime(), call setCurrentRealTime() in the fakeAsyncTest
187+ api . patchMethod ( Timer , 'setSystemTime' , delegate => {
188+ return function ( self : any , args : any [ ] ) {
189+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
190+ if ( fakeAsyncZoneSpec && isPatchingFakeTimer ( ) ) {
191+ fakeAsyncZoneSpec . setCurrentRealTime ( args [ 0 ] ) ;
192+ } else {
193+ return delegate . apply ( self , args ) ;
194+ }
195+ }
196+ } ) ;
197+
198+ // patch getSystemTime(), call getCurrentRealTime() in the fakeAsyncTest
199+ api . patchMethod ( Timer , 'getRealSystemTime' , delegate => {
200+ return function ( self : any , args : any [ ] ) {
201+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
202+ if ( fakeAsyncZoneSpec && isPatchingFakeTimer ( ) ) {
203+ return fakeAsyncZoneSpec . getCurrentRealTime ( args [ 0 ] ) ;
204+ } else {
205+ return delegate . apply ( self , args ) ;
206+ }
207+ }
208+ } ) ;
209+
210+ // patch runAllTicks(), run all microTasks inside fakeAsync
211+ api . patchMethod ( Timer , 'runAllTicks' , delegate => {
212+ return function ( self : any , args : any [ ] ) {
213+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
214+ if ( fakeAsyncZoneSpec ) {
215+ fakeAsyncZoneSpec . flushMicrotasks ( ) ;
216+ } else {
217+ return delegate . apply ( self , args ) ;
218+ }
219+ }
220+ } ) ;
221+
222+ // patch runAllTimers(), run all macroTasks inside fakeAsync
223+ api . patchMethod ( Timer , 'runAllTimers' , delegate => {
224+ return function ( self : any , args : any [ ] ) {
225+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
226+ if ( fakeAsyncZoneSpec ) {
227+ fakeAsyncZoneSpec . flush ( 100 , true ) ;
228+ } else {
229+ return delegate . apply ( self , args ) ;
230+ }
231+ }
232+ } ) ;
233+
234+ // patch advanceTimersByTime(), call tick() in the fakeAsyncTest
235+ api . patchMethod ( Timer , 'advanceTimersByTime' , delegate => {
236+ return function ( self : any , args : any [ ] ) {
237+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
238+ if ( fakeAsyncZoneSpec ) {
239+ fakeAsyncZoneSpec . tick ( args [ 0 ] ) ;
240+ } else {
241+ return delegate . apply ( self , args ) ;
242+ }
243+ }
244+ } ) ;
245+
246+ // patch runOnlyPendingTimers(), call flushOnlyPendingTimers() in the fakeAsyncTest
247+ api . patchMethod ( Timer , 'runOnlyPendingTimers' , delegate => {
248+ return function ( self : any , args : any [ ] ) {
249+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
250+ if ( fakeAsyncZoneSpec ) {
251+ fakeAsyncZoneSpec . flushOnlyPendingTimers ( ) ;
252+ } else {
253+ return delegate . apply ( self , args ) ;
254+ }
255+ }
256+ } ) ;
257+
258+ // patch advanceTimersToNextTimer(), call tickToNext() in the fakeAsyncTest
259+ api . patchMethod ( Timer , 'advanceTimersToNextTimer' , delegate => {
260+ return function ( self : any , args : any [ ] ) {
261+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
262+ if ( fakeAsyncZoneSpec ) {
263+ fakeAsyncZoneSpec . tickToNext ( args [ 0 ] ) ;
264+ } else {
265+ return delegate . apply ( self , args ) ;
266+ }
267+ }
268+ } ) ;
269+
270+ // patch clearAllTimers(), call removeAllTimers() in the fakeAsyncTest
271+ api . patchMethod ( Timer , 'clearAllTimers' , delegate => {
272+ return function ( self : any , args : any [ ] ) {
273+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
274+ if ( fakeAsyncZoneSpec ) {
275+ fakeAsyncZoneSpec . removeAllTimers ( ) ;
276+ } else {
277+ return delegate . apply ( self , args ) ;
278+ }
279+ }
280+ } ) ;
281+
282+ // patch getTimerCount(), call getTimerCount() in the fakeAsyncTest
283+ api . patchMethod ( Timer , 'getTimerCount' , delegate => {
284+ return function ( self : any , args : any [ ] ) {
285+ const fakeAsyncZoneSpec = Zone . current . get ( 'FakeAsyncTestZoneSpec' ) ;
286+ if ( fakeAsyncZoneSpec ) {
287+ return fakeAsyncZoneSpec . getTimerCount ( ) ;
288+ } else {
289+ return delegate . apply ( self , args ) ;
290+ }
291+ }
292+ } ) ;
293+ }
128294} ) ;
0 commit comments