UNPKG

cqt-agent

Version:
613 lines (554 loc) • 19 kB
# Test Failure Analyzer This utility analyzes test failures and provides specific, actionable remediation steps to help users resolve issues quickly and effectively. ## Purpose Automatically categorize test failures, identify root causes, and provide clear, step-by-step remediation instructions with code examples and scripts. ## Failure Analysis Framework ### 1. Failure Categorization System ```javascript const FailureCategories = { SYNTAX_ERROR: { priority: 'HIGH', category: 'syntax', description: 'Code syntax or compilation errors', fixable: true }, ENVIRONMENT_ERROR: { priority: 'HIGH', category: 'environment', description: 'Missing dependencies, configuration, or runtime issues', fixable: true }, LOGIC_ERROR: { priority: 'MEDIUM', category: 'logic', description: 'Test logic errors or incorrect assertions', fixable: true }, DATA_ERROR: { priority: 'MEDIUM', category: 'data', description: 'Test data issues or database problems', fixable: true }, TIMING_ERROR: { priority: 'LOW', category: 'timing', description: 'Race conditions or timeout issues', fixable: true }, EXTERNAL_ERROR: { priority: 'LOW', category: 'external', description: 'Third-party service or network issues', fixable: false } }; ``` ### 2. Error Pattern Recognition ```javascript const ErrorPatterns = { // Syntax Errors typescript: [ { pattern: /Cannot find module ['"`](.+)['"`]/, category: 'SYNTAX_ERROR', message: 'Missing import or module not found', solution: 'install_missing_dependency' }, { pattern: /Property ['"`](.+)['"`] does not exist on type/, category: 'SYNTAX_ERROR', message: 'TypeScript type error', solution: 'fix_typescript_types' }, { pattern: /Expected (\d+) arguments, but got (\d+)/, category: 'SYNTAX_ERROR', message: 'Function call argument mismatch', solution: 'fix_function_arguments' } ], // Environment Errors environment: [ { pattern: /ECONNREFUSED.*:(\d+)/, category: 'ENVIRONMENT_ERROR', message: 'Cannot connect to service on port', solution: 'start_required_service' }, { pattern: /Command ['"`](.+)['"`] not found/, category: 'ENVIRONMENT_ERROR', message: 'Required command or tool not installed', solution: 'install_missing_tool' }, { pattern: /Permission denied/, category: 'ENVIRONMENT_ERROR', message: 'Insufficient permissions', solution: 'fix_permissions' } ], // Test Logic Errors playwright: [ { pattern: /locator\.click: Timeout (\d+)ms exceeded/, category: 'LOGIC_ERROR', message: 'Element not found or not clickable within timeout', solution: 'fix_element_locator' }, { pattern: /expect.*\.toHaveText.*received/, category: 'LOGIC_ERROR', message: 'Text content assertion failed', solution: 'update_text_assertion' }, { pattern: /Navigation timeout exceeded/, category: 'TIMING_ERROR', message: 'Page navigation too slow', solution: 'increase_navigation_timeout' } ], // API Test Errors karate: [ { pattern: /status code was: (\d+), expected: (\d+)/, category: 'LOGIC_ERROR', message: 'API response status code mismatch', solution: 'fix_api_expectation' }, { pattern: /path not found: (.+)/, category: 'DATA_ERROR', message: 'JSON path assertion failed', solution: 'fix_json_path' }, { pattern: /connection refused/i, category: 'ENVIRONMENT_ERROR', message: 'Cannot connect to API server', solution: 'start_api_server' } ] }; ``` ## Failure Analysis Engine ### 1. Error Log Parser ```javascript function parseTestFailures(testResults) { const failures = []; // Parse different test result formats if (testResults.type === 'playwright') { testResults.suites.forEach(suite => { suite.tests.forEach(test => { if (test.outcome === 'failed') { failures.push({ testName: test.title, testFile: suite.file, error: test.error, framework: 'playwright', rawLog: test.stderr }); } }); }); } if (testResults.type === 'karate') { testResults.scenarioResults.forEach(scenario => { if (!scenario.passed) { failures.push({ testName: scenario.scenario.name, testFile: scenario.scenario.feature.resource.file, error: scenario.error, framework: 'karate', rawLog: scenario.stepResults.map(s => s.error).filter(Boolean).join('\n') }); } }); } if (testResults.type === 'jest') { testResults.testResults.forEach(testFile => { testFile.assertionResults.forEach(test => { if (test.status === 'failed') { failures.push({ testName: test.title, testFile: testFile.testFilePath, error: test.failureMessages.join('\n'), framework: 'jest', rawLog: test.failureMessages.join('\n') }); } }); }); } return failures; } ``` ### 2. Root Cause Analysis ```javascript function analyzeFailure(failure) { const analysis = { testName: failure.testName, testFile: failure.testFile, framework: failure.framework, category: 'UNKNOWN', rootCause: 'Unable to determine root cause', confidence: 0, remediation: [] }; // Match against known error patterns for (const [framework, patterns] of Object.entries(ErrorPatterns)) { if (framework === failure.framework || framework === 'environment') { for (const pattern of patterns) { const match = failure.error.match(pattern.pattern); if (match) { analysis.category = pattern.category; analysis.rootCause = pattern.message; analysis.confidence = 0.9; analysis.patternMatch = match; analysis.solutionKey = pattern.solution; break; } } } } // Additional heuristic analysis if (analysis.confidence < 0.5) { analysis = performHeuristicAnalysis(failure, analysis); } // Generate remediation steps analysis.remediation = generateRemediation(analysis); return analysis; } function performHeuristicAnalysis(failure, analysis) { const error = failure.error.toLowerCase(); // Check for common indicators if (error.includes('timeout') || error.includes('exceeded')) { analysis.category = 'TIMING_ERROR'; analysis.rootCause = 'Operation timed out'; analysis.confidence = 0.7; } else if (error.includes('not found') || error.includes('404')) { analysis.category = 'LOGIC_ERROR'; analysis.rootCause = 'Resource or element not found'; analysis.confidence = 0.7; } else if (error.includes('permission') || error.includes('403')) { analysis.category = 'ENVIRONMENT_ERROR'; analysis.rootCause = 'Permission denied'; analysis.confidence = 0.7; } else if (error.includes('network') || error.includes('econnreset')) { analysis.category = 'EXTERNAL_ERROR'; analysis.rootCause = 'Network connectivity issue'; analysis.confidence = 0.6; } return analysis; } ``` ### 3. Remediation Generator ```javascript const RemediationStrategies = { install_missing_dependency: (analysis) => ({ priority: 'HIGH', estimatedTime: '2-5 minutes', steps: [ { action: 'Install missing dependency', command: `npm install ${analysis.patternMatch[1]}`, description: `Install the missing module: ${analysis.patternMatch[1]}` }, { action: 'Verify installation', command: `npm list ${analysis.patternMatch[1]}`, description: 'Confirm the dependency was installed correctly' }, { action: 'Re-run tests', command: 'npm test', description: 'Execute tests again to verify fix' } ], preventionTips: [ 'Run "npm ci" before test execution', 'Keep package.json dependencies up to date', 'Use package-lock.json for consistent dependencies' ] }), fix_typescript_types: (analysis) => ({ priority: 'HIGH', estimatedTime: '5-10 minutes', steps: [ { action: 'Identify missing type', description: `Property '${analysis.patternMatch[1]}' is not recognized`, manual: true }, { action: 'Add type definition', codeExample: ` interface ExpectedType { ${analysis.patternMatch[1]}: string; // Adjust type as needed }`, description: 'Add the missing property to your interface' }, { action: 'Update import statements', description: 'Ensure all required types are imported' } ], automaticFix: { available: true, script: generateTypeFixScript(analysis) } }), start_required_service: (analysis) => ({ priority: 'HIGH', estimatedTime: '1-3 minutes', steps: [ { action: 'Check service status', command: `netstat -tulpn | grep :${analysis.patternMatch[1]}`, description: `Verify if service is running on port ${analysis.patternMatch[1]}` }, { action: 'Start database service', command: 'docker-compose up -d db', description: 'Start the required database service', condition: 'if database port (5432, 3306, 27017)' }, { action: 'Start application server', command: 'npm run start:dev', description: 'Start the application server', condition: 'if application port (3000, 8080, etc.)' }, { action: 'Wait for service ready', command: `until nc -z localhost ${analysis.patternMatch[1]}; do sleep 1; done`, description: 'Wait for service to be available' } ], healthCheck: { command: `curl -f http://localhost:${analysis.patternMatch[1]}/health || echo "Service not ready"`, description: 'Verify service health' } }), fix_element_locator: (analysis) => ({ priority: 'MEDIUM', estimatedTime: '5-15 minutes', steps: [ { action: 'Inspect element selectors', description: 'Open browser dev tools and verify element exists', manual: true }, { action: 'Try alternative selectors', codeExample: ` // Instead of: await page.locator('[data-testid=old-selector]').click(); // Try these alternatives: await page.locator('text="Button Text"').click(); await page.locator('[aria-label="Button Label"]').click(); await page.locator('button:has-text("Submit")').click();`, description: 'Use more robust selector strategies' }, { action: 'Add explicit wait', codeExample: ` await page.waitForSelector('[data-testid=element]', { state: 'visible' }); await page.locator('[data-testid=element]').click();`, description: 'Wait for element to be ready before interaction' } ], debuggingTips: [ 'Use page.screenshot() to capture current page state', 'Add console.log to verify page navigation', 'Check if element is in viewport with scrollIntoView()' ] }) }; function generateRemediation(analysis) { const strategy = RemediationStrategies[analysis.solutionKey]; if (strategy) { return strategy(analysis); } // Generic remediation for unknown issues return { priority: 'MEDIUM', estimatedTime: '10-30 minutes', steps: [ { action: 'Review test logs', description: 'Examine the full error message and stack trace' }, { action: 'Check test environment', description: 'Verify all services and dependencies are running' }, { action: 'Run test in isolation', description: 'Execute the failing test individually to isolate the issue' }, { action: 'Consult documentation', description: 'Review framework documentation for similar issues' } ] }; } ``` ## User-Friendly Report Generation ### 1. Failure Summary Report ```javascript function generateFailureReport(failures) { const analyses = failures.map(analyzeFailure); const report = { summary: { totalFailures: failures.length, categorized: groupBy(analyses, 'category'), highPriority: analyses.filter(a => a.category === 'SYNTAX_ERROR' || a.category === 'ENVIRONMENT_ERROR').length, autoFixable: analyses.filter(a => a.remediation?.automaticFix?.available).length }, detailedAnalysis: analyses.map(formatAnalysisForUser), quickActions: generateQuickActions(analyses), executionScript: generateFixScript(analyses) }; return report; } function formatAnalysisForUser(analysis) { return { testFile: analysis.testFile, testName: analysis.testName, issue: { category: FailureCategories[analysis.category]?.description || 'Unknown issue', rootCause: analysis.rootCause, confidence: `${Math.round(analysis.confidence * 100)}% confident` }, remedy: { priority: analysis.remediation?.priority || 'MEDIUM', estimatedTime: analysis.remediation?.estimatedTime || 'Unknown', steps: analysis.remediation?.steps || [], autoFixAvailable: !!analysis.remediation?.automaticFix } }; } ``` ### 2. Interactive Fix Script Generator ```javascript function generateFixScript(analyses) { const script = ['#!/bin/bash', '# Automated Test Failure Fixes', '']; // Group fixes by priority const highPriorityFixes = analyses.filter(a => a.category === 'SYNTAX_ERROR' || a.category === 'ENVIRONMENT_ERROR'); const mediumPriorityFixes = analyses.filter(a => a.category === 'LOGIC_ERROR' || a.category === 'DATA_ERROR'); if (highPriorityFixes.length > 0) { script.push('echo "šŸ”§ Applying high-priority fixes..."'); highPriorityFixes.forEach(analysis => { if (analysis.remediation?.steps) { analysis.remediation.steps.forEach(step => { if (step.command) { script.push(`echo "Executing: ${step.description}"`); script.push(step.command); script.push(''); } }); } }); } script.push('echo "āœ… High-priority fixes completed. Re-running tests..."'); script.push('npm test'); return script.join('\n'); } ``` ### 3. HTML Report Generator ```javascript function generateHTMLReport(failureReport) { return ` <!DOCTYPE html> <html> <head> <title>Test Failure Analysis Report</title> <style> body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; } .header { text-align: center; border-bottom: 2px solid #007bff; padding-bottom: 15px; } .summary-card { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 15px 0; } .failure-item { background: white; border-left: 4px solid #dc3545; padding: 15px; margin: 10px 0; } .high-priority { border-left-color: #dc3545; } .medium-priority { border-left-color: #ffc107; } .low-priority { border-left-color: #28a745; } .remediation-steps { background: #e9ecef; padding: 10px; border-radius: 3px; } .command { background: #2d3748; color: #e2e8f0; padding: 8px; border-radius: 3px; font-family: monospace; } .auto-fix-btn { background: #28a745; color: white; padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; } </style> </head> <body> <div class="container"> <div class="header"> <h1>šŸ” Test Failure Analysis Report</h1> <p>Generated on ${new Date().toLocaleString()}</p> </div> <div class="summary-card"> <h2>šŸ“Š Summary</h2> <p><strong>Total Failures:</strong> ${failureReport.summary.totalFailures}</p> <p><strong>High Priority Issues:</strong> ${failureReport.summary.highPriority}</p> <p><strong>Auto-fixable Issues:</strong> ${failureReport.summary.autoFixable}</p> </div> <div class="quick-actions"> <h2>⚔ Quick Actions</h2> <button class="auto-fix-btn" onclick="downloadFixScript()">Download Fix Script</button> <button class="auto-fix-btn" onclick="runAutoFixes()">Run Auto-Fixes</button> </div> <div class="failures-list"> <h2>🚨 Detailed Analysis</h2> ${failureReport.detailedAnalysis.map(renderFailureItem).join('')} </div> </div> <script> function downloadFixScript() { const script = \`${failureReport.executionScript}\`; const blob = new Blob([script], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'test-fixes.sh'; a.click(); } function runAutoFixes() { alert('Auto-fix functionality would be implemented here'); } </script> </body> </html>`; } ``` ## Main Analysis Function ```javascript function analyzeTestFailures(testResultsPath) { console.log('šŸ” Analyzing test failures...'); // Load test results const testResults = loadTestResults(testResultsPath); // Parse failures from results const failures = parseTestFailures(testResults); if (failures.length === 0) { console.log('āœ… No test failures detected!'); return; } console.log(`Found ${failures.length} test failures. Analyzing...`); // Generate comprehensive failure report const failureReport = generateFailureReport(failures); // Save reports fs.writeFileSync('test-results/failure-analysis.json', JSON.stringify(failureReport, null, 2)); fs.writeFileSync('test-results/failure-report.html', generateHTMLReport(failureReport)); fs.writeFileSync('test-results/auto-fix.sh', failureReport.executionScript, { mode: 0o755 }); // Display summary console.log('\nšŸŽÆ Failure Analysis Complete'); console.log('============================'); console.log(`šŸ“Š Total failures analyzed: ${failures.length}`); console.log(`šŸ”„ High priority issues: ${failureReport.summary.highPriority}`); console.log(`šŸ”§ Auto-fixable issues: ${failureReport.summary.autoFixable}`); console.log(`šŸ“‹ Detailed report: test-results/failure-report.html`); console.log(`⚔ Auto-fix script: test-results/auto-fix.sh`); return failureReport; } ``` This utility provides comprehensive failure analysis with specific, actionable remediation steps, automated fix scripts, and clear reporting to help users resolve test issues quickly and effectively.