ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
251 lines (240 loc) • 10.6 kB
JavaScript
import { BaseToolHandler } from './base-handler.js';
export class MaintenanceHandler extends BaseToolHandler {
maintenanceEngine;
tools = [
{
name: 'analyze_test_health',
description: 'Analyze the health of your test suite and identify issues',
inputSchema: {
type: 'object',
properties: {
sessionId: {
type: 'string',
description: 'The debug session ID'
},
testCode: {
type: 'string',
description: 'The test code to analyze'
}
},
required: ['sessionId', 'testCode']
}
},
{
name: 'detect_stale_tests',
description: 'Detect stale selectors and outdated test code',
inputSchema: {
type: 'object',
properties: {
sessionId: {
type: 'string',
description: 'The debug session ID'
},
testCode: {
type: 'string',
description: 'The test code to check for staleness'
}
},
required: ['sessionId', 'testCode']
}
},
{
name: 'auto_fix_tests',
description: 'Automatically fix simple test issues like outdated selectors',
inputSchema: {
type: 'object',
properties: {
sessionId: {
type: 'string',
description: 'The debug session ID'
},
testCode: {
type: 'string',
description: 'The test code to fix'
}
},
required: ['sessionId', 'testCode']
}
},
{
name: 'find_test_coverage_gaps',
description: 'Find UI elements that are not covered by tests',
inputSchema: {
type: 'object',
properties: {
sessionId: {
type: 'string',
description: 'The debug session ID'
},
testSelectors: {
type: 'array',
items: { type: 'string' },
description: 'List of selectors used in current tests'
}
},
required: ['sessionId', 'testSelectors']
}
},
{
name: 'generate_test_maintenance_report',
description: 'Generate a comprehensive test maintenance report',
inputSchema: {
type: 'object',
properties: {
sessionId: {
type: 'string',
description: 'The debug session ID'
},
testSuiteId: {
type: 'string',
description: 'Identifier for the test suite'
}
},
required: ['sessionId', 'testSuiteId']
}
}
];
constructor(maintenanceEngine) {
super();
this.maintenanceEngine = maintenanceEngine;
}
getTools() {
return this.tools;
}
async handle(toolName, args, sessions) {
try {
const session = this.getSession(args.sessionId, sessions);
switch (toolName) {
case 'analyze_test_health':
return await this.analyzeTestHealth(args, session);
case 'detect_stale_tests':
return await this.detectStaleTests(args, session);
case 'auto_fix_tests':
return await this.autoFixTests(args, session);
case 'find_test_coverage_gaps':
return await this.findCoverageGaps(args, session);
case 'generate_test_maintenance_report':
return await this.generateMaintenanceReport(args, session);
default:
return this.createErrorResponse(`Unknown tool: ${toolName}`);
}
}
catch (error) {
return this.createErrorResponse(error instanceof Error ? error.message : 'Unknown error occurred');
}
}
async analyzeTestHealth(args, session) {
try {
const staleSelectors = await this.maintenanceEngine.detectStaleSelectors(args.testCode, session.page);
const elementChanges = await this.maintenanceEngine.detectElementChanges(args.testCode, session.page);
const removedFeatures = await this.maintenanceEngine.detectRemovedFeatures(args.testCode, session.page);
const allIssues = [...staleSelectors, ...elementChanges, ...removedFeatures];
const health = await this.maintenanceEngine.calculateTestHealth({
totalTests: (args.testCode.match(/test\(/g) || []).length,
staleSelectors: staleSelectors.length,
duplicateTests: 0,
flakyTests: 0,
coverageGaps: 0
});
const report = `🔍 **Test Health Analysis**
**Overall Health Score:** ${health.score || 'N/A'}/100
**Issues Found:** ${allIssues.length}
${allIssues.map((issue) => `• ${issue.type || 'Unknown'}: ${issue.description || issue.message || 'No description'}`).join('\n')}
**Recommendations:**
${health.recommendations.map(rec => `• ${rec}`).join('\n')}`;
return this.createTextResponse(report);
}
catch (error) {
return this.createErrorResponse(`Failed to analyze test health: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async detectStaleTests(args, session) {
try {
const staleTests = await this.maintenanceEngine.detectStaleSelectors(args.testCode, session.page);
const report = `🔍 **Stale Test Detection**
**Found ${staleTests.length} stale test(s):**
${staleTests.map((test) => `• ${test.selector || 'Unknown selector'}: ${test.reason || test.message || 'No reason provided'}`).join('\n')}
${staleTests.length === 0 ? '✅ No stale tests detected!' : '⚠️ Update these tests to prevent failures'}`;
return this.createTextResponse(report);
}
catch (error) {
return this.createErrorResponse(`Failed to detect stale tests: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async autoFixTests(args, session) {
try {
const issues = await this.maintenanceEngine.detectStaleSelectors(args.testCode, session.page);
// Get suggestions for fixes
const issuesWithFixes = await Promise.all(issues.map(async (issue) => {
if (issue.selector) {
const suggestions = await this.maintenanceEngine.suggestSelectorUpdate(issue.selector, session.page);
if (suggestions.length > 0) {
return {
...issue,
suggestedFix: suggestions[0].newSelector
};
}
}
return issue;
}));
const fixedCode = await this.maintenanceEngine.autoFixTests(args.testCode, issuesWithFixes);
const appliedFixes = issuesWithFixes.filter(i => i.suggestedFix);
const report = `🛠️ **Auto-Fix Test Results**
**Applied ${appliedFixes.length} fix(es):**
${appliedFixes.map(fix => `• ${fix.selector} → ${fix.suggestedFix}`).join('\n')}
**Updated Test Code:**
\`\`\`javascript
${fixedCode}
\`\`\``;
return this.createTextResponse(report);
}
catch (error) {
return this.createErrorResponse(`Failed to auto-fix tests: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async findCoverageGaps(args, session) {
try {
const gaps = await this.maintenanceEngine.findCoverageGaps(args.testSelectors, session.page);
// Get element details for gaps
const gapDetails = await session.page.evaluate((selectors) => {
return selectors.map((selector) => {
const el = document.querySelector(selector);
if (el) {
return {
selector,
type: el.tagName.toLowerCase(),
label: el.innerText?.trim() || el.value || ''
};
}
return { selector, type: 'unknown', label: '' };
});
}, gaps);
const coverage = {
percentage: Math.round((args.testSelectors.length / (args.testSelectors.length + gaps.length)) * 100),
covered: args.testSelectors.length,
total: args.testSelectors.length + gaps.length
};
const report = `📊 **Test Coverage Analysis**
**Coverage: ${coverage.percentage}%** (${coverage.covered}/${coverage.total} elements)
**Uncovered Elements:**
${gapDetails.map((el) => `• ${el.type}: "${el.label}" (${el.selector})`).join('\n')}
${coverage.percentage >= 80 ? '✅ Good coverage!' : '⚠️ Consider adding tests for uncovered elements'}`;
return this.createTextResponse(report);
}
catch (error) {
return this.createErrorResponse(`Failed to find coverage gaps: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async generateMaintenanceReport(args, session) {
try {
const report = await this.maintenanceEngine.generateMaintenanceReport(args.testSuiteId, session.page);
const formattedReport = `📋 **Test Maintenance Report**
${typeof report === 'string' ? report : JSON.stringify(report, null, 2)}`;
return this.createTextResponse(formattedReport);
}
catch (error) {
return this.createErrorResponse(`Failed to generate maintenance report: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}
//# sourceMappingURL=maintenance-handler.js.map