From e5c85ff6e82dfef2f90486b7702f7e2976c30480 Mon Sep 17 00:00:00 2001 From: Goutham Annem Date: Wed, 9 Sep 2026 12:08:44 -0700 Subject: [PATCH 1/2] test(web): add unit tests for debounce utility Covers the core debounce behaviour: - function is not called before the wait period elapses - function is called exactly once after the wait period - default wait of 300 ms is applied when no wait is specified - repeated calls within the wait window reset the timer and fire only once - the most recent argument set is forwarded to the underlying function - the debounced function can be called again after a previous firing - clear() cancels a pending invocation - clear() is safe to call with no pending invocation Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Goutham Annem --- web/src/utils/debounce.test.ts | 101 +++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 web/src/utils/debounce.test.ts diff --git a/web/src/utils/debounce.test.ts b/web/src/utils/debounce.test.ts new file mode 100644 index 0000000000..7bb2b03f7f --- /dev/null +++ b/web/src/utils/debounce.test.ts @@ -0,0 +1,101 @@ +import debounce from "./debounce"; + +beforeEach(() => { + jest.useFakeTimers(); +}); + +afterEach(() => { + jest.useRealTimers(); +}); + +describe("debounce", () => { + it("does not call the function before the wait period elapses", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + expect(fn).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(299); + expect(fn).not.toHaveBeenCalled(); + }); + + it("calls the function after the wait period", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + jest.advanceTimersByTime(300); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it("uses the default wait of 300ms when no wait is specified", () => { + const fn = jest.fn(); + const debounced = debounce(fn); + + debounced(); + jest.advanceTimersByTime(299); + expect(fn).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it("resets the timer on repeated calls and fires only once", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + jest.advanceTimersByTime(200); + debounced(); + jest.advanceTimersByTime(200); + debounced(); + jest.advanceTimersByTime(300); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + it("passes the most recent arguments to the underlying function", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced("first"); + jest.advanceTimersByTime(100); + debounced("second"); + jest.advanceTimersByTime(300); + + expect(fn).toHaveBeenCalledWith("second"); + }); + + it("can be called again after the wait period fires", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + jest.advanceTimersByTime(300); + debounced(); + jest.advanceTimersByTime(300); + + expect(fn).toHaveBeenCalledTimes(2); + }); + + it("clear() prevents the pending call from executing", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + jest.advanceTimersByTime(100); + debounced.clear(); + jest.advanceTimersByTime(300); + + expect(fn).not.toHaveBeenCalled(); + }); + + it("clear() is a no-op when called with no pending invocation", () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + expect(() => debounced.clear()).not.toThrow(); + expect(fn).not.toHaveBeenCalled(); + }); +}); From c8d8170cfae5d6fee2b051335ed447220f506533 Mon Sep 17 00:00:00 2001 From: Goutham Annem Date: Sun, 13 Sep 2026 10:34:06 -0700 Subject: [PATCH 2/2] test(web): address timer cleanup and assertion strength in debounce tests - Follow use-interval.test.ts pattern: useFakeTimers once in beforeAll, clearAllTimers in afterEach, useRealTimers in afterAll - Assert toHaveBeenCalledTimes(1) alongside toHaveBeenCalledWith to ensure the debounced fn is not called more than once Signed-off-by: Goutham Annem --- web/src/utils/debounce.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/src/utils/debounce.test.ts b/web/src/utils/debounce.test.ts index 7bb2b03f7f..b66ab722d7 100644 --- a/web/src/utils/debounce.test.ts +++ b/web/src/utils/debounce.test.ts @@ -1,10 +1,14 @@ import debounce from "./debounce"; -beforeEach(() => { +beforeAll(() => { jest.useFakeTimers(); }); afterEach(() => { + jest.clearAllTimers(); +}); + +afterAll(() => { jest.useRealTimers(); }); @@ -64,6 +68,7 @@ describe("debounce", () => { debounced("second"); jest.advanceTimersByTime(300); + expect(fn).toHaveBeenCalledTimes(1); expect(fn).toHaveBeenCalledWith("second"); });