// Wave 1 part-1 interaction smoke (scratch): S3 toast归正 + S4 modal ESC/进场动画. import { chromium } from "playwright"; import fs from "node:fs"; import path from "node:path"; const BASE = process.env.BASE || "http://localhost:5173"; const TOKEN = process.env.TOKEN || ""; const OUT = path.resolve(process.env.OUT || "../../../_qa_shots/wave1"); fs.mkdirSync(OUT, { recursive: true }); const browser = await chromium.launch({ headless: true }); const result = {}; // 1) S3 — 登录页「忘记密码」应弹设计系统 .toast(右下),不再是旧 .login-toast { const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } }); const p = await ctx.newPage(); await p.goto(BASE + "/login", { waitUntil: "load" }); await p.waitForTimeout(400); await p.locator("a", { hasText: "忘记密码" }).first().click(); await p.waitForTimeout(500); result.authToast = { toastShown: await p.locator(".toast.show").count(), toastText: await p.locator(".toast .txt").innerText().catch(() => ""), legacyLoginToast: await p.locator(".login-toast").count(), }; await p.screenshot({ path: path.join(OUT, "auth-toast.png") }); await ctx.close(); } // 2) S4 — 团队页「创建账户」弹窗:打开有 .show / ESC 能关 { const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } }); const seed = await ctx.newPage(); await seed.goto(BASE + "/", { waitUntil: "domcontentloaded" }); await seed.evaluate((t) => localStorage.setItem("airshelf_token", t), TOKEN); await seed.close(); const p = await ctx.newPage(); await p.goto(BASE + "/team", { waitUntil: "load" }); await p.waitForTimeout(1200); if (await p.locator("#open-invite").count()) { await p.click("#open-invite"); await p.waitForTimeout(450); // 让进场动画跑完 const shownOnOpen = await p.locator(".modal-bg.show").count(); const modalTransform = await p.locator(".modal-bg.show .modal").first() .evaluate((el) => getComputedStyle(el).transform).catch(() => "n/a"); await p.screenshot({ path: path.join(OUT, "team-modal-open.png") }); await p.keyboard.press("Escape"); await p.waitForTimeout(500); // 退场动画 + 卸载 const modalGone = (await p.locator(".modal-bg").count()) === 0; await p.screenshot({ path: path.join(OUT, "team-modal-after-esc.png") }); result.teamModal = { opened: true, shownOnOpen, modalTransform, closedByEsc: modalGone }; } else { result.teamModal = { opened: false, note: "#open-invite not found" }; } await ctx.close(); } await browser.close(); console.log(JSON.stringify(result, null, 2)); fs.writeFileSync(path.join(OUT, "summary.json"), JSON.stringify(result, null, 2));