buckets/frontend/src/auth/guards.test.tsx

103 lines
2.9 KiB
TypeScript

import { render, screen } from '@testing-library/react'
import { delay, http, HttpResponse } from 'msw'
import { MemoryRouter, Route, Routes } from 'react-router'
import { describe, expect, it } from 'vitest'
import { server } from '../test/server'
import { AuthProvider } from './AuthProvider'
import { RequireAuth, RequireGuest } from './guards'
/**
* Minimal route tree exercising both guards, mirroring the real router
* skeleton: a guest-only /login and an auth-only app placeholder at /.
*/
function GuardedApp() {
return (
<AuthProvider>
<Routes>
<Route element={<RequireGuest />}>
<Route path="/login" element={<div>LOGIN</div>} />
</Route>
<Route element={<RequireAuth />}>
<Route path="/" element={<div>SCENARIOS</div>} />
</Route>
</Routes>
</AuthProvider>
)
}
describe('RequireAuth', () => {
it('redirects an unauthenticated user to /login', async () => {
server.use(
http.get('http://localhost/api/me', () =>
HttpResponse.json({ message: 'unauthorized' }, { status: 401 }),
),
)
render(
<MemoryRouter initialEntries={['/']}>
<GuardedApp />
</MemoryRouter>,
)
expect(await screen.findByText('LOGIN')).toBeInTheDocument()
expect(screen.queryByText('SCENARIOS')).not.toBeInTheDocument()
})
it('renders children for an authenticated user', async () => {
server.use(
http.get('http://localhost/api/me', () =>
HttpResponse.json({ id: 'u1', email: 'a@b.com' }),
),
)
render(
<MemoryRouter initialEntries={['/']}>
<GuardedApp />
</MemoryRouter>,
)
expect(await screen.findByText('SCENARIOS')).toBeInTheDocument()
expect(screen.queryByText('LOGIN')).not.toBeInTheDocument()
})
})
describe('RequireGuest', () => {
it('redirects an authenticated user away from /login', async () => {
server.use(
http.get('http://localhost/api/me', () =>
HttpResponse.json({ id: 'u1', email: 'a@b.com' }),
),
)
render(
<MemoryRouter initialEntries={['/login']}>
<GuardedApp />
</MemoryRouter>,
)
expect(await screen.findByText('SCENARIOS')).toBeInTheDocument()
expect(screen.queryByText('LOGIN')).not.toBeInTheDocument()
})
})
describe('auth probe loading state (no flash)', () => {
it('does not flash protected or login content while the auth probe is pending', async () => {
server.use(
http.get('http://localhost/api/me', async () => {
await delay(50)
return HttpResponse.json({ id: 'u1', email: 'a@b.com' })
}),
)
render(
<MemoryRouter initialEntries={['/']}>
<GuardedApp />
</MemoryRouter>,
)
expect(screen.queryByText('SCENARIOS')).not.toBeInTheDocument()
expect(screen.queryByText('LOGIN')).not.toBeInTheDocument()
expect(await screen.findByText('SCENARIOS')).toBeInTheDocument()
})
})