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
JavaScript
/**
* 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