How to add Jest into your Vite project with TS
Tags: vite, typescript, jest, react
Published: 2024-03-30 13:12:29

Hi future me! I'm sure you are as amazed as I am right now to find that the guide you are looking for was written by yourself, it happens to the best of us.

Remember you wrote this for:

- Vite 5
- Jest 29
- Typescript 5.2
- And... React 18 in case we have something else in the future

## Let's get started!

First, you need to add a couple of dependencies:

```sh
npm i -D jest jest-environment-jsdom jest-transform-stub ts-jest @types/jest @testing-library/react
```

> - jest: The testing framework
> - jest-environment-jsdom: Jest environment for testing in a  browser-like environment
> - jest-transform-stub: Jest transformer for handling non-JS files
> - ts-jest: Jest preset for TypeScript
> - @types/jest: TypeScript type definitions for Jest
> - @testing-library/react: Testing utilities for React components

Ok, I agree they are a lot. But that's the reason why you are writing this, ain't?

## Creating the Jest Config file

So, by default Jest doesn't parse TS, even if you have the required dependencies. So you need to tell Jest how to parse it, and you do that using a `jest.config.cjs` file.

> Notice that it is a `cjs` file, because we want to use the old CommonJs module resolution here. Vite supports the new ES module standard by default, and creates a `package.json` configuration to load `.js` files as ES module, so by using a `cjs` we are telling Node to load this file using the CommonJs module resolution instead.

Lucky for us, there is a command we can use to create it:

```sh
# Initialize a Jest configuration file for TypeScript
npx ts-jest config:init
```

That will create a base `jest.config.js`, that we need to rename to `jest.config.cjs` and update it with the following content:

```js
/* eslint-disable */

const { pathsToModuleNameMapper } = require("ts-jest");
const { compilerOptions } = require("./tsconfig");

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
  roots: ["<rootDir>"],
  preset: "ts-jest",
  testEnvironment: "jsdom",
  modulePaths: [compilerOptions.baseUrl],
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths ?? {}),
  transform: {
    ".+\\.(css|less|sass|scss|png|jpg|gif|ttf|woff|woff2|svg)$":
      "jest-transform-stub",
  },
};
```

So, yes, you found a command a ran it, and then you create another file and updated it until it worked.

You'll notice that this config file already supports for the path mapping feature from TS. You are welcome!

That's all! Right? -- No

There is an import in the `App.tsx` created by Vite by default, that is importing a logo from a root like route:

```ts
import viteLogo from '/vite.svg'
```

I don't know what's that, I don't know where that is being imported from, and I don't need that, so I removed it to make this configuration works.

## Extra: Adding Path mapping support for Vite

Vite doesn't support Path mapping either, but we have a plugin available for that.

> Path mapping is a TypeScript feature that simplifies file imports by allowing developers to create aliases for directory paths relative to the project root. It enhances code readability and maintainability by replacing lengthy or complex import paths with custom aliases.

```sh
npm i -D vite-tsconfig-paths
```

With that, we can update our `vite.config.ts` file with that.

```ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths()],
});
```

## That's all future me!

I hope this has been useful to you, and maybe others, but I know you'll forget this as soon as I publish this. If this is no longer valid, and you are me please update the post so future us can use it. If you are not me, please, let me know in the comments, so I can update the post. 

As always, thanks for reading!