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 => {