feat(cli): Add new timeout and file range options with enhanced logfile diff logging

This commit is contained in:
2025-05-24 01:32:41 +00:00
parent b525754035
commit 31bf090410
7 changed files with 266 additions and 30 deletions

View File

@@ -18,6 +18,7 @@ export class TsTest {
public filterTags: string[];
public startFromFile: number | null;
public stopAtFile: number | null;
public timeoutSeconds: number | null;
public smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
@@ -28,13 +29,14 @@ export class TsTest {
public tsbundleInstance = new plugins.tsbundle.TsBundle();
constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode, logOptions: LogOptions = {}, tags: string[] = [], startFromFile: number | null = null, stopAtFile: number | null = null) {
constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode, logOptions: LogOptions = {}, tags: string[] = [], startFromFile: number | null = null, stopAtFile: number | null = null, timeoutSeconds: 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;
this.timeoutSeconds = timeoutSeconds;
}
async run() {
@@ -147,7 +149,30 @@ export class TsTest {
const execResultStreaming = await this.smartshellInstance.execStreamingSilent(
`tsrun ${fileNameArg}${tsrunOptions}`
);
await tapParser.handleTapProcess(execResultStreaming.childProcess);
// Handle timeout if specified
if (this.timeoutSeconds !== null) {
const timeoutMs = this.timeoutSeconds * 1000;
const timeoutPromise = new Promise<void>((_resolve, reject) => {
setTimeout(() => {
execResultStreaming.childProcess.kill('SIGTERM');
reject(new Error(`Test file timed out after ${this.timeoutSeconds} seconds`));
}, timeoutMs);
});
try {
await Promise.race([
tapParser.handleTapProcess(execResultStreaming.childProcess),
timeoutPromise
]);
} catch (error) {
// Handle timeout error
tapParser.handleTimeout(this.timeoutSeconds);
}
} else {
await tapParser.handleTapProcess(execResultStreaming.childProcess);
}
return tapParser;
}
@@ -205,9 +230,10 @@ export class TsTest {
});
});
// lets do the browser bit
// lets do the browser bit with timeout handling
await this.smartbrowserInstance.start();
await this.smartbrowserInstance.evaluateOnPage(
const evaluatePromise = this.smartbrowserInstance.evaluateOnPage(
`http://localhost:3007/test?bundleName=${bundleFileName}`,
async () => {
// lets enable real time comms
@@ -264,6 +290,29 @@ export class TsTest {
return logStore.join('\n');
}
);
// Handle timeout if specified
if (this.timeoutSeconds !== null) {
const timeoutMs = this.timeoutSeconds * 1000;
const timeoutPromise = new Promise<void>((_resolve, reject) => {
setTimeout(() => {
reject(new Error(`Test file timed out after ${this.timeoutSeconds} seconds`));
}, timeoutMs);
});
try {
await Promise.race([
evaluatePromise,
timeoutPromise
]);
} catch (error) {
// Handle timeout error
tapParser.handleTimeout(this.timeoutSeconds);
}
} else {
await evaluatePromise;
}
await this.smartbrowserInstance.stop();
await server.stop();
wss.close();
@@ -280,28 +329,38 @@ export class TsTest {
private async movePreviousLogFiles() {
const logDir = plugins.path.join('.nogit', 'testlogs');
const previousDir = plugins.path.join('.nogit', 'testlogs', 'previous');
const errDir = plugins.path.join('.nogit', 'testlogs', '00err');
const diffDir = plugins.path.join('.nogit', 'testlogs', '00diff');
try {
// Get all files in log directory
// Delete 00err and 00diff directories if they exist
if (await plugins.smartfile.fs.isDirectory(errDir)) {
await plugins.smartfile.fs.remove(errDir);
}
if (await plugins.smartfile.fs.isDirectory(diffDir)) {
await plugins.smartfile.fs.remove(diffDir);
}
// Get all .log files in log directory (not in subdirectories)
const files = await plugins.smartfile.fs.listFileTree(logDir, '*.log');
if (files.length === 0) {
const logFiles = files.filter(file => !file.includes('/'));
if (logFiles.length === 0) {
return;
}
// Ensure previous directory exists
await plugins.smartfile.fs.ensureDir(previousDir);
// Move each file to previous directory
for (const file of files) {
// Move each log file to previous directory
for (const file of logFiles) {
const filename = plugins.path.basename(file);
const sourcePath = plugins.path.join(logDir, filename);
const destPath = plugins.path.join(previousDir, filename);
try {
// Read file content and write to new location
const content = await plugins.smartfile.fs.toStringSync(sourcePath);
await plugins.smartfile.fs.toFs(content, destPath);
// Remove original file
// Copy file to new location and remove original
await plugins.smartfile.fs.copy(sourcePath, destPath);
await plugins.smartfile.fs.remove(sourcePath);
} catch (error) {
// Silently continue if a file can't be moved