// 期3 走查:资产库「角色/素材」改名 + 最终成片隐藏 + pipeline 资产趴「角色」+ 演员库「模特库」tab。 import { chromium } from "playwright"; import fs from "node:fs"; import path from "node:path"; const BASE = process.env.BASE || "http://localhost:5200"; const PROJECT = process.env.PROJECT || "bdbf39d3-1a89-4135-b1b7-26d3331a7021"; const OUT = path.resolve("../../../_qa_shots/models-p3"); fs.mkdirSync(OUT, { recursive: true }); const browser = await chromium.launch({ headless: true }); const r = { consoleErrors: [], library: {}, pipeline: {} }; 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 }); // ── 资产库:角色 / 素材 改名,人物 / 成片 消失 ── await p.goto(BASE + "/library", { waitUntil: "load" }); await p.waitForTimeout(2600); const libTabs = await p.locator(".tabs .tab, .tab").allInnerTexts().catch(() => []); const tabText = libTabs.join(" | "); r.library.tabs = tabText; r.library.hasRoleTab = /角色/.test(tabText); r.library.hasMaterialTab = /素材/.test(tabText); r.library.peopleTabGone = !/(^|\|)\s*人物\s*\d/.test(tabText) && !/人物\s/.test(tabText); r.library.finalsTabGone = !/成片/.test(tabText); await p.screenshot({ path: path.join(OUT, "library-tabs.png"), fullPage: true }); // ── pipeline 资产趴(stage 2):KIND_LABEL person → 角色 ── await p.goto(BASE + "/pipeline/" + PROJECT, { waitUntil: "load" }); await p.waitForTimeout(2500); await p.locator('a[data-stage="2"]').first().click().catch(() => {}); await p.waitForTimeout(2500); const bodyText = await p.locator('[data-stage-pane="2"]').innerText().catch(() => ""); r.pipeline.hasRole = /角色/.test(bodyText); r.pipeline.hasPersonOld = /(新增人物|暂无人物)/.test(bodyText); await p.screenshot({ path: path.join(OUT, "pipeline.png"), fullPage: true }); // 尝试打开演员库(点资产趴里的「新增角色」或「模特库」入口),验证「模特库」tab let openedActorLib = false; const trigger = p.locator("button", { hasText: /新增角色|模特库|演员库/ }).first(); if (await trigger.count()) { await trigger.click().catch(() => {}); await p.waitForTimeout(1800); const alText = await p.locator(".actorlib").innerText().catch(() => ""); r.pipeline.actorLibText = alText.slice(0, 120); r.pipeline.actorLibHasModelLib = /模特库/.test(alText); openedActorLib = await p.locator(".actorlib").count() > 0; if (openedActorLib) await p.screenshot({ path: path.join(OUT, "actor-library.png"), fullPage: true }); } r.pipeline.openedActorLib = openedActorLib; await browser.close(); console.log(JSON.stringify(r, null, 2)); fs.writeFileSync(path.join(OUT, "summary.json"), JSON.stringify(r, null, 2));