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,
},
};