I've been working on unit testing a simple component that utilizes translations with the vue-i18n module. Here's an overview of the files involved:
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",
...
},
}
While running the yarn test:unit
command specified in the package.json, I encountered the following error message:
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 ... As we can see from the error <code>TypeError: $setup.t is not a function
, it appears that thet
function from thei18n
module cannot be found to carry out the translations.I have attempted to mock the
t
function in various ways duringshallowMount
, but none have proven effective as the error persists. These are the different configurations I have 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 experimented with these configurations, but unfortunately, the issue persists:
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, }, };