nuxt / test-utils

πŸ§ͺ Test utilities for Nuxt
http://nuxt.com/docs/getting-started/testing
MIT License
286 stars 74 forks source link

Plugins not overwritten when passed as options #828

Closed lucasecdb closed 1 month ago

lucasecdb commented 2 months ago

Environment

Reproduction

https://github.com/lucasecdb/nuxt-app-pinia-repro

Install dependencies with pnpm install then run pnpm test

Describe the bug

If you configure a vue plugin using Nuxt plugins, like the following:

// plugins/pinia.ts
import { createPinia } from 'pinia';

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(createPinia())
})

Given the following component and Pinia store:

<template>
  <p>{{ date }}</p>
</template>

<script setup>
const store = useMyStore();

const date = computed(() => store.date);
</script>
// stores/my-store.ts
export const useMyStore = defineStore('my-store', () => {
  return {
    date: ref(new Date()),
  };
});

And then try to overwrite that plugin during testing:

// __tests__/my-component.test.ts
import { createPinia } from 'pinia';

it('should pass', async () => {
  await renderSuspended(MyComponent, {
    global: { plugins: [createPinia()] },
  });

  // ...
});

The instance returned by useMyStore will always use the Pinia store supplied by plugins/pinia, and not from the global.plugins parameter provided to renderSuspended.

Additional context

No response

Logs

No response

dosubot[bot] commented 2 months ago

The issue you're experiencing with Pinia store instances not being overwritten during testing with renderSuspended is likely due to how the testing setup is configured, specifically with regard to state management plugins like Pinia. The renderSuspended function, as part of the test setup, does not directly manipulate or replace state management instances. It focuses on Vue's global properties, provided values, and components, without specific handling for Pinia stores.

To address this, you'll need to manually configure or mock the Pinia store before running your tests to ensure that the mock store is used. This might involve mocking global imports or providing a mock store through the global properties or provides context in your test setup.

Additionally, there's a similar issue discussed in the @nuxt/test-utils repository, specifically in mountSuspended & createTestingPinia, which points to an existing issue on the @pinia/nuxt repository. For more detailed information and potential solutions, you might want to check out the discussion there: https://github.com/vuejs/pinia/issues/2555. This could provide further insights into how others have approached and potentially resolved similar testing challenges with Pinia in a Nuxt environment.

#### Details **Sources** - [mountSuspended & createTestingPinia](https://github.com/nuxt/test-utils/issues/737) - [src/runtime-utils/render.ts](https://github.com/nuxt/test-utils/blob/main/src/runtime-utils/render.ts) - [src/runtime-utils/mount.ts](https://github.com/nuxt/test-utils/blob/main/src/runtime-utils/mount.ts)
**About Dosu** This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a πŸ‘ if this is helpful and πŸ‘Ž if it is irrelevant.

To continue the conversation, mention @dosu.

lucasecdb commented 2 months ago

The issue occurs because on @vue/test-utils the plugins from the global properties are applied before the provides:

And since renderSuspended copies over the provided values from the global instance down to the render call, the plugin will always be overwritten back by the vueApp provided value:

https://github.com/nuxt/test-utils/blob/08ee35155579889e6c41f8abe9340d0e5f803111/src/runtime-utils/render.ts#L136

lucasecdb commented 2 months ago

The issue @dosubot linked is caused by this exact same issue, the pinia store is being overwritten by the value injected in @pinia/nuxt plugin.

Changing the test to use either mount or render (from testing-library) solves the issue because we are not reusing the context from nuxtApp.vueApp.

dosubot[bot] commented 2 months ago

To address the issue of the Pinia store instance not being overwritten during testing in your Nuxt application, you've pinpointed the core problem related to the order in which plugins and provides are applied in @vue/test-utils. Specifically, the global plugins are applied before the provides, leading to the original Pinia store being reinstated by the vueApp provided value.

For a solution, consider manually setting up or mocking the Pinia store before running your tests. This ensures the mock store is utilized instead of the default instance from plugins/pinia.ts. Here's how you can do it:

import { createPinia, setActivePinia } from 'pinia';
import { renderSuspended } from '@nuxt/test-utils/runtime';
import MyComponent from '@/components/MyComponent.vue';

it('correctly overwrites the Pinia store instance', async () => {
  const mockStore = createPinia();
  setActivePinia(mockStore);

  const options = {
    global: {
      plugins: [mockStore],
    },
  };

  const { html } = await renderSuspended(MyComponent, options);
  // Your test assertions here
});

This approach involves creating a mock Pinia store and setting it as active before the test runs. By passing this mock store in the global.plugins array of the options object to renderSuspended, you ensure that your component under test uses the mocked store.

Additionally, there are discussions and potential solutions in related threads such as mountSuspended & createTestingPinia and Unable to test component using pinia store, which might offer further insights or alternative approaches.

This method allows you to control the Pinia instance used during tests, ensuring that your tests use the intended store configuration. If you encounter any further issues or need more detailed guidance, feel free to ask.

#### Details **Sources** - [mountSuspended & createTestingPinia](https://github.com/nuxt/test-utils/issues/737) - [Unable to test component using pinia store](https://github.com/nuxt/test-utils/issues/523) - [Testing with a Pinia store best practices](https://github.com/nuxt/test-utils/issues/571) - [src/runtime-utils/render.ts](https://github.com/nuxt/test-utils/blob/main/src/runtime-utils/render.ts) - [examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts](https://github.com/nuxt/test-utils/blob/main/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts) - [examples/app-vitest-full/nuxt.config.ts](https://github.com/nuxt/test-utils/blob/main/examples/app-vitest-full/nuxt.config.ts) - [CHANGELOG.md](https://github.com/nuxt/test-utils/blob/main/CHANGELOG.md) - [src/runtime-utils/mount.ts](https://github.com/nuxt/test-utils/blob/main/src/runtime-utils/mount.ts) - [examples/app-vitest-full/plugins/inject-value.ts](https://github.com/nuxt/test-utils/blob/main/examples/app-vitest-full/plugins/inject-value.ts)
**About Dosu** This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a πŸ‘ if this is helpful and πŸ‘Ž if it is irrelevant.

To continue the conversation, mention @dosu.

danielroe commented 1 month ago

This is now resolved in the latest @vue/test-utils: https://github.com/vuejs/test-utils/releases/tag/v2.4.6.