UNPKG

@vizzly-testing/cli

Version:

Visual review platform for UI developers and designers

122 lines (115 loc) 7.33 kB
#!/usr/bin/env node import 'dotenv/config'; import { program } from 'commander'; import { init } from './commands/init.js'; import { uploadCommand, validateUploadOptions } from './commands/upload.js'; import { runCommand, validateRunOptions } from './commands/run.js'; import { tddCommand, validateTddOptions } from './commands/tdd.js'; import { statusCommand, validateStatusOptions } from './commands/status.js'; import { finalizeCommand, validateFinalizeOptions } from './commands/finalize.js'; import { doctorCommand, validateDoctorOptions } from './commands/doctor.js'; import { getPackageVersion } from './utils/package-info.js'; program.name('vizzly').description('Vizzly CLI for visual regression testing').version(getPackageVersion()).option('-c, --config <path>', 'Config file path').option('--token <token>', 'Vizzly API token').option('-v, --verbose', 'Verbose output').option('--json', 'Machine-readable output').option('--no-color', 'Disable colored output'); program.command('init').description('Initialize Vizzly in your project').option('--force', 'Overwrite existing configuration').action(async options => { const globalOptions = program.opts(); await init({ ...globalOptions, ...options }); }); program.command('upload').description('Upload screenshots to Vizzly').argument('<path>', 'Path to screenshots directory or file').option('-b, --build-name <name>', 'Build name for grouping').option('-m, --metadata <json>', 'Additional metadata as JSON').option('--batch-size <n>', 'Upload batch size', v => parseInt(v, 10)).option('--upload-timeout <ms>', 'Upload timeout in milliseconds', v => parseInt(v, 10)).option('--branch <branch>', 'Git branch').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').option('--upload-all', 'Upload all screenshots without SHA deduplication').option('--parallel-id <id>', 'Unique identifier for parallel test execution').action(async (path, options) => { const globalOptions = program.opts(); // Validate options const validationErrors = validateUploadOptions(path, options); if (validationErrors.length > 0) { console.error('Validation errors:'); validationErrors.forEach(error => console.error(` - ${error}`)); process.exit(1); } await uploadCommand(path, options, globalOptions); }); program.command('tdd').description('Run tests in TDD mode with local visual comparisons').argument('<command>', 'Test command to run').option('--port <port>', 'Port for screenshot server', '47392').option('--branch <branch>', 'Git branch override').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--baseline-build <id>', 'Use specific build as baseline').option('--baseline-comparison <id>', 'Use specific comparison as baseline').option('--set-baseline', 'Accept current screenshots as new baseline (overwrites existing)').option('--allow-no-token', 'Allow running without API token (no baselines)').action(async (command, options) => { const globalOptions = program.opts(); // Validate options const validationErrors = validateTddOptions(command, options); if (validationErrors.length > 0) { console.error('Validation errors:'); validationErrors.forEach(error => console.error(` - ${error}`)); process.exit(1); } const { result, cleanup } = await tddCommand(command, options, globalOptions); // Set up cleanup on process signals const handleCleanup = async () => { await cleanup(); }; process.once('SIGINT', () => { handleCleanup().then(() => process.exit(1)); }); process.once('SIGTERM', () => { handleCleanup().then(() => process.exit(1)); }); if (result && !result.success && result.exitCode > 0) { await cleanup(); process.exit(result.exitCode); } await cleanup(); }); program.command('run').description('Run tests with Vizzly integration').argument('<command>', 'Test command to run').option('--port <port>', 'Port for screenshot server', '47392').option('-b, --build-name <name>', 'Custom build name').option('--branch <branch>', 'Git branch override').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--allow-no-token', 'Allow running without API token').option('--upload-all', 'Upload all screenshots without SHA deduplication').option('--parallel-id <id>', 'Unique identifier for parallel test execution').action(async (command, options) => { const globalOptions = program.opts(); // Validate options const validationErrors = validateRunOptions(command, options); if (validationErrors.length > 0) { console.error('Validation errors:'); validationErrors.forEach(error => console.error(` - ${error}`)); process.exit(1); } try { const result = await runCommand(command, options, globalOptions); if (result && !result.success && result.exitCode > 0) { process.exit(result.exitCode); } } catch (error) { console.error('Command failed:', error.message); if (globalOptions.verbose) { console.error('Stack trace:', error.stack); } process.exit(1); } }); program.command('status').description('Check the status of a build').argument('<build-id>', 'Build ID to check status for').action(async (buildId, options) => { const globalOptions = program.opts(); // Validate options const validationErrors = validateStatusOptions(buildId, options); if (validationErrors.length > 0) { console.error('Validation errors:'); validationErrors.forEach(error => console.error(` - ${error}`)); process.exit(1); } await statusCommand(buildId, options, globalOptions); }); program.command('finalize').description('Finalize a parallel build after all shards complete').argument('<parallel-id>', 'Parallel ID to finalize').action(async (parallelId, options) => { const globalOptions = program.opts(); // Validate options const validationErrors = validateFinalizeOptions(parallelId, options); if (validationErrors.length > 0) { console.error('Validation errors:'); validationErrors.forEach(error => console.error(` - ${error}`)); process.exit(1); } await finalizeCommand(parallelId, options, globalOptions); }); program.command('doctor').description('Run diagnostics to check your environment and configuration').option('--api', 'Include API connectivity checks').action(async options => { const globalOptions = program.opts(); // Validate options const validationErrors = validateDoctorOptions(options); if (validationErrors.length > 0) { console.error('Validation errors:'); validationErrors.forEach(error => console.error(` - ${error}`)); process.exit(1); } await doctorCommand(options, globalOptions); }); program.parse();