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

Can't run tests with jest 28.0 #10117

Closed
Tirke opened this issue May 3, 2022 · 29 comments · Fixed by #10857
Closed

Can't run tests with jest 28.0 #10117

Tirke opened this issue May 3, 2022 · 29 comments · Fixed by #10857
Assignees
Labels
outdated scope: testing tools Issues related to Cypress / Jest / Playwright / Vitest support in Nx type: bug

Comments

@Tirke
Copy link

Tirke commented May 3, 2022

Current Behavior

I tried updating jest to the latest version (28) and started experiencing the classic TypeScript errors that you get when there are Babel / compiling TypeScript issues.
For example: SyntaxError: Unexpected token 'export' or SyntaxError: Cannot use import statement outside a module
I'm using babel-jest as the transform for my TypeScript tests.

Expected Behavior

I should be able to use jest 28 to run my tests.

Steps to Reproduce

Here is a repo reproduction.
https://github.com/Tirke/jest-28

If you switch back to jest 27 it will work.
They did communicate on some changes with Babel here

Failure Logs

Environment

❯ pnx report

 >  NX   Report complete - copy this into the issue template

   Node : 16.14.2
   OS   : darwin arm64
   pnpm : 7.0.0

   nx : 14.0.5
   @nrwl/angular : Not Found
   @nrwl/cypress : 14.0.5
   @nrwl/detox : Not Found
   @nrwl/devkit : 14.0.5
   @nrwl/eslint-plugin-nx : 14.0.5
   @nrwl/express : Not Found
   @nrwl/jest : 14.0.5
   @nrwl/js : 14.0.5
   @nrwl/linter : 14.0.5
   @nrwl/nest : Not Found
   @nrwl/next : Not Found
   @nrwl/node : 14.0.5
   @nrwl/nx-cloud : Not Found
   @nrwl/nx-plugin : Not Found
   @nrwl/react : Not Found
   @nrwl/react-native : Not Found
   @nrwl/schematics : Not Found
   @nrwl/storybook : Not Found
   @nrwl/web : 14.0.5
   @nrwl/workspace : 14.0.5
   typescript : 4.6.4
   rxjs : 6.6.7
   ---------------------------------------
   Community plugins:
@Tirke Tirke added the type: bug label May 3, 2022
@Tirke Tirke changed the title Can't update to jest 28.0 Can't run test with jest 28.0 May 3, 2022
@Tirke Tirke changed the title Can't run test with jest 28.0 Can't run tests with jest 28.0 May 3, 2022
@AgentEnder AgentEnder added the scope: testing tools Issues related to Cypress / Jest / Playwright / Vitest support in Nx label May 3, 2022
@barbados-clemens
Copy link
Contributor

Jest 28 support is still in the works, but probably need to update ts-jest to v28 since transformer return changed a little. I think ts-jest v28 was still prerelease

@barbados-clemens barbados-clemens self-assigned this May 3, 2022
@rfprod
Copy link
Contributor

rfprod commented May 6, 2022

@barbados-clemens seem like ts-jest 28 is now available https://www.npmjs.com/package/ts-jest

@barbados-clemens
Copy link
Contributor

Looks like it. there are still @types/jest and jest-preset-angular that need to drop support for jest v28 as well.
jest-preset-angular hast a next tag and expected to release soon-ish.

@mkhib
Copy link

mkhib commented May 8, 2022

When updated to jest 28 my test ran to this issue:
Cannot read properties of undefined (reading 'html')

@rfprod
Copy link
Contributor

rfprod commented May 8, 2022

@mkhib It's most probably jest-preset-angular. It has to be updated to support jest 28.
If you're impatient, give this pre-release a try https://github.com/thymikee/jest-preset-angular/releases/tag/v12.0.0-next.1
However, no guarantees it will work, I haven't tried.

@barbados-clemens
Copy link
Contributor

@mkhib you probably are needing the same version match for jest-environment-jsdom if I had to guess and like @rfprod said if you're needing jest 28 and angular then you'll be needing the updated jest-preset-angular version

@satanTime
Copy link

The problem here is that @nrwl/jest@^14 locks some jest packages to stay on 27 and it keeps jest-environment-jsdom at 27 too, whereas jest@28 requires jest-environment-jsdom@28.

@barbados-clemens
Copy link
Contributor

you can manually install jest-environment-jsdom v28 and jest v28 without any problems. @nrwl/jest isn't going to stop you from manually updating those.

I'm working on getting a PR for jest 28 support. just mainly working through the migrations for the changes. but in my limited testing you can just bump all your jest versions and it should just work with the exception of using @nrwl/react/plugins/jest as it needs to be updated for the new transformer return type and of course if you're using any of the changed apis in the jest vv27-v28 migration guide.

@satanTime
Copy link

yep, it works, a hotfix is:

  • add jest-environment-jsdom@^28 to dev dependencies
  • execute npm i
  • delete jest-environment-jsdom@^28 from dev dependencies
  • execute npm i
  • profit, lock file has jest-environment-jsdom@^28 now without explicit dependency in package.json

@barbados-clemens
Copy link
Contributor

with the jest v28 changes (and comming migration) you should explicitly include jest-environment-jsdom in your devDeps. So I would recommend you keep it in your devDeps.
https://jestjs.io/docs/upgrading-to-jest28#jsdom

@satanTime
Copy link

satanTime commented May 17, 2022

I think it's an ask for nx, because it doesn't add jest-environment-jsdom to package.json.

Also, why should I, if it's a known dependency for Angular and jest?
For me, it would make sense if either nx or jest-preset-angular would add it.

@barbados-clemens
Copy link
Contributor

nx will add it via migration and going forward when jest v28 support is released. Just saying in the meantime it would probably be best to be explicit about the devDep to prevent it from being accidentally removed from others in the project.

@hardikpatel043
Copy link
Contributor

Any tentative release date for jest v28 support?

@barbados-clemens
Copy link
Contributor

it's in progress. the migrations are a bit tricky for this one.

@rfprod
Copy link
Contributor

rfprod commented May 23, 2022

If anyone needs an example of an nx workspace that uses jest 28, check this one. There are two frameworks: NestJS, Angular.

Changes that have to be made:

If @angular/fire if used, one will have to mock the package depending on what's used like

...
jest.mock('@angular/fire/app', () => firebaseAppMockValue);
jest.mock('@angular/fire/auth', () => firebaseAuthMockValue);
jest.mock('@angular/fire/database', () => firebaseDatabaseMockValue);
...

@eglove
Copy link

eglove commented Jun 5, 2022

Just curious, if I manually migrate now, will it affect NX's ability to do migrations in the future? I'd like NX to maintain control, but I've never been sure what kinds of changes I can make without affecting those migrations.

@rfprod
Copy link
Contributor

rfprod commented Jun 6, 2022

Just curious, if I manually migrate now, will it affect NX's ability to do migrations in the future? I'd like NX to maintain control, but I've never been sure what kinds of changes I can make without affecting those migrations.

@eglove my guess is that Nx will override your manual changes, if applicable, when you apply migrations.

@barbados-clemens
Copy link
Contributor

@eglove @rfprod it shouldn't break anything once the nx migration is ran. the only thing that would get overridden I think would get overridden would be the jest version if they don't match. Any breaking changes from jest 28 the migration would update would presumably already be fixed in your code base.

I'm going to try to finish the jest 28 migration this week. Just putting the final touches on the Cypress 10 support.

@dereekb
Copy link

dereekb commented Jun 13, 2022

I've just about finished my manual migration and other cleanup. The biggest thing I was running into is documented here.

thymikee/jest-preset-angular#1625

I had to work on setting up my resolver to remove object properties from the parsed package.json so it would target main instead of the module/exports fields for importing.

@Ezard
Copy link
Contributor

Ezard commented Jun 30, 2022

@barbados-clemens something to keep on your radar when you're implementing this:
After upgrading to Jest 28, certain packages (such as uuid, as mentioned in the Jest docs here)
The suggested way to resolve this is via the packageFilter field in a custom resolver, e.g.

packageFilter: pkg => {
  if (pkg.name === 'uuid') {
    delete pkg['exports'];
    delete pkg['module'];
  }
  return pkg;
}

However, since Nx uses its own custom resolver, it's not possible to implement this workaround whilst using Nx
This is of particular note, since Nest makes use of the uuid package, so any Jest tests run with Nx and Jest 28 will fail
I'm not sure what the best way forward is here though - maybe the fix for uuid could be handled internally by Nx (since Nest is one of Nx's main supported platforms), but then also a way for people to supply an additional "packageFilter" function somehow (e.g. by writing a custom resolver that passes additional config to the Nx resolver)

I tested adding this extra packageFilter logic into Nx's resolver and it resolved the issue for me; obviously it'd need more testing with non-Nest frameworks, to make sure it doesn't affect them (I don't see how it would, but you never know)

@Brian-McBride
Copy link

Brian-McBride commented Jun 30, 2022

@Ezard I've been running my own resolver for a while with Nx.
I had to basically take the Nx resolver and drop in my own.

jest.preset.js

const nxPreset = require('@nrwl/jest/preset').default;
module.exports = { ...nxPreset, resolver: `${process.cwd()}/jest.resolver.js` };

jest.resolver.js

const fs = require('fs');
const path = require('path');
const nrwlResolver = require('@nrwl/jest/plugins/resolver');

// Compose your own resolver function
function jestNodeExportsResolver(request, options) {
  // Modify stuff
return nrwlResolver(request,{
  ...options,
  packageFilter: pkg => {
    if (pkg.name === 'uuid') {
      delete pkg['exports'];
      delete pkg['module'];
    }
    return pkg;
  }
});
}

module.exports = jestNodeExportsResolver;

If you look into the actual @nrwl/jest resolver, you'll see that it first tries to resolve using the Jest default resolver with the options you provide. If that fails, then it overwrites with its own packageFilter. So in the example above, your resolver should get first shot at it. Since you are dealing with node_modules it should work out.

try {
// Try to use the defaultResolver with default options
return options.defaultResolver(path, options);
} catch {
// Try to use the defaultResolver with a packageFilter
return options.defaultResolver(path, {
...options,
packageFilter: (pkg) => ({
...pkg,
main: pkg.main || pkg.es2015 || pkg.module,
}),
pathFilter: (pkg) => {
if (!pkg.exports) {
return path;
}
return resolveExports(pkg, path) || path;
},
});
}


Edit:

I will add that all configuration settings for tooling should be extendable/composable. A lot of Nx is, but there are some pain points like Next's config and this resolver.

@dereekb
Copy link

dereekb commented Jun 30, 2022

As another heads up, the jest-preset-angular package ultimately ended up posting a workaround writeup for some nested dependencies in the troubleshooting section.

https://thymikee.github.io/jest-preset-angular/docs/guides/troubleshooting/#resolver-needed-for-some-javascript-library-or-nested-dependencies

Aside from adding a big list of affected packages to the NX resolver, it's going to be challenging to make sure everyone's environment stays working after the migration. You will probably want to make the resolver exposed or configurable enough to add packages to, at the very least.

@barbados-clemens
Copy link
Contributor

This is tricky as providing a resolver to jest is just a path to the resolver. So there isn't really a jest built-in way to extend a resolver without manually extending as demoed above. I am very hesitant to handle all the various projects that need custom logic internally in the resolver. I'll look more into this.

@Ezard
Copy link
Contributor

Ezard commented Jul 8, 2022

@barbados-clemens the uuid package would definitely be a good candidate for Nx to handle, since that's used internally by Nest (and Nest is obviously one of the major platforms that Nx supports)

Currently, any Nx project that uses Nest with Jest 28 will need to use the above workaround (maybe the above patch could only be automatically applied when Nx is setup to contain Nest projects?)

@barbados-clemens
Copy link
Contributor

Talking with other on this issue I think we'll go with maintaining the list internally inside the nrwl resolver and if there is a reason you need to extend our resolver then following @Brian-McBride example on how to do it is a good way to do it.

While maintaining that list isn't ideal, it does prevent

  1. people having to implement/nx generating a custom resolver
  2. not having a custom resolver makes migrations easier in the future
  3. we are reasonably good at being able to quickly make new releases if we need to update that list so shouldn't be a huge issue for the most part.

Right now I know of the packages mentioned via jest-preset-angular docs and uuid. If there are any more packages that we need to target please let me know either here on in the jest 28 PR.

uuid
rxjs
@firebase/auth
@firebase/storage
@firebase/functions
@firebase/database
@firebase/auth-compat
@firebase/database-compat
@firebase/app-compat
@firebase/firestore
@firebase/firestore-compat
@firebase/messaging
@firebase/util
firebase

@Brian-McBride
Copy link

Brian-McBride commented Jul 8, 2022

Don't forget the firebase-auth side of things. I bet they face the same issues.
I think nanoid also released a new version that is only ESM. I haven't looked into how to get that one working.

The whole ESM migration is poorly thought out it seems now that we are in it all.

While there is a lot of focus on Angular within the Nx team, please don't forget React! lol :)

@redjab
Copy link

redjab commented Aug 1, 2022

While waiting for this issue to be resolved, I modified the react transformer so it works with jest 28

// Adapted from @nrwl/react/plugins/jest with modifications to make it work with jest 28
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const path = require('path');
const devkit_1 = require('@nrwl/devkit');
const JS_SOURCE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
module.exports = {
  process(src, filename, options) {
    const assetFilename = JSON.stringify(path.basename(filename));
    if (filename.match(/\.svg$/)) {
      // Based on how SVGR generates a component name:
      // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
      const pascalCaseFilename = (0, devkit_1.names)(
        path.parse(filename).name
      ).className;
      const componentName = `Svg${pascalCaseFilename}`;
      const value = `const React = require('react');
      module.exports = {
        __esModule: true,
        default: ${assetFilename},
        ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
          return {
            $$typeof: Symbol.for('react.element'),
            type: 'svg',
            ref: ref,
            key: null,
            props: Object.assign({}, props, {
              children: ${assetFilename}
            })
          };
        }),
      };`;
      return { code: value };
    }
    if (JS_SOURCE_EXTENSIONS.includes(path.extname(filename))) {
      const transformer = getJsTransform();
      if (transformer) return transformer.process(src, filename, options);
    }
    return {
      code: `module.exports = ${assetFilename};`,
    };
  },
};
function getJsTransform() {
  try {
    return require('babel-jest').default.createTransformer();
  } catch (_a) {
    // ignored
  }
  try {
    return require('@swc/jest').createTransformer();
  } catch (_b) {
    // ignored
  }
}

Then in jest.config.ts

	transform: {
		"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)":
			"<rootDir>/../../jest-28-transformer.js",
		"^.+\\.[tj]sx?$": ["babel-jest", { rootMode: "upward" }],
	},

@tvthatsme
Copy link

In my case (a @nrwl/next app), changing the jest.config.ts transform from

'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }]

to

'^.+\\.[tj]sx?$': [ 'babel-jest', { presets: ['next/babel'] }]

did the trick.

@github-actions
Copy link

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated scope: testing tools Issues related to Cypress / Jest / Playwright / Vitest support in Nx type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.