import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import App from '../src/App' const fetchMock = vi.fn() beforeEach(() => { fetchMock.mockReset() vi.stubGlobal('fetch', fetchMock) window.history.replaceState(null, '', '/') }) function json(status: number, body: unknown) { return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' }, }) } function loggedInServer() { fetchMock.mockImplementation((url: string) => { if (url === '/api/setup') return Promise.resolve(json(200, { needed: false })) if (url === '/api/me') return Promise.resolve(json(200, { user: { id: 1, username: 'sam', isAdmin: true } })) return Promise.resolve(json(404, { error: 'not_found' })) }) } describe('routing', () => { it('shows the setup form when the server needs setup', async () => { fetchMock.mockImplementation((url: string) => url === '/api/setup' ? Promise.resolve(json(200, { needed: true })) : Promise.resolve(json(404, {})) ) render() await waitFor(() => expect(screen.getByRole('heading', { name: /welcome/i })).toBeTruthy()) }) it('shows the login form when unauthenticated', async () => { fetchMock.mockImplementation((url: string) => { if (url === '/api/setup') return Promise.resolve(json(200, { needed: false })) return Promise.resolve(json(401, { error: 'unauthorized' })) }) render() await waitFor(() => expect(screen.getByRole('heading', { name: /sign in/i })).toBeTruthy()) }) it('lands on Library with the tab bar when authenticated', async () => { loggedInServer() render() await waitFor(() => expect(screen.getByRole('tab', { name: /library/i })).toBeTruthy()) for (const tab of ['Library', 'Scan', 'Add', 'Settings']) { expect(screen.getByRole('tab', { name: new RegExp(tab, 'i') })).toBeTruthy() } }) it('login form authenticates and enters the app', async () => { fetchMock.mockImplementation((url: string) => { if (url === '/api/setup') return Promise.resolve(json(200, { needed: false })) if (url === '/api/me') return Promise.resolve(json(401, { error: 'unauthorized' })) if (url === '/api/login') { return Promise.resolve(json(200, { user: { id: 1, username: 'sam', isAdmin: true } })) } return Promise.resolve(json(404, {})) }) render() await waitFor(() => expect(screen.getByLabelText(/username/i)).toBeTruthy()) await userEvent.type(screen.getByLabelText(/username/i), 'sam') await userEvent.type(screen.getByLabelText(/password/i), 'password123') await userEvent.click(screen.getByRole('button', { name: /sign in/i })) await waitFor(() => expect(screen.getByRole('tab', { name: /library/i })).toBeTruthy()) }) it('setup form creates the admin account and enters the app', async () => { fetchMock.mockImplementation((url: string, init?: RequestInit) => { if (url === '/api/setup' && (!init || !init.method || init.method === 'GET')) { return Promise.resolve(json(200, { needed: true })) } if (url === '/api/setup' && init?.method === 'POST') { return Promise.resolve(json(200, { user: { id: 1, username: 'boss', isAdmin: true } })) } return Promise.resolve(json(404, {})) }) render() await waitFor(() => expect(screen.getByLabelText(/username/i)).toBeTruthy()) await userEvent.type(screen.getByLabelText(/username/i), 'boss') await userEvent.type(screen.getByLabelText(/password/i), 'password123') await userEvent.click(screen.getByRole('button', { name: /create/i })) await waitFor(() => expect(screen.getByRole('tab', { name: /library/i })).toBeTruthy()) }) it('tab navigation switches pages', async () => { loggedInServer() render() await waitFor(() => expect(screen.getByRole('tab', { name: /settings/i })).toBeTruthy()) await userEvent.click(screen.getByRole('tab', { name: /settings/i })) await waitFor(() => expect(screen.getByRole('heading', { name: /settings/i })).toBeTruthy()) expect(screen.getByText(/account/i)).toBeTruthy() }) })