import { mkdirSync } from "node:fs"; import { resolve } from "node:path"; import { expect, test, type Page } from "@playwright/test"; import { createServer, type ViteDevServer } from "vite"; import { adminOverviewFixture } from "../fixtures/wp6-01-admin-overview.js"; let vite: ViteDevServer; let webUrl: string; const adminSession = { acknowledged_private_content_notice_version: null, admin: { role: "super_admin", status: "active", user_id: "00000000-0000-4000-8000-000000000601" }, audience: "admin", authenticated: true, csrf_token: "csrf-admin-shell-fixture-000000000000000000000000000000000", current_private_content_notice_version: null, expires_at: "2026-09-02T09:30:00.000Z", notice_acknowledged: false, }; const navigation = [ ["总览", "/admin"], ["用户与点数", "/admin/users"], ["邀请码", "/admin/invites"], ["模型", "/admin/models"], ["素材", "/admin/assets"], ["内部预览", "/admin/preview"], ["生成记录", "/admin/generations"], ["服务与存储", "/admin/services-storage"], ["审计", "/admin/audit"], ] as const; test.beforeAll(async () => { vite = await createServer({ configFile: resolve("apps/web/vite.config.ts"), root: resolve("apps/web"), server: { host: "127.0.0.1", port: 0 }, }); await vite.listen(); const address = vite.httpServer?.address(); if (!address || typeof address === "string") throw new Error("Vite did not expose a test port."); webUrl = `http://127.0.0.1:${address.port}`; }); test.afterAll(async () => vite.close()); async function routeActiveAdmin(page: Page) { await page.route("**/api/v1/admin-auth/session", (route) => route.fulfill({ body: JSON.stringify(adminSession), contentType: "application/json", status: 200, })); await page.route("**/api/v1/admin/overview", (route) => route.fulfill({ body: JSON.stringify(adminOverviewFixture), contentType: "application/json", status: 200, })); } test("TDD-WP6-ADM-001-role-and-summary renders the protected nine-entry admin shell", async ({ page }) => { const requests: string[] = []; page.on("request", (request) => requests.push(request.url())); await routeActiveAdmin(page); await page.goto(`${webUrl}/admin`); await expect(page.getByRole("heading", { level: 2, name: "运营总览" })).toBeVisible(); const sidebar = page.getByRole("navigation", { name: "后台主导航" }); await expect(sidebar).toBeVisible(); for (const [name, href] of navigation) { await expect(sidebar.getByRole("link", { name, exact: true })).toHaveAttribute("href", href); } expect(Math.round((await sidebar.boundingBox())?.width ?? 0)).toBe(216); await expect(page.getByText("4 / 10", { exact: true })).toBeVisible(); await expect(page.getByText("待人工核对 1", { exact: true })).toBeVisible(); await expect(page.getByText("85.0%", { exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "有异常", exact: true })).toBeVisible(); await expect(page.locator("body")).not.toContainText(/forbidden|example\.invalid|api[_ -]?key|完整提示词/i); expect(requests.some((url) => /prompt|private-content|image-content/i.test(url))).toBe(false); const evidenceRoot = process.env.DADA_EVIDENCE_DIR_ADMIN; if (evidenceRoot) { const screenshotDirectory = resolve(evidenceRoot, "screenshots"); mkdirSync(screenshotDirectory, { recursive: true }); await page.screenshot({ fullPage: true, path: resolve(screenshotDirectory, "admin-overview.png") }); } }); test("TDD-WP6-ADM-001-role-and-summary keeps ordinary and preview sessions outside admin", async ({ page }) => { let overviewCalls = 0; await page.route("**/api/v1/admin-auth/session", (route) => route.fulfill({ body: JSON.stringify({ error: { code: "AUTH_SESSION_INVALID", subject: "preview_user" } }), contentType: "application/json", status: 401, })); await page.route("**/api/v1/admin/overview", (route) => { overviewCalls += 1; return route.fulfill({ body: "null", contentType: "application/json", status: 401 }); }); await page.goto(`${webUrl}/admin`); await expect(page).toHaveURL(`${webUrl}/admin/login`); await expect(page.getByRole("heading", { name: "管理员邮箱验证码登录" })).toBeVisible(); expect(overviewCalls).toBe(0); await expect(page.getByText("DADA ADMIN", { exact: true })).toHaveCount(0); const evidenceRoot = process.env.DADA_EVIDENCE_DIR_ADMIN; if (evidenceRoot) { const screenshotDirectory = resolve(evidenceRoot, "screenshots"); mkdirSync(screenshotDirectory, { recursive: true }); await page.screenshot({ fullPage: true, path: resolve(screenshotDirectory, "admin-denied.png") }); } }); test("TDD-WP6-ADM-001-role-and-summary ejects a disabled admin when the session is rechecked", async ({ page }) => { let active = true; await page.route("**/api/v1/admin-auth/session", (route) => route.fulfill(active ? { body: JSON.stringify(adminSession), contentType: "application/json", status: 200, } : { body: JSON.stringify({ error: { code: "AUTH_SESSION_INVALID" } }), contentType: "application/json", status: 401, })); await page.route("**/api/v1/admin/overview", (route) => route.fulfill({ body: JSON.stringify(adminOverviewFixture), contentType: "application/json", status: 200, })); await page.goto(`${webUrl}/admin`); await expect(page.getByRole("heading", { level: 2, name: "运营总览" })).toBeVisible(); active = false; await page.evaluate(() => window.dispatchEvent(new Event("dada:session-invalid"))); await expect(page).toHaveURL(`${webUrl}/admin/login`); });