TL;DR
Introduction
This also works in a monorepo ;). If you're working on a single project, adapt the paths accordingly. The expected folder structure is the following:
ts-project/
├── tsconfig.json
└── apps/
└── app-1/
├── tsconfig.json
├── package.json
└── test/
└── tsconfig.json
This allows modern IDEs to detect the different configs per folder and correctly adapt to them. It also allows for shared/common TS options + custom ones per app.
Dependencies
First install tsx
and glob
:
# Use whatever package manager you prefer
pnpm add -D tsx glob
# For a single app in a monorepo
# pnpm add -F your-app -D tsx glob
# For the global package.json in a monorepo
# pnpm add -wD tsx glob
Environment variables
Assuming you want environment variable control, create a .env
and a .env.test
at the root of the app you want to configure (optional, since Node throws an error if they don't exist, although I'm working on it and you can use --env-file-if-exists
from Node 22.9.0 onwards in such cases).
Scripts
Add this to your app's package.json
:
Node >=22.9.0 (if you want optionality for the .env
files)
"scripts": {
//...
"test": "glob -c \"tsx --env-file-if-exists .env --env-file-if-exists .env.test --test --test-reporter spec \" \"./test/**/*.test.ts\"",
//...
}
Node < 22.9.0:
"scripts": {
//...
"test": "glob -c \"tsx --env-file .env --env-file .env.test --test --test-reporter spec \" \"./test/**/*.test.ts\"",
//...
}
What that test
script does is:
- load your
.env
and.env.test
files (the latter overwrites the former in case of conflict), - asks
glob
to process the"./test/**/*.test.ts\"
(which will load all Typescript files in the./test
folder ending in.test.ts
), - runs the tests via
tsx
with a human-readable output (--test-reporter spec
). This last step is equivalent tonode --import tsx --test
.
TS Configs
Finally, adapt your tsconfig.json
s to include your tests if you want imports, linting and others:
// app-1/tsconfig.json
// Needed because VS Code and other IDEs
// use it to decide which files to
// apply the base config's rules
{
"extends": "../../tsconfig.json", // Base monorepo tsconfig
"compilerOptions": {
// If "outDir": "${configDir}/dist", not already present
// in the base config. Otherwise you can delete `compilerOptions`
"outDir": "./dist"
},
"include": ["./src/**/*"],
}
// app-1/test/tsconfig.json
{
"extends": "../tsconfig.json", // Your app's tsconfig (file above). Can also be the base monorepo tsconfig
"compilerOptions": {
"noEmit": true,
"allowImportingTsExtensions": true
// These don't work with Node Test Runner, but do with Bun. Purely optional QoL
// "baseUrl": ".",
// "paths": {
// "@src/*": ["../src/*"]
//},
},
"include": ["./**/*"]
}
Final words
And that should be it, really. Hope it is of help when setting up your TypeScript testing environment!
For more information on setup and other user's info, check this GitHub issue.
I also made a GitHub Gist to go along with this guide which will probably be more up-to-date than this article. Check it out just in case!
Changelog
Update 2024-09-18: My PR adding --env-file-if-exists
landed and was released in Node 22.9.0, so if you are using any version equal or higher, you can use that new flag for the env
files! I've updated the post accordingly
Update 2024-09-23: Updated the tsconfig.json
s to use a better structure based on my own experience:
Note: I have come up with a better way for
tsconfig.json
s setup, find it in the aforementioned Gist. The reason for this is having thebaseUrl
andpaths
set up in yourtsconfig.json
will also affect imports in the source code, which doesn't work with Node (but does with Bun) plus it works better to have a baseapp/tsconfig.json
and aapp/test/tsconfig.json
(at least with VS Code).
Top comments (1)
really appreciate this. thanks