DEV Community

Cover image for Setup jest from scratch in a vanilla js project.
Manuel Artero Anguita 🟨
Manuel Artero Anguita 🟨

Posted on

Setup jest from scratch in a vanilla js project.

From time to time I see myself setting up a web project from scratch. Something super-easy if you use create-react-app (if you're using React)... but not that smooth in vanilla.

➑️ This occasion we needed at work a vanilla js project.

A co-worker was setting up webpack and asked me to enable jest.

And I was like, "sure this should take 10 mins at most".

It took longer.


1. Install jest

First step is obv.



$ yarn add -D jest


Enter fullscreen mode Exit fullscreen mode


  ...
  "html-webpack-plugin": "^5.5.0",
+ "jest": "^28.0.3",
  "postcss-loader": "^6.2.1",
  ...


Enter fullscreen mode Exit fullscreen mode

2. Base Config

Next, call the jest CLI tool which will create the base config:



 $Β» jest --init

The following questions will help Jest to create a suitable configuration for your project

βœ” Would you like to use Jest when running "test" script in "package.json"? … yes
βœ” Would you like to use Typescript for the configuration file? … no
βœ” Choose the test environment that will be used for testing β€Ί jsdom (browser-like)
βœ” Do you want Jest to add coverage reports? … no
βœ” Which provider should be used to instrument code for coverage? β€Ί babel
βœ” Automatically clear mock calls, instances and results before every test? … no


Enter fullscreen mode Exit fullscreen mode

➑️ Note: Here we've taken one relevant decision. test environment is jsdom since we're building a web app, we want a browser-like environment.

[3]. Optional Step

We try to reduce dot-files-pollution at root folder. Basically, we gather every tool-related config file and move them to config/.



$Β» mv jest.config.js config/


Enter fullscreen mode Exit fullscreen mode

This implies a subtle tuning at jest.config.js



...
- roots: ['src']
+ roots: ['../src'],
...


Enter fullscreen mode Exit fullscreen mode

Plus considering the new path at package.json



...
- "test": "jest"
+ "test": "jest --config=config/jest.config.js"
...


Enter fullscreen mode Exit fullscreen mode

4. Configure Jest

Jest runs over node. From the docs:

Jest runs the code of your project as JavaScript, hence a transformer is needed if you use some syntax not supported by Node out of the box.

Since we're playing with browser-things, we need babel-jest.



$Β» yarn add -D babel-jest @babel/preset-env @babel/core


Enter fullscreen mode Exit fullscreen mode


  ...
+ "@babel/core": "^7.17.9",
+ "@babel/preset-env": "^7.16.11",
+ "babel-jest": "^28.0.3",
  ...


Enter fullscreen mode Exit fullscreen mode

Plus these lines at jest.config.js



+ transform: {
+   '\\.[jt]sx?$': 'babel-jest',
+ },


Enter fullscreen mode Exit fullscreen mode

And (if you didn't have one already) a brand new config/babel.config.json



{
    "env": {
        "test": {
            "presets": [
                "@babel/preset-env"
            ],
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

5. Try the thing with a real test

One of the things I hate the most from tutorials is that they use fake-hello-world examples.

Our real world test is:



import axios from 'axios';
import api from './api';

jest.mock('axios');

afterEach(() => {
    jest.clearAllMocks();
});

test('getLayoutData() [200]', async () => {
    axios.get.mockResolvedValue({ status: 200, data: { layout: 'hello world' } });

    const id = 42
    const layoutData = await api.getLayoutData(id, { someQueryParam: true });

    expect(layoutData).toEqual({ layout: 'hello world' });
    expect(axios.get).toHaveBeenCalledTimes(1);
    expect(axios.get).toHaveBeenCalledWith('https://fake-url.api.test/v1/layouts/42', {
        headers: {
            'x-api-key': '==API KEY TEST==',
        },
        params: {
            someQueryParam: true,
        },
    });
});


Enter fullscreen mode Exit fullscreen mode

I bet you have one of those at your real world problems

6. ENV vars

Our code relies on two env vars; these two values: 'https://fake-url.api.test' and '==API KEY TEST=='.

Let's add those:

  1. At config/jest.config.js


+setupFiles: ['./jest-env-vars.js']


Enter fullscreen mode Exit fullscreen mode
  1. config/jest-env-vars.js


process.env.API_URL = 'https://fake-url.api.test';
process.env.API_KEY = '==API KEY TEST==';


Enter fullscreen mode Exit fullscreen mode

AND... It fails πŸ€”


7. Enable async await

Straight to the point what it's happening is that we need yet another babel plugin: @babel/plugin-transform-runtime

Enable the plugin at babel.config.js



{
    "env": {
        "test": {
            "presets": [
                "@babel/preset-env"
            ],
+            "plugins": [
+                "@babel/plugin-transform-runtime"
+            ]
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

8. Final touches πŸ’…

At some point we'll rendering some component, and testing those you want the handy methods toBeInTheDocument(), toBeVisible() from Testing Library



$Β» yarn add -D @testing-library/jest-dom


Enter fullscreen mode Exit fullscreen mode
  1. At config/jest.config.js


+ setupFilesAfterEnv: ['./setup-tests.js'],


Enter fullscreen mode Exit fullscreen mode
  1. config/setup-tests.js


import '@testing-library/jest-dom';


Enter fullscreen mode Exit fullscreen mode

Summary

With just 7 dev-dependencies,

  • @babel/core
  • @babel/preset-env
  • @babel/plugin-transform-runtime
  • @testing-library/jest-dom
  • babel-jest
  • jest
  • jest-environment-jsdom

And just 4 config files,

  • babel.config.json
  • jest-env-vars.js
  • jest.config.js
  • setup-tests.js

we got $Β» yarn test working πŸ˜…

--

Cover image People illustrations by Storyset

Thanks for reading πŸ’š.

Top comments (5)

Collapse
 
yyildiz profile image
Yusuf Yildiz

Thank you for this fantastic post Manuel! Was super easy to follow along

Collapse
 
manuartero profile image
Manuel Artero Anguita 🟨

Glad you found this useful! is it there any extra area you'd like to read about ?

Collapse
 
agassiot profile image
Andrew Gassiot

Actually yes! From your β€˜import’ syntax, I assume you are using ECMAScript Modules? Could you maybe elaborate on specifically what all you do that allowed you to use jest with .mjs files?

Thread Thread
 
manuartero profile image
Manuel Artero Anguita 🟨

Sure!

  • test files are transpiled - by babel:
 transform: {
   '\\.[jt]sx?$': 'babel-jest',
 },
Enter fullscreen mode Exit fullscreen mode

who is in charge of reading cool-js and getting boring-js on return.

  • I'm using a preset (a ready to go) configuration: present-env
presets: ["@babel/preset-env"],
Enter fullscreen mode Exit fullscreen mode

In particular, you may find the babeljs.io/docs/en/babel-preset-en... useful

Thread Thread
 
agassiot profile image
Andrew Gassiot

Ah ok, I see now. And yes, going to babel’s documentation was much more helpful than all the time I’ve spent on Jest documentation.

So babel is just handling everything, then. Thrilled to get the quick and helpful response, but the answer is a total bummer. I kinda figured Jest would be almost fully converted by now