Jest not work after migrating to Angular 19.x ( Unexpected token "export" )
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',
],
};
I'm experiencing the same issue. Just come here to report. Glad you did that first
Same here..
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.
transformIgnorePatterns: ['node_modules/(?!(.*.mjs$|keycloak.js))'],
Worked for me.
Only downgrading to keycloak-js version 25 via overriding solves this issue. transformIgnorePatterns doesn't help in my case.
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?
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
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
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
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 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 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.
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.
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))'],
What worked for me was to add transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|keycloak.js)'] in all my jest.config.js files (every modules if any)
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})`]
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 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?
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 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
@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.
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
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 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?