import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { Auth } from './Auth' import { useAuth } from '@/context/AuthContext' import { useWorkspace } from '@/context/WorkspaceContext' vi.mock('@/context/AuthContext') vi.mock('@/context/WorkspaceContext') const mockNavigate = vi.fn() vi.mock('react-router-dom', async () => { const actual = await vi.importActual('react-router-dom') return { ...actual, useNavigate: () => mockNavigate, } }) const mockUseAuth = vi.mocked(useAuth) const mockUseWorkspace = vi.mocked(useWorkspace) describe('Auth', () => { const mockLogin = vi.fn() const mockRegister = vi.fn() beforeEach(() => { vi.clearAllMocks() mockUseAuth.mockReturnValue({ accessToken: null, isAuthenticated: false, isLoading: false, login: mockLogin, register: mockRegister, logout: vi.fn(), }) mockUseWorkspace.mockReturnValue({ workspace: null, isLoading: false, error: null, workspaceId: null, setWorkspaceId: vi.fn(), refetch: vi.fn(), children: [], selectedChildId: null, selectChild: vi.fn(), personalDataUuid: null, parentName: null, refreshContext: vi.fn(), planVersion: 0, bumpPlanVersion: vi.fn(), }) }) it('renders the login form by default, with the login tab active', () => { render() expect(screen.getByRole('heading', { name: 'Logga in' })).toBeInTheDocument() expect(screen.getByPlaceholderText('E-post')).toBeInTheDocument() expect(screen.getByPlaceholderText('Lösenord')).toBeInTheDocument() expect(screen.queryByPlaceholderText('Namn')).not.toBeInTheDocument() // Both tabs' text is unique except "Logga in", which the login-mode // submit button also renders — the tabs carry their own testids so // these queries don't collide with it. expect(screen.getByTestId('auth-tab-login')).toHaveAttribute('aria-pressed', 'true') expect(screen.getByTestId('auth-tab-register')).toHaveAttribute('aria-pressed', 'false') }) it('switches to the registration form via the tab', () => { render() fireEvent.click(screen.getByTestId('auth-tab-register')) expect(screen.getByRole('heading', { name: 'Skapa konto' })).toBeInTheDocument() expect(screen.getByPlaceholderText('Namn')).toBeInTheDocument() expect(screen.getByTestId('auth-tab-register')).toHaveAttribute('aria-pressed', 'true') expect(screen.getByTestId('auth-tab-login')).toHaveAttribute('aria-pressed', 'false') }) it('switches back to the login form via the tab', () => { render() fireEvent.click(screen.getByTestId('auth-tab-register')) fireEvent.click(screen.getByTestId('auth-tab-login')) expect(screen.getByRole('heading', { name: 'Logga in' })).toBeInTheDocument() expect(screen.queryByPlaceholderText('Namn')).not.toBeInTheDocument() }) it('clears the error banner when switching between the Log in and Create account tabs', async () => { mockLogin.mockRejectedValue(new Error('Fel användarnamn')) render() fireEvent.click(screen.getByTestId('auth-submit')) expect(await screen.findByRole('alert')).toHaveTextContent('Fel användarnamn') fireEvent.click(screen.getByTestId('auth-tab-register')) expect(screen.queryByRole('alert')).not.toBeInTheDocument() }) it('logs in with the entered credentials and navigates home', async () => { mockLogin.mockResolvedValue(undefined) render() fireEvent.change(screen.getByPlaceholderText('E-post'), { target: { value: 'a@b.com' } }) fireEvent.change(screen.getByPlaceholderText('Lösenord'), { target: { value: 'secret' } }) fireEvent.click(screen.getByTestId('auth-submit')) await waitFor(() => expect(mockLogin).toHaveBeenCalledWith({ email: 'a@b.com', password: 'secret' })) expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true }) }) it('registers with the entered details and navigates home', async () => { mockRegister.mockResolvedValue(undefined) render() fireEvent.click(screen.getByTestId('auth-tab-register')) fireEvent.change(screen.getByPlaceholderText('Namn'), { target: { value: 'Alex' } }) fireEvent.change(screen.getByPlaceholderText('E-post'), { target: { value: 'a@b.com' } }) fireEvent.change(screen.getByPlaceholderText('Lösenord'), { target: { value: 'secret' } }) fireEvent.click(screen.getByTestId('auth-submit')) await waitFor(() => expect(mockRegister).toHaveBeenCalledWith({ email: 'a@b.com', password: 'secret', displayName: 'Alex' }), ) expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true }) }) it('shows the error message from a failed login', async () => { mockLogin.mockRejectedValue(new Error('Fel användarnamn')) render() fireEvent.click(screen.getByTestId('auth-submit')) expect(await screen.findByRole('alert')).toHaveTextContent('Fel användarnamn') }) it('shows a generic error message when login rejects with a non-Error value', async () => { mockLogin.mockRejectedValue('nope') render() fireEvent.click(screen.getByTestId('auth-submit')) expect(await screen.findByRole('alert')).toHaveTextContent('Något gick fel') }) it('redirects to the dashboard once authenticated and the workspace has loaded', () => { mockUseAuth.mockReturnValue({ accessToken: 'token', isAuthenticated: true, isLoading: false, login: mockLogin, register: mockRegister, logout: vi.fn(), }) render() expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true }) }) it('does not redirect while the workspace is still loading', () => { mockUseAuth.mockReturnValue({ accessToken: 'token', isAuthenticated: true, isLoading: false, login: mockLogin, register: mockRegister, logout: vi.fn(), }) mockUseWorkspace.mockReturnValue({ workspace: null, isLoading: true, error: null, workspaceId: null, setWorkspaceId: vi.fn(), refetch: vi.fn(), children: [], selectedChildId: null, selectChild: vi.fn(), personalDataUuid: null, parentName: null, refreshContext: vi.fn(), planVersion: 0, bumpPlanVersion: vi.fn(), }) render() expect(mockNavigate).not.toHaveBeenCalled() }) })