feat(cli): Add --startFrom and --stopAt options to filter test files by range

This commit is contained in:
Philipp Kunz 2025-05-23 23:05:38 +00:00
parent 83b324b09f
commit 27c950c1a1
4 changed files with 80 additions and 10 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## 2025-05-23 - 1.10.0 - feat(cli)
Add --startFrom and --stopAt options to filter test files by range
- Introduced CLI options --startFrom and --stopAt in ts/index.ts for selective test execution
- Added validation to ensure provided range values are positive and startFrom is not greater than stopAt
- Propagated file range filtering into test grouping in tstest.classes.tstest.ts, applying the range filter across serial and parallel groups
- Updated usage messages to include the new options
## 2025-05-23 - 1.9.4 - fix(docs)
Update documentation and configuration for legal notices and CI permissions. This commit adds a new local settings file for tool permissions, refines the legal and trademark sections in the readme, and improves glob test files with clearer log messages.

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/tstest',
version: '1.9.4',
version: '1.10.0',
description: 'a test utility to run tests that match test/**/*.ts'
}

View File

@ -13,6 +13,8 @@ export const runCli = async () => {
const logOptions: LogOptions = {};
let testPath: string | null = null;
let tags: string[] = [];
let startFromFile: number | null = null;
let stopAtFile: number | null = null;
// Parse options
for (let i = 0; i < args.length; i++) {
@ -42,6 +44,32 @@ export const runCli = async () => {
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;
default:
if (!arg.startsWith('-')) {
testPath = arg;
@ -49,16 +77,24 @@ export const runCli = async () => {
}
}
// 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 <path> [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(' --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 <tags> Run only tests with specified tags (comma-separated)');
console.error(' --startFrom <n> Start running from test file number n');
console.error(' --stopAt <n> Stop running at test file number n');
process.exit(1);
}
@ -73,6 +109,11 @@ export const runCli = async () => {
executionMode = TestExecutionMode.DIRECTORY;
}
const tsTestInstance = new TsTest(process.cwd(), testPath, executionMode, logOptions, tags);
const tsTestInstance = new TsTest(process.cwd(), testPath, executionMode, logOptions, tags, startFromFile, stopAtFile);
await tsTestInstance.run();
};
// Execute CLI when this file is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
runCli();
}

View File

@ -16,6 +16,8 @@ export class TsTest {
public executionMode: TestExecutionMode;
public logger: TsTestLogger;
public filterTags: string[];
public startFromFile: number | null;
public stopAtFile: number | null;
public smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
@ -26,16 +28,35 @@ export class TsTest {
public tsbundleInstance = new plugins.tsbundle.TsBundle();
constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode, logOptions: LogOptions = {}, tags: string[] = []) {
constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode, logOptions: LogOptions = {}, tags: string[] = [], startFromFile: number | null = null, stopAtFile: number | null = null) {
this.executionMode = executionModeArg;
this.testDir = new TestDirectory(cwdArg, testPathArg, executionModeArg);
this.logger = new TsTestLogger(logOptions);
this.filterTags = tags;
this.startFromFile = startFromFile;
this.stopAtFile = stopAtFile;
}
async run() {
const testGroups = await this.testDir.getTestFileGroups();
const allFiles = [...testGroups.serial, ...Object.values(testGroups.parallelGroups).flat()];
let allFiles = [...testGroups.serial, ...Object.values(testGroups.parallelGroups).flat()];
// Apply file range filtering if specified
if (this.startFromFile !== null || this.stopAtFile !== null) {
const startIndex = this.startFromFile ? this.startFromFile - 1 : 0; // Convert to 0-based index
const endIndex = this.stopAtFile ? this.stopAtFile : allFiles.length;
allFiles = allFiles.slice(startIndex, endIndex);
// Filter the serial and parallel groups based on remaining files
testGroups.serial = testGroups.serial.filter(file => allFiles.includes(file));
Object.keys(testGroups.parallelGroups).forEach(groupName => {
testGroups.parallelGroups[groupName] = testGroups.parallelGroups[groupName].filter(file => allFiles.includes(file));
// Remove empty groups
if (testGroups.parallelGroups[groupName].length === 0) {
delete testGroups.parallelGroups[groupName];
}
});
}
// Log test discovery
this.logger.testDiscovery(