1
1
import { Notification } from 'rxjs/Notification' ;
2
2
import { Observable } from 'rxjs/Observable' ;
3
- import { VirtualTimeScheduler } from 'rxjs/scheduler/VirtualTimeScheduler ' ;
3
+ import { AsyncAction } from 'rxjs/scheduler/AsyncAction ' ;
4
4
import { VirtualAction } from 'rxjs/scheduler/VirtualTimeScheduler' ;
5
+ import { VirtualTimeScheduler } from 'rxjs/scheduler/VirtualTimeScheduler' ;
5
6
import { Subscription } from 'rxjs/Subscription' ;
6
7
import { ColdObservable } from 'rxjs/testing/ColdObservable' ;
7
8
import { HotObservable } from 'rxjs/testing/HotObservable' ;
@@ -18,19 +19,20 @@ class TestScheduler extends VirtualTimeScheduler {
18
19
private readonly coldObservables : Array < ColdObservable < any > > = [ ] ;
19
20
private readonly hotObservables : Array < HotObservable < any > > = [ ] ;
20
21
private flushed : boolean = false ;
22
+ private flushing : boolean = false ;
23
+
24
+ private readonly _maxFrame : number ;
25
+ public get maxFrame ( ) : number {
26
+ return this . _maxFrame ;
27
+ }
21
28
22
- constructor ( private readonly autoFlush = false , private readonly frameTimeFactor = 1 ) {
29
+ constructor ( private readonly autoFlush : boolean , private readonly frameTimeFactor : number , maxFrameValue : number ) {
23
30
super ( VirtualAction , Number . POSITIVE_INFINITY ) ;
31
+ this . _maxFrame = maxFrameValue * frameTimeFactor ;
24
32
}
25
33
26
34
public flush ( ) : void {
27
- const hotObservables = this . hotObservables ;
28
- while ( hotObservables . length > 0 ) {
29
- hotObservables . shift ( ) ! . setup ( ) ;
30
- }
31
-
32
- super . flush ( ) ;
33
- this . flushed = true ;
35
+ this . flushUntil ( ) ;
34
36
}
35
37
36
38
public getMessages < T = string > ( observable : Observable < T > , unsubscriptionMarbles : string | null = null ) {
@@ -87,7 +89,7 @@ class TestScheduler extends VirtualTimeScheduler {
87
89
88
90
const messages = Array . isArray ( marbleValue )
89
91
? marbleValue
90
- : parseObservableMarble ( marbleValue , value , error , false , this . frameTimeFactor ) as any ;
92
+ : parseObservableMarble ( marbleValue , value , error , false , this . frameTimeFactor , this . _maxFrame ) as any ;
91
93
const observable = new ColdObservable < T > ( messages as Array < TestMessage < T | Array < TestMessage < T > > > > , this ) ;
92
94
this . coldObservables . push ( observable ) ;
93
95
return observable ;
@@ -104,12 +106,26 @@ class TestScheduler extends VirtualTimeScheduler {
104
106
105
107
const messages = Array . isArray ( marbleValue )
106
108
? marbleValue
107
- : parseObservableMarble ( marbleValue , value , error , false , this . frameTimeFactor ) as any ;
109
+ : parseObservableMarble ( marbleValue , value , error , false , this . frameTimeFactor , this . _maxFrame ) as any ;
108
110
const subject = new HotObservable < T > ( messages as Array < TestMessage < T | Array < TestMessage < T > > > > , this ) ;
109
111
this . hotObservables . push ( subject ) ;
112
+ subject . setup ( ) ;
110
113
return subject ;
111
114
}
112
115
116
+ public advanceTo ( toFrame : number ) : void {
117
+ if ( this . autoFlush ) {
118
+ throw new Error ( 'Cannot advance frame manually with autoflushing scheduler' ) ;
119
+ }
120
+
121
+ if ( toFrame < 0 || toFrame < this . frame ) {
122
+ throw new Error ( `Cannot advance frame, given frame is either negative or smaller than current frame` ) ;
123
+ }
124
+
125
+ this . flushUntil ( toFrame ) ;
126
+ this . frame = toFrame ;
127
+ }
128
+
113
129
private materializeInnerObservable < T > ( observable : Observable < any > , outerFrame : number ) : Array < TestMessage < T > > {
114
130
const innerObservableMetadata : Array < TestMessage < T > > = [ ] ;
115
131
const pushMetaData = ( notification : Notification < T > ) =>
@@ -123,6 +139,45 @@ class TestScheduler extends VirtualTimeScheduler {
123
139
124
140
return innerObservableMetadata ;
125
141
}
142
+
143
+ private peek ( ) : AsyncAction < any > | null {
144
+ const { actions } = this ;
145
+ return actions && actions . length > 0 ? actions [ 0 ] : null ;
146
+ }
147
+
148
+ private flushUntil ( toFrame : number = this . maxFrame ) : void {
149
+ if ( this . flushing ) {
150
+ return ;
151
+ }
152
+
153
+ this . flushing = true ;
154
+
155
+ const { actions } = this ;
156
+ let error : any ;
157
+ let action : AsyncAction < any > | null | undefined = null ;
158
+
159
+ while ( this . flushing && ( action = this . peek ( ) ) && action . delay <= toFrame ) {
160
+ const action : AsyncAction < any > = actions . shift ( ) ! ;
161
+ this . frame = action . delay ;
162
+
163
+ if ( ( error = action . execute ( action . state , action . delay ) ) ) {
164
+ break ;
165
+ }
166
+ }
167
+
168
+ this . flushing = false ;
169
+
170
+ if ( toFrame >= this . maxFrame ) {
171
+ this . flushed = true ;
172
+ }
173
+
174
+ if ( error ) {
175
+ while ( ( action = actions . shift ( ) ) ) {
176
+ action . unsubscribe ( ) ;
177
+ }
178
+ throw error ;
179
+ }
180
+ }
126
181
}
127
182
128
183
export { TestScheduler } ;
0 commit comments