Duplicate directory names break module mocking
Describe the bug
When mocking a module where the source path contains a module called app and the source is located inside a directory called /app then the mocking stops working.
Consider a project like this:
./app.js <-- module to be mocked
./app.test.js <-- mocks the module
When checked out to /home/app (or /different) the mocking works and the tests pass. When checked out to
/app (or /a or /ap) the tests do not pass because the mocking does not work.
Reproduction
https://github.com/ArloL/vitest-mocking-reproduction
System Info
System:
OS: macOS 12.5.1
CPU: (10) arm64 Apple M1 Pro
Memory: 129.59 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 18.8.0 - /opt/homebrew/bin/node
Yarn: 1.22.19 - /opt/homebrew/bin/yarn
npm: 8.18.0 - /opt/homebrew/bin/npm
Browsers:
Firefox: 104.0.1
Safari: 15.6.1
npmPackages:
vitest: ^0.22.1 => 0.22.1
Used Package Manager
yarn
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
I think this is duplicate of https://github.com/vitest-dev/vitest/issues/1196
Thanks for the pointer. The issues do seem related. Apparently I did not search thoroughly enough - sorry for that.
When I update the vitest version in https://github.com/prowe/vitest-mock-bug everything works as expected - so I guess #1196 can be closed.
Nonetheless that sample helped me to further reduce my sample.
I tested additional working directory name combinations:
- a ❌
- aa ✅
- ab ✅
- ap ❌
- apa ✅
- apb ✅
- app ❌
- b ✅
- c ✅
- d ✅
- e ✅
- f ✅
- g ✅
- h ✅
- work ✅
- foo ✅
- bar ✅
So, the problem is here:
https://github.com/vitest-dev/vitest/blob/aa7eb7d14bf5a5df43e67c2078f035da454f7e31/packages/vitest/src/runtime/mocker.ts#L145
It blindly replaces path, so you get:
- /app/app.js
+ /app.js
- /app.js
+ .js
Thank you so much for taking the time to look into this and releasing a fix this fast.
I can confirm that my current reproduction state is fixed with 0.23.1.
Sadly when I move ./app.js and ./app.test.js into a directory called ./app/ the tests still fail. I have updated the repository accordingly.
I did some more testing. The mocking breaks when the filesystem location and the source code structure match e.g. like this /theRoot/aSubDir/theRoot/aSubDir/moreDirs/app.js. I updated my repository to make it a bit easier to understand.
I also tried building vitest inside a docker container to test my assumption but I couldn't get pnpm run build to work.
Dockerfile:
FROM docker.io/library/node:18@sha256:8a45c95c328809e7e10e8c9ed5bf8374620d62e52de1df7ef8e71a9596ec8676
WORKDIR /test
RUN npm --global install pnpm
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . ./
RUN pnpm run build
RUN pnpm run test:run
WORKDIR ./test/vite-config
RUN pnpm run test
.dockerignore:
**/node_modules
**/dist
I am no pnpm expert - so I might just be using it wrong.
Part of the error message:
…
#11 2.663 packages/vite-node build: src/server.ts(4,25): error TS2307: Cannot find module 'debug' or its corresponding type declarations.
#11 2.664 packages/vite-node build: [!] (plugin dts) Error: Failed to compile. Check the logs above.
#11 2.664 packages/vite-node build: src/server.ts
#11 2.670 packages/vite-node build: Error: Failed to compile. Check the logs above.
#11 2.671 packages/vite-node build: at error (/test/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/shared/rollup.js:198:30)
#11 2.671 packages/vite-node build: at throwPluginError (/test/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/shared/rollup.js:21919:12)
#11 2.671 packages/vite-node build: at Object.error (/test/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/shared/rollup.js:22641:20)
#11 2.671 packages/vite-node build: at Object.error (/test/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/shared/rollup.js:22096:42)
#11 2.674 packages/vite-node build: at Object.transform (/test/node_modules/.pnpm/[email protected]_ihkqvyh37tzxtjmovjyy2yrv7m/node_modules/rollup-plugin-dts/dist/rollup-plugin-dts.cjs:1618:26)
#11 2.674 packages/vite-node build: at /test/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/shared/rollup.js:22848:40
#11 2.675 packages/ws-client build: Failed
#11 2.676 undefined
#11 2.676 /test/packages/ws-client:
#11 2.676 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL @vitest/[email protected] build: `rimraf dist && rollup -c`
#11 2.676 Exit status 1
#11 2.676 WARN Local package.json exists, but node_modules missing, did you mean to install?
#11 2.684 ELIFECYCLE Command failed with exit code 1.
I updated my repository to make it a bit easier to understand.
This is the function that determines mock path:
https://github.com/vitest-dev/vitest/blob/95da4d66933db46da368d5d66f422afd26fba4c1/packages/vite-node/src/utils.ts#L49
When root is /home/path, It can receive paths:
/app.js/home/path/app.js/@fs/home/path/app.js
In all cases it should return /app.js. Try debugging it there.
Thanks for the pointer 👍
I'll write down my current state of investigations:
pathFromRoot is working as expected - but it is being called twice.
With root=/foo and path=/foo/foo/app.js this is what happens:
mocker.requestWithMock- mocker#L337:
mocker.resolveMocksmocker.mockPathnormalizePathwith/foo/foo/app.jsand returns/foo/app.js
- mocker#L340:
normalizePathwith/foo/app.jsand returns/app.js
- mocker#L337:
At least as far as I can tell.
- mocker#L340:
normalizePathwith/foo/app.jsand returns/app.js
dep here can be /foo/app.js and /foo/foo/app.js, so maybe we need to path.resolve(root, dep) before passing to normalizePath? The only problem is that it can also be /@fs/foo/foo/app.js, so we need to strip it down.
This issue is no longer reproducible with 0.26.0
Thanks!