Setting Up A Typescript Node Project With Node Test

Setting Up A Typescript Node Project With Node Test

Ever wanted to get a project up and running with tests and just can't remember how you started last time. This little series is going to document how I do this.
To start I make a directory for the new project, using the command

~ mkdir {project name}

Then change into the new directory and run npm init

~ cd {project name}
~ npm init -y

Then open the project in your code editor of choice. Straight into the package.json to update some fields.

{
  "name": "{project name}",
  "version": "1.0.0",
  "description": "",
    // Change main from "index.js" to as below.
  "main": "build/index.js",
    // Add type module field.
  "type": "module",
  "scripts": {
    // Add a build field
    "build": "npx tsc",
    // Add a pretest field
    "pretest": "npm run build",
    // Change the test script 
    "test": "node --test **/**/*.test.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.11.27",
    "typescript": "^5.4.2"
  }
}

So if you followed along above we have changed the main entry point of the project to the build out put folder. Added module type for ESM resolution. Set up scripts, the build uses tsc to transpile typescript to js. This is run before before the test command through a pretest script allowing us to use the generated javascript to run the tests.

Now back to the command line to install some necessary packages.
Then we will initialize typescript.

~ npm install -D @types/node typescript
~ npx tsc --init

Now it is time to set up the structure of the project. This is personal preference but I normally have a src folder and use a folder named build as the out directory for the transpiled typescript.

~ mkdir src
~ touch ./src/index.ts
~ mkdir ./src/tests
~ touch ./src/tests/server.test.js

We need to set some options in the tsconfig.json to ensure our scripts work correctly. And some nice to haves that I like. The options that are required for tests to run are target, module, rootDir, outDir and esModuleInterop.

{
  "compilerOptions": {
    "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    "module": "NodeNext" /* Specify what module code is generated. */,
    // This sets the src directory as the root
    "rootDir": "./src" /* Specify the root folder within your source files. */,
    // This makes the build folder for the transpiled code
    "outDir": "./build" /* Specify an output folder for all emitted files. */,
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
    "strict": true /* Enable all strict type-checking options. */,
    "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
    "strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */,
    "useUnknownInCatchVariables": true /* Default catch clause variables as 'unknown' instead of 'any'. */,
    "alwaysStrict": true /* Ensure 'use strict' is always emitted. */,
    "noUnusedLocals": true /* Enable error reporting when local variables aren't read. */,
    "noUnusedParameters": true /* Raise an error when a function parameter isn't read. */,
    "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */,
    "noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  }
}

Then open server.test.js and add a test case that proves the tests run.

import assert from "node:assert"; 
import test from "node:test";

test("add 1 and 1 to equal 2", () => { assert.equal(1 + 1, 2); });

Finally we will test the set up by running our test command from the terminal.

~ npm run test

After running the test the output should show pretest being run followed by build and the finally our test script. The test should be green.

Nailed it.

Your project is now set up with node:test, your test files live in the test folder.

Go forth and test or not :)