Setting up Jest on Preact + Mobx +TypeScript + Pug environment

Vardan HakobyanVardan Hakobyan
3 min read

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.

0
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.