From 44a991e2454a22779c07c1bf6b4a16fd2025c4b1 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 28 Aug 2015 16:23:28 -0700 Subject: [PATCH] refactor(test_lib): do not execute jasmine test as async if not required fixes #3893 --- .../angular2/src/test_lib/test_injector.ts | 10 +- modules/angular2/src/test_lib/test_lib.ts | 107 +++++++++++------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/modules/angular2/src/test_lib/test_injector.ts b/modules/angular2/src/test_lib/test_injector.ts index 4728cbff52..0cfa69bee3 100644 --- a/modules/angular2/src/test_lib/test_injector.ts +++ b/modules/angular2/src/test_lib/test_injector.ts @@ -195,13 +195,7 @@ export function inject(tokens: any[], fn: Function): FunctionWithParamTokens { } export class FunctionWithParamTokens { - _tokens: any[]; - _fn: Function; - - constructor(tokens: any[], fn: Function) { - this._tokens = tokens; - this._fn = fn; - } + constructor(private _tokens: any[], private _fn: 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)); return FunctionWrapper.apply(this._fn, params); } + + hasToken(token: any): boolean { return this._tokens.indexOf(token) > -1; } } diff --git a/modules/angular2/src/test_lib/test_lib.ts b/modules/angular2/src/test_lib/test_lib.ts index 88f51c7b38..1076218547 100644 --- a/modules/angular2/src/test_lib/test_lib.ts +++ b/modules/angular2/src/test_lib/test_lib.ts @@ -2,7 +2,7 @@ import {DOM} from 'angular2/src/core/dom/dom_adapter'; 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 {bind} from 'angular2/di'; @@ -18,6 +18,10 @@ var _global: jasmine.GlobalPolluter = (typeof window === 'undefined' ? glob export var afterEach = _global.afterEach; +type SyncTestFn = () => void; +type AsyncTestFn = (done: () => void) => void; +type AnyTestFn = SyncTestFn | AsyncTestFn; + export interface NgMatchers extends jasmine.Matchers { toBe(expected: any): boolean; toEqual(expected: any): boolean; @@ -34,11 +38,9 @@ export interface NgMatchers extends jasmine.Matchers { export var expect: (actual: any) => NgMatchers = _global.expect; export class AsyncTestCompleter { - _done: Function; + constructor(private _done: Function) {} - constructor(done: Function) { this._done = done; } - - done() { this._done(); } + done(): void { this._done(); } } var jsmBeforeEach = _global.beforeEach; @@ -54,19 +56,23 @@ var inIt = false; 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 { - _fns: FunctionWithParamTokens[]; - _parent: BeforeEachRunner; - constructor(parent: BeforeEachRunner) { - this._fns = []; - this._parent = parent; - } + private _fns: Array = []; - 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); - this._fns.forEach((fn) => fn.execute(injector)); + this._fns.forEach((fn) => { + return isFunction(fn) ? (fn)() : (fn).execute(injector); + }); } } @@ -94,17 +100,13 @@ export function xdescribe(...args) { return _describe(jsmXDescribe, ...args); } -export function beforeEach(fn) { +export function beforeEach(fn: FunctionWithParamTokens | SyncTestFn) { if (runnerStack.length > 0) { // Inside a describe block, beforeEach() uses a BeforeEachRunner - var runner = runnerStack[runnerStack.length - 1]; - if (!(fn instanceof FunctionWithParamTokens)) { - fn = inject([], fn); - } - runner.beforeEach(fn); + runnerStack[runnerStack.length - 1].beforeEach(fn); } else { // Top level beforeEach() are delegated to jasmine - jsmBeforeEach(fn); + jsmBeforeEach(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]; - jsmFn(name, function(done) { - var async = false; + if (testFn instanceof FunctionWithParamTokens) { + // The test case uses inject(). ie `it('test', inject([AsyncTestCompleter], (async) => { ... + // }));` - var completerBinding = - bind(AsyncTestCompleter) - .toFactory(() => { - // 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()"'); - async = true; - return new AsyncTestCompleter(done); - }); + if (testFn.hasToken(AsyncTestCompleter)) { + jsmFn(name, (done) => { + var completerBinding = + bind(AsyncTestCompleter) + .toFactory(() => { + // 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()"'); + return new AsyncTestCompleter(done); + }); - var injector = createTestInjector([...testBindings, completerBinding]); - runner.run(injector); + var injector = createTestInjector([...testBindings, completerBinding]); + runner.run(injector); - if (!(fn instanceof FunctionWithParamTokens)) { - fn = inject([], fn); + inIt = true; + testFn.execute(injector); + inIt = false; + }, timeOut); + } else { + jsmFn(name, () => { + var injector = createTestInjector(testBindings); + runner.run(injector); + testFn.execute(injector); + }, timeOut); } - inIt = true; - fn.execute(injector); - inIt = false; + } else { + // The test case doesn't use inject(). ie `it('test', (done) => { ... }));` - if (!async) done(); - }, timeOut); + if ((testFn).length === 0) { + jsmFn(name, () => { + var injector = createTestInjector(testBindings); + runner.run(injector); + (testFn)(); + }, timeOut); + } else { + jsmFn(name, (done) => { + var injector = createTestInjector(testBindings); + runner.run(injector); + (testFn)(done); + }, timeOut); + } + } } export function it(name, fn, timeOut = null) {