ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
172 lines • 8.86 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runCommand = runCommand;
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const path_1 = __importDefault(require("path"));
const testRunner_1 = require("../core/testRunner");
const fileUtils_1 = require("../utils/fileUtils");
const table_1 = require("table");
const figures_1 = __importDefault(require("figures"));
function runCommand(program) {
program
.command('run')
.description('Execute generated end-to-end tests')
.argument('[test-path]', 'Path to test file or directory', './tests')
.option('-b, --browser <browser>', 'Browser to run tests in', 'chromium')
.option('-h, --headless', 'Run browser in headless mode', true)
.option('-t, --timeout <timeout>', 'Test timeout in seconds', '30')
.option('-r, --reporter <reporter>', 'Test reporter format', 'list')
.option('-w, --workers <workers>', 'Number of parallel workers to use', '1')
.option('-v, --verbose', 'Show detailed test output', false)
.option('-f, --filter <filter>', 'Filter tests by name pattern')
.option('--retries <retries>', 'Number of retries for failed tests', '0')
.option('--update-snapshots', 'Update snapshots', false)
.option('--type <type>', 'Filter tests by type (ui, api, security)')
.action(async (testPath, options) => {
const spinner = (0, ora_1.default)('Preparing test environment...').start();
try {
// Validate test path
const absoluteTestPath = path_1.default.resolve(process.cwd(), testPath);
if (!await (0, fileUtils_1.isValidTestPath)(absoluteTestPath)) {
spinner.fail(`Invalid test path: ${testPath}`);
return;
}
// Build filter pattern from type option if specified
let filter = options.filter || '';
if (options.type) {
const typeMappings = {
'ui': 'UI/UX',
'api': 'API',
'security': 'Security'
};
const typeFilter = typeMappings[options.type.toLowerCase()];
if (typeFilter) {
filter = filter ? `${filter}|${typeFilter}` : typeFilter;
}
}
// Initialize test runner
const runner = new testRunner_1.TestRunner({
browser: options.browser,
headless: options.headless === 'false' ? false : Boolean(options.headless),
timeout: parseInt(options.timeout, 10),
reporter: options.reporter,
workers: parseInt(options.workers, 10),
filter,
retries: parseInt(options.retries, 10),
verbose: options.verbose,
updateSnapshots: options.updateSnapshots
});
// Set up event listeners for real-time feedback
if (options.verbose) {
runner.on('run:start', ({ testFiles }) => {
spinner.stop();
console.log(chalk_1.default.blue(`\nRunning ${testFiles.length} test files${options.workers > 1 ? ` with ${options.workers} workers` : ''}...`));
});
runner.on('file:start', ({ path: filePath }) => {
console.log(chalk_1.default.cyan(`\nRunning tests in ${path_1.default.relative(process.cwd(), filePath)}...`));
});
runner.on('test:start', ({ path: filePath, name }) => {
console.log(chalk_1.default.gray(` ${figures_1.default.play} ${name}`));
});
runner.on('test:end', (result) => {
const icon = result.status === 'passed' ? chalk_1.default.green(figures_1.default.tick) :
result.status === 'failed' ? chalk_1.default.red(figures_1.default.cross) : chalk_1.default.yellow(figures_1.default.warning);
console.log(` ${icon} ${result.name} (${Math.round(result.duration)}ms)`);
if (result.status === 'failed' && result.error) {
console.log(chalk_1.default.red(` ${result.error.message}`));
}
});
runner.on('error', (error) => {
console.error(chalk_1.default.red(`Error in ${path_1.default.relative(process.cwd(), error.testPath)}: ${error.message}`));
});
}
spinner.text = 'Running tests...';
const results = await runner.runTests(absoluteTestPath);
// Clear spinner
spinner.stop();
// Print final results
console.log('\n' + chalk_1.default.bgBlue.white(' TEST RESULTS ') + '\n');
// Format duration to be more readable
const formatDuration = (ms) => {
if (ms < 1000)
return `${ms.toFixed(0)}ms`;
return `${(ms / 1000).toFixed(1)}s`;
};
// Print summary table
const summaryData = [
[chalk_1.default.bold('Total'), chalk_1.default.bold('Passed'), chalk_1.default.bold('Failed'), chalk_1.default.bold('Skipped'), chalk_1.default.bold('Duration')],
[
results.total.toString(),
chalk_1.default.green(results.passed.toString()),
results.failed > 0 ? chalk_1.default.red(results.failed.toString()) : '0',
results.skipped > 0 ? chalk_1.default.yellow(results.skipped.toString()) : '0',
formatDuration(results.duration)
]
];
console.log((0, table_1.table)(summaryData, {
border: {
topBody: '─',
topJoin: '┬',
topLeft: '┌',
topRight: '┐',
bottomBody: '─',
bottomJoin: '┴',
bottomLeft: '└',
bottomRight: '┘',
bodyLeft: '│',
bodyRight: '│',
bodyJoin: '│',
joinBody: '─',
joinLeft: '├',
joinRight: '┤',
joinJoin: '┼',
}
}));
// If there are failures, print details
if (results.failed > 0) {
console.log(chalk_1.default.red.bold('\nTest Failures:'));
// Group failures by file
const fileResults = results.fileResults.filter(file => file.failed > 0);
fileResults.forEach((fileResult, index) => {
console.log(`\n${chalk_1.default.bold(`${index + 1}. ${path_1.default.relative(process.cwd(), fileResult.path)}`)}`);
// Print failed tests in this file
fileResult.tests
.filter(test => test.status === 'failed')
.forEach(test => {
console.log(chalk_1.default.red(` ${figures_1.default.cross} ${test.name}`));
if (test.error) {
console.log(chalk_1.default.gray(` ${test.error.message.replace(/\n/g, '\n ')}`));
}
});
// If no tests found but file has errors
if (fileResult.tests.length === 0 && fileResult.errors.length > 0) {
fileResult.errors.forEach(error => {
console.log(chalk_1.default.red(` ${figures_1.default.cross} ${error.message.split('\n')[0]}`));
if (options.verbose && error.stack) {
console.log(chalk_1.default.gray(` ${error.stack.replace(/\n/g, '\n ')}`));
}
});
}
});
}
// Print success message or exit with failure
if (results.failed === 0) {
console.log(chalk_1.default.green.bold('\n✓ All tests passed!'));
}
else {
console.log(chalk_1.default.red.bold(`\n✗ ${results.failed} test(s) failed.`));
process.exit(1);
}
}
catch (error) {
spinner.fail(`Test execution failed: ${error.message}`);
console.error(chalk_1.default.red(error.stack));
process.exit(1);
}
});
}
//# sourceMappingURL=run.js.map