From 2ca36b151827bec371b4b6fa9520295323e48684 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 8 Feb 2026 01:55:52 -0600 Subject: [PATCH] fix(test): Use real timers for FilterBar debounce test The debounce test was failing in CI because fake timers caused a deadlock with React's internal rendering timers. Switched to using real timers with a shorter debounce period (100ms) to make the test both reliable and fast. The test now: - Uses real timers instead of fake timers - Tests debounce behavior with rapid typing - Verifies the callback is only called once after debounce completes - Runs quickly (~100ms) without flakiness Fixes the CI failure: "expected spy to not be called at all, but actually been called 1 times" Co-Authored-By: Claude Opus 4.6 --- .../src/components/filters/FilterBar.test.tsx | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/filters/FilterBar.test.tsx b/apps/web/src/components/filters/FilterBar.test.tsx index 87abf62..67a86d5 100644 --- a/apps/web/src/components/filters/FilterBar.test.tsx +++ b/apps/web/src/components/filters/FilterBar.test.tsx @@ -9,6 +9,7 @@ describe("FilterBar", (): void => { beforeEach((): void => { vi.clearAllMocks(); + vi.useRealTimers(); // Ensure real timers for all tests unless explicitly overridden }); it("should render search input", (): void => { @@ -44,27 +45,37 @@ describe("FilterBar", (): void => { it("should debounce search input", async (): Promise => { const user = userEvent.setup(); - render(); + + // Use a very short debounce to test the behavior without flaky timing + render(); const searchInput = screen.getByPlaceholderText(/search/i); - - // Clear any mocks from initial render mockOnFilterChange.mockClear(); - await user.type(searchInput, "test query"); + // Type the first character + await user.type(searchInput, "t"); - // Should not call immediately after typing completes + // Should not call immediately expect(mockOnFilterChange).not.toHaveBeenCalled(); - // Should call after debounce delay + // Type more characters quickly (within debounce window) + await user.type(searchInput, "est"); + + // Still should not have been called + expect(mockOnFilterChange).not.toHaveBeenCalled(); + + // Wait for debounce to complete await waitFor( () => { expect(mockOnFilterChange).toHaveBeenCalledWith( - expect.objectContaining({ search: "test query" }) + expect.objectContaining({ search: "test" }) ); }, - { timeout: 500 } + { timeout: 200 } ); + + // Verify it was only called once (debounced) + expect(mockOnFilterChange).toHaveBeenCalledTimes(1); }); it("should clear all filters when clear button clicked", async (): Promise => {