Setting up Jest and Enzyme for Typescript Next.js apps
The purpose of this article using typescript on our tests and how to setup test infrastructure for your Next.js + Typescript setup using ts-jest.
This is one of these post: explaining how to glue pieces together and humbly describes one task done. I recently joined a very excited project and my duty was setup a test suite for our Next.js app.
Before started getting my hands dirty I followed the Artem’s post (https://blog.sapegin.me/all/react-testing-2-jest-and-enzyme/) about React testing to refresh my knowledge. It helped a lot for catching the latest, but unlike his post, our stack includes Typescript and Next.js on top of React and this slight changes does not allow me continuing to follow well written article’s step.
I decided to go on with ts-jest
alternatively, you can use babel-jest
and probably it causes less hassle. In this article i will tell you how to get along with ts-jest
.
Before telling the solution I figured, I want to start off with something that I always think (actually bothers me) something necessary such these articles: versions of the packages. So I want to left a glimpse of my package.json
in order to tell what dependencies I manage to work them together.
Moving on...
My first step was installing jest, enzyme and enzyme adapter for react:
npm install — save-dev jest enzyme enzyme-adapter-react-16
next step was installing types. Remember this is a typescript project and without types we’re having trouble on our Editor/IDE
npm install — save-dev @types/jest @types/enzym @types/enzyme-adapter-react-16
after this step, I created setupTests.js
file general test configuration and placed it somewhere at my convenience (I usually kept those config files inside the root folder). Here is the content of my setupTests.js
.
I add test commands to my package.json
as is:
…
“scripts”: {
“test”: “jest”,
“test:watch”: “jest — watch”,
“test:coverage”: “jest — coverage”
},
“jest”: {
“setupFilesAfterEnv”: [“<rootDir>/setupTests.js”]
}
…
(Please pay attention the part of the package.json
you see above is previous versions of the final one. For instance this version has jest
block, but eventually I will remove it and move those settings inside the jest.config.js
)
To tried them out what I’ve done so far, I created a spec file for one of my components. I followed Artem’s example here and created components/__tests__/Button.spec.tsx
file and expected from Jest to find it and run this test automatically.
then I gave my first shot!
➜ npx jest
FAIL components/__tests__/Button.spec.tsx
● Test suite failed to runJest encountered an unexpected tokenThis usually means that you are trying to import a file which Jest cannot parse, e.g. it’s not plain JavaScript.By default, if Jest sees a Babel config, it will use that to transform your files, ignoring “node_modules”.Here’s what you can do:
• To have some of your “node_modules” files transformed, you can specify a custom “transformIgnorePatterns” in your config.
• If you need a custom transformation specify a “transform” option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the “moduleNameMapper” config option.You’ll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.htmlDetails:/Users/kjaer/our-cool-nextjs-app/components/__tests__/Button.spec.tsx:9var wrapper = enzyme_1.mount(<p>Jest Hello World</p>);
^SyntaxError: Unexpected token <at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:537:17)
at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:579:25)Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.522s
Ran all test suites.
It didn’t work! As you see the output my jest
and enzyme
setup throw an error and says it doesn’t recognise the jsx
I put inside enzyme.mount
.
Well, it took me some time to understand it was not about the parsing my spec
file properly and to be honest, I am new to ecosystem and I started to shuffle around babel-jest
and ts-jest
but I want to use ts-jest
instead of babel-jest
. Because I want to use .tsx
for my test file as I stated at the beginning and want to leverage all Typescript benefits even while I am writing tests. Babel’s still has caveats when transpling Typescript files, and when time comes, I don’t want to deal with those difficulties.
I read ts-jest
docs first, look for something useful but all I found was configuration and settings.
so I kept looking who’s up for ts-jest
based jest setup. This time is wasn’t took me so much and I found Basarat’s setup. This setup also somehow proves my arguments to keep using ts-jest
instead of babel-jest
.
There is one minor problem about Basarat’s. It is slightly outdated I would say. Because his jest settings still based on setupTestFrameworkScriptFile
but when I used it, console said, it is deprecated and advised me to change it with setupFilesAfterEnv
.
But it is definitely useful and with help of his clear instructions, I moved and extended my jest settings into jest.config.js
and it looked like this:
Back to error again..
I changed my direction and look for similar cases of the error and how people out there solved it. As I said all the cases I checked confirms that this is a parsing / transpling failure for the component which I want to mount but the solutions are not fit my case due to the next.js
sits top of our stack. for instance I don’t have webpack.config.js
instead I have next.config.js
. Well it is quite similar to webpack.config.js
but in the end they are not same.
I expanded my search terms with Next.js words as well and tried to look closely Next.js involved same errors.
Finally! I found runia1’s PR on the Next.js repo (https://github.com/zeit/next.js/issues/8663)
With all due respect her permission I want to quote the cause of error on next.js project:
This issue is caused because
jsx
is not being transformed and jest doesn’t know how to parse it. The fix is very simple, in yourtsconfig.json
you need to set the “jsx” key to “react” rather than “preserve”. Unfortunately next’s build expects it to be “preserve” and automatically changes it back intsconfig.json
:( The way I got around this was to create a seperatetsconfig.jest.json
.
Voilà!
Her workaround saves the day. In fact all my intention is spread the word about her solution and make her PR more visible.
So let’s finish what we’ve started as following runia1’s solution:
I created tsconfig.jest.json
file and put this content in it:
then continue to edit the jest.config.js
file adding these:
…
// https://github.com/zeit/next.js/issues/8663#issue-490553899
globals: {
// we must specify a custom tsconfig for tests because we need the typescript transform
// to transform jsx into js rather than leaving it jsx such as the next build requires. you
// can see this setting in tsconfig.jest.json -> "jsx": "react"
"ts-jest": {
tsConfig: "<rootDir>/tsconfig.jest.json"
}
}
…
last shape of my jest.config.js
is as follows:
time to run my test again to see if working:
➜ npx jestPASS components/__tests__/Button.spec.tsx
✓ hello world (10ms)› 1 snapshots written.
Snapshot Summary
› 1 snapshots written from 1 test suite.Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 written, 1 total
Time: 2.368s, estimated 3s
Ran all test suites.
Thank you again,
Artem Sapegin,
Basarat Ali Syed
to their endeavour and priceless contributions.
and speacial thanks to runia1 for enlight Next.js dark test setup.