@@ -42,15 +42,15 @@ it('supports cancelling and creating timers while suspended and repeated visibil
4242 expect ( callback ) . toHaveBeenCalledTimes ( 1 )
4343} )
4444
45- it ( 'defers business callbacks in FIFO order until resume without replaying cancelled timers' , ( ) => {
45+ it ( 'defers business callbacks in FIFO order until resume without replaying cancelled timers' , async ( ) => {
4646 const events = [ ]
4747 scheduler . pause ( )
4848 scheduler . dispatch ( ( ) => events . push ( 'network' ) )
4949 scheduler . dispatch ( ( ) => events . push ( 'animation' ) )
5050 vi . runOnlyPendingTimers ( )
5151 expect ( events ) . toEqual ( [ ] )
5252 scheduler . resume ( )
53- vi . runOnlyPendingTimers ( )
53+ await vi . runAllTimersAsync ( )
5454 expect ( events ) . toEqual ( [ 'network' , 'animation' ] )
5555} )
5656
@@ -61,3 +61,84 @@ it('an interval cancelled from its callback is not rearmed', () => {
6161 expect ( callback ) . toHaveBeenCalledTimes ( 1 )
6262 expect ( vi . getTimerCount ( ) ) . toBe ( 0 )
6363} )
64+
65+
66+ it ( 'retains FIFO order when a resumed callback dispatches another callback synchronously' , async ( ) => {
67+ const events = [ ]
68+ scheduler . pause ( )
69+ scheduler . dispatch ( ( ) => {
70+ events . push ( 'first' )
71+ scheduler . dispatch ( ( ) => events . push ( 'nested' ) )
72+ } )
73+ scheduler . dispatch ( ( ) => events . push ( 'second' ) )
74+ scheduler . resume ( )
75+ await vi . runAllTimersAsync ( )
76+ expect ( events ) . toEqual ( [ 'first' , 'second' , 'nested' ] )
77+ } )
78+
79+ it ( 'finishes the full Promise chain of one message before delivering the next message' , async ( ) => {
80+ const events = [ ]
81+ scheduler . pause ( )
82+ scheduler . dispatch ( ( ) => {
83+ events . push ( 'success' )
84+ Promise . resolve ( ) . then ( ( ) => events . push ( 'then' ) ) . then ( ( ) => events . push ( 'chained' ) )
85+ } )
86+ scheduler . dispatch ( ( ) => events . push ( 'complete' ) )
87+ scheduler . resume ( )
88+ await vi . runAllTimersAsync ( )
89+ expect ( events ) . toEqual ( [ 'success' , 'then' , 'chained' , 'complete' ] )
90+ } )
91+
92+ it ( 'does not run the rest of the backlog when a Promise callback hides the app again' , async ( ) => {
93+ const events = [ ]
94+ scheduler . pause ( )
95+ scheduler . dispatch ( ( ) => Promise . resolve ( ) . then ( ( ) => scheduler . pause ( ) ) )
96+ scheduler . dispatch ( ( ) => events . push ( 'later' ) )
97+ scheduler . resume ( )
98+ await vi . runAllTimersAsync ( )
99+ expect ( events ) . toEqual ( [ ] )
100+ scheduler . resume ( )
101+ await vi . runAllTimersAsync ( )
102+ expect ( events ) . toEqual ( [ 'later' ] )
103+ } )
104+
105+ it ( 'ignores an already queued native timer callback from before suspension' , ( ) => {
106+ const queued = [ ]
107+ const clock = { now : ( ) => 0 , setTimeout : fn => { queued . push ( fn ) ; return queued . length } , clearTimeout ( ) { } }
108+ const runtime = new BackgroundScheduler ( clock )
109+ const callback = vi . fn ( )
110+ runtime . set ( callback , 10 , true )
111+ runtime . pause ( )
112+ runtime . resume ( )
113+ queued [ 0 ] ( ) // Already submitted by native before clearTimeout; it cannot be withdrawn.
114+ expect ( callback ) . not . toHaveBeenCalled ( )
115+ queued [ 1 ] ( )
116+ expect ( callback ) . toHaveBeenCalledTimes ( 1 )
117+ expect ( queued ) . toHaveLength ( 3 )
118+ } )
119+
120+
121+ it ( 'restores a large backlog in order with at most one pending host task' , async ( ) => {
122+ const events = [ ]
123+ scheduler . pause ( )
124+ for ( let index = 0 ; index < 257 ; index ++ ) scheduler . dispatch ( ( ) => events . push ( index ) )
125+ expect ( vi . getTimerCount ( ) ) . toBe ( 0 )
126+ scheduler . resume ( )
127+ for ( let index = 0 ; index < 257 ; index ++ ) {
128+ expect ( vi . getTimerCount ( ) ) . toBe ( 1 )
129+ await vi . advanceTimersToNextTimerAsync ( )
130+ }
131+ expect ( events ) . toEqual ( Array . from ( { length : 257 } , ( _ , index ) => index ) )
132+ expect ( vi . getTimerCount ( ) ) . toBe ( 0 )
133+ } )
134+
135+ it ( 'an exception in one pending callback does not discard following messages' , ( ) => {
136+ const events = [ ]
137+ scheduler . pause ( )
138+ scheduler . dispatch ( ( ) => { throw new Error ( 'business callback' ) } )
139+ scheduler . dispatch ( ( ) => events . push ( 'next' ) )
140+ scheduler . resume ( )
141+ expect ( ( ) => vi . advanceTimersToNextTimer ( ) ) . toThrow ( 'business callback' )
142+ vi . runAllTimers ( )
143+ expect ( events ) . toEqual ( [ 'next' ] )
144+ } )
0 commit comments