import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, expect, it, vi, beforeEach } from 'vitest'
import './i18n'
import App from './App'
import * as api from './api'
vi.mock('./api')
const statsBody = { entities: 7, sessions: 42, utterances: 133, anomalies: 1024 }
const codexBody = { entities: [] }
function mockFetch() {
vi.stubGlobal(
'fetch',
vi.fn(async (input: RequestInfo | URL) => {
const url = String(input)
const body = url.includes('/api/stats') ? statsBody : codexBody
return new Response(JSON.stringify(body), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}),
)
}
describe('App', () => {
beforeEach(() => {
mockFetch()
vi.mocked(api.me).mockRejectedValue(new Error('not authenticated'))
})
it('renders the landing page with the Quantumancy title', async () => {
render()
await waitFor(() =>
expect(screen.getAllByText('QUANTUMANCY').length).toBeGreaterThan(0),
)
})
it('shows live veil activity from /api/stats', async () => {
render()
// counters animate up from 0 over ~900ms — wait for the final values
await waitFor(
() => {
const values = Array.from(document.querySelectorAll('.stat-value')).map(
(el) => el.textContent,
)
expect(values).toContain('42')
expect(values).toContain('7')
},
{ timeout: 4000 },
)
})
it('takes anonymous visitors straight to the séance, with no sign-in wall', async () => {
// The open door: a stranger must reach the séance itself. SeancePage
// provisions a wanderer for them on arrival; /enter is an upgrade path
// (keep your codex, pair a device), never a gate.
render()
await waitFor(() =>
expect(screen.getAllByText('QUANTUMANCY').length).toBeGreaterThan(0),
)
const cta = screen.getByRole('button', { name: /begin the séance/i })
await userEvent.click(cta)
await waitFor(() => expect(window.location.pathname).toBe('/seance'))
})
})