import { z } from 'zod'; import { test, expect } from '../fixtures'; import { BUDGET_MS, expectWithinBudget } from '../timing-budgets'; import { DEFAULT_ADMIN_BASE_URL } from '../env-defaults'; const TokenResponse = z.object({ access_token: z.string().min(1), refresh_token: z.string().min(1), }); /** * FastAPI-admin UI smoke: core review workflows load without error. * Uses E2E_API_USERNAME/PASSWORD (admin FastAPI user — not NextAuth viewer creds). */ test.describe('admin review pages @smoke', () => { const adminBaseUrl = process.env.PLAYKIT_ADMIN_BASE_URL || DEFAULT_ADMIN_BASE_URL; test('identify, auto-match, and approve pages load for admin', async ({ page, api, timings, }) => { const username = process.env.E2E_API_USERNAME || ''; const password = process.env.E2E_API_PASSWORD || ''; test.skip(!username || !password, 'E2E_API_USERNAME/PASSWORD required for admin UI'); await timings.measure('admin_login', async () => { const tokens = await api.post>('/api/v1/auth/login', { body: { username, password }, expectedStatus: 200, schema: TokenResponse, }); // Seed the admin SPA session (same keys as AuthContext) — more reliable than // driving the login form on the shared CI runner. await page.goto(`${adminBaseUrl}/login`); await page.evaluate( ({ access, refresh }) => { localStorage.setItem('access_token', access); localStorage.setItem('refresh_token', refresh); }, { access: tokens.data.access_token, refresh: tokens.data.refresh_token, }, ); await page.goto(`${adminBaseUrl}/`); await expect(page.getByRole('button', { name: /Log\s*out/i })).toBeVisible({ timeout: 30_000, }); }); expectWithinBudget(timings, 'admin_login', BUDGET_MS.uiLogin); await timings.measure('admin_identify', async () => { await page.goto(`${adminBaseUrl}/people/identify`); await expect(page.getByRole('heading', { name: /Identify/i })).toBeVisible({ timeout: 20_000, }); await expect(page.getByRole('button', { name: /Identify Faces/i }).first()).toBeVisible(); }); expectWithinBudget(timings, 'admin_identify', BUDGET_MS.uiAction); await timings.measure('admin_auto_match', async () => { await page.goto(`${adminBaseUrl}/people/auto-match`); await expect(page.getByRole('heading', { name: /Auto-Match/i })).toBeVisible({ timeout: 20_000, }); await expect( page.getByRole('button', { name: /Run Auto-Match|No Matches Available|Processing/i, }), ).toBeVisible(); }); expectWithinBudget(timings, 'admin_auto_match', BUDGET_MS.uiAction); await timings.measure('admin_approve', async () => { await page.goto(`${adminBaseUrl}/approve-identified`); await expect(page.getByRole('heading', { name: /Approve Identified|User-identified faces/i })).toBeVisible({ timeout: 20_000, }); }); expectWithinBudget(timings, 'admin_approve', BUDGET_MS.uiAction); }); });