UNPKG

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

666 lines (646 loc) 27 kB
/** * Test Planner for VineGuard * Creates comprehensive test strategies and execution plans */ export class TestPlanner { testIdSequence = { 'U': 0, // Unit 'I': 0, // Integration 'E': 0, // End-to-end 'C': 0, // Component 'A': 0, // Accessibility 'P': 0, // Performance 'S': 0, // Security 'V': 0 // Visual }; constructor() { } /** * Generate comprehensive test plan from PRD */ async generateTestPlan(prd, options) { const { coverageTarget = 85, includePerfomance = false, includeAccessibility = true, includeSecurity = true, prioritizeTests = true, projectType = 'web' } = options || {}; // Generate test scenarios from PRD const scenarios = await this.generateTestScenarios(prd, { includePerfomance, includeAccessibility, includeSecurity }); // Organize scenarios into test suites const testSuites = this.organizeScenariosIntoSuites(scenarios, projectType); // Create risk matrix const riskMatrix = this.createRiskMatrix(scenarios); // Define test pyramid based on project type const testPyramid = this.defineTestPyramid(projectType); // Create timeline and phases const timeline = this.createTestTimeline(testSuites, scenarios); const testPlan = { projectName: prd.projectName, version: prd.version, generatedAt: new Date().toISOString(), strategy: { approach: this.defineTestingApproach(projectType), objectives: this.defineTestingObjectives(prd), scope: this.defineTestingScope(prd), outOfScope: this.defineOutOfScope() }, testPyramid, testSuites, scenarios, riskMatrix, coverageStrategy: { overall: coverageTarget, critical: Math.min(100, coverageTarget + 15), businessLogic: Math.min(100, coverageTarget + 10), ui: Math.max(60, coverageTarget - 15), api: Math.min(100, coverageTarget + 5) }, timeline, resources: this.defineRequiredResources(testSuites), qualityGates: this.defineQualityGates() }; return testPlan; } /** * Generate test scenarios from PRD components */ async generateTestScenarios(prd, options) { const scenarios = []; // Generate scenarios from user stories for (const userStory of prd.userStories) { const storyScenarios = await this.generateScenariosFromUserStory(userStory); scenarios.push(...storyScenarios); } // Generate scenarios from functional requirements for (const requirement of prd.functionalRequirements) { const reqScenarios = await this.generateScenariosFromRequirement(requirement); scenarios.push(...reqScenarios); } // Generate non-functional test scenarios if (options.includePerfomance) { scenarios.push(...this.generatePerformanceScenarios(prd)); } if (options.includeAccessibility) { scenarios.push(...this.generateAccessibilityScenarios(prd)); } if (options.includeSecurity) { scenarios.push(...this.generateSecurityScenarios(prd)); } // Generate critical path scenarios scenarios.push(...this.generateCriticalPathScenarios(prd)); return scenarios; } /** * Generate test scenarios from user story */ async generateScenariosFromUserStory(userStory) { const scenarios = []; const componentName = userStory.component || 'component'; // Happy path scenario scenarios.push({ id: `TS-${userStory.id}-001`, testId: this.generateTestId('e2e', `${componentName}_happy_path`), title: `Happy path: ${userStory.title}`, description: `Verify that ${userStory.description.toLowerCase()} works correctly under normal conditions`, type: 'e2e', priority: userStory.priority === 'high' ? 'critical' : userStory.priority === 'medium' ? 'high' : 'medium', framework: 'playwright', estimatedComplexity: 'medium', dependencies: [], preconditions: ['User has valid access', 'System is available'], expectedOutcome: 'User successfully completes the intended action', riskLevel: userStory.priority === 'high' ? 'high' : 'medium', businessImpact: 'high', automationFeasibility: 'high' }); // Error handling scenario scenarios.push({ id: `TS-${userStory.id}-002`, testId: this.generateTestId('e2e', `${componentName}_error_handling`), title: `Error handling: ${userStory.title}`, description: `Verify error handling when ${userStory.description.toLowerCase()} encounters invalid conditions`, type: 'e2e', priority: 'medium', framework: 'playwright', estimatedComplexity: 'medium', dependencies: [], preconditions: ['System is available'], expectedOutcome: 'System handles errors gracefully with appropriate user feedback', riskLevel: 'medium', businessImpact: 'medium', automationFeasibility: 'high' }); // Component unit tests if component specified if (userStory.component) { scenarios.push({ id: `TS-${userStory.id}-003`, testId: this.generateTestId('unit', `${componentName}_unit`), title: `Unit test: ${userStory.component}`, description: `Verify ${userStory.component} component functions correctly in isolation`, type: 'unit', priority: 'high', framework: 'jest', estimatedComplexity: 'simple', dependencies: [], preconditions: [], expectedOutcome: 'Component renders and behaves correctly with various props', riskLevel: 'low', businessImpact: 'medium', automationFeasibility: 'high' }); } return scenarios; } /** * Generate scenarios from functional requirement */ async generateScenariosFromRequirement(requirement) { const scenarios = []; if (requirement.testable) { scenarios.push({ id: `TS-${requirement.id}-001`, testId: this.generateTestId('integration', requirement.category.toLowerCase().replace(/\s+/g, '_')), title: `Integration test: ${requirement.category}`, description: requirement.description, type: 'integration', priority: 'high', framework: 'jest', estimatedComplexity: requirement.dependencies.length > 2 ? 'complex' : 'medium', dependencies: requirement.dependencies, preconditions: requirement.businessLogic, expectedOutcome: 'Requirement is satisfied with proper integration', riskLevel: requirement.dependencies.length > 2 ? 'high' : 'medium', businessImpact: 'high', automationFeasibility: 'high' }); } return scenarios; } /** * Generate performance test scenarios */ generatePerformanceScenarios(prd) { return [ { id: 'TS-PERF-001', testId: this.generateTestId('performance', 'page_load_time'), title: 'Page load performance', description: 'Verify application loads within acceptable time limits', type: 'performance', priority: 'high', framework: 'playwright', estimatedComplexity: 'medium', dependencies: [], preconditions: ['Network conditions are stable'], expectedOutcome: 'Pages load within 3 seconds', riskLevel: 'medium', businessImpact: 'high', automationFeasibility: 'high' }, { id: 'TS-PERF-002', testId: this.generateTestId('performance', 'api_response_time'), title: 'API response performance', description: 'Verify API endpoints respond within acceptable time limits', type: 'performance', priority: 'high', framework: 'playwright', estimatedComplexity: 'simple', dependencies: [], preconditions: ['API services are running'], expectedOutcome: 'API responses within 500ms', riskLevel: 'medium', businessImpact: 'high', automationFeasibility: 'high' } ]; } /** * Generate accessibility test scenarios */ generateAccessibilityScenarios(prd) { return [ { id: 'TS-A11Y-001', testId: this.generateTestId('accessibility', 'wcag_compliance'), title: 'WCAG 2.1 AA compliance', description: 'Verify application meets WCAG 2.1 AA accessibility standards', type: 'accessibility', priority: 'high', framework: 'playwright', estimatedComplexity: 'medium', dependencies: [], preconditions: ['Application is fully rendered'], expectedOutcome: 'No WCAG 2.1 AA violations detected', riskLevel: 'medium', businessImpact: 'high', automationFeasibility: 'high' }, { id: 'TS-A11Y-002', testId: this.generateTestId('accessibility', 'keyboard_navigation'), title: 'Keyboard navigation', description: 'Verify all functionality is accessible via keyboard', type: 'accessibility', priority: 'high', framework: 'playwright', estimatedComplexity: 'medium', dependencies: [], preconditions: [], expectedOutcome: 'All interactive elements accessible via keyboard', riskLevel: 'medium', businessImpact: 'high', automationFeasibility: 'high' } ]; } /** * Generate security test scenarios */ generateSecurityScenarios(prd) { return [ { id: 'TS-SEC-001', testId: this.generateTestId('security', 'input_validation'), title: 'Input validation security', description: 'Verify application properly validates and sanitizes user input', type: 'security', priority: 'critical', framework: 'playwright', estimatedComplexity: 'complex', dependencies: [], preconditions: [], expectedOutcome: 'All inputs are properly validated and sanitized', riskLevel: 'high', businessImpact: 'high', automationFeasibility: 'medium' }, { id: 'TS-SEC-002', testId: this.generateTestId('security', 'authentication'), title: 'Authentication security', description: 'Verify authentication mechanisms are secure', type: 'security', priority: 'critical', framework: 'playwright', estimatedComplexity: 'complex', dependencies: [], preconditions: [], expectedOutcome: 'Authentication is secure against common attacks', riskLevel: 'high', businessImpact: 'high', automationFeasibility: 'medium' } ]; } /** * Generate critical path scenarios */ generateCriticalPathScenarios(prd) { const scenarios = []; for (const criticalPath of prd.testingImplications.criticalPaths) { scenarios.push({ id: `TS-CRITICAL-${scenarios.length + 1}`, testId: this.generateTestId('e2e', criticalPath.toLowerCase().replace(/\s+/g, '_')), title: `Critical path: ${criticalPath}`, description: `End-to-end verification of critical business path: ${criticalPath}`, type: 'e2e', priority: 'critical', framework: 'playwright', estimatedComplexity: 'complex', dependencies: [], preconditions: ['System is fully operational'], expectedOutcome: 'Critical business functionality works end-to-end', riskLevel: 'high', businessImpact: 'high', automationFeasibility: 'high' }); } return scenarios; } /** * Organize scenarios into logical test suites */ organizeScenariosIntoSuites(scenarios, projectType) { const suiteMap = new Map(); // Group scenarios by type and framework for (const scenario of scenarios) { const key = `${scenario.type}-${scenario.framework}`; if (!suiteMap.has(key)) { suiteMap.set(key, []); } suiteMap.get(key).push(scenario); } const testSuites = []; for (const [key, scenarioGroup] of suiteMap.entries()) { const [type, framework] = key.split('-'); testSuites.push({ name: `${type.charAt(0).toUpperCase() + type.slice(1)} Test Suite (${framework})`, description: `${type} tests using ${framework} framework`, framework, scenarios: scenarioGroup, setupRequirements: this.getSetupRequirements(type, framework), executionOrder: scenarioGroup.map(s => s.id), parallelizable: this.isParallelizable(type), estimatedDuration: this.estimateDuration(scenarioGroup), coverageTarget: this.getCoverageTarget(type) }); } return testSuites; } /** * Create risk matrix from scenarios */ createRiskMatrix(scenarios) { return { high: scenarios.filter(s => s.riskLevel === 'high'), medium: scenarios.filter(s => s.riskLevel === 'medium'), low: scenarios.filter(s => s.riskLevel === 'low') }; } /** * Define test pyramid distribution */ defineTestPyramid(projectType) { switch (projectType) { case 'api': return { unit: { percentage: 70, rationale: 'Focus on business logic and service functions' }, integration: { percentage: 25, rationale: 'Test API endpoints and data layer integration' }, e2e: { percentage: 5, rationale: 'Critical workflow validation' } }; case 'react': case 'vue': case 'angular': return { unit: { percentage: 60, rationale: 'Component logic and utility functions' }, integration: { percentage: 25, rationale: 'Component integration and API calls' }, e2e: { percentage: 15, rationale: 'User workflows and critical paths' } }; default: return { unit: { percentage: 65, rationale: 'Core functionality and business logic' }, integration: { percentage: 25, rationale: 'Module and service integration' }, e2e: { percentage: 10, rationale: 'Critical user journeys' } }; } } /** * Create test execution timeline */ createTestTimeline(testSuites, scenarios) { return { phases: [ { name: 'Phase 1: Unit Testing', duration: '1-2 weeks', deliverables: ['Unit test suites', 'Component tests', 'Utility function tests'], testTypes: ['unit', 'component'] }, { name: 'Phase 2: Integration Testing', duration: '1-2 weeks', deliverables: ['API integration tests', 'Service integration tests', 'Database tests'], testTypes: ['integration'] }, { name: 'Phase 3: End-to-End Testing', duration: '2-3 weeks', deliverables: ['E2E test suites', 'Critical path tests', 'User journey tests'], testTypes: ['e2e'] }, { name: 'Phase 4: Specialized Testing', duration: '1-2 weeks', deliverables: ['Accessibility tests', 'Performance tests', 'Security tests', 'Visual tests'], testTypes: ['accessibility', 'performance', 'security', 'visual'] } ], milestones: [ { name: 'Unit Testing Complete', criteria: ['All unit tests passing', 'Code coverage targets met', 'No critical bugs'], dependencies: ['Phase 1 completion'] }, { name: 'Integration Testing Complete', criteria: ['All integration tests passing', 'API contracts validated', 'Data flow verified'], dependencies: ['Phase 2 completion'] }, { name: 'E2E Testing Complete', criteria: ['All critical paths tested', 'User workflows validated', 'Cross-browser compatibility'], dependencies: ['Phase 3 completion'] }, { name: 'Testing Complete', criteria: ['All test suites passing', 'Quality gates met', 'Performance benchmarks achieved'], dependencies: ['All phases complete'] } ] }; } // Helper methods generateTestId(type, descriptor) { const typeCode = type.toUpperCase().charAt(0); if (!(typeCode in this.testIdSequence)) { this.testIdSequence[typeCode] = 0; } this.testIdSequence[typeCode]++; const sequenceStr = this.testIdSequence[typeCode].toString().padStart(3, '0'); const cleanDescriptor = descriptor .toLowerCase() .replace(/[^a-z0-9]/g, '_') .replace(/_+/g, '_') .replace(/^_|_$/g, ''); return `VG_${typeCode}_${sequenceStr}_${cleanDescriptor}`; } defineTestingApproach(projectType) { switch (projectType) { case 'api': return 'API-first testing approach focusing on contract testing and data validation'; case 'react': case 'vue': case 'angular': return 'Component-driven testing approach with emphasis on user interactions and accessibility'; default: return 'Comprehensive testing approach covering all layers of the application'; } } defineTestingObjectives(prd) { return [ 'Ensure all user stories meet acceptance criteria', 'Verify functional requirements are properly implemented', 'Validate non-functional requirements are met', 'Ensure application security and data integrity', 'Verify accessibility standards compliance', 'Validate performance benchmarks' ]; } defineTestingScope(prd) { return `Testing covers all user-facing functionality, business logic, API endpoints, data persistence, security measures, and accessibility features as defined in the PRD for ${prd.projectName}.`; } defineOutOfScope() { return [ 'Third-party service functionality (mocked/stubbed)', 'Infrastructure and deployment testing', 'Load testing beyond basic performance', 'Browser compatibility beyond modern browsers', 'Mobile-specific testing (unless specified)' ]; } getSetupRequirements(type, framework) { const requirements = []; switch (framework) { case 'jest': requirements.push('Jest test environment configured', 'Test data and mocks prepared'); break; case 'playwright': requirements.push('Playwright browsers installed', 'Test environment running', 'Base URL configured'); break; case 'cypress': requirements.push('Cypress environment configured', 'Test data prepared', 'Application server running'); break; } if (type === 'accessibility') { requirements.push('Accessibility testing tools configured'); } return requirements; } isParallelizable(type) { return ['unit', 'component', 'integration'].includes(type); } estimateDuration(scenarios) { const complexityPoints = scenarios.reduce((total, scenario) => { switch (scenario.estimatedComplexity) { case 'simple': return total + 1; case 'medium': return total + 2; case 'complex': return total + 4; default: return total + 2; } }, 0); const hours = Math.ceil(complexityPoints * 0.5); // 30 minutes per complexity point if (hours <= 4) return `${hours} hours`; if (hours <= 16) return `${Math.ceil(hours / 8)} days`; return `${Math.ceil(hours / 40)} weeks`; } getCoverageTarget(type) { switch (type) { case 'unit': return 90; case 'integration': return 80; case 'component': return 85; case 'e2e': return 70; case 'security': return 95; default: return 75; } } defineRequiredResources(testSuites) { const frameworks = [...new Set(testSuites.map(suite => suite.framework))]; return { tools: [ ...frameworks, 'Test reporting tools', 'Coverage analysis tools', 'CI/CD pipeline integration' ], environments: [ 'Development environment', 'Testing environment', 'Staging environment (for E2E)', 'Browser testing environments' ], skills: [ 'Test automation development', 'Testing framework expertise', 'Domain knowledge of application', 'Test strategy and planning' ] }; } defineQualityGates() { return [ { phase: 'Unit Testing', criteria: ['All unit tests pass', 'Code coverage >= 90%', 'No critical bugs'], metrics: ['Test pass rate', 'Code coverage percentage', 'Cyclomatic complexity'] }, { phase: 'Integration Testing', criteria: ['All integration tests pass', 'API contracts validated', 'No integration failures'], metrics: ['Integration test pass rate', 'API response times', 'Data consistency checks'] }, { phase: 'E2E Testing', criteria: ['All critical paths pass', 'Cross-browser compatibility', 'Performance benchmarks met'], metrics: ['E2E test pass rate', 'Page load times', 'User workflow completion rates'] }, { phase: 'Release', criteria: ['All tests pass', 'Quality gates met', 'Security scan clean', 'Accessibility compliant'], metrics: ['Overall test pass rate', 'Defect density', 'Performance scores', 'Accessibility score'] } ]; } /** * Export test plan to different formats */ async exportTestPlan(testPlan, format = 'json') { switch (format) { case 'json': return JSON.stringify(testPlan, null, 2); case 'markdown': return this.generateMarkdownTestPlan(testPlan); case 'html': return this.generateHTMLTestPlan(testPlan); default: return JSON.stringify(testPlan, null, 2); } } generateMarkdownTestPlan(testPlan) { return `# Test Plan: ${testPlan.projectName} **Version:** ${testPlan.version} **Generated:** ${new Date(testPlan.generatedAt).toLocaleDateString()} ## Strategy ### Approach ${testPlan.strategy.approach} ### Objectives ${testPlan.strategy.objectives.map(obj => `- ${obj}`).join('\n')} ### Scope ${testPlan.strategy.scope} ## Test Pyramid - **Unit Tests:** ${testPlan.testPyramid.unit.percentage}% - ${testPlan.testPyramid.unit.rationale} - **Integration Tests:** ${testPlan.testPyramid.integration.percentage}% - ${testPlan.testPyramid.integration.rationale} - **E2E Tests:** ${testPlan.testPyramid.e2e.percentage}% - ${testPlan.testPyramid.e2e.rationale} ## Test Scenarios ${testPlan.scenarios.map(scenario => ` ### ${scenario.testId}: ${scenario.title} **Type:** ${scenario.type} | **Priority:** ${scenario.priority} | **Framework:** ${scenario.framework} ${scenario.description} **Expected Outcome:** ${scenario.expectedOutcome} **Risk Level:** ${scenario.riskLevel} | **Business Impact:** ${scenario.businessImpact} `).join('\n')} ## Timeline ${testPlan.timeline.phases.map(phase => ` ### ${phase.name} **Duration:** ${phase.duration} **Test Types:** ${phase.testTypes.join(', ')} **Deliverables:** ${phase.deliverables.map(d => `- ${d}`).join('\n')} `).join('\n')} ## Quality Gates ${testPlan.qualityGates.map(gate => ` ### ${gate.phase} **Criteria:** ${gate.criteria.map(c => `- ${c}`).join('\n')} **Metrics:** ${gate.metrics.map(m => `- ${m}`).join('\n')} `).join('\n')} `; } generateHTMLTestPlan(testPlan) { // Placeholder for HTML generation return `<html><body><h1>Test Plan: ${testPlan.projectName}</h1><p>Generated: ${testPlan.generatedAt}</p></body></html>`; } } //# sourceMappingURL=test-planner.js.map