103 lines
2.9 KiB
TypeScript
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()
|
|
})
|
|
})
|