1- import { describe , it , expect , beforeEach , afterEach , jest } from "@jest/globals" ;
2- import { RefreshController } from "./RefreshController" ;
1+ import { describe , it , expect , mock } from "bun:test" ;
32
4- describe ( "RefreshController" , ( ) => {
5- beforeEach ( ( ) => {
6- jest . useFakeTimers ( ) ;
7- } ) ;
3+ import { RefreshController } from "./RefreshController" ;
84
9- afterEach ( ( ) => {
10- jest . useRealTimers ( ) ;
11- } ) ;
5+ const sleep = ( ms : number ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
126
13- it ( "debounces multiple schedule() calls" , ( ) => {
14- const onRefresh = jest . fn < ( ) => void > ( ) ;
15- const controller = new RefreshController ( { onRefresh, debounceMs : 100 } ) ;
7+ describe ( "RefreshController" , ( ) => {
8+ it ( "debounces multiple schedule() calls" , async ( ) => {
9+ const onRefresh = mock < ( ) => void > ( ( ) => undefined ) ;
10+ const controller = new RefreshController ( { onRefresh, debounceMs : 50 } ) ;
1611
1712 controller . schedule ( ) ;
1813 controller . schedule ( ) ;
1914 controller . schedule ( ) ;
2015
2116 expect ( onRefresh ) . not . toHaveBeenCalled ( ) ;
2217
23- jest . advanceTimersByTime ( 100 ) ;
18+ // Use real timers because bun's jest compatibility doesn't expose timer controls.
19+ await sleep ( 120 ) ;
2420
2521 expect ( onRefresh ) . toHaveBeenCalledTimes ( 1 ) ;
2622
2723 controller . dispose ( ) ;
2824 } ) ;
2925
30- it ( "requestImmediate() bypasses debounce" , ( ) => {
31- const onRefresh = jest . fn < ( ) => void > ( ) ;
32- const controller = new RefreshController ( { onRefresh, debounceMs : 100 } ) ;
26+ it ( "requestImmediate() bypasses debounce" , async ( ) => {
27+ const onRefresh = mock < ( ) => void > ( ( ) => undefined ) ;
28+ const controller = new RefreshController ( { onRefresh, debounceMs : 50 } ) ;
3329
3430 controller . schedule ( ) ;
3531 expect ( onRefresh ) . not . toHaveBeenCalled ( ) ;
@@ -38,7 +34,7 @@ describe("RefreshController", () => {
3834 expect ( onRefresh ) . toHaveBeenCalledTimes ( 1 ) ;
3935
4036 // Original debounce timer should be cleared
41- jest . advanceTimersByTime ( 100 ) ;
37+ await sleep ( 120 ) ;
4238 expect ( onRefresh ) . toHaveBeenCalledTimes ( 1 ) ;
4339
4440 controller . dispose ( ) ;
@@ -47,15 +43,15 @@ describe("RefreshController", () => {
4743 it ( "guards against concurrent sync refreshes (in-flight queuing)" , ( ) => {
4844 // Track if refresh is currently in-flight
4945 let inFlight = false ;
50- const onRefresh = jest . fn ( ( ) => {
46+ const onRefresh = mock ( ( ) => {
5147 // Simulate sync operation that takes time
5248 expect ( inFlight ) . toBe ( false ) ; // Should never be called while already in-flight
5349 inFlight = true ;
5450 // Immediately complete (sync)
5551 inFlight = false ;
5652 } ) ;
5753
58- const controller = new RefreshController ( { onRefresh, debounceMs : 100 } ) ;
54+ const controller = new RefreshController ( { onRefresh, debounceMs : 50 } ) ;
5955
6056 // Multiple immediate requests should only call once (queued ones execute after)
6157 controller . requestImmediate ( ) ;
@@ -64,14 +60,14 @@ describe("RefreshController", () => {
6460 controller . dispose ( ) ;
6561 } ) ;
6662
67- it ( "isRefreshing reflects in-flight state" , ( ) => {
63+ it ( "isRefreshing reflects in-flight state" , async ( ) => {
6864 let resolveRefresh : ( ) => void ;
6965 const refreshPromise = new Promise < void > ( ( resolve ) => {
7066 resolveRefresh = resolve ;
7167 } ) ;
7268
73- const onRefresh = jest . fn ( ( ) => refreshPromise ) ;
74- const controller = new RefreshController ( { onRefresh, debounceMs : 100 } ) ;
69+ const onRefresh = mock ( ( ) => refreshPromise ) ;
70+ const controller = new RefreshController ( { onRefresh, debounceMs : 50 } ) ;
7571
7672 expect ( controller . isRefreshing ) . toBe ( false ) ;
7773
@@ -80,31 +76,34 @@ describe("RefreshController", () => {
8076
8177 // Complete the promise
8278 resolveRefresh ! ( ) ;
79+ await Promise . resolve ( ) ;
80+
81+ expect ( controller . isRefreshing ) . toBe ( false ) ;
8382
8483 controller . dispose ( ) ;
8584 } ) ;
8685
87- it ( "dispose() cleans up debounce timer" , ( ) => {
88- const onRefresh = jest . fn < ( ) => void > ( ) ;
89- const controller = new RefreshController ( { onRefresh, debounceMs : 100 } ) ;
86+ it ( "dispose() cleans up debounce timer" , async ( ) => {
87+ const onRefresh = mock < ( ) => void > ( ( ) => undefined ) ;
88+ const controller = new RefreshController ( { onRefresh, debounceMs : 50 } ) ;
9089
9190 controller . schedule ( ) ;
9291 controller . dispose ( ) ;
9392
94- jest . advanceTimersByTime ( 100 ) ;
93+ await sleep ( 120 ) ;
9594
9695 expect ( onRefresh ) . not . toHaveBeenCalled ( ) ;
9796 } ) ;
9897
99- it ( "does not refresh after dispose" , ( ) => {
100- const onRefresh = jest . fn < ( ) => void > ( ) ;
101- const controller = new RefreshController ( { onRefresh, debounceMs : 100 } ) ;
98+ it ( "does not refresh after dispose" , async ( ) => {
99+ const onRefresh = mock < ( ) => void > ( ( ) => undefined ) ;
100+ const controller = new RefreshController ( { onRefresh, debounceMs : 50 } ) ;
102101
103102 controller . dispose ( ) ;
104103 controller . schedule ( ) ;
105104 controller . requestImmediate ( ) ;
106105
107- jest . advanceTimersByTime ( 100 ) ;
106+ await sleep ( 120 ) ;
108107
109108 expect ( onRefresh ) . not . toHaveBeenCalled ( ) ;
110109 } ) ;
0 commit comments