fix(fake_async): share zone between beforeEach and it

This is needed for the case if a `beforeEach` instantiates
`NgZone`and the `it` uses
`TestComponentBuilder.createFakeAsync`.

Otherwise the `NgZone` will use the root zone as parent,
and `TestComponentBuilder.createFakeAsync` will always return
undefined as `tick` does not forward promises created
under the zone of `NgZone`.
This commit is contained in:
Tobias Bosch 2016-08-02 04:45:15 -07:00
parent 3ce11ed58c
commit 16cc9b46aa
3 changed files with 72 additions and 27 deletions

View File

@ -285,5 +285,21 @@ export function main() {
}).toThrowError('The code should be running in the fakeAsync zone to call this function'); }).toThrowError('The code should be running in the fakeAsync zone to call this function');
}); });
}); });
describe('only one `fakeAsync` zone per test', () => {
let zoneInBeforeEach: Zone;
let zoneInTest1: Zone;
beforeEach(fakeAsync(() => { zoneInBeforeEach = Zone.current; }));
it('should use the same zone as in beforeEach', fakeAsync(() => {
zoneInTest1 = Zone.current;
expect(zoneInTest1).toBe(zoneInBeforeEach);
}));
it('should use a different zone between tests', fakeAsync(() => {
expect(Zone.current).toBe(zoneInBeforeEach);
expect(Zone.current).not.toBe(zoneInTest1);
}));
});
}); });
} }

View File

@ -10,6 +10,22 @@ import {BaseException} from '../index';
let _FakeAsyncTestZoneSpecType = (Zone as any /** TODO #9100 */)['FakeAsyncTestZoneSpec']; let _FakeAsyncTestZoneSpecType = (Zone as any /** TODO #9100 */)['FakeAsyncTestZoneSpec'];
let _fakeAsyncZone: Zone = null;
let _fakeAsyncTestZoneSpec: any = null;
/**
* Clears out the shared fake async zone for a test.
* To be called in a global `beforeEach`.
*
* @experimental
*/
export function resetFakeAsyncZone() {
_fakeAsyncZone = null;
_fakeAsyncTestZoneSpec = null;
}
let _inFakeAsyncCall = false;
/** /**
* Wraps a function to be executed in the fakeAsync zone: * Wraps a function to be executed in the fakeAsync zone:
* - microtasks are manually executed by calling `flushMicrotasks()`, * - microtasks are manually executed by calling `flushMicrotasks()`,
@ -29,40 +45,49 @@ let _FakeAsyncTestZoneSpecType = (Zone as any /** TODO #9100 */)['FakeAsyncTestZ
* @experimental * @experimental
*/ */
export function fakeAsync(fn: Function): (...args: any[]) => any { export function fakeAsync(fn: Function): (...args: any[]) => any {
if (Zone.current.get('FakeAsyncTestZoneSpec') != null) {
throw new BaseException('fakeAsync() calls can not be nested');
}
let fakeAsyncTestZoneSpec = new _FakeAsyncTestZoneSpecType();
let fakeAsyncZone = Zone.current.fork(fakeAsyncTestZoneSpec);
return function(...args: any[] /** TODO #9100 */) { return function(...args: any[] /** TODO #9100 */) {
let res = fakeAsyncZone.run(() => { if (_inFakeAsyncCall) {
let res = fn(...args); throw new BaseException('fakeAsync() calls can not be nested');
flushMicrotasks(); }
_inFakeAsyncCall = true;
try {
if (!_fakeAsyncZone) {
if (Zone.current.get('FakeAsyncTestZoneSpec') != null) {
throw new BaseException('fakeAsync() calls can not be nested');
}
_fakeAsyncTestZoneSpec = new _FakeAsyncTestZoneSpecType();
_fakeAsyncZone = Zone.current.fork(_fakeAsyncTestZoneSpec);
}
let res = _fakeAsyncZone.run(() => {
let res = fn(...args);
flushMicrotasks();
return res;
});
if (_fakeAsyncTestZoneSpec.pendingPeriodicTimers.length > 0) {
throw new BaseException(
`${_fakeAsyncTestZoneSpec.pendingPeriodicTimers.length} ` +
`periodic timer(s) still in the queue.`);
}
if (_fakeAsyncTestZoneSpec.pendingTimers.length > 0) {
throw new BaseException(
`${_fakeAsyncTestZoneSpec.pendingTimers.length} timer(s) still in the queue.`);
}
return res; return res;
}); } finally {
_inFakeAsyncCall = false;
if (fakeAsyncTestZoneSpec.pendingPeriodicTimers.length > 0) {
throw new BaseException(
`${fakeAsyncTestZoneSpec.pendingPeriodicTimers.length} ` +
`periodic timer(s) still in the queue.`);
} }
if (fakeAsyncTestZoneSpec.pendingTimers.length > 0) {
throw new BaseException(
`${fakeAsyncTestZoneSpec.pendingTimers.length} timer(s) still in the queue.`);
}
return res;
}; };
} }
function _getFakeAsyncZoneSpec(): any { function _getFakeAsyncZoneSpec(): any {
let zoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); if (_fakeAsyncTestZoneSpec == null) {
if (zoneSpec == null) {
throw new Error('The code should be running in the fakeAsync zone to call this function'); throw new Error('The code should be running in the fakeAsync zone to call this function');
} }
return zoneSpec; return _fakeAsyncTestZoneSpec;
} }
/** /**

View File

@ -14,15 +14,19 @@
import {SchemaMetadata} from '../index'; import {SchemaMetadata} from '../index';
import {resetFakeAsyncZone} from './fake_async';
import {TestBed, TestModuleMetadata, getTestBed} from './test_bed'; import {TestBed, TestModuleMetadata, getTestBed} from './test_bed';
declare var global: any; declare var global: any;
var _global = <any>(typeof window === 'undefined' ? global : window); var _global = <any>(typeof window === 'undefined' ? global : window);
// Reset the test providers before each test. // Reset the test providers and the fake async zone before each test.
if (_global.beforeEach) { if (_global.beforeEach) {
_global.beforeEach(() => { TestBed.resetTestingModule(); }); _global.beforeEach(() => {
TestBed.resetTestingModule();
resetFakeAsyncZone();
});
} }
/** /**