ts-node-test
Version:
Use the Node.js test runner, but with TypeScript via ts-node
78 lines (77 loc) • 2.83 kB
JavaScript
import { spawn } from 'node:child_process';
import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';
import { resolveTestPaths } from './resolution.js';
const DEFAULT_TEST_EXTENSIONS = ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'];
export function getTestExtensions() {
if (process.env.TEST_EXTENSIONS != null) {
return process.env.TEST_EXTENSIONS.split(',').map(e => e.trim());
}
return DEFAULT_TEST_EXTENSIONS;
}
export async function main(paths, options) {
const require = createRequire(import.meta.url);
const esmLoader = pathToFileURL(require.resolve('ts-node/esm')).toString();
const extensions = getTestExtensions();
const resolvedPaths = await resolveTestPaths(paths, extensions);
if (resolvedPaths.length === 0) {
throw new Error('no test files found');
}
spawnChild(esmLoader, resolvedPaths, options);
}
/**
* Execute the Node.js test runner with the given loader and set of test file paths.
*
* @param loader The loader to use (fully resolved path to the loader script).
* @param resolvedTestPaths The files under test.
* @param options Additional options.
*/
function spawnChild(loader, resolvedTestPaths, options) {
const args = ['--loader', loader, '--test', ...getOptionFlags(options), ...resolvedTestPaths];
const child = spawn(process.execPath, args, {
stdio: 'inherit',
argv0: process.argv0
});
child.on('error', (error) => {
console.error(error);
process.exit(1);
});
child.on('exit', (code) => {
child.removeAllListeners();
process.off('SIGINT', sendSignalToChild);
process.off('SIGTERM', sendSignalToChild);
process.exitCode = code === null ? 1 : code;
});
process.on('SIGINT', sendSignalToChild);
process.on('SIGTERM', sendSignalToChild);
function sendSignalToChild(signal) {
if (child.pid != null) {
process.kill(child.pid, signal);
}
}
}
function getOptionFlags(options) {
const flags = [];
for (const pattern of options['test-name-pattern'] ?? []) {
flags.push('--test-name-pattern', pattern);
}
for (const reporter of options['test-reporter'] ?? []) {
flags.push('--test-reporter', reporter);
}
for (const destination of options['test-reporter-destination'] ?? []) {
flags.push('--test-reporter-destination', destination);
}
if (options['test-only'] === true) {
flags.push('--test-only');
}
if (options.watch === true) {
flags.push('--watch');
}
if (options['watch-preserve-output'] === true) {
flags.push('--watch-preserve-output');
}
if (options['experimental-test-coverage'] === true) {
flags.push('--experimental-test-coverage');
}
return flags;
}