keycloak-angular icon indicating copy to clipboard operation
keycloak-angular copied to clipboard

Jest not work after migrating to Angular 19.x ( Unexpected token "export" )

Open ValentinDEJ opened this issue 11 months ago • 24 comments

Bug Report or Feature Request (mark with an x)

  • [X] bug report -> please search for issues before submitting
  • [ ] feature request

Versions.

  • keycloak-angular: 19.0.2
  • keycloak-js: 26.0.7
  • angular: 19.0.6
  • jest: 29.7.0

Problems.

The described issue is occurring within an nx-workspace, using the mentioned versions above of angular, jest, keycloak-angular and keycloak-js.

I tried to adapt my jest configuration with suggestion mentionned in this issue but is not work, i have same problem.

The log given by the failure.

 FAIL   authentication  projects/shared/authentication/web/src/ensure-authenticated.guard.spec.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /home/valentin/Workspace/poc-csuite-v3/node_modules/keycloak-js/lib/keycloak.js:2
    import { __awaiter } from "tslib";
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      1 | import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router';
    > 2 | import { AuthGuardData, createAuthGuard } from 'keycloak-angular';
        | ^
      3 |
      4 | // This is exported only to facilitate testing. It should not be used directly.
      5 | export const internalEnsureAuthenticated = async (_route: ActivatedRouteSnapshot, state: RouterStateSnapshot, authData: AuthGuardData) => {

      at Runtime.createScriptFromCode (../../../node_modules/jest-runtime/build/index.js:1505:14)
      at Object.<anonymous> (../../../node_modules/keycloak-angular/fesm2022/keycloak-angular.mjs:6:22)
      at Object.<anonymous> (web/src/ensure-authenticated.guard.ts:2:1)
      at Object.<anonymous> (web/src/ensure-authenticated.guard.spec.ts:4:1)

jest.config.ts:

export default {
  displayName: 'authentication',
  preset: '../../../jest.preset.js',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  coverageDirectory: '../../../coverage/projects/shared/authentication',
  transform: {
    '^.+\\.(ts|mjs|js|html)$': [
      'jest-preset-angular',
      {
        tsconfig: '<rootDir>/tsconfig.spec.json',
        stringifyContentPathRegex: '\\.(html|svg)$',
      },
    ],
  },
  transformIgnorePatterns: ['node_modules/(?!(.*.mjs$|keycloak.js))'],
  snapshotSerializers: [
    'jest-preset-angular/build/serializers/no-ng-attributes',
    'jest-preset-angular/build/serializers/ng-snapshot',
    'jest-preset-angular/build/serializers/html-comment',
  ],
};

ValentinDEJ avatar Jan 14 '25 08:01 ValentinDEJ

I'm experiencing the same issue. Just come here to report. Glad you did that first

image

koscik avatar Jan 14 '25 09:01 koscik

Same here..

dvag-carsten-zedler avatar Jan 14 '25 09:01 dvag-carsten-zedler

It might be more of a problem with keycloak-js rather than keycloak-angular. I use Jest too and encoutered a similar issue. For me downgrading keycloak-js to version 25 solved the issue.

ArnaudFlaesch avatar Jan 14 '25 11:01 ArnaudFlaesch

transformIgnorePatterns: ['node_modules/(?!(.*.mjs$|keycloak.js))'], Worked for me.

ash-burns avatar Jan 14 '25 13:01 ash-burns

Only downgrading to keycloak-js version 25 via overriding solves this issue. transformIgnorePatterns doesn't help in my case.

dvag-carsten-zedler avatar Jan 14 '25 16:01 dvag-carsten-zedler

Just a wild guess if transformIgnorePatterns is not working. Could it be the case that there are multiple configs for Jest in the project, hence that the adjusted configuration is maybe not working / not effective, as it is probably overwritten?

LukasMachetanz avatar Jan 14 '25 19:01 LukasMachetanz

For my case, downgrade to keycloak-js 25 solved the problem and transformIgnorePatterns doesn't work but i actualy have multiple jest configuration for respond to @LukasMachetanz

ValentinDEJ avatar Jan 15 '25 07:01 ValentinDEJ

I have opened a ticket in the official repo of keycloak: https://github.com/keycloak/keycloak/issues/36522

Maybe we get an official answer there

JuNe98 avatar Jan 16 '25 11:01 JuNe98

Hi, I am experiencing the same problem. What does work for me are the following combinations:

  • angular 19, keycloak-angular 19, keycloak-js 25 with or without transformIgnorePatterns
  • angular 18, keycloak-angular 16, keycloak-js 26 with transformIgnorePatterns

bjpe avatar Jan 24 '25 11:01 bjpe

Foe me, this part in my jest.config.ts worked

"moduleNameMapper": {
    "^keycloak.*$": "keycloak-angular/fesm2022/keycloak-angular.mjs",
}

This is also faster than the transformIgnorePattern. After upgrading angular to latest (current 19), and also nx (current 20), the transformIgnorePatterns stopped working for me.

Chris2011 avatar Jan 24 '25 14:01 Chris2011

@Chris2011 doesn't work for me. Error message: Could not locate module keycloak-angular mapped as: keycloak-angular/fesm2022/keycloak-angular.mjs.

Please check your configuration for these entries:
{
  "moduleNameMapper": {
    "/^keycloak.*$/": "keycloak-angular/fesm2022/keycloak-angular.mjs"
  },
  "resolver": undefined
}

dvag-carsten-zedler avatar Jan 24 '25 14:01 dvag-carsten-zedler

@dvag-carsten-zedler So maybe we are using different keycloak versions where the folders change. Please check, whether this folder exists in your node_modules folder. If not, try use the correct one.

Chris2011 avatar Jan 24 '25 15:01 Chris2011

I my case , I had to change the modeResolution from node to bundler in the tsconfig.json . This allowed keycloak-js to be resolved. Earlier , it was not resolving withing the project.

arvind-das avatar Jan 26 '25 13:01 arvind-das

Setting moduleResolution to bundler for my tsconfig.spec.json did not help alone (but was still necessary to deal with tsc typechecks)

It helped to wrap hide this import import Keycloak from 'keycloak-js'; away in a service, import (and mock) this service in all UI code & tests.

Then use this pattern mentioned above: transformIgnorePatterns: ['node_modules/(?!(.*.mjs$|keycloak.js))'],

pct-cclausen avatar Feb 04 '25 15:02 pct-cclausen

What worked for me was to add transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|keycloak.js)'] in all my jest.config.js files (every modules if any)

pierre-chevallier357 avatar Feb 11 '25 08:02 pierre-chevallier357

Unfortunately none of the solutions above worked. I have moduleResolution set to bundler and also added the relevant modules to the array of modules which SHOULD be transformed (the regex logic in transformIgnorePatterns is inverse!!) in jest.config.ts.

const esModules: string = [
  'keycloak-angular',
  'keycloak-js'
].join('|');

transformIgnorePatterns: [`<rootDir>/node_modules/(?!${esModules})`]

berkon avatar Feb 21 '25 10:02 berkon

I have a feeling it is not about Angular 19 but rather TypeScript 5.7. The issue here is that TypeScript started to output es modules irrespectively of the setting of "module". There are many reference about it. Of course you should also do transformIgnorePatterns to process your node_modules es to cjs. But do it with esbuild instead of TypeScript! I will share here my jest.config.ts with comments to solve this issue and some other. The key here is processWithEsbuild:

import presets from 'jest-preset-angular/presets';
import type { Config } from 'jest';
import { pathsToModuleNameMapper } from 'ts-jest';
import fs from 'fs';

const tsConfig = JSON.parse(fs.readFileSync('./tsconfig.json', 'utf-8'));
const esModules = ['ip-cidr', 'ip-address', 'camelcase', 'is-ip', 'ip-regex', 'super-regex', 'function-timeout', 'time-span', 'convert-hrtime', 'clone-regexp', 'is-regexp', '.*\\.mjs$'].join('|');

const jestConfig: Config = {
    // This is where you import a preset
    ...presets.createCjsPreset({
    }),
    globals: {
        ngJest: {
            // Process all files in node_modules with esbuild to always have CJS module
            // Typescript is broken here and gives ESM module
            processWithEsbuild: ['**/node_modules/**/*.js']
        }
    },
    roots: ['<rootDir>/src/'],
    testMatch: ['**/+(*.)+(spec).+(ts)'],
    // Still process ES modules that can be found in node_modules folder
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
    setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
    // Use local cache directory so CI can easily delete it
    cacheDirectory: '<rootDir>/.cache',
    // This is needed to support aliases from tsconfig.json
    moduleNameMapper: Object.assign(pathsToModuleNameMapper(tsConfig.compilerOptions.paths, {
        prefix: '<rootDir>/src/',
    }) as any, {
        // Replace some files with a simple mock
        '\\.(svg|mp3)$': '<rootDir>/__mocks__/fileMock.js',
    }),
};

export default jestConfig;

sherlock1982 avatar Feb 28 '25 15:02 sherlock1982

@sherlock1982 Thanks, unluckily it seems to still return the same error for me. Do you think it would be possible for you to also share important lines from your package.json, jest-setup.ts and tsconfig.spec.ts for comparison?

JoshThunar avatar Mar 08 '25 00:03 JoshThunar

For those using jest-preset-angular, release 14.5.2 might have solved this issue : https://github.com/thymikee/jest-preset-angular/issues/2913

ArnaudFlaesch avatar Mar 08 '25 13:03 ArnaudFlaesch

@ArnaudFlaesch It's true that is solved a lot of issues, f.e. chart-js works effortlessly, but unluckily it seems that the keycloak anguar issue persists, also on 14.5.3 version

JoshThunar avatar Mar 08 '25 21:03 JoshThunar

@Chris2011 Thanks four your answer, this works for me.

Foe me, this part in my jest.config.ts worked

"moduleNameMapper": { "^keycloak.*$": "keycloak-angular/fesm2022/keycloak-angular.mjs", }

This is also faster than the transformIgnorePattern. After upgrading angular to latest (current 19), and also nx (current 20), the transformIgnorePatterns stopped working for me.

I just updated the path. Added <rootDir>/node_modules/ before keycloak-angular/ then it works. @dvag-carsten-zedler perhaps it will help you too.

inpercima avatar Mar 25 '25 09:03 inpercima

This is also faster than the transformIgnorePattern

Interestingly, with no cache, this is considerably slower for me than the alternative.

transformIgnorePatterns - 142.43 s moduleNameMapper - 184.093 s

ash-burns avatar Mar 25 '25 10:03 ash-burns

A small tip if you just wanna get it out the way is to mock it away completely:

// null-module.js
module.exports = null;
{
  moduleNameMapper: {
    'keycloak-js': __dirname + '/whatever/null-module.js',
  }
}

Then whenever you actually wanna test it you can do something like this at the top of the test:

// the-thing.spec.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
class MockKeycloak {
  updateToken = jest.fn();
  // ...whatever you wanna mock..
}

jest.mock('keycloak-js', () => MockKeycloak);

// ....import stuff....

import Keycloak from 'keycloak-js';

// ...the tests....

prewk avatar Apr 28 '25 11:04 prewk

@prewk Thank you. I discovered, that it works effortlessly when running a single test, but when running all tests it randomly fails, throwing the same error as before. I tried to fix it with jest.resetModules() and jest.clearAllMocks() but it's still not deterministic. Using const Keycloak = require('keycloak-js'); instead of import in component allows tests to succeed, but it's difficult to maintain. Did you maybe have such problems?

JoshThunar avatar Aug 10 '25 22:08 JoshThunar