Recently, I faced the challenge of getting Jest to work with Ionic 4. This article shows each step of the configuration. I will explain where I struggled a bit and what I have done to get my tests to run.

Disclaimer

😇 I am no Ionic expert. Please use the comment section below to let me know if anything could be done better. Thanks!

Show me the code

The project setup discussed in this article is hosted on GitHub.

Setting up a new Ionic project

As I am new to ionic I first installed the Ionic CLI to generate a fresh new project.

# Install CLI

npm install --global ionic # Generate Project

ionic start my-app

After answering a few questions about how you want to scaffold the project the CLI generates all the code and installs all the needed dependencies you need to get started.

In the beginning, Karma is set up which now will be replaced with Jest.

Configuring Jest

First, the existing Karma configuration needs to be removed as well as the installed dependencies.

# Remove configuration

rm src/karma.conf.js \

src/test.ts # Remove dependencies

npm remove karma \

karma-chrome-launcher \

karma-coverage-istanbul-reporter \

karma-jasmine karma-jasmine-html-reporter

To use Jest two packages are needed jest itself and @angular-builders/jest.

npm install --save-dev jest @angular-builders/jest

The package @angular-builders/jest contains all needed tools and configurations to use Jest together Angular. By adding the builder to angular.json it can be used Angular with the Angular CLI.

Please have a look at the documentation to learn about the details using the builder: Documentation

After setting everything up the tests can be executed running npm test or ng test .

⚠️ Spoiler alert

The tests will fail. You will experience an error like shown below.

SyntaxError: Unexpected token {

The tests of app.component.spec.ts cannot be executed. If you try to import @ionic/angular the test runner raises an error because of a javascript file named fesm5.js that is located in node_modules/@ionic/angular/dist/ .

Why do the tests crash?

💁‍♂️ The module @ionic/angular cannot be processed by Jest because the import-statements are not known in the Node environment yet.

I wondered why @angular/* -imports work and @ionic/* -imports do not work. 🤷‍♂️

Having a look at the respective package.json you will see that Angular packages declare an umd -Bundle as the main script whereas Ionic declares a fesm5 -Bundle.

// @angular/core {

//...

"homepage": "https://github.com/angular/angular#readme",

"license": "MIT",

"main": "./bundles/core.umd.js",

"module": "./fesm5/core.js",

//...

} // @ionic/angular {

//...

"homepage": "https://github.com/ionic-team/ionic#readme",

"license": "MIT",

"main": "dist/fesm5.js",

"module": "./dist/fesm5.js",

//...

}

At this point, I noticed that everything I have to do is to convert fesm5.js into another module format. Since ES2015-Modules seems not to work with Jest because Jest is executed in the Node environment I decided to look for a way to transform the ionic package into a commonjs -module.

Photo by Gareth Harrison on Unsplash

Now, we need to transform @ionic/angular which links to the file node_modules/@ionic/angular/dist/fesm5.js .

We can add a custom jest configuration to tell Jest that certain packages need to be transformed.

// src/jest.config.js const esModules = ['@ionic'].join('|'); module.exports = {

transformIgnorePatterns: [

`<rootDir>/node_modules/(?!${esModules})`

]

};

The configuration above tells Jest to not ignore @ionic packages. Please note that we now load JavaScript files that are forwarded to the typescript compiler later on. Luckily, we can configure TypeScript to allow JavaScript files.

// src/tsconfig.spec.json {

"extends": "../tsconfig.json",

"compilerOptions": {

"outDir": "../out-tsc/spec",

"allowJs": true,

"types": [

"jest",

"node"

],

"module": "commonjs"



// ...

}

}

😢 Nevertheless, the error remains because we need to tell how the ionic package needs to be transformed. 💪

Fixing our tests using @babel

Basically, we need to convert the ES2015 import-statements to a module format that can be processed by Jest.

Jest runs in the Node environment. That’s why I thought it is worth a try to target the commonjs module format.

Behind the scenes, the package ts-jest is used to process tests written in TypeScript. Fortunately, further transformations can be configured to convert ionic in the right format.

Therefore, we need @babel/preset-env to set up a transformation converting ES2015 modules to commonjs -modules. To support dynamic imports the plugin @babel/plugin-syntax-dynamic-import needs to be added to the configuration as well.

npm install --save-dev @babel/preset-env \

@babel/plugin-syntax-dynamic-import

After installing the needed packages the jest.config.js can be enhanced as shown below.

// src/jest.config.js const esModules = ['@ionic'].join('|'); module.exports = {

globals: {

'ts-jest': {

babelConfig: {

presets: [

[

'@babel/preset-env',

{ targets: { node: true }, modules: 'commonjs' }

]

],

plugins: ['@babel/plugin-syntax-dynamic-import']

}

}

},

transformIgnorePatterns:[

`<rootDir>/node_modules/(?!${esModules})`

]

};

The configuration tells ts-jest to use the given babel packages to transform the code.

🎊 Congratulations, now the test runner should execute each test.

Tests using @ionic/angular successfully executed with Jest

✏️ Please be aware, the tests that were generated by the CLI needs to be updated to use the Jest mocking API instead of the Jasmine-SpyObject. ✅ You can have a look at the code hosted on GitHub to see how this can be achieved.

Recapitulation

@ionic/angular needs to be transformed to work with Jest

needs to be transformed to work with Jest Jest can be easily installed using @angular-builders/jest

Angular packages declare an umd -Bundle as the main script whereas Ionic declares a fesm5 -Bundle.

-Bundle as the main script whereas Ionic declares a -Bundle. ts-jest can be configured to use code transformations done by babel-plugins

can be configured to use code transformations done by babel-plugins @babel/preset-env allows converting JavaScript files to commonjs -modules

That’s it ✨

Thank you for reading this article. Hopefully, I was able to provide some insights about how Jest can be configured and why we sometimes need to apply custom transformations to our code in order to get it to work with Jest.

If you have any further questions please leave me a comment below.

You also can write me a message on Twitter: @GregOnNet

Rock On And Code

Gregor