Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
b9fd8c7b02 | |||
d6842326ad | |||
175b4463fa | |||
6a8417e400 |
20
changelog.md
20
changelog.md
@@ -1,5 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-10-17 - 2.5.2 - fix(runtime.node)
|
||||
Improve Node runtime adapter to use tsrun.spawnPath, strengthen tsrun detection, and improve process lifecycle and loader handling; update tsrun dependency.
|
||||
|
||||
- Use tsrun.spawnPath to spawn Node test processes and pass structured spawn options (cwd, env, args, stdio).
|
||||
- Detect tsrun availability via plugins.tsrun and require spawnPath; provide a clearer error message when tsrun is missing or outdated.
|
||||
- Pass --web via spawn args and set TSTEST_FILTER_TAGS on the spawned process env instead of mutating the parent process.env.
|
||||
- When a 00init.ts exists, create a temporary loader that imports both 00init.ts and the test file, run the loader via tsrun.spawnPath, and clean up the loader after execution.
|
||||
- Use tsrunProcess.terminate()/kill for timeouts to ensure proper process termination and improve cleanup handling.
|
||||
- Export tsrun from ts/tstest.plugins.ts so runtime code can access tsrun APIs via the plugins object.
|
||||
- Bump dependency @git.zone/tsrun from ^1.3.4 to ^1.6.2 in package.json.
|
||||
|
||||
## 2025-10-16 - 2.5.1 - fix(deps)
|
||||
Bump dependencies and add local tooling settings
|
||||
|
||||
- Bumped @api.global/typedserver from ^3.0.78 to ^3.0.79
|
||||
- Bumped @git.zone/tsrun from ^1.3.3 to ^1.3.4
|
||||
- Bumped @push.rocks/smartjson from ^5.0.20 to ^5.2.0
|
||||
- Bumped @push.rocks/smartlog from ^3.1.9 to ^3.1.10
|
||||
- Add local settings configuration file for developer tooling
|
||||
|
||||
## 2025-10-12 - 2.5.0 - feat(tstest.classes.runtime.parser)
|
||||
Add support for "all" runtime token and update docs/tests; regenerate lockfile and add local settings
|
||||
|
||||
|
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@git.zone/tstest",
|
||||
"version": "2.5.0",
|
||||
"version": "2.5.2",
|
||||
"private": false,
|
||||
"description": "a test utility to run tests that match test/**/*.ts",
|
||||
"exports": {
|
||||
@@ -28,9 +28,9 @@
|
||||
"@types/node": "^22.15.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@api.global/typedserver": "^3.0.78",
|
||||
"@api.global/typedserver": "^3.0.79",
|
||||
"@git.zone/tsbundle": "^2.5.1",
|
||||
"@git.zone/tsrun": "^1.3.3",
|
||||
"@git.zone/tsrun": "^1.6.2",
|
||||
"@push.rocks/consolecolor": "^2.0.3",
|
||||
"@push.rocks/qenv": "^6.1.3",
|
||||
"@push.rocks/smartbrowser": "^2.0.8",
|
||||
@@ -40,8 +40,8 @@
|
||||
"@push.rocks/smartenv": "^5.0.13",
|
||||
"@push.rocks/smartexpect": "^2.5.0",
|
||||
"@push.rocks/smartfile": "^11.2.7",
|
||||
"@push.rocks/smartjson": "^5.0.20",
|
||||
"@push.rocks/smartlog": "^3.1.9",
|
||||
"@push.rocks/smartjson": "^5.2.0",
|
||||
"@push.rocks/smartlog": "^3.1.10",
|
||||
"@push.rocks/smartmongo": "^2.0.12",
|
||||
"@push.rocks/smartnetwork": "^4.4.0",
|
||||
"@push.rocks/smartpath": "^6.0.0",
|
||||
|
588
pnpm-lock.yaml
generated
588
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@git.zone/tstest',
|
||||
version: '2.5.0',
|
||||
version: '2.5.2',
|
||||
description: 'a test utility to run tests that match test/**/*.ts'
|
||||
}
|
||||
|
@@ -35,18 +35,11 @@ export class NodeRuntimeAdapter extends RuntimeAdapter {
|
||||
// Check Node.js version
|
||||
const nodeVersion = process.version;
|
||||
|
||||
// Check if tsrun is available
|
||||
const result = await this.smartshellInstance.exec('tsrun --version', {
|
||||
cwd: process.cwd(),
|
||||
onError: () => {
|
||||
// Ignore error
|
||||
}
|
||||
});
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
// Check if tsrun module is available (imported as dependency)
|
||||
if (!plugins.tsrun || !plugins.tsrun.spawnPath) {
|
||||
return {
|
||||
available: false,
|
||||
error: 'tsrun not found. Install with: pnpm install --save-dev @git.zone/tsrun',
|
||||
error: 'tsrun module not found or outdated (requires version 1.6.0+)',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -96,7 +89,7 @@ export class NodeRuntimeAdapter extends RuntimeAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a test file in Node.js
|
||||
* Execute a test file in Node.js using tsrun's spawnPath API
|
||||
*/
|
||||
async run(
|
||||
testFile: string,
|
||||
@@ -109,28 +102,35 @@ export class NodeRuntimeAdapter extends RuntimeAdapter {
|
||||
|
||||
const mergedOptions = this.mergeOptions(options);
|
||||
|
||||
// Build tsrun command
|
||||
let tsrunOptions = '';
|
||||
// Build spawn options
|
||||
const spawnOptions: any = {
|
||||
cwd: mergedOptions.cwd || process.cwd(),
|
||||
env: { ...mergedOptions.env },
|
||||
args: [] as string[],
|
||||
stdio: 'pipe' as const,
|
||||
};
|
||||
|
||||
// Add --web flag if needed
|
||||
if (process.argv.includes('--web')) {
|
||||
tsrunOptions += ' --web';
|
||||
spawnOptions.args.push('--web');
|
||||
}
|
||||
|
||||
// Set filter tags as environment variable
|
||||
if (this.filterTags.length > 0) {
|
||||
process.env.TSTEST_FILTER_TAGS = this.filterTags.join(',');
|
||||
spawnOptions.env.TSTEST_FILTER_TAGS = this.filterTags.join(',');
|
||||
}
|
||||
|
||||
// Check for 00init.ts file in test directory
|
||||
const testDir = plugins.path.dirname(testFile);
|
||||
const initFile = plugins.path.join(testDir, '00init.ts');
|
||||
let runCommand = `tsrun ${testFile}${tsrunOptions}`;
|
||||
|
||||
const initFileExists = await plugins.smartfile.fs.fileExists(initFile);
|
||||
|
||||
// If 00init.ts exists, run it first
|
||||
// Determine which file to run
|
||||
let fileToRun = testFile;
|
||||
let loaderPath: string | null = null;
|
||||
|
||||
// If 00init.ts exists, create a loader file
|
||||
if (initFileExists) {
|
||||
// Create a temporary loader file that imports both 00init.ts and the test file
|
||||
const absoluteInitFile = plugins.path.resolve(initFile);
|
||||
const absoluteTestFile = plugins.path.resolve(testFile);
|
||||
const loaderContent = `
|
||||
@@ -139,10 +139,12 @@ import '${absoluteTestFile.replace(/\\/g, '/')}';
|
||||
`;
|
||||
loaderPath = plugins.path.join(testDir, `.loader_${plugins.path.basename(testFile)}`);
|
||||
await plugins.smartfile.memory.toFs(loaderContent, loaderPath);
|
||||
runCommand = `tsrun ${loaderPath}${tsrunOptions}`;
|
||||
fileToRun = loaderPath;
|
||||
}
|
||||
|
||||
const execResultStreaming = await this.smartshellInstance.execStreamingSilent(runCommand);
|
||||
// Spawn the test process using tsrun's spawnPath API
|
||||
// Pass undefined for fromFileUrl since fileToRun is already an absolute path
|
||||
const tsrunProcess = plugins.tsrun.spawnPath(fileToRun, undefined, spawnOptions);
|
||||
|
||||
// If we created a loader file, clean it up after test execution
|
||||
if (loaderPath) {
|
||||
@@ -156,8 +158,8 @@ import '${absoluteTestFile.replace(/\\/g, '/')}';
|
||||
}
|
||||
};
|
||||
|
||||
execResultStreaming.childProcess.on('exit', cleanup);
|
||||
execResultStreaming.childProcess.on('error', cleanup);
|
||||
tsrunProcess.childProcess.on('exit', cleanup);
|
||||
tsrunProcess.childProcess.on('error', cleanup);
|
||||
}
|
||||
|
||||
// Start warning timer if no timeout was specified
|
||||
@@ -180,15 +182,15 @@ import '${absoluteTestFile.replace(/\\/g, '/')}';
|
||||
|
||||
const timeoutPromise = new Promise<void>((_resolve, reject) => {
|
||||
timeoutId = setTimeout(async () => {
|
||||
// Use smartshell's terminate() to kill entire process tree
|
||||
await execResultStreaming.terminate();
|
||||
// Use tsrun's terminate() to gracefully kill the process
|
||||
await tsrunProcess.terminate();
|
||||
reject(new Error(`Test file timed out after ${this.timeoutSeconds} seconds`));
|
||||
}, timeoutMs);
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.race([
|
||||
tapParser.handleTapProcess(execResultStreaming.childProcess),
|
||||
tapParser.handleTapProcess(tsrunProcess.childProcess),
|
||||
timeoutPromise
|
||||
]);
|
||||
// Clear timeout if test completed successfully
|
||||
@@ -200,16 +202,16 @@ import '${absoluteTestFile.replace(/\\/g, '/')}';
|
||||
}
|
||||
// Handle timeout error
|
||||
tapParser.handleTimeout(this.timeoutSeconds);
|
||||
// Ensure entire process tree is killed if still running
|
||||
// Ensure process is killed if still running
|
||||
try {
|
||||
await execResultStreaming.kill(); // This kills the entire process tree with SIGKILL
|
||||
tsrunProcess.kill('SIGKILL');
|
||||
} catch (killError) {
|
||||
// Process tree might already be dead
|
||||
// Process might already be dead
|
||||
}
|
||||
await tapParser.evaluateFinalResult();
|
||||
}
|
||||
} else {
|
||||
await tapParser.handleTapProcess(execResultStreaming.childProcess);
|
||||
await tapParser.handleTapProcess(tsrunProcess.childProcess);
|
||||
}
|
||||
|
||||
// Clear warning timer if it was set
|
||||
|
@@ -37,8 +37,9 @@ export {
|
||||
|
||||
// @git.zone scope
|
||||
import * as tsbundle from '@git.zone/tsbundle';
|
||||
import * as tsrun from '@git.zone/tsrun';
|
||||
|
||||
export { tsbundle };
|
||||
export { tsbundle, tsrun };
|
||||
|
||||
// sindresorhus
|
||||
import figures from 'figures';
|
||||
|
Reference in New Issue
Block a user