CI / skip-ci-check (pull_request) Successful in 30s
CI / python-lint (pull_request) Successful in 32s
CI / docker-ci (pull_request) Successful in 31s
CI / secret-scan (pull_request) Successful in 39s
CI / viewer-unit (pull_request) Successful in 1m44s
CI / admin-unit (pull_request) Successful in 2m1s
CI / e2e (pull_request) Failing after 2m2s
Nest Identify/Auto-Match/Modify under /people with redirects; reject blur/extreme pose at Process; Name-cluster on Identify; axe smoke tests; helpers to rotate DEV admin password and seed match_decisions.
85 lines
3.1 KiB
TypeScript
85 lines
3.1 KiB
TypeScript
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<z.infer<typeof TokenResponse>>('/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);
|
|
});
|
|
});
|