Как издеваться над Supabase в Vitest?

У меня есть приложение 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,
  };
});
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
111
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для потомков, я думаю, я понял это:

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);

Другие вопросы по теме