import { TsTest } from './tstest.classes.tstest.js'; import type { LogOptions } from './tstest.logging.js'; export enum TestExecutionMode { DIRECTORY = 'directory', FILE = 'file', GLOB = 'glob' } export const runCli = async () => { // Parse command line arguments const args = process.argv.slice(2); const logOptions: LogOptions = {}; let testPath: string | null = null; let tags: string[] = []; let startFromFile: number | null = null; let stopAtFile: number | null = null; let timeoutSeconds: number | null = null; // Parse options for (let i = 0; i < args.length; i++) { const arg = args[i]; switch (arg) { case '--quiet': case '-q': logOptions.quiet = true; break; case '--verbose': case '-v': logOptions.verbose = true; break; case '--no-color': logOptions.noColor = true; break; case '--json': logOptions.json = true; break; case '--log-file': case '--logfile': logOptions.logFile = true; // Set this as a flag, not a value break; case '--tags': if (i + 1 < args.length) { tags = args[++i].split(','); } break; case '--startFrom': if (i + 1 < args.length) { const value = parseInt(args[++i], 10); if (isNaN(value) || value < 1) { console.error('Error: --startFrom must be a positive integer'); process.exit(1); } startFromFile = value; } else { console.error('Error: --startFrom requires a number argument'); process.exit(1); } break; case '--stopAt': if (i + 1 < args.length) { const value = parseInt(args[++i], 10); if (isNaN(value) || value < 1) { console.error('Error: --stopAt must be a positive integer'); process.exit(1); } stopAtFile = value; } else { console.error('Error: --stopAt requires a number argument'); process.exit(1); } break; case '--timeout': if (i + 1 < args.length) { const value = parseInt(args[++i], 10); if (isNaN(value) || value < 1) { console.error('Error: --timeout must be a positive integer (seconds)'); process.exit(1); } timeoutSeconds = value; } else { console.error('Error: --timeout requires a number argument (seconds)'); process.exit(1); } break; default: if (!arg.startsWith('-')) { testPath = arg; } } } // Validate test file range options if (startFromFile !== null && stopAtFile !== null && startFromFile > stopAtFile) { console.error('Error: --startFrom cannot be greater than --stopAt'); process.exit(1); } if (!testPath) { console.error('You must specify a test directory/file/pattern as argument. Please try again.'); console.error('\nUsage: tstest [options]'); console.error('\nOptions:'); console.error(' --quiet, -q Minimal output'); console.error(' --verbose, -v Verbose output'); console.error(' --no-color Disable colored output'); console.error(' --json Output results as JSON'); console.error(' --logfile Write logs to .nogit/testlogs/[testfile].log'); console.error(' --tags Run only tests with specified tags (comma-separated)'); console.error(' --startFrom Start running from test file number n'); console.error(' --stopAt Stop running at test file number n'); console.error(' --timeout Timeout test files after s seconds'); process.exit(1); } let executionMode: TestExecutionMode; // Detect execution mode based on the argument if (testPath.includes('*') || testPath.includes('?') || testPath.includes('[') || testPath.includes('{')) { executionMode = TestExecutionMode.GLOB; } else if (testPath.endsWith('.ts')) { executionMode = TestExecutionMode.FILE; } else { executionMode = TestExecutionMode.DIRECTORY; } const tsTestInstance = new TsTest(process.cwd(), testPath, executionMode, logOptions, tags, startFromFile, stopAtFile, timeoutSeconds); await tsTestInstance.run(); }; // Execute CLI when this file is run directly if (import.meta.url === `file://${process.argv[1]}`) { runCli(); }