feat(tests): manage asynchronous tests using zones
Instead of using injectAsync and returning a promise, use the `async` function
to wrap tests. This will run the test inside a zone which does not complete
the test until all asynchronous tasks have been completed.
`async` may be used with the `inject` function, or separately.
BREAKING CHANGE:
`injectAsync` is now deprecated. Instead, use the `async` function
to wrap any asynchronous tests.
Before:
```
it('should wait for returned promises', injectAsync([FancyService], (service) => {
return service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
}));
it('should wait for returned promises', injectAsync([], () => {
return somePromise.then(() => { expect(true).toEqual(true); });
}));
```
After:
```
it('should wait for returned promises', async(inject([FancyService], (service) => {
service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
})));
// Note that if there is no injection, we no longer need `inject` OR `injectAsync`.
it('should wait for returned promises', async(() => {
somePromise.then() => { expect(true).toEqual(true); });
}));
```
Closes #7735
This commit is contained in:
committed by
Robert Messerle
parent
ecb9bb96f0
commit
8490921fb3
@@ -130,6 +130,7 @@ export class InjectSetupWrapper {
|
||||
return new FunctionWithParamTokens(tokens, fn, false, this._providers);
|
||||
}
|
||||
|
||||
/** @Deprecated {use async(withProviders().inject())} */
|
||||
injectAsync(tokens: any[], fn: Function): FunctionWithParamTokens {
|
||||
return new FunctionWithParamTokens(tokens, fn, true, this._providers);
|
||||
}
|
||||
@@ -140,6 +141,8 @@ export function withProviders(providers: () => any) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @Deprecated {use async(inject())}
|
||||
*
|
||||
* Allows injecting dependencies in `beforeEach()` and `it()`. The test must return
|
||||
* a promise which will resolve when all asynchronous activity is complete.
|
||||
*
|
||||
@@ -161,6 +164,32 @@ export function injectAsync(tokens: any[], fn: Function): FunctionWithParamToken
|
||||
return new FunctionWithParamTokens(tokens, fn, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [];
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ import {bind} from 'angular2/core';
|
||||
import {
|
||||
FunctionWithParamTokens,
|
||||
inject,
|
||||
async,
|
||||
injectAsync,
|
||||
TestInjector,
|
||||
getTestInjector
|
||||
} from './test_injector';
|
||||
|
||||
export {inject, injectAsync} from './test_injector';
|
||||
export {inject, async, injectAsync} from './test_injector';
|
||||
|
||||
export {expect, NgMatchers} from './matchers';
|
||||
|
||||
@@ -122,6 +123,14 @@ export function beforeEachProviders(fn): void {
|
||||
});
|
||||
}
|
||||
|
||||
function runInAsyncTestZone(fnToExecute, finishCallback: Function, failCallback: Function,
|
||||
testName = ''): any {
|
||||
var AsyncTestZoneSpec = Zone['AsyncTestZoneSpec'];
|
||||
var testZoneSpec = new AsyncTestZoneSpec(finishCallback, failCallback, testName);
|
||||
var testZone = Zone.current.fork(testZoneSpec);
|
||||
return testZone.run(fnToExecute);
|
||||
}
|
||||
|
||||
function _isPromiseLike(input): boolean {
|
||||
return input && !!(input.then);
|
||||
}
|
||||
@@ -129,30 +138,13 @@ function _isPromiseLike(input): boolean {
|
||||
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) => {
|
||||
var returnedTestValue;
|
||||
try {
|
||||
returnedTestValue = testInjector.execute(testFnT);
|
||||
} catch (err) {
|
||||
done.fail(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (testFnT.isAsync) {
|
||||
if (_isPromiseLike(returnedTestValue)) {
|
||||
(<Promise<any>>returnedTestValue).then(() => { done(); }, (err) => { done.fail(err); });
|
||||
} else {
|
||||
done.fail('Error: injectAsync was expected to return a promise, but the ' +
|
||||
' returned value was: ' + returnedTestValue);
|
||||
}
|
||||
runInAsyncTestZone(() => testInjector.execute(testFnT), done, done.fail, name);
|
||||
} else {
|
||||
if (!(returnedTestValue === undefined)) {
|
||||
done.fail('Error: inject returned a value. Did you mean to use injectAsync? Returned ' +
|
||||
'value was: ' + returnedTestValue);
|
||||
}
|
||||
testInjector.execute(testFnT);
|
||||
done();
|
||||
}
|
||||
}, timeOut);
|
||||
@@ -166,8 +158,6 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
|
||||
* Wrapper around Jasmine beforeEach function.
|
||||
*
|
||||
* beforeEach may be used with the `inject` function to fetch dependencies.
|
||||
* The test will automatically wait for any asynchronous calls inside the
|
||||
* injected test function to complete.
|
||||
*
|
||||
* See http://jasmine.github.io/ for more details.
|
||||
*
|
||||
@@ -181,26 +171,10 @@ export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
|
||||
// }));`
|
||||
let fnT = fn;
|
||||
jsmBeforeEach((done) => {
|
||||
|
||||
var returnedTestValue;
|
||||
try {
|
||||
returnedTestValue = testInjector.execute(fnT);
|
||||
} catch (err) {
|
||||
done.fail(err);
|
||||
return;
|
||||
}
|
||||
if (fnT.isAsync) {
|
||||
if (_isPromiseLike(returnedTestValue)) {
|
||||
(<Promise<any>>returnedTestValue).then(() => { done(); }, (err) => { done.fail(err); });
|
||||
} else {
|
||||
done.fail('Error: injectAsync was expected to return a promise, but the ' +
|
||||
' returned value was: ' + returnedTestValue);
|
||||
}
|
||||
runInAsyncTestZone(() => testInjector.execute(fnT), done, done.fail, 'beforeEach');
|
||||
} else {
|
||||
if (!(returnedTestValue === undefined)) {
|
||||
done.fail('Error: inject returned a value. Did you mean to use injectAsync? Returned ' +
|
||||
'value was: ' + returnedTestValue);
|
||||
}
|
||||
testInjector.execute(fnT);
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -217,10 +191,8 @@ export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
|
||||
/**
|
||||
* Define a single test case with the given test name and execution function.
|
||||
*
|
||||
* The test function can be either a synchronous function, an asynchronous function
|
||||
* that takes a completion callback, or an injected function created via {@link inject}
|
||||
* or {@link injectAsync}. The test will automatically wait for any asynchronous calls
|
||||
* inside the injected test function to complete.
|
||||
* The test function can be either a synchronous function, the result of {@link async},
|
||||
* or an injected function created via {@link inject}.
|
||||
*
|
||||
* Wrapper around Jasmine it function. See http://jasmine.github.io/ for more details.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user