Files
syndarix/frontend/tests/lib/hooks/useDebounce.test.ts
Felipe Cardoso 68f1865a1e feat(frontend): implement agent configuration pages (#41)
- Add agent types list page with search and filter functionality
- Add agent type detail/edit page with tabbed interface
- Create AgentTypeForm component with React Hook Form + Zod validation
- Implement model configuration (temperature, max tokens, top_p)
- Add MCP permission management with checkboxes
- Include personality prompt editor textarea
- Create TanStack Query hooks for agent-types API
- Add useDebounce hook for search optimization
- Comprehensive unit tests for all components (68 tests)

Components:
- AgentTypeList: Grid view with status badges, expertise tags
- AgentTypeDetail: Full detail view with model config, MCP permissions
- AgentTypeForm: Create/edit with 4 tabs (Basic, Model, Permissions, Personality)

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 23:48:49 +01:00

155 lines
4.0 KiB
TypeScript

import { renderHook, act } from '@testing-library/react';
import { useDebounce } from '@/lib/hooks/useDebounce';
describe('useDebounce', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('returns the initial value immediately', () => {
const { result } = renderHook(() => useDebounce('initial', 500));
expect(result.current).toBe('initial');
});
it('updates the debounced value after the delay', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: 'initial', delay: 500 } }
);
// Change the value
rerender({ value: 'updated', delay: 500 });
// Value should still be initial before delay
expect(result.current).toBe('initial');
// Fast forward time
act(() => {
jest.advanceTimersByTime(500);
});
// Value should now be updated
expect(result.current).toBe('updated');
});
it('does not update the value before the delay', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: 'initial', delay: 500 } }
);
rerender({ value: 'updated', delay: 500 });
// Only advance 300ms (not enough)
act(() => {
jest.advanceTimersByTime(300);
});
expect(result.current).toBe('initial');
});
it('resets the timer when value changes rapidly', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: 'initial', delay: 500 } }
);
// First change
rerender({ value: 'first', delay: 500 });
// Advance 300ms
act(() => {
jest.advanceTimersByTime(300);
});
// Second change (should reset timer)
rerender({ value: 'second', delay: 500 });
// Advance another 300ms (total 600ms from first, but only 300ms from second)
act(() => {
jest.advanceTimersByTime(300);
});
// Value should still be initial (timer was reset)
expect(result.current).toBe('initial');
// Advance the remaining 200ms
act(() => {
jest.advanceTimersByTime(200);
});
// Now should show 'second'
expect(result.current).toBe('second');
});
it('cleans up timeout on unmount', () => {
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
const { unmount, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: 'initial', delay: 500 } }
);
rerender({ value: 'updated', delay: 500 });
unmount();
expect(clearTimeoutSpy).toHaveBeenCalled();
clearTimeoutSpy.mockRestore();
});
it('works with different delay values', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: 'initial', delay: 1000 } }
);
rerender({ value: 'updated', delay: 1000 });
act(() => {
jest.advanceTimersByTime(500);
});
expect(result.current).toBe('initial');
act(() => {
jest.advanceTimersByTime(500);
});
expect(result.current).toBe('updated');
});
it('works with different value types', () => {
// Test with number
const { result: numberResult } = renderHook(() => useDebounce(42, 500));
expect(numberResult.current).toBe(42);
// Test with object
const obj = { foo: 'bar' };
const { result: objectResult } = renderHook(() => useDebounce(obj, 500));
expect(objectResult.current).toEqual({ foo: 'bar' });
// Test with null
const { result: nullResult } = renderHook(() => useDebounce(null, 500));
expect(nullResult.current).toBeNull();
});
it('handles zero delay', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: 'initial', delay: 0 } }
);
rerender({ value: 'updated', delay: 0 });
act(() => {
jest.advanceTimersByTime(0);
});
expect(result.current).toBe('updated');
});
});