diff --git a/tests/components/render-helpers.tsx b/tests/components/render-helpers.tsx index 42bbf9b39..777a2d00c 100644 --- a/tests/components/render-helpers.tsx +++ b/tests/components/render-helpers.tsx @@ -10,36 +10,112 @@ import { HelmetProvider } from "react-helmet-async"; import { vi } from "vitest"; // --- Supabase mock --- -vi.mock("@/integrations/supabase/client", () => ({ - supabase: { - auth: { - getSession: vi.fn().mockResolvedValue({ data: { session: null }, error: null }), - onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } }), - getUser: vi.fn().mockResolvedValue({ data: { user: null }, error: null }), - }, - from: vi.fn().mockReturnValue({ - select: vi.fn().mockReturnThis(), - insert: vi.fn().mockReturnThis(), - update: vi.fn().mockReturnThis(), - delete: vi.fn().mockReturnThis(), - eq: vi.fn().mockReturnThis(), - neq: vi.fn().mockReturnThis(), - in: vi.fn().mockReturnThis(), - order: vi.fn().mockReturnThis(), - limit: vi.fn().mockReturnThis(), - single: vi.fn().mockResolvedValue({ data: null, error: null }), - maybeSingle: vi.fn().mockResolvedValue({ data: null, error: null }), - then: vi.fn().mockResolvedValue({ data: [], error: null }), - }), - functions: { - invoke: vi.fn().mockResolvedValue({ data: null, error: null }), +// Issue #59 fix: builder chainable completo + auth.signOut/refreshSession/mfa. +// Antes faltavam `.like()`, `.ilike()`, `.gte()`, `.lte()`, etc., causando +// "supabase.from(...).select(...).like is not a function" em testes de admin. +vi.mock("@/integrations/supabase/client", () => { + // Builder retornado por `.from(table)` — todos os métodos retornam o próprio + // builder (this) para permitir chain arbitrário, e `single`/`maybeSingle`/`then` + // resolvem com `{ data, error }` no shape do supabase-js 2.x. + const builderFactory = () => { + const builder: Record = {}; + const chainMethods = [ + "select", "insert", "update", "delete", "upsert", + "eq", "neq", "gt", "gte", "lt", "lte", + "like", "ilike", "in", "is", "not", "or", "and", + "match", "contains", "containedBy", "overlaps", + "filter", "order", "limit", "range", "abortSignal", + "csv", "explain", "rollback", "returns", + ]; + for (const m of chainMethods) { + builder[m] = vi.fn().mockReturnValue(builder); + } + // Terminais — resolvem com { data, error }. + builder.single = vi.fn().mockResolvedValue({ data: null, error: null }); + builder.maybeSingle = vi.fn().mockResolvedValue({ data: null, error: null }); + builder.then = vi.fn((onFulfilled?: (v: { data: unknown[]; error: null }) => unknown) => + Promise.resolve({ data: [], error: null }).then(onFulfilled), + ); + return builder; + }; + + return { + supabase: { + auth: { + getSession: vi.fn().mockResolvedValue({ data: { session: null }, error: null }), + onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } }), + getUser: vi.fn().mockResolvedValue({ data: { user: null }, error: null }), + getClaims: vi.fn().mockResolvedValue({ data: { claims: null }, error: null }), + signInWithPassword: vi.fn().mockResolvedValue({ data: null, error: null }), + signInWithOAuth: vi.fn().mockResolvedValue({ data: null, error: null }), + signUp: vi.fn().mockResolvedValue({ data: null, error: null }), + signOut: vi.fn().mockResolvedValue({ error: null }), + refreshSession: vi.fn().mockResolvedValue({ data: { session: null }, error: null }), + resetPasswordForEmail: vi.fn().mockResolvedValue({ data: null, error: null }), + updateUser: vi.fn().mockResolvedValue({ data: { user: null }, error: null }), + mfa: { + getAuthenticatorAssuranceLevel: vi + .fn() + .mockResolvedValue({ data: { currentLevel: "aal1", nextLevel: "aal1" }, error: null }), + listFactors: vi.fn().mockResolvedValue({ data: { totp: [], all: [] }, error: null }), + enroll: vi.fn().mockResolvedValue({ data: null, error: null }), + challenge: vi.fn().mockResolvedValue({ data: null, error: null }), + verify: vi.fn().mockResolvedValue({ data: null, error: null }), + unenroll: vi.fn().mockResolvedValue({ data: null, error: null }), + }, + }, + from: vi.fn(builderFactory), + functions: { + invoke: vi.fn().mockResolvedValue({ data: null, error: null }), + }, + channel: vi.fn().mockReturnValue({ + on: vi.fn().mockReturnThis(), + subscribe: vi.fn().mockReturnThis(), + unsubscribe: vi.fn().mockReturnThis(), + }), + removeChannel: vi.fn(), + removeAllChannels: vi.fn(), + rpc: vi.fn().mockResolvedValue({ data: null, error: null }), + storage: { + from: vi.fn().mockReturnValue({ + upload: vi.fn().mockResolvedValue({ data: null, error: null }), + download: vi.fn().mockResolvedValue({ data: null, error: null }), + remove: vi.fn().mockResolvedValue({ data: null, error: null }), + list: vi.fn().mockResolvedValue({ data: [], error: null }), + getPublicUrl: vi.fn().mockReturnValue({ data: { publicUrl: "" } }), + createSignedUrl: vi.fn().mockResolvedValue({ data: { signedUrl: "" }, error: null }), + }), + }, }, - channel: vi.fn().mockReturnValue({ - on: vi.fn().mockReturnThis(), - subscribe: vi.fn().mockReturnThis(), - }), - removeChannel: vi.fn(), - rpc: vi.fn().mockResolvedValue({ data: null, error: null }), + }; +}); + +// --- @/hooks/admin mock (Issue #59) --- +// Auth.tsx importa `useDevGate` + `useIPValidation` daqui; AdminLayout importa +// vários outros. Mock genérico cobre todos sem precisar atualizar test files. +vi.mock("@/hooks/admin", () => ({ + useDevGate: vi.fn().mockReturnValue({ isAllowed: false, isDev: false }), + useIPValidation: vi.fn().mockReturnValue({ + validateIPForAuthenticatedUser: vi.fn().mockResolvedValue({ isAllowed: true }), + logLoginAttempt: vi.fn(), + fetchCurrentIP: vi.fn().mockResolvedValue("0.0.0.0"), + }), + useAllowedIPs: vi.fn().mockReturnValue({ ips: [], isLoading: false, refetch: vi.fn() }), + useIPValidationConfig: vi.fn().mockReturnValue({ config: null, isLoading: false }), +})); + +// --- @/services/authService mock (Issue #59) --- +// AuthContext.tsx importa authService.signOut + outros. Mock cobre todos os +// métodos para evitar "authService.signOut is not a function" em testes que +// não fazem mock explícito. +vi.mock("@/services/authService", () => ({ + authService: { + signIn: vi.fn().mockResolvedValue({ data: null, error: null }), + signOut: vi.fn().mockResolvedValue({ error: null }), + fetchAAL: vi.fn().mockResolvedValue({ currentAAL: "aal1", nextAAL: "aal1", hasMFA: false }), + queryRoles: vi.fn().mockResolvedValue({ data: [], error: null }), + fetchProfile: vi.fn().mockResolvedValue({ data: null, error: null }), + updateLastLogin: vi.fn().mockResolvedValue({ data: null, error: null }), }, }));