Files
yingqing/core/qa/visual-parity/_seg2-shots.mjs
T

66 lines
2.9 KiB
JavaScript

// 二期第2段验收抓图:设定卡「表现形式 × 视频结构 × 人物 × 时长」+ 组合联动 + 时长提醒
import { chromium } from "playwright";
import { mkdirSync } from "node:fs";
const BASE = process.env.BASE || "http://127.0.0.1:5173";
const API = process.env.API || "http://127.0.0.1:8010";
const OUT = process.argv[2] || "shots-seg2";
mkdirSync(OUT, { recursive: true });
const login = await (await fetch(`${API}/api/auth/login/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
})).json();
const tok = login.token;
const auth = { Authorization: `Token ${tok}`, "Content-Type": "application/json" };
// 设定卡只在「还没脚本」的空态出现,所以现建一个干净项目,抓完删掉
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
const target = await (await fetch(`${API}/api/projects/`, {
method: "POST", headers: auth,
body: JSON.stringify({ name: `二期第2段抓图-${Date.now() % 100000}`, product: prods[0].id })
})).json();
console.log("project:", target.id, "product:", prods[0].title, "/", prods[0].category);
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
const page = await ctx.newPage();
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
const pane = () => page.locator(".chat-pane").first();
async function shot(name) {
await page.screenshot({ path: `${OUT}/${name}.png`, clip: await pane().boundingBox() });
console.log("shot", name);
}
await page.goto(`${BASE}/pipeline/${target.id}?st=1#stage-1`, { waitUntil: "networkidle", timeout: 40000 });
await page.waitForTimeout(2000);
await page.locator('.chat-mode[data-mode="ai"]').click();
await page.waitForTimeout(700);
await shot("1-setup-default");
const selects = page.locator(".setup-card .setup-select");
const structureOptions = async () =>
selects.nth(1).locator("option").evaluateAll((els) => els.map((e) => e.textContent));
console.log("默认结构可选:", await structureOptions());
// 1.8 组合联动:选「短剧」后,视频结构里应当没有「测评验证」
await selects.nth(0).selectOption("drama");
await page.waitForTimeout(400);
console.log("短剧下结构可选:", await structureOptions());
await shot("2-setup-drama-filtered");
// 时长调到最短 → 结构最短时长提醒(honey 色 .setup-rec.warn)
await selects.nth(0).selectOption("oral");
await selects.nth(1).selectOption("review");
await selects.nth(3).selectOption("5");
await page.waitForTimeout(400);
await shot("3-setup-duration-warn");
await browser.close();
await fetch(`${API}/api/projects/${target.id}/`, { method: "DELETE", headers: auth });
console.log("DONE ->", OUT);