// @vitest-environment jsdom import { beforeEach, describe, expect, it, vi } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; // import userEvent from '@testing-library/user-event'; import { AuthProvider, useAuth } from './AuthContext'; import { tableNameProfiles, tableNameUsers } from '../lib/const'; const { getSessionMock, signOutMock, fromMock, usersSingleMock, profilesLimitMock, profilesInsertMock, unsubscribeMock, tradingAuthState } = vi.hoisted(() => ({ getSessionMock: vi.fn(), signOutMock: vi.fn(), fromMock: vi.fn(), usersSingleMock: vi.fn(), profilesLimitMock: vi.fn(), profilesInsertMock: vi.fn(), unsubscribeMock: vi.fn(), tradingAuthState: { user: { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' } as any, isLoading: false, logout: vi.fn() } })); vi.mock('../lib/tradingAuth', () => ({ TradingAuthProvider: ({ children }: { children: React.ReactNode }) => children, useTradingAuth: () => tradingAuthState })); vi.mock('../lib/supabaseClient', () => ({ supabase: { auth: { getSession: getSessionMock, signOut: signOutMock }, from: fromMock } })); const Probe = () => { const auth = useAuth(); return (
{String(auth.loading)}
{auth.user?.id || 'none'}
{auth.profile?.role || 'none'}
); }; describe('AuthContext DOM behavior', () => { beforeEach(() => { getSessionMock.mockReset(); signOutMock.mockReset(); fromMock.mockReset(); usersSingleMock.mockReset(); profilesLimitMock.mockReset(); profilesInsertMock.mockReset(); unsubscribeMock.mockReset(); tradingAuthState.user = { id: 'user-1', email: 'sarah@example.com', role: 'admin', name: 'Sarah Algo' }; tradingAuthState.isLoading = false; tradingAuthState.logout.mockReset(); signOutMock.mockResolvedValue({ error: null }); usersSingleMock.mockResolvedValue({ data: { user_id: 'user-1', first_name: 'Sarah', last_name: 'Algo', email: 'sarah@example.com', role: 'admin', trade_enable: true }, error: null }); profilesLimitMock.mockResolvedValue({ data: [], error: null }); profilesInsertMock.mockResolvedValue({ error: null }); fromMock.mockImplementation((table: string) => { if (table === tableNameUsers) { const usersBuilder: any = { select: vi.fn(() => usersBuilder), eq: vi.fn(() => usersBuilder), single: usersSingleMock }; return usersBuilder; } if (table === tableNameProfiles) { const profilesBuilder: any = { select: vi.fn(() => profilesBuilder), eq: vi.fn(() => profilesBuilder), limit: profilesLimitMock, insert: profilesInsertMock }; return profilesBuilder; } return { select: vi.fn(), eq: vi.fn(), single: vi.fn(), limit: vi.fn(), insert: vi.fn() }; }); }); it('loads session/profile, ensures default profile, and cleans up subscription', async () => { getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'user-1' } } } }); const dispatchSpy = vi.spyOn(window, 'dispatchEvent'); render( ); await waitFor(() => { expect(screen.getByTestId('loading')).toHaveTextContent('false'); expect(screen.getByTestId('user')).toHaveTextContent('user-1'); expect(screen.getByTestId('role')).toHaveTextContent('admin'); }); expect(profilesInsertMock).toHaveBeenCalledTimes(1); expect(profilesInsertMock.mock.calls[0][0]).toEqual([ expect.objectContaining({ user_id: 'user-1', name: 'My First Strategy' }) ]); expect(dispatchSpy).toHaveBeenCalled(); expect((dispatchSpy.mock.calls[0][0] as Event).type).toBe('profiles-updated'); }, 20000); it('handles no initial session gracefully', async () => { tradingAuthState.user = null; getSessionMock.mockResolvedValue({ data: { session: null } }); render(); await waitFor(() => { expect(screen.getByTestId('loading')).toHaveTextContent('false'); expect(screen.getByTestId('user')).toHaveTextContent('none'); }); expect(usersSingleMock).not.toHaveBeenCalled(); }); it('handles auth state changes with no session', async () => { getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); const { rerender } = render(); await waitFor(() => { expect(screen.getByTestId('user')).toHaveTextContent('u1'); }); tradingAuthState.user = null; rerender(); await waitFor(() => { expect(screen.getByTestId('user')).toHaveTextContent('none'); expect(screen.getByTestId('role')).toHaveTextContent('none'); }); }); it('logs error when profile fetch fails', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' }; getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); usersSingleMock.mockResolvedValue({ data: null, error: { message: 'Profile Not Found' } }); render(); await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith('Error fetching user profile:', expect.objectContaining({ message: 'Profile Not Found' })); }); consoleSpy.mockRestore(); }); it('handles unexpected errors in fetchProfile', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' }; getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); usersSingleMock.mockImplementation(() => { throw new Error('Crashed'); }); render(); await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith('Unexpected error fetching profile:', expect.any(Error)); }); consoleSpy.mockRestore(); }); it('handles unexpected errors in ensureDefaultProfile', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); tradingAuthState.user = { id: 'u1', email: 'u1@example.com', role: 'member', name: 'U One' }; getSessionMock.mockResolvedValue({ data: { session: { user: { id: 'u1' } } } }); profilesLimitMock.mockImplementation(() => { throw new Error('Limit Error'); }); render(); await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith('[Auth] Error ensuring default profile:', expect.any(Error)); }); consoleSpy.mockRestore(); }); });