chore(testing): Refactor test methods to have a uniform interface.
Remove FunctionWithParamTokens. All test wrappers async, fakeAsync and inject now return just a Function instead of FunctionWithParamTokens. This makes them directly consumable by the test framework. Also the test framework code does not have to handle a union of Function and FunctionWithParamTokens everywhere. The Function returned by the above methods are considered asynchronous by the test framework if they return a Promise, synchronous otherwise. Closes #8257
This commit is contained in:
parent
d2efac18ed
commit
35cd0ded22
4
modules/angular2/src/testing/async.dart
Normal file
4
modules/angular2/src/testing/async.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// This symbol is not used on the Dart side. This exists just as a stub.
|
||||||
|
async(Function fn) {
|
||||||
|
throw 'async() test wrapper not available for Dart.';
|
||||||
|
}
|
25
modules/angular2/src/testing/async.ts
Normal file
25
modules/angular2/src/testing/async.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Wraps a test function in an asynchronous test zone. The test will automatically
|
||||||
|
* complete when all asynchronous calls within this zone are done. Can be used
|
||||||
|
* to wrap an {@link inject} call.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* it('...', async(inject([AClass], (object) => {
|
||||||
|
* object.doSomething.then(() => {
|
||||||
|
* expect(...);
|
||||||
|
* })
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function async(fn: Function): Function {
|
||||||
|
return () => {
|
||||||
|
return new Promise<void>((finishCallback, failCallback) => {
|
||||||
|
var AsyncTestZoneSpec = Zone['AsyncTestZoneSpec'];
|
||||||
|
var testZoneSpec = new AsyncTestZoneSpec(finishCallback, failCallback, 'test');
|
||||||
|
var testZone = Zone.current.fork(testZoneSpec);
|
||||||
|
return testZone.run(fn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
13
modules/angular2/src/testing/async_test_completer.ts
Normal file
13
modules/angular2/src/testing/async_test_completer.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {PromiseCompleter} from 'angular2/src/facade/promise';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injectable completer that allows signaling completion of an asynchronous test. Used internally.
|
||||||
|
*/
|
||||||
|
export class AsyncTestCompleter {
|
||||||
|
private _completer = new PromiseCompleter<any>();
|
||||||
|
done(value?: any) { this._completer.resolve(value); }
|
||||||
|
|
||||||
|
fail(error?: any, stackTrace?: string) { this._completer.reject(error, stackTrace); }
|
||||||
|
|
||||||
|
get promise(): Promise<any> { return this._completer.promise; }
|
||||||
|
}
|
@ -4,8 +4,6 @@ import 'dart:async' show runZoned, ZoneSpecification;
|
|||||||
import 'package:quiver/testing/async.dart' as quiver;
|
import 'package:quiver/testing/async.dart' as quiver;
|
||||||
import 'package:angular2/src/facade/exceptions.dart' show BaseException;
|
import 'package:angular2/src/facade/exceptions.dart' show BaseException;
|
||||||
|
|
||||||
import 'test_injector.dart' show getTestInjector, FunctionWithParamTokens;
|
|
||||||
|
|
||||||
const _u = const Object();
|
const _u = const Object();
|
||||||
|
|
||||||
quiver.FakeAsync _fakeAsync = null;
|
quiver.FakeAsync _fakeAsync = null;
|
||||||
@ -22,24 +20,11 @@ quiver.FakeAsync _fakeAsync = null;
|
|||||||
*
|
*
|
||||||
* Returns a `Function` that wraps [fn].
|
* Returns a `Function` that wraps [fn].
|
||||||
*/
|
*/
|
||||||
Function fakeAsync(dynamic /* Function | FunctionWithParamTokens */ fn) {
|
Function fakeAsync(Function fn) {
|
||||||
if (_fakeAsync != null) {
|
if (_fakeAsync != null) {
|
||||||
throw 'fakeAsync() calls can not be nested';
|
throw 'fakeAsync() calls can not be nested';
|
||||||
}
|
}
|
||||||
|
|
||||||
Function innerFn = null;
|
|
||||||
if (fn is FunctionWithParamTokens) {
|
|
||||||
if (fn.isAsync) {
|
|
||||||
throw 'Cannot wrap async test with fakeAsync';
|
|
||||||
}
|
|
||||||
innerFn = () { getTestInjector().execute(fn); };
|
|
||||||
} else if (fn is Function) {
|
|
||||||
innerFn = fn;
|
|
||||||
} else {
|
|
||||||
throw 'fakeAsync can wrap only test functions but got object of type ' +
|
|
||||||
fn.runtimeType.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ([a0 = _u,
|
return ([a0 = _u,
|
||||||
a1 = _u,
|
a1 = _u,
|
||||||
a2 = _u,
|
a2 = _u,
|
||||||
@ -58,7 +43,7 @@ Function fakeAsync(dynamic /* Function | FunctionWithParamTokens */ fn) {
|
|||||||
List args = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]
|
List args = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]
|
||||||
.takeWhile((a) => a != _u)
|
.takeWhile((a) => a != _u)
|
||||||
.toList();
|
.toList();
|
||||||
var res = Function.apply(innerFn, args);
|
var res = Function.apply(fn, args);
|
||||||
_fakeAsync.flushMicrotasks();
|
_fakeAsync.flushMicrotasks();
|
||||||
|
|
||||||
if (async.periodicTimerCount > 0) {
|
if (async.periodicTimerCount > 0) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {getTestInjector, FunctionWithParamTokens} from './test_injector';
|
import {getTestInjector} from './test_injector';
|
||||||
|
|
||||||
let _FakeAsyncTestZoneSpecType = Zone['FakeAsyncTestZoneSpec'];
|
let _FakeAsyncTestZoneSpecType = Zone['FakeAsyncTestZoneSpec'];
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ let _FakeAsyncTestZoneSpecType = Zone['FakeAsyncTestZoneSpec'];
|
|||||||
* @param fn
|
* @param fn
|
||||||
* @returns {Function} The function wrapped to be executed in the fakeAsync zone
|
* @returns {Function} The function wrapped to be executed in the fakeAsync zone
|
||||||
*/
|
*/
|
||||||
export function fakeAsync(fn: Function | FunctionWithParamTokens): Function {
|
export function fakeAsync(fn: Function): Function {
|
||||||
if (Zone.current.get('FakeAsyncTestZoneSpec') != null) {
|
if (Zone.current.get('FakeAsyncTestZoneSpec') != null) {
|
||||||
throw new BaseException('fakeAsync() calls can not be nested');
|
throw new BaseException('fakeAsync() calls can not be nested');
|
||||||
}
|
}
|
||||||
@ -27,20 +27,9 @@ export function fakeAsync(fn: Function | FunctionWithParamTokens): Function {
|
|||||||
let fakeAsyncTestZoneSpec = new _FakeAsyncTestZoneSpecType();
|
let fakeAsyncTestZoneSpec = new _FakeAsyncTestZoneSpecType();
|
||||||
let fakeAsyncZone = Zone.current.fork(fakeAsyncTestZoneSpec);
|
let fakeAsyncZone = Zone.current.fork(fakeAsyncTestZoneSpec);
|
||||||
|
|
||||||
let innerTestFn: Function = null;
|
|
||||||
|
|
||||||
if (fn instanceof FunctionWithParamTokens) {
|
|
||||||
if (fn.isAsync) {
|
|
||||||
throw new BaseException('Cannot wrap async test with fakeAsync');
|
|
||||||
}
|
|
||||||
innerTestFn = () => { getTestInjector().execute(fn as FunctionWithParamTokens); };
|
|
||||||
} else {
|
|
||||||
innerTestFn = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
return function(...args) {
|
return function(...args) {
|
||||||
let res = fakeAsyncZone.run(() => {
|
let res = fakeAsyncZone.run(() => {
|
||||||
let res = innerTestFn(...args);
|
let res = fn(...args);
|
||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,11 @@ import {BaseException, ExceptionHandler} from 'angular2/src/facade/exceptions';
|
|||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {FunctionWrapper, isPresent, Type} from 'angular2/src/facade/lang';
|
import {FunctionWrapper, isPresent, Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
import {async} from './async';
|
||||||
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
|
|
||||||
|
export {async} from './async';
|
||||||
|
|
||||||
export class TestInjector {
|
export class TestInjector {
|
||||||
private _instantiated: boolean = false;
|
private _instantiated: boolean = false;
|
||||||
|
|
||||||
@ -35,15 +40,19 @@ export class TestInjector {
|
|||||||
return this._injector;
|
return this._injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(fn: FunctionWithParamTokens): any {
|
get(token: any) {
|
||||||
var additionalProviders = fn.additionalProviders();
|
|
||||||
if (additionalProviders.length > 0) {
|
|
||||||
this.addProviders(additionalProviders);
|
|
||||||
}
|
|
||||||
if (!this._instantiated) {
|
if (!this._instantiated) {
|
||||||
this.createInjector();
|
this.createInjector();
|
||||||
}
|
}
|
||||||
return fn.execute(this._injector);
|
return this._injector.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(tokens: any[], fn: Function): any {
|
||||||
|
if (!this._instantiated) {
|
||||||
|
this.createInjector();
|
||||||
|
}
|
||||||
|
var params = tokens.map(t => this._injector.get(t));
|
||||||
|
return FunctionWrapper.apply(fn, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,22 +126,47 @@ export function resetBaseTestProviders() {
|
|||||||
*
|
*
|
||||||
* @param {Array} tokens
|
* @param {Array} tokens
|
||||||
* @param {Function} fn
|
* @param {Function} fn
|
||||||
* @return {FunctionWithParamTokens}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
export function inject(tokens: any[], fn: Function): FunctionWithParamTokens {
|
export function inject(tokens: any[], fn: Function): Function {
|
||||||
return new FunctionWithParamTokens(tokens, fn, false);
|
let testInjector = getTestInjector();
|
||||||
|
if (tokens.indexOf(AsyncTestCompleter) >= 0) {
|
||||||
|
// Return an async test method that returns a Promise if AsyncTestCompleter is one of the
|
||||||
|
// injected tokens.
|
||||||
|
return () => {
|
||||||
|
let completer: AsyncTestCompleter = testInjector.get(AsyncTestCompleter);
|
||||||
|
testInjector.execute(tokens, fn);
|
||||||
|
return completer.promise;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Return a synchronous test method with the injected tokens.
|
||||||
|
return () => { return getTestInjector().execute(tokens, fn); };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InjectSetupWrapper {
|
export class InjectSetupWrapper {
|
||||||
constructor(private _providers: () => any) {}
|
constructor(private _providers: () => any) {}
|
||||||
|
|
||||||
inject(tokens: any[], fn: Function): FunctionWithParamTokens {
|
private _addProviders() {
|
||||||
return new FunctionWithParamTokens(tokens, fn, false, this._providers);
|
var additionalProviders = this._providers();
|
||||||
|
if (additionalProviders.length > 0) {
|
||||||
|
getTestInjector().addProviders(additionalProviders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inject(tokens: any[], fn: Function): Function {
|
||||||
|
return () => {
|
||||||
|
this._addProviders();
|
||||||
|
return inject(tokens, fn)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @Deprecated {use async(withProviders().inject())} */
|
/** @Deprecated {use async(withProviders().inject())} */
|
||||||
injectAsync(tokens: any[], fn: Function): FunctionWithParamTokens {
|
injectAsync(tokens: any[], fn: Function): Function {
|
||||||
return new FunctionWithParamTokens(tokens, fn, true, this._providers);
|
return () => {
|
||||||
|
this._addProviders();
|
||||||
|
return injectAsync(tokens, fn)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,53 +192,8 @@ export function withProviders(providers: () => any) {
|
|||||||
*
|
*
|
||||||
* @param {Array} tokens
|
* @param {Array} tokens
|
||||||
* @param {Function} fn
|
* @param {Function} fn
|
||||||
* @return {FunctionWithParamTokens}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
export function injectAsync(tokens: any[], fn: Function): FunctionWithParamTokens {
|
export function injectAsync(tokens: any[], fn: Function): Function {
|
||||||
return new FunctionWithParamTokens(tokens, fn, true);
|
return async(inject(tokens, fn));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps a test function in an asynchronous test zone. The test will automatically
|
|
||||||
* complete when all asynchronous calls within this zone are done. Can be used
|
|
||||||
* to wrap an {@link inject} call.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* it('...', async(inject([AClass], (object) => {
|
|
||||||
* object.doSomething.then(() => {
|
|
||||||
* expect(...);
|
|
||||||
* })
|
|
||||||
* });
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export function async(fn: Function | FunctionWithParamTokens): FunctionWithParamTokens {
|
|
||||||
if (fn instanceof FunctionWithParamTokens) {
|
|
||||||
fn.isAsync = true;
|
|
||||||
return fn;
|
|
||||||
} else if (fn instanceof Function) {
|
|
||||||
return new FunctionWithParamTokens([], fn, true);
|
|
||||||
} else {
|
|
||||||
throw new BaseException('argument to async must be a function or inject(<Function>)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function emptyArray(): Array<any> {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FunctionWithParamTokens {
|
|
||||||
constructor(private _tokens: any[], public fn: Function, public isAsync: boolean,
|
|
||||||
public additionalProviders: () => any = emptyArray) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the executed function.
|
|
||||||
*/
|
|
||||||
execute(injector: ReflectiveInjector): any {
|
|
||||||
var params = this._tokens.map(t => injector.get(t));
|
|
||||||
return FunctionWrapper.apply(this.fn, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasToken(token: any): boolean { return this._tokens.indexOf(token) > -1; }
|
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,9 @@
|
|||||||
* Public Test Library for unit testing Angular2 Applications. Uses the
|
* Public Test Library for unit testing Angular2 Applications. Uses the
|
||||||
* Jasmine framework.
|
* Jasmine framework.
|
||||||
*/
|
*/
|
||||||
import {global} from 'angular2/src/facade/lang';
|
import {global, isPromise} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {bind} from 'angular2/core';
|
|
||||||
|
|
||||||
import {
|
import {inject, async, injectAsync, TestInjector, getTestInjector} from './test_injector';
|
||||||
FunctionWithParamTokens,
|
|
||||||
inject,
|
|
||||||
async,
|
|
||||||
injectAsync,
|
|
||||||
TestInjector,
|
|
||||||
getTestInjector
|
|
||||||
} from './test_injector';
|
|
||||||
|
|
||||||
export {inject, async, injectAsync} from './test_injector';
|
export {inject, async, injectAsync} from './test_injector';
|
||||||
|
|
||||||
@ -73,22 +64,6 @@ export var fdescribe: Function = _global.fdescribe;
|
|||||||
*/
|
*/
|
||||||
export var xdescribe: Function = _global.xdescribe;
|
export var xdescribe: Function = _global.xdescribe;
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature for a synchronous test function (no arguments).
|
|
||||||
*/
|
|
||||||
export type SyncTestFn = () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature for an asynchronous test function which takes a
|
|
||||||
* `done` callback.
|
|
||||||
*/
|
|
||||||
export type AsyncTestFn = (done: () => void) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature for any simple testing function.
|
|
||||||
*/
|
|
||||||
export type AnyTestFn = SyncTestFn | AsyncTestFn | Function;
|
|
||||||
|
|
||||||
var jsmBeforeEach = _global.beforeEach;
|
var jsmBeforeEach = _global.beforeEach;
|
||||||
var jsmIt = _global.it;
|
var jsmIt = _global.it;
|
||||||
var jsmIIt = _global.fit;
|
var jsmIIt = _global.fit;
|
||||||
@ -123,35 +98,27 @@ export function beforeEachProviders(fn): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function runInAsyncTestZone(fnToExecute, finishCallback: Function, failCallback: Function,
|
function _wrapTestFn(fn: Function) {
|
||||||
testName = ''): any {
|
// Wraps a test or beforeEach function to handle synchronous and asynchronous execution.
|
||||||
var AsyncTestZoneSpec = Zone['AsyncTestZoneSpec'];
|
return (done: any) => {
|
||||||
var testZoneSpec = new AsyncTestZoneSpec(finishCallback, failCallback, testName);
|
if (fn.length === 0) {
|
||||||
var testZone = Zone.current.fork(testZoneSpec);
|
let retVal = fn();
|
||||||
return testZone.run(fnToExecute);
|
if (isPromise(retVal)) {
|
||||||
}
|
// Asynchronous test function - wait for completion.
|
||||||
|
(<Promise<any>>retVal).then(done, done.fail);
|
||||||
function _isPromiseLike(input): boolean {
|
|
||||||
return input && !!(input.then);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | AnyTestFn,
|
|
||||||
testTimeOut: number): void {
|
|
||||||
var timeOut = testTimeOut;
|
|
||||||
if (testFn instanceof FunctionWithParamTokens) {
|
|
||||||
let testFnT = testFn;
|
|
||||||
jsmFn(name, (done) => {
|
|
||||||
if (testFnT.isAsync) {
|
|
||||||
runInAsyncTestZone(() => testInjector.execute(testFnT), done, done.fail, name);
|
|
||||||
} else {
|
} else {
|
||||||
testInjector.execute(testFnT);
|
// Synchronous test function - complete immediately.
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
}, timeOut);
|
} else {
|
||||||
} else {
|
// Asynchronous test function that takes "done" as parameter.
|
||||||
// The test case doesn't use inject(). ie `it('test', (done) => { ... }));`
|
fn(done);
|
||||||
jsmFn(name, testFn, timeOut);
|
}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: number): void {
|
||||||
|
jsmFn(name, _wrapTestFn(testFn), testTimeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,27 +132,8 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
|
|||||||
*
|
*
|
||||||
* {@example testing/ts/testing.ts region='beforeEach'}
|
* {@example testing/ts/testing.ts region='beforeEach'}
|
||||||
*/
|
*/
|
||||||
export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
|
export function beforeEach(fn: Function): void {
|
||||||
if (fn instanceof FunctionWithParamTokens) {
|
jsmBeforeEach(_wrapTestFn(fn));
|
||||||
// The test case uses inject(). ie `beforeEach(inject([ClassA], (a) => { ...
|
|
||||||
// }));`
|
|
||||||
let fnT = fn;
|
|
||||||
jsmBeforeEach((done) => {
|
|
||||||
if (fnT.isAsync) {
|
|
||||||
runInAsyncTestZone(() => testInjector.execute(fnT), done, done.fail, 'beforeEach');
|
|
||||||
} else {
|
|
||||||
testInjector.execute(fnT);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// The test case doesn't use inject(). ie `beforeEach((done) => { ... }));`
|
|
||||||
if ((<any>fn).length === 0) {
|
|
||||||
jsmBeforeEach(() => { (<SyncTestFn>fn)(); });
|
|
||||||
} else {
|
|
||||||
jsmBeforeEach((done) => { (<AsyncTestFn>fn)(done); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,8 +148,7 @@ export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
|
|||||||
*
|
*
|
||||||
* {@example testing/ts/testing.ts region='describeIt'}
|
* {@example testing/ts/testing.ts region='describeIt'}
|
||||||
*/
|
*/
|
||||||
export function it(name: string, fn: FunctionWithParamTokens | AnyTestFn,
|
export function it(name: string, fn: Function, timeOut: number = null): void {
|
||||||
timeOut: number = null): void {
|
|
||||||
return _it(jsmIt, name, fn, timeOut);
|
return _it(jsmIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,16 +163,14 @@ export function it(name: string, fn: FunctionWithParamTokens | AnyTestFn,
|
|||||||
*
|
*
|
||||||
* {@example testing/ts/testing.ts region='xit'}
|
* {@example testing/ts/testing.ts region='xit'}
|
||||||
*/
|
*/
|
||||||
export function xit(name: string, fn: FunctionWithParamTokens | AnyTestFn,
|
export function xit(name: string, fn: Function, timeOut: number = null): void {
|
||||||
timeOut: number = null): void {
|
|
||||||
return _it(jsmXIt, name, fn, timeOut);
|
return _it(jsmXIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link fit}.
|
* See {@link fit}.
|
||||||
*/
|
*/
|
||||||
export function iit(name: string, fn: FunctionWithParamTokens | AnyTestFn,
|
export function iit(name: string, fn: Function, timeOut: number = null): void {
|
||||||
timeOut: number = null): void {
|
|
||||||
return _it(jsmIIt, name, fn, timeOut);
|
return _it(jsmIIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +184,6 @@ export function iit(name: string, fn: FunctionWithParamTokens | AnyTestFn,
|
|||||||
*
|
*
|
||||||
* {@example testing/ts/testing.ts region='fit'}
|
* {@example testing/ts/testing.ts region='fit'}
|
||||||
*/
|
*/
|
||||||
export function fit(name: string, fn: FunctionWithParamTokens | AnyTestFn,
|
export function fit(name: string, fn: Function, timeOut: number = null): void {
|
||||||
timeOut: number = null): void {
|
|
||||||
return _it(jsmIIt, name, fn, timeOut);
|
return _it(jsmIIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {global, isFunction, Math} from 'angular2/src/facade/lang';
|
import {global, isPromise, Math} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {provide} from 'angular2/core';
|
import {provide} from 'angular2/core';
|
||||||
|
|
||||||
import {getTestInjector, FunctionWithParamTokens, inject} from './test_injector';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
|
import {getTestInjector, inject} from './test_injector';
|
||||||
import {browserDetection} from './utils';
|
import {browserDetection} from './utils';
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
|
||||||
|
|
||||||
|
export {AsyncTestCompleter} from './async_test_completer';
|
||||||
export {inject} from './test_injector';
|
export {inject} from './test_injector';
|
||||||
|
|
||||||
export {expect, NgMatchers} from './matchers';
|
export {expect, NgMatchers} from './matchers';
|
||||||
@ -17,19 +18,6 @@ var _global = <any>(typeof window === 'undefined' ? global : window);
|
|||||||
|
|
||||||
export var afterEach: Function = _global.afterEach;
|
export var afterEach: Function = _global.afterEach;
|
||||||
|
|
||||||
export type SyncTestFn = () => void;
|
|
||||||
type AsyncTestFn = (done: () => void) => void;
|
|
||||||
type AnyTestFn = SyncTestFn | AsyncTestFn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injectable completer that allows signaling completion of an asynchronous test. Used internally.
|
|
||||||
*/
|
|
||||||
export class AsyncTestCompleter {
|
|
||||||
constructor(private _done: Function) {}
|
|
||||||
|
|
||||||
done(): void { this._done(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
var jsmBeforeEach = _global.beforeEach;
|
var jsmBeforeEach = _global.beforeEach;
|
||||||
var jsmDescribe = _global.describe;
|
var jsmDescribe = _global.describe;
|
||||||
var jsmDDescribe = _global.fdescribe;
|
var jsmDDescribe = _global.fdescribe;
|
||||||
@ -51,18 +39,15 @@ var testInjector = getTestInjector();
|
|||||||
* Note: Jasmine own `beforeEach` is used by this library to handle DI providers.
|
* Note: Jasmine own `beforeEach` is used by this library to handle DI providers.
|
||||||
*/
|
*/
|
||||||
class BeforeEachRunner {
|
class BeforeEachRunner {
|
||||||
private _fns: Array<FunctionWithParamTokens | SyncTestFn> = [];
|
private _fns: Array<Function> = [];
|
||||||
|
|
||||||
constructor(private _parent: BeforeEachRunner) {}
|
constructor(private _parent: BeforeEachRunner) {}
|
||||||
|
|
||||||
beforeEach(fn: FunctionWithParamTokens | SyncTestFn): void { this._fns.push(fn); }
|
beforeEach(fn: Function): void { this._fns.push(fn); }
|
||||||
|
|
||||||
run(): void {
|
run(): void {
|
||||||
if (this._parent) this._parent.run();
|
if (this._parent) this._parent.run();
|
||||||
this._fns.forEach((fn) => {
|
this._fns.forEach((fn) => { fn(); });
|
||||||
return isFunction(fn) ? (<SyncTestFn>fn)() :
|
|
||||||
(testInjector.execute(<FunctionWithParamTokens>fn));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,13 +75,13 @@ export function xdescribe(...args): void {
|
|||||||
return _describe(jsmXDescribe, ...args);
|
return _describe(jsmXDescribe, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function beforeEach(fn: FunctionWithParamTokens | SyncTestFn): void {
|
export function beforeEach(fn: Function): void {
|
||||||
if (runnerStack.length > 0) {
|
if (runnerStack.length > 0) {
|
||||||
// Inside a describe block, beforeEach() uses a BeforeEachRunner
|
// Inside a describe block, beforeEach() uses a BeforeEachRunner
|
||||||
runnerStack[runnerStack.length - 1].beforeEach(fn);
|
runnerStack[runnerStack.length - 1].beforeEach(fn);
|
||||||
} else {
|
} else {
|
||||||
// Top level beforeEach() are delegated to jasmine
|
// Top level beforeEach() are delegated to jasmine
|
||||||
jsmBeforeEach(<SyncTestFn>fn);
|
jsmBeforeEach(fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,55 +112,37 @@ export function beforeEachBindings(fn): void {
|
|||||||
beforeEachProviders(fn);
|
beforeEachProviders(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | AnyTestFn,
|
function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: number): void {
|
||||||
testTimeOut: number): void {
|
|
||||||
var runner = runnerStack[runnerStack.length - 1];
|
var runner = runnerStack[runnerStack.length - 1];
|
||||||
var timeOut = Math.max(globalTimeOut, testTimeOut);
|
var timeOut = Math.max(globalTimeOut, testTimeOut);
|
||||||
|
|
||||||
if (testFn instanceof FunctionWithParamTokens) {
|
jsmFn(name, (done) => {
|
||||||
// The test case uses inject(). ie `it('test', inject([AsyncTestCompleter], (async) => { ...
|
var completerProvider = provide(AsyncTestCompleter, {
|
||||||
// }));`
|
useFactory: () => {
|
||||||
let testFnT = testFn;
|
// 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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testInjector.addProviders([completerProvider]);
|
||||||
|
runner.run();
|
||||||
|
|
||||||
if (testFn.hasToken(AsyncTestCompleter)) {
|
inIt = true;
|
||||||
jsmFn(name, (done) => {
|
if (testFn.length == 0) {
|
||||||
var completerProvider = provide(AsyncTestCompleter, {
|
let retVal = testFn();
|
||||||
useFactory: () => {
|
if (isPromise(retVal)) {
|
||||||
// Mark the test as async when an AsyncTestCompleter is injected in an it()
|
// Asynchronous test function that returns a Promise - wait for completion.
|
||||||
if (!inIt) throw new Error('AsyncTestCompleter can only be injected in an "it()"');
|
(<Promise<any>>retVal).then(done, done.fail);
|
||||||
return new AsyncTestCompleter(done);
|
} else {
|
||||||
}
|
// Synchronous test function - complete immediately.
|
||||||
});
|
done();
|
||||||
|
}
|
||||||
testInjector.addProviders([completerProvider]);
|
|
||||||
runner.run();
|
|
||||||
|
|
||||||
inIt = true;
|
|
||||||
testInjector.execute(testFnT);
|
|
||||||
inIt = false;
|
|
||||||
}, timeOut);
|
|
||||||
} else {
|
} else {
|
||||||
jsmFn(name, () => {
|
// Asynchronous test function that takes in 'done' parameter.
|
||||||
runner.run();
|
testFn(done);
|
||||||
testInjector.execute(testFnT);
|
|
||||||
}, timeOut);
|
|
||||||
}
|
}
|
||||||
|
inIt = false;
|
||||||
} else {
|
}, timeOut);
|
||||||
// The test case doesn't use inject(). ie `it('test', (done) => { ... }));`
|
|
||||||
|
|
||||||
if ((<any>testFn).length === 0) {
|
|
||||||
jsmFn(name, () => {
|
|
||||||
runner.run();
|
|
||||||
(<SyncTestFn>testFn)();
|
|
||||||
}, timeOut);
|
|
||||||
} else {
|
|
||||||
jsmFn(name, (done) => {
|
|
||||||
runner.run();
|
|
||||||
(<AsyncTestFn>testFn)(done);
|
|
||||||
}, timeOut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function it(name, fn, timeOut = null): void {
|
export function it(name, fn, timeOut = null): void {
|
||||||
@ -190,7 +157,6 @@ export function iit(name, fn, timeOut = null): void {
|
|||||||
return _it(jsmIIt, name, fn, timeOut);
|
return _it(jsmIIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface GuinessCompatibleSpy extends jasmine.Spy {
|
export interface GuinessCompatibleSpy extends jasmine.Spy {
|
||||||
/** By chaining the spy with and.returnValue, all calls to the function will return a specific
|
/** By chaining the spy with and.returnValue, all calls to the function will return a specific
|
||||||
* value. */
|
* value. */
|
||||||
|
@ -26,31 +26,18 @@ import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
|
|||||||
import 'package:angular2/src/core/di/provider.dart' show bind;
|
import 'package:angular2/src/core/di/provider.dart' show bind;
|
||||||
import 'package:angular2/src/facade/collection.dart' show StringMapWrapper;
|
import 'package:angular2/src/facade/collection.dart' show StringMapWrapper;
|
||||||
|
|
||||||
|
import 'async_test_completer.dart';
|
||||||
|
export 'async_test_completer.dart' show AsyncTestCompleter;
|
||||||
|
|
||||||
import 'test_injector.dart';
|
import 'test_injector.dart';
|
||||||
export 'test_injector.dart' show inject;
|
export 'test_injector.dart' show inject;
|
||||||
|
|
||||||
TestInjector _testInjector = getTestInjector();
|
TestInjector _testInjector = getTestInjector();
|
||||||
bool _isCurrentTestAsync;
|
|
||||||
Future _currentTestFuture;
|
|
||||||
bool _inIt = false;
|
bool _inIt = false;
|
||||||
bool _initialized = false;
|
bool _initialized = false;
|
||||||
List<dynamic> _platformProviders = [];
|
List<dynamic> _platformProviders = [];
|
||||||
List<dynamic> _applicationProviders = [];
|
List<dynamic> _applicationProviders = [];
|
||||||
|
|
||||||
class AsyncTestCompleter {
|
|
||||||
final _completer = new Completer();
|
|
||||||
|
|
||||||
AsyncTestCompleter() {
|
|
||||||
_currentTestFuture = this.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
void done() {
|
|
||||||
_completer.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future get future => _completer.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDartBaseTestProviders(List<dynamic> platform, List<dynamic> application) {
|
void setDartBaseTestProviders(List<dynamic> platform, List<dynamic> application) {
|
||||||
_platformProviders = platform;
|
_platformProviders = platform;
|
||||||
_applicationProviders = application;
|
_applicationProviders = application;
|
||||||
@ -70,18 +57,15 @@ void testSetup() {
|
|||||||
|
|
||||||
gns.beforeEach(() {
|
gns.beforeEach(() {
|
||||||
_testInjector.reset();
|
_testInjector.reset();
|
||||||
_currentTestFuture = null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var completerProvider = bind(AsyncTestCompleter).toFactory(() {
|
var completerProvider = bind(AsyncTestCompleter).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 'AsyncTestCompleter can only be injected in an "it()"';
|
if (!_inIt) throw 'AsyncTestCompleter can only be injected in an "it()"';
|
||||||
_isCurrentTestAsync = true;
|
|
||||||
return new AsyncTestCompleter();
|
return new AsyncTestCompleter();
|
||||||
});
|
});
|
||||||
|
|
||||||
gns.beforeEach(() {
|
gns.beforeEach(() {
|
||||||
_isCurrentTestAsync = false;
|
|
||||||
_testInjector.addProviders([completerProvider]);
|
_testInjector.addProviders([completerProvider]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -113,22 +97,16 @@ void beforeEachBindings(Function fn) {
|
|||||||
|
|
||||||
void beforeEach(fn) {
|
void beforeEach(fn) {
|
||||||
testSetup();
|
testSetup();
|
||||||
if (fn is! FunctionWithParamTokens) fn =
|
gns.beforeEach(fn);
|
||||||
new FunctionWithParamTokens([], fn, false);
|
|
||||||
gns.beforeEach(() {
|
|
||||||
_testInjector.execute(fn);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _it(gnsFn, name, fn) {
|
void _it(gnsFn, name, fn) {
|
||||||
testSetup();
|
testSetup();
|
||||||
if (fn is! FunctionWithParamTokens) fn =
|
|
||||||
new FunctionWithParamTokens([], fn, false);
|
|
||||||
gnsFn(name, () {
|
gnsFn(name, () {
|
||||||
_inIt = true;
|
_inIt = true;
|
||||||
_testInjector.execute(fn);
|
var retVal = fn();
|
||||||
_inIt = false;
|
_inIt = false;
|
||||||
if (_isCurrentTestAsync) return _currentTestFuture;
|
return retVal;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +150,19 @@ export function main() {
|
|||||||
expect(value).toEqual('async value');
|
expect(value).toEqual('async value');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should allow use of "done"', (done) => {
|
||||||
|
inject([FancyService], (service) => {
|
||||||
|
let count = 0;
|
||||||
|
let id = setInterval(() => {
|
||||||
|
count++;
|
||||||
|
if (count > 2) {
|
||||||
|
clearInterval(id);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}, 5);
|
||||||
|
})(); // inject needs to be invoked explicitly with ().
|
||||||
|
});
|
||||||
|
|
||||||
describe('using beforeEach', () => {
|
describe('using beforeEach', () => {
|
||||||
beforeEach(inject([FancyService],
|
beforeEach(inject([FancyService],
|
||||||
(service) => { service.value = 'value modified in beforeEach'; }));
|
(service) => { service.value = 'value modified in beforeEach'; }));
|
||||||
@ -174,6 +187,15 @@ export function main() {
|
|||||||
withProviders(() => [bind(FancyService).toValue(new FancyService())])
|
withProviders(() => [bind(FancyService).toValue(new FancyService())])
|
||||||
.inject([FancyService],
|
.inject([FancyService],
|
||||||
(service) => { expect(service.value).toEqual('real value'); }));
|
(service) => { expect(service.value).toEqual('real value'); }));
|
||||||
|
|
||||||
|
it('should return value from inject', () => {
|
||||||
|
let retval = withProviders(() => [bind(FancyService).toValue(new FancyService())])
|
||||||
|
.inject([FancyService], (service) => {
|
||||||
|
expect(service.value).toEqual('real value');
|
||||||
|
return 10;
|
||||||
|
})();
|
||||||
|
expect(retval).toBe(10);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,8 +79,7 @@ dynamic _runInjectableFunction(Function fn) {
|
|||||||
tokens.add(token);
|
tokens.add(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
var injectFn = new FunctionWithParamTokens(tokens, fn, false);
|
return _testInjector.execute(tokens, fn);
|
||||||
return _testInjector.execute(injectFn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use the test injector to get bindings and run a function.
|
/// Use the test injector to get bindings and run a function.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user