X Tutup
Skip to content

Commit a8b75c3

Browse files
committed
feat(testability): hook zone into whenstable api with async support
closes(#428)
1 parent 19d8b22 commit a8b75c3

File tree

9 files changed

+508
-51
lines changed

9 files changed

+508
-51
lines changed

modules/angular2/src/core/testability/testability.ts

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
33
import {Map, MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
44
import {StringWrapper, isBlank, BaseException} from 'angular2/src/facade/lang';
55
import * as getTestabilityModule from './get_testability';
6+
import {NgZone} from '../zone/ng_zone';
7+
import {PromiseWrapper} from 'angular2/src/facade/async';
68

79

810
/**
@@ -12,40 +14,57 @@ import * as getTestabilityModule from './get_testability';
1214
*/
1315
@Injectable()
1416
export class Testability {
15-
_pendingCount: number;
16-
_callbacks: List<Function>;
17+
_pendingCount: number = 0;
18+
_callbacks: List<Function> = [];
19+
_isAngularEventPending: boolean = false;
1720

18-
constructor() {
19-
this._pendingCount = 0;
20-
this._callbacks = [];
21+
constructor(public _ngZone: NgZone) { this._watchAngularEvents(_ngZone); }
22+
23+
_watchAngularEvents(_ngZone: NgZone): void {
24+
_ngZone.overrideOnTurnStart(() => { this._isAngularEventPending = true; });
25+
_ngZone.overrideOnEventDone(() => {
26+
this._isAngularEventPending = false;
27+
this._runCallbacksIfReady();
28+
}, true);
2129
}
2230

23-
increaseCount(delta: number = 1): number {
24-
this._pendingCount += delta;
31+
increasePendingRequestCount(): number {
32+
this._pendingCount += 1;
33+
return this._pendingCount;
34+
}
35+
36+
decreasePendingRequestCount(): number {
37+
this._pendingCount -= 1;
2538
if (this._pendingCount < 0) {
2639
throw new BaseException('pending async requests below zero');
27-
} else if (this._pendingCount == 0) {
28-
this._runCallbacks();
2940
}
41+
this._runCallbacksIfReady();
3042
return this._pendingCount;
3143
}
3244

33-
_runCallbacks() {
34-
while (this._callbacks.length !== 0) {
35-
ListWrapper.removeLast(this._callbacks)();
45+
_runCallbacksIfReady(): void {
46+
if (this._pendingCount != 0 || this._isAngularEventPending) {
47+
return; // Not ready
3648
}
49+
50+
// Schedules the call backs in a new frame so that it is always async.
51+
PromiseWrapper.resolve(null).then((_) => {
52+
while (this._callbacks.length !== 0) {
53+
(this._callbacks.pop())();
54+
}
55+
});
3756
}
3857

39-
whenStable(callback: Function) {
58+
whenStable(callback: Function): void {
4059
this._callbacks.push(callback);
41-
42-
if (this._pendingCount === 0) {
43-
this._runCallbacks();
44-
}
45-
// TODO(juliemr) - hook into the zone api.
60+
this._runCallbacksIfReady();
4661
}
4762

48-
getPendingCount(): number { return this._pendingCount; }
63+
getPendingRequestCount(): number { return this._pendingCount; }
64+
65+
// This only accounts for ngZone, and not pending counts. Use `whenStable` to
66+
// check for stability.
67+
isAngularEventPending(): boolean { return this._isAngularEventPending; }
4968

5069
findBindings(using: any, binding: string, exactMatch: boolean): List<any> {
5170
// TODO(juliemr): implement.
@@ -55,13 +74,9 @@ export class Testability {
5574

5675
@Injectable()
5776
export class TestabilityRegistry {
58-
_applications: Map<any, Testability>;
77+
_applications: Map<any, Testability> = new Map();
5978

60-
constructor() {
61-
this._applications = new Map();
62-
63-
getTestabilityModule.GetTestability.addToWindow(this);
64-
}
79+
constructor() { getTestabilityModule.GetTestability.addToWindow(this); }
6580

6681
registerApplication(token: any, testability: Testability) {
6782
this._applications.set(token, testability);

modules/angular2/src/core/zone/ng_zone.dart

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,36 @@ import 'package:stack_trace/stack_trace.dart' show Chain;
66
typedef void ZeroArgFunction();
77
typedef void ErrorHandlingFn(error, stackTrace);
88

9+
/**
10+
* A `Timer` wrapper that lets you specify additional functions to call when it
11+
* is cancelled.
12+
*/
13+
class WrappedTimer implements Timer {
14+
15+
Timer _timer;
16+
ZeroArgFunction _onCancelCb;
17+
18+
WrappedTimer(Timer timer) {
19+
_timer = timer;
20+
}
21+
22+
void addOnCancelCb(ZeroArgFunction onCancelCb) {
23+
if (this._onCancelCb != null) {
24+
throw "On cancel cb already registered";
25+
}
26+
this._onCancelCb = onCancelCb;
27+
}
28+
29+
void cancel() {
30+
if (this._onCancelCb != null) {
31+
this._onCancelCb();
32+
}
33+
_timer.cancel();
34+
}
35+
36+
bool get isActive => _timer.isActive;
37+
}
38+
939
/**
1040
* A `Zone` wrapper that lets you schedule tasks after its private microtask queue is exhausted but
1141
* before the next "VM turn", i.e. event loop iteration.
@@ -45,6 +75,8 @@ class NgZone {
4575

4676
bool _inVmTurnDone = false;
4777

78+
List<Timer> _pendingTimers = [];
79+
4880
/**
4981
* Associates with this
5082
*
@@ -92,8 +124,16 @@ class NgZone {
92124
*
93125
* This hook is useful for validating application state (e.g. in a test).
94126
*/
95-
void overrideOnEventDone(ZeroArgFunction onEventDoneFn) {
127+
void overrideOnEventDone(ZeroArgFunction onEventDoneFn, [bool waitForAsync = false]) {
96128
_onEventDone = onEventDoneFn;
129+
130+
if (waitForAsync) {
131+
_onEventDone = () {
132+
if (_pendingTimers.length == 0) {
133+
onEventDoneFn();
134+
}
135+
};
136+
}
97137
}
98138

99139
/**
@@ -224,14 +264,29 @@ class NgZone {
224264
}
225265
}
226266

267+
Timer _createTimer(Zone self, ZoneDelegate parent, Zone zone, Duration duration, fn()) {
268+
WrappedTimer wrappedTimer;
269+
var cb = () {
270+
fn();
271+
_pendingTimers.remove(wrappedTimer);
272+
};
273+
Timer timer = parent.createTimer(zone, duration, cb);
274+
wrappedTimer = new WrappedTimer(timer);
275+
wrappedTimer.addOnCancelCb(() => _pendingTimers.remove(wrappedTimer));
276+
277+
_pendingTimers.add(wrappedTimer);
278+
return wrappedTimer;
279+
}
280+
227281
Zone _createInnerZone(Zone zone, {handleUncaughtError}) {
228282
return zone.fork(
229283
specification: new ZoneSpecification(
230284
scheduleMicrotask: _scheduleMicrotask,
231285
run: _run,
232286
runUnary: _runUnary,
233287
runBinary: _runBinary,
234-
handleUncaughtError: handleUncaughtError),
288+
handleUncaughtError: handleUncaughtError,
289+
createTimer: _createTimer),
235290
zoneValues: {'_innerZone': true});
236291
}
237292
}

modules/angular2/src/core/zone/ng_zone.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export class NgZone {
4040

4141
_inVmTurnDone: boolean = false;
4242

43+
_pendingTimeouts: List<number> = [];
44+
4345
/**
4446
* Associates with this
4547
*
@@ -93,8 +95,17 @@ export class NgZone {
9395
*
9496
* This hook is useful for validating application state (e.g. in a test).
9597
*/
96-
overrideOnEventDone(onEventDoneFn: Function): void {
97-
this._onEventDone = normalizeBlank(onEventDoneFn);
98+
overrideOnEventDone(onEventDoneFn: Function, opt_waitForAsync: boolean): void {
99+
var normalizedOnEventDone = normalizeBlank(onEventDoneFn);
100+
if (opt_waitForAsync) {
101+
this._onEventDone = () => {
102+
if (!this._pendingTimeouts.length) {
103+
normalizedOnEventDone();
104+
}
105+
};
106+
} else {
107+
this._onEventDone = normalizedOnEventDone;
108+
}
98109
}
99110

100111
/**
@@ -215,6 +226,24 @@ export class NgZone {
215226
parentScheduleMicrotask.call(this, microtask);
216227
};
217228
},
229+
'$setTimeout': function(parentSetTimeout) {
230+
return function(fn: Function, delay: number, ...args) {
231+
var id;
232+
var cb = function() {
233+
fn();
234+
ListWrapper.remove(ngZone._pendingTimeouts, id);
235+
};
236+
id = parentSetTimeout(cb, delay, args);
237+
ngZone._pendingTimeouts.push(id);
238+
return id;
239+
};
240+
},
241+
'$clearTimeout': function(parentClearTimeout) {
242+
return function(id: number) {
243+
parentClearTimeout(id);
244+
ListWrapper.remove(ngZone._pendingTimeouts, id);
245+
};
246+
},
218247
_innerZone: true
219248
});
220249
}

0 commit comments

Comments
 (0)
X Tutup