Skip to content

Commit 122af30

Browse files
atscottalxhub
authored andcommitted
refactor(core): Merge autoDetectChanges behaviors between fixtures (angular#57416)
This commit updates the implementations of `autoDetectChanges` to be shared between the zone-based and zoneless fixtures. This now allows `autoDetect` to be turned off for zoneless fixtures after it was previously on because the host view is no longer directly attached to `ApplicationRef`. PR Close angular#57416
1 parent 564a8d5 commit 122af30

File tree

2 files changed

+21
-48
lines changed

2 files changed

+21
-48
lines changed

packages/core/src/application/application_ref.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ export class ApplicationRef {
331331
// Needed for ComponentFixture temporarily during migration of autoDetect behavior
332332
// Eventually the hostView of the fixture should just attach to ApplicationRef.
333333
private externalTestViews: Set<InternalViewRef<unknown>> = new Set();
334-
private beforeRender = new Subject<boolean>();
335334
/** @internal */
336335
afterTick = new Subject<void>();
337336
/** @internal */
@@ -662,7 +661,6 @@ export class ApplicationRef {
662661
this.dirtyFlags |= ApplicationRefDirtyFlags.AfterRender;
663662

664663
// Check all potentially dirty views.
665-
this.beforeRender.next(useGlobalCheck);
666664
for (let {_lView, notifyErrorHandler} of this.allViews) {
667665
detectChangesInViewIfRequired(
668666
_lView,

packages/core/testing/src/component_fixture.ts

Lines changed: 21 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import {
2323
ɵEffectScheduler as EffectScheduler,
2424
ɵgetDeferBlocks as getDeferBlocks,
2525
ɵNoopNgZone as NoopNgZone,
26+
ɵZONELESS_ENABLED as ZONELESS_ENABLED,
2627
ɵPendingTasks as PendingTasks,
2728
} from '@angular/core';
28-
import {Subject, Subscription} from 'rxjs';
29+
import {Subscription} from 'rxjs';
2930

3031
import {DeferBlockFixture} from './defer';
3132
import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone} from './test_bed_common';
@@ -85,6 +86,7 @@ export abstract class ComponentFixture<T> {
8586
/** @internal */
8687
protected abstract _autoDetect: boolean;
8788
private readonly scheduler = inject(ɵChangeDetectionScheduler, {optional: true});
89+
private readonly zonelessEnabled = inject(ZONELESS_ENABLED);
8890

8991
// TODO(atscott): Remove this from public API
9092
ngZone = this._noZoneOptionIsSet ? null : this._ngZone;
@@ -104,6 +106,7 @@ export abstract class ComponentFixture<T> {
104106
if (this._autoDetect) {
105107
this._testAppRef.externalTestViews.add(this.componentRef.hostView);
106108
this.scheduler?.notify(ɵNotificationSource.ViewAttached);
109+
this.scheduler?.notify(ɵNotificationSource.MarkAncestorsForTraversal);
107110
}
108111
this.componentRef.hostView.onDestroy(() => {
109112
this._testAppRef.externalTestViews.delete(this.componentRef.hostView);
@@ -127,7 +130,22 @@ export abstract class ComponentFixture<T> {
127130
*
128131
* Also runs detectChanges once so that any existing change is detected.
129132
*/
130-
abstract autoDetectChanges(autoDetect?: boolean): void;
133+
autoDetectChanges(autoDetect = true): void {
134+
if (this._noZoneOptionIsSet && !this.zonelessEnabled) {
135+
throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set.');
136+
}
137+
138+
if (autoDetect !== this._autoDetect) {
139+
if (autoDetect) {
140+
this._testAppRef.externalTestViews.add(this.componentRef.hostView);
141+
} else {
142+
this._testAppRef.externalTestViews.delete(this.componentRef.hostView);
143+
}
144+
}
145+
146+
this._autoDetect = autoDetect;
147+
this.detectChanges();
148+
}
131149

132150
/**
133151
* Return whether the fixture is currently stable or has async tasks that have not been completed
@@ -213,13 +231,6 @@ export class ScheduledComponentFixture<T> extends ComponentFixture<T> {
213231
/** @internal */
214232
protected override _autoDetect = inject(ComponentFixtureAutoDetect, {optional: true}) ?? true;
215233

216-
override initialize(): void {
217-
super.initialize();
218-
if (this._autoDetect) {
219-
this._appRef.attachView(this.componentRef.hostView);
220-
}
221-
}
222-
223234
override detectChanges(checkNoChanges = true): void {
224235
if (!checkNoChanges) {
225236
throw new Error(
@@ -231,19 +242,6 @@ export class ScheduledComponentFixture<T> extends ComponentFixture<T> {
231242
this._appRef.tick();
232243
this._effectRunner.flush();
233244
}
234-
235-
override autoDetectChanges(autoDetect = true): void {
236-
if (!autoDetect) {
237-
throw new Error(
238-
'Cannot disable autoDetect after it has been enabled when using the zoneless scheduler. ' +
239-
'To disable autoDetect, add `{provide: ComponentFixtureAutoDetect, useValue: false}` to the TestBed providers.',
240-
);
241-
} else if (!this._autoDetect) {
242-
this._autoDetect = autoDetect;
243-
this._appRef.attachView(this.componentRef.hostView);
244-
}
245-
this.detectChanges();
246-
}
247245
}
248246

249247
interface TestAppRef {
@@ -259,13 +257,7 @@ export class PseudoApplicationComponentFixture<T> extends ComponentFixture<T> {
259257
override _autoDetect = inject(ComponentFixtureAutoDetect, {optional: true}) ?? false;
260258

261259
override initialize(): void {
262-
if (this._autoDetect) {
263-
this._testAppRef.externalTestViews.add(this.componentRef.hostView);
264-
}
265-
this.componentRef.hostView.onDestroy(() => {
266-
this._testAppRef.externalTestViews.delete(this.componentRef.hostView);
267-
});
268-
260+
super.initialize();
269261
// Create subscriptions outside the NgZone so that the callbacks run outside
270262
// of NgZone.
271263
this._ngZone.runOutsideAngular(() => {
@@ -294,23 +286,6 @@ export class PseudoApplicationComponentFixture<T> extends ComponentFixture<T> {
294286
this._effectRunner.flush();
295287
}
296288

297-
override autoDetectChanges(autoDetect = true): void {
298-
if (this._noZoneOptionIsSet) {
299-
throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set.');
300-
}
301-
302-
if (autoDetect !== this._autoDetect) {
303-
if (autoDetect) {
304-
this._testAppRef.externalTestViews.add(this.componentRef.hostView);
305-
} else {
306-
this._testAppRef.externalTestViews.delete(this.componentRef.hostView);
307-
}
308-
}
309-
310-
this._autoDetect = autoDetect;
311-
this.detectChanges();
312-
}
313-
314289
override destroy(): void {
315290
this._subscriptions.unsubscribe();
316291
super.destroy();

0 commit comments

Comments
 (0)