@@ -18,10 +18,31 @@ import {NgZone} from '../zone/ng_zone';
18
18
*/
19
19
export declare interface PublicTestability {
20
20
isStable ( ) : boolean ;
21
- whenStable ( callback : Function ) : void ;
21
+ whenStable ( callback : Function , timeout ?: number , updateCallback ?: Function ) : void ;
22
22
findProviders ( using: any , provider : string , exactMatch : boolean ) : any[ ] ;
23
23
}
24
24
25
+ // Angular internal, not intended for public API.
26
+ export interface PendingMacrotask {
27
+ source : string ;
28
+ isPeriodic : boolean ;
29
+ delay ?: number ;
30
+ creationLocation : Error ;
31
+ xhr ?: XMLHttpRequest ;
32
+ }
33
+
34
+ // Angular internal, not intended for public API.
35
+ export type DoneCallback = ( didWork : boolean , tasks ?: PendingMacrotask [ ] ) => void ;
36
+ export type UpdateCallback = ( tasks : PendingMacrotask [ ] ) => boolean ;
37
+
38
+ interface WaitCallback {
39
+ // Needs to be 'any' - setTimeout returns a number according to ES6, but
40
+ // on NodeJS it returns a Timer.
41
+ timeoutId : any ;
42
+ doneCb : DoneCallback ;
43
+ updateCb ?: UpdateCallback ;
44
+ }
45
+
25
46
/**
26
47
* The Testability service provides testing hooks that can be accessed from
27
48
* the browser and by services such as Protractor. Each bootstrapped Angular
@@ -30,23 +51,25 @@ export declare interface PublicTestability {
30
51
*/
31
52
@Injectable ( )
32
53
export class Testability implements PublicTestability {
33
- /** @internal */
34
- _pendingCount : number = 0 ;
35
- /** @internal */
36
- _isZoneStable : boolean = true ;
54
+ private _pendingCount : number = 0 ;
55
+ private _isZoneStable : boolean = true ;
37
56
/**
38
57
* Whether any work was done since the last 'whenStable' callback. This is
39
58
* useful to detect if this could have potentially destabilized another
40
59
* component while it is stabilizing.
41
60
* @internal
42
61
*/
43
- _didWork : boolean = false ;
44
- /** @internal */
45
- _callbacks : Function [ ] = [ ] ;
46
- constructor ( private _ngZone : NgZone ) { this . _watchAngularEvents ( ) ; }
62
+ private _didWork : boolean = false ;
63
+ private _callbacks : WaitCallback [ ] = [ ] ;
47
64
48
- /** @internal */
49
- _watchAngularEvents ( ) : void {
65
+ private taskTrackingZone : any ;
66
+
67
+ constructor ( private _ngZone : NgZone ) {
68
+ this . _watchAngularEvents ( ) ;
69
+ _ngZone . run ( ( ) => { this . taskTrackingZone = Zone . current . get ( 'TaskTrackingZone' ) ; } ) ;
70
+ }
71
+
72
+ private _watchAngularEvents ( ) : void {
50
73
this . _ngZone . onUnstable . subscribe ( {
51
74
next : ( ) => {
52
75
this . _didWork = true ;
@@ -69,6 +92,7 @@ export class Testability implements PublicTestability {
69
92
70
93
/**
71
94
* Increases the number of pending request
95
+ * @deprecated pending requests are now tracked with zones.
72
96
*/
73
97
increasePendingRequestCount ( ) : number {
74
98
this . _pendingCount += 1 ;
@@ -78,6 +102,7 @@ export class Testability implements PublicTestability {
78
102
79
103
/**
80
104
* Decreases the number of pending request
105
+ * @deprecated pending requests are now tracked with zones
81
106
*/
82
107
decreasePendingRequestCount ( ) : number {
83
108
this . _pendingCount -= 1 ;
@@ -92,36 +117,93 @@ export class Testability implements PublicTestability {
92
117
* Whether an associated application is stable
93
118
*/
94
119
isStable ( ) : boolean {
95
- return this . _isZoneStable && this . _pendingCount == 0 && ! this . _ngZone . hasPendingMacrotasks ;
120
+ return this . _isZoneStable && this . _pendingCount === 0 && ! this . _ngZone . hasPendingMacrotasks ;
96
121
}
97
122
98
- /** @internal */
99
- _runCallbacksIfReady ( ) : void {
123
+ private _runCallbacksIfReady ( ) : void {
100
124
if ( this . isStable ( ) ) {
101
125
// Schedules the call backs in a new frame so that it is always async.
102
126
scheduleMicroTask ( ( ) => {
103
127
while ( this . _callbacks . length !== 0 ) {
104
- ( this . _callbacks . pop ( ) ! ) ( this . _didWork ) ;
128
+ let cb = this . _callbacks . pop ( ) ! ;
129
+ clearTimeout ( cb . timeoutId ) ;
130
+ cb . doneCb ( this . _didWork ) ;
105
131
}
106
132
this . _didWork = false ;
107
133
} ) ;
108
134
} else {
109
- // Not Ready
135
+ // Still not stable, send updates.
136
+ let pending = this . getPendingTasks ( ) ;
137
+ this . _callbacks = this . _callbacks . filter ( ( cb ) => {
138
+ if ( cb . updateCb && cb . updateCb ( pending ) ) {
139
+ clearTimeout ( cb . timeoutId ) ;
140
+ return false ;
141
+ }
142
+
143
+ return true ;
144
+ } ) ;
145
+
110
146
this . _didWork = true ;
111
147
}
112
148
}
113
149
150
+ private getPendingTasks ( ) : PendingMacrotask [ ] {
151
+ if ( ! this . taskTrackingZone ) {
152
+ return [ ] ;
153
+ }
154
+
155
+ return this . taskTrackingZone . macroTasks . map ( ( t : Task ) => {
156
+ return {
157
+ source : t . source ,
158
+ isPeriodic : t . data . isPeriodic ,
159
+ delay : t . data . delay ,
160
+ // From TaskTrackingZone:
161
+ // https://github.com/angular/zone.js/blob/master/lib/zone-spec/task-tracking.ts#L40
162
+ creationLocation : ( t as any ) . creationLocation as Error ,
163
+ // Added by Zones for XHRs
164
+ // https://github.com/angular/zone.js/blob/master/lib/browser/browser.ts#L133
165
+ xhr : ( t . data as any ) . target
166
+ } ;
167
+ } ) ;
168
+ }
169
+
170
+ private addCallback ( cb : DoneCallback , timeout ?: number , updateCb ?: UpdateCallback ) {
171
+ let timeoutId : any = - 1 ;
172
+ if ( timeout && timeout > 0 ) {
173
+ timeoutId = setTimeout ( ( ) => {
174
+ this . _callbacks = this . _callbacks . filter ( ( cb ) => cb . timeoutId !== timeoutId ) ;
175
+ cb ( this . _didWork , this . getPendingTasks ( ) ) ;
176
+ } , timeout ) ;
177
+ }
178
+ this . _callbacks . push ( < WaitCallback > { doneCb : cb , timeoutId : timeoutId , updateCb : updateCb } ) ;
179
+ }
180
+
114
181
/**
115
- * Run callback when the application is stable
116
- * @param callback function to be called after the application is stable
182
+ * Wait for the application to be stable with a timeout. If the timeout is reached before that
183
+ * happens, the callback receives a list of the macro tasks that were pending, otherwise null.
184
+ *
185
+ * @param doneCb The callback to invoke when Angular is stable or the timeout expires
186
+ * whichever comes first.
187
+ * @param timeout Optional. The maximum time to wait for Angular to become stable. If not
188
+ * specified, whenStable() will wait forever.
189
+ * @param updateCb Optional. If specified, this callback will be invoked whenever the set of
190
+ * pending macrotasks changes. If this callback returns true doneCb will not be invoked
191
+ * and no further updates will be issued.
117
192
*/
118
- whenStable ( callback : Function ) : void {
119
- this . _callbacks . push ( callback ) ;
193
+ whenStable ( doneCb : Function , timeout ?: number , updateCb ?: Function ) : void {
194
+ if ( updateCb && ! this . taskTrackingZone ) {
195
+ throw new Error (
196
+ 'Task tracking zone is required when passing an update callback to ' +
197
+ 'whenStable(). Is "zone.js/dist/task-tracking.js" loaded?' ) ;
198
+ }
199
+ // These arguments are 'Function' above to keep the public API simple.
200
+ this . addCallback ( doneCb as DoneCallback , timeout , updateCb as UpdateCallback ) ;
120
201
this . _runCallbacksIfReady ( ) ;
121
202
}
122
203
123
204
/**
124
205
* Get the number of pending requests
206
+ * @deprecated pending requests are now tracked with zones
125
207
*/
126
208
getPendingRequestCount ( ) : number { return this . _pendingCount ; }
127
209
0 commit comments