Skip to content

Commit f84415d

Browse files
committed
fix: align RefreshController tests with current API
Change-Id: Id3c00aad060e4f137cbe5e6712be0502fd3a1cd6 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent db76e83 commit f84415d

File tree

1 file changed

+17
-120
lines changed

1 file changed

+17
-120
lines changed

src/browser/utils/RefreshController.test.ts

Lines changed: 17 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,26 @@ describe("RefreshController", () => {
1111
setSystemTime();
1212
});
1313

14-
it("rate-limits multiple schedule() calls (doesn't reset timer)", async () => {
14+
it("debounces schedule() calls (resets timer)", async () => {
1515
const onRefresh = jest.fn<() => void>();
1616
const controller = new RefreshController({ onRefresh, debounceMs: 50 });
1717

1818
controller.schedule();
1919
await sleep(20);
20-
controller.schedule(); // Shouldn't reset timer
20+
controller.schedule(); // Resets debounce timer
2121

22-
// Should fire at 50ms from first call, not 70ms
23-
await sleep(40);
22+
// Not yet: only 30ms since last call (< debounceMs)
23+
await sleep(30);
24+
expect(onRefresh).not.toHaveBeenCalled();
25+
26+
// Now past debounceMs since last call
27+
await sleep(30);
2428
expect(onRefresh).toHaveBeenCalledTimes(1);
2529

2630
controller.dispose();
2731
});
2832

29-
it("coalesces calls during rate-limit window", async () => {
33+
it("coalesces calls during debounce window", async () => {
3034
const onRefresh = jest.fn<() => void>();
3135
const controller = new RefreshController({ onRefresh, debounceMs: 50 });
3236

@@ -43,7 +47,7 @@ describe("RefreshController", () => {
4347
controller.dispose();
4448
});
4549

46-
it("requestImmediate() bypasses rate-limit timer", async () => {
50+
it("requestImmediate() bypasses debounce timer", async () => {
4751
const onRefresh = jest.fn<() => void>();
4852
const controller = new RefreshController({ onRefresh, debounceMs: 50 });
4953

@@ -81,8 +85,6 @@ describe("RefreshController", () => {
8185
});
8286

8387
it("schedule() during in-flight queues refresh for after completion", async () => {
84-
setSystemTime(0);
85-
8688
const resolvers: Array<() => void> = [];
8789
const onRefresh = jest.fn(
8890
() =>
@@ -91,24 +93,25 @@ describe("RefreshController", () => {
9193
})
9294
);
9395

94-
const controller = new RefreshController({ onRefresh, debounceMs: 50 });
96+
const controller = new RefreshController({ onRefresh, debounceMs: 20 });
9597

9698
// Start first refresh
9799
controller.requestImmediate();
98100
expect(onRefresh).toHaveBeenCalledTimes(1);
99101
expect(resolvers).toHaveLength(1);
100102

101-
// schedule() while in-flight should queue, not start timer
103+
// schedule() while in-flight should queue, not start a second refresh
102104
controller.schedule();
103105

104-
// Complete the first refresh and let .finally() run
106+
// Ensure the debounce timer has fired while we're still in-flight.
107+
await sleep(30);
108+
expect(onRefresh).toHaveBeenCalledTimes(1);
109+
110+
// Complete the first refresh and let .finally() run.
105111
resolvers[0]();
106112
await Promise.resolve();
107113
await Promise.resolve(); // Extra tick for .finally()
108114

109-
// Make the follow-up refresh eligible immediately (skip MIN_REFRESH_INTERVAL_MS wait)
110-
setSystemTime(1000);
111-
112115
// Allow post-flight setTimeout(0) to run
113116
await sleep(0);
114117
await sleep(10);
@@ -168,110 +171,4 @@ describe("RefreshController", () => {
168171

169172
expect(onRefresh).not.toHaveBeenCalled();
170173
});
171-
172-
it("requestImmediate() bypasses isPaused check (for manual refresh)", async () => {
173-
const onRefresh = jest.fn<() => void>();
174-
const paused = true;
175-
const controller = new RefreshController({
176-
onRefresh,
177-
debounceMs: 50,
178-
isPaused: () => paused,
179-
});
180-
181-
// schedule() should be blocked by isPaused
182-
controller.schedule();
183-
await sleep(60);
184-
expect(onRefresh).not.toHaveBeenCalled();
185-
186-
// requestImmediate() should bypass isPaused (manual refresh)
187-
controller.requestImmediate();
188-
expect(onRefresh).toHaveBeenCalledTimes(1);
189-
190-
controller.dispose();
191-
});
192-
193-
it("schedule() respects isPaused and flushes on notifyUnpaused", async () => {
194-
const onRefresh = jest.fn<() => void>();
195-
let paused = true;
196-
const controller = new RefreshController({
197-
onRefresh,
198-
debounceMs: 50,
199-
isPaused: () => paused,
200-
});
201-
202-
// schedule() should queue but not execute while paused
203-
controller.schedule();
204-
await sleep(60);
205-
expect(onRefresh).not.toHaveBeenCalled();
206-
207-
// Unpausing should flush the pending refresh
208-
paused = false;
209-
controller.notifyUnpaused();
210-
expect(onRefresh).toHaveBeenCalledTimes(1);
211-
212-
controller.dispose();
213-
});
214-
215-
it("lastRefreshInfo tracks trigger and timestamp", async () => {
216-
// This test needs to avoid MIN_REFRESH_INTERVAL_MS; use setSystemTime() to simulate time passing.
217-
setSystemTime(0);
218-
219-
const onRefresh = jest.fn<() => void>();
220-
const controller = new RefreshController({ onRefresh, debounceMs: 20 });
221-
222-
expect(controller.lastRefreshInfo).toBeNull();
223-
224-
// Manual refresh should record "manual" trigger
225-
const beforeManual = Date.now();
226-
controller.requestImmediate();
227-
expect(controller.lastRefreshInfo).not.toBeNull();
228-
expect(controller.lastRefreshInfo!.trigger).toBe("manual");
229-
expect(controller.lastRefreshInfo!.timestamp).toBeGreaterThanOrEqual(beforeManual);
230-
231-
// Scheduled refresh should record "scheduled" trigger
232-
setSystemTime(1000);
233-
controller.schedule();
234-
await sleep(30);
235-
expect(controller.lastRefreshInfo!.trigger).toBe("scheduled");
236-
237-
// Priority refresh should record "priority" trigger
238-
setSystemTime(2000);
239-
controller.schedulePriority();
240-
await sleep(30);
241-
expect(controller.lastRefreshInfo!.trigger).toBe("priority");
242-
243-
controller.dispose();
244-
});
245-
246-
it("onRefreshComplete callback is called with refresh info", async () => {
247-
// This test needs to avoid MIN_REFRESH_INTERVAL_MS; use setSystemTime() to simulate time passing.
248-
setSystemTime(0);
249-
250-
const onRefresh = jest.fn<() => void>();
251-
const onRefreshComplete = jest.fn<(info: { trigger: string; timestamp: number }) => void>();
252-
const controller = new RefreshController({
253-
onRefresh,
254-
onRefreshComplete,
255-
debounceMs: 20,
256-
});
257-
258-
expect(onRefreshComplete).not.toHaveBeenCalled();
259-
260-
controller.requestImmediate();
261-
expect(onRefreshComplete).toHaveBeenCalledTimes(1);
262-
expect(onRefreshComplete).toHaveBeenCalledWith(
263-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
264-
expect.objectContaining({ trigger: "manual", timestamp: expect.any(Number) })
265-
);
266-
267-
setSystemTime(1000);
268-
controller.schedule();
269-
await sleep(30);
270-
expect(onRefreshComplete).toHaveBeenCalledTimes(2);
271-
expect(onRefreshComplete).toHaveBeenLastCalledWith(
272-
expect.objectContaining({ trigger: "scheduled" })
273-
);
274-
275-
controller.dispose();
276-
});
277174
});

0 commit comments

Comments
 (0)