import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; import { createMemoryRouter, RouterProvider, type RouteObject } from 'react-router-dom'; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; const { useSessionMock } = vi.hoisted(() => ({ useSessionMock: vi.fn(), })); vi.mock('@/lib/auth-client', () => ({ useSession: useSessionMock, })); import { AuthGuard, GuestGuard } from './guards'; interface RenderedRouter { container: HTMLDivElement; router: ReturnType; } const mountedRoots: Root[] = []; beforeAll(() => { Object.defineProperty(globalThis, 'IS_REACT_ACT_ENVIRONMENT', { configurable: true, value: true, }); }); afterAll(() => { Reflect.deleteProperty(globalThis, 'IS_REACT_ACT_ENVIRONMENT'); }); async function renderRouter( routeObjects: RouteObject[], initialEntry: string, ): Promise { const container = document.createElement('div'); document.body.append(container); const router = createMemoryRouter(routeObjects, { initialEntries: [initialEntry] }); const root = createRoot(container); mountedRoots.push(root); await act(async () => { root.render(); }); return { container, router }; } afterEach(async () => { for (const root of mountedRoots.splice(0)) { await act(async () => { root.unmount(); }); } document.body.replaceChildren(); useSessionMock.mockReset(); }); const guestRoutes: RouteObject[] = [ { path: '/login', element: , children: [{ index: true, element:

Guest page

}], }, { path: '/chat', element:

Chat page

}, ]; const authenticatedRoutes: RouteObject[] = [ { path: '/chat', element: , children: [{ index: true, element:

Private page

}], }, { path: '/login', element:

Login page

}, ]; describe('GuestGuard', () => { it('renders the guest outlet while session lookup is pending', async () => { useSessionMock.mockReturnValue({ data: null, isPending: true }); const view = await renderRouter(guestRoutes, '/login'); expect(view.container.textContent).toContain('Guest page'); expect(view.router.state.location.pathname).toBe('/login'); }); it('renders the guest outlet when no session exists', async () => { useSessionMock.mockReturnValue({ data: null, isPending: false }); const view = await renderRouter(guestRoutes, '/login'); expect(view.container.textContent).toContain('Guest page'); expect(view.router.state.location.pathname).toBe('/login'); }); it('redirects an authenticated session to chat', async () => { useSessionMock.mockReturnValue({ data: { user: { id: 'user-1' } }, isPending: false }); const view = await renderRouter(guestRoutes, '/login'); expect(view.container.textContent).toContain('Chat page'); expect(view.router.state.location.pathname).toBe('/chat'); }); }); describe('AuthGuard', () => { it('renders the existing loading treatment while session lookup is pending', async () => { useSessionMock.mockReturnValue({ data: null, isPending: true }); const view = await renderRouter(authenticatedRoutes, '/chat'); expect(view.container.textContent).toContain('Loading...'); expect(view.router.state.location.pathname).toBe('/chat'); }); it('redirects an unauthenticated visitor to login', async () => { useSessionMock.mockReturnValue({ data: null, isPending: false }); const view = await renderRouter(authenticatedRoutes, '/chat'); expect(view.container.textContent).toContain('Login page'); expect(view.router.state.location.pathname).toBe('/login'); }); it('renders the authenticated outlet when a session exists', async () => { useSessionMock.mockReturnValue({ data: { user: { id: 'user-1' } }, isPending: false }); const view = await renderRouter(authenticatedRoutes, '/chat'); expect(view.container.textContent).toContain('Private page'); expect(view.router.state.location.pathname).toBe('/chat'); }); });