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
JavaScript
/**
* 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