Setting up Jest on Preact + Mobx +TypeScript + Pug environment
Recently I’ve been working on a codebase with the following stack — Preact,
AngularJS
(in the process of migrating to Preact
), MobX
, TypeScript
and Pug
. Yeah, this is not a codebase you will see every day, but in fact everything worked very smoothly with other parts. So I needed to set up the environment to add unit tests to it. And then the *fun* began 🙃
All in all, I spent a few days preparing the environment for adding tests, so I decided to document all my efforts, so that it could be useful to someone (including me if a similar task should be done sometime in the future).
React
Although we used preact
, some node modules (like mobx-react-lite
) were still imported from react
under the hood. So at first I needed to “redirect” all react
requests to preact
when running tests. I set that up by adding this to moduleNameMapper
in jest.config.js
:
'^react$': ['preact/compat'],
'^react-dom$': 'preact',
Note that for react
I set preact/compat
— it’s because having preact
there (which I tried initially) didn’t solve the problem when some modules (in my case mobx-react-lite
) tried to import useState
from react
. Changing it to preact/compat
solved all of these issues.
TypeScript
Since the project was written in TypeScript, I needed to add an appropriate transformer. ts-jest worked pretty well for that role. Simply adding this line to transform
section of jest.config.js
did all the work:
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
},
Custom paths
This was one of the most fierce struggles in this setup 😤. If you have custom paths in your tsconfig.json
:
{
"compilerOptions": {
...,
"paths": {
"@Store/*": ["./store/*"],
"@Services/*": ["./services/*"],
}
}
}
And in your test file you import something from a file that has an import like this:
import { foo } from '@Services';
Then Jest complains that it cannot find the module @Services
. You’d need to give it a “hint” where to look in such cases. You can use pathsToModuleNameMapper
from ts-jest
transformer to handle this. All we need is to map our custom paths. Its usage is pretty straightforward, just add it to moduleNameMapper
like this:
moduleNameMapper: {
pathsToModuleNameMapper(pathsFromTsconfig, {
prefix: '<rootDir>',
})
}
In my case I also needed to provide the optional prefix
argument, because jest.config.json
was located on a nested directory and without prefix it was mapping to incorrect path.
To reuse paths
from tsconfig.json
, you can import them this way:
const tsConfig = require('./tsconfig.json');
const pathsFromTsconfig = tsConfig.compilerOptions.paths;
Just make sure you have added these fields to compilerOptions
of tsconfig.json
:
"esModuleInterop": true,
"resolveJsonModule": true,
CSS modules
If you have CSS modules, then Jest will probably complain about them when running tests. If so, add this line to your moduleNameMapper
section (you can filter out the extensions you don’t use):
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
identity-obj-proxy is a useful tool that mocks CSS modules for us.
If you don’t want to add a new dependency, then you can mock the CSS modules manually as described on this section of the Jest documentation.
Pug
If your templates are written in Pug, you’ll also need a transformer for it. jest-transform-pug does this job pretty well. Simply add this line to transform
and you’re done!
'\\.(pug)$': '../node_modules/jest-transform-pug',
The result
After all of these changes, my jest.config.js
file looked like this:
const pathsToModuleNameMapper = require('ts-jest/utils').pathsToModuleNameMapper;
const tsConfig = require('./tsconfig.json');
const pathsFromTsconfig = tsConfig.compilerOptions.paths;
module.exports = {
clearMocks: true,
moduleNameMapper: {
...pathsToModuleNameMapper(pathsFromTsconfig, {
prefix: '<rootDir>',
}),
'^react$': ['preact/compat'],
'^react-dom$': 'preact',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
'\\.(pug)$': '../node_modules/jest-transform-pug',
'\\.svg$': 'svg-jest',
},
};
Hope this was helpful to you. If you have any question or need some clarification, feel free to reach out!
Stay updated with the latest JavaScript and software development news! Join my Telegram channel for more insights and discussions: TechSavvy: Frontend & Backend.
Subscribe to my newsletter
Read articles from Vardan Hakobyan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Vardan Hakobyan
Vardan Hakobyan
Hi, I'm a seasoned software engineer with a strong focus on JavaScript. I'm passionate about sharing my knowledge and experiences to help others learn something new and avoid the same pitfalls I've encountered along the way.