66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import { Inject , Injectable , InjectionToken } from '../di' ;
9+ import { inject , Inject , Injectable , InjectionToken } from '../di' ;
10+ import { isInInjectionContext } from '../di/contextual' ;
11+ import { DestroyRef } from '../linker/destroy_ref' ;
1012import { NgZone } from '../zone/ng_zone' ;
1113
1214/**
@@ -84,13 +86,21 @@ export class Testability implements PublicTestability {
8486 private _isZoneStable : boolean = true ;
8587 private _callbacks : WaitCallback [ ] = [ ] ;
8688
87- private taskTrackingZone : { macroTasks : Task [ ] } | null = null ;
89+ private _taskTrackingZone : { macroTasks : Task [ ] } | null = null ;
90+
91+ private _destroyRef ?: DestroyRef ;
8892
8993 constructor (
9094 private _ngZone : NgZone ,
9195 private registry : TestabilityRegistry ,
9296 @Inject ( TESTABILITY_GETTER ) testabilityGetter : GetTestability ,
9397 ) {
98+ // Attempt to retrieve a `DestroyRef` optionally.
99+ // For backwards compatibility reasons, this cannot be required.
100+ if ( isInInjectionContext ( ) ) {
101+ this . _destroyRef = inject ( DestroyRef , { optional : true } ) ?? undefined ;
102+ }
103+
94104 // If there was no Testability logic registered in the global scope
95105 // before, register the current testability getter as a global one.
96106 if ( ! _testabilityGetter ) {
@@ -99,19 +109,19 @@ export class Testability implements PublicTestability {
99109 }
100110 this . _watchAngularEvents ( ) ;
101111 _ngZone . run ( ( ) => {
102- this . taskTrackingZone =
112+ this . _taskTrackingZone =
103113 typeof Zone == 'undefined' ? null : Zone . current . get ( 'TaskTrackingZone' ) ;
104114 } ) ;
105115 }
106116
107117 private _watchAngularEvents ( ) : void {
108- this . _ngZone . onUnstable . subscribe ( {
118+ const onUnstableSubscription = this . _ngZone . onUnstable . subscribe ( {
109119 next : ( ) => {
110120 this . _isZoneStable = false ;
111121 } ,
112122 } ) ;
113123
114- this . _ngZone . runOutsideAngular ( ( ) => {
124+ const onStableSubscription = this . _ngZone . runOutsideAngular ( ( ) =>
115125 this . _ngZone . onStable . subscribe ( {
116126 next : ( ) => {
117127 NgZone . assertNotInAngularZone ( ) ;
@@ -120,7 +130,12 @@ export class Testability implements PublicTestability {
120130 this . _runCallbacksIfReady ( ) ;
121131 } ) ;
122132 } ,
123- } ) ;
133+ } ) ,
134+ ) ;
135+
136+ this . _destroyRef ?. onDestroy ( ( ) => {
137+ onUnstableSubscription . unsubscribe ( ) ;
138+ onStableSubscription . unsubscribe ( ) ;
124139 } ) ;
125140 }
126141
@@ -156,12 +171,12 @@ export class Testability implements PublicTestability {
156171 }
157172
158173 private getPendingTasks ( ) : PendingMacrotask [ ] {
159- if ( ! this . taskTrackingZone ) {
174+ if ( ! this . _taskTrackingZone ) {
160175 return [ ] ;
161176 }
162177
163178 // Copy the tasks data so that we don't leak tasks.
164- return this . taskTrackingZone . macroTasks . map ( ( t : Task ) => {
179+ return this . _taskTrackingZone . macroTasks . map ( ( t : Task ) => {
165180 return {
166181 source : t . source ,
167182 // From TaskTrackingZone:
@@ -196,7 +211,7 @@ export class Testability implements PublicTestability {
196211 * and no further updates will be issued.
197212 */
198213 whenStable ( doneCb : Function , timeout ?: number , updateCb ?: Function ) : void {
199- if ( updateCb && ! this . taskTrackingZone ) {
214+ if ( updateCb && ! this . _taskTrackingZone ) {
200215 throw new Error (
201216 'Task tracking zone is required when passing an update callback to ' +
202217 'whenStable(). Is "zone.js/plugins/task-tracking" loaded?' ,
0 commit comments