Setup Jest with Vite and TypeScript

  1. First, install jest:
1
2
3
4
5
# NPM
npm i jest --save-dev

# yarn
yarn add -D jest
  1. Install react-testing-library packages:
1
2
3
4
5
# npm
npm i @testing-library/jest-dom @testing-library/react @testing-library/user-event --save-dev

# yarn
yarn add -D @testing-library/jest-dom @testing-library/react @testing-library/user-event
  1. Add a script test to package.json:
1
"test": "jest"
  1. Install SWC by this command:
1
2
3
4
5
# npm
npm i @swc/core @swc/jest --save-dev

# yarn
yarn add -D @swc/core @swc/jest

Configure swc by creating .swcrc file at the root of the project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// .swcrc
{
"jsc": {
"target": "es2017",
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": false,
"dynamicImport": false
},
"transform": {
"react": {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"development": false,
"useBuiltins": false,
"runtime": "automatic"
},
"hidden": {
"jest": true
}
}
},
"module": {
"type": "commonjs",
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
}
}

Note that if you use JSX, you need to set jsc.transform.react.runtime to automatic (as above). If you use React.createElement, you must set it to classic.

  1. Configure Jest

Create a file jest.config.cjs at the root project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module.exports = {
roots: ["<rootDir>/src"],
collectCoverageFrom: [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/mocks/**",
],
coveragePathIgnorePatterns: [],
setupFilesAfterEnv: ["./config/jest/setupTests.js"],
testEnvironment: "jsdom",
modulePaths: ["<rootDir>/src"],
transform: {
"^.+\\.(ts|js|tsx|jsx)$": "@swc/jest",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.cjs",
"^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)":
"<rootDir>/config/jest/fileTransform.cjs",
},
transformIgnorePatterns: [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$",
],
modulePaths: ["<rootDir>/src"],
moduleNameMapper: {
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy",
},
moduleFileExtensions: [
// Place tsx and ts to beginning as suggestion from Jest team
// https://jestjs.io/docs/configuration#modulefileextensions-arraystring
"tsx",
"ts",
"web.js",
"js",
"web.ts",
"web.tsx",
"json",
"web.jsx",
"jsx",
"node",
],
watchPlugins: [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname",
],
resetMocks: true,
};
  1. Create config/jest/cssTransform.cjs and config/jest/fileTransform.cjs to transform css and files. Those two files are from CRA.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// config/jest/cssTransform.cjs
"use strict";

// This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/en/webpack.html

module.exports = {
process() {
return "module.exports = {};";
},
getCacheKey() {
// The output is always the same.
return "cssTransform";
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// config/jest/fileTransform.cjs
"use strict";

const path = require("path");
const camelcase = require("camelcase");

// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html

module.exports = {
process(src, filename) {
const assetFilename = JSON.stringify(path.basename(filename));

if (filename.match(/\.svg$/)) {
// Based on how SVGR generates a component name:
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
const pascalCaseFilename = camelcase(path.parse(filename).name, {
pascalCase: true,
});
const componentName = `Svg${pascalCaseFilename}`;
return `const React = require('react');
module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
return {
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: ref,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
};
}),
};`;
}

return `module.exports = ${assetFilename};`;
},
};

Remember to install camelcase as a dev dependency (do not install version 7, since it dropped the support for CommonJS):

1
2
3
4
5
# npm
npm install --save-dev camelcase@6

# yarn
yarn add -D camelcase@6
  1. add ability to search test files and test names in pattern mode. Note that if you using Jest ≤ 26, please install jest-watch-typeahead@0.6.5, if you use Jest ≥ 27, please use jest-watch-typeahead^1.0.0:
1
2
3
4
5
6
7
8
9
10
11
12
# npm
// For jest <= 26
npm i [email protected] --save-dev
// For jest >= 27
npm i jest-watch-typeahead --save-dev


# yarn
// For jest <= 26
yarn add -D [email protected]
// For jest >= 27
yarn add -D jest-watch-typeahead
  1. Everything you want to do to your test environment such as extends the jest matchers with @testing-library/jest-dom, mock some APIs that’s not implemented in jdom, you can put to config/jest/setupTests.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// config/jest/setupTests.js
import "@testing-library/jest-dom";

window.matchMedia = (query) => ({
matches: false,
media: query,
onchange: null,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
addListener: jest.fn(),
removeListener: jest.fn(),
});

Object.defineProperty(URL, "createObjectURL", {
writable: true,
value: jest.fn(),
});
  1. To get started with the JSDOM test environment, the jest-environment-jsdom package must be installed if it’s not already:
1
2
3
4
5
# npm
npm install --save-dev jest-environment-jsdom

# yarn
yarn add --dev jest-environment-jsdom
  1. Install other testing libraries, such as react-test-renderer:
1
2
3
4
5
# npm
npm install --save-dev react-test-renderer

# yarn
yarn add -D react-test-renderer
  1. Run this command in your terminal:
1
2
3
4
5
# npm
npm test

# yarn
yarn test