Skip to content

Commit 962b59b

Browse files
atscottkirjs
authored andcommitted
fix(core): Ensure ComponentFixture does not duplicate error reporting from FakeAsync (#60104)
When using `fakeAsync`, if errors happen inside the Angular zone, they are also handled by the fakeAsync machinery. Rethrowing errors from the `NgZone.onError` subscription is not appropriate in this case because it results in the error being thrown twice. https://github.com/angular/angular/blob/db2f2d99c82aae52d8a0ae46616c6411d070b35e/packages/zone.js/lib/zone-spec/fake-async-test.ts#L783-L784 https://github.com/angular/angular/blob/db2f2d99c82aae52d8a0ae46616c6411d070b35e/packages/zone.js/lib/zone-spec/fake-async-test.ts#L473-L478 PR Close #60104
1 parent 919c452 commit 962b59b

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

packages/core/test/component_fixture_spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import {
2121
import {
2222
ComponentFixtureAutoDetect,
2323
ComponentFixtureNoNgZone,
24+
fakeAsync,
2425
TestBed,
26+
tick,
2527
waitForAsync,
2628
withModule,
2729
} from '@angular/core/testing';
@@ -372,6 +374,37 @@ describe('ComponentFixture', () => {
372374
expect(() => fixture.detectChanges()).toThrow();
373375
});
374376

377+
it('should not duplicate errors when used with fake async', fakeAsync(() => {
378+
@Component({
379+
template: '<button (click)="doThrow()">a</button>',
380+
})
381+
class Throwing {
382+
doThrow() {
383+
throw new Error('thrown');
384+
}
385+
}
386+
TestBed.configureTestingModule({
387+
providers: [
388+
{
389+
provide: ErrorHandler,
390+
useClass: class {
391+
handleError(e: unknown) {
392+
throw e;
393+
}
394+
},
395+
},
396+
],
397+
});
398+
const fix = TestBed.createComponent(Throwing);
399+
try {
400+
fix.nativeElement.querySelector('button').click();
401+
tick();
402+
fail('should have thrown');
403+
} catch (e) {
404+
expect((e as Error).message).toMatch('thrown');
405+
}
406+
}));
407+
375408
describe('errors during ApplicationRef.tick', () => {
376409
@Component({
377410
template: '',

packages/core/testing/src/component_fixture.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ export class ComponentFixture<T> {
122122
this.subscriptions.add(
123123
this._ngZone.onError.subscribe({
124124
next: (error: any) => {
125+
// The rethrow here is to ensure that errors don't go unreported. Since `NgZone.onHandleError` returns `false`,
126+
// ZoneJS will not throw the error coming out of a task. Instead, the handling is defined by
127+
// the chain of parent delegates and whether they indicate the error is handled in some way (by returning `false`).
128+
// Unfortunately, 'onError' does not forward the information about whether the error was handled by a parent zone
129+
// so cannot know here whether throwing is appropriate. As a half-solution, we can check to see if we're inside
130+
// a fakeAsync context, which we know has its own error handling.
131+
// https://github.com/angular/angular/blob/db2f2d99c82aae52d8a0ae46616c6411d070b35e/packages/zone.js/lib/zone-spec/fake-async-test.ts#L783-L784
132+
// https://github.com/angular/angular/blob/db2f2d99c82aae52d8a0ae46616c6411d070b35e/packages/zone.js/lib/zone-spec/fake-async-test.ts#L473-L478
133+
if (typeof Zone === 'undefined' || Zone.current.get('FakeAsyncTestZoneSpec')) {
134+
return;
135+
}
125136
throw error;
126137
},
127138
}),

packages/zone.js/lib/zone-spec/fake-async-test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,8 @@ class FakeAsyncTestZoneSpec implements ZoneSpec {
780780
targetZone: Zone,
781781
error: any,
782782
): boolean {
783+
// ComponentFixture has a special-case handling to detect FakeAsyncTestZoneSpec
784+
// and prevent rethrowing the error from the onError subscription since it's handled here.
783785
this._lastError = error;
784786
return false; // Don't propagate error to parent zone.
785787
}

0 commit comments

Comments
 (0)