UNPKG

cqt-agent

Version:
605 lines (499 loc) 19.1 kB
// Comprehensive Unit Test Template - Auto-Generated // This template generates complete unit tests for any component/service/utility import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { vi, describe, it, expect, beforeEach, afterEach, beforeAll, afterAll } from 'vitest'; import { {{COMPONENT_NAME}} } from './{{COMPONENT_PATH}}'; {{#each IMPORTS}} import { {{this.name}} } from '{{this.path}}'; {{/each}} // Mock external dependencies {{#each MOCKED_DEPENDENCIES}} vi.mock('{{this.path}}', () => ({ {{this.name}}: vi.fn({{this.mockImplementation}}) })); {{/each}} // Test data factory const TestDataFactory = { // Valid test data valid: { {{#each VALID_TEST_DATA}} {{this.name}}: {{this.value}}, {{/each}} }, // Invalid test data invalid: { {{#each INVALID_TEST_DATA}} {{this.name}}: {{this.value}}, // {{this.reason}} {{/each}} }, // Edge case data edgeCases: { {{#each EDGE_CASE_DATA}} {{this.name}}: {{this.value}}, // {{this.description}} {{/each}} } }; describe('{{COMPONENT_NAME}}', () => { let user: ReturnType<typeof userEvent.setup>; beforeAll(() => { // Global setup {{#each GLOBAL_SETUP}} {{this.code}} {{/each}} }); beforeEach(() => { user = userEvent.setup(); vi.clearAllMocks(); {{#each BEFORE_EACH_SETUP}} {{this.code}} {{/each}} }); afterEach(() => { vi.clearAllTimers(); vi.restoreAllMocks(); {{#each AFTER_EACH_CLEANUP}} {{this.code}} {{/each}} }); afterAll(() => { {{#each GLOBAL_CLEANUP}} {{this.code}} {{/each}} }); // === RENDERING TESTS === describe('Rendering', () => { it('renders without crashing', () => { expect(() => render(<{{COMPONENT_NAME}} />)).not.toThrow(); }); it('renders with default props', () => { render(<{{COMPONENT_NAME}} />); expect(screen.getByTestId('{{DEFAULT_TEST_ID}}')).toBeInTheDocument(); }); {{#each REQUIRED_PROPS}} it('renders with required prop: {{this.name}}', () => { const props = { {{this.name}}: TestDataFactory.valid.{{this.name}} }; render(<{{COMPONENT_NAME}} {...props} />); expect(screen.getByTestId('{{../DEFAULT_TEST_ID}}')).toBeInTheDocument(); }); {{/each}} it('renders with all props provided', () => { const allProps = { {{#each ALL_PROPS}} {{this.name}}: TestDataFactory.valid.{{this.name}}, {{/each}} }; render(<{{COMPONENT_NAME}} {...allProps} />); expect(screen.getByTestId('{{DEFAULT_TEST_ID}}')).toBeInTheDocument(); }); {{#if HAS_CONDITIONAL_RENDERING}} describe('Conditional Rendering', () => { {{#each CONDITIONAL_RENDERS}} it('{{this.description}}', () => { const props = {{this.props}}; render(<{{../COMPONENT_NAME}} {...props} />); {{#if this.shouldRender}} expect(screen.getByTestId('{{this.testId}}')).toBeInTheDocument(); {{else}} expect(screen.queryByTestId('{{this.testId}}')).not.toBeInTheDocument(); {{/if}} }); {{/each}} }); {{/if}} {{#if HAS_LOADING_STATES}} describe('Loading States', () => { it('shows loading state', () => { render(<{{COMPONENT_NAME}} loading={true} />); expect(screen.getByTestId('loading-indicator')).toBeInTheDocument(); }); it('hides content during loading', () => { render(<{{COMPONENT_NAME}} loading={true} />); expect(screen.queryByTestId('{{CONTENT_TEST_ID}}')).not.toBeInTheDocument(); }); }); {{/if}} {{#if HAS_ERROR_STATES}} describe('Error States', () => { it('displays error message when error prop is provided', () => { const errorMessage = 'Test error message'; render(<{{COMPONENT_NAME}} error={errorMessage} />); expect(screen.getByText(errorMessage)).toBeInTheDocument(); }); it('displays error boundary fallback on render error', () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); // Force an error by providing invalid props render(<{{COMPONENT_NAME}} {{ERROR_TRIGGERING_PROPS}} />); expect(screen.getByText(/something went wrong/i)).toBeInTheDocument(); consoleSpy.mockRestore(); }); }); {{/if}} }); // === INTERACTION TESTS === describe('User Interactions', () => { {{#each USER_INTERACTIONS}} describe('{{this.name}} Interaction', () => { it('{{this.description}}', async () => { const mockHandler = vi.fn(); const props = { {{this.handlerProp}}: mockHandler }; render(<{{../COMPONENT_NAME}} {...props} />); const element = screen.getBy{{this.selector}}('{{this.identifier}}'); {{#if this.isUserEvent}} await user.{{this.action}}(element); {{else}} fireEvent.{{this.action}}(element); {{/if}} {{#if this.hasAsyncBehavior}} await waitFor(() => { expect(mockHandler).toHaveBeenCalledTimes({{this.expectedCalls}}); }); {{else}} expect(mockHandler).toHaveBeenCalledTimes({{this.expectedCalls}}); {{/if}} {{#if this.expectedArgs}} expect(mockHandler).toHaveBeenCalledWith({{this.expectedArgs}}); {{/if}} }); {{#if this.hasValidation}} it('validates {{this.name}} input', async () => { render(<{{../COMPONENT_NAME}} />); const element = screen.getBy{{this.selector}}('{{this.identifier}}'); // Test invalid input await user.{{this.action}}(element); await user.type(element, '{{this.invalidValue}}'); expect(screen.getByText('{{this.errorMessage}}')).toBeInTheDocument(); }); {{/if}} }); {{/each}} describe('Keyboard Interactions', () => { it('handles Tab navigation correctly', async () => { render(<{{COMPONENT_NAME}} />); {{#each TAB_ORDER}} await user.tab(); expect(screen.getByTestId('{{this.testId}}')).toHaveFocus(); {{/each}} }); {{#each KEYBOARD_SHORTCUTS}} it('handles {{this.keys}} keyboard shortcut', async () => { const mockHandler = vi.fn(); render(<{{../COMPONENT_NAME}} {{this.handlerProp}}={mockHandler} />); await user.keyboard('{{this.keys}}'); expect(mockHandler).toHaveBeenCalled(); }); {{/each}} }); {{#if HAS_FORM_ELEMENTS}} describe('Form Interactions', () => { it('submits form with valid data', async () => { const mockSubmit = vi.fn(); render(<{{COMPONENT_NAME}} onSubmit={mockSubmit} />); // Fill form fields {{#each FORM_FIELDS}} const {{this.name}}Field = screen.getByLabelText('{{this.label}}'); await user.type({{this.name}}Field, TestDataFactory.valid.{{this.name}}); {{/each}} // Submit form const submitButton = screen.getByRole('button', { name: /submit/i }); await user.click(submitButton); expect(mockSubmit).toHaveBeenCalledWith({ {{#each FORM_FIELDS}} {{this.name}}: TestDataFactory.valid.{{this.name}}, {{/each}} }); }); it('prevents submission with invalid data', async () => { const mockSubmit = vi.fn(); render(<{{COMPONENT_NAME}} onSubmit={mockSubmit} />); // Submit without filling required fields const submitButton = screen.getByRole('button', { name: /submit/i }); await user.click(submitButton); expect(mockSubmit).not.toHaveBeenCalled(); {{#each REQUIRED_FORM_FIELDS}} expect(screen.getByText('{{this.name}} is required')).toBeInTheDocument(); {{/each}} }); }); {{/if}} }); // === STATE MANAGEMENT TESTS === {{#if HAS_INTERNAL_STATE}} describe('State Management', () => { {{#each STATE_VARIABLES}} describe('{{this.name}} State', () => { it('initializes with correct default value', () => { render(<{{../../COMPONENT_NAME}} />); expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.defaultValue}}'); }); it('updates when {{this.trigger}} occurs', async () => { render(<{{../../COMPONENT_NAME}} />); const trigger = screen.getByTestId('{{this.triggerTestId}}'); await user.click(trigger); await waitFor(() => { expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.updatedValue}}'); }); }); {{#if this.hasValidation}} it('validates {{this.name}} before updating', async () => { render(<{{../../COMPONENT_NAME}} />); const input = screen.getByTestId('{{this.inputTestId}}'); await user.type(input, '{{this.invalidValue}}'); expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.defaultValue}}'); expect(screen.getByText('{{this.errorMessage}}')).toBeInTheDocument(); }); {{/if}} }); {{/each}} it('resets all state correctly', async () => { render(<{{COMPONENT_NAME}} />); // Modify state {{#each STATE_VARIABLES}} const {{this.name}}Trigger = screen.getByTestId('{{this.triggerTestId}}'); await user.click({{this.name}}Trigger); {{/each}} // Reset state const resetButton = screen.getByTestId('reset-button'); await user.click(resetButton); // Verify all state is reset {{#each STATE_VARIABLES}} expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.defaultValue}}'); {{/each}} }); }); {{/if}} // === PROPS VALIDATION TESTS === describe('Props Validation', () => { {{#each ALL_PROPS}} describe('{{this.name}} Prop', () => { it('accepts valid {{this.type}} value', () => { const props = { {{this.name}}: TestDataFactory.valid.{{this.name}} }; expect(() => render(<{{../COMPONENT_NAME}} {...props} />)).not.toThrow(); }); {{#if this.isRequired}} it('handles missing required prop gracefully', () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); render(<{{../COMPONENT_NAME}} />); expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('{{this.name}}') ); consoleSpy.mockRestore(); }); {{/if}} {{#if this.hasDefaultValue}} it('uses default value when prop not provided', () => { render(<{{../COMPONENT_NAME}} />); expect(screen.getByTestId('{{this.testId}}')).toHaveTextContent('{{this.defaultValue}}'); }); {{/if}} {{#each this.validations}} it('validates {{../name}}: {{this.description}}', () => { const props = { {{../name}}: {{this.testValue}} }; {{#if this.shouldThrow}} expect(() => render(<{{../../COMPONENT_NAME}} {...props} />)).toThrow(); {{else}} expect(() => render(<{{../../COMPONENT_NAME}} {...props} />)).not.toThrow(); {{/if}} }); {{/each}} }); {{/each}} it('spreads additional props correctly', () => { const additionalProps = { 'data-custom': 'custom-value', className: 'custom-class', id: 'custom-id' }; render(<{{COMPONENT_NAME}} {...additionalProps} />); const element = screen.getByTestId('{{DEFAULT_TEST_ID}}'); expect(element).toHaveAttribute('data-custom', 'custom-value'); expect(element).toHaveClass('custom-class'); expect(element).toHaveAttribute('id', 'custom-id'); }); }); // === ASYNC BEHAVIOR TESTS === {{#if HAS_ASYNC_BEHAVIOR}} describe('Async Behavior', () => { {{#each ASYNC_OPERATIONS}} describe('{{this.name}}', () => { it('handles successful {{this.name}}', async () => { const mockData = TestDataFactory.valid.{{this.responseData}}; const mockApi = vi.fn().mockResolvedValue(mockData); {{this.mockSetup}} render(<{{../COMPONENT_NAME}} />); const trigger = screen.getByTestId('{{this.triggerTestId}}'); await user.click(trigger); await waitFor(() => { expect(screen.getByTestId('{{this.successTestId}}')).toBeInTheDocument(); }); expect(mockApi).toHaveBeenCalledWith({{this.expectedArgs}}); }); it('handles {{this.name}} error', async () => { const errorMessage = 'API Error'; const mockApi = vi.fn().mockRejectedValue(new Error(errorMessage)); {{this.mockSetup}} render(<{{../COMPONENT_NAME}} />); const trigger = screen.getByTestId('{{this.triggerTestId}}'); await user.click(trigger); await waitFor(() => { expect(screen.getByText(errorMessage)).toBeInTheDocument(); }); }); it('shows loading state during {{this.name}}', async () => { let resolvePromise: (value: any) => void; const mockApi = vi.fn(() => new Promise(resolve => { resolvePromise = resolve; })); {{this.mockSetup}} render(<{{../COMPONENT_NAME}} />); const trigger = screen.getByTestId('{{this.triggerTestId}}'); await user.click(trigger); expect(screen.getByTestId('loading-{{this.name}}')).toBeInTheDocument(); act(() => { resolvePromise(TestDataFactory.valid.{{this.responseData}}); }); await waitFor(() => { expect(screen.queryByTestId('loading-{{this.name}}')).not.toBeInTheDocument(); }); }); }); {{/each}} }); {{/if}} // === ACCESSIBILITY TESTS === describe('Accessibility', () => { it('has proper ARIA attributes', () => { render(<{{COMPONENT_NAME}} />); const element = screen.getByTestId('{{DEFAULT_TEST_ID}}'); {{#each ARIA_ATTRIBUTES}} expect(element).toHaveAttribute('{{this.name}}', '{{this.value}}'); {{/each}} }); it('supports screen readers', () => { render(<{{COMPONENT_NAME}} />); {{#each SCREEN_READER_ELEMENTS}} const element = screen.getBy{{this.selector}}('{{this.identifier}}'); expect(element).toHaveAttribute('aria-label', '{{this.ariaLabel}}'); {{/each}} }); it('maintains focus management', async () => { render(<{{COMPONENT_NAME}} />); // Test focus trap if applicable {{#if HAS_FOCUS_TRAP}} const firstFocusable = screen.getByTestId('{{FIRST_FOCUSABLE_ELEMENT}}'); const lastFocusable = screen.getByTestId('{{LAST_FOCUSABLE_ELEMENT}}'); firstFocusable.focus(); await user.tab({ shift: true }); expect(lastFocusable).toHaveFocus(); lastFocusable.focus(); await user.tab(); expect(firstFocusable).toHaveFocus(); {{/if}} }); {{#each KEYBOARD_NAVIGATION_TESTS}} it('{{this.description}}', async () => { render(<{{../COMPONENT_NAME}} />); {{#each this.steps}} await user.keyboard('{{this.key}}'); expect(screen.getByTestId('{{this.expectedFocus}}')).toHaveFocus(); {{/each}} }); {{/each}} }); // === PERFORMANCE TESTS === describe('Performance', () => { it('does not cause unnecessary re-renders', () => { const renderSpy = vi.fn(); const TestWrapper = React.memo((props: any) => { renderSpy(); return <{{COMPONENT_NAME}} {...props} />; }); const { rerender } = render(<TestWrapper />); expect(renderSpy).toHaveBeenCalledTimes(1); // Re-render with same props rerender(<TestWrapper />); expect(renderSpy).toHaveBeenCalledTimes(1); // Re-render with different props rerender(<TestWrapper newProp="value" />); expect(renderSpy).toHaveBeenCalledTimes(2); }); {{#if HAS_MEMOIZATION}} it('memoizes expensive calculations', () => { const expensiveCalculation = vi.fn().mockReturnValue('calculated-value'); render(<{{COMPONENT_NAME}} calculate={expensiveCalculation} input="test" />); expect(expensiveCalculation).toHaveBeenCalledTimes(1); // Re-render with same input render(<{{COMPONENT_NAME}} calculate={expensiveCalculation} input="test" />); expect(expensiveCalculation).toHaveBeenCalledTimes(1); // Should not recalculate // Re-render with different input render(<{{COMPONENT_NAME}} calculate={expensiveCalculation} input="new-test" />); expect(expensiveCalculation).toHaveBeenCalledTimes(2); // Should recalculate }); {{/if}} }); // === EDGE CASES AND ERROR HANDLING === describe('Edge Cases', () => { {{#each EDGE_CASES}} it('handles {{this.description}}', {{#if this.isAsync}}async {{/if}}() => { {{#if this.setup}} {{this.setup}} {{/if}} const props = {{this.props}}; {{#if this.shouldThrow}} expect(() => render(<{{../COMPONENT_NAME}} {...props} />)).toThrow('{{this.expectedError}}'); {{else}} expect(() => render(<{{../COMPONENT_NAME}} {...props} />)).not.toThrow(); {{/if}} {{#if this.additionalAssertions}} {{#each this.additionalAssertions}} {{this.assertion}} {{/each}} {{/if}} }); {{/each}} it('handles component unmounting gracefully', () => { const { unmount } = render(<{{COMPONENT_NAME}} />); expect(() => unmount()).not.toThrow(); }); {{#if HAS_CLEANUP}} it('cleans up resources on unmount', () => { const mockCleanup = vi.fn(); {{CLEANUP_SETUP}} const { unmount } = render(<{{COMPONENT_NAME}} />); unmount(); expect(mockCleanup).toHaveBeenCalled(); }); {{/if}} }); }); // === UTILITY FUNCTION TESTS (if applicable) === {{#if HAS_UTILITY_FUNCTIONS}} {{#each UTILITY_FUNCTIONS}} describe('{{this.name}} utility', () => { {{#each this.testCases}} it('{{this.description}}', {{#if this.isAsync}}async {{/if}}() => { const input = {{this.input}}; const expected = {{this.expected}}; {{#if this.isAsync}} const result = await {{../name}}(input); {{else}} const result = {{../name}}(input); {{/if}} expect(result).toEqual(expected); }); {{/each}} {{#each this.errorCases}} it('throws error: {{this.description}}', {{#if this.isAsync}}async {{/if}}() => { const input = {{this.input}}; {{#if this.isAsync}} await expect({{../name}}(input)).rejects.toThrow('{{this.expectedError}}'); {{else}} expect(() => {{../name}}(input)).toThrow('{{this.expectedError}}'); {{/if}} }); {{/each}} }); {{/each}} {{/if}} // Export test utilities for reuse export { TestDataFactory };