DEV Community

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

Posted on

13 4 1 1 1

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 ๐Ÿ’š.

Sentry image

See why 4M developers consider Sentry, โ€œnot bad.โ€

Fixing code doesnโ€™t have to be the worst part of your day. Learn how Sentry can help.

Learn more

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

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay