UNPKG

vineguard-mcp-server-standalone

Version:

VineGuard MCP Server v2.1 - Intelligent QA Workflow System with advanced test generation for Jest/RTL, Cypress, and Playwright. Features smart project analysis, progressive testing strategies, and comprehensive quality patterns for React/Vue/Angular proje

567 lines (463 loc) 17.4 kB
/** * Enhanced Jest/React Testing Library test generator * Supports unit, integration, and accessibility testing patterns */ export class JestGenerator { /** * Generate Jest test based on component analysis */ static generateTest(options) { const { testType, isComponent, isReact } = options; if (isComponent && isReact) { switch (testType) { case 'unit': return this.generateUnitComponentTest(options); case 'integration': return this.generateIntegrationComponentTest(options); case 'accessibility': return this.generateAccessibilityTest(options); default: return this.generateBasicComponentTest(options); } } else { switch (testType) { case 'unit': return this.generateUnitFunctionTest(options); case 'integration': return this.generateIntegrationFunctionTest(options); default: return this.generateBasicFunctionTest(options); } } } /** * Generate comprehensive unit test for React components */ static generateUnitComponentTest(options) { const { componentName, filePath, hasProps = true, hasState = false, hasAsync = false } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ${componentName} } from '${importPath}'; // Mock any external dependencies jest.mock('next/router', () => ({ useRouter: () => ({ push: jest.fn(), pathname: '/', }), })); describe('${componentName} - Unit Tests', () => { // Setup const user = userEvent.setup(); beforeEach(() => { // Reset all mocks before each test jest.clearAllMocks(); }); afterEach(() => { // Cleanup after each test jest.restoreAllMocks(); }); describe('Rendering', () => { it('should render without crashing', () => { render(<${componentName} />); expect(screen.getByTestId('${componentName.toLowerCase()}')).toBeInTheDocument(); }); it('should render with default content', () => { render(<${componentName} />); // Add specific content assertions based on your component expect(screen.getByRole('main')).toBeInTheDocument(); }); ${hasProps ? ` it('should render with custom props', () => { const props = { title: 'Test Title', variant: 'primary', disabled: false, }; render(<${componentName} {...props} />); expect(screen.getByText('Test Title')).toBeInTheDocument(); });` : ''} }); ${hasState ? ` describe('State Management', () => { it('should handle state changes correctly', async () => { render(<${componentName} />); const button = screen.getByRole('button'); await user.click(button); await waitFor(() => { expect(screen.getByText(/updated/i)).toBeInTheDocument(); }); }); });` : ''} describe('User Interactions', () => { it('should handle click events', async () => { const mockOnClick = jest.fn(); render(<${componentName} onClick={mockOnClick} />); const clickableElement = screen.getByRole('button'); await user.click(clickableElement); expect(mockOnClick).toHaveBeenCalledTimes(1); }); it('should handle keyboard events', async () => { const mockOnKeyDown = jest.fn(); render(<${componentName} onKeyDown={mockOnKeyDown} />); const element = screen.getByRole('button'); await user.type(element, '{enter}'); expect(mockOnKeyDown).toHaveBeenCalled(); }); }); ${hasAsync ? ` describe('Async Operations', () => { it('should handle loading states', async () => { render(<${componentName} />); // Should show loading state initially expect(screen.getByText(/loading/i)).toBeInTheDocument(); // Wait for content to load await waitFor(() => { expect(screen.queryByText(/loading/i)).not.toBeInTheDocument(); }); }); it('should handle error states', async () => { // Mock API failure jest.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('API Error')); render(<${componentName} />); await waitFor(() => { expect(screen.getByText(/error/i)).toBeInTheDocument(); }); }); });` : ''} describe('Edge Cases', () => { it('should handle null/undefined props gracefully', () => { const props = { data: null, items: undefined, }; expect(() => render(<${componentName} {...props} />)).not.toThrow(); }); it('should handle empty data', () => { const props = { items: [], text: '', }; render(<${componentName} {...props} />); expect(screen.getByText(/no items/i)).toBeInTheDocument(); }); }); describe('Prop Types & Validation', () => { it('should handle invalid prop types gracefully', () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); // Test with invalid props (if using PropTypes) render(<${componentName} count="invalid" />); // Should not crash, might show console error in development expect(consoleSpy).toHaveBeenCalledTimes(0); // Adjust based on your validation consoleSpy.mockRestore(); }); }); });`; } /** * Generate integration test for React components */ static generateIntegrationComponentTest(options) { const { componentName, filePath, hasContext = false } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ${componentName} } from '${importPath}'; ${hasContext ? `import { TestProvider } from '../../../test-utils/TestProvider';` : ''} // Integration test setup - minimal mocking const renderWithProviders = (ui: React.ReactElement, options = {}) => { return render( ${hasContext ? `<TestProvider> {ui} </TestProvider>` : 'ui'}, options ); }; describe('${componentName} - Integration Tests', () => { const user = userEvent.setup(); beforeEach(() => { // Setup real API mocks or test server jest.clearAllMocks(); }); describe('Component Integration', () => { it('should integrate with parent components correctly', async () => { renderWithProviders(<${componentName} />); // Test integration with parent state/context const element = screen.getByTestId('${componentName.toLowerCase()}'); expect(element).toBeInTheDocument(); }); ${hasContext ? ` it('should consume context correctly', async () => { renderWithProviders(<${componentName} />); // Verify context consumption await waitFor(() => { expect(screen.getByText(/context value/i)).toBeInTheDocument(); }); });` : ''} it('should handle component lifecycle with real dependencies', async () => { renderWithProviders(<${componentName} />); // Test mounting, updating, unmounting behaviors const button = screen.getByRole('button'); await user.click(button); await waitFor(() => { expect(screen.getByText(/updated/i)).toBeInTheDocument(); }); }); }); describe('API Integration', () => { it('should handle real API calls (with test server)', async () => { // Use MSW or similar for real API testing renderWithProviders(<${componentName} />); await waitFor(() => { expect(screen.getByText(/data loaded/i)).toBeInTheDocument(); }); }); it('should handle API errors gracefully', async () => { // Mock API error response renderWithProviders(<${componentName} />); await waitFor(() => { expect(screen.getByText(/error occurred/i)).toBeInTheDocument(); }); }); }); describe('State Persistence', () => { it('should maintain state across re-renders', async () => { const { rerender } = renderWithProviders(<${componentName} initialValue="test" />); const input = screen.getByRole('textbox'); await user.type(input, 'user input'); rerender(<${componentName} initialValue="test" />); expect(input).toHaveValue('testuser input'); }); }); });`; } /** * Generate accessibility test */ static generateAccessibilityTest(options) { const { componentName, filePath } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { axe, toHaveNoViolations } from 'jest-axe'; import { ${componentName} } from '${importPath}'; // Extend Jest matchers expect.extend(toHaveNoViolations); describe('${componentName} - Accessibility Tests', () => { const user = userEvent.setup(); describe('WCAG Compliance', () => { it('should not have accessibility violations', async () => { const { container } = render(<${componentName} />); const results = await axe(container); expect(results).toHaveNoViolations(); }); it('should have proper ARIA labels', () => { render(<${componentName} />); // Check for ARIA labels const element = screen.getByRole('button'); expect(element).toHaveAccessibleName(); }); it('should support keyboard navigation', async () => { render(<${componentName} />); const element = screen.getByRole('button'); element.focus(); expect(element).toHaveFocus(); await user.keyboard('{Tab}'); // Verify focus moves to next element }); it('should have proper color contrast', () => { render(<${componentName} />); // Test color contrast (can be automated with specific tools) const element = screen.getByRole('button'); const styles = window.getComputedStyle(element); // Add contrast ratio checks if needed expect(styles.color).toBeDefined(); expect(styles.backgroundColor).toBeDefined(); }); it('should work with screen readers', () => { render(<${componentName} />); // Test ARIA attributes const element = screen.getByRole('button'); expect(element).toHaveAttribute('aria-label'); }); it('should handle focus management', async () => { render(<${componentName} />); const element = screen.getByRole('button'); await user.click(element); // Verify focus is managed correctly after interaction expect(document.activeElement).toBe(element); }); }); describe('Screen Reader Support', () => { it('should provide meaningful text alternatives', () => { render(<${componentName} />); // Check for alt text, ARIA labels, etc. const images = screen.getAllByRole('img'); images.forEach(img => { expect(img).toHaveAccessibleName(); }); }); it('should announce dynamic content changes', async () => { render(<${componentName} />); // Test live regions for dynamic content const liveRegion = screen.getByRole('status'); expect(liveRegion).toBeInTheDocument(); }); }); describe('Motor Impairment Support', () => { it('should have sufficiently large click targets', () => { render(<${componentName} />); const button = screen.getByRole('button'); const rect = button.getBoundingClientRect(); // WCAG AA: 44x44px minimum expect(rect.width).toBeGreaterThanOrEqual(44); expect(rect.height).toBeGreaterThanOrEqual(44); }); it('should support alternative input methods', async () => { render(<${componentName} />); const element = screen.getByRole('button'); // Test keyboard activation element.focus(); await user.keyboard('{Enter}'); await user.keyboard(' '); // Space bar // Verify both methods work }); }); });`; } /** * Generate basic component test (fallback) */ static generateBasicComponentTest(options) { const { componentName, filePath } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { render, screen } from '@testing-library/react'; import { ${componentName} } from '${importPath}'; describe('${componentName}', () => { it('should render successfully', () => { render(<${componentName} />); expect(screen.getByTestId('${componentName.toLowerCase()}')).toBeInTheDocument(); }); it('should display expected content', () => { render(<${componentName} />); // Add specific assertions based on your component expect(screen.getByRole('main')).toBeInTheDocument(); }); });`; } /** * Generate unit test for utility functions */ static generateUnitFunctionTest(options) { const { componentName, filePath } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { ${componentName} } from '${importPath}'; describe('${componentName} - Unit Tests', () => { describe('Input Validation', () => { it('should return expected result for valid input', () => { const validInput = 'test input'; const result = ${componentName}(validInput); expect(result).toBeDefined(); expect(typeof result).toBe('string'); // Adjust type as needed }); it('should handle edge cases', () => { expect(() => ${componentName}(null)).toThrow(); expect(() => ${componentName}(undefined)).toThrow(); expect(${componentName}('')).toBe(''); // Or appropriate default }); it('should handle invalid input types', () => { expect(() => ${componentName}(123 as any)).toThrow(); expect(() => ${componentName}({} as any)).toThrow(); expect(() => ${componentName}([] as any)).toThrow(); }); }); describe('Business Logic', () => { it('should process data correctly', () => { const testData = { // Add test data structure }; const result = ${componentName}(testData); expect(result).toEqual({ // Expected result structure }); }); it('should handle boundary conditions', () => { // Test minimum values expect(${componentName}(0)).toBeDefined(); // Test maximum values (if applicable) expect(${componentName}(Number.MAX_SAFE_INTEGER)).toBeDefined(); }); }); describe('Performance', () => { it('should execute within reasonable time', () => { const start = performance.now(); ${componentName}('performance test input'); const end = performance.now(); expect(end - start).toBeLessThan(100); // 100ms threshold }); it('should handle large inputs efficiently', () => { const largeInput = 'x'.repeat(10000); expect(() => ${componentName}(largeInput)).not.toThrow(); }); }); });`; } /** * Generate integration test for functions */ static generateIntegrationFunctionTest(options) { const { componentName, filePath } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { ${componentName} } from '${importPath}'; describe('${componentName} - Integration Tests', () => { beforeEach(() => { // Setup integration test environment jest.clearAllMocks(); }); describe('External Dependencies', () => { it('should integrate with external services', async () => { // Test real API calls, file system operations, etc. const result = await ${componentName}('integration test input'); expect(result).toBeDefined(); }); it('should handle service failures gracefully', async () => { // Mock service failure jest.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('Service unavailable')); await expect(${componentName}('test')).rejects.toThrow('Service unavailable'); }); }); describe('Data Persistence', () => { it('should persist data correctly', async () => { const testData = { key: 'value' }; await ${componentName}(testData); // Verify data was persisted // This would depend on your persistence mechanism }); }); });`; } /** * Generate basic function test (fallback) */ static generateBasicFunctionTest(options) { const { componentName, filePath } = options; const importPath = filePath.replace(/\.(jsx?|tsx?)$/, ''); return `import { ${componentName} } from '${importPath}'; describe('${componentName}', () => { it('should be defined', () => { expect(${componentName}).toBeDefined(); expect(typeof ${componentName}).toBe('function'); }); it('should return expected result', () => { const result = ${componentName}('test input'); expect(result).toBeDefined(); }); it('should handle edge cases', () => { expect(() => ${componentName}(null)).not.toThrow(); expect(() => ${componentName}(undefined)).not.toThrow(); }); });`; } } //# sourceMappingURL=jest-generator.js.map