UNPKG

@claude-powers/slash-commands

Version:

πŸš€ Claude Powers - Essential slash commands for Claude Code

555 lines (465 loc) β€’ 17.3 kB
# Generate Tests Command Automatically generates comprehensive tests using advanced AI to maximize coverage, quality and edge cases with minimal effort. ## Description The `/generate-tests` command revolutionizes test writing by completely automating the process: - **Automatic generation** of unit, integration and E2E tests - **Code analysis** to identify all necessary test cases - **Automatic edge cases** that humans often forget - **Intelligent mocks** for external dependencies - **Optimized coverage** to reach desired metrics - **Multiple frameworks** (Jest, Vitest, Cypress, Playwright, etc.) - **Best practices patterns** in testing built-in - **Maintainable tests** with clear naming and logical structure ## Usage ``` /generate-tests [file/directory] [--type] [--framework] [--coverage] [--mocks] ``` ### Parameters - `file/directory`: Source code for which to generate tests - `--type`: Types of tests (unit, integration, e2e, performance, accessibility) - `--framework`: Testing framework (jest, vitest, cypress, playwright, mocha) - `--coverage`: Coverage target (80%, 90%, 95%) - `--mocks`: Mocking strategy (auto, manual, none) - `--patterns`: Testing patterns (aaa, bdd, tdd) - `--edge-cases`: Edge cases level (basic, comprehensive, paranoid) - `--performance`: Include performance tests - `--accessibility`: Include accessibility tests ### Examples ``` /generate-tests src/components/UserProfile.tsx /generate-tests --type=unit,integration --coverage=95% /generate-tests api/ --framework=jest --mocks=auto /generate-tests --edge-cases=comprehensive --patterns=bdd /generate-tests frontend/ --type=e2e --framework=playwright /generate-tests utils/ --performance=true --coverage=100% ``` ## Generated Test Types ### πŸ§ͺ Unit Tests ```javascript // Auto-generado para UserProfile.tsx describe('UserProfile Component', () => { const mockUser = { id: '123', name: 'John Doe', email: 'john@example.com', avatar: 'https://example.com/avatar.jpg' }; beforeEach(() => { render(<UserProfile user={mockUser} />); }); describe('Rendering', () => { it('should render user name correctly', () => { expect(screen.getByText('John Doe')).toBeInTheDocument(); }); it('should render user email correctly', () => { expect(screen.getByText('john@example.com')).toBeInTheDocument(); }); it('should render avatar with correct src', () => { const avatar = screen.getByAltText('John Doe avatar'); expect(avatar).toHaveAttribute('src', 'https://example.com/avatar.jpg'); }); }); describe('Edge Cases', () => { it('should handle missing avatar gracefully', () => { const userWithoutAvatar = { ...mockUser, avatar: null }; render(<UserProfile user={userWithoutAvatar} />); expect(screen.getByText('JD')).toBeInTheDocument(); // Initials fallback }); it('should handle extremely long names', () => { const userWithLongName = { ...mockUser, name: 'A'.repeat(100) }; render(<UserProfile user={userWithLongName} />); expect(screen.getByText(/A{50}\.\.\.$/)).toBeInTheDocument(); // Truncated }); it('should handle special characters in name', () => { const userWithSpecialChars = { ...mockUser, name: 'JosΓ© MarΓ­a Aznar-LΓ³pez' }; render(<UserProfile user={userWithSpecialChars} />); expect(screen.getByText('JosΓ© MarΓ­a Aznar-LΓ³pez')).toBeInTheDocument(); }); }); describe('Interactions', () => { it('should call onEdit when edit button is clicked', () => { const mockOnEdit = jest.fn(); render(<UserProfile user={mockUser} onEdit={mockOnEdit} />); fireEvent.click(screen.getByRole('button', { name: /edit/i })); expect(mockOnEdit).toHaveBeenCalledWith(mockUser.id); }); it('should handle rapid multiple clicks gracefully', () => { const mockOnEdit = jest.fn(); render(<UserProfile user={mockUser} onEdit={mockOnEdit} />); const editButton = screen.getByRole('button', { name: /edit/i }); fireEvent.click(editButton); fireEvent.click(editButton); fireEvent.click(editButton); expect(mockOnEdit).toHaveBeenCalledTimes(1); // Debounced }); }); describe('Accessibility', () => { it('should have proper ARIA labels', () => { render(<UserProfile user={mockUser} />); expect(screen.getByRole('img')).toHaveAttribute('alt', 'John Doe avatar'); expect(screen.getByRole('button', { name: /edit/i })).toBeInTheDocument(); }); it('should be keyboard navigable', () => { render(<UserProfile user={mockUser} />); const editButton = screen.getByRole('button', { name: /edit/i }); expect(editButton).toHaveAttribute('tabIndex', '0'); }); }); describe('Performance', () => { it('should render within performance budget', async () => { const start = performance.now(); render(<UserProfile user={mockUser} />); const end = performance.now(); expect(end - start).toBeLessThan(16); // 60fps budget }); it('should not cause memory leaks', () => { const { unmount } = render(<UserProfile user={mockUser} />); unmount(); // Verificar que no hay listeners activos }); }); }); ``` ### πŸ”— Integration Tests ```javascript // Auto-generado para UserService integration describe('UserService Integration', () => { let userService; let mockApiClient; beforeEach(() => { mockApiClient = { get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn() }; userService = new UserService(mockApiClient); }); describe('User CRUD Operations', () => { it('should fetch user by id successfully', async () => { const mockUser = { id: '123', name: 'John Doe' }; mockApiClient.get.mockResolvedValue({ data: mockUser }); const result = await userService.getUserById('123'); expect(mockApiClient.get).toHaveBeenCalledWith('/users/123'); expect(result).toEqual(mockUser); }); it('should handle network errors gracefully', async () => { mockApiClient.get.mockRejectedValue(new Error('Network Error')); await expect(userService.getUserById('123')).rejects.toThrow('Failed to fetch user'); }); it('should retry failed requests with exponential backoff', async () => { mockApiClient.get .mockRejectedValueOnce(new Error('500')) .mockRejectedValueOnce(new Error('500')) .mockResolvedValue({ data: { id: '123' } }); const result = await userService.getUserById('123'); expect(mockApiClient.get).toHaveBeenCalledTimes(3); expect(result).toEqual({ id: '123' }); }); }); describe('Caching Behavior', () => { it('should cache successful responses', async () => { const mockUser = { id: '123', name: 'John Doe' }; mockApiClient.get.mockResolvedValue({ data: mockUser }); await userService.getUserById('123'); await userService.getUserById('123'); expect(mockApiClient.get).toHaveBeenCalledTimes(1); }); it('should invalidate cache after TTL', async () => { jest.useFakeTimers(); const mockUser = { id: '123', name: 'John Doe' }; mockApiClient.get.mockResolvedValue({ data: mockUser }); await userService.getUserById('123'); jest.advanceTimersByTime(300000); // 5 minutes await userService.getUserById('123'); expect(mockApiClient.get).toHaveBeenCalledTimes(2); jest.useRealTimers(); }); }); }); ``` ### 🌐 E2E Tests ```javascript // Auto-generado con Playwright describe('User Management E2E', () => { test('complete user registration flow', async ({ page }) => { // Navigate to registration await page.goto('/register'); // Fill registration form await page.fill('[data-testid="email"]', 'test@example.com'); await page.fill('[data-testid="password"]', 'SecurePass123!'); await page.fill('[data-testid="confirmPassword"]', 'SecurePass123!'); // Submit form await page.click('[data-testid="submit"]'); // Verify email verification screen await expect(page.locator('h1')).toContainText('Check your email'); // Simulate email verification click await page.goto('/verify-email?token=mock-token'); // Verify successful registration await expect(page.locator('[data-testid="welcome-message"]')) .toContainText('Welcome to the platform!'); }); test('handles registration errors gracefully', async ({ page }) => { await page.goto('/register'); // Try to register with existing email await page.fill('[data-testid="email"]', 'existing@example.com'); await page.fill('[data-testid="password"]', 'SecurePass123!'); await page.fill('[data-testid="confirmPassword"]', 'SecurePass123!'); await page.click('[data-testid="submit"]'); // Verify error message await expect(page.locator('[data-testid="error-message"]')) .toContainText('Email already exists'); }); test('form validation works correctly', async ({ page }) => { await page.goto('/register'); // Submit empty form await page.click('[data-testid="submit"]'); // Verify validation errors await expect(page.locator('[data-testid="email-error"]')) .toContainText('Email is required'); await expect(page.locator('[data-testid="password-error"]')) .toContainText('Password is required'); }); }); ``` ## ConfiguraciΓ³n El comando utiliza `.claude/generate-tests-config.json`: ```json { "defaultFramework": "jest", "defaultCoverage": 80, "testTypes": { "unit": { "enabled": true, "patterns": ["aaa", "bdd"], "mockStrategy": "auto", "edgeCases": "comprehensive" }, "integration": { "enabled": true, "apiMocking": true, "databaseMocking": "in-memory", "cacheTests": true }, "e2e": { "enabled": false, "framework": "playwright", "browsers": ["chromium", "firefox"], "devices": ["desktop", "mobile"] }, "performance": { "enabled": false, "budgets": { "renderTime": "16ms", "bundleSize": "500KB", "memoryUsage": "50MB" } }, "accessibility": { "enabled": true, "standards": ["wcag2.1", "section508"], "levels": ["AA"] } }, "frameworks": { "jest": { "setupFiles": ["@testing-library/jest-dom"], "testEnvironment": "jsdom", "collectCoverageFrom": ["src/**/*.{js,jsx,ts,tsx}"] }, "vitest": { "environment": "jsdom", "setupFiles": ["@testing-library/jest-dom"] }, "cypress": { "baseUrl": "http://localhost:3000", "viewportWidth": 1280, "viewportHeight": 720 }, "playwright": { "baseURL": "http://localhost:3000", "use": { "screenshot": "only-on-failure", "video": "retain-on-failure" } } }, "mocking": { "apiRequests": { "strategy": "msw", "generateMockData": true, "realisticData": true }, "modules": { "strategy": "auto", "preserveImplementation": false }, "dependencies": { "external": "mock", "internal": "spy" } }, "codeAnalysis": { "detectTestableUnits": true, "identifyEdgeCases": true, "analyzeDependencies": true, "extractBusinessLogic": true } } ``` ## Salida del Comando ### AnΓ‘lisis Pre-generaciΓ³n ``` πŸ§ͺ CLAUDE POWER - TEST GENERATION ANALYSIS ========================================== πŸ“ CΓ“DIGO ANALIZADO: src/components/UserProfile.tsx (127 lΓ­neas) β€’ 3 props analizadas (user, onEdit, className) β€’ 5 estados internos detectados β€’ 8 mΓ©all identificados β€’ 12 casos edge identificados πŸ“Š PLAN DE TESTS: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Tipo Test β”‚ Cantidadβ”‚ Cobertura β”‚ Tiempo Est. β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Unit Tests β”‚ 24 β”‚ 95% β”‚ 8 min β”‚ β”‚ Integration β”‚ 6 β”‚ 85% β”‚ 4 min β”‚ β”‚ E2E Tests β”‚ 3 β”‚ 70% β”‚ 12 min β”‚ β”‚ A11y Tests β”‚ 8 β”‚ 90% β”‚ 3 min β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 🎯 CASOS DETECTADOS: βœ… Rendering con props vΓ‘lidas βœ… Manejo de props faltantes/nulas βœ… Eventos de interacciΓ³n βœ… Estados de loading/error βœ… Casos edge (nombres largos, caracteres especiales) βœ… Accessibility compliance βœ… Performance budgets βœ… Mobile responsiveness ``` ### Tests Generados ``` πŸ“ TESTS GENERADOS EXITOSAMENTE: =============================== πŸ“ files creados: β€’ src/components/__tests__/UserProfile.test.tsx (312 lΓ­neas) β€’ src/components/__tests__/UserProfile.integration.test.tsx (156 lΓ­neas) β€’ e2e/user-profile.spec.ts (89 lΓ­neas) β€’ src/components/__tests__/UserProfile.a11y.test.tsx (67 lΓ­neas) πŸ“Š COBERTURA PROYECTADA: β€’ Statements: 96.8% β€’ Branches: 94.2% β€’ Functions: 100% β€’ Lines: 95.9% πŸ§ͺ RESUMEN DE TESTS: β€’ Total tests: 41 β€’ Unit tests: 24 (βœ… Rendering, interactions, edge cases) β€’ Integration tests: 6 (βœ… API calls, state management) β€’ E2E tests: 3 (βœ… User flows completos) β€’ Accessibility tests: 8 (βœ… WCAG 2.1 compliance) ⚑ PERFORMANCE: β€’ Tiempo de ejecuciΓ³n estimado: 12.4s β€’ Memory usage: 85MB β€’ CPU usage: Medium 🎯 CALIDAD: β€’ Best practices: βœ… Implementadas β€’ Mocking strategy: βœ… Optimizada β€’ Test maintainability: βœ… Alta β€’ Edge cases coverage: βœ… Comprehensive ``` ## IntegraciΓ³n Avanzada ### CI/CD Pipeline ```yaml name: Generated Tests on: push: branches: [main, develop] pull_request: branches: [main] jobs: test-generated: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Generate missing tests run: | npx claude-power generate-tests \ --type=unit,integration \ --coverage=90% \ --auto-update - name: Run generated tests run: npm test -- --coverage --passWithNoTests - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage/lcov.info - name: Comment PR with coverage uses: actions/github-script@v6 with: script: | const fs = require('fs'); const coverage = JSON.parse(fs.readFileSync('./coverage/coverage-summary.json')); const comment = ` ## πŸ§ͺ Test Coverage Report | Metric | Percentage | Status | |--------|------------|--------| | Statements | ${coverage.total.statements.pct}% | ${coverage.total.statements.pct >= 90 ? 'βœ…' : '⚠️'} | | Branches | ${coverage.total.branches.pct}% | ${coverage.total.branches.pct >= 90 ? 'βœ…' : '⚠️'} | | Functions | ${coverage.total.functions.pct}% | ${coverage.total.functions.pct >= 90 ? 'βœ…' : '⚠️'} | | Lines | ${coverage.total.lines.pct}% | ${coverage.total.lines.pct >= 90 ? 'βœ…' : '⚠️'} | Generated by Claude Power Test Generator πŸ€– `; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment }); ``` ### VS Code Integration ```json { "commands": [ { "command": "claude-power.generateTests", "title": "Generate Tests", "category": "Claude Power" } ], "keybindings": [ { "command": "claude-power.generateTests", "key": "ctrl+shift+t", "when": "editorTextFocus" } ], "tasks": [ { "label": "Generate Tests for Current File", "type": "shell", "command": "npx", "args": [ "claude-power", "generate-tests", "${file}", "--coverage=90%" ] } ] } ``` --- *Parte del ecosistema **Claude Power** - Tests perfectos sin esfuerzo* πŸ§ͺπŸš€