简体   繁体   中英

Vitest: $setup.t is not a function (Vite, Vue 3, TypeScript, vue-i18n)

I'm trying to unit test a simple component that uses translations with the vue-i18n module. Here are the files used:

src/i18n/index.ts

import { createI18n } from 'vue-i18n';
    
export function loadLanguages() {
  const context = import.meta.globEager('./languages/*.ts');

  const languages: Record<string, any> = {};

  const langs = Object.keys(context);
  for (const key of langs) {
    if (key === './index.ts') return;
    const { lang } = context[key];
    const name = key.replace(/(\.\/languages\/|\.ts)/g, '');
    languages[name] = lang;
  }

  return languages;
}

export const i18n = createI18n({
  legacy: false,
  locale: 'es',
  fallbackLocale: 'es',
  messages: loadLanguages(),
  missingWarn: false,
  fallbackWarn: false,
});

export const i18nGlobal = i18n.global;

export function setLanguage(locale: string) {
  i18n.global.locale.value = locale;
}

src/i18n/hooks/helper.ts

import { useI18n } from 'vue-i18n';
import { watch } from 'vue';
import { useGetters } from '@store-common/hooks/helpers';

export const useI18nGlobal = () => useI18n({ useScope: 'global' });

export const useI18nLocal = () => {
  const { locale } = useI18nGlobal();

  const local = useI18n({
    locale: locale.value as string,
    inheritLocale: true,
    useScope: 'local',
  });

  const { getLocale } = useGetters();

  watch(getLocale, (loc: string) => {
    local.locale.value = loc;
  });

  return local;
};

src/components/Example.vue

<template>
  <div>
    {{ greeting }}
    {{ t('common.btn.send') }}
    {{ translate }}
  </div>
</template>

<script setup lang="ts">
import { useI18nLocal } from '@i18n/hooks/helper';

const { t } = useI18nLocal();

const greeting = 'Vue and TDD';
const translate = t('common.btn.send');
</script>

src/components/ tests /Example.spec.ts

import { shallowMount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import Example from '../Example.vue';

describe('Example.vue', () => {
  it('Traducciones i18n', () => {
    const wrapper = shallowMount(Example);
    expect(wrapper.text()).toMatch('Vue and TDD');
    expect(wrapper.vm.translate).toMatch('Enviar');
  });
});

package.json

{
  "name": "PROJECT_NAME",
  "version": "1.0.0",
  "scripts": {
    ...
    "test:unit": "vitest --environment jsdom --dir src/ --coverage",
    ...
  },
}

When I launch the yarn test:unit command, declared in the package.json , the console gives me the following error:

cmd> yarn test:unit

yarn run v1.22.11
warning package.json: No license field
warning ..\..\package.json: No license field
$ vitest --environment jsdom --dir src/ --coverage Example

 DEV  v0.25.5 C:/Users/jgomezle/projects/HISVAR_FRONT
      Coverage enabled with c8

 ❯ src/shared/components/__tests__/Example.spec.ts (1)
   ❯ Example.vue (1)
     × Traducciones i18n

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
 FAIL  src/shared/components/__tests__/Example.spec.ts > Example.vue > Traducciones i18n
TypeError: $setup.t is not a function
 ❯ Proxy._sfc_render src/shared/components/Example.vue:20:207

 ❯ renderComponentRoot node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:891:44
 ❯ ReactiveEffect.componentUpdateFn [as fn] node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5570:57
 ❯ ReactiveEffect.run node_modules/@vue/runtime-core/node_modules/@vue/reactivity/dist/reactivity.cjs.js:191:25
 ❯ instance.update node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5684:56
 ❯ setupRenderEffect node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5698:9
 ❯ mountComponent node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5480:9
 ❯ processComponent node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5438:17
 ❯ patch node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5042:21
 ❯ ReactiveEffect.componentUpdateFn [as fn] node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5577:21



⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
 Test Files  1 failed (1)
      Tests  1 failed (1)
   Start at  11:13:05
   Duration  3.60s (transform 1.27s, setup 1ms, collect 956ms, tests 29ms)

As we can see in the error TypeError: $setup.t is not a function it seems that it cannot find the t function of the i18n module to perform the translations.

I have tried mocking the t function in multiple ways when doing shallowMount , but none of them have worked for me and the error remains the same. These are all the ways I've tried:

const wrapper = shallowMount(Example, {
      mocks: {
        t: (str) => str,
        setup: {
          t: (str) => str,
        },
        $setup: {
          t: (str) => str,
        },
      },
      global: {
        mocks: {
          useI18n: {
            t: (msg) => msg,
            $t: (msg) => msg,
          },
          $setup: {
            t: (msg) => msg,
            $t: (msg) => msg,
          },
          setup: {
            t: (msg) => msg,
            $t: (msg) => msg,
          },
          t: (msg) => msg,
          $t: (msg) => msg,
        },
        // plugins: [i18n],
      },
    });

I have also tried these configurations, but the result is still the same:

import { config } from '@vue/test-utils';

config.global.mocks = {
  $t: (msg) => msg,
  t: (msg) => msg,
  $setup: {
    $t: (msg) => msg,
    t: (msg) => msg,
  },
  setup: {
    $t: (msg) => msg,
    t: (msg) => msg,
  },
};

Lol, found your question when was looking for solution:)

Luckily, this stuff worked for me. Remembered i saw somebody with the same problem and found you again:)

The key concept you and I were missing is that when you mount component in tests, it has nothing in relation with your application. It's just an isolated, incapsulated piece of code. So, if you want to support translations (and not just mock them), you have to manually initialize i18n plugin on each test run (or globally for all tests, like in the answer in my link).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM