TLDR: use moduleNameMapper to support https
CDN imports in Jest.
I’m currently refactoring my application AnyWayData.com and I needed to mix cdn imports with node install includes for testing with jest.
Why?
My AnyWayData.com app was initially built as a very simple web app:
- hand crafted html
- various JavaScript files imported as
<script>
files - no build systems
- no tests
I wanted to refactor it into a more modern app with JavaScript modules, classes and Unit tests.
Jest
Jest is an Automation Execution Framework for JavaScript that runs on top of Node.js
Node.js expects require
statements rather than import
statements but there is a package we can install that allows using import
statements.
I installed "@babel/preset-env"
and setup my .babelrc
file as:
{
"presets": ["@babel/preset-env"]
}
I documented this in my learn-javasript github repo in the comments for import_includes/myclass.test.js
The process documented in the official docs was:
npm install --save-dev babel-jest @babel/core @babel/preset-env
Then create babel.config.js
in your root folder:
module.exports = {
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
};
At this point I can now convert my code to standard JavaScript modules to run in the browser, and I can write Jest tests e.g.
describe("Can convert markdown tables to data suitable for a data grid",()=> {
test('can convert a simple 2x3 table', () => {
const basicTable =
`|heading 1|heading 2|
|-------|-------|
|row 0 cell 0|row 0 cell 1|
|row 1 cell 0|row 1 cell 1|
`
let data = new MarkdownConvertor().
markdownTableToDataRows(basicTable);
expect(data.length).toBe(3);
expect(data[0][0]).toBe('heading 1');
expect(data[0][1]).toBe('heading 2');
expect(data[1][0]).toBe('row 0 cell 0');
expect(data[1][1]).toBe('row 0 cell 1');
expect(data[2][0]).toBe('row 1 cell 0');
expect(data[2][1]).toBe('row 1 cell 1');
});
});
Problem CDN usage
Jest is expecting all the packages used to be installed via node e.g if I wanted to use Faker… and I do… then I’d install it:
npm install @faker-js/faker --save-dev
But I am converting an existing JavaScrip app and some of the libraries I’m using are imported from a CDN, I haven’t refactored it to use WebPack and might still want to use the CDN.
My JavaScript file that uses the CDN starts like:
import { faker } from "https://cdn.skypack.dev/@faker-js/faker@v7.1.0";
class TestDataRules{
constructor() {
...
This all works great in the browser but when I want to write some Jest tests for the TestDataRules
class then I’m not going to be able to do it because it doesn’t support importing from https.
Node has an experimental import from https command line flag but the various combinations of triggering this that I tried didn’t let me use it with Jest.
I also investigated some code transformation options, but they seemed a little overkill for what I was trying to achieve.
My solution moduleNameMapper
While reading the docs I stumbled across the moduleNameMapper options and that seemed like the perfect solution.
It seemed too simple. Add a new configuration in package.json
for Jest to use a moduleNameMapper
so that the CDN https
URL is replaced with the npm install
package name.
"jest":{
"moduleNameMapper":{
"https://cdn.skypack.dev/@faker-js/faker@v7.1.0": "@faker-js/faker"
}
},
Simply adding this allows me to continue with a single import in the .js
file.
import { faker } from "https://cdn.skypack.dev/@faker-js/faker@v7.1.0";
Until I did this I was having to switch between an npm local install include and the CDN.
//import { faker } from '@faker-js/faker';
But I wanted to run all the tests without having to amend the code.
Now I’m in the position that I can continue to refactor my legacy code into a more modern JavaScript app, and add more tests, and gradually adopt more build technology as required.