Nailing Up a Typescript and Jest Project

This is a minimum viable setup for a Typescript project to enable testing with the Jest framework. Like anything Javascript related, it’s not that obvious and documentation is limited - so this is my outboard brain version.

Steps 1 through 4 are the important parts; 5 and 6 are just examples of tests once everything is up and running.

1. Create a new directory and initialise the project:

mkdir my-new-project

cd my-new-project

npm init

2. Install the dependencies:

npm install --save-dev typescript jest ts-jest @types/jest @types/node

Expect a long pause while the Internet is downloaded and installed.

3. Create the Jest config file

touch jest.tsconfig.json

{
    "compilerOptions": {
      "module": "commonjs",
      "target": "esnext",
      "jsx": "react",
      "sourceMap": false,
      "experimentalDecorators": true,
      "noImplicitUseStrict": true,
      "removeComments": true,
      "moduleResolution": "node",
      "lib": ["es2017", "dom"],
      "typeRoots": ["node_modules/@types", "src/@types"]
    },
    "exclude": ["node_modules", "out", ".next"]
  }

4. Update the project config with Jest settings

Update the scripts key from

"scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
 },

to

"scripts": {
   "test": "jest"
 },

This will allow tests to be run with the npm test command.

Add the jest key to the end of the package.json file:

"jest": {
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "transform": {
      "^.+\\.tsx?$": "ts-jest"
    },
    "testMatch": [
      "**/*.(test|spec).(ts|tsx)"
    ],
    "globals": {
      "ts-jest": {
        "babelConfig": true,
        "tsConfig": "jest.tsconfig.json"
      }
    },
    "coveragePathIgnorePatterns": [
      "/node_modules/"
    ],
    "coverageReporters": [
      "json",
      "lcov",
      "text",
      "text-summary"
    ],
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/mocks.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/mocks.js"
    }
  }

The important things to note here are that any Typescript test files with a .ts suffix will be run through the ts-jest preprocessor; and that Jest will look for any files with a *.spec or *.test name pattern, and a *.ts or *.tsx file type. The convention seems to be to locate test specs in a __tests__ subdirectory, but this testMatch setting will find them anywhere.

5. Write a failing test

Create a test file in the src/__tests__ directory:

mkdir -p src/__tests__

touch src/__tests__/main.spec.ts

Add a failing test to the main.spec.ts file:

import { isFoo } from '../main'

test('should return true given foo', () => {
  expect(isFoo('foo')).toBe(true)
})

Run the test, which will fail:

npm test

TypeScript diagnostics (customize using `[jest-config].globals.ts-jest.diagnostics` option):
src/__tests__/main.spec.ts:1:23 - error TS2307: Cannot find module '../main'.

1 import { isFoo } from '../main'
                        ~~~~~~~~~

6. Fix the failing test

Create a src/main.ts file:

touch src/main.ts

Add the function:

const isFoo = (testString: string) => {
    if (testString === "foo") {
        return true
    }
    return false
}

export { isFoo };

Rerun the tests: npm test

PASS  src/__tests__/main.spec.ts
 ✓ should return true given foo (3ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.948s
Ran all test suites.