TypeScript Node.js Test Runner Wrapper
ts-node-test is a command-line interface (CLI) wrapper that enables the use of Node.js's native test runner (introduced in Node.js 18.7.0) with TypeScript files. It addresses a limitation in Node.js where the built-in test runner does not natively support custom file extensions for automatic test file discovery, requiring explicit file paths for `.ts` files. This package works by recursively searching specified directories for `.ts`, `.mts`, `.cts`, `.js`, `.mjs`, and `.cjs` files, then passing the discovered files to the Node.js test runner via `ts-node` for execution. The current stable version is 0.4.4, with releases occurring intermittently, focusing on bug fixes, dependency updates, and feature parity with newer Node.js test runner flags. Its primary differentiator is simplifying the setup for TypeScript projects wanting to leverage Node.js's native testing capabilities without complex build steps or manual file listings.
Common errors
-
Error: Cannot find module 'typescript' from '...' at Function.resolveSync
cause The `typescript` package is a peer dependency of `ts-node-test` but is not installed in your project.fixInstall TypeScript as a dev dependency: `npm install -D typescript` or `yarn add -D typescript`. -
Error: The Node.js test runner API is only available in Node.js >= 18.7.0.
cause You are trying to run `ts-node-test` with an older version of Node.js that does not meet the minimum requirement of 18.7.0.fixUpgrade your Node.js environment to version 18.7.0 or newer. Consider using a tool like `nvm` to manage Node.js versions. -
TypeError [ERR_INVALID_ARG_TYPE]: The 'path' argument must be of type string or an instance of 'URL'. Received null
cause This error can occur if `TEST_EXTENSIONS` environment variable is set to an invalid value (e.g., empty or null) or if the test paths provided to `ts-node-test` are incorrect or resolve to nothing.fixEnsure `TEST_EXTENSIONS` is a comma-separated string of valid extensions (e.g., `.ts,.js`). Double-check the glob patterns or file paths passed to `ts-node-test` to ensure they point to existing files.
Warnings
- breaking ts-node-test requires Node.js version 18.7.0 or higher due to its reliance on the native Node.js test runner API. Earlier Node.js versions are not supported.
- gotcha A `typescript` peer dependency is required. Ensure `typescript` is installed in your project (e.g., as a dev dependency) alongside `ts-node-test`. Missing this will result in compilation errors.
- gotcha Prior to v0.4.1, `ts-node-test` would recursively search for test files within `node_modules` if a directory path like `./` was provided, potentially leading to unintended test execution or performance issues. This behavior was fixed to explicitly exclude `node_modules` unless specified.
- deprecated Older versions of `ts-node-test` might not fully support all command-line flags available in the latest Node.js test runner. For example, `--watch` mode was introduced in v0.3.0, and support for additional flags like `--experimental-test-coverage` was added in v0.4.0.
Install
-
npm install ts-node-test -
yarn add ts-node-test -
pnpm add ts-node-test
Imports
- Basic CLI Usage
npm i -D ts-node-test && npm test
- CLI with Custom Extensions
TEST_EXTENSIONS=.test.ts,.spec.ts npm test
- CLI with Node.js Test Flags
npm i -D ts-node-test && ts-node-test --watch --test-name-pattern='my_test' test/**/*.ts
Quickstart
/* package.json */
{
"name": "my-ts-project",
"version": "1.0.0",
"scripts": {
"test": "ts-node-test 'src/**/*.test.ts' 'test/**/*.test.ts'"
},
"devDependencies": {
"ts-node-test": "^0.4.4",
"typescript": "^5.0.0",
"@types/node": "^18.7.0"
},
"engines": {
"node": ">=18.7.0"
}
}
/* test/example.test.ts */
import { test, mock } from 'node:test';
import { equal, ok } from 'node:assert/strict';
const greet = mock.fn((name: string) => `Hello, ${name}!`);
test('synchronous passing test', () => {
equal(1, 1, 'Numbers should be equal');
});
test('asynchronous passing test', async () => {
const promise = Promise.resolve(42);
const result = await promise;
equal(result, 42, 'Result should be 42');
});
test('test with mocks and assertions', () => {
greet('World');
equal(greet.mock.callCount(), 1, 'greet should be called once');
equal(greet.mock.calls[0].arguments[0], 'World', 'Argument should be World');
equal(greet.mock.calls[0].result, 'Hello, World!', 'Return value should be correct');
});
test('test with subtest and tags', { tags: ['core', 'feature'] }, async (t) => {
await t.test('subtest 1: addition', () => {
ok(2 + 2 === 4, '2 + 2 should be 4');
});
await t.test('subtest 2: skipped example', { skip: true }, () => {
// This test will be skipped and not executed
equal(true, false, 'This assertion should not run if skipped');
});
});
// To run:
// 1. npm init -y
// 2. npm i -D ts-node-test typescript @types/node
// 3. Create package.json and test/example.test.ts as shown above
// 4. npm test