import { expect, test } from '@playwright/test'; // #38 — ADMIN DB CRUD panel. READ-ONLY on purpose: specs share the CI DB across retries, so no // insert/update/delete here (the mutation endpoints are covered by unit tests in lib/db/admin.test.ts). interface AdminColumnDto { name: string; type: string; pk: number } interface AdminTableDto { name: string; columns: AdminColumnDto[]; count: number } interface AdminRowsDto { rows: Record[]; total: number } test('ADMIN button opens the panel and lists every table (#38)', async ({ page }) => { await page.goto('/'); // The ADMIN button sits in the header next to the SECTOR SCAN badge. await page.getByRole('button', { name: 'ADMIN' }).click(); await expect(page.getByText('ADMIN // DB CRUD')).toBeVisible(); // Cross-check against the API: every table it lists must render in the panel (and vice versa). const tables = (await (await page.request.get('/api/admin/tables')).json()) as AdminTableDto[]; expect(tables.length).toBeGreaterThanOrEqual(10); for (const t of tables) { await expect(page.getByRole('button', { name: new RegExp(`^${t.name}\\b`) })).toBeVisible(); } }); test('opening a table renders its rows from the API (#38)', async ({ page }) => { await page.goto('/'); await page.getByRole('button', { name: 'ADMIN' }).click(); // saves always has exactly one row (single-player by design) — deterministic across retries. const api = (await (await page.request.get('/api/admin/tables/saves/rows')).json()) as AdminRowsDto; expect(api.total).toBeGreaterThan(0); await page.getByRole('button', { name: /^saves\b/ }).click(); // Column headers come from the API's column list. for (const col of api.rows[0] ? Object.keys(api.rows[0]) : []) { await expect(page.locator('table th', { hasText: col })).toBeVisible(); } // Row count matches what the API reports, and a cell shows real data from it. const body = page.locator('table tbody tr'); await expect(body).toHaveCount(api.rows.length); await expect(page.locator('table tbody').getByText(String(api.rows[0].seed))).toBeVisible(); });