feat(testability): hook zone into whenstable api with async support

closes(#428)
This commit is contained in:
Hank Duan
2015-07-24 12:46:12 -07:00
parent 19d8b221b4
commit a8b75c3d41
9 changed files with 511 additions and 54 deletions

View File

@ -6,6 +6,36 @@ import 'package:stack_trace/stack_trace.dart' show Chain;
typedef void ZeroArgFunction();
typedef void ErrorHandlingFn(error, stackTrace);
/**
* A `Timer` wrapper that lets you specify additional functions to call when it
* is cancelled.
*/
class WrappedTimer implements Timer {
Timer _timer;
ZeroArgFunction _onCancelCb;
WrappedTimer(Timer timer) {
_timer = timer;
}
void addOnCancelCb(ZeroArgFunction onCancelCb) {
if (this._onCancelCb != null) {
throw "On cancel cb already registered";
}
this._onCancelCb = onCancelCb;
}
void cancel() {
if (this._onCancelCb != null) {
this._onCancelCb();
}
_timer.cancel();
}
bool get isActive => _timer.isActive;
}
/**
* A `Zone` wrapper that lets you schedule tasks after its private microtask queue is exhausted but
* before the next "VM turn", i.e. event loop iteration.
@ -45,6 +75,8 @@ class NgZone {
bool _inVmTurnDone = false;
List<Timer> _pendingTimers = [];
/**
* Associates with this
*
@ -92,8 +124,16 @@ class NgZone {
*
* This hook is useful for validating application state (e.g. in a test).
*/
void overrideOnEventDone(ZeroArgFunction onEventDoneFn) {
void overrideOnEventDone(ZeroArgFunction onEventDoneFn, [bool waitForAsync = false]) {
_onEventDone = onEventDoneFn;
if (waitForAsync) {
_onEventDone = () {
if (_pendingTimers.length == 0) {
onEventDoneFn();
}
};
}
}
/**
@ -224,6 +264,20 @@ class NgZone {
}
}
Timer _createTimer(Zone self, ZoneDelegate parent, Zone zone, Duration duration, fn()) {
WrappedTimer wrappedTimer;
var cb = () {
fn();
_pendingTimers.remove(wrappedTimer);
};
Timer timer = parent.createTimer(zone, duration, cb);
wrappedTimer = new WrappedTimer(timer);
wrappedTimer.addOnCancelCb(() => _pendingTimers.remove(wrappedTimer));
_pendingTimers.add(wrappedTimer);
return wrappedTimer;
}
Zone _createInnerZone(Zone zone, {handleUncaughtError}) {
return zone.fork(
specification: new ZoneSpecification(
@ -231,7 +285,8 @@ class NgZone {
run: _run,
runUnary: _runUnary,
runBinary: _runBinary,
handleUncaughtError: handleUncaughtError),
handleUncaughtError: handleUncaughtError,
createTimer: _createTimer),
zoneValues: {'_innerZone': true});
}
}

View File

@ -40,6 +40,8 @@ export class NgZone {
_inVmTurnDone: boolean = false;
_pendingTimeouts: List<number> = [];
/**
* Associates with this
*
@ -93,8 +95,17 @@ export class NgZone {
*
* This hook is useful for validating application state (e.g. in a test).
*/
overrideOnEventDone(onEventDoneFn: Function): void {
this._onEventDone = normalizeBlank(onEventDoneFn);
overrideOnEventDone(onEventDoneFn: Function, opt_waitForAsync: boolean): void {
var normalizedOnEventDone = normalizeBlank(onEventDoneFn);
if (opt_waitForAsync) {
this._onEventDone = () => {
if (!this._pendingTimeouts.length) {
normalizedOnEventDone();
}
};
} else {
this._onEventDone = normalizedOnEventDone;
}
}
/**
@ -215,6 +226,24 @@ export class NgZone {
parentScheduleMicrotask.call(this, microtask);
};
},
'$setTimeout': function(parentSetTimeout) {
return function(fn: Function, delay: number, ...args) {
var id;
var cb = function() {
fn();
ListWrapper.remove(ngZone._pendingTimeouts, id);
};
id = parentSetTimeout(cb, delay, args);
ngZone._pendingTimeouts.push(id);
return id;
};
},
'$clearTimeout': function(parentClearTimeout) {
return function(id: number) {
parentClearTimeout(id);
ListWrapper.remove(ngZone._pendingTimeouts, id);
};
},
_innerZone: true
});
}