2015-06-17 14:17:21 -04:00
|
|
|
/// <reference path="../../typings/jasmine/jasmine.d.ts"/>
|
2015-05-20 20:19:46 -04:00
|
|
|
|
2015-05-12 10:28:57 -04:00
|
|
|
import {BaseException, global} from 'angular2/src/facade/lang';
|
|
|
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
2015-05-20 20:19:46 -04:00
|
|
|
import {NgZoneZone} from 'angular2/src/core/zone/ng_zone';
|
2015-05-12 10:28:57 -04:00
|
|
|
|
|
|
|
var _scheduler;
|
2015-05-20 20:19:46 -04:00
|
|
|
var _microtasks: List<Function> = [];
|
2015-05-12 10:28:57 -04:00
|
|
|
var _pendingPeriodicTimers: List<number> = [];
|
|
|
|
var _pendingTimers: List<number> = [];
|
|
|
|
var _error = null;
|
|
|
|
|
2015-05-20 20:19:46 -04:00
|
|
|
interface FakeAsyncZone extends NgZoneZone {
|
|
|
|
_inFakeAsyncZone: boolean;
|
|
|
|
}
|
|
|
|
|
2015-05-12 10:28:57 -04:00
|
|
|
/**
|
|
|
|
* Wraps a function to be executed in the fakeAsync zone:
|
|
|
|
* - microtasks are manually executed by calling `flushMicrotasks()`,
|
|
|
|
* - timers are synchronous, `tick()` simulates the asynchronous passage of time.
|
|
|
|
*
|
|
|
|
* If there are any pending timers at the end of the function, an exception will be thrown.
|
|
|
|
*
|
|
|
|
* @param fn
|
|
|
|
* @returns {Function} The function wrapped to be executed in the fakeAsync zone
|
|
|
|
*/
|
|
|
|
export function fakeAsync(fn: Function): Function {
|
2015-05-19 03:13:17 -04:00
|
|
|
if ((<FakeAsyncZone>global.zone)._inFakeAsyncZone) {
|
|
|
|
throw new Error('fakeAsync() calls can not be nested');
|
|
|
|
}
|
2015-05-12 10:28:57 -04:00
|
|
|
|
2015-05-20 20:19:46 -04:00
|
|
|
var fakeAsyncZone = <FakeAsyncZone>global.zone.fork({
|
2015-05-12 10:28:57 -04:00
|
|
|
setTimeout: _setTimeout,
|
|
|
|
clearTimeout: _clearTimeout,
|
|
|
|
setInterval: _setInterval,
|
|
|
|
clearInterval: _clearInterval,
|
|
|
|
scheduleMicrotask: _scheduleMicrotask,
|
|
|
|
_inFakeAsyncZone: true
|
|
|
|
});
|
|
|
|
|
2015-07-10 05:29:41 -04:00
|
|
|
return function(...args) {
|
2015-05-20 20:19:46 -04:00
|
|
|
// TODO(tbosch): This class should already be part of the jasmine typings but it is not...
|
|
|
|
_scheduler = new (<any>jasmine).DelayedFunctionScheduler();
|
2015-05-12 10:28:57 -04:00
|
|
|
ListWrapper.clear(_microtasks);
|
|
|
|
ListWrapper.clear(_pendingPeriodicTimers);
|
|
|
|
ListWrapper.clear(_pendingTimers);
|
|
|
|
|
2015-06-02 13:55:03 -04:00
|
|
|
let res = fakeAsyncZone.run(() => {
|
2015-07-10 05:29:41 -04:00
|
|
|
let res = fn(...args);
|
2015-06-02 13:55:03 -04:00
|
|
|
flushMicrotasks();
|
|
|
|
return res;
|
|
|
|
});
|
2015-05-12 10:28:57 -04:00
|
|
|
|
|
|
|
if (_pendingPeriodicTimers.length > 0) {
|
2015-05-20 20:19:46 -04:00
|
|
|
throw new BaseException(
|
|
|
|
`${_pendingPeriodicTimers.length} periodic timer(s) still in the queue.`);
|
2015-05-12 10:28:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_pendingTimers.length > 0) {
|
|
|
|
throw new BaseException(`${_pendingTimers.length} timer(s) still in the queue.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
_scheduler = null;
|
|
|
|
ListWrapper.clear(_microtasks);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simulates the asynchronous passage of time for the timers in the fakeAsync zone.
|
|
|
|
*
|
2015-05-20 20:19:46 -04:00
|
|
|
* The microtasks queue is drained at the very start of this function and after any timer callback
|
|
|
|
* has been executed.
|
2015-05-12 10:28:57 -04:00
|
|
|
*
|
|
|
|
* @param {number} millis Number of millisecond, defaults to 0
|
|
|
|
*/
|
|
|
|
export function tick(millis: number = 0): void {
|
|
|
|
_assertInFakeAsyncZone();
|
|
|
|
flushMicrotasks();
|
|
|
|
_scheduler.tick(millis);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flush any pending microtasks.
|
|
|
|
*/
|
|
|
|
export function flushMicrotasks(): void {
|
|
|
|
_assertInFakeAsyncZone();
|
|
|
|
while (_microtasks.length > 0) {
|
|
|
|
var microtask = ListWrapper.removeAt(_microtasks, 0);
|
|
|
|
microtask();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-10 05:29:41 -04:00
|
|
|
function _setTimeout(fn: Function, delay: number, ...args): number {
|
2015-05-12 10:28:57 -04:00
|
|
|
var cb = _fnAndFlush(fn);
|
|
|
|
var id = _scheduler.scheduleFunction(cb, delay, args);
|
2015-06-17 14:17:21 -04:00
|
|
|
_pendingTimers.push(id);
|
2015-05-12 10:28:57 -04:00
|
|
|
_scheduler.scheduleFunction(_dequeueTimer(id), delay);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _clearTimeout(id: number) {
|
|
|
|
_dequeueTimer(id);
|
|
|
|
return _scheduler.removeFunctionWithId(id);
|
|
|
|
}
|
|
|
|
|
2015-07-10 05:29:41 -04:00
|
|
|
function _setInterval(fn: Function, interval: number, ...args) {
|
2015-05-12 10:28:57 -04:00
|
|
|
var cb = _fnAndFlush(fn);
|
|
|
|
var id = _scheduler.scheduleFunction(cb, interval, args, true);
|
|
|
|
_pendingPeriodicTimers.push(id);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _clearInterval(id: number) {
|
|
|
|
ListWrapper.remove(_pendingPeriodicTimers, id);
|
|
|
|
return _scheduler.removeFunctionWithId(id);
|
|
|
|
}
|
|
|
|
|
2015-05-20 20:19:46 -04:00
|
|
|
function _fnAndFlush(fn: Function): Function {
|
2015-07-10 05:29:41 -04:00
|
|
|
return (...args) => {
|
|
|
|
fn.apply(global, args);
|
|
|
|
flushMicrotasks();
|
|
|
|
}
|
2015-05-12 10:28:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function _scheduleMicrotask(microtask: Function): void {
|
2015-06-17 14:17:21 -04:00
|
|
|
_microtasks.push(microtask);
|
2015-05-12 10:28:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function _dequeueTimer(id: number): Function {
|
2015-05-20 20:19:46 -04:00
|
|
|
return function() { ListWrapper.remove(_pendingTimers, id); }
|
2015-05-12 10:28:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function _assertInFakeAsyncZone(): void {
|
2015-05-30 14:55:17 -04:00
|
|
|
if (!global.zone || !(<FakeAsyncZone>global.zone)._inFakeAsyncZone) {
|
2015-05-12 10:28:57 -04:00
|
|
|
throw new Error('The code should be running in the fakeAsync zone to call this function');
|
|
|
|
}
|
|
|
|
}
|