UNPKG

veriqa-test-advisor

Version:

AI-powered regression test case advisor CLI tool with integrated Manual QA support

1,119 lines (970 loc) โ€ข 43.9 kB
#!/usr/bin/env node const banner = String.raw` VeriQA .__________ _____ ___ __ ___________|__\_____ \ / _ \ \ \/ // __ \_ __ \ |/ / \ \ / /_\ \ \ /\ ___/| | \/ / \_/. \/ | \ \_/ \___ >__| |__\_____\ \_/\____|__ / \/ \__> \/ `; console.log(banner); // ๐Ÿ‘‡ NEW: brand header with Made in India + version + flag let chalk; try { chalk = require('chalk'); } catch { chalk = { bold: x=>x, dim: x=>x, cyan: x=>x }; } const pkg = require('./package.json'); const brandLine = `๐Ÿ‡ฎ๐Ÿ‡ณ ${chalk.bold('Made in India')} โ€ข ${chalk.bold.cyan('VeriQA')} ${chalk.dim(`v${pkg.version}`)}`; console.log(brandLine + '\n'); const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const logger = require('./src/utils/logger'); const { detectFramework } = require('./src/utils/frameworkDetector'); // adjust if your detector file is named differently const simpleGit = require('simple-git'); const { getAISuggestions } = require('./src/aiSuggestor'); const { EnhancedGitDiffParser } = require('./src/enhancedGitDiffParser'); const { SmartAnalyzer } = require('./scripts/smart-analyzer'); const VersionManager = require('./src/versionManager'); const VersionAnalytics = require('./src/versionAnalytics'); // ๐Ÿš€ NEW: Cutting-edge feature imports const NLPTestCreator = require('./src/nlp/test-creator'); const LiveCoverageMonitor = require('./src/coverage/live-monitor'); const PredictiveModelTrainer = require('./src/prediction/train-models'); // --------------------- // CLI Flags (Enhanced with cutting-edge features) // --------------------- const isAI = process.argv.includes('--ai'); const isHelp = process.argv.includes('--help') || process.argv.includes('-h'); const isVersion = process.argv.includes('--version') || process.argv.includes('-v'); const isRun = process.argv.includes('--run'); const isDryRun = process.argv.includes('--dry-run'); const isVerbose = process.argv.includes('--verbose'); const isGitAnalyze = process.argv.includes('--git-analyze'); const isSmartAnalyze = process.argv.includes('--smart'); // ๐Ÿš€ NEW: Cutting-edge AI features flags const isGenerate = process.argv.includes('--generate'); const isSmartPrioritize = process.argv.includes('--smart-prioritize'); const isCoverageWatch = process.argv.includes('--coverage-watch'); const isPredict = process.argv.includes('--predict'); const isBrowserMatrix = process.argv.includes('--browser-matrix'); const isAutoFix = process.argv.includes('--auto-fix'); const isMultiTenant = process.argv.includes('--multi-tenant'); const isVisualAI = process.argv.includes('--visual-ai'); const isPerformance = process.argv.includes('--performance'); const isNLP = process.argv.includes('--nlp'); const isTrainModels = process.argv.includes('--train-models'); // ๐Ÿ“Š NEW: Project scanning flags const isBaseline = process.argv.includes('--baseline'); const isChanges = process.argv.includes('--changes'); const isScan = process.argv.includes('--scan'); const tagIndex = process.argv.indexOf('--tag'); const envIndex = process.argv.indexOf('--env'); const moduleIndex = process.argv.indexOf('--module'); const confidenceIndex = process.argv.indexOf('--confidence'); const thresholdIndex = process.argv.indexOf('--threshold'); const targetIndex = process.argv.indexOf('--coverage-target'); const tagValue = tagIndex > -1 ? process.argv[tagIndex + 1] : null; const envValue = envIndex > -1 ? process.argv[envIndex + 1] : null; const moduleValue = moduleIndex > -1 ? process.argv[moduleIndex + 1] : null; const confidenceValue = confidenceIndex > -1 ? parseFloat(process.argv[confidenceIndex + 1]) : 0.8; const thresholdValue = thresholdIndex > -1 ? process.argv[thresholdIndex + 1] : null; const coverageTarget = targetIndex > -1 ? parseInt(process.argv[targetIndex + 1]) : 90; // --- Allure cleanup helper (Option A) --- function cleanAllureDirs() { const dirs = ['allure-results', 'output/allure-results', '.allure-merged', 'report']; for (const d of dirs) { try { if (fs.existsSync(d)) fs.rmSync(d, { recursive: true, force: true }); } catch (e) { // ignore } } } function loadJsonSafe(p) { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; } } function getLabelValues(labels, key) { return (labels || []).filter(l => l.name === key).map(l => l.value); } function collectAllureResults(dir) { if (!dir || !fs.existsSync(dir)) return []; const files = fs.readdirSync(dir).filter(f => f.endsWith('-result.json') || f.endsWith('result.json')); const out = []; for (const f of files) { const j = loadJsonSafe(path.join(dir, f)); if (!j) continue; out.push({ name: j.name || j.fullName || 'unknown', status: j.status || 'unknown', durationMs: j.stop && j.start ? (j.stop - j.start) : (j.time?.duration ?? 0), labels: j.labels || [], suite: getLabelValues(j.labels, 'suite')[0] || 'unspecified', // tags can be emitted by frameworks or embedded in test titles like @smoke; we attempt both tags: [ ...getLabelValues(j.labels, 'tag'), ...((j.name || '').match(/@\w+/g) || []).map(t => t.replace('@','')) ] }); } return out; } function human(ms) { if (!ms || ms < 1000) return `${ms | 0} ms`; const s = ms / 1000; if (s < 60) return `${s.toFixed(2)} s`; const m = Math.floor(s / 60), r = (s % 60).toFixed(2); return `${m}m ${r}s`; } function pct(a, b) { if (!b) return '0.0%'; return `${((a / b) * 100).toFixed(1)}%`; } function padRight(str, len) { str = String(str ?? ''); return str.length >= len ? str : str + ' '.repeat(len - str.length); } function percentile(arr, p) { if (!arr.length) return 0; const s = [...arr].sort((a,b)=>a-b); const idx = Math.min(s.length - 1, Math.ceil(p * (s.length - 1))); return s[idx]; } function frameworkFromLabels(labels) { const fw = getLabelValues(labels, 'framework')[0]; if (fw) return fw; const suite = (getLabelValues(labels, 'suite')[0] || '').toLowerCase(); if (suite.includes('playwright')) return 'playwright'; if (suite.includes('codecept')) return 'codeceptjs'; return 'mixed'; } function histogram(items, keyFn) { const m = new Map(); for (const it of items) { const k = keyFn(it); m.set(k, (m.get(k) || 0) + 1); } return [...m.entries()].sort((a,b)=>b[1]-a[1]); } // Parse threshold string like: "p95<3000;pass>=95;fail==0" function evalThresholds(thresholdStr, stats) { if (!thresholdStr) return { ok: true, failures: [] }; const checks = thresholdStr.split(';').map(s => s.trim()).filter(Boolean); const fails = []; for (const c of checks) { const m = c.match(/^(p50|p90|p95|p99|pass|fail|broken)\s*(<=|<|>=|>|==|!=)\s*([0-9.]+)$/i); if (!m) continue; const [, key, op, raw] = m; // map keys to numeric values; pass/fail/broken are percentages (0-100) const valueMap = { p50: stats.p50, p90: stats.p90, p95: stats.p95, p99: stats.p99, pass: stats.passPct, fail: stats.failPct, broken: stats.brokenPct }; const left = valueMap[key.toLowerCase()]; const right = Number(raw); let ok = true; switch (op) { case '<': ok = left < right; break; case '<=': ok = left <= right; break; case '>': ok = left > right; break; case '>=': ok = left >= right; break; case '==': ok = left === right; break; case '!=': ok = left !== right; break; } if (!ok) fails.push(`${key} ${op} ${right} (actual ${left})`); } return { ok: fails.length === 0, failures: fails }; } function renderK6StyleSummary(results, wallMs, thresholdStr) { const total = results.length; const passed = results.filter(r => r.status === 'passed').length; const failed = results.filter(r => r.status === 'failed').length; const broken = results.filter(r => r.status === 'broken').length; const skipped = results.filter(r => r.status === 'skipped').length; const durations = results.map(r => r.durationMs || 0); const avg = durations.length ? durations.reduce((a,b)=>a+b,0)/durations.length : 0; const p50 = percentile(durations, 0.50); const p90 = percentile(durations, 0.90); const p95 = percentile(durations, 0.95); const p99 = percentile(durations, 0.99); const rps = total ? (total / Math.max(1, wallMs/1000)).toFixed(2) : '0.00'; const byFw = histogram(results, r => frameworkFromLabels(r.labels)); // tags breakdown (flatten all tags arrays) const tagPairs = histogram(results.flatMap(r => (r.tags || []).map(t => ({ t }))), x => x.t); const slowest = [...results].sort((a,b)=>(b.durationMs||0)-(a.durationMs||0)).slice(0,5); const bySuite = histogram(results, r => r.suite || 'unspecified'); const slowestSuite = bySuite[0]?.[0] || 'unspecified'; const passPct = total ? +( (passed/total)*100 ).toFixed(1) : 0; const failPct = total ? +( (failed/total)*100 ).toFixed(1) : 0; const brokenPct = total ? +( (broken/total)*100 ).toFixed(1) : 0; const line = (k, v) => `${padRight(k + ':', 18)} ${v}`; console.log('\nrunning (veriqa-advisor) ...'); console.log(line('wall time', human(wallMs))); console.log(line('tests', total)); console.log(line('โœ“ passed', `${passed} (${passPct}%)`)); console.log(line('โœ— failed', `${failed} (${failPct}%)`)); if (broken) console.log(line('โš  broken', `${broken} (${brokenPct}%)`)); if (skipped) console.log(line('โ†ท skipped', `${skipped} (${pct(skipped,total)})`)); console.log(line('avg duration', human(avg))); console.log(line('p50 | p90', `${human(p50)} | ${human(p90)}`)); console.log(line('p95 | p99', `${human(p95)} | ${human(p99)}`)); console.log(line('RPS', rps)); if (byFw.length) { console.log('\nby framework:'); for (const [k,v] of byFw) console.log(` ${padRight(k, 12)} ${v} (${pct(v,total)})`); } if (tagPairs.length) { console.log('\nby tag:'); for (const [tag,count] of tagPairs.slice(0,10)) console.log(` ${padRight(tag, 12)} ${count} (${pct(count,total)})`); } console.log('\nslowest suite:', slowestSuite); if (slowest.length) { console.log('top 5 slow tests:'); for (const r of slowest) console.log(` - ${padRight(human(r.durationMs || 0), 8)} ${r.name}`); } // thresholds const statsForThreshold = { p50, p90, p95, p99, passPct, failPct, brokenPct }; const th = evalThresholds(thresholdStr, statsForThreshold); const ok = (failed === 0 && broken === 0) && th.ok; if (th.thresholdStr) { /* reserved */ } if (!th.ok) { console.log('\nthresholds failed:'); for (const f of th.failures) console.log(` - ${f}`); } console.log('\n' + (ok ? 'โœ“ Summary: all tests passed' : 'โœ— Summary: some tests failed or thresholds breached') + '\n'); return { ok }; } function printConsoleSummary(wallMs, thresholdStr) { const dirs = [ process.env.ALLURE_RESULTS_DIR, '.allure-merged/allure-results', 'allure-results', 'output/allure-results' ].filter(Boolean); let results = []; for (const d of dirs) results = results.concat(collectAllureResults(d)); if (!results.length) { logger.warn('Allure JSON not found (no console summary). Enable allure reporters in Playwright/CodeceptJS.'); return { ok: true }; } return renderK6StyleSummary(results, wallMs, thresholdStr); } // --------------------- // Version Command // --------------------- if (isVersion) { console.log(`${chalk.cyan('VeriQA Test Advisor')} ${chalk.bold(`v${pkg.version}`)}`); console.log(`${chalk.dim('Node:')} ${process.version}`); console.log(`${chalk.dim('Platform:')} ${process.platform}`); process.exit(0); } // --------------------- // Enhanced Help Command // --------------------- if (isHelp) { console.log(chalk.cyan('\n๐Ÿง  VeriQA Test Advisor - AI-Powered Regression Testing\n')); console.log(chalk.dim('๐Ÿ‡ฎ๐Ÿ‡ณ Made in India โ€ข Intelligent test case selection for faster CI/CD')); console.log(chalk.bold('\n๐Ÿ“– Three Essential Commands:')); console.log(chalk.white(` 1๏ธโƒฃ ${chalk.cyan('veriqa-test-advisor --ai')} Get AI suggestions 2๏ธโƒฃ ${chalk.cyan('veriqa-test-advisor --smart')} Git + AI analysis (BEST) 3๏ธโƒฃ ${chalk.cyan('veriqa-test-advisor --run --tag smoke')} Run suggested tests `)); console.log(chalk.bold('\n๐Ÿš€ NEW: Cutting-Edge AI Features:')); console.log(chalk.white(` ๐Ÿง  ${chalk.cyan('veriqa-test-advisor --generate')} AI auto-generate tests ๐ŸŽฏ ${chalk.cyan('veriqa-test-advisor --smart-prioritize')} ML-based test prioritization ๐Ÿ“Š ${chalk.cyan('veriqa-test-advisor --coverage-watch')} Real-time coverage monitoring ๐Ÿ”ฎ ${chalk.cyan('veriqa-test-advisor --predict')} Predict test failures ๐ŸŒ ${chalk.cyan('veriqa-test-advisor --browser-matrix')} Multi-browser testing ๐Ÿ”ง ${chalk.cyan('veriqa-test-advisor --auto-fix')} Auto-fix failing tests ๐Ÿ‘€ ${chalk.cyan('veriqa-test-advisor --visual-ai')} Visual regression AI โšก ${chalk.cyan('veriqa-test-advisor --performance')} Performance testing ๐Ÿ’ฌ ${chalk.cyan('veriqa-test-advisor --nlp')} Natural language tests ๐ŸŽ“ ${chalk.cyan('veriqa-test-advisor --train-models')} Train ML models `)); console.log(chalk.bold('\nโšก Setup & Help:')); // Check if scripts exist and provide fallbacks const fs = require('fs'); const path = require('path'); const scriptsExist = fs.existsSync(path.join(__dirname, 'scripts', 'end-user-demo.js')); if (scriptsExist) { console.log(chalk.white(` ${chalk.green('First time?')} npx veriqa-setup ${chalk.green('Setup GitHub AI?')} npm run setup:github ${chalk.green('Having issues?')} veriqa-troubleshoot ${chalk.green('See simple demo?')} npm run demo:simple ${chalk.green('Check updates?')} veriqa-version --check `)); } else { console.log(chalk.white(` ${chalk.green('First time?')} veriqa-wizard ${chalk.green('Setup GitHub AI?')} ${chalk.dim('Manual: Add GITHUB_TOKEN to .env')} ${chalk.green('Having issues?')} veriqa-troubleshoot --help ${chalk.green('See demo?')} veriqa-test-advisor --smart --verbose ${chalk.green('Check updates?')} veriqa-version --check `)); console.log(chalk.yellow(' ๐Ÿ’ก Note: Some scripts missing. Core functionality still works!')); } console.log(chalk.bold('\n๐ŸŽฏ Quick Workflow:')); console.log(chalk.white(` ${chalk.dim('# After making code changes:')} veriqa-test-advisor --smart ${chalk.dim('# Get smart recommendations')} veriqa-test-advisor --run --tag smoke ${chalk.dim('# Run only what matters')} `)); console.log(chalk.green('\n๐Ÿš€ That\'s it! Smart testing in 2 commands.')); process.exit(0); } // --------------------- // Auto-generate test mapping // --------------------- function generateTestMapping() { const testsDir = path.join(process.cwd(), 'tests'); const mappingFile = path.join(process.cwd(), 'mappings/module_test_map.json'); const modules = {}; function walk(dir) { if (!fs.existsSync(dir)) return; const files = fs.readdirSync(dir); files.forEach(file => { const fullPath = path.join(dir, file); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { walk(fullPath); } else if (file.endsWith('.js') || file.endsWith('.spec.js')) { const relativePath = path.relative(testsDir, fullPath).replace(/\\/g, '/'); const moduleName = relativePath.split('/')[0]; if (!modules[moduleName]) modules[moduleName] = []; modules[moduleName].push('tests/' + relativePath); } }); } walk(testsDir); const mappingFolder = path.dirname(mappingFile); if (!fs.existsSync(mappingFolder)) fs.mkdirSync(mappingFolder, { recursive: true }); fs.writeFileSync(mappingFile, JSON.stringify(modules, null, 2)); console.log(':white_tick: Mapping generated at:', mappingFile); return modules; } // --------------------- // Map tests for given modules // --------------------- function mapTestsToModules(modulesList) { const mapping = generateTestMapping(); // always generate fresh mapping let suggestedTests = []; modulesList.forEach(module => { if (mapping[module]) { suggestedTests.push(...mapping[module]); } }); return [...new Set(suggestedTests)]; } // --------------------- // Get changed modules via Git // --------------------- async function getChangedModules() { const git = simpleGit(); const diffSummary = await git.diffSummary(['HEAD']); const changedFiles = diffSummary.files.map(f => f.file.replace(/\\/g, '/')); const modules = new Set(); changedFiles.forEach(f => { const parts = f.split('/'); if (parts[0] === 'tests') return; // skip test files themselves if (parts.length > 0) modules.add(parts[0]); }); return Array.from(modules); } // --------------------- // Main Execution // --------------------- (async () => { const t0 = Date.now(); let anyRan = false; // Initialize analytics and version management let versionAnalytics, versionManager; try { versionAnalytics = new VersionAnalytics(); versionManager = new VersionManager(); // Track version usage const currentVersion = pkg.version; const command = process.argv.slice(2).join(' ') || 'default'; versionAnalytics.trackVersionUsage(currentVersion, command); // Check for version updates (non-blocking, silent) await versionManager.checkForUpdates(true); } catch (error) { // Silently ignore version check errors } logger.info(':brain: VeriQA Regression Advisor\n'); // --------------------- // ๐Ÿš€ NEW: Cutting-Edge AI Features // --------------------- // AI Test Generation Mode if (isGenerate) { console.log(chalk.cyan('\n๐Ÿง  AI Test Generation Mode Activated!')); try { const TestGenerator = require('./src/ai-generation/test-generator-simple'); const generator = new TestGenerator(); console.log(chalk.yellow('๐Ÿ” Analyzing codebase...')); const results = await generator.generateTests({ framework: await detectFramework(), outputDir: './tests/generated', count: 10 }); console.log(chalk.green(`โœ… Generated ${results.count} test cases successfully!`)); console.log(chalk.dim(`๐Ÿ“ Tests saved to: ${results.outputDir}`)); process.exit(0); } catch (error) { console.log(chalk.red('โŒ AI Test Generation failed:'), error.message); process.exit(1); } } // Smart Test Prioritization Mode if (isSmartPrioritize) { console.log(chalk.cyan('\n๐ŸŽฏ Smart Test Prioritization Mode Activated!')); try { const SmartTestPrioritizer = require('./src/smart-prioritization/prioritizer-simple'); const prioritizer = new SmartTestPrioritizer(); console.log(chalk.yellow('๐Ÿ” Analyzing test risks and priorities...')); const results = await prioritizer.prioritizeTests({ analysisType: 'full', riskThreshold: 0.7 }); console.log(chalk.green('\n๐Ÿง  Smart Test Prioritization Complete!')); console.log(chalk.red(`๐Ÿ”ฅ High Priority Tests: ${results.high.length} tests (${results.high.avgRisk}% avg failure risk)`)); console.log(chalk.yellow(`โšก Medium Priority Tests: ${results.medium.length} tests (${results.medium.avgRisk}% avg failure risk)`)); console.log(chalk.blue(`๐Ÿ’ก Low Priority Tests: ${results.low.length} tests (${results.low.avgRisk}% avg failure risk)`)); console.log(chalk.dim(`โฑ๏ธ Estimated execution time: ${results.summary.estimatedTime} (${results.summary.timeReduction}% faster)`)); console.log(chalk.dim(`๐ŸŽฏ Expected coverage: ${results.summary.coverage}%`)); process.exit(0); } catch (error) { console.log(chalk.red('โŒ Smart Prioritization failed:'), error.message); process.exit(1); } } // Predictive Analysis Mode if (isPredict) { console.log(chalk.cyan('\n๐Ÿ”ฎ Predictive Analysis Mode Activated!')); try { const TestPredictor = require('./src/prediction/test-predictor-simple'); const predictor = new TestPredictor(); console.log(chalk.yellow('๐Ÿ” Analyzing failure patterns...')); const results = await predictor.predictFailures(); console.log(chalk.green('\n๐Ÿ”ฎ Failure Prediction Complete!')); console.log(chalk.red(`โš ๏ธ High Risk Tests: ${results.highRisk.length} tests`)); console.log(chalk.yellow(`โšก Medium Risk Tests: ${results.mediumRisk.length} tests`)); console.log(chalk.dim(`๐Ÿ“Š Prediction accuracy: ${results.accuracy}%`)); process.exit(0); } catch (error) { console.log(chalk.red('โŒ Prediction analysis failed:'), error.message); process.exit(1); } } // Natural Language Processing Mode if (isNLP) { console.log(chalk.cyan('\n๐Ÿ’ฌ Natural Language Test Creation Mode!')); try { const NLPCreator = require('./src/nlp/test-creator'); const creator = new NLPCreator(); const description = process.argv[process.argv.indexOf('--nlp') + 1]; if (!description) { console.log(chalk.red('โŒ Please provide test description: --nlp "user should login successfully"')); process.exit(1); } console.log(chalk.yellow(`๐Ÿ” Converting: "${description}"`)); const results = await creator.createFromNaturalLanguage(description); console.log(chalk.green('โœ… Test created successfully!')); console.log(chalk.dim(`๐Ÿ“ Test saved to: ${results.filePath}`)); process.exit(0); } catch (error) { console.log(chalk.red('โŒ NLP test creation failed:'), error.message); process.exit(1); } } // Coverage Watch Mode if (isCoverageWatch) { console.log(chalk.cyan('\n๐Ÿ“Š Real-time Coverage Monitoring Activated!')); try { const CoverageMonitor = require('./src/coverage/live-monitor'); const monitor = new CoverageMonitor(); console.log(chalk.yellow('๐Ÿ‘€ Watching for file changes...')); await monitor.startWatching(); // This keeps running until user stops it } catch (error) { console.log(chalk.red('โŒ Coverage monitoring failed:'), error.message); process.exit(1); } } // Train ML Models Mode if (isTrainModels) { console.log(chalk.cyan('\n๐ŸŽ“ ML Model Training Mode Activated!')); try { const ModelTrainer = require('./src/prediction/train-models'); const trainer = new ModelTrainer(); console.log(chalk.yellow('๐Ÿ” Training models with historical data...')); const results = await trainer.trainModels(); console.log(chalk.green('โœ… Models trained successfully!')); console.log(chalk.dim(`๐Ÿ“Š Model accuracy: ${results.accuracy}%`)); process.exit(0); } catch (error) { console.log(chalk.red('โŒ Model training failed:'), error.message); process.exit(1); } } // ๐Ÿ“Š NEW: Project Scanning Features // Handle baseline, changes, and scan commands if (isBaseline || isChanges || isScan) { (async () => { try { if (isBaseline) { console.log('๐Ÿ“Š VeriQA Project Baseline Scanner'); console.log('==================================\n'); const ProjectBaselineScanner = require('./src/scanning/project-baseline-scanner'); const scanner = new ProjectBaselineScanner(); await scanner.createBaseline(); console.log('\nโœ… Baseline scan completed successfully!'); console.log('๐Ÿ’ก Next: Use --changes to detect modifications'); } else if (isChanges) { console.log('๐Ÿ”„ VeriQA Change Detection'); console.log('=========================\n'); const ChangeDetectionSystem = require('./src/scanning/change-detection-system'); const detector = new ChangeDetectionSystem(); const changes = await detector.detectChanges(); // Display actionable insights if (changes.suggestedTests.length > 0) { console.log('\n๐ŸŽฏ SUGGESTED ACTIONS:'); console.log('===================='); const highPriorityTests = changes.suggestedTests.filter(t => t.priority === 'high'); const mediumPriorityTests = changes.suggestedTests.filter(t => t.priority === 'medium'); if (highPriorityTests.length > 0) { console.log('\n๐Ÿ”ฅ HIGH PRIORITY (Run immediately):'); highPriorityTests.forEach(test => { console.log(` โ–ถ veriqa-test-advisor --run tests/${test.testFile}`); }); } if (mediumPriorityTests.length > 0) { console.log('\nโšก MEDIUM PRIORITY (Run before commit):'); mediumPriorityTests.forEach(test => { console.log(` โ–ถ veriqa-test-advisor --run tests/${test.testFile}`); }); } console.log('\n๐Ÿ’ก Recommendation:', changes.riskAssessment.recommendedAction); } else { console.log('\nโœ… No significant changes detected - minimal testing needed'); } } else if (isScan) { console.log('๐Ÿ” VeriQA Quick Scan'); console.log('===================\n'); // Quick project overview const fs = require('fs'); const path = require('path'); let testCount = 0; let jsCount = 0; function quickScan(dir) { try { const items = fs.readdirSync(dir); items.forEach(item => { if (item.startsWith('.') || item === 'node_modules') return; const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { quickScan(fullPath); } else { if (item.match(/\.(test|spec)\.js$/)) testCount++; if (item.match(/\.js$/)) jsCount++; } }); } catch (error) { // Skip inaccessible directories } } quickScan('./tests'); quickScan('./src'); console.log(`๐Ÿ“Š Quick Project Overview:`); console.log(` ๐Ÿงช Test Files: ${testCount}`); console.log(` ๐Ÿ“ JS Files: ${jsCount}`); console.log(` ๐Ÿ—๏ธ Project: ${path.basename(process.cwd())}`); if (fs.existsSync('./veriqa-baseline.json')) { console.log(` โœ… Baseline: Available`); console.log('\n๐Ÿ’ก Use --changes to see what\'s modified'); } else { console.log(` ๐Ÿ“‹ Baseline: Not created`); console.log('\n๐Ÿ’ก Use --baseline to create project baseline'); } } process.exit(0); } catch (error) { console.error('\nโŒ Scanning failed:', error.message); console.log('\n๐Ÿ’ก Try: veriqa-test-advisor --help'); process.exit(1); } })(); return; } // :point_right: Smart Analysis Mode (Git + AI Combined) if (isSmartAnalyze) { // Track feature usage if (versionAnalytics) { versionAnalytics.trackFeatureUsage('smart-analysis', { verbose: isVerbose }); } const smartAnalyzer = new SmartAnalyzer(); const results = await smartAnalyzer.performSmartAnalysis({ verbose: isVerbose }); if (results && results.finalRecommendations.length > 0) { console.log(chalk.cyan('\n๐Ÿš€ Ready to execute recommended tests? Use:')); console.log(chalk.white('veriqa-test-advisor --run --tag smoke')); } printConsoleSummary(Date.now() - t0); return; } // :point_right: Enhanced Git Analysis Mode if (isGitAnalyze) { // Track feature usage if (versionAnalytics) { versionAnalytics.trackFeatureUsage('git-analysis', { verbose: isVerbose }); } const gitParser = new EnhancedGitDiffParser(); const analysis = await gitParser.analyzeChanges({ commitCount: 1, includeStaged: true, analyzeContent: true, verbose: isVerbose }); if (analysis) { console.log(chalk.bold.green('\n๐ŸŽฏ Enhanced Git Analysis Results:')); // Show summary console.log(chalk.cyan('\n๐Ÿ“Š Summary:')); console.log(`Files analyzed: ${analysis.summary.filesAnalyzed}`); console.log(`Test suggestions: ${analysis.summary.totalSuggestions}`); console.log(`Confidence level: ${analysis.summary.confidence}`); // Show suggestions by priority if (analysis.suggestions.highPriority.length > 0) { console.log(chalk.bold.green('\n๐Ÿ”ฅ High Priority Tests:')); analysis.suggestions.highPriority.forEach(s => { console.log(chalk.green(` โ€ข ${s.testFile} - ${s.reason}`)); }); } if (analysis.suggestions.mediumPriority.length > 0) { console.log(chalk.bold.yellow('\nโšก Medium Priority Tests:')); analysis.suggestions.mediumPriority.forEach(s => { console.log(chalk.yellow(` โ€ข ${s.testFile} - ${s.reason}`)); }); } if (analysis.suggestions.lowPriority.length > 0) { console.log(chalk.bold.blue('\n๐Ÿ’ก Low Priority Tests:')); analysis.suggestions.lowPriority.forEach(s => { console.log(chalk.blue(` โ€ข ${s.testFile} - ${s.reason}`)); }); } // Show reasoning if (analysis.suggestions.reasoning.length > 0) { console.log(chalk.cyan('\n๐Ÿง  Analysis Reasoning:')); analysis.suggestions.reasoning.forEach(r => { console.log(`${chalk.white(r.file)}: ${r.reason}`); }); } } printConsoleSummary(Date.now() - t0); return; } // :point_right: AI Mode if (isAI) { // Track feature usage if (versionAnalytics) { versionAnalytics.trackFeatureUsage('ai-suggestions'); } const git = simpleGit(); const log = await git.log(['-1']); const latestMessage = log.latest.message; logger.info(':page_facing_up: Latest Commit Message:\n' + latestMessage + '\n'); logger.info(':robot_face: Contacting OpenAI for suggestions...'); try { const aiSuggestion = await getAISuggestions(latestMessage); logger.success('\n:white_tick: AI Suggested Regression Test Cases:\n'); console.log(aiSuggestion); } catch (err) { logger.error(':x: AI Suggestion Failed: ' + (err.message || err)); } // Even if AI mode only, still attempt summary if tests ran previously in same job (optional) printConsoleSummary(Date.now() - t0); return; } // :point_right: Default Static Mapping // const modules = await getChangedModules(); const modules = Object.keys(generateTestMapping()); // all modules from mapping if (!modules.length) { logger.warn('No impacted modules detected.'); printConsoleSummary(Date.now() - t0); return; } logger.success(':package: Modules Changed: ' + modules.join(', ')); const tests = mapTestsToModules(modules); if (!tests.length) { logger.error(':x: No regression test cases found for these modules.'); printConsoleSummary(Date.now() - t0); return; } logger.success('\n:white_tick: Suggested Regression Test Cases:'); tests.forEach(tc => console.log(`- ${tc}`)); // :point_right: Smart Framework Execution if --run (supports both Playwright & CodeceptJS) if (isRun) { // ๐Ÿ”ฅ Option A: auto-clean previous run artifacts so counts don't inflate if (!isDryRun) { logger.info(':๐Ÿ”ฅ: Cleaning previous allure artifacts...'); const dirs = ['allure-results', 'output/allure-results', '.allure-merged', 'report']; for (const d of dirs) { try { if (fs.existsSync(d)) fs.rmSync(d, { recursive: true, force: true }); } catch (e) { // ignore errors, continue } } } const playwrightTests = []; const codeceptTests = []; tests.forEach(f => { const content = fs.readFileSync(f, 'utf-8'); if (content.includes('test(')) { playwrightTests.push(f); } else if (content.includes('Feature(')) { codeceptTests.push(f); } }); // Run Playwright tests if (playwrightTests.length > 0) { let cmd = `npx playwright test ${playwrightTests.map(f => `"${f}"`).join(' ')}`; if (tagValue) cmd += ` --grep "${tagValue}"`; if (envValue) cmd = `ENV=${envValue} ` + cmd; if (isDryRun) { logger.info('\n:test_tube: Dry Run Mode โ€” Playwright Command:'); console.log(cmd); } else { logger.info('\n:๐Ÿ”ฅ: Running Playwright tests...'); try { execSync(cmd, { stdio: 'inherit' }); anyRan = true; } catch (err) { anyRan = true; // tests attempted; allow summary to show failures logger.error(':x: Playwright execution failed.'); } } } // Run CodeceptJS tests if (codeceptTests.length > 0) { let cmd = `npx codeceptjs run ${codeceptTests.join(' ')}`; if (tagValue) cmd += ` --grep ${tagValue}`; if (envValue) cmd += ` --profile ${envValue}`; if (isDryRun) { logger.info('\n:test_tube: Dry Run Mode โ€” CodeceptJS Command:'); console.log(cmd); } else { logger.info('\n:๐Ÿ”ฅ: Running CodeceptJS tests...'); try { execSync(cmd, { stdio: 'inherit' }); anyRan = true; } catch (err) { anyRan = true; logger.error(':x: CodeceptJS execution failed.'); } } } } // k6-style console summary (works if Allure JSON exists) const wall = Date.now() - t0; if (anyRan) { printConsoleSummary(wall); } else { // If user didn't run tests, try anyway (maybe previous step produced allure JSON) printConsoleSummary(wall); } // ๐Ÿš€ NEW: Cutting-edge feature implementations // AI Test Generation if (isGenerate) { console.log(chalk.bold.cyan('\n๐Ÿค– AI Test Generation Mode')); try { const TestGenerator = require('./src/ai-generation/test-generator'); const generator = new TestGenerator(); const options = { module: moduleValue || 'all', coverageTarget: coverageTarget, framework: 'playwright', // default, can be enhanced to detect verbose: isVerbose }; console.log(`๐Ÿ“‹ Generating tests for module: ${options.module}`); console.log(`๐ŸŽฏ Coverage target: ${options.coverageTarget}%`); const results = await generator.generateTests(options); console.log(chalk.green(`\nโœ… Generated ${results.testsCreated} test files`)); console.log(chalk.cyan(`๐Ÿ“Š Estimated coverage improvement: +${results.coverageIncrease}%`)); if (results.files.length > 0) { console.log(chalk.white('\n๐Ÿ“ Generated files:')); results.files.forEach(file => console.log(` โ€ข ${file}`)); } } catch (error) { console.log(chalk.red(`โŒ Test generation failed: ${error.message}`)); } printConsoleSummary(Date.now() - t0); return; } // Smart Test Prioritization if (isSmartPrioritize) { console.log(chalk.bold.cyan('\nโšก Smart Test Prioritization Mode')); try { const SmartPrioritizer = require('./src/smart-prioritization/prioritizer'); const prioritizer = new SmartPrioritizer(); const options = { riskAnalysis: process.argv.includes('--risk-analysis'), maxExecutionTime: thresholdValue ? parseInt(thresholdValue) : 300, // 5 min default confidence: confidenceValue, verbose: isVerbose }; console.log('๐Ÿง  Analyzing code changes and test history...'); const prioritization = await prioritizer.prioritizeTests(options); console.log(chalk.green(`\n๐ŸŽฏ Test Prioritization Results:`)); console.log(`๐Ÿ“Š ${prioritization.highPriority.length} high priority tests`); console.log(`โšก ${prioritization.mediumPriority.length} medium priority tests`); console.log(`๐Ÿ’ก ${prioritization.lowPriority.length} low priority tests`); console.log(`โฑ๏ธ Estimated execution time: ${prioritization.estimatedTime}s`); console.log(`๐ŸŽฏ Expected coverage: ${prioritization.expectedCoverage}%`); if (prioritization.highPriority.length > 0) { console.log(chalk.bold.red('\n๐Ÿ”ฅ High Priority Tests (Run First):')); prioritization.highPriority.forEach(test => { console.log(` โ€ข ${test.name} (${test.confidence}% confidence)`); }); } } catch (error) { console.log(chalk.red(`โŒ Prioritization failed: ${error.message}`)); } printConsoleSummary(Date.now() - t0); return; } // Live Coverage Monitoring if (isCoverageWatch) { console.log(chalk.bold.cyan('\n๐Ÿ“Š Live Coverage Monitoring Mode')); try { const monitor = new LiveCoverageMonitor({ thresholds: { warning: 80, error: 60 }, updateInterval: 3000 }); console.log('๐Ÿš€ Starting real-time coverage monitoring...'); console.log('๐Ÿ‘€ Watching for file changes...'); console.log('๐ŸŽฏ Press Ctrl+C to stop monitoring'); monitor.startMonitoring(); // Graceful shutdown process.on('SIGINT', () => { monitor.stopMonitoring(); printConsoleSummary(Date.now() - t0); process.exit(0); }); // Keep process alive return new Promise(() => {}); } catch (error) { console.log(chalk.red(`โŒ Coverage monitoring failed: ${error.message}`)); } return; } // Predictive Test Failure Analysis if (isPredict) { console.log(chalk.bold.cyan('\n๐Ÿ”ฎ Predictive Failure Analysis Mode')); try { const predictor = new PredictiveModelTrainer(); console.log('๐Ÿง  Loading ML models...'); await predictor.loadHistoricalData(); const TestPredictor = require('./src/prediction/test-predictor'); const testPredictor = new TestPredictor(); const predictions = await testPredictor.predictFailures({ confidence: confidenceValue, includeReasons: true, verbose: isVerbose }); console.log(chalk.green(`\n๐Ÿ”ฎ Prediction Results:`)); console.log(`๐Ÿšจ ${predictions.likelyToFail.length} tests likely to fail`); console.log(`โœ… ${predictions.likelyToPass.length} tests likely to pass`); console.log(`๐ŸŽฏ Overall confidence: ${(predictions.confidence * 100).toFixed(1)}%`); if (predictions.likelyToFail.length > 0) { console.log(chalk.bold.red('\nโš ๏ธ Tests Likely to Fail:')); predictions.likelyToFail.forEach(test => { console.log(` โ€ข ${test.name} (${(test.failureProbability * 100).toFixed(1)}% failure chance)`); if (test.reason) console.log(` ${chalk.dim('Reason:')} ${test.reason}`); }); } } catch (error) { console.log(chalk.red(`โŒ Prediction failed: ${error.message}`)); } printConsoleSummary(Date.now() - t0); return; } // Natural Language Processing Test Creation if (isNLP) { console.log(chalk.bold.cyan('\n๐Ÿ’ฌ Natural Language Test Creation Mode')); try { const nlpDescription = process.argv.find((arg, i) => process.argv[i-1] === '--nlp' && !arg.startsWith('--') ); if (!nlpDescription) { console.log(chalk.red('โŒ Please provide a test description after --nlp')); console.log(chalk.white('Example: veriqa-test-advisor --nlp "Test login with invalid email shows error"')); return; } const creator = new NLPTestCreator(process.env.OPENAI_API_KEY); const framework = process.argv.includes('--codeceptjs') ? 'codeceptjs' : process.argv.includes('--cypress') ? 'cypress' : 'playwright'; console.log(`๐Ÿค– Converting to ${framework} test: "${nlpDescription}"`); const result = await creator.createTestFromDescription(nlpDescription, framework); console.log(chalk.green(`\nโœ… Test created successfully!`)); console.log(`๐Ÿ“ File: ${result.filePath}`); console.log(`๐Ÿท๏ธ Tags: ${result.metadata.tags.join(', ')}`); console.log(`๐Ÿ“‹ Steps: ${result.metadata.steps.length} test steps`); } catch (error) { console.log(chalk.red(`โŒ NLP test creation failed: ${error.message}`)); } printConsoleSummary(Date.now() - t0); return; } // Train ML Models if (isTrainModels) { console.log(chalk.bold.cyan('\n๐Ÿง  ML Model Training Mode')); try { const trainer = new PredictiveModelTrainer(); console.log('๐Ÿ“Š Loading historical test data...'); await trainer.loadHistoricalData(); console.log('๐Ÿš€ Training predictive models...'); await trainer.trainModel(); console.log(chalk.green('\nโœ… Model training completed successfully!')); console.log('๐ŸŽฏ Models are now ready for predictive analysis'); } catch (error) { console.log(chalk.red(`โŒ Model training failed: ${error.message}`)); } printConsoleSummary(Date.now() - t0); return; } // Enhanced Help for cutting-edge features if (isHelp) { console.log(chalk.bold.cyan('\n๐Ÿš€ VeriQA Test Advisor - Market-Leading AI Testing Tool\n')); console.log(chalk.white('๐Ÿ“‹ Basic Usage:')); console.log(' veriqa-test-advisor [options]'); console.log(chalk.white('\n๐Ÿ”ฅ Core Features:')); console.log(' --run Execute tests'); console.log(' --ai Get AI suggestions'); console.log(' --smart Smart analysis mode'); console.log(' --git-analyze Enhanced git analysis'); console.log(' --dry-run Preview without execution'); console.log(chalk.bold.cyan('\n๐Ÿš€ AI-Powered Features (Market Leaders):')); console.log(' --generate ๐Ÿค– Auto-generate test cases with AI'); console.log(' --smart-prioritize โšก ML-based test prioritization'); console.log(' --coverage-watch ๐Ÿ“Š Real-time coverage monitoring'); console.log(' --predict ๐Ÿ”ฎ Predict test failures before running'); console.log(' --nlp "description" ๐Ÿ’ฌ Create tests from natural language'); console.log(' --train-models ๐Ÿง  Train ML models on historical data'); console.log(chalk.white('\n๐ŸŽฏ Options:')); console.log(' --module <name> Target specific module'); console.log(' --coverage-target <n> Set coverage target (default: 90)'); console.log(' --confidence <n> ML confidence threshold (0-1, default: 0.8)'); console.log(' --threshold <value> Performance/coverage thresholds'); console.log(' --tag <tag> Filter by test tags'); console.log(' --verbose Detailed output'); console.log(chalk.green('\n๐Ÿ’ก Quick Examples:')); console.log(' veriqa-test-advisor --generate --module auth --coverage-target 95'); console.log(' veriqa-test-advisor --smart-prioritize --risk-analysis'); console.log(' veriqa-test-advisor --predict --confidence 0.9'); console.log(' veriqa-test-advisor --nlp "Test user can reset password"'); console.log(' veriqa-test-advisor --coverage-watch'); console.log(chalk.bold.blue('\n๐Ÿ† Why VeriQA is Market-Leading:')); console.log(' โœ… Only tool with AI test generation'); console.log(' โœ… ML-powered failure prediction'); console.log(' โœ… Real-time coverage intelligence'); console.log(' โœ… Natural language test creation'); console.log(' โœ… Smart risk-based prioritization'); return; } })();