@@ -10,6 +10,8 @@ import {
1010 ApplicationRef ,
1111 ChangeDetectorRef ,
1212 ComponentRef ,
13+ ɵChangeDetectionScheduler ,
14+ ɵNotificationSource ,
1315 DebugElement ,
1416 ElementRef ,
1517 getDebugNode ,
@@ -18,7 +20,6 @@ import {
1820 RendererFactory2 ,
1921 ViewRef ,
2022 ɵDeferBlockDetails as DeferBlockDetails ,
21- ɵdetectChangesInViewIfRequired ,
2223 ɵEffectScheduler as EffectScheduler ,
2324 ɵgetDeferBlocks as getDeferBlocks ,
2425 ɵNoopNgZone as NoopNgZone ,
@@ -81,6 +82,9 @@ export abstract class ComponentFixture<T> {
8182 protected readonly _testAppRef = this . _appRef as unknown as TestAppRef ;
8283 private readonly pendingTasks = inject ( PendingTasks ) ;
8384 private readonly appErrorHandler = inject ( TestBedApplicationErrorHandler ) ;
85+ /** @internal */
86+ protected abstract _autoDetect : boolean ;
87+ private readonly scheduler = inject ( ɵChangeDetectionScheduler , { optional : true } ) ;
8488
8589 // TODO(atscott): Remove this from public API
8690 ngZone = this . _noZoneOptionIsSet ? null : this . _ngZone ;
@@ -95,6 +99,17 @@ export abstract class ComponentFixture<T> {
9599 this . componentRef = componentRef ;
96100 }
97101
102+ /** @internal */
103+ initialize ( ) : void {
104+ if ( this . _autoDetect ) {
105+ this . _testAppRef . externalTestViews . add ( this . componentRef . hostView ) ;
106+ this . scheduler ?. notify ( ɵNotificationSource . ViewAttached ) ;
107+ }
108+ this . componentRef . hostView . onDestroy ( ( ) => {
109+ this . _testAppRef . externalTestViews . delete ( this . componentRef . hostView ) ;
110+ } ) ;
111+ }
112+
98113 /**
99114 * Trigger a change detection cycle for the component.
100115 */
@@ -180,6 +195,7 @@ export abstract class ComponentFixture<T> {
180195 * Trigger component destruction.
181196 */
182197 destroy ( ) : void {
198+ this . _testAppRef . externalTestViews . delete ( this . componentRef . hostView ) ;
183199 if ( ! this . _isDestroyed ) {
184200 this . componentRef . destroy ( ) ;
185201 this . _isDestroyed = true ;
@@ -194,9 +210,11 @@ export abstract class ComponentFixture<T> {
194210 * `ApplicationRef.isStable`, and `autoDetectChanges` cannot be disabled.
195211 */
196212export class ScheduledComponentFixture < T > extends ComponentFixture < T > {
197- private _autoDetect = inject ( ComponentFixtureAutoDetect , { optional : true } ) ?? true ;
213+ /** @internal */
214+ protected override _autoDetect = inject ( ComponentFixtureAutoDetect , { optional : true } ) ?? true ;
198215
199- initialize ( ) : void {
216+ override initialize ( ) : void {
217+ super . initialize ( ) ;
200218 if ( this . _autoDetect ) {
201219 this . _appRef . attachView ( this . componentRef . hostView ) ;
202220 }
@@ -230,26 +248,24 @@ export class ScheduledComponentFixture<T> extends ComponentFixture<T> {
230248
231249interface TestAppRef {
232250 externalTestViews : Set < ViewRef > ;
233- beforeRender : Subject < boolean > ;
234- afterTick : Subject < void > ;
235251}
236252
237253/**
238254 * ComponentFixture behavior that attempts to act as a "mini application".
239255 */
240256export class PseudoApplicationComponentFixture < T > extends ComponentFixture < T > {
241257 private _subscriptions = new Subscription ( ) ;
242- private _autoDetect = inject ( ComponentFixtureAutoDetect , { optional : true } ) ?? false ;
243- private afterTickSubscription : Subscription | undefined = undefined ;
244- private beforeRenderSubscription : Subscription | undefined = undefined ;
258+ /** @internal */
259+ override _autoDetect = inject ( ComponentFixtureAutoDetect , { optional : true } ) ?? false ;
245260
246- initialize ( ) : void {
261+ override initialize ( ) : void {
247262 if ( this . _autoDetect ) {
248- this . subscribeToAppRefEvents ( ) ;
263+ this . _testAppRef . externalTestViews . add ( this . componentRef . hostView ) ;
249264 }
250265 this . componentRef . hostView . onDestroy ( ( ) => {
251- this . unsubscribeFromAppRefEvents ( ) ;
266+ this . _testAppRef . externalTestViews . delete ( this . componentRef . hostView ) ;
252267 } ) ;
268+
253269 // Create subscriptions outside the NgZone so that the callbacks run outside
254270 // of NgZone.
255271 this . _ngZone . runOutsideAngular ( ( ) => {
@@ -285,54 +301,17 @@ export class PseudoApplicationComponentFixture<T> extends ComponentFixture<T> {
285301
286302 if ( autoDetect !== this . _autoDetect ) {
287303 if ( autoDetect ) {
288- this . subscribeToAppRefEvents ( ) ;
304+ this . _testAppRef . externalTestViews . add ( this . componentRef . hostView ) ;
289305 } else {
290- this . unsubscribeFromAppRefEvents ( ) ;
306+ this . _testAppRef . externalTestViews . delete ( this . componentRef . hostView ) ;
291307 }
292308 }
293309
294310 this . _autoDetect = autoDetect ;
295311 this . detectChanges ( ) ;
296312 }
297313
298- private subscribeToAppRefEvents ( ) {
299- this . _ngZone . runOutsideAngular ( ( ) => {
300- this . afterTickSubscription = this . _testAppRef . afterTick . subscribe ( ( ) => {
301- this . checkNoChanges ( ) ;
302- } ) ;
303- this . beforeRenderSubscription = this . _testAppRef . beforeRender . subscribe ( ( isFirstPass ) => {
304- try {
305- ɵdetectChangesInViewIfRequired (
306- ( this . componentRef . hostView as any ) . _lView ,
307- ( this . componentRef . hostView as any ) . notifyErrorHandler ,
308- isFirstPass ,
309- false /** zoneless enabled */ ,
310- ) ;
311- } catch ( e : unknown ) {
312- // If an error occurred during change detection, remove the test view from the application
313- // ref tracking. Note that this isn't exactly desirable but done this way because of how
314- // things used to work with `autoDetect` and uncaught errors. Ideally we would surface
315- // this error to the error handler instead and continue refreshing the view like
316- // what would happen in the application.
317- this . unsubscribeFromAppRefEvents ( ) ;
318-
319- throw e ;
320- }
321- } ) ;
322- this . _testAppRef . externalTestViews . add ( this . componentRef . hostView ) ;
323- } ) ;
324- }
325-
326- private unsubscribeFromAppRefEvents ( ) {
327- this . afterTickSubscription ?. unsubscribe ( ) ;
328- this . beforeRenderSubscription ?. unsubscribe ( ) ;
329- this . afterTickSubscription = undefined ;
330- this . beforeRenderSubscription = undefined ;
331- this . _testAppRef . externalTestViews . delete ( this . componentRef . hostView ) ;
332- }
333-
334314 override destroy ( ) : void {
335- this . unsubscribeFromAppRefEvents ( ) ;
336315 this . _subscriptions . unsubscribe ( ) ;
337316 super . destroy ( ) ;
338317 }
0 commit comments