parent
6c6bc95ac0
commit
ccceff5ecc
|
@ -1,8 +1,7 @@
|
||||||
// tslint:disable-next-line:no-unused-variable
|
// tslint:disable-next-line:no-unused-variable
|
||||||
import { async, fakeAsync, tick } from '@angular/core/testing';
|
import { async, fakeAsync, tick } from '@angular/core/testing';
|
||||||
|
import { interval, of } from 'rxjs';
|
||||||
import { of } from 'rxjs';
|
import { delay, take } from 'rxjs/operators';
|
||||||
import { delay } from 'rxjs/operators';
|
|
||||||
|
|
||||||
describe('Angular async helper', () => {
|
describe('Angular async helper', () => {
|
||||||
let actuallyDone = false;
|
let actuallyDone = false;
|
||||||
|
@ -23,6 +22,13 @@ describe('Angular async helper', () => {
|
||||||
it('should run async test with task',
|
it('should run async test with task',
|
||||||
async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
|
async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
|
||||||
|
|
||||||
|
it('should run async test with task', async(() => {
|
||||||
|
const id = setInterval(() => {
|
||||||
|
actuallyDone = true;
|
||||||
|
clearInterval(id);
|
||||||
|
}, 100);
|
||||||
|
}));
|
||||||
|
|
||||||
it('should run async test with successful promise', async(() => {
|
it('should run async test with successful promise', async(() => {
|
||||||
const p = new Promise(resolve => { setTimeout(resolve, 10); });
|
const p = new Promise(resolve => { setTimeout(resolve, 10); });
|
||||||
p.then(() => { actuallyDone = true; });
|
p.then(() => { actuallyDone = true; });
|
||||||
|
@ -33,37 +39,101 @@ describe('Angular async helper', () => {
|
||||||
p.catch(() => { actuallyDone = true; });
|
p.catch(() => { actuallyDone = true; });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Use done. Cannot use setInterval with async or fakeAsync
|
// Use done. Can also use async or fakeAsync.
|
||||||
// See https://github.com/angular/angular/issues/10127
|
|
||||||
it('should run async test with successful delayed Observable', (done: DoneFn) => {
|
it('should run async test with successful delayed Observable', (done: DoneFn) => {
|
||||||
const source = of(true).pipe(delay(10));
|
const source = of (true).pipe(delay(10));
|
||||||
source.subscribe(
|
source.subscribe(val => actuallyDone = true, err => fail(err), done);
|
||||||
val => actuallyDone = true,
|
|
||||||
err => fail(err),
|
|
||||||
done
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cannot use setInterval from within an async zone test
|
// #docregion fake-async-test-tick
|
||||||
// See https://github.com/angular/angular/issues/10127
|
it('should run timeout callback with delay after call tick with millis', fakeAsync(() => {
|
||||||
// xit('should run async test with successful delayed Observable', async(() => {
|
let called = false;
|
||||||
// const source = of(true).pipe(delay(10));
|
setTimeout(() => { called = true; }, 100);
|
||||||
// source.subscribe(
|
tick(100);
|
||||||
// val => actuallyDone = true,
|
expect(called).toBe(true);
|
||||||
// err => fail(err)
|
}));
|
||||||
// );
|
// #enddocregion fake-async-test-tick
|
||||||
// }));
|
|
||||||
|
|
||||||
// // Fail message: Error: 1 periodic timer(s) still in the queue
|
// #docregion fake-async-test-date
|
||||||
// // See https://github.com/angular/angular/issues/10127
|
it('should get Date diff correctly in fakeAsync', fakeAsync(() => {
|
||||||
// xit('should run async test with successful delayed Observable', fakeAsync(() => {
|
const start = Date.now();
|
||||||
// const source = of(true).pipe(delay(10));
|
tick(100);
|
||||||
// source.subscribe(
|
const end = Date.now();
|
||||||
// val => actuallyDone = true,
|
expect(end - start).toBe(100);
|
||||||
// err => fail(err)
|
}));
|
||||||
// );
|
// #enddocregion fake-async-test-date
|
||||||
|
|
||||||
// tick();
|
// #docregion fake-async-test-rxjs
|
||||||
// }));
|
it('should get Date diff correctly in fakeAsync with rxjs scheduler', fakeAsync(() => {
|
||||||
|
// need to add `import 'zone.js/dist/zone-patch-rxjs-fake-async'
|
||||||
|
// to patch rxjs scheduler
|
||||||
|
let result = null;
|
||||||
|
of ('hello').pipe(delay(1000)).subscribe(v => { result = v; });
|
||||||
|
expect(result).toBeNull();
|
||||||
|
tick(1000);
|
||||||
|
expect(result).toBe('hello');
|
||||||
|
|
||||||
|
const start = new Date().getTime();
|
||||||
|
let dateDiff = 0;
|
||||||
|
interval(1000).pipe(take(2)).subscribe(() => dateDiff = (new Date().getTime() - start));
|
||||||
|
|
||||||
|
tick(1000);
|
||||||
|
expect(dateDiff).toBe(1000);
|
||||||
|
tick(1000);
|
||||||
|
expect(dateDiff).toBe(2000);
|
||||||
|
}));
|
||||||
|
// #enddocregion fake-async-test-rxjs
|
||||||
|
|
||||||
|
// #docregion fake-async-test-clock
|
||||||
|
describe('use jasmine.clock()', () => {
|
||||||
|
// need to config __zone_symbol__fakeAsyncPatchLock flag
|
||||||
|
// before loading zone.js/dist/zone-testing
|
||||||
|
beforeEach(() => { jasmine.clock().install(); });
|
||||||
|
afterEach(() => { jasmine.clock().uninstall(); });
|
||||||
|
it('should auto enter fakeAsync', () => {
|
||||||
|
// is in fakeAsync now, don't need to call fakeAsync(testFn)
|
||||||
|
let called = false;
|
||||||
|
setTimeout(() => { called = true; }, 100);
|
||||||
|
jasmine.clock().tick(100);
|
||||||
|
expect(called).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// #enddocregion fake-async-test-clock
|
||||||
|
|
||||||
|
// #docregion async-test-promise-then
|
||||||
|
describe('test jsonp', () => {
|
||||||
|
function jsonp(url: string, callback: Function) {
|
||||||
|
// do a jsonp call which is not zone aware
|
||||||
|
}
|
||||||
|
// need to config __zone_symbol__supportWaitUnResolvedChainedPromise flag
|
||||||
|
// before loading zone.js/dist/zone-testing
|
||||||
|
it('should wait until promise.then is called', async(() => {
|
||||||
|
let finished = false;
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
jsonp('localhost:8080/jsonp', () => {
|
||||||
|
// success callback and resolve the promise
|
||||||
|
finished = true;
|
||||||
|
res();
|
||||||
|
});
|
||||||
|
}).then(() => {
|
||||||
|
// async will wait until promise.then is called
|
||||||
|
// if __zone_symbol__supportWaitUnResolvedChainedPromise is set
|
||||||
|
expect(finished).toBe(true);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
// #enddocregion async-test-promise-then
|
||||||
|
|
||||||
|
it('should run async test with successful delayed Observable', async(() => {
|
||||||
|
const source = of (true).pipe(delay(10));
|
||||||
|
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should run async test with successful delayed Observable', fakeAsync(() => {
|
||||||
|
const source = of (true).pipe(delay(10));
|
||||||
|
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||||
|
|
||||||
|
tick(10);
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -356,3 +356,24 @@ If you develop angular locally with `ng serve`, there will be `websocket` connec
|
||||||
|
|
||||||
In windows, by default one application can only have 6 websocket connections, <a href="https://msdn.microsoft.com/library/ee330736%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#websocket_maxconn" title="MSDN WebSocket settings">MSDN WebSocket Settings</a>.
|
In windows, by default one application can only have 6 websocket connections, <a href="https://msdn.microsoft.com/library/ee330736%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#websocket_maxconn" title="MSDN WebSocket settings">MSDN WebSocket Settings</a>.
|
||||||
So if IE was refreshed manunally or automatically by `ng serve`, sometimes, the websocket will not close properly, when websocket connections exceed limitations, `SecurityError` will be thrown, this error will not affect the angular application, you can just restart IE to clear this error, or modify the windows registry to update the limitations.
|
So if IE was refreshed manunally or automatically by `ng serve`, sometimes, the websocket will not close properly, when websocket connections exceed limitations, `SecurityError` will be thrown, this error will not affect the angular application, you can just restart IE to clear this error, or modify the windows registry to update the limitations.
|
||||||
|
|
||||||
|
## Appendix: test using `fakeAsync()/async()`
|
||||||
|
|
||||||
|
If you use the `fakeAsync()/async()` helper function to run unit tests (for details, read [testing guide](guide/testing#async-test-with-fakeasync)), you need to import `zone.js/dist/zone-testing` in your test setup file.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
If you create project with `Angular/CLI`, it is already imported in `src/test.ts`.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
And in the earlier versions of `Angular`, the following files were imported or added in your html file:
|
||||||
|
|
||||||
|
```
|
||||||
|
import 'zone.js/dist/long-stack-trace-zone';
|
||||||
|
import 'zone.js/dist/proxy';
|
||||||
|
import 'zone.js/dist/sync-test';
|
||||||
|
import 'zone.js/dist/jasmine-patch';
|
||||||
|
import 'zone.js/dist/async-test';
|
||||||
|
import 'zone.js/dist/fake-async-test';
|
||||||
|
```
|
||||||
|
|
||||||
|
You can still load those files separately, but the order is important, you must import `proxy` before `sync-test`, `async-test`, `fake-async-test` and `jasmine-patch`. And you also need to import `sync-test` before `jasmine-patch`, so it is recommended to just import `zone-testing` instead of loading those separated files.
|
|
@ -1237,6 +1237,8 @@ value becomes available. The test must become _asynchronous_.
|
||||||
|
|
||||||
#### Async test with _fakeAsync()_
|
#### Async test with _fakeAsync()_
|
||||||
|
|
||||||
|
To use `fakeAsync()` functionality, you need to import `zone-testing`, for details, please read [setup guide](guide/setup#appendix-test-using-fakeasyncasync).
|
||||||
|
|
||||||
The following test confirms the expected behavior when the service returns an `ErrorObservable`.
|
The following test confirms the expected behavior when the service returns an `ErrorObservable`.
|
||||||
|
|
||||||
<code-example
|
<code-example
|
||||||
|
@ -1250,7 +1252,7 @@ Note that the `it()` function receives an argument of the following form.
|
||||||
fakeAsync(() => { /* test body */ })`
|
fakeAsync(() => { /* test body */ })`
|
||||||
```
|
```
|
||||||
|
|
||||||
The `fakeAsync` function enables a linear coding style by running the test body in a special _fakeAsync test zone_.
|
The `fakeAsync()` function enables a linear coding style by running the test body in a special `fakeAsync test zone`.
|
||||||
The test body appears to be synchronous.
|
The test body appears to be synchronous.
|
||||||
There is no nested syntax (like a `Promise.then()`) to disrupt the flow of control.
|
There is no nested syntax (like a `Promise.then()`) to disrupt the flow of control.
|
||||||
|
|
||||||
|
@ -1263,12 +1265,55 @@ You do have to call `tick()` to advance the (virtual) clock.
|
||||||
Calling `tick()` simulates the passage of time until all pending asynchronous activities finish.
|
Calling `tick()` simulates the passage of time until all pending asynchronous activities finish.
|
||||||
In this case, it waits for the error handler's `setTimeout()`;
|
In this case, it waits for the error handler's `setTimeout()`;
|
||||||
|
|
||||||
The `tick` function is one of the Angular testing utilities that you import with `TestBed`.
|
The `tick()` function accepts milliseconds as parameter (defaults to 0 if not provided). The parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback.
|
||||||
It's a companion to `fakeAsync` and you can only call it within a `fakeAsync` body.
|
|
||||||
|
<code-example
|
||||||
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
region="fake-async-test-tick">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
The `tick()` function is one of the Angular testing utilities that you import with `TestBed`.
|
||||||
|
It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body.
|
||||||
|
|
||||||
|
#### Comparing dates inside fakeAsync()
|
||||||
|
|
||||||
|
`fakeAsync()` simulates passage of time, which allows you to calculate the difference between dates inside `fakeAsync()`.
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
region="fake-async-test-date">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
#### jasmine.clock with fakeAsync()
|
||||||
|
|
||||||
|
Jasmine also provides a `clock` feature to mock dates. Angular automatically runs tests that are run after
|
||||||
|
`jasmine.clock().install()` is called inside a `fakeAsync()` method until `jasmine.clock().uninstall()` is called. `fakeAsync()` is not needed and throws an error if nested.
|
||||||
|
|
||||||
|
By default, this feature is disabled. To enable it, set a global flag before import `zone-testing`.
|
||||||
|
|
||||||
|
If you use the Angular CLI, configure this flag in `src/test.ts`.
|
||||||
|
|
||||||
|
```
|
||||||
|
(window as any)['__zone_symbol__fakeAsyncPatchLock'] = true;
|
||||||
|
import 'zone.js/dist/zone-testing';
|
||||||
|
```
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
region="fake-async-test-clock">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
#### Using the RxJS scheduler inside fakeAsync()
|
||||||
|
|
||||||
|
You can also use RxJS scheduler in `fakeAsync()` just like using `setTimeout()` or `setInterval()`, but you need to import `zone.js/dist/zone-patch-rxjs-fake-async` to patch RxJS scheduler.
|
||||||
|
<code-example
|
||||||
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
region="fake-async-test-rxjs">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
#### Support more macroTasks
|
#### Support more macroTasks
|
||||||
|
|
||||||
By default `fakeAsync` supports the following `macroTasks`.
|
By default `fakeAsync()` supports the following `macroTasks`.
|
||||||
|
|
||||||
- setTimeout
|
- setTimeout
|
||||||
- setInterval
|
- setInterval
|
||||||
|
@ -1289,7 +1334,7 @@ If you run other `macroTask` such as `HTMLCanvasElement.toBlob()`, `Unknown macr
|
||||||
</code-pane>
|
</code-pane>
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
If you want to support such case, you need to define the `macroTask` you want to support in `beforeEach`.
|
If you want to support such case, you need to define the `macroTask` you want to support in `beforeEach()`.
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
@ -1386,6 +1431,8 @@ Then you can assert that the quote element displays the expected text.
|
||||||
|
|
||||||
#### Async test with _async()_
|
#### Async test with _async()_
|
||||||
|
|
||||||
|
To use `async()` functionality, you need to import `zone-testing`, for details, please read [setup guide](guide/setup#appendix-test-using-fakeasyncasync).
|
||||||
|
|
||||||
The `fakeAsync()` utility function has a few limitations.
|
The `fakeAsync()` utility function has a few limitations.
|
||||||
In particular, it won't work if the test body makes an `XHR` call.
|
In particular, it won't work if the test body makes an `XHR` call.
|
||||||
|
|
||||||
|
@ -1409,12 +1456,13 @@ Here's the previous `fakeAsync()` test, re-written with the `async()` utility.
|
||||||
|
|
||||||
The `async()` utility hides some asynchronous boilerplate by arranging for the tester's code
|
The `async()` utility hides some asynchronous boilerplate by arranging for the tester's code
|
||||||
to run in a special _async test zone_.
|
to run in a special _async test zone_.
|
||||||
You don't have to pass Jasmine's `done()` into the test and call `done()`
|
You don't need to pass Jasmine's `done()` into the test and call `done()` because it is `undefined` in promise or observable callbacks.
|
||||||
in promise or observable callbacks.
|
|
||||||
|
|
||||||
But the test's asynchronous nature is revealed by the call to `fixture.whenStable()`,
|
But the test's asynchronous nature is revealed by the call to `fixture.whenStable()`,
|
||||||
which breaks the linear flow of control.
|
which breaks the linear flow of control.
|
||||||
|
|
||||||
|
When using an `intervalTimer()` such as `setInterval()` in `async()`, remember to cancel the timer with `clearInterval()` after the test, otherwise the `async()` never ends.
|
||||||
|
|
||||||
{@a when-stable}
|
{@a when-stable}
|
||||||
|
|
||||||
#### _whenStable_
|
#### _whenStable_
|
||||||
|
@ -1433,18 +1481,19 @@ update the quote element with the expected text.
|
||||||
|
|
||||||
#### Jasmine _done()_
|
#### Jasmine _done()_
|
||||||
|
|
||||||
While the `async` and `fakeAsync` functions greatly
|
While the `async()` and `fakeAsync()` functions greatly
|
||||||
simplify Angular asynchronous testing,
|
simplify Angular asynchronous testing,
|
||||||
you can still fall back to the traditional technique
|
you can still fall back to the traditional technique
|
||||||
and pass `it` a function that takes a
|
and pass `it` a function that takes a
|
||||||
[`done` callback](http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support).
|
[`done` callback](http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support).
|
||||||
|
|
||||||
|
You can't call `done()` in `async()` or `fakeAsync()` functions, because the `done parameter`
|
||||||
|
is `undefined`.
|
||||||
|
|
||||||
Now you are responsible for chaining promises, handling errors, and calling `done()` at the appropriate moments.
|
Now you are responsible for chaining promises, handling errors, and calling `done()` at the appropriate moments.
|
||||||
|
|
||||||
Writing test functions with `done()`, is more cumbersome than `async`and `fakeAsync`.
|
Writing test functions with `done()`, is more cumbersome than `async()`and `fakeAsync()`.
|
||||||
But it is occasionally necessary.
|
But it is occasionally necessary when code involves the `intervalTimer()` like `setInterval`.
|
||||||
For example, you can't call `async` or `fakeAsync` when testing
|
|
||||||
code that involves the `intervalTimer()` or the RxJS `delay()` operator.
|
|
||||||
|
|
||||||
Here are two more versions of the previous test, written with `done()`.
|
Here are two more versions of the previous test, written with `done()`.
|
||||||
The first one subscribes to the `Observable` exposed to the template by the component's `quote` property.
|
The first one subscribes to the `Observable` exposed to the template by the component's `quote` property.
|
||||||
|
@ -2307,7 +2356,6 @@ Here are a few more `HeroDetailComponent` tests to reinforce the point.
|
||||||
|
|
||||||
{@a compile-components}
|
{@a compile-components}
|
||||||
### Calling _compileComponents()_
|
### Calling _compileComponents()_
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
You can ignore this section if you _only_ run tests with the CLI `ng test` command
|
You can ignore this section if you _only_ run tests with the CLI `ng test` command
|
||||||
|
@ -2871,7 +2919,7 @@ Here's a summary of the stand-alone functions, in order of likely utility:
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
When a `fakeAsync` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks),
|
When a `fakeAsync()` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks),
|
||||||
the test fails with a clear error message.
|
the test fails with a clear error message.
|
||||||
|
|
||||||
In general, a test should end with no queued tasks.
|
In general, a test should end with no queued tasks.
|
||||||
|
@ -2888,7 +2936,7 @@ Here's a summary of the stand-alone functions, in order of likely utility:
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
When a `fakeAsync` test ends with pending _micro-tasks_ such as unresolved promises,
|
When a `fakeAsync()` test ends with pending _micro-tasks_ such as unresolved promises,
|
||||||
the test fails with a clear error message.
|
the test fails with a clear error message.
|
||||||
|
|
||||||
In general, a test should wait for micro-tasks to finish.
|
In general, a test should wait for micro-tasks to finish.
|
||||||
|
|
Loading…
Reference in New Issue