import { expect, test } from "@playwright/test"; import { createServer, type ViteDevServer } from "vite"; import { resolve } from "node:path"; let vite: ViteDevServer; let webUrl: string; 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()); test("TDD-WP1-ADM-001 renders the isolated admin login and hands success to /admin", async ({ page }) => { await page.route("**/api/v1/admin-auth/login/send", (route) => route.fulfill({ contentType: "application/json", status: 200, body: JSON.stringify({ challenge_expires_at: "2026-07-28T12:10:00.000Z", registration_id: "00000000-0000-4000-8000-000000000007", resend_available_at: "2026-07-28T12:01:00.000Z", status: "verification_sent", }), })); await page.route("**/api/v1/admin-auth/login/complete", (route) => route.fulfill({ contentType: "application/json", status: 200, body: JSON.stringify({ audience: "admin", status: "authenticated" }), })); await page.goto(`${webUrl}/admin/login`); await expect(page.getByRole("heading", { name: "管理员邮箱验证码登录" })).toBeVisible(); await expect(page.locator(".auth-art")).toHaveCount(0); await expect(page.getByRole("link", { name: "返回普通用户登录" })).toHaveAttribute("href", "/"); await page.getByRole("textbox", { name: "管理员邮箱" }).fill("admin-ui@example.invalid"); const sendButton = page.getByRole("button", { name: "获取验证码" }); const widthBefore = (await sendButton.boundingBox())?.width; await sendButton.click(); const codeInput = page.getByRole("textbox", { name: "验证码" }); await expect(codeInput).toBeVisible(); expect((await sendButton.boundingBox())?.width).toBe(widthBefore); await codeInput.fill("418205"); await page.getByRole("button", { name: "登录后台" }).click(); await expect(page).toHaveURL(`${webUrl}/admin`); }); test("TDD-WP1-ADM-001 shows a generic allowlist rejection without exposing identity state", async ({ page }) => { await page.route("**/api/v1/admin-auth/login/send", (route) => route.fulfill({ contentType: "application/json", status: 409, body: JSON.stringify({ error: { code: "AUTH_ENTRY_REJECTED", correlation_id: "00000000-0000-4000-8000-000000000008", details: { field_errors: [{ field: "email", message_key: "admin.auth.not_allowed" }] }, message_key: "auth.entry_rejected", }, }), })); await page.goto(`${webUrl}/admin/login`); await page.getByRole("textbox", { name: "管理员邮箱" }).fill("not-admin@example.invalid"); await page.getByRole("button", { name: "获取验证码" }).click(); await expect(page.getByRole("alert")).toContainText("无法使用管理员入口"); await expect(page.getByRole("alert")).not.toContainText("普通用户"); await expect(page.getByRole("alert")).not.toContainText("白名单"); });