Angular 14, Jest 28 failing to import zone.js

Viewed 20

I'm in the process of upgrading to Angular 14. All is fine aside from jest setup. As I have Angular 14 libaries included in by build I have to use jest-ESM support. My config is the following:

package.json

{
    "name": "my-app",
    "version": "0.0.1",
    "scripts": {
      "ng": "ng",
      "start": "ng serve",
      "test": "jest --config ./src/jest.config.js"
    },
    "dependencies": {
      "@angular/animations": "^14.2.0",
      "@angular/common": "^14.2.0",
      "@angular/compiler": "^14.2.0",
      "@angular/core": "^14.2.0",
      "@angular/forms": "^14.2.0",
      "@angular/platform-browser": "^14.2.0",
      "@angular/platform-browser-dynamic": "^14.2.0",
      "@angular/router": "^14.2.0",
      "angular-password-strength-meter": "^5.0.0",
      "keycloak-angular": "^12.1.0",
      "keycloak-js": "^19.0.1",
      "lodash-es": "^4.17.15",
      "ng-inline-svg": "^13.0.0",
      "rxjs": "~7.5.6",
      "sonar-scanner": "^3.1.0",
      "tslib": "^2.4.0",
      "zone.js": "~0.11.8",
    },
    "devDependencies": {
      "@angular-builders/custom-webpack": "^14.0.0",
      "@angular-builders/jest": "^14.0.0",
      "@angular-devkit/build-angular": "^14.2.1",
      "@angular-eslint/builder": "^14.0.0",
      "@angular-eslint/eslint-plugin": "^14.0.0",
      "@angular-eslint/eslint-plugin-template": "^14.0.0",
      "@angular-eslint/schematics": "^14.0.0",
      "@angular-eslint/template-parser": "^14.0.0",
      "@angular/cli": "^14.2.1",
      "@angular/compiler-cli": "^14.2.0",
      "@openapitools/openapi-generator-cli": "^2.5.2",
      "@types/crypto-js": "^4.0.0",
      "@types/google-libphonenumber": "^7.4.18",
      "@types/jest": "^28.1.3",
      "@types/lodash-es": "^4.17.3",
      "@types/node": "^14.0.26",
      "@typescript-eslint/eslint-plugin": "^4.28.4",
      "@typescript-eslint/parser": "^4.28.4",
      "barrelsby": "^2.2.0",
      "commit-message-validator": "^1.0.0",
      "conventional-changelog-cli": "^2.1.0",
      "jest": "^28.1.3",
      "jest-preset-angular": "^12.2.2",
      "ts-jest": "28.0.8",
      "ng-packagr": "^14.3.0",
      "terser-webpack-plugin": "5.3.1",
      "ts-node": "~8.10.2",
      "typescript": "~4.6.4"
    }
  }
  

tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "target": "ES2015",
    "module": "ES2015",
    "lib": ["ES2018", "dom"],
    "strictPropertyInitialization": false,
    "strictNullChecks": false,
    "noStrictGenericChecks": true,
    "paths": {
      "my-lib": ["dist/my-lib/my-lib", "dist/my-lib"]
    }
  },
  "exclude": ["**/node_modules/*", "**/coverage/*", "**/dist/*", "**/node/*"],
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": false,
    "strictTemplates": true
  }
}

tsconfig-esm.spec.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "esModuleInterop": true,
    "outDir": "./out-tsc/spec",
    "module": "ES2020",
    "types": ["jest"]
  },
  "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

jest.config.js

const { pathsToModuleNameMapper } = require('ts-jest');
const { paths } = require('./tsconfig.json').compilerOptions;

/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
  preset: 'jest-preset-angular/presets/defaults-esm',
  globalSetup: 'jest-preset-angular/global-setup',
  globals: {
    'ts-jest': {
      useESM: true,
      stringifyContentPathRegex: '\\.(html|svg)$',
      tsconfig: '<rootDir>/tsconfig-esm.spec.json',
    },
  },
  moduleNameMapper: {
    ...pathsToModuleNameMapper(paths, {prefix: '<rootDir>/'}),
    tslib: 'tslib/tslib.es6.js',
  },
  modulePathIgnorePatterns: ['<rootDir>/dist'],
  setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
  moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs'],
  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)']
};

./src/jest.config.js

const baseConfig = require('../jest.config');

module.exports = {
  ...baseConfig,
  rootDir: '../',
  roots: ['<rootDir>/src'],
  modulePaths: ['<rootDir>'],
  coveragePathIgnorePatterns: ['/dist/', '/node_modules/', '/testing/'],
};

When I run ng test I get the following:

 Details:

    C:\path\to\my\project\node_modules\jest-preset-angular\setup-jest.mjs:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import 'zone.js/fesm2015/zone-testing-bundle.min.js';
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

    > 1 | import 'jest-preset-angular/setup-jest.mjs';
        | ^
      2 | import './jest-global-mocks';
      3 |

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1796:14)
      at Object.<anonymous> (setup-jest.ts:1:1)

Which is strange as the error originates basically from the import 'jest-preset-angular/setup-jest.mjs'; statement in setup-jest.ts.

Even if I put zone.js in the transformIgnorePatterns array it still doesn't work. It shouldn't have a problem anyway as the latest zone.js supports ESM.

Any idea what I'm doing wrong?

0 Answers
Related