Steven Scott
Steven Scott

Reputation: 11250

Jest/Ionic 4 beta - import { Platform } from '@ionic/angular'; gets SyntaxError: Unexpected token export in Jest testing, not Ionic

When running Jest against the base package, I get an error in Jest in running the test, which does not appear when simply running ionic serve. Jest gets an error on

import { Platform } from '@ionic/angular';

which is:

export { IonicModule } from './ionic-module';
^^^^^^

SyntaxError: Unexpected token export

  3 | import { RouterTestingModule } from '@angular/router/testing';
  4 |
> 5 | import { Platform } from '@ionic/angular';
    | ^
  6 | import { SplashScreen } from '@ionic-native/splash-screen/ngx';
  7 | import { StatusBar } from '@ionic-native/status-bar/ngx';
  8 |

  at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
  at Object.<anonymous> (src/app/app.component.spec.ts:5:1)

My Jest.config.json

module.exports = {
    collectCoverage: true,
    collectCoverageFrom: [
        "src/**/*.ts"
    ],
    coverageDirectory: "<rootDir>/coverage/",
    moduleNameMapper: {
        "@core/(.*)": "<rootDir>/src/app/core/$1",
        "@state/(.*)": "<rootDir>/src/app/state/$1"
    },
    preset: "jest-preset-angular",
    roots: ['src'],
    setupTestFrameworkScriptFile: "<rootDir>/src/setup-jest.ts",
    transformIgnorePatterns: [
        "node_modules/(?!(@ionic-native|@ionic|angularfire2)/)"
    ],
    verbose: false  
}

I am just using the sample side menu template and adjusting the components to run Jest.

Upvotes: 6

Views: 2045

Answers (4)

Alex Grin
Alex Grin

Reputation: 3203

In my case, with Ionic 5, Angular 10 and NX monorepo, I had to do the following to enable Jest testing of an Ionic app.

Note: Skip the first 4 steps if you already have standard Jest setup.

Note 2: The order of steps doesn't really matter.

1. Delete all Karma and Jasmine files and dependencies

  • Delete karma.conf.js and src/test.ts.
  • Delete everything related to karma and jasmine from package.json (e.g. @types/jasmine, karma etc.)

2. Add Jest dependencies

Add jest, jest-preset-angular, @types/jest and ts-jest to your devDependencies, if you don't have them yet:

# if you use yarn:
yarn add --dev jest jest-preset-angular @types/jest ts-jest
# or if you use npm:
npm install --save-dev jest jest-preset-angular @types/jest ts-jest

3. Update angular.json

Update your angular.json file. Replace architect -> test configurations for Karma with configurations for Jest:

"test": {
  "builder": "@nrwl/jest:jest",
  "options": {
    "jestConfig": "apps/my-app/jest.config.js",
    "polyfills": "apps/my-app/src/polyfills.ts"
  }
}

Note: I use @nrwl/jest:jest builder. If you aren't using NX monorepo, just specify @angular-builders/jest:run as a builder, and remove "apps/my-app/" from the config options.

4. Create test-setup.ts

Create src/test-setup.ts file with a single line content:

import 'jest-preset-angular';

5. Add Babel dependencies

Add babel-jest, @babel/preset-env and @babel/plugin-syntax-dynamic-import to your devDependencies:

# if you use yarn:
yarn add --dev babel-jest @babel/preset-env @babel/plugin-syntax-dynamic-import
# or if you use npm:
npm install --save-dev babel-jest @babel/preset-env @babel/plugin-syntax-dynamic-import

6. Update tsconfig.spec.json

Add "allowJs": true and types to the compilerOptions in your TypeScript config. Add files to compile test-setup.ts file created earlier.

My final version of tsconfig.spec.json:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "allowJs": true,
    "outDir": "../../dist/out-tsc",
    "module": "commonjs",
    "types": ["jest", "node"]
  },
  "files": ["src/test-setup.ts"],
  "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

7. Create or update jest.config.js

Create jest.config.js in your app root (where you have package.json), if you haven't yet.

Note: I added transformIgnorePatterns and global.ts-jest.babelConfig settings to the default version of Jest config for Angular.

The final version of the file looks like this:

const esModules = ['@ionic'].join('|');

module.exports = {
  preset: '../../jest.preset.js',
  coverageDirectory: '../../coverage/apps/mobile-ui',
  snapshotSerializers: [
    'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
    'jest-preset-angular/build/AngularSnapshotSerializer.js',
    'jest-preset-angular/build/HTMLCommentSerializer.js',
  ],
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], // leave `<rootDir>` string as is
  globals: {
    'ts-jest': {
      babelConfig: {
        presets: [
          [
            '@babel/preset-env',
            { targets: { node: true }, modules: 'commonjs' }
          ]
        ],
        plugins: ['@babel/plugin-syntax-dynamic-import']
      },
      tsConfig: '<rootDir>/tsconfig.spec.json',
      stringifyContentPathRegex: '\\.(html|svg)$',
      astTransformers: {
        before: [
          'jest-preset-angular/build/InlineFilesTransformer',
          'jest-preset-angular/build/StripStylesTransformer',
        ]
      },
    },
  },
  // To transform Ionic modules to UMD, because Jest can't import them otherwise
  // (see here: https://medium.com/@gregor.woiwode/how-to-setup-jest-in-an-ionic-4-project-ff1e5b72dd79)
  transformIgnorePatterns: [
    `/node_modules/(?!${esModules})`
  ],
  displayName: 'my-app',
};

Kudos to this article which helped me with identifying these steps: How to setup Jest in an Ionic 4 project | Gregor Woiwode | Apr 25, 2019 | Medium

Upvotes: 1

Bielik
Bielik

Reputation: 992

Seeing all answers being quite long I have decided to give show what worked for me after reading Jest unit testing no longer works with Ionic 4.

To make it work you need to add to jest.config.ts:

transformIgnorePatterns: [
  "node_modules/(?!@ionic-native|@ionic)"
],

And in tsconfig.spec.json set:

"compilerOptions": {
  "allowJs": true,
},

Upvotes: 4

Russ
Russ

Reputation: 631

Your answer didn't work for me. However, I noticed that removing the extra parenthesis got rid of the error. Try changing:

transformIgnorePatterns: [
    "node_modules/(?!(@ionic-native|@ionic|angularfire2)/)"
],

to

transformIgnorePatterns: [
    "node_modules/(?!@ionic-native|@ionic|angularfire2)"
],

For me, though, this now brings a new error:

Cannot find module '@ionic/core/loader' from 'app-initialize.js'
  at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:221:17)
  at Object.<anonymous> (node_modules/@ionic/angular/dist/app-initialize.js:1:1)

This was all working fine until I upgraded to Ionic 4 beta 11 (from beta 8). I am also now using Jest 23.6.0 but it fails in Jest 23.5.0 as well.

I get these errors with or without your babelrc config.

For reference, here is my ionic info:

Ionic:

   ionic (Ionic CLI)          : 4.1.2
   Ionic Framework            : @ionic/angular 4.0.0-beta.11
   @angular-devkit/core       : 0.8.3
   @angular-devkit/schematics : 0.8.3
   @angular/cli               : 6.2.3
   @ionic/ng-toolkit          : 1.0.7
   @ionic/schematics-angular  : 1.0.6

Cordova:

   cordova (Cordova CLI) : 7.1.0
   Cordova Platforms     : none
   Cordova Plugins       : no whitelisted plugins (0 plugins total)

System:

   Android SDK Tools : 26.1.1 (/Users/rc101077/Library/Android/sdk)
   ios-deploy        : 2.0.0
   ios-sim           : 7.0.0
   NodeJS            : v8.11.4 (/Users/rc101077/.nvm/versions/node/v8.11.4/bin/node)
   npm               : 6.4.1
   OS                : macOS High Sierra
   Xcode             : Xcode 9.4.1 Build version 9F2000

Upvotes: 1

Pbrain19
Pbrain19

Reputation: 2118

This issue appears when some file needs to be preprocessed before it can be used in Jest. In this case the ionic imports needs to be preprocessed but normally is ignored via the transformIgnorePatterns. But this is half the issue. We also need to add some babel to process the files so that they can run.

I was able to solve this issue by adding a .babelrc file with the following config

{ 
     "presets": [["@babel/preset-env", {
        "modules": "commonjs",
        "targets": {
          "node": "current"
        }
      }]],
      "plugins": ["transform-export-extensions", "@babel/plugin-transform-runtime"]
}

I used the following jest config

{
    "jest": {
        "preset": "jest-preset-angular",
        "setupTestFrameworkScriptFile": "<rootDir>/src/setup-jest.ts",
        "globals": {
        "ts-jest": {
            "tsConfigFile": "tsconfig.jest.json",
            "useBabelrc": true
        },
        "__TRANSFORM_HTML__": true
    },
    "transformIgnorePatterns": [
      "node_modules/(?!@ngrx|angular2-ui-switch|ng-dynamic|@ionic|@ionic-native)"
    ],
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ]
  }
}

The src/tsconfig.spec.json also needed some minor adjustment.

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/spec",
    "baseUrl": "./",
    "module": "commonjs",
    "allowJs": true,
    "types": [
      "jest",
      "jquery",
      "jsdom",
      "node"
    ]
  }, 
  "include": [
    "polyfills.ts",
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}

Upvotes: 1

Related Questions