У меня есть приложение Nuxt3, которое использует модуль @nuxtjs/supabase для аутентификации и хранения, и я пытаюсь добавить к нему модульное тестирование Vitest.
В моем app.vue у меня есть следующее:
<script lang = "ts" setup>
import type { Database } from '@/types';
const supabase = useSupabaseClient<Database>();
supabase.auth.onAuthStateChange(async (event) => {
if (event === 'SIGNED_OUT') {
await supabase.removeAllChannels();
}
});
</script>
<template>
<div class = "bg-white h-full">
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>
И в моем tests/unit/app.spec.ts:
import { mountSuspended } from '@nuxt/test-utils/runtime';
import { describe, expect, it, vi } from 'vitest';
import app from '@/app.vue';
// const supabaseClientMock = vi.fn(() => ({
// auth: {
// onAuthStateChange: vi.fn(),
// },
// realtime: {
// removeAllChannels: vi.fn(),
// },
// }));
// vi.stubGlobal('useSupabaseClient', supabaseClientMock);
// vi.mock('#imports', () => {
// return {
// useNuxtApp: () => ({
// $supabase: {
// client: {
// auth: {
// onAuthStateChange: vi.fn(),
// signIn: vi.fn(),
// signOut: vi.fn(),
// },
// realtime: {
// removeAllChannels: vi.fn(),
// },
// // Add other properties and methods as needed
// },
// },
// }),
// };
// });
const supabaseClientMock = vi.fn(() => ({
auth: {
onAuthStateChange: vi.fn(),
},
realtime: {
removeAllChannels: vi.fn(),
},
}));
vi.stubGlobal('useSupabaseClient', supabaseClientMock);
describe('App', async () => {
describe('App Component', async () => {
it('renders the app', async () => {
const wrapper = await mountSuspended(app);
expect(wrapper.exists())
.toBe(true);
});
});
});
Как видите, я пробовал много разных вариантов, однако продолжаю сталкиваться с одной и той же ошибкой:
TypeError: Cannot read properties of undefined (reading 'auth')
❯ setup app.vue:7:7
5| supabase.auth.onAuthStateChange(async (event) => {
6| if (event === 'SIGNED_OUT') {
7| await supabase.removeAllChannels();
| ^
8| }
9| });
❯ wrappedSetup node_modules/@nuxt/test-utils/dist/runtime-utils/index.mjs:93:26
❯ clonedComponent.setup node_modules/@nuxt/test-utils/dist/runtime-utils/index.mjs:141:48
❯ callWithErrorHandling node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:195:19
❯ setupStatefulComponent node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7572:25
❯ setupComponent node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7533:36
❯ mountComponent node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5861:7
❯ processComponent node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5827:9
❯ patch node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5306:11
❯ ReactiveEffect.componentUpdateFn [as fn] node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5971:11
This error originated in "tests/unit/app.spec.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The latest test that might've caused the error is "renders the app". It might mean one of the following:
- The error was thrown, while Vitest was running this test.
- If the error occurred after the test had been completed, this was the last documented test before it was thrown.
В идеале я мог бы переместить это в файл для использования в чем-то вроде setupFiles вместо того, чтобы издеваться над Supabase в каждом тестовом файле, однако, если мне просто нужно сначала заставить его работать!
Чего бы это ни стоило, я получил его корректно работающую версию в другом тестовом файле, который использует Supabase во вспомогательной функции, но я не могу воспроизвести ее в спецификации приложения:
vi.mock('@/utils/helpers', () => {
const buildImageSrc = vi.fn((url: string) => url);
return {
useSupabaseClient: () => ({
storage: {
from: () => ({
getPublicUrl: (url: string) => ({
data: { publicUrl: url },
}),
}),
},
}),
buildImageSrc,
};
});





Для потомков, я думаю, я понял это:
const { useSupabaseClient } = vi.hoisted(() => {
return {
useSupabaseClient: vi.fn()
.mockImplementation(() => {
return {
auth: {
onAuthStateChange: vi.fn(),
},
realtime: {
removeAllChannels: vi.fn(),
},
storage: {
from: () => ({
getPublicUrl: (url: string) => ({
data: { publicUrl: url },
}),
}),
},
};
}),
};
});
mockNuxtImport('useSupabaseClient', () => useSupabaseClient);