docs: fix docs and associated code snippets for enabling more macro tasks in `fakeAsync()` (#35778)
In the `testing` guide, there is a section discussing configuring `fakeAsync()` to handle more macro tasks (e.g. `HTMLCanvasElement#toBlob()`). Previously, the corresponding code snippets (some of which were hard-coded in the guide) were incorrect/incomplete and the associated tests were broken. This was discovered while enabling docs examples unit tests in #34374. This commit fixes the code snippets and associated tests and ensures the examples used in the guide come from an example app (i.e. are not hard-coded). Note: The docs examples unit tests are currently not run on CI. This will be fixed in #34374. PR Close #35778
This commit is contained in:
parent
ef2721b903
commit
8eb4a9d395
|
@ -1,6 +1,21 @@
|
||||||
|
// #docplaster
|
||||||
|
// #docregion without-toBlob-macrotask
|
||||||
import { TestBed, async, tick, fakeAsync } from '@angular/core/testing';
|
import { TestBed, async, tick, fakeAsync } from '@angular/core/testing';
|
||||||
import { CanvasComponent } from './canvas.component';
|
import { CanvasComponent } from './canvas.component';
|
||||||
|
|
||||||
describe('CanvasComponent', () => {
|
describe('CanvasComponent', () => {
|
||||||
|
// #enddocregion without-toBlob-macrotask
|
||||||
|
// #docregion enable-toBlob-macrotask
|
||||||
|
beforeEach(() => {
|
||||||
|
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
|
||||||
|
{
|
||||||
|
source: 'HTMLCanvasElement.toBlob',
|
||||||
|
callbackArgs: [{ size: 200 }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
// #enddocregion enable-toBlob-macrotask
|
||||||
|
// #docregion without-toBlob-macrotask
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -8,20 +23,16 @@ describe('CanvasComponent', () => {
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
beforeEach(() => {
|
|
||||||
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
|
|
||||||
{
|
|
||||||
source: 'HTMLCanvasElement.toBlob',
|
|
||||||
callbackArgs: [{ size: 200 }]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
});
|
|
||||||
it('should be able to generate blob data from canvas', fakeAsync(() => {
|
it('should be able to generate blob data from canvas', fakeAsync(() => {
|
||||||
const fixture = TestBed.createComponent(CanvasComponent);
|
const fixture = TestBed.createComponent(CanvasComponent);
|
||||||
|
const canvasComp = fixture.debugElement.componentInstance;
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
expect(canvasComp.blobSize).toBe(0);
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
const app = fixture.debugElement.componentInstance;
|
expect(canvasComp.blobSize).toBeGreaterThan(0);
|
||||||
expect(app.blobSize).toBeGreaterThan(0);
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
// #enddocregion without-toBlob-macrotask
|
||||||
|
|
|
@ -1,25 +1,32 @@
|
||||||
|
// #docplaster
|
||||||
|
// #docregion import-canvas-patch
|
||||||
|
// Import patch to make async `HTMLCanvasElement` methods (such as `.toBlob()`) Zone.js-aware.
|
||||||
|
// Either import in `polyfills.ts` (if used in more than one places in the app) or in the component
|
||||||
|
// file using `HTMLCanvasElement` (if it is only used in a single file).
|
||||||
|
import 'zone.js/dist/zone-patch-canvas';
|
||||||
|
// #enddocregion import-canvas-patch
|
||||||
|
// #docregion main
|
||||||
import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
|
import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'sample-canvas',
|
selector: 'sample-canvas',
|
||||||
template: '<canvas #sampleCanvas width="200" height="200"></canvas>'
|
template: '<canvas #sampleCanvas width="200" height="200"></canvas>',
|
||||||
})
|
})
|
||||||
export class CanvasComponent implements AfterViewInit {
|
export class CanvasComponent implements AfterViewInit {
|
||||||
blobSize: number;
|
blobSize = 0;
|
||||||
@ViewChild('sampleCanvas') sampleCanvas: ElementRef;
|
@ViewChild('sampleCanvas') sampleCanvas: ElementRef;
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
const canvas = this.sampleCanvas.nativeElement;
|
const canvas: HTMLCanvasElement = this.sampleCanvas.nativeElement;
|
||||||
const context = canvas.getContext('2d');
|
const context = canvas.getContext('2d');
|
||||||
if (context) {
|
|
||||||
context.clearRect(0, 0, 200, 200);
|
context.clearRect(0, 0, 200, 200);
|
||||||
context.fillStyle = '#FF1122';
|
context.fillStyle = '#FF1122';
|
||||||
context.fillRect(0, 0, 200, 200);
|
context.fillRect(0, 0, 200, 200);
|
||||||
canvas.toBlob((blob: any) => {
|
|
||||||
this.blobSize = blob.size;
|
canvas.toBlob(blob => {
|
||||||
});
|
this.blobSize = blob.size;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// #enddocregion main
|
||||||
|
|
|
@ -899,8 +899,7 @@ In production, change detection kicks in automatically
|
||||||
when Angular creates a component or the user enters a keystroke or
|
when Angular creates a component or the user enters a keystroke or
|
||||||
an asynchronous activity (e.g., AJAX) completes.
|
an asynchronous activity (e.g., AJAX) completes.
|
||||||
|
|
||||||
The `TestBed.createComponent` does _not_ trigger change detection.
|
The `TestBed.createComponent` does _not_ trigger change detection; a fact confirmed in the revised test:
|
||||||
a fact confirmed in the revised test:
|
|
||||||
|
|
||||||
<code-example
|
<code-example
|
||||||
path="testing/src/app/banner/banner.component.spec.ts" region="test-w-o-detect-changes"></code-example>
|
path="testing/src/app/banner/banner.component.spec.ts" region="test-w-o-detect-changes"></code-example>
|
||||||
|
@ -1051,8 +1050,7 @@ attempt to reach an authentication server.
|
||||||
These behaviors can be hard to intercept.
|
These behaviors can be hard to intercept.
|
||||||
It is far easier and safer to create and register a test double in place of the real `UserService`.
|
It is far easier and safer to create and register a test double in place of the real `UserService`.
|
||||||
|
|
||||||
This particular test suite supplies a minimal mock of the `UserService` that satisfies the needs of the `WelcomeComponent`
|
This particular test suite supplies a minimal mock of the `UserService` that satisfies the needs of the `WelcomeComponent` and its tests:
|
||||||
and its tests:
|
|
||||||
|
|
||||||
<code-example
|
<code-example
|
||||||
path="testing/src/app/welcome/welcome.component.spec.ts"
|
path="testing/src/app/welcome/welcome.component.spec.ts"
|
||||||
|
@ -1266,8 +1264,7 @@ You do have to call [tick()](api/core/testing/tick) to advance the (virtual) clo
|
||||||
Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish.
|
Calling [tick()](api/core/testing/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()](api/core/testing/tick) function accepts milliseconds and tickOptions as parameters, the millisecond (defaults to 0 if not provided) 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. The tickOptions is an optional parameter with a property called processNewMacroTasksSynchronously (defaults is true) represents whether to invoke
|
The [tick()](api/core/testing/tick) function accepts milliseconds and tickOptions as parameters, the millisecond (defaults to 0 if not provided) 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. The tickOptions is an optional parameter with a property called `processNewMacroTasksSynchronously` (defaults to true) represents whether to invoke new generated macro tasks when ticking.
|
||||||
new generated macro tasks when ticking.
|
|
||||||
|
|
||||||
<code-example
|
<code-example
|
||||||
path="testing/src/app/demo/async-helper.spec.ts"
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
@ -1331,51 +1328,46 @@ You can also use RxJS scheduler in `fakeAsync()` just like using `setTimeout()`
|
||||||
|
|
||||||
#### Support more macroTasks
|
#### Support more macroTasks
|
||||||
|
|
||||||
By default `fakeAsync()` supports the following `macroTasks`.
|
By default, `fakeAsync()` supports the following macro tasks.
|
||||||
|
|
||||||
- setTimeout
|
- `setTimeout`
|
||||||
- setInterval
|
- `setInterval`
|
||||||
- requestAnimationFrame
|
- `requestAnimationFrame`
|
||||||
- webkitRequestAnimationFrame
|
- `webkitRequestAnimationFrame`
|
||||||
- mozRequestAnimationFrame
|
- `mozRequestAnimationFrame`
|
||||||
|
|
||||||
If you run other `macroTask` such as `HTMLCanvasElement.toBlob()`, `Unknown macroTask scheduled in fake async test` error will be thrown.
|
If you run other macro tasks such as `HTMLCanvasElement.toBlob()`, an _"Unknown macroTask scheduled in fake async test"_ error will be thrown.
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
<code-pane
|
<code-pane
|
||||||
|
header="src/app/shared/canvas.component.spec.ts (failing)"
|
||||||
path="testing/src/app/shared/canvas.component.spec.ts"
|
path="testing/src/app/shared/canvas.component.spec.ts"
|
||||||
header="src/app/shared/canvas.component.spec.ts">
|
region="without-toBlob-macrotask">
|
||||||
</code-pane>
|
</code-pane>
|
||||||
<code-pane
|
<code-pane
|
||||||
|
header="src/app/shared/canvas.component.ts"
|
||||||
path="testing/src/app/shared/canvas.component.ts"
|
path="testing/src/app/shared/canvas.component.ts"
|
||||||
header="src/app/shared/canvas.component.ts">
|
region="main">
|
||||||
</code-pane>
|
</code-pane>
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
If you want to support such a case, you need to define the `macroTask` you want to support in `beforeEach()`.
|
If you want to support such a case, you need to define the macro task you want to support in `beforeEach()`.
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```javascript
|
<code-example
|
||||||
beforeEach(() => {
|
header="src/app/shared/canvas.component.spec.ts (excerpt)"
|
||||||
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
|
path="testing/src/app/shared/canvas.component.spec.ts"
|
||||||
{
|
region="enable-toBlob-macrotask">
|
||||||
source: 'HTMLCanvasElement.toBlob',
|
</code-example>
|
||||||
callbackArgs: [{ size: 200 }]
|
|
||||||
}
|
Note that in order to make the `<canvas>` element Zone.js-aware in your app, you need to import the `zone-patch-canvas` patch (either in `polyfills.ts` or in the specific file that uses `<canvas>`):
|
||||||
];
|
|
||||||
});
|
<code-example
|
||||||
|
header="src/polyfills.ts or src/app/shared/canvas.component.ts"
|
||||||
|
path="testing/src/app/shared/canvas.component.ts"
|
||||||
|
region="import-canvas-patch">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
it('toBlob should be able to run in fakeAsync', fakeAsync(() => {
|
|
||||||
const canvas: HTMLCanvasElement = document.getElementById('canvas') as HTMLCanvasElement;
|
|
||||||
let blob = null;
|
|
||||||
canvas.toBlob(function(b) {
|
|
||||||
blob = b;
|
|
||||||
});
|
|
||||||
tick();
|
|
||||||
expect(blob.size).toBe(200);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Async observables
|
#### Async observables
|
||||||
|
|
||||||
|
@ -3635,7 +3627,7 @@ next to their corresponding helper files.
|
||||||
#### Keep it simple
|
#### Keep it simple
|
||||||
|
|
||||||
[Component class testing](#component-class-testing) should be kept very clean and simple.
|
[Component class testing](#component-class-testing) should be kept very clean and simple.
|
||||||
It should test only a single unit. On a first glance, you should be able to understand
|
It should test only a single unit. On a first glance, you should be able to understand
|
||||||
what the test is testing. If it's doing more, then it doesn't belong here.
|
what the test is testing. If it's doing more, then it doesn't belong here.
|
||||||
|
|
||||||
{@a q-end-to-end}
|
{@a q-end-to-end}
|
||||||
|
|
Loading…
Reference in New Issue