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
JavaScript
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;
}
})();