vitest
vitest copied to clipboard
server.deps.inline not mocking dependency of dependency
Describe the bug
I have the package remix-validated-form that is using the @remix-run/react dependency.
I'm mocking the function useActionData of @remix-run/react as follows:
vi.mock("@remix-run/react", async () => {
const actual = (await vi.importActual("@remix-run/react")) as any;
return {
...actual,
useActionData: vi.fn(),
};
});
Then, I have in my vitest config server.deps.inline: ["remix-validated-form"]
useActionData is being mocked in my component but not in the remix-validated-form. That's why I'm getting the error useActionData must be used within a data router that is coming from the remix-validated-form package.
Reproduction
https://codesandbox.io/p/sandbox/react-typescript-forked-vzq65s?file=%2Fsrc%2FApp.test.tsx%3A23%2C1
System Info
See code sandbox
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'm facing the same issue when trying to mock network requests that are initiated by a direct dependency.
This issue mentions deps.inline as possible solution. This option is no longer available though and server.deps.inline does not appear to help.
Here is another minimal test case to illustrate the behavior https://github.com/grinspins/vitest-nested-mock/tree/main.
I am also experiencing the same issue. @sheremet-va has something changed since the last discussion here https://github.com/vitest-dev/vitest/issues/1336 . Any pointers on how to debug locally why the mocking is not functioning? E.g. some debug output from vite or vitest that would help diagnose the issue?
Poking @sheremet-va again, as this is a huge roadblock for me and my team. We are faced with abandoning a more straightforward approach (mocking out a transitive dependency), and having to rethink how we test a certain component.
Even if you give some pointers of where to look - in config, even if I can instrument module loading/resolution/mocking in vitest itself etc. I can try and fix this issue myself, but lack of response really doesn't help.
Nothing changed. If you need to mock a dependency, every file that import this dependency should be inlined:
// need to mock "package3", this is how they are imported:
// package1->package2->package3
export default {
test: {
server: {
deps: {
inline: ['package1', 'package2']
}
}
}
}
Not sure if it helps but i've stumbled on something similar before and worked around it by hoisting my mock even more, moving my vi.mock statement to a file that runs before the test file itself (eg https://vitest.dev/config/#setupfiles)
@sheremet-va Thanks! The server.deps.inline option worked.
I think the documentation for mocking should be updated to clarify this. Currently users have to go diving through github issues to find this suggestions (at least I couldn't find it in the official documentation). The other problem is the comment was out of date: https://github.com/vitest-dev/vitest/issues/1336#issuecomment-1131419190 . deps.inline has become server.deps.inline . I swear I also saw somewhere people talking about inline being deprecated and replaced by optimization settings.
Anyway the point is it's not clear from the current documentation.
Thank you for clarifying.
@KholdStare how did you manage to make it work? I tried it as described by @sheremet-va the following without success:
I have the useActionData function that needs to be mocked from the remix-validated-form dependency.
The useActionData function is being imported as follows: remix-validated-form <- @remix-run/react <- react-router-dom <- react-router.
I have this in my vitest.config:
test: {
server:{
deps:{
inline:["@remix-run/react","react-router-dom" , "react-router" ]
}
},
}
Then, in my test, I have the following:
vi.mock("remix-validated-form", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const actual = (await vi.importActual("remix-validated-form")) as any;
return {
...actual,
useActionData: vi.fn(),
};
});
@cjoecker I used a __mocks__ directory - I am not sure if that is the reason. However, can you also see if you're including all possible libraries that may import remix-validated-form? Take a look at your yarn.lock to see which other libs might be depending on it. I had an issue where I had to "plug all the holes" for the transitive dependency to be mocked properly.
@cjoecker did you ever get this working? I'm experiencing similar issues to yourself and neither deps.inline or server.deps.inline seem to be doing the trick for me. Thanks!
@markmckimbv nope. I still have the issue.
@cjoecker thanks for confirming. I spent hours trying to get this working to no avail. Ended up giving up and changing the test to avoid the issue. Still frustrating that I couldn't get the above working.
Any updates on this? Facing the same issue. Setting server.deps.inline to true did not work.
It was working in v0.27.1 under deps.inline. It stopped working when I updated Vitest to v1.4.0 and switched to server.deps.inline.
Worked for me putting in all:
// mock for dependecyA: dependecyA -> dependencyB -> dependencyC
const dependencies = ['dependencyB', 'dependencyC']
export default defineConfig({
test: {
environment: 'happy-dom',
deps: {
optimizer: {
web: {
include: dependencies,
},
ssr: {
include: dependencies,
},
},
},
server: {
deps: {
inline: dependencies,
},
},
},
})
Versions: "vitest": "^1.5.0" "nuxt": "3.11.2"
Piling on top of this, https://stackblitz.com/~/github.com/kristof-mattei/vitest-broken-mock-repro same issue.
Putting joi (my dependency) or @sideway/address (joi's depency), or both in server.deps.inline do not change the outcome, i.e. the mock fails.
Edit: I dug into joi. It requires packages, and vitest does not support mocking required packages.
I have same problem, Our project uses @kp/react-ui and it uses react-chartjs-2 (ESM) I want to mock Chart element of react-chartjs-2. in the project
I spend 3 days to find a solution but finally reach here. Anyone has an idea?
Project/Test -> @kp/react-ui/TimeScaleBarChart ->react-chartjs-2/Chart
vitest.config.ts
const dependencies = ['@kp/react-ui', 'react-chartjs-2'];
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
deps: {
optimizer: {
web: {
include: dependencies,
},
},
},
server: {
deps: {
inline: dependencies,
},
},
environmentOptions: {
jsdom: {
resources: 'usable',
},
},
},
});
barchart.test.tsx
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { vi } from 'vitest';
import { TimeScaleBarChart } from '@kp/react-ui';
test('renders TimeScaleBarChart using mocked Chart component', () => {
vi.mock('react-chartjs-2', async () => {
console.log('MOCKED')
return {
Chart: () => '<div data-testid="test-chart">Mocked Chart</div>',
};
});
/* vi.mock('@kp/react-ui', () => ({
TimeScaleBarChart: () => 'TEST',
}));
*/
render(
<TimeScaleBarChart
data={{ datasets: [] }}
locale={undefined}
time={{}}
size={'small'}
collapsed={false}
/>,
);
screen.debug(undefined, 999999);
expect(screen.findByTestId('test-chart')).toBeInTheDocument();
});
I'm shocked this doesn't have like 100 upvotes. Seems like such an essential feature.
Nothing changed. If you need to mock a dependency, every file that import this dependency should be inlined
That doesn't seem to be accurate, see: https://github.com/s-h-a-d-o-w/vitest-transitive-repro
I hope that this repro is minimal enough. It illustrates that no matter how the dependencies are listed, it doesn't work. Even though I use the beta there, the behavior is the same with 2.1.8.
@s-h-a-d-o-w Thanks for the repro. @nivo/xxx packages are cjs on node, so mocking doesn't work as is (see "main" and "module" fields in https://publint.dev/@nivo/[email protected]). Either you can try aliasing each dependency from .cjs.js to .es.js or you may try enabling mainFields: ["module"]. Also you probably need @nivo/core installed in your project since otherwise it's not visible by Vitest directly. Here is an updated example https://stackblitz.com/edit/github-ot8rhgwh?file=vite.config.ts
For others and OP, the initial reproduction seems out-dated now. Please provide a new reproduction to review the issue :pray:
Hello @cjoecker. Please provide a minimal reproduction using a GitHub repository or StackBlitz (you can also use examples). Issues marked with needs reproduction will be closed if they have no activity within 3 days.
I have same issue with vitest 2.1.8. I've try mock fs import like @nx/devkit > nx > fs. @nx/devkit and nx is commonjs lib. I've try with this config, but it doesn't work
export default defineConfig(({ mode }) => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/packages/nx-plugin',
plugins: [nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
test: {
watch: false,
globals: true,
environment: 'node',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/packages/nx-plugin',
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
},
isolate: false,
passWithNoTests: true,
server: {
deps: {
inline: ['@nx/devkit', 'nx'],
fallbackCJS: true,
},
},
deps: {
interopDefault: true,
optimizer: {
ssr: { include: ['@nx/devkit', 'nx'] },
},
},
},
define: {
'import.meta.vitest': mode !== 'production',
},
}));
@hi-ogawa
Thank you very much! Because of your explanation, I found a solution that seems decent. But first, I would like to bring up a few other things, the first of which seems pretty problematic:
- Vitest didn't throw an error about CJS. Hence why I assumed that the problem is about transitive dependencies, not bad module definitions. (But I'm pretty sure that I have seen that error elsewhere either by vitest or vite - that require can't be used from an ES module.)
- Adding
@nivo/coreas a dependency seems less than ideal to me, since it's not directly used in the project. More importantly, it means that over time, this local dependency and the one@nivo/treemapexpects can become incompatible. - Based on the docs,
mainFieldsshould already use "module" (since "browser" doesn't exist)? Or is "main" actually the primary prop that's used? Do the docs need an update? Also - seems like overriding this could have a problematic impact on other dependencies?
Your resolution suggestion seems much more elegant to me—future-proof, transparent and implicitly solves the transitivity problem. (Getting the path is only necessary because pnpm doesn't flatten dependencies. I'm guessing with npm, it could be resolved just like @nivo/treemap.)
import { defineConfig } from "vitest/config";
import { execSync } from "node:child_process";
const nivoCorePath = execSync('pnpm why --parseable @nivo/core', { encoding: 'utf-8' }).split('\n')[1]
export default defineConfig({
test: {
environment: "jsdom",
globals: true,
alias: {
"@nivo/treemap": "@nivo/treemap/dist/nivo-treemap.es.js",
"@nivo/core": nivoCorePath + "/dist/nivo-core.es.js",
}
},
});
While this was necessary in my case because of the faulty module declarations, it makes me wonder whether such a resolution override might generally be a more straightforward solution for transitive dependencies than server.deps.inline, since there's no ambiguity about the chain of imports. Just telling vitest where to find the entry point for the dependency that's being mocked.
Based on the docs,
mainFieldsshould already use "module" (since "browser" doesn't exist)? Or is "main" actually the primary prop that's used? Do the docs need an update? Also - seems like overriding this could have a problematic impact on other dependencies?
Yes, Vite's default resolution includes module https://vite.dev/config/shared-options.html#resolve-mainfields as it's intended to run on browser, but Vitest overrides it and use main field instead, which is Node's behavior. I don't remember exactly why, but I'd assume doing the opposite as default might be more prone to get caught by bad package's module file.
https://github.com/vitest-dev/vitest/blob/f9a628438a5462436b59dd9bdeffddada19a9e81/packages/vitest/src/node/plugins/index.ts#L89-L91