TEST TEMPLATE

/** * Test Template - Use this as a reference when writing new tests * * Place this file next to your component/hook/utility with a .test.ts(x) suffix * Example: MyComponent.tsx -> MyComponent.test.tsx */

// ============================================================================ // HOOK TEST TEMPLATE // ============================================================================

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { renderHook, act, waitFor } from '@testing-library/react'; import { useMyHook } from '@/src/hooks/use-my-hook';

describe('useMyHook', () => { beforeEach(() => { vi.clearAllMocks(); });

afterEach(() => { vi.restoreAllMocks(); });

it('should initialize with default value', () => { const { result } = renderHook(() => useMyHook()); expect(result.current).toEqual(expectedDefault); });

it('should update state when action is performed', async () => { const { result } = renderHook(() => useMyHook());

await act(async () => {
  result.current.doSomething();
});

expect(result.current.state).toBe(expectedState);

});

it('should cleanup on unmount', () => { const cleanupSpy = vi.spyOn(global, 'clearInterval'); const { unmount } = renderHook(() => useMyHook());

unmount();

expect(cleanupSpy).toHaveBeenCalled();
cleanupSpy.mockRestore();

}); });

// ============================================================================ // COMPONENT TEST TEMPLATE // ============================================================================

import { render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MyComponent } from '@/src/components/MyComponent';

describe('MyComponent', () => { it('should render component with expected content', () => { render();

expect(screen.getByText('Test Title')).toBeInTheDocument();

});

it('should handle user interactions', async () => { const user = userEvent.setup(); const handleClick = vi.fn();

render(<MyComponent onClick={handleClick} />);

const button = screen.getByRole('button');
await user.click(button);

expect(handleClick).toHaveBeenCalledTimes(1);

});

it('should accept custom className', () => { const { container } = render(); expect(container.firstChild).toHaveClass('custom-class'); });

it('should render children', () => { render( Child content );

expect(screen.getByText('Child content')).toBeInTheDocument();

});

it('should be accessible', () => { render(); expect(screen.getByRole('button')).toHaveAccessibleName(); }); });

// ============================================================================ // UTILITY/SERVICE TEST TEMPLATE // ============================================================================

import { myUtility, asyncUtility } from '@/src/lib/my-utility';

// Mock external dependencies vi.mock('@/src/services/externalService', () => ({ externalCall: vi.fn(), }));

import { externalCall } from '@/src/services/externalService';

describe('myUtility', () => { beforeEach(() => { vi.clearAllMocks(); });

it('should process input correctly', () => { const result = myUtility('input'); expect(result).toBe('expected output'); });

it('should handle edge cases', () => { expect(myUtility('')).toBe('default'); expect(myUtility(null)).toBe('default'); });

it('should call external service correctly', async () => { (externalCall as any).mockResolvedValue({ data: 'response' });

const result = await asyncUtility('input');

expect(externalCall).toHaveBeenCalledWith('input');
expect(result).toEqual({ data: 'response' });

});

it('should handle errors gracefully', async () => { (externalCall as any).mockRejectedValue(new Error('Network error'));

const result = await asyncUtility('input');

expect(result).toBeNull(); // or your error handling behavior

}); });

// ============================================================================ // FORM COMPONENT TEST TEMPLATE // ============================================================================

import { MyForm } from '@/src/components/forms/MyForm';

describe('MyForm', () => { it('should validate required fields', async () => { const user = userEvent.setup(); render();

await user.click(screen.getByRole('button', { name: /submit/i }));

expect(screen.getByText(/email is required/i)).toBeInTheDocument();

});

it('should submit form with valid data', async () => { const user = userEvent.setup(); const handleSubmit = vi.fn();

render(<MyForm onSubmit={handleSubmit} />);

const emailInput = screen.getByLabelText(/email/i);
await user.type(emailInput, 'test@example.com');

await user.click(screen.getByRole('button', { name: /submit/i }));

expect(handleSubmit).toHaveBeenCalledWith({
  email: 'test@example.com',
});

});

it('should show loading state during submission', async () => { const user = userEvent.setup(); render( new Promise(() => {})} />);

await user.click(screen.getByRole('button', { name: /submit/i }));

expect(screen.getByRole('button')).toBeDisabled();

}); });

// ============================================================================ // COMMON TESTING PATTERNS // ============================================================================

describe('Common Patterns', () => { // Testing async state changes it('should handle async state updates', async () => { const { result } = renderHook(() => useMyAsyncHook());

await waitFor(() => {
  expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toBeDefined();

});

// Testing with providers it('should work with context provider', () => { render( );

expect(screen.getByText('test')).toBeInTheDocument();

});

// Testing error boundaries it('should catch errors in error boundary', () => { // Suppress console.error for this test vi.spyOn(console, 'error').mockImplementation(() => {});

render(
  <ErrorBoundary>
    <ThrowingComponent />
  </ErrorBoundary>
);

expect(screen.getByText(/something went wrong/i)).toBeInTheDocument();

});

// Testing with fake timers it('should handle debounced input', () => { vi.useFakeTimers();

const { result } = renderHook(() => useDebounce('test', 500));

act(() => {
  vi.advanceTimersByTime(500);
});

expect(result.current).toBe('test');

vi.useRealTimers();

}); });