Closed ronaldkonjer closed 11 months ago
same problem here. I can't seem to set up an environment where I can have the vitest and i18n modules running at the same time 😅
I've had a similar issue with a slightly different setup where a different @vue/test-utils
was being called during a mount
function, so it did not have the global mocks.
I've solved it by setting an alias in the resolve
section of my config file:
//vitest.config.ts
resolve: {
alias: [
{
find: '@vue/test-utils',
replacement: '/node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js'
}
]
}
Does that work for you?
So I added your suggested resolve alias and changed the package of the mountSuspended import. And now mountSuspended has not thrown the SyntaxError: Need to install with app.use
function error.
Although my tests pass now I still have some unhandled errors thrown by Vitest which seem to originate in the Nuxt layout component.
TypeError: default[props.name] is not a function
❯ setup node_modules/.pnpm/nuxt@3.6.5_@types+node@20.4.5_eslint@8.46.0_typescript@5.1.6/node_modules/nuxt/dist/app/components/layout.js:25:76
26| type: [String, Boolean, Object],
27| default: null
28| }
| ^
29| },
30| setup(props, context) {
❯ callWithErrorHandling node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:156:18
❯ setupStatefulComponent node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7190:25
❯ setupComponent node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7151:36
❯ mountComponent node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5555:7
❯ processComponent node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5521:9
❯ patch node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5007:11
❯ ReactiveEffect.componentUpdateFn [as fn] node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5664:11
❯ ReactiveEffect.run node_modules/.pnpm/@vue+reactivity@3.3.4/node_modules/@vue/reactivity/dist/reactivity.cjs.js:182:19
❯ instance.update node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5770:51
I remove the alias again to determine if it has anything to do with this error being thrown. But it points out that in my config changing the import package did the trick, not adding the alias you suggested.
import { mountSuspended } from 'vitest-environment-nuxt/utils'
=>
import { mountSuspended } from 'nuxt-vitest/utils'
If anyone has an idea what is causing this error is thrown while running any vitest test. Please enlighten me.
I am also using @nuxtjs/i18n
package for translation and when I run my tests, I get this error:
SyntaxError: Need to install with `app.use` function
❯ Module.createCompileError ../node_modules/.pnpm/@intlify+message-compiler@9.3.0-beta.27/node_modules/@intlify/message-compiler/dist/message-compiler.node.mjs:78:19
❯ createI18nError ../node_modules/.pnpm/vue-i18n@9.3.0-beta.27/node_modules/vue-i18n/dist/vue-i18n.mjs:107:34
105| [I18nErrorCodes.UNEXPECTED_RETURN_TYPE]: 'Unexpected return type in composer',
106| [I18nErrorCodes.INVALID_ARGUMENT]: 'Invalid argument',
107| [I18nErrorCodes.MUST_BE_CALL_SETUP_TOP]: 'Must be called at the top of a `setup` function',
| ^
108| [I18nErrorCodes.NOT_INSTALLED]: 'Need to install with `app.use` function',
109| [I18nErrorCodes.UNEXPECTED_ERROR]: 'Unexpected error',
❯ Module.useI18n ../node_modules/.pnpm/vue-i18n@9.3.0-beta.27/node_modules/vue-i18n/dist/vue-i18n.mjs:2291:15
❯ setup components/global/ToolbarAccountMenu.vue:8:7
❯ callWithErrorHandling ../node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:156:18
❯ setupStatefulComponent ../node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7190:25
❯ setupComponent ../node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7151:36
❯ mountComponent ../node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5555:7
❯ processComponent ../node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5521:9
❯ patch ../node_modules/.pnpm/@vue+runtime-core@3.3.4/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5007:11
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: {
"code": 24,
"domain": undefined,
}
I am getting this error. Still not fixed !
Same problem here. I have tried many alternatives to be able to test components but none of them work correctly. I believe that component testing is essential for development of any type, not having a way to perform this type of testing is a huge blocker. @danielroe Do you have information about official support for this feature or what you recommend to solve this?
Just stumbled across. I've managed to get some successful i18n tests.
I stumbled for a while on only being able to access the i18n key when I had a requirement to test the actual value.
The repo is public if it helps anyone. There's still lots I'm trying to figure out myself.
Here's my setup.
// vitest.config.ts
import { fileURLToPath } from "node:url";
import { defineVitestConfig } from "nuxt-vitest/config";
export default defineVitestConfig({
test: {
globals: true,
include: "**/*.nuxt.spec.ts",
environment: "nuxt",
environmentOptions: {
nuxt: {
rootDir: fileURLToPath(new URL("./", import.meta.url)),
},
},
setupFiles: ["./tests/setupFiles/i18n.ts"],
},
});
// tests/setupFiles/i18n.ts
import { config } from "@vue/test-utils";
import { createI18n } from "vue-i18n";
import messages from "@/locales/";
import { useRootStore } from "@/stores/store.root";
const rootStore = useRootStore();
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: rootStore.locale,
locales: rootStore.locales,
fallbackLocale: rootStore.fallbackLocale,
messages: messages,
});
config.global.plugins.push(i18n);
// Header.nuxt.spec.t
import { mountSuspended } from "nuxt-vitest/utils";
import { describe, it, expect } from "vitest";
import Header from "./Header.vue";
import { useAccountStore } from "@/stores/store.account";
import { useRootStore } from "@/stores/store.root";
let initialPropsData = {
someProp: "value1",
};
let wrapper;
const wrapperFactory = (propsData = {}) => {
const mockPropsData = Object.keys(propsData).length > 0 ? propsData : initialPropsData;
return mountSuspended(Header, {
props: mockPropsData,
});
};
describe("Header", () => {
const accountStore = useAccountStore();
const rootStore = useRootStore();
it("Mounts", async () => {
expect(useAppConfig()).toBeTruthy();
});
it("Component i18n html", async () => {
wrapper = await wrapperFactory();
const textCheck = wrapper.find("h1");
expect(textCheck.html()).toMatchInlineSnapshot('"<h1 data-v-b96573a8=\\"\\" class=\\"text-header-large text-color-orange\\">[en] Header text from i18n dynamic imports</h1>"');
});
it("Shared i18n text", async () => {
wrapper = await wrapperFactory();
const textCheck = wrapper.find("[data-test-id='shared-text-test']");
expect(textCheck.text()).toEqual("[en] Sample shared title entry");
});
it("Store value entry", async () => {
wrapper = await wrapperFactory();
const textCheck = wrapper.find("[data-test-id='store-test']");
expect(textCheck.text()).toEqual("someString value");
});
it("Store value updated", async () => {
wrapper = await wrapperFactory();
rootStore.someString = "Some new value";
expect(rootStore.someString).toBe("Some new value");
await nextTick();
const textCheck = wrapper.find("[data-test-id='store-test']");
expect(textCheck.text()).toEqual("Some new value");
});
it("Store value patched", async () => {
wrapper = await wrapperFactory();
rootStore.$patch({ someString: "Another new value" });
expect(rootStore.someString).toBe("Another new value");
await nextTick();
const textCheck = wrapper.find("[data-test-id='store-test']");
expect(textCheck.text()).toEqual("Another new value");
});
it("should render default props within nuxt suspense", async () => {
const props = {
someProp: "value1",
};
wrapper = await wrapperFactory(props);
const textCheck = wrapper.find("[data-test-id='props-test']");
expect(textCheck.text()).toEqual("value1");
});
it("should render store signed out within nuxt suspense", async () => {
wrapper = await wrapperFactory();
accountStore.signedIn = false;
expect((accountStore.signedIn = false)).toBeFalsy();
await nextTick();
const storeCheck = wrapper.find("[data-test-id='account-state-test']");
expect(storeCheck.text()).toEqual("[en] Signed out");
});
it("should render store signed in within nuxt suspense", async () => {
wrapper = await wrapperFactory();
accountStore.signedIn = true;
expect((accountStore.signedIn = true)).toBeTruthy();
await nextTick();
const storeCheck = wrapper.find("[data-test-id='account-state-test']");
expect(storeCheck.text()).toEqual("[en] Signed in");
});
});
If someone is still experiencing this, would you provide a reproduction?
I've opened a sample PR here with an runtime test with i18n which seems to be working fine: https://github.com/nuxt/test-utils/pull/633
Would you be able to provide a reproduction? 🙏
This issue was closed because it was open for 7 days without a reproduction.
@danielroe I have a reproduction in a public repository, WordPress/openverse, where we're attemping to migrate to Nuxt 3 and are running into this very problem.
Unfortunately things are a bit messy there still, because we're also still working through other issues: https://github.com/WordPress/openverse/pull/3571
You can reproduce the issue by running pnpm run test:unit v-image-cell
inside the frontend
directory.
What I'm seeing when I run vitest in the frontend
directory is that globalThis.useNuxtApp().$i18n
has a working vuei18n instance. I can pass it keys and I get back the right strings. However, if I retrieve vueApp
from globalThis.__unctx__.get("nuxt-app").vueApp
and then access vueApp._context.provides[vueApp.__VUE_I18N_SYMBOL__]
, that vuei18n object has zero configured instances, it doesn't look installed. Though, I'm not sure I'm looking at this right. I'm not familiar with the guts of vue-i18n and have just been digging through the test-utils code to figure out how this module works to set up the render function's context with the configured Nuxt environment.
If you'd like me to open a separate issue for this, please let me know. The exact same presentation for us as in this issue:
vue:setup
[nuxt-app] vue:setup: 259.213ms []
vue:error
[nuxt-app] vue:error: 0.816ms [
SyntaxError: Need to install with `app.use` function
at Module.createCompileError (/var/home/sara/projects/openverse/node_modules/.pnpm/@intlify+message-compiler@9.8.0/node_modules/@intlify/message-compiler/dist/message-compiler.mjs:78:19)
at createI18nError (/var/home/sara/projects/openverse/node_modules/.pnpm/vue-i18n@9.8.0_vue@3.3.12/node_modules/vue-i18n/dist/vue-i18n.mjs:105:34)
at Module.useI18n (/var/home/sara/projects/openverse/node_modules/.pnpm/vue-i18n@9.8.0_vue@3.3.12/node_modules/vue-i18n/dist/vue-i18n.mjs:2313:15)
at setup (/var/home/sara/projects/openverse/frontend/src/components/VImageCell/VImageCell.vue:138:11)
at clonedComponent.setup (/var/home/sara/projects/openverse/node_modules/.pnpm/@nuxt+test-utils@3.9.0_@testing-library+vue@8.0.1_@vitest+ui@1.1.0_@vue+test-utils@2.4.3_h3@1_prr4umkikytza6o2cai2dcbbye/node_modules/@nuxt/test-utils/dist/runtime-utils/index.mjs:215:37)
at callWithErrorHandling (/var/home/sara/projects/openverse/node_modules/.pnpm/@vue+runtime-core@3.3.12/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:156:18)
at setupStatefulComponent (/var/home/sara/projects/openverse/node_modules/.pnpm/@vue+runtime-core@3.3.12/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7285:25)
at setupComponent (/var/home/sara/projects/openverse/node_modules/.pnpm/@vue+runtime-core@3.3.12/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7246:36)
at mountComponent (/var/home/sara/projects/openverse/node_modules/.pnpm/@vue+runtime-core@3.3.12/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5643:7)
at processComponent (/var/home/sara/projects/openverse/node_modules/.pnpm/@vue+runtime-core@3.3.12/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5609:9) {
code: 27,
domain: undefined
},
{
image: [Getter/Setter],
searchTerm: [Getter/Setter],
aspectRatio: [Getter/Setter],
kind: [Getter/Setter],
relatedTo: [Getter/Setter]
},
'setup function'
]
page:finish
[nuxt-app] page:finish: 261.044ms []
cc @obulat who is the Openverse maintainer actually leading our Nuxt 3 migration effort. I'm just lending a hand today.
Actually, I was able to reproduce this inside the application: https://github.com/nuxt/test-utils/pull/673
The issue is with the useI18n
composable. Using the template (which the example didn't test either) via $t
OR using useNuxtApp().$i18n
does work. So it seems like there's some kind of vue-i18n wiring that is getting lost in the Nuxt environment construction.
Can confirm @sarayourfriend It's just happening when using
const { t } = useI18n()
after changing all my t()-function-calls to
useNuxtApp().$i18n.t()
and removing the above useI18n-composable call, the error is gone.
@renky It is indeed gone but useNuxtApp().$i18n().t()
cannot be used if you want component based translation. It can only be done using useI18n
like
const { t } = useI18n({useScope: 'local', messages: { en: { test: 'test' } } } })
i18n
component option does not work either for local scoped translations!
Spent two days on this. It's worth fixing this issue. I solved the problem by mocking the "vue-i18n" module and assigning it to global plugins
tests/setup.ts
import { beforeAll, afterAll, vi } from 'vitest'
import { ref } from 'vue'
import { config } from '@vue/test-utils'
import { createI18n } from 'vue-i18n'
beforeAll(() => {
vi.mock('vue-i18n', async (importOriginal) => {
const module = await importOriginal<typeof import('vue-i18n')>()
return {
...module,
useI18n: () => ({
locale: ref('en'),
locales: ref([
{
name: 'English',
code: 'en',
iso: 'en-GB',
file: 'en.ts',
},
]),
}),
}
})
})
afterAll(() => {
vi.restoreAllMocks()
})
// setup i18n
const i18n = createI18n({
legacy: false,
locale: 'en',
locales: [
{
name: 'English',
code: 'en',
iso: 'en-GB',
file: 'en.ts',
},
],
messages: {},
missing: (locale: string, key: string) => key,
})
config.global.plugins.push(i18n)
@martio solution work for me, thank you
@martio Solution is perfect :)
Spent two days on this. It's worth fixing this issue. I solved the problem by mocking the "vue-i18n" module and assigning it to global plugins
tests/setup.ts
import { beforeAll, afterAll, vi } from 'vitest' import { ref } from 'vue' import { config } from '@vue/test-utils' import { createI18n } from 'vue-i18n'
beforeAll(() => { vi.mock('vue-i18n', async (importOriginal) => { const module = await importOriginal<typeof import('vue-i18n')>()
return { ...module, useI18n: () => ({ locale: ref('en'), locales: ref([ { name: 'English', code: 'en', iso: 'en-GB', file: 'en.ts', }, ]), }), }
}) })
afterAll(() => { vi.restoreAllMocks() })
// setup i18n const i18n = createI18n({ legacy: false, locale: 'en', locales: [ { name: 'English', code: 'en', iso: 'en-GB', file: 'en.ts', }, ], messages: {}, missing: (locale: string, key: string) => key, })
config.global.plugins.push(i18n)
This does not work for me, is there something missing before this is doable (or am I just missing an obvious step)?
[Vue warn]: Directive "t" has already been registered in target app.
I'm setting up the nuxt-vitest module in my project. Though I'm running into some issues getting a simple test, I use mountSuspended(Logo) to test the mounting of a Logo component. This component uses i18n. Whereas when I'm using mount from Vitest the test passes.
The error I'm getting is as follows:
And while we are at it. Is there a way to test with the translated i18n messages iso the keys?
Here are some snippets from my configuration. vitest.config.js
./test/nuxt/components/Logo.nuxt.spec.ts (with mountSuspended)
./tests/unit.setup.ts
./tests/nuxt/components/Logo.nuxt.spec.ts (without mountSuspended)