// 期1 模特库走查:登录 → /models → 断言卡片/官方标签/tab,逐态截图,抓 console/pageerror。 import { chromium } from "playwright"; import fs from "node:fs"; import path from "node:path"; const BASE = process.env.BASE || "http://localhost:5188"; const OUT = path.resolve("../../../_qa_shots/models-p1"); fs.mkdirSync(OUT, { recursive: true }); const browser = await chromium.launch({ headless: true }); const r = { consoleErrors: [], steps: {} }; const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } }); const p = await ctx.newPage(); p.on("console", (m) => { if (m.type() === "error") r.consoleErrors.push(m.text()); }); p.on("pageerror", (e) => r.consoleErrors.push("PAGEERR:" + e.message)); // 登录 await p.goto(BASE + "/login", { waitUntil: "load" }); await p.waitForTimeout(400); await p.fill("#auth-username", "airshelf"); await p.fill("#auth-pwd", "Restraint2026"); await p.click("button.btn-cta"); await p.waitForFunction(() => !location.pathname.startsWith("/login"), { timeout: 12000 }); // 左菜单是否有「模特库」入口 r.steps.navHasModels = await p.locator("nav a, aside a, .sidebar a, button").filter({ hasText: "模特库" }).count(); // 进模特库 await p.goto(BASE + "/models", { waitUntil: "load" }); await p.waitForTimeout(2800); r.steps.h1 = await p.locator("h1").first().innerText().catch(() => ""); r.steps.tabCount = await p.locator(".tabs .tab").count(); r.steps.cardCountAll = await p.locator(".model-card").count(); r.steps.officialPill = await p.locator(".model-official").count(); r.steps.triviewThumbs = await p.locator(".model-triview").count(); r.steps.uploadBtn = await p.locator(".actions button", { hasText: /上传/ }).count(); await p.screenshot({ path: path.join(OUT, "models-all.png"), fullPage: true }); // 官方模板 tab await p.locator(".tabs .tab", { hasText: "官方模板" }).click(); await p.waitForTimeout(2200); r.steps.cardCountOfficial = await p.locator(".model-card").count(); r.steps.officialPillInTab = await p.locator(".model-official").count(); await p.screenshot({ path: path.join(OUT, "models-official.png"), fullPage: true }); // 我的模特 tab await p.locator(".tabs .tab", { hasText: "我的模特" }).click(); await p.waitForTimeout(2200); r.steps.cardCountMine = await p.locator(".model-card").count(); await p.screenshot({ path: path.join(OUT, "models-mine.png"), fullPage: true }); await browser.close(); console.log(JSON.stringify(r, null, 2)); fs.writeFileSync(path.join(OUT, "summary.json"), JSON.stringify(r, null, 2));