UNPKG

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

413 lines • 16.8 kB
/** * Test Expectation Tracker * Compares test expectations vs actual UI rendering * Based on Cycle 22 user feedback - widget key comparison */ export class TestExpectationTracker { static instance; static getInstance() { if (!TestExpectationTracker.instance) { TestExpectationTracker.instance = new TestExpectationTracker(); } return TestExpectationTracker.instance; } expectations = new Map(); trackingHistory = new Map(); constructor() { this.initializeCommonExpectations(); } /** * Initialize common expectations from Cycle 22 success pattern */ initializeCommonExpectations() { const commonExpectations = [ { key: 'family_overview_section', type: 'exact', description: 'Main family overview container section', criticality: 'must_have', dependencies: ['unifiedAuthNotifierProvider', 'unifiedFamilyMembersProvider'], testFile: 'dashboard_test.dart', testFunction: 'testFamilyOverviewSection' }, { key: 'family_member_status_card_', type: 'pattern', description: 'Family member status cards with member ID suffix', criticality: 'must_have', dependencies: ['unifiedFamilyMembersProvider'], testFile: 'dashboard_test.dart', testFunction: 'testFamilyMemberCards' }, { key: 'add_family_event_quick_action', type: 'exact', description: 'Quick action button for adding family events', criticality: 'should_have', dependencies: ['unifiedAuthNotifierProvider'], testFile: 'dashboard_test.dart', testFunction: 'testQuickActions' }, { key: 'add_family_chore_quick_action', type: 'exact', description: 'Quick action button for adding family chores', criticality: 'should_have', dependencies: ['unifiedAuthNotifierProvider'], testFile: 'dashboard_test.dart', testFunction: 'testQuickActions' }, { key: 'family_wellbeing_indicator', type: 'exact', description: 'Family wellbeing status indicator', criticality: 'nice_to_have', dependencies: ['unifiedFamilyMembersProvider', 'wellbeingProvider'], testFile: 'dashboard_test.dart', testFunction: 'testWellbeingIndicator' }, { key: 'family_calendar_sync_status', type: 'exact', description: 'Calendar synchronization status display', criticality: 'nice_to_have', dependencies: ['calendarSyncProvider'], testFile: 'dashboard_test.dart', testFunction: 'testCalendarSync' } ]; commonExpectations.forEach(expectation => { this.expectations.set(expectation.key, expectation); }); } /** * Track test expectations against actual rendering */ async trackExpectations(sessionId, page) { const actualState = await this.captureActualRenderingState(page); const comparisons = []; // Compare each expectation with actual state for (const [key, expectation] of this.expectations) { const comparison = this.compareExpectationWithActual(expectation, actualState); comparisons.push(comparison); } // Generate overall result const result = { overallStatus: this.determineOverallStatus(comparisons), totalExpectations: this.expectations.size, metExpectations: comparisons.filter(c => c.status === 'met').length, comparisons, implementationPlan: this.generateImplementationPlan(comparisons), criticalGaps: this.identifyCriticalGaps(comparisons) }; // Store in history this.trackingHistory.set(sessionId, result); return result; } /** * Capture actual rendering state from the page */ async captureActualRenderingState(page) { const state = { renderedKeys: [], missingKeys: [], extraKeys: [], elementCounts: new Map(), renderingTimestamp: Date.now(), errorMessages: [] }; if (!page) { state.errorMessages.push('No page available for rendering state capture'); return state; } try { const renderingAnalysis = await page.evaluate(() => { const analysis = { renderedKeys: [], elementCounts: {}, errorMessages: [] }; try { // Find all elements with test keys const testElements = document.querySelectorAll('[data-test-key]'); testElements.forEach(element => { const key = element.getAttribute('data-test-key'); if (key) { analysis.renderedKeys.push(key); analysis.elementCounts[key] = (analysis.elementCounts[key] || 0) + 1; } }); // Also check for Flutter-specific key attributes const flutterKeys = document.querySelectorAll('[data-flutter-key]'); flutterKeys.forEach(element => { const key = element.getAttribute('data-flutter-key'); if (key) { analysis.renderedKeys.push(key); analysis.elementCounts[key] = (analysis.elementCounts[key] || 0) + 1; } }); // Check for common pattern-based keys const patternKeys = document.querySelectorAll('[class*="test-key-"], [id*="test-key-"]'); patternKeys.forEach(element => { const className = element.className; const id = element.id; // Extract test keys from class names and IDs const classMatches = className.match(/test-key-([a-zA-Z0-9_-]+)/g); const idMatches = id.match(/test-key-([a-zA-Z0-9_-]+)/g); [...(classMatches || []), ...(idMatches || [])].forEach(match => { const key = match.replace('test-key-', ''); analysis.renderedKeys.push(key); analysis.elementCounts[key] = (analysis.elementCounts[key] || 0) + 1; }); }); } catch (error) { analysis.errorMessages.push(`Error analyzing rendered elements: ${error}`); } return analysis; }); state.renderedKeys = [...new Set(renderingAnalysis.renderedKeys)]; // Remove duplicates state.elementCounts = new Map(Object.entries(renderingAnalysis.elementCounts)); state.errorMessages = renderingAnalysis.errorMessages; } catch (error) { state.errorMessages.push(`Failed to capture rendering state: ${error}`); } return state; } /** * Compare expectation with actual rendering state */ compareExpectationWithActual(expectation, actualState) { const comparison = { expectation, status: 'not_met', actualElements: [], expectedElements: [expectation.key], gap: '', recommendation: '', implementationHint: '' }; switch (expectation.type) { case 'exact': comparison.actualElements = actualState.renderedKeys.filter(key => key === expectation.key); comparison.status = comparison.actualElements.length > 0 ? 'met' : 'not_met'; break; case 'pattern': comparison.actualElements = actualState.renderedKeys.filter(key => key.startsWith(expectation.key)); comparison.status = comparison.actualElements.length > 0 ? 'met' : 'not_met'; break; case 'count': const count = actualState.elementCounts.get(expectation.key) || 0; comparison.actualElements = count > 0 ? [expectation.key] : []; comparison.status = count > 0 ? 'met' : 'not_met'; break; case 'conditional': // Custom logic for conditional expectations comparison.status = this.evaluateConditionalExpectation(expectation, actualState); break; } // Generate gap analysis and recommendations comparison.gap = this.generateGapAnalysis(comparison); comparison.recommendation = this.generateRecommendation(comparison); comparison.implementationHint = this.generateImplementationHint(comparison); return comparison; } /** * Evaluate conditional expectations */ evaluateConditionalExpectation(expectation, actualState) { // Check if dependencies are met first const dependenciesMet = expectation.dependencies.every(dep => actualState.renderedKeys.some(key => key.includes(dep) || key.startsWith(dep))); if (!dependenciesMet) { return 'error'; // Cannot evaluate without dependencies } // Look for the actual key const found = actualState.renderedKeys.some(key => key === expectation.key || key.startsWith(expectation.key)); return found ? 'met' : 'not_met'; } /** * Generate gap analysis */ generateGapAnalysis(comparison) { if (comparison.status === 'met') { return 'Expectation fully met'; } const { expectation, actualElements } = comparison; if (actualElements.length === 0) { return `Missing: ${expectation.key} (${expectation.description})`; } if (expectation.type === 'pattern' && actualElements.length > 0) { return `Partial match: Found ${actualElements.length} elements with pattern ${expectation.key}`; } return `Incomplete implementation: ${expectation.description}`; } /** * Generate recommendation */ generateRecommendation(comparison) { const { expectation, status } = comparison; if (status === 'met') { return 'Continue with current implementation'; } switch (expectation.criticality) { case 'must_have': return `CRITICAL: Implement ${expectation.key} immediately. This blocks core functionality.`; case 'should_have': return `HIGH PRIORITY: Add ${expectation.key} for important user features.`; case 'nice_to_have': return `ENHANCEMENT: Consider implementing ${expectation.key} for improved UX.`; default: return `Implement ${expectation.key} as needed.`; } } /** * Generate implementation hint */ generateImplementationHint(comparison) { const { expectation } = comparison; const hintMap = { 'family_overview_section': 'Add Container or Section widget with key: Key("family_overview_section")', 'family_member_status_card_': 'Create ListView.builder with cards having keys: Key("family_member_status_card_${member.id}")', 'add_family_event_quick_action': 'Add ElevatedButton or FloatingActionButton with key: Key("add_family_event_quick_action")', 'add_family_chore_quick_action': 'Add ElevatedButton or IconButton with key: Key("add_family_chore_quick_action")', 'family_wellbeing_indicator': 'Add status indicator widget with key: Key("family_wellbeing_indicator")', 'family_calendar_sync_status': 'Add status display widget with key: Key("family_calendar_sync_status")' }; return hintMap[expectation.key] || `Add widget with key: Key("${expectation.key}") in the appropriate component`; } /** * Determine overall status */ determineOverallStatus(comparisons) { const critical = comparisons.filter(c => c.expectation.criticality === 'must_have'); const criticalMet = critical.filter(c => c.status === 'met'); if (criticalMet.length === critical.length) { const allMet = comparisons.filter(c => c.status === 'met'); return allMet.length === comparisons.length ? 'passing' : 'partial'; } return 'failing'; } /** * Generate implementation plan */ generateImplementationPlan(comparisons) { const steps = []; let order = 1; // Sort by criticality and dependencies const sortedComparisons = [...comparisons] .filter(c => c.status !== 'met') .sort((a, b) => { const criticalityOrder = { 'must_have': 0, 'should_have': 1, 'nice_to_have': 2 }; return criticalityOrder[a.expectation.criticality] - criticalityOrder[b.expectation.criticality]; }); sortedComparisons.forEach(comparison => { const { expectation } = comparison; steps.push({ order: order++, action: `Implement ${expectation.description}`, target: expectation.key, code: this.generateImplementationCode(expectation), reason: `Required for test: ${expectation.testFunction || 'dashboard tests'}`, dependencies: expectation.dependencies, estimatedEffort: this.estimateImplementationEffort(expectation) }); }); return steps; } /** * Generate implementation code */ generateImplementationCode(expectation) { const codeTemplates = { 'family_overview_section': ` Container( key: const Key('family_overview_section'), child: Column( children: [ // Family overview content ], ), )`, 'family_member_status_card_': ` ListView.builder( itemCount: familyMembers.length, itemBuilder: (context, index) { final member = familyMembers[index]; return Card( key: Key('family_member_status_card_\${member.id}'), child: ListTile( title: Text(member.name), // Add status indicator ), ); }, )`, 'add_family_event_quick_action': ` ElevatedButton( key: const Key('add_family_event_quick_action'), onPressed: () => _addFamilyEvent(), child: const Text('Add Event'), )`, 'add_family_chore_quick_action': ` ElevatedButton( key: const Key('add_family_chore_quick_action'), onPressed: () => _addFamilyChore(), child: const Text('Add Chore'), )` }; return codeTemplates[expectation.key] || `Widget(\n key: const Key('${expectation.key}'),\n // Implement ${expectation.description}\n)`; } /** * Estimate implementation effort */ estimateImplementationEffort(expectation) { if (expectation.dependencies.length === 0) return 'small'; if (expectation.dependencies.length <= 2) return 'medium'; return 'large'; } /** * Identify critical gaps */ identifyCriticalGaps(comparisons) { return comparisons .filter(c => c.expectation.criticality === 'must_have' && c.status !== 'met') .map(c => `${c.expectation.key}: ${c.gap}`); } /** * Add custom expectation */ addExpectation(expectation) { this.expectations.set(expectation.key, expectation); } /** * Get expectation by key */ getExpectation(key) { return this.expectations.get(key); } /** * Get tracking history */ getTrackingHistory(sessionId) { return this.trackingHistory.get(sessionId); } /** * Clear expectations (for testing) */ clearExpectations() { this.expectations.clear(); this.initializeCommonExpectations(); } /** * Get all expectations */ getAllExpectations() { return Array.from(this.expectations.values()); } } //# sourceMappingURL=test-expectation-tracker.js.map