Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 26a2b61

Browse files
committed
feat(test): add global interface of zone-testing about async/fakeAsync
1 parent d95f5b7 commit 26a2b61

13 files changed

+287
-69
lines changed

lib/jasmine/jasmine-patch.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ Zone.__load_patch('jasmine', (global: any) => {
4646

4747
const symbol = Zone.__symbol__;
4848

49-
// whether patch jasmine clock when in fakeAsync
50-
const enableClockPatch = global[symbol('fakeAsyncPatchLock')] === true;
51-
5249
// Monkey patch all of the jasmine DSL so that each function runs in appropriate zone.
5350
const jasmineEnv: any = jasmine.getEnv();
5451
['describe', 'xdescribe', 'fdescribe'].forEach(methodName => {
@@ -79,7 +76,7 @@ Zone.__load_patch('jasmine', (global: any) => {
7976
};
8077
});
8178

82-
patchJasmineClock(jasmine, enableClockPatch);
79+
patchJasmineClock(jasmine, global);
8380
/**
8481
* Gets a function wrapping the body of a Jasmine `describe` block to execute in a
8582
* synchronous-only zone.
@@ -92,10 +89,10 @@ Zone.__load_patch('jasmine', (global: any) => {
9289
}
9390

9491
function runInTestZone(testBody: Function, applyThis: any, queueRunner: any, done?: Function) {
95-
const isClockInstalled = !!(jasmine as any)[symbol('clockInstalled')];
92+
const isClockInstalled = (jasmine as any)[symbol('clockInstalled')] === true;
9693
const testProxyZoneSpec = queueRunner.testProxyZoneSpec;
9794
const testProxyZone = queueRunner.testProxyZone;
98-
if (isClockInstalled && enableClockPatch) {
95+
if (isClockInstalled) {
9996
// auto run a fakeAsync
10097
const fakeAsyncModule = (Zone as any)[Zone.__symbol__('fakeAsyncTest')];
10198
if (fakeAsyncModule && typeof fakeAsyncModule.fakeAsync === 'function') {

lib/jasmine/jasmine.clock.ts

+13-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
// need to patch jasmine.clock().mockDate and jasmine.clock().tick() so
99
// they can work properly in FakeAsyncTest
10-
export function patchJasmineClock(jasmine: any, enableClockPatch: boolean) {
10+
export function patchJasmineClock(jasmine: any, global: any) {
1111
const symbol = Zone.__symbol__;
1212
const originalClockFn: Function = ((jasmine as any)[symbol('clock')] = jasmine['clock']);
1313
(jasmine as any)['clock'] = function() {
@@ -35,19 +35,18 @@ export function patchJasmineClock(jasmine: any, enableClockPatch: boolean) {
3535
return originalMockDate.apply(this, arguments);
3636
};
3737
// for auto go into fakeAsync feature, we need the flag to enable it
38-
if (enableClockPatch) {
39-
['install', 'uninstall'].forEach(methodName => {
40-
const originalClockFn: Function = (clock[symbol(methodName)] = clock[methodName]);
41-
clock[methodName] = function() {
42-
const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
43-
if (FakeAsyncTestZoneSpec) {
44-
(jasmine as any)[symbol('clockInstalled')] = 'install' === methodName;
45-
return;
46-
}
47-
return originalClockFn.apply(this, arguments);
48-
};
49-
});
50-
}
38+
['install', 'uninstall'].forEach(methodName => {
39+
const originalClockFn: Function = (clock[symbol(methodName)] = clock[methodName]);
40+
clock[methodName] = function () {
41+
const enableClockPatch = global[symbol('fakeAsyncPatchLock')] === true;
42+
const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
43+
if (enableClockPatch && FakeAsyncTestZoneSpec) {
44+
(jasmine as any)[symbol('clockInstalled')] = 'install' === methodName;
45+
return;
46+
}
47+
return originalClockFn.apply(this, arguments);
48+
};
49+
});
5150
}
5251
return clock;
5352
};

lib/mocha/jasmine-bridge/jasmine.clock.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {patchJasmineClock} from '../../jasmine/jasmine.clock';
9-
export function addJasmineClock(jasmine: any) {
9+
export function addJasmineClock(jasmine: any, global: any) {
1010
jasmine.clock = function() {
1111
return {
1212
tick: function() {},
@@ -15,5 +15,5 @@ export function addJasmineClock(jasmine: any) {
1515
mockDate: function() {}
1616
};
1717
};
18-
patchJasmineClock(jasmine, true);
18+
patchJasmineClock(jasmine, global);
1919
}

lib/mocha/jasmine-bridge/jasmine.expect.ts

-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ function buildCustomMatchers(jasmine: any, actual: any) {
5252
args.unshift(actual);
5353
const result = customMatcher.compare.apply(null, args);
5454
if (!result.pass) {
55-
console.log('compare ', args);
5655
const message = result.messge || util.buildFailureMessage(key, false, actual, expects);
5756
throw new Error(message);
5857
}

lib/mocha/jasmine-bridge/jasmine.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Zone.__load_patch('jasmine2mocha', (global: any) => {
3636
addJasmineSpy(jasmine, global.Mocha, global);
3737

3838
// Add jasmine clock functionality
39-
addJasmineClock(jasmine);
39+
addJasmineClock(jasmine, global);
4040

4141
Object.defineProperty(jasmine, 'DEFAULT_TIMEOUT_INTERVAL', {
4242
configurable: true,

lib/mocha/jest-bridge/jest.clock.ts

+17-36
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,38 @@ export function addJestTimer(jest: any, global: any) {
1616
}
1717

1818
jest.clearAllTimers = function() {
19-
const zs = getFakeAsyncTestZoneSpec();
20-
if (!zs) {
21-
return;
22-
}
23-
// TODO: @JiaLiPassion, add clear method in fakeAsyncZoneSpec
24-
// flush();
19+
clearAllMacrotasks();
2520
};
2621

2722
jest.runAllTimers = function() {
2823
const zs = getFakeAsyncTestZoneSpec();
2924
if (!zs) {
3025
return;
3126
}
32-
// TODO: @JiaLiPassion, now flush can only flush
33-
// non periodic timers, should flush periodic too.
34-
flush();
35-
};
36-
37-
jest.runAllImmediates = function() {
38-
const zs = getFakeAsyncTestZoneSpec();
39-
if (!zs) {
40-
return;
27+
if (zs.pendingPeriodicTimers.length > 0) {
28+
throw new Error('Can not runAllTimers when having interval timers.');
4129
}
42-
// TODO: @JiaLiPassion, should we support this one?
43-
flush();
30+
// flush non-perodic-tasks with 10000 maxTurns
31+
flush(10000);
4432
};
4533

4634
jest.runOnlyPendingTimers = function() {
4735
const zs = getFakeAsyncTestZoneSpec();
4836
if (!zs) {
4937
return;
5038
}
51-
// TODO: @JiaLiPassion, should we support this one?
52-
flush();
39+
// flush both periodic tasks and non-perodic-tasks
40+
flush(10000, true);
5341
};
5442

55-
jest.advanceTimersByTime = function(msToRun: number) {
43+
jest.advanceTimersByTime = function(msToRun: number, doTick?: (elapsed: number) => void) {
5644
const zs = getFakeAsyncTestZoneSpec();
5745
if (!zs) {
5846
return;
5947
}
60-
tick(msToRun);
48+
tick(msToRun, doTick);
6149
};
6250

63-
6451
jest.runAllTicks = function() {
6552
const zs = getFakeAsyncTestZoneSpec();
6653
if (!zs) {
@@ -74,22 +61,16 @@ export function addJestTimer(jest: any, global: any) {
7461
if (zs) {
7562
return;
7663
}
77-
const fakeAsyncTestZoneSpec = new FakeAsyncTestZoneSpec();
78-
const proxyZoneSpec = ProxyZoneSpec.get();
79-
jest.__zone_symbol__last_delegate_spec = proxyZoneSpec.getDelegate();
80-
proxyZoneSpec.setDelegate(fakeAsyncTestZoneSpec);
81-
fakeAsyncTestZoneSpec.lockDatePatch();
64+
/**
65+
* a wrapper of jasmine.clock().install()
66+
*/
67+
global['__zone_symbol__originFakeAsyncPatchLock'] = global['__zone_symbol__fakeAsyncPatchLock'];
68+
global['__zone_symbol__fakeAsyncPatchLock'] = true;
69+
global.jasmine && global.jasmine.clock().install();
8270
};
8371

8472
jest.useRealTimers = function () {
85-
const zs = getFakeAsyncTestZoneSpec();
86-
if (!zs) {
87-
throw new Error('Must use real timers in the same block with useFakeTimers');
88-
}
89-
const proxyZoneSpec = ProxyZoneSpec.get();
90-
const lastDelegate = jest.__zone_symbol__last_delegate_spec;
91-
jest.__zone_symbol__last_delegate_spec = null;
92-
proxyZoneSpec.setDelegate(lastDelegate);
93-
zs.unlockDatePatch();
73+
global['__zone_symbol__fakeAsyncPatchLock'] = global['__zone_symbol__originFakeAsyncPatchLock'];
74+
global.jasmine && global.jasmine.clock().uninstall();
9475
};
9576
}

lib/testing/async-testing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Zone.__load_patch('asynctest', (global: any, Zone: ZoneType, api: _ZonePrivate)
1212
* Wraps a test function in an asynchronous test zone. The test will automatically
1313
* complete when all asynchronous calls within this zone are done.
1414
*/
15-
(Zone as any)[api.symbol('asyncTest')] = function asyncTest(fn: Function): (done: any) => any {
15+
global['asyncTest'] = (Zone as any)[api.symbol('asyncTest')] = function asyncTest(fn: Function): (done: any) => any {
1616
// If we're running using the Jasmine test framework, adapt to call the 'done'
1717
// function when asynchronous activity is finished.
1818
if (global.jasmine) {

lib/testing/fake-async.ts

+42-5
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate)
5555
checkRemainingMacrotasks?: boolean
5656
} = {checkNested: true, checkRemainingMacrotasks: true}): (...args: any[]) => any {
5757
// Not using an arrow function to preserve context passed from call site
58+
if (global['__zone_symbol__fakeAsyncCheckRemaining'] === false) {
59+
options.checkRemainingMacrotasks = false;
60+
}
5861
return function(...args: any[]) {
5962
const proxyZoneSpec = ProxyZoneSpec.assertPresent();
6063
if (Zone.current.get('FakeAsyncTestZoneSpec')) {
@@ -132,8 +135,8 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate)
132135
*
133136
* @experimental
134137
*/
135-
function tick(millis: number = 0): void {
136-
_getFakeAsyncZoneSpec().tick(millis);
138+
function tick(millis: number = 0, doTick?: (elapsed: number) => void): void {
139+
_getFakeAsyncZoneSpec().tick(millis, doTick);
137140
}
138141

139142
/**
@@ -146,8 +149,8 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate)
146149
*
147150
* @experimental
148151
*/
149-
function flush(maxTurns?: number): number {
150-
return _getFakeAsyncZoneSpec().flush(maxTurns);
152+
function flush(maxTurns?: number, isPeriodic: boolean = false): number {
153+
return _getFakeAsyncZoneSpec().flush(maxTurns, isPeriodic);
151154
}
152155

153156
/**
@@ -169,6 +172,40 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate)
169172
function flushMicrotasks(): void {
170173
_getFakeAsyncZoneSpec().flushMicrotasks();
171174
}
175+
176+
/**
177+
* Clear all microtasks
178+
*
179+
* @experimental
180+
*/
181+
function clearAllMacrotasks(): void {
182+
_getFakeAsyncZoneSpec().clearAllMacrotasks();
183+
}
184+
185+
/**
186+
* flush all macroTasks and discard periodic tasks
187+
*
188+
* @experimental
189+
*/
190+
function flushAndDiscardPeriodicTasks(): void {
191+
const fakeAsyncSpec = _getFakeAsyncZoneSpec();
192+
fakeAsyncSpec.flush(100, true);
193+
discardPeriodicTasks();
194+
}
195+
196+
172197
(Zone as any)[api.symbol('fakeAsyncTest')] =
173-
{resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync};
198+
{resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync, clearAllMacrotasks};
199+
200+
/**
201+
* expose those function to global
202+
*/
203+
global['resetFakeAsyncZone'] = resetFakeAsyncZone;
204+
global['flushMicrotasks'] = flushMicrotasks;
205+
global['discardPeriodicTasks'] = discardPeriodicTasks;
206+
global['tick'] = tick;
207+
global['flush'] = flush;
208+
global['fakeAsyncTest'] = fakeAsync;
209+
global['clearAllMacrotasks'] = clearAllMacrotasks;
210+
global['flushAndDiscardPeriodicTasks'] = flushAndDiscardPeriodicTasks;
174211
});

lib/testing/zone-testing.typing.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
/**
99
* Zone testing type definition, mix jasmine/mocha/jest
1010
*/
11+
/**
12+
* Jasmine/Jest/Mocha BDD/TDD interface
13+
*/
1114
declare const describe: ZoneTest.Describe;
1215
declare const xdescribe: ZoneTest.Describe;
1316
declare const fdescribe: ZoneTest.Describe;
@@ -37,6 +40,23 @@ declare const suiteTeardown: ZoneTest.BeforeAndAfter;
3740

3841
declare const expect: ZoneTest.Expect;
3942
declare function fail(error?: any): void;
43+
declare function pending(reason?: string): void;
44+
45+
/**
46+
* Zone testing global interface for asyncTest/syncTest/fakeAsyncTest
47+
*/
48+
49+
declare function asyncTest(fn: ZoneTest.BeforeAndAfter | ZoneTest.Test): (done: ZoneTest.DoneCallback) => PromiseLike<any> | void;
50+
declare function fakeAsyncTest(fn: ZoneTest.BeforeAndAfter | ZoneTest.Test, fakeAsyncTestOptions?: {
51+
checkNested?: boolean, checkRemainingMacrotasks?: boolean
52+
}): (done: ZoneTest.DoneCallback) => PromiseLike<any> | void;
53+
declare function resetFakeAsyncZone(): void;
54+
declare function flushMicrotasks(): void;
55+
declare function flushAndDiscardPeriodicTasks(): void;
56+
declare function discardPeriodicTasks(): void;
57+
declare function tick(millis?: number, doTick?: (elapsed: number) => void): void;
58+
declare function flush(maxTurns?: number): void;
59+
declare function clearAllMacrotasks(): void;
4060

4161
declare namespace ZoneTest {
4262
interface DoneCallback extends Function {
@@ -229,7 +249,6 @@ declare namespace jest {
229249
function fn<T extends {}>(implementation: (...args: any[]) => T): Mock<T>;
230250
function fn<T>(implementation?: (...args: any[]) => any): Mock<T>;
231251
function isMockFunction(fn: any): fn is Mock<any>;
232-
function runAllImmediates(): typeof jest;
233252
function runAllTicks(): typeof jest;
234253
function runAllTimers(): typeof jest;
235254
function runOnlyPendingTimers(): typeof jest;
@@ -268,4 +287,3 @@ declare namespace jest {
268287
}
269288
}
270289

271-
declare function pending(reason?: string): void;

lib/zone-spec/fake-async-test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,18 @@
416416
return elapsed;
417417
}
418418

419+
clearAllMacrotasks() {
420+
while (this.pendingTimers.length > 0) {
421+
const timerId = this.pendingTimers.shift();
422+
this._clearTimeout(timerId);
423+
}
424+
425+
while (this.pendingPeriodicTimers.length > 0) {
426+
const intervalId = this.pendingPeriodicTimers.shift();
427+
this._clearInterval(intervalId);
428+
}
429+
}
430+
419431
// ZoneSpec implementation below.
420432

421433
name: string;

0 commit comments

Comments
 (0)