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

SyntaxError about import when using in jsdom testEnvironment #363

Closed
smmoosavi opened this issue May 25, 2022 · 19 comments
Closed

SyntaxError about import when using in jsdom testEnvironment #363

smmoosavi opened this issue May 25, 2022 · 19 comments

Comments

@smmoosavi
Copy link

When testEnvironment: 'jsdom' is set it tries to load node_modules/nanoid/index.browser.js and shows an error about the import syntrax.

note: in testEnvironment: 'node' import has no problem

jest config:

export default {
  testEnvironment: 'jsdom',
  moduleFileExtensions: ['js', 'ts', 'tsx', 'json'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
};

tsconfig

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": false,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["./src"]
}

error:

    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:

    /<path-to-project>/node_modules/.pnpm/nanoid@3.3.4/node_modules/nanoid/index.browser.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import { urlAlphabet } from './url-alphabet/index.js'
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

    > 1 | import { nanoid } from 'nanoid';
        | ^

node: v14.17.5
nanoid 3.3.4
@ai
Copy link
Owner

ai commented May 25, 2022

The problem is with Jest config (ESM support is very tricky with Jest). Sorry, you need to ask Jest community.

@ai ai closed this as completed May 25, 2022
@Wesley-ChannelEngine
Copy link

Wesley-ChannelEngine commented May 30, 2022

Adding nanoid to transformIgnorePatterns and moduleNameMapper to the jest.config.ts helped in my case with the same error:

import type { Config } from "@jest/types";

const esModules = ["lodash-es", "nanoid"].join("|");

const config: Config.InitialOptions = {
    ... // rest of your config
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
    moduleNameMapper: {
        "^lodash-es(/(.*)|$)": "lodash$1",
        "^nanoid(/(.*)|$)": "nanoid$1",
    }
};

export default config;

@jacquesg
Copy link

jacquesg commented Jun 7, 2022

@ai I believe both uuid and nanoid have the same problem. Please see this thread for the full explanation: microsoft/accessibility-insights-web#5421 (comment).

My understanding is that some changes are required to make it compliant?

@domharrington
Copy link

domharrington commented Jun 17, 2022

Yeah I think the issue is that Jest is now looking for package.json exports fields: https://jestjs.io/docs/upgrading-to-jest28#packagejson-exports

I dont know enough about how they work, but could be an issue with this?

nanoid/package.json

Lines 24 to 35 in fd80ce3

"exports": {
".": {
"browser": "./index.browser.js",
"default": "./index.js"
},
"./async": {
"browser": "./async/index.browser.js",
"default": "./async/index.js"
},
"./non-secure": "./non-secure/index.js",
"./package.json": "./package.json"
},

Some similar discussion happening over on the uuid issue tracker: uuidjs/uuid#616

I got it to work doing something similar to the custom jest resolver solution recommended here

@ai
Copy link
Owner

ai commented Jun 17, 2022

The problem is that Jest use browser version, but do not emulate browser stack fully.

You need a crypto polyfill.

@domharrington
Copy link

Ahh that makes sense, thanks for the fast reply! The custom jest resolver worked for me for now.

@ai
Copy link
Owner

ai commented Jun 17, 2022

I added a note about Jest c9df0ab

@ai
Copy link
Owner

ai commented Jun 17, 2022

I also created an issue in Jest jestjs/jest#12947

@Pavel910
Copy link

Pavel910 commented Jun 18, 2022

@ai I'm not sure this is only a polyfill error, because it's not really complaining about crypto, but about import. As you can see in the screenshot, I'm specifically telling him which file to import.

Tried all of the ideas in this issue, none of them worked for me. Would anyone be so kind and share their tsconfig.json and jest.config.js?

No matter what I do, I'm getting this:
CleanShot 2022-06-18 at 12 23 55

Thanks!

@jacquesg
Copy link

@Pavel910

I have the following in src/tests/resolver.js:

function Resolver(path, options) {
  return options.defaultResolver(path, {
    ...options,
    packageFilter: pkg => {
      if (pkg.name === 'nanoid') {
        // eslint-disable-next-line
        delete pkg.exports;
        // eslint-disable-next-line
        delete pkg.module;
      }
      return pkg;
    },
  });
}

module.exports = Resolver;

and finally in jest.config.ts:

  resolver: `<rootDir>/src/tests/resolver.js`,

@Pavel910
Copy link

Pavel910 commented Jun 18, 2022

@jacquesg thanks! I finally found a working combo this very minute, sharing it here if anyone runs into this issue:

The solution has 2 parts:

  1. jest.config.ts needs to process nanoid (because it's an ESM), so we add it to transformIgnorePatterns. We also MUST add the js pattern to be transformed by ts-jest (this was the missing piece for me).
  2. We need to add the crypto polyfill @ai mentioned.

CleanShot 2022-06-18 at 12 42 35

@ai
Copy link
Owner

ai commented Jun 18, 2022

Another way is to switch Jest to ESM mode by Jest docs

@jacquesg
Copy link

It is unfortunate to have these type of issues where there are multiple solutions to the same problem that IMHO should not exist. Imagine not knowing how to debug or even google this. It requires a good understanding of interactions between different systems, ESM vs CJS, browser vs node, the list goes on... (I for one do not understand all of it or necessarily "care" to understand all of it).

A zeroconf solution is really the only way this doesn't continue biting developers.

@ai
Copy link
Owner

ai commented Jun 18, 2022

A zeroconf solution is really the only way this doesn't continue biting developers

It is mostly the problem of Jest. I can recommend uvu where nanoid and ESM works very well.

Also there is vitest if you need to test React.

@jacquesg
Copy link

@ai I'll check out vitest, thank you.

@SunXinFei
Copy link

jest.mock("nanoid", () => { return {
  nanoid : ()=>{}
} });

use mock in setupTests.js of jest

@njsaugat
Copy link

njsaugat commented Dec 4, 2023

jest.mock("nanoid", () => { return {
  nanoid : ()=>{}
} });

use mock in setupTests.js of jest

This really worked for me.

@jzamituy
Copy link

jzamituy commented Mar 7, 2024

jest.mock("nanoid", () => { return {
  nanoid : ()=>{}
} });

use mock in setupTests.js of jest
@SunXinFei
Thank you this worked for me also in a nextjs app.

@jaxxreal
Copy link

jaxxreal commented Aug 8, 2024

jest.mock("nanoid", () => { return {
  nanoid : ()=>{}
} });

Thanks for the inspiration!

I have faker on a project, so made nanoid do its job:

jest.mock('nanoid', () => {
  return {
    nanoid: () => faker.string.alphanumeric(10),
  };
});

gregtatum added a commit to gregtatum/indie-web that referenced this issue Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants