wouter icon indicating copy to clipboard operation
wouter copied to clipboard

How to use V3 with Jest?

Open justingrant opened this issue 1 year ago • 3 comments

I'm porting a project from wouter V2 to wouter V3. The new version only ships ESM. Has anyone figured out how to get V3 working with Jest? Currently I'm getting this error:

Must use import to load ES Module:  <redacted path>/node_modules/wouter/esm/index.js

I'm using NODE_OPTIONS=--experimental-vm-modules when running Jest, as recommended by the Jest docs.

But I couldn't figure out the magic set of options that would allow Jest to import wouter.

justingrant avatar Feb 12 '24 22:02 justingrant

Add this to your jest.config.js

transformIgnorePatterns: [`/node_modules/(?!wouter)`],

stanlemon avatar Feb 18 '24 18:02 stanlemon

@stanlemon - This config change didn't change the errors we get when trying to run jest on a project that uses wounter V3.

If we run NODE_OPTIONS=--experimental-vm-modules jest, then the error we get is Must use import to load ES Module: /<my root dir here>/node_modules/wouter/esm/index.js.

But if we just run jest, without NODE_OPTIONS=--experimental-vm-modules then I get a Jest encountered an unexpected token error on the first line of /node_modules/wouter/esm/index.js. Which makes sense because it's expecting CJS but got ESM import.

Adding "transformIgnorePatterns": ["/node_modules/(?!wouter)"] doesn't seem to change this result at all.

Any idea what we should try next? It'd be great to see a working example from another project. Do you know of any TypeScript project in GitHub, CodeSandbox, etc. that uses wouter V3 and Jest successfully?

Note that the rest of our tests run fine with or without --experimental-vm-modules arg and with or without transformIgnorePatterns. It's only wouter V3 (or presumably any other ESM-only library used by Jest) that will fail.

If it's helpful to spot what's going wrong, here's our jest config:

  "jest": {
    "moduleNameMapper": {
      "^_/(.*).(woff|woff2)$": "<rootDir>/assets/test/fileMock.ts",
      "^_/(.*)$": "<rootDir>/src/$1",
      "^wouter$": "<rootDir>/node_modules/wouter/esm",
      "^uuid$": "<rootDir>/node_modules/uuid/dist/index.js",
      "^react-markdown$": "<rootDir>/node_modules/react-markdown/react-markdown.min.js"
    },
    "setupFilesAfterEnv": [
      "<rootDir>/jest-setup.ts"
    ],
    "transformIgnorePatterns": ["/node_modules/(?!wouter)"],
    "snapshotResolver": "<rootDir>/snapshot-resolver.ts",
    "testEnvironment": "jsdom"
  }

Here's our dependencies, in case it's helpful:

  "dependencies": {
    "@lingui/core": "^4.7.0",
    "@lingui/macro": "^4.7.0",
    "@radix-ui/react-slider": "^1.1.2",
    "@react-three/drei": "^9.97.5",
    "@react-three/fiber": "^8.15.16",
    "@tanstack/react-table": "^8.11.8",
    "@types/d3-color": "^3.1.3",
    "@types/d3-scale-chromatic": "^3.0.3",
    "@visx/visx": "^3.8.0",
    "axios": "^1.6.7",
    "boring-avatars": "^1.10.1",
    "d3-color": "^3.1.0",
    "d3-scale-chromatic": "^3.0.0",
    "dayjs": "^1.11.10",
    "jotai": "^2.6.4",
    "path-to-regexp": "^6.2.1",
    "polished": "^4.3.1",
    "ramda": "^0.29.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-dropzone": "^14.2.3",
    "react-hook-form": "^7.50.1",
    "react-icons": "^5.0.1",
    "react-markdown": "^9.0.1",
    "react-query": "^3.39.3",
    "react-select": "^5.8.0",
    "rehype-highlight": "^7.0.0",
    "remark-gfm": "^4.0.0",
    "styled-components": "^6.1.8",
    "three": "^0.161.0",
    "uplot": "^1.6.30",
    "uplot-react": "^1.1.5",
    "uuid": "^9.0.1",
    "wouter": "^3.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.23.9",
    "@babel/plugin-transform-runtime": "^7.23.9",
    "@babel/preset-env": "^7.23.9",
    "@babel/preset-react": "^7.23.3",
    "@babel/preset-typescript": "^7.23.3",
    "@lingui/babel-plugin-extract-messages": "^4.7.0",
    "@lingui/babel-preset-react": "^2.9.2",
    "@lingui/cli": "^4.7.0",
    "@mdx-js/mdx": "^3.0.0",
    "@storybook/addon-actions": "^7.6.14",
    "@storybook/addon-essentials": "^7.6.14",
    "@storybook/addon-interactions": "^7.6.14",
    "@storybook/addon-links": "^7.6.14",
    "@storybook/react": "^7.6.14",
    "@storybook/react-webpack5": "^7.6.14",
    "@storybook/testing-library": "^0.2.2",
    "@testing-library/dom": "^9.3.4",
    "@testing-library/jest-dom": "^6.4.2",
    "@testing-library/react": "^14.2.1",
    "@testing-library/user-event": "^14.5.2",
    "@types/jest": "^29.5.12",
    "@types/ramda": "^0.29.10",
    "@types/react": "^18.2.55",
    "@types/react-dom": "^18.2.19",
    "@types/three": "^0.161.2",
    "@types/uuid": "^9.0.8",
    "@types/webpack": "^5.28.5",
    "@typescript-eslint/eslint-plugin": "^7.0.1",
    "@typescript-eslint/parser": "^7.0.1",
    "babel-loader": "^9.1.3",
    "babel-plugin-macros": "^3.1.0",
    "babel-plugin-styled-components": "^2.1.4",
    "babel-runtime": "^6.26.0",
    "copy-webpack-plugin": "^12.0.2",
    "core-js": "^3.35.1",
    "css-loader": "^6.10.0",
    "eslint": "^8.56.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-json": "^3.1.0",
    "eslint-plugin-prettier": "^5.1.3",
    "eslint-plugin-react": "^7.33.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-storybook": "^0.6.15",
    "file-loader": "^6.2.0",
    "fork-ts-checker-webpack-plugin": "^9.0.2",
    "html-webpack-plugin": "^5.6.0",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "jest-styled-components": "^7.2.0",
    "knip": "^5.0.0",
    "msw": "^1.3.2",
    "prettier": "^3.2.5",
    "shader-loader": "^1.3.1",
    "storybook": "^7.6.14",
    "style-loader": "^3.3.4",
    "ts-loader": "^9.5.1",
    "ts-node": "^10.9.2",
    "typescript": "^5.2.2",
    "webpack-cli": "^5.1.4",
    "worker-loader": "^3.0.8"
  },

And here's our tsconfig.json:

{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
    "esModuleInterop": true,
    "jsx": "react",
    "module": "es6",
    "lib": ["dom", "es2022"],
    "moduleResolution": "node",
    "noImplicitAny": false,
    "noImplicitReturns": true,
    "outDir": "./build",
    "paths": {
      "_/*": ["src/*"]
    },
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "ES2022"
  },
  "exclude": ["node_modules", "build"]
}

justingrant avatar Feb 23 '24 06:02 justingrant

@justingrant Take a look at https://github.com/stanlemon/javascript/tree/main/apps/template for a working example of wouter + jest. You can find my jest and babel configs in that same repo.

stanlemon avatar Feb 24 '24 16:02 stanlemon

In case somebody using pnpm, you should set

transformIgnorePatterns: [`/node_modules/.pnpm/(?!wouter)`],

eggcllnt avatar May 26 '24 13:05 eggcllnt