Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jest tests keep failing after upgrade to angular 13 and following the migration guide #1413

Closed
innoveltec opened this issue Apr 24, 2022 · 28 comments · Fixed by #1455
Closed
Labels
🐛 Bug Confirmed Bug is confirmed

Comments

@innoveltec
Copy link

innoveltec commented Apr 24, 2022

Version

11.1.1

Steps to reproduce

Hello,

Upgraded an existing angular application from v12.2.0 to 13.2.7. After the angular upgrade tests were failing so i followed the Migration steps from Angular < 13 guide and made the changes according the angular 13 example app. Using jest 28.0.0-alpha.8. When running 'node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest-esm.config.mjs --no-cache' all my tests keep failing with the same following error =>

Test suite failed to run
TypeError: Cannot read properties of undefined (reading 'html')
at new JSDOMEnvironment (node_modules/jest-environment-jsdom/build/index.js:72:44)

Kind regards,

Gerry

Expected behavior

Running my tests should not give any errors after the upgrade to angular 13.

Actual behavior

All my tests are failing after upgrading from angular 12 to 13. Even after making the changes like it has be done in the angular 13 example app.

Additional context

No response

Environment

System:
    OS: Windows 10 10.0.19042
    CPU: (8) x64 Intel(R) Core(TM) i5-8365U CPU @ 1.60GHz
Binaries:
    Node: 16.13.1 - C:\Program Files\nodejs\node.EXE     
    npm: 8.1.2 - C:\Program Files\nodejs\npm.CMD
npmPackages:
    jest: 28.0.0-alpha.8 => 28.0.0-alpha.8 
@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 24, 2022

Would you pls make a small reproducible example? You can also use https://github.com/thymikee/jest-preset-angular/tree/main/examples/example-app-v13 to make a reproduce problem which will help a lot to debug.

@innoveltec
Copy link
Author

@ahnpnl I have just cloned the angular 13 example app you proposed to use for a reproducable and when I try to run the tests with npm run test-esm all the tests are failing with the same error as i am experiencing in my angular 13 application. I didn' t changed anything. Just cloned and npm install and the run the tests.

Kind regards,

Gerry

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 24, 2022

hmm that's strange, in GitHub action there aren't any issues. My laptop doesn't have issues either. It seems like something odd with Jest and ESM.

I forgot one thing that in main this repo is using Jest next tag. Would you please try with https://github.com/thymikee/jest-preset-angular/tree/v11.1.1/examples/example-app-v13 instead?

@innoveltec
Copy link
Author

@ahnpnl ok now the tests are running in the angular 13 example app. I changed my jest version to 27.5.1 as it was in the angular 13 example app but now i am getting a different error for all my unit tests =>

Test suite failed to run
SyntaxError: The requested module '@googlemaps/markerclusterer' does not provide an export named 'MarkerClusterer'
at Runtime.linkAndEvaluateModule (node_modules/jest-runtime/build/index.js:779:5)

The @googlemaps/markerclusterer package is used in an angular ui component library published via npm which was custom made by us and is installed in our angular 13 application. The ui component library itself is created within a nx repo using also angular 13 and jest for testing. Running the tests in this library from within the nx repo does not give any errors.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 24, 2022

I think this is the problem with @googlemaps/markerclusterer not playing well with Jest. I found this https://stackoverflow.com/questions/58540086/angular-8-and-google-maps-with-marker-clustering-type-error-markercluster-is-n not sure if it helps. I'm not familiar with that package but the issue is about Jest can't resolve the way of importing the package.

@innoveltec
Copy link
Author

Ok no idea how to solve that :-) we are just importing it like this => import { MarkerClusterer } from '@googlemaps/markerclusterer'; inside an angular component. There are also no issues while running or building the angular application. And the page containing the component which uses the markerclusterer also works without any issues. It's only my tests that are failing. Also what i find strange is that it is giving this same error for all my tests also all the components which are not using the angular component with the markerclusterer.

@innoveltec
Copy link
Author

I also found a similar issue => https://stackoverflow.com/questions/70985255/jest-es6-modules-does-not-provide-export-named
They are saying it should be solved in in jest v28 but i can not try with v28 because then the tests are failing with the other error i had before using the v28 alpha :-(

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 24, 2022

You can try jest 28 now locally with the next tag of jest-preset-angular (just published)

@innoveltec
Copy link
Author

Cloned the latest version of the angular 13 example app. Ran npm install. After that try to run the tests with npm run test-esm but getting this error now =>

Error: Cannot find module 'ts-jest/dist/ts-jest-transformer'
Require stack:

  • C:\Users\G53188\example-app-v13\node_modules\jest-preset-angular\build\ng-jest-transformer.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-preset-angular\build\index.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-util\build\requireOrImportModule.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-util\build\index.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-config\build\getCacheDirectory.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-config\build\Defaults.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-config\build\normalize.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-config\build\index.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-cli\build\init\index.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-cli\build\cli\index.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-cli\build\index.js
  • C:\Users\G53188\example-app-v13\node_modules\jest-cli\bin\jest.js
  • C:\Users\G53188\example-app-v13\node_modules\jest\bin\jest.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object. (C:\Users\G53188\example-app-v13\node_modules\jest-preset-angular\build\ng-jest-transformer.js:9:31)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 25, 2022

I just updated examples to use next tag. The error above should be gone now

@innoveltec
Copy link
Author

Thank you error is gone. Unit tests example app are working but my unit tests in my angular app are still giving the same error =>

SyntaxError: The requested module '@googlemaps/markerclusterer' does not provide an export named 'MarkerClusterer'
at Runtime.linkAndEvaluateModule (node_modules/jest-runtime/build/index.js:826:5)

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 25, 2022

I think you might need to use moduleNameMapper to tell Jest where to look the correct files. This is out of scope for this preset to solve.

I will try to debug to see more

@innoveltec
Copy link
Author

Okay thank you for the feedback. This goes a little bit above my knowledge but I will have a look :-) It's just a pity that I have to disable all my tests in all our angular 13 projects which are using our custom ui component library that implemented the @googlemaps/markerclusterer because otherwise the testing step in our build pipelines will fail :-(

@e-oz
Copy link

e-oz commented Apr 28, 2022

If someone found a workaround, or some solution how to change config files - please post it here.

It's not a request to contributors, I'm posting it here because this page is a first in Google when searching for "angular jest 28" :)

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 30, 2022

Would you guys pls provide a test case to how to use that library to the example app? That would help a lot.

@ahnpnl ahnpnl added 🤷‍♂️ Needs More Info waiting for more information from author of the issue Needs Repo Need a minimium repository to reproduce the problem and removed Bug Report Needs Triage labels Apr 30, 2022
@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 30, 2022

@innoveltec I've debugged and checked, the package @google/markerclusterer has a file markclusterer.esm.js. This file only provides default export but not name export. Therefore when you do

import { MarkClusterer } from '@google/markclusterer';

Jest will throw error. You have to use default import

import MarkerClusterer from '@google/markerclusterer';

I hope this helps.

@maxens-lpsil
Copy link

Hello guys !

I have the same problem than @innoveltec 's first post.
I am using jest 28.0.3 and jest-preset-angular 11.1.2 and the error TypeError: Cannot read properties of undefined (reading 'html') is happening.
If i come back to jest 27.5.1 others problems occurs.
My test were perfectly working before migrating to Angular 13 and i'm a bit lost actually.

Does someone found a solution ?

Thanks.

@ahnpnl
Copy link
Collaborator

ahnpnl commented May 3, 2022

You should use next tag of jest-preset-angular. Jest 28 is a major version and we will put a new major release to adopt Jest 28.

@innoveltec
Copy link
Author

innoveltec commented May 3, 2022

@innoveltec I've debugged and checked, the package @google/markerclusterer has a file markclusterer.esm.js. This file only provides default export but not name export. Therefore when you do

import { MarkClusterer } from '@google/markclusterer';

Jest will throw error. You have to use default import

import MarkerClusterer from '@google/markerclusterer';

I hope this helps.

@ahnpnl it is not @google/markerclusterer which we use but @googlemaps/markerclusterer which is having named exports. https://developers.google.com/maps/documentation/javascript/marker-clustering#maps_marker_clustering-typescript
The @google/markerclusterer was an old version which seems to be deprecated.

@ahnpnl
Copy link
Collaborator

ahnpnl commented May 3, 2022

OK I found the problem. This is how it happens in several scenarios under ESM Jest mode when doing import { MarkerClusterer } from '@googlemaps/markerclusterer';

With default resolution from Jest default resolver

Jest will resolve @googlemaps/markerclusterer as @googlemaps/markerclusterer/dist/index.umd.js. However, the umd file doesn't have name export and since it's an umd file, Jest doesn't ask jest-preset-angular to process it either.

In this scenario, because jest-preset-angular doesn't process the file and Jest tries to execute the file which leads to the error

 SyntaxError: The requested module '@googlemaps/markerclusterer' does not provide an export named 'MarkerClusterer'

With adjustment in Jest config

To tell Jest to load the ESM file of @googlemaps/markerclusterer, your jest.config.js needs to be adjusted to be

// jest.config.js
module.exports = {
     // ...
     moduleNameMapper: {
         tslib: 'tslib/tslib.es6.js',
         '@googlemaps/markerclusterer': '<rootDir>/node_modules/@googlemaps/markerclusterer/dist/index.esm.js',
         rxjs: '<rootDir>/node_modules/rxjs/dist/bundles/rxjs.umd.js',
     },
     transformIgnorePatterns: ['node_modules/(?!tslib|@googlemaps/markerclusterer)'],
}

By using moduleNameMapper, we tell Jest to load index.esm.js as the correct ESM file. This file contains name export, e.g. MarkClusterer. transformIgnorePatterns will inform Jest that this file needs to be index.esm.js because when specifying moduleNameMapper, any files in this option will be treated as CommonJS.

Now our config is good to use the package @googlemaps/markerclusterer. However, this package has the similar problem like tslib that when it is transformed by esbuild, the transform result to CommonJS doesn't match with what Jest wants, which resolves into this error

 SyntaxError: The requested module '@googlemaps/markerclusterer' does not provide an export named 'MarkerClusterer'

Workaround

Using default export works for me without the need of adjusting Jest config. This is the minimum Jest config

import ngPreset from 'jest-preset-angular/presets/index.js';

/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
const jestConfig = {
  ...ngPreset.defaultsESM,
  globals: {
    'ts-jest': {
      ...ngPreset.defaultsESM.globals["ts-jest"],
      tsconfig: '<rootDir>/tsconfig-esm.spec.json',
    },
  },
  globalSetup: 'jest-preset-angular/global-setup',
  moduleNameMapper: {
    tslib: 'tslib/tslib.es6.js',
    rxjs: '<rootDir>/node_modules/rxjs/dist/bundles/rxjs.umd.js',
  },
  setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
}

export default jestConfig;

and then in the code I use default export

import foo from '@googlemaps/markerclusterer';

console.warn(foo.MarkerClusterer);

If you still want to use name exports, jest-preset-angular needs to use TypeScript API to transform @googlemaps/markerclusterer instead of using esbuild

@innoveltec
Copy link
Author

Hello @ahnpnl thank you for all your work and help. But there are some things that are not clear to me. So basically the 'With adjustment in Jest config' and the 'Workaround' are both 2 solutions which I can try to fix my issue?

For the 'With adjustment in Jest config' solution, these changes need to be done in the jest.config.js file or should they be done in the jest-esm.config.mjs file? If the changes need to be made in the jest.config.js file which is used when running jest without esm modules then do you mean that I should be able to run my tests in my angular 13 application which uses our custom library that is using the @googlemaps/markerclusterer package without using the esm feature but just the standard jest. Also for this solution which version of jest, ts-jest and jest-preset-angular do I need to use?

For the 'Workaround' solution, I suppose the minimum jest config you are talking about is the config from the jest-esm.config.mjs file? This solution will run jest in esm mode so for this solution i can just use jest v28.0.3, jest-preset-angular v12.0.0-next.1 and ts-jest is not needed. Am I correct on this? Still if I try to do something like import foo from '@googlemaps/markerclusterer' in the custom angular library which is using the markerclusterer, I am getting the error that it has no default export.

Kind regards,

Gerry

@ahnpnl
Copy link
Collaborator

ahnpnl commented May 4, 2022

Sorry it wasn’t so clear.

The workaround is the solution to go. The other 2 headings were the different debugging approaches but they turned out to be not working.

With the workaround, I only tested with example-app-v13 by adding to app.component.spec.ts. Indeed the config comes from the example app we have here too. That config is indeed to tell Jest to run tests in ESM mode.

For the case of using in an Angular library, I think your Angular library should have similar Jest config. I haven’t tried out yet but in theory it should work the same.

Did you try using the @googlemaps/markerclusterer in app.component.spec.ts? Did it work?

Another workaround (not tested)

Use transform config in Jest and tell Jest to use ts-jest to process the @googlemaps/markerclusterer. Something like

transform: {
      ‘@googlemaps/markerclusterer’: ‘ts-jest’,
}

Way to go forward

We still need to do a proper fix for this by not using esbuild for this package.

@ahnpnl ahnpnl removed 🤷‍♂️ Needs More Info waiting for more information from author of the issue Needs Repo Need a minimium repository to reproduce the problem labels May 4, 2022
@ahnpnl ahnpnl added 🐛 Bug Confirmed Bug is confirmed labels May 4, 2022
@innoveltec
Copy link
Author

Hello @ahnpnl ,

Thank you for the feedback. I will try to do the workaround. The angular library which is using the markerclusterer package is in a nrwl nx repo and nx is managing the jest config by themself I think. I see they are using jest v27.2.3, ts-jest v27.0.5 and jest-preset-angular v11.1.1 It's also weird that running the tests of my library in the nx repo is not giving any errors on the markerclusterer and are all succeeding but nx probably does some custom stuff behind the scenes. No idea :-) Any ways I will try your workarounds and keep you posted on the results.

Thanks, Gerry

@innoveltec
Copy link
Author

innoveltec commented May 6, 2022

Hello @ahnpnl,
For some reason in my angular library in the nx repo where I am using the markerclusterer package I am not able to use default imports. It gives me an error saying .../node_modules/@googlemaps/markerclusterer/dist/index has no default export. In your example v13 application I can use default import like in your workaround but if I try to run the application importing it in the app.component I am also getting a lot of build errors on the package. I have no idea why I can not use default imports for the markerclusterer package in my components library. So I am a little bit out of option. Maybe it is because some configuration I am missing or different version of some dependencies. I have no clue. My package.json in my nx repo is having the following dependencies:

"dependencies": {
"@angular/animations": "13.2.7",
"@angular/common": "13.2.7",
"@angular/compiler": "13.2.7",
"@angular/core": "13.2.7",
"@angular/forms": "13.2.7",
"@angular/platform-browser": "13.2.7",
"@angular/platform-browser-dynamic": "13.2.7",
"@angular/router": "13.2.7",
"@googlemaps/markerclusterer": "^2.0.6",
"@ng-select/ng-select": "^8.1.1",
"@ngneat/until-destroy": "^9.0.1",
"@ngx-translate/core": "^14.0.0",
"@ngx-translate/http-loader": "^7.0.0",
"@nrwl/angular": "13.9.5",
"angular-in-memory-web-api": "^0.13.0",
"angular-oauth2-oidc": "^13.0.0",
"angular-oauth2-oidc-jwks": "^13.0.0",
"bootstrap": "^4.6.0",
"google-libphonenumber": "^3.2.25",
"intl-tel-input": "^17.0.15",
"lodash-es": "^4.17.21",
"ngx-bootstrap": "^8.0.0",
"ngx-cookie-service": "^13.1.2",
"ngx-i18n-combine": "^1.2.0",
"rxjs": ">=6.6.0",
"storybook-dark-mode": "^1.0.9",
"tslib": "^2.3.1",
"zone.js": "0.11.5"
},
"devDependencies": {
"@angular-devkit/architect": "^0.1303.1",
"@angular-devkit/build-angular": "13.2.6",
"@angular-devkit/core": "^13.3.1",
"@angular-eslint/eslint-plugin": "13.0.1",
"@angular-eslint/eslint-plugin-template": "13.0.1",
"@angular-eslint/template-parser": "13.0.1",
"@angular/cli": "13.2.6",
"@angular/compiler-cli": "13.2.7",
"@angular/language-service": "13.2.7",
"@nrwl/cli": "13.1.4",
"@nrwl/cypress": "13.9.5",
"@nrwl/eslint-plugin-nx": "13.9.5",
"@nrwl/jest": "13.9.5",
"@nrwl/linter": "13.9.5",
"@nrwl/nx-cloud": "13.2.2",
"@nrwl/storybook": "13.9.5",
"@nrwl/workspace": "13.9.5",
"@storybook/addon-essentials": "6.4.20",
"@storybook/angular": "6.4.20",
"@storybook/builder-webpack5": "6.4.20",
"@storybook/manager-webpack5": "6.4.20",
"@types/google-libphonenumber": "^7.4.23",
"@types/google.maps": "^3.48.6",
"@types/jest": "27.0.2",
"@types/lodash-es": "^4.17.5",
"@types/node": "14.14.33",
"@typescript-eslint/eslint-plugin": "5.10.2",
"@typescript-eslint/parser": "5.10.2",
"cypress": "^8.3.0",
"eslint": "8.7.0",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-cypress": "^2.10.3",
"jest": "27.2.3",
"jest-junit": "^13.0.0",
"jest-preset-angular": "11.1.1",
"jest-sonar-reporter": "^2.0.0",
"ng-packagr": "13.2.1",
"nx": "13.9.5",
"postcss": "^8.3.9",
"postcss-import": "^14.0.2",
"postcss-preset-env": "^6.7.0",
"postcss-url": "^10.1.1",
"prettier": "2.5.1",
"ts-jest": "27.0.5",
"typescript": "4.5.5"
},

@ahnpnl
Copy link
Collaborator

ahnpnl commented May 7, 2022

If i use import default in app.component.ts, indeed build Angular will show error like

Error: export 'default' (imported as 'foo') was not found in '@googlemaps/markerclusterer'

Build Angular only accepts either name import or import star.

I think for now, you should run your tests without ESM mode. I can see that CommonJS mode works fine with name export

ahnpnl added a commit that referenced this issue May 8, 2022
…1455)

Fixes #1413
Fixes #1437

BREAKING CHANGE
Previously, we always checked file extension `.mjs` and any files from `node_modules` excluding `tslib` to be processed with `esbuild`. With the new option `processWithEsbuild`, now we put default all `.mjs` files to be processed by `esbuild`.

Files like `lodash-es` default isn't processed by `esbuild`. If you wish to use `esbuild` to process such files, please configure in your Jest config like
```
// jest.config.js
module.exports = {
    //...
    globals: {
         ngJest: {
              processWithEsbuild: ['**/node_modules/lodash-es/*.js],
         }
    }
}
```
@e-oz
Copy link

e-oz commented May 15, 2022

To fix this issue, I had to:

  1. install jest-environment-jsdom
  2. update jest-preset-angular to 12.1.0-next.0

Thanks a lot to all the contributors ;)

@onero
Copy link

onero commented May 16, 2022

To fix this issue, I had to:

  1. install jest-environment-jsdom"
  2. update jest-preset-angular to 12.1.0-next.0

Thanks a lot to all the contributors ;)

This fixed it for me as well!

@OzkrMonroy
Copy link

To fix this issue, I had to:

  1. install jest-environment-jsdom
  2. update jest-preset-angular to 12.1.0-next.0

Thanks a lot to all the contributors ;)

This worked for me as well! Thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 Bug Confirmed Bug is confirmed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants