Skip to content

Commit 84b606b

Browse files
committed
docs: fix docs and associated code snippets for enabling more macro tasks in fakeAsync()
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.
1 parent 5bc39f8 commit 84b606b

3 files changed

Lines changed: 72 additions & 62 deletions

File tree

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
1+
// #docplaster
2+
// #docregion without-toBlob-macrotask
13
import { TestBed, async, tick, fakeAsync } from '@angular/core/testing';
24
import { CanvasComponent } from './canvas.component';
5+
36
describe('CanvasComponent', () => {
7+
// #enddocregion without-toBlob-macrotask
8+
// #docregion enable-toBlob-macrotask
9+
beforeEach(() => {
10+
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
11+
{
12+
source: 'HTMLCanvasElement.toBlob',
13+
callbackArgs: [{ size: 200 }],
14+
},
15+
];
16+
});
17+
// #enddocregion enable-toBlob-macrotask
18+
// #docregion without-toBlob-macrotask
419
beforeEach(async(() => {
520
TestBed.configureTestingModule({
621
declarations: [
722
CanvasComponent
823
],
924
}).compileComponents();
1025
}));
11-
beforeEach(() => {
12-
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
13-
{
14-
source: 'HTMLCanvasElement.toBlob',
15-
callbackArgs: [{ size: 200 }]
16-
}
17-
];
18-
});
26+
1927
it('should be able to generate blob data from canvas', fakeAsync(() => {
2028
const fixture = TestBed.createComponent(CanvasComponent);
29+
const canvasComp = fixture.debugElement.componentInstance;
30+
2131
fixture.detectChanges();
32+
expect(canvasComp.blobSize).toBe(0);
33+
2234
tick();
23-
const app = fixture.debugElement.componentInstance;
24-
expect(app.blobSize).toBeGreaterThan(0);
35+
expect(canvasComp.blobSize).toBeGreaterThan(0);
2536
}));
2637
});
27-
38+
// #enddocregion without-toBlob-macrotask
Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
1+
// #docplaster
2+
// #docregion import-canvas-patch
3+
// Import patch to make async `HTMLCanvasElement` methods (such as `.toBlob()`) Zone.js-aware.
4+
// Either import in `polyfills.ts` (if used in more than one places in the app) or in the component
5+
// file using `HTMLCanvasElement` (if it is only used in a single file).
6+
import 'zone.js/dist/zone-patch-canvas';
7+
// #enddocregion import-canvas-patch
8+
// #docregion main
19
import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
210

311
@Component({
412
selector: 'sample-canvas',
5-
template: '<canvas #sampleCanvas width="200" height="200"></canvas>'
13+
template: '<canvas #sampleCanvas width="200" height="200"></canvas>',
614
})
715
export class CanvasComponent implements AfterViewInit {
8-
blobSize: number;
16+
blobSize = 0;
917
@ViewChild('sampleCanvas') sampleCanvas: ElementRef;
1018

11-
constructor() { }
12-
1319
ngAfterViewInit() {
14-
const canvas = this.sampleCanvas.nativeElement;
20+
const canvas: HTMLCanvasElement = this.sampleCanvas.nativeElement;
1521
const context = canvas.getContext('2d');
16-
if (context) {
17-
context.clearRect(0, 0, 200, 200);
18-
context.fillStyle = '#FF1122';
19-
context.fillRect(0, 0, 200, 200);
20-
canvas.toBlob((blob: any) => {
21-
this.blobSize = blob.size;
22-
});
23-
}
22+
23+
context.clearRect(0, 0, 200, 200);
24+
context.fillStyle = '#FF1122';
25+
context.fillRect(0, 0, 200, 200);
26+
27+
canvas.toBlob(blob => {
28+
this.blobSize = blob.size;
29+
});
2430
}
2531
}
32+
// #enddocregion main

aio/content/guide/testing.md

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -899,8 +899,7 @@ In production, change detection kicks in automatically
899899
when Angular creates a component or the user enters a keystroke or
900900
an asynchronous activity (e.g., AJAX) completes.
901901

902-
The `TestBed.createComponent` does _not_ trigger change detection.
903-
a fact confirmed in the revised test:
902+
The `TestBed.createComponent` does _not_ trigger change detection; a fact confirmed in the revised test:
904903

905904
<code-example
906905
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.
10511050
These behaviors can be hard to intercept.
10521051
It is far easier and safer to create and register a test double in place of the real `UserService`.
10531052

1054-
This particular test suite supplies a minimal mock of the `UserService` that satisfies the needs of the `WelcomeComponent`
1055-
and its tests:
1053+
This particular test suite supplies a minimal mock of the `UserService` that satisfies the needs of the `WelcomeComponent` and its tests:
10561054

10571055
<code-example
10581056
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
12661264
Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish.
12671265
In this case, it waits for the error handler's `setTimeout()`.
12681266

1269-
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
1270-
new generated macro tasks when ticking.
1267+
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.
12711268

12721269
<code-example
12731270
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()`
13311328

13321329
#### Support more macroTasks
13331330

1334-
By default `fakeAsync()` supports the following `macroTasks`.
1331+
By default, `fakeAsync()` supports the following macro tasks.
13351332

1336-
- setTimeout
1337-
- setInterval
1338-
- requestAnimationFrame
1339-
- webkitRequestAnimationFrame
1340-
- mozRequestAnimationFrame
1333+
- `setTimeout`
1334+
- `setInterval`
1335+
- `requestAnimationFrame`
1336+
- `webkitRequestAnimationFrame`
1337+
- `mozRequestAnimationFrame`
13411338

1342-
If you run other `macroTask` such as `HTMLCanvasElement.toBlob()`, `Unknown macroTask scheduled in fake async test` error will be thrown.
1339+
If you run other macro tasks such as `HTMLCanvasElement.toBlob()`, an _"Unknown macroTask scheduled in fake async test"_ error will be thrown.
13431340

13441341
<code-tabs>
13451342
<code-pane
1343+
header="src/app/shared/canvas.component.spec.ts (failing)"
13461344
path="testing/src/app/shared/canvas.component.spec.ts"
1347-
header="src/app/shared/canvas.component.spec.ts">
1345+
region="without-toBlob-macrotask">
13481346
</code-pane>
13491347
<code-pane
1348+
header="src/app/shared/canvas.component.ts"
13501349
path="testing/src/app/shared/canvas.component.ts"
1351-
header="src/app/shared/canvas.component.ts">
1350+
region="main">
13521351
</code-pane>
13531352
</code-tabs>
13541353

1355-
If you want to support such a case, you need to define the `macroTask` you want to support in `beforeEach()`.
1354+
If you want to support such a case, you need to define the macro task you want to support in `beforeEach()`.
13561355
For example:
13571356

1358-
```javascript
1359-
beforeEach(() => {
1360-
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
1361-
{
1362-
source: 'HTMLCanvasElement.toBlob',
1363-
callbackArgs: [{ size: 200 }]
1364-
}
1365-
];
1366-
});
1367-
1368-
it('toBlob should be able to run in fakeAsync', fakeAsync(() => {
1369-
const canvas: HTMLCanvasElement = document.getElementById('canvas') as HTMLCanvasElement;
1370-
let blob = null;
1371-
canvas.toBlob(function(b) {
1372-
blob = b;
1373-
});
1374-
tick();
1375-
expect(blob.size).toBe(200);
1376-
})
1377-
);
1378-
```
1357+
<code-example
1358+
header="src/app/shared/canvas.component.spec.ts (excerpt)"
1359+
path="testing/src/app/shared/canvas.component.spec.ts"
1360+
region="enable-toBlob-macrotask">
1361+
</code-example>
1362+
1363+
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>`):
1364+
1365+
<code-example
1366+
header="src/polyfills.ts or src/app/shared/canvas.component.ts"
1367+
path="testing/src/app/shared/canvas.component.ts"
1368+
region="import-canvas-patch">
1369+
</code-example>
1370+
13791371

13801372
#### Async observables
13811373

@@ -3635,7 +3627,7 @@ next to their corresponding helper files.
36353627
#### Keep it simple
36363628

36373629
[Component class testing](#component-class-testing) should be kept very clean and simple.
3638-
It should test only a single unit. On a first glance, you should be able to understand
3630+
It should test only a single unit. On a first glance, you should be able to understand
36393631
what the test is testing. If it's doing more, then it doesn't belong here.
36403632

36413633
{@a q-end-to-end}

0 commit comments

Comments
 (0)