vitest-dev / vitest

Next generation testing framework powered by Vite.
https://vitest.dev
MIT License
13.06k stars 1.17k forks source link

Browser tests break when importing path-to-regexp@8 and mocking modules in React tests #6540

Open Janpot opened 1 month ago

Janpot commented 1 month ago

Describe the bug

Testing a component that imports latest path-to-regexp, and that also mocks some modules.

This results in error:

TypeError: Failed to fetch dynamically imported module: http://localhost:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=0d22d530

Looking at the devtools, it seems like mock service worker is using an unintended version of path-to-regexp:

devUtils.mjs?v=0d22d530:11 [MSW] Uncaught exception in the request handler for "GET http://localhost:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=0d22d530":

TypeError: str is not iterable
    at lexer (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:52:25)
    at lexer.next (<anonymous>)
    at Iter.peek (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:110:36)
    at Iter.tryConsume (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:116:28)
    at Iter.text (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:132:29)
    at consume (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:151:27)
    at parse (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:182:22)
    at http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:282:79
    at Array.map (<anonymous>)
    at pathToRegexp (http://localhost:5173/node_modules/.vite/deps/path-to-regexp.js?v=0d22d530:282:27)

This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses

Reproduction

  1. Clone https://github.com/Janpot/vitest-path-to-regexp-repro
  2. pnpm install
  3. pnpm test

System Info

System:
    OS: macOS 14.6.1
    CPU: (8) arm64 Apple M1
    Memory: 73.31 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.17.0 - ~/.nvm/versions/node/v20.17.0/bin/node
    npm: 10.8.2 - ~/.nvm/versions/node/v20.17.0/bin/npm
    pnpm: 9.10.0 - ~/.nvm/versions/node/v20.17.0/bin/pnpm
  Browsers:
    Chrome: 128.0.6613.138
    Safari: 17.6
  npmPackages:
    @vitejs/plugin-react: ^4.3.1 => 4.3.1 
    @vitest/browser: ^2.1.1 => 2.1.1 
    vitest: ^2.1.1 => 2.1.1

Used Package Manager

pnpm

Validations

hi-ogawa commented 1 month ago

Vitest's browser mocking relies msw, which has a dependency path-to-regexp@6. It looks like user code's path-to-regexp@8 mixes up dependency versions due to Vite cjs dependency optimization.

eduardhasanaj commented 1 month ago

I am encountering same issue when using vitetest browser mode.

Failed to fetch dynamically imported module: http://localhost:5173/@fs/C:/Users/web-client/virtual:wxt-setup?import&v=1727619790892

In my case I could not use any environment such jsdom due to poor support of selectors in querySelector API. Error:

// jsdom
SyntaxError: unknown pseudo-class selector ':not([type]'
//happy-dom
DOMException: Failed to execute 'matches' on 'HTMLButtonElement': 'button:not' is not a valid selector.

This code has no errors when I run in browser.

kettanaito commented 3 weeks ago

@hi-ogawa, does that happen because MSW is not treated as ESM by Vite? Otherwise, why would Vite attempt to optimize CJS dependency?

hi-ogawa commented 3 weeks ago

does that happen because MSW is not treated as ESM by Vite? Otherwise, why would Vite attempt to optimize CJS dependency?

The issue might be a bit more complicated.

Vitest configures optimizeDeps.exclude so that msw (and its dependencies including path-to-regexp@6) won't be optimized and thus they are served to browser directly as ESM https://github.com/vitest-dev/vitest/blob/5e6de2742f329b7e65fe3dff4827b5134d777009/packages/browser/src/node/plugin.ts#L204-L205

However, when users have its own path-to-regexp@8 in their Vite app (or Vitest browser test), Vite currently picks up the one from the user, which is considered a bug on Vite https://github.com/vitejs/vite/issues/9948

I thought users can explicitly do optimizeDeps.exclude: ["path-to-regexp"] as a workaround, but it looks like the latest path-to-regexp@8 dropped "esm-ish" export "module": "dist.es2015/index.js", which was available on path-to-regexp@6, so Vite have no choice but to optimize cjs-only path-to-regexp@8, which is then deduping path-to-regexp@6 inside msw.

This was my observation so far and unfortunately I haven't found a way to make this setup work (or even workaround) yet.