Search code examples
javascripttypescriptvue.jsvuejs3pinia

How to mock Pinia and vue-i18n at the same time?


I'm using Vue 3's Composition API like so:

store.ts

import { ref, Ref } from 'vue';
import { defineStore } from 'pinia';

export const useStore = defineStore('store', () => {
  const isLoading: Ref<boolean> = ref(true);

  function init() {
    isLoading.value = true;
    setTimeout(() => (isLoading.value = false), 3000);
  }

  return {
    init,
    isLoading,
  };
});

Component.vue

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useStore } from './store';

const { t } = useI18n();
const store = useStore();
const { isLoading } = storeToRefs(store);
</script>

<template>
  <wb-button @click="store.init()">{{ t('init') }}</wb-button>
  <p>Loading: {{ isLoading ? 'Yes' : 'No' }}</p>
</template>

That all works fine and dandy but when I try to test stuff it gets messy. So far I have mocked vue-i18n globally like this:

testSetup.ts

import { vi } from 'vitest';

vi.mock('vue-i18n', () => ({
  useI18n: () => ({
    t: (key: string) => key,
    d: (key: string) => key,
  }),
}));

However, that works only until I try mocking the store simultaneously like this:

Component.test.ts

import { it, expect, vi } from 'vitest';
import { mount} from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import Component from './Component.vue';

const options = {
  global: {
    plugins: [
      createTestingPinia({
        createSpy: vi.fn(),
        initialState: {
          items: [],
        },
      }),
    ],
  },
};

it('works', () => {
  const wrapper = mount(Component, options);
  expect(wrapper.findComponent(Component)).toBeTruthy();
});

As soon as I add the change to the plugin configuration to enable the Pinia mock, the vue-i18n mock stops working and throws the old familiar TypeError: $setup.t is not a function. I also tried to use config.global.mocks and config.global.plugins to mock vue-i18n but all of these solutions stop working as soon as I add Pinia mocking to the tests. I think the reason is that the config.global object is somehow reset by @pinia/testing but I don't know how to prevent this behavior.

package.json

"@pinia/testing": "0.0.16",
"@types/jsdom": "21.1.1",
"@types/node": "18.16.2",
"@vue/test-utils": "2.3.2",
"jsdom": "21.1.1"
"typescript": "~4.8.4",
"vite": "4.3.3",
"vitest": "0.30.1",

Solution

  • The issue is that the createSpy property expects a function but a call was supplied. Change vi.fn() to vi.fn and everything works as expected.

    createTestingPinia({
      createSpy: vi.fn, <--
      ...
    })
    

    Source: https://pinia.vuejs.org/cookbook/testing.html#specifying-the-createspy-function