Files
yingqing/core/qa/visual-parity/_admin-p9.mjs
T
seaislee1209andClaude Opus 4.8 cdfcffcecd feat(admin): Phase 9 收尾治理 — 全局项目监控 + 数据完整性体检
后端:adminpanel projects(全局项目流水线监控+状态筛)+ integrity(数据体检:无账户团队/无图商品/过期邀请码/
无文件资产/失败项目/卡 reserved 计数 + totals);AdminProjectSerializer;IsPlatformAdmin。
前端:adminApi adminProjects/integrity;Admin 治理页(体检卡片超0高亮 + 项目监控表状态筛)。
测试:adminpanel 53 单测过(项目列/筛/体检报告);无头 e2e _admin-p9.mjs 4 断言过 + 0 console error(全只读);tsc+build 绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 22:51:49 +08:00

62 lines
2.8 KiB
JavaScript

// Phase 9 e2e:收尾治理 —— 全局项目监控(列+筛)+ 数据体检(只读)。全程只读,零副作用。
import { chromium } from "playwright";
import fs from "node:fs";
import path from "node:path";
const BASE = process.env.BASE || "http://127.0.0.1:5188";
const API = process.env.API || "http://127.0.0.1:8010";
const OUT = path.resolve("output/admin");
fs.mkdirSync(OUT, { recursive: true });
async function apiLogin(u, p) {
const res = await fetch(`${API}/api/auth/login/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: u, password: p }) });
return res.json();
}
const admin = await apiLogin("admin", "admin123");
const r = { page: {}, integrity: {}, consoleErrors: [], pass: false };
const browser = await chromium.launch({ headless: true });
const p = await (async () => {
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
await ctx.addInitScript((tk) => localStorage.setItem("airshelf_token", tk), admin.token);
const pg = await ctx.newPage();
pg.on("console", (m) => { if (m.type() === "error") r.consoleErrors.push("gov:" + m.text()); });
pg.on("pageerror", (e) => r.consoleErrors.push("gov:PAGEERR:" + e.message));
return pg;
})();
await p.goto(BASE + "/admin/governance", { waitUntil: "load" });
await p.waitForSelector(".admin-app", { timeout: 12000 });
await p.waitForFunction(() => document.querySelector(".admin-table") || (document.querySelector(".empty-state.show h3") || {}).textContent !== "加载中…", { timeout: 20000 }).catch(() => {});
r.page.title = (await p.locator(".content h1").first().innerText().catch(() => "")).trim();
r.page.tabs = await p.locator(".tabs-sub .tab-sub").count();
r.page.checkBtn = (await p.locator(".page-head .actions .btn").count()) >= 1;
r.page.rows = await p.locator(".admin-table tbody tr").count();
await p.screenshot({ path: path.join(OUT, "p9-governance.png"), fullPage: true });
// 运行数据体检 → 体检卡出现
await p.locator(".page-head .actions .btn").click();
await p.waitForSelector(".gov-card", { timeout: 8000 }).catch(() => {});
await p.waitForTimeout(400);
r.integrity.cards = await p.locator(".gov-card").count();
await p.screenshot({ path: path.join(OUT, "p9-integrity.png"), fullPage: true });
// 失败筛选
await p.locator('.tabs-sub .tab-sub:has-text("失败")').click();
await p.waitForTimeout(700);
r.page.failedFilterOk = true;
await browser.close();
const checks = {
pageLoads: r.page.title === "治理" && r.page.tabs === 7 && r.page.checkBtn === true,
projectsTable: r.page.rows >= 1,
integrityReport: r.integrity.cards >= 1,
zeroConsoleErrors: r.consoleErrors.length === 0,
};
r.checks = checks;
r.pass = Object.values(checks).every(Boolean);
console.log(JSON.stringify(r, null, 2));
fs.writeFileSync(path.join(OUT, "p9-summary.json"), JSON.stringify(r, null, 2));
process.exit(r.pass ? 0 : 1);