vitest-dev / vitest

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

server.deps.inline not mocking dependency of dependency #3936

Open cjoecker opened 1 year ago

cjoecker commented 1 year ago

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

grinspins commented 1 year ago

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.

KholdStare commented 1 year ago

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?

KholdStare commented 1 year ago

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.

sheremet-va commented 1 year ago

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']
      }
    }
  }
}
mumumilk commented 1 year ago

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)

KholdStare commented 1 year ago

@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.

cjoecker commented 11 months ago

@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(),
    };
});
KholdStare commented 11 months ago

@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.

markmckimbv commented 10 months ago

@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!

cjoecker commented 10 months ago

@markmckimbv nope. I still have the issue.

markmckimbv commented 10 months ago

@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.

sagargurtu commented 6 months ago

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.

linspw commented 5 months ago

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"

kristof-mattei commented 3 months ago

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.

ekuzu commented 2 months ago

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();
});