refactor(test_lib): do not execute jasmine test as async if not required

fixes #3893
This commit is contained in:
Victor Berchet 2015-08-28 16:23:28 -07:00
parent 46f751bd2f
commit 44a991e245
2 changed files with 69 additions and 48 deletions

View File

@ -195,13 +195,7 @@ export function inject(tokens: any[], fn: Function): FunctionWithParamTokens {
} }
export class FunctionWithParamTokens { export class FunctionWithParamTokens {
_tokens: any[]; constructor(private _tokens: any[], private _fn: Function) {}
_fn: Function;
constructor(tokens: any[], fn: Function) {
this._tokens = tokens;
this._fn = fn;
}
/** /**
* Returns the value of the executed function. * Returns the value of the executed function.
@ -210,4 +204,6 @@ export class FunctionWithParamTokens {
var params = ListWrapper.map(this._tokens, (t) => injector.get(t)); var params = ListWrapper.map(this._tokens, (t) => injector.get(t));
return FunctionWrapper.apply(this._fn, params); return FunctionWrapper.apply(this._fn, params);
} }
hasToken(token: any): boolean { return this._tokens.indexOf(token) > -1; }
} }

View File

@ -2,7 +2,7 @@
import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {StringMapWrapper} from 'angular2/src/core/facade/collection'; import {StringMapWrapper} from 'angular2/src/core/facade/collection';
import {global} from 'angular2/src/core/facade/lang'; import {global, isFunction} from 'angular2/src/core/facade/lang';
import {NgZoneZone} from 'angular2/src/core/zone/ng_zone'; import {NgZoneZone} from 'angular2/src/core/zone/ng_zone';
import {bind} from 'angular2/di'; import {bind} from 'angular2/di';
@ -18,6 +18,10 @@ var _global: jasmine.GlobalPolluter = <any>(typeof window === 'undefined' ? glob
export var afterEach = _global.afterEach; export var afterEach = _global.afterEach;
type SyncTestFn = () => void;
type AsyncTestFn = (done: () => void) => void;
type AnyTestFn = SyncTestFn | AsyncTestFn;
export interface NgMatchers extends jasmine.Matchers { export interface NgMatchers extends jasmine.Matchers {
toBe(expected: any): boolean; toBe(expected: any): boolean;
toEqual(expected: any): boolean; toEqual(expected: any): boolean;
@ -34,11 +38,9 @@ export interface NgMatchers extends jasmine.Matchers {
export var expect: (actual: any) => NgMatchers = <any>_global.expect; export var expect: (actual: any) => NgMatchers = <any>_global.expect;
export class AsyncTestCompleter { export class AsyncTestCompleter {
_done: Function; constructor(private _done: Function) {}
constructor(done: Function) { this._done = done; } done(): void { this._done(); }
done() { this._done(); }
} }
var jsmBeforeEach = _global.beforeEach; var jsmBeforeEach = _global.beforeEach;
@ -54,19 +56,23 @@ var inIt = false;
var testBindings; var testBindings;
/**
* Mechanism to run `beforeEach()` functions of Angular tests.
*
* Note: Jasmine own `beforeEach` is used by this library to handle DI bindings.
*/
class BeforeEachRunner { class BeforeEachRunner {
_fns: FunctionWithParamTokens[]; private _fns: Array<FunctionWithParamTokens | SyncTestFn> = [];
_parent: BeforeEachRunner;
constructor(parent: BeforeEachRunner) {
this._fns = [];
this._parent = parent;
}
beforeEach(fn: FunctionWithParamTokens) { this._fns.push(fn); } constructor(private _parent: BeforeEachRunner) {}
run(injector) { beforeEach(fn: FunctionWithParamTokens | SyncTestFn): void { this._fns.push(fn); }
run(injector): void {
if (this._parent) this._parent.run(injector); if (this._parent) this._parent.run(injector);
this._fns.forEach((fn) => fn.execute(injector)); this._fns.forEach((fn) => {
return isFunction(fn) ? (<SyncTestFn>fn)() : (<FunctionWithParamTokens>fn).execute(injector);
});
} }
} }
@ -94,17 +100,13 @@ export function xdescribe(...args) {
return _describe(jsmXDescribe, ...args); return _describe(jsmXDescribe, ...args);
} }
export function beforeEach(fn) { export function beforeEach(fn: FunctionWithParamTokens | SyncTestFn) {
if (runnerStack.length > 0) { if (runnerStack.length > 0) {
// Inside a describe block, beforeEach() uses a BeforeEachRunner // Inside a describe block, beforeEach() uses a BeforeEachRunner
var runner = runnerStack[runnerStack.length - 1]; runnerStack[runnerStack.length - 1].beforeEach(fn);
if (!(fn instanceof FunctionWithParamTokens)) {
fn = inject([], fn);
}
runner.beforeEach(fn);
} else { } else {
// Top level beforeEach() are delegated to jasmine // Top level beforeEach() are delegated to jasmine
jsmBeforeEach(fn); jsmBeforeEach(<SyncTestFn>fn);
} }
} }
@ -128,34 +130,57 @@ export function beforeEachBindings(fn) {
}); });
} }
function _it(jsmFn, name, fn, timeOut) { function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | AnyTestFn,
timeOut: number) {
var runner = runnerStack[runnerStack.length - 1]; var runner = runnerStack[runnerStack.length - 1];
jsmFn(name, function(done) { if (testFn instanceof FunctionWithParamTokens) {
var async = false; // The test case uses inject(). ie `it('test', inject([AsyncTestCompleter], (async) => { ...
// }));`
if (testFn.hasToken(AsyncTestCompleter)) {
jsmFn(name, (done) => {
var completerBinding = var completerBinding =
bind(AsyncTestCompleter) bind(AsyncTestCompleter)
.toFactory(() => { .toFactory(() => {
// Mark the test as async when an AsyncTestCompleter is injected in an it() // Mark the test as async when an AsyncTestCompleter is injected in an it()
if (!inIt) throw new Error('AsyncTestCompleter can only be injected in an "it()"'); if (!inIt)
async = true; throw new Error('AsyncTestCompleter can only be injected in an "it()"');
return new AsyncTestCompleter(done); return new AsyncTestCompleter(done);
}); });
var injector = createTestInjector([...testBindings, completerBinding]); var injector = createTestInjector([...testBindings, completerBinding]);
runner.run(injector); runner.run(injector);
if (!(fn instanceof FunctionWithParamTokens)) { inIt = true;
fn = inject([], fn); testFn.execute(injector);
inIt = false;
}, timeOut);
} else {
jsmFn(name, () => {
var injector = createTestInjector(testBindings);
runner.run(injector);
testFn.execute(injector);
}, timeOut);
} }
inIt = true; } else {
fn.execute(injector); // The test case doesn't use inject(). ie `it('test', (done) => { ... }));`
inIt = false;
if (!async) done(); if ((<any>testFn).length === 0) {
jsmFn(name, () => {
var injector = createTestInjector(testBindings);
runner.run(injector);
(<SyncTestFn>testFn)();
}, timeOut); }, timeOut);
} else {
jsmFn(name, (done) => {
var injector = createTestInjector(testBindings);
runner.run(injector);
(<AsyncTestFn>testFn)(done);
}, timeOut);
}
}
} }
export function it(name, fn, timeOut = null) { export function it(name, fn, timeOut = null) {