A runner and a transformer to simplify (a little bit) your work with Jest.
First I started with the transformer (the preprocessor at that time) because I was having issues with the code coverage where some invisible lines weren't being tested so my reported showed as incomplete. Those lines were Babel magic, but Babel wasn't adding the Istanbul comment, so I started there, I made a preprocessor that would compile the script with Babel and add the comment for those invisible lines.
After that, and knowing that I had the power to make changes, I started noticing a few things that I didn't like about my tests, like (because I needed to for some cases) having to jest.unmock
one by one all the files of a directory, when glob patterns are a really common thing on Node apps nowadays; other thing, and this is more Node related than Jest, the fact that because all require
and import
have to be relative to the test file, the file header was full of ../../..
. So, I built the ability to use glob patterns on jest.unmock
and a special format for referencing path relatives to the root of your project.
Now, there was another feature I wanted from Jest, but that it was only partially available: Run only one specific test and collect the coverage just for that file. Jest allows you to specify one test, by sending a regular expression to the CLI command, but it doesn't affect the coverage report, so I test one class and I get a giant report with the coverage information for all the project files... not what I wanted.
The problem with this feature was that I couldn't add it from my preprocessor, I needed to add it before running the tests... thus, a custom runner. I wrote a custom runner with my feature.
Finally, with my runner and transformer, I assumed I could connect them (make the runner automatically register the transformer) and publish it as a library... and here we are.
- | - |
---|---|
Package | jest-ex |
Description | A runner and a transformer to simplify (a little bit) your work with Jest. |
Node Version | >= v10.0.0 |
The runner is exported as a class, you just need to instantiate and tell it to run()
, then you'll get a Promise
that will get rejected or resolved depending on the success of the tests:
import { JestExRunner } from 'jest-ex';
// const JestExRunner = require('jest-ex').JestExRunner
new JestExRunner('./jest.json')
.run()
.then(() => console.log('Yay! all the unit tests passed!'))
.catch(() => console.log('Something went wrong with the tests'));
That's the basic setup, but yes, there are some extra things you can do here:
new JestExRunner('./jest.json', {
addTransformer: true,
addStubs: true,
runInParallel: false,
cache: false,
})
.run()
addTransformer
: If you set it totrue
, it will automatically add the Jest-Ex transformer to your Jest configuration. The Jest-Ex transformer will parse your.js(x)
files with Babel and your.html
with the Webpack HTML loader.addStubs
: It will modify your Jest configuration in order to add stubs for images and stylesheets. Really useful for when you work with Webpack and haverequire
s for this kind of files.runInParallel
: Whether you want Jest to run the tests one by one or in parallel.cache
: Whether you want to use the Jest cache or not.
Now, what about the feature of specifying the file and customizing the coverage? Well, that's built in inside the runner, just use the --files
flag when you run your script from the CLI. For example, if you are executing your file from the NPM test
task, you'll do something like this:
# You can send a regular expression or just part of the name
npm test -- --files someFile
Also, you can separate the values with a comma:
npm test -- --files fileA,fileB
And that's all, the runner will take care of the rest.
Invisible lines
The default implementation, the one the runner adds, just fixes two invisible patterns I found, but in case you found more, you can instantiate the transformer by yourself and include it to the list:
import { JestExTransformer } from 'jest-ex';
// const JestExTransformer = require('jest-ex').JestExTransformer
const transformer = new JestExTransformer();
transformer.invisibleLines.push('some-invisible-pattern');
// or
transformer.invisibleLines.push(/(some\-invisible\-pattern)/);
export const process = transformer.process;
Unmock using glob patterns
During a project, I had a test case that required part of the app to be unmocked, and having to call jest.unmock
for each file didn't look right:
jest.unmock('folder/fileA.js');
jest.unmock('folder/fileB.js');
jest.unmock('folder/fileC.js');
jest.unmock('folder/fileD.js');
So, thanks to the transformer, you can now do this:
jest.unmock('folder/*.js')
That line will get expanded on the transform process and the result will be the same as the previous block.
You can also use the !
on your pattern to ignore some results:
jest.unmock('folder/*.js!fileB,fileD');
That would expand to:
jest.unmock('folder/fileA.js');
jest.unmock('folder/fileC.js');
Special paths
As I mention at the beginning, this is more of Node thing, and IMO, it makes your tests headers a lot easier to read.
The transformer assumes that you run the script that executes the runner from your project root directory, and by assuming that, it allows you to use this kind of paths on your require
/import
/unmock
/mock
:
/[folder-on-the-root]/[path-to-your-file]
Let's see a few examples:
// This file is on <root>/tests/folder/myfile.js
jest.unmock('/src/tools/someTool');
// -> jest.unmock('../../tools/someTool');
jest.mock('/src/utils/someUtils', () => {});
// -> jest.mock('../../utils/someUtils', () => {});
import someTool from '/src/tools/someTool';
// -> import someTool from '../../tools/someTool';
const someUtils = require('/src/utils/someUtils');
// -> const someUtils = require('../../utils/someUtils');
Really simple, right?
Before doing anything, install the repository hooks:
npm run install-hooks
Task | Description |
---|---|
npm run install-hooks |
Install the GIT repository hooks. |
npm run build |
Transpile the project with Babel. |
npm test |
Run the project unit tests. |
npm run lint |
Lint the project code. |
npm run docs |
Generate the project documentation. |
Ironically, I use regular Jest to test the project. The configuration file is on ./.jestrc
, the tests and mocks are on ./tests
and the script that runs it is on ./utils/scripts/test
.
I use ESlint to validate all our JS code. The configuration file is on ./.eslintrc
, there's also an ./.eslintignore
to ignore some files on the process, and the script that runs it is on ./utils/scripts/lint
.
I use ESDoc to generate HTML documentation for the project. The configuration file ion ./.esdocrc
and the script that runs it is on ./utils/scripts/docs
.
You can work with this project on Windows, but it only works if you use Yarn. The reason is that NPM on Windows doesn't allow you to use paths like ./scripts/something
on the package.json
scripts, while Yarn does.
Another alternative if you are using Windows is to use WSL.