cqt-agent
Version:
605 lines (499 loc) • 19.1 kB
text/typescript
// 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 };