// Wave 3 batch1 walkthrough (scratch): team 5 弹窗 + account 充值弹窗/筛选条. 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("../../../_qa_shots/wave3-batch1"); fs.mkdirSync(OUT, { recursive: true }); const browser = await chromium.launch({ headless: true }); const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, colorScheme: "light" }); 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 r = { team: {}, account: {}, consoleErrors: [] }; // ---- TEAM ---- { const p = await ctx.newPage(); p.on("console", (m) => { if (m.type() === "error") r.consoleErrors.push("team:" + m.text()); }); p.on("pageerror", (e) => r.consoleErrors.push("team:PAGEERROR:" + e.message)); await p.goto(BASE + "/team", { waitUntil: "load" }); await p.waitForTimeout(1500); // 创建账户 if (await p.locator("#open-invite").count()) { await p.click("#open-invite"); await p.waitForTimeout(450); r.team.createAcct = { modalShown: await p.locator(".modal-bg.show").count(), roleChoiceCards: await p.locator(".role-choice, .role-choices .role-choice").count(), quotaGrid: await p.locator(".quota-grid, .quota-cell").count(), genButtons: await p.locator(".input-with-gen button, button:has-text('生成')").count(), }; await p.screenshot({ path: path.join(OUT, "team-create-account.png") }); await p.keyboard.press("Escape"); await p.waitForTimeout(450); r.team.createAcctClosedByEsc = (await p.locator(".modal-bg").count()) === 0; } else r.team.createAcct = "no #open-invite"; // 月限额 if (await p.locator("#open-limit").count()) { await p.click("#open-limit"); await p.waitForTimeout(450); r.team.limit = { modalShown: await p.locator(".modal-bg.show").count(), presets: await p.locator(".limit-presets .lp, .limit-presets button").count(), }; await p.screenshot({ path: path.join(OUT, "team-limit.png") }); await p.keyboard.press("Escape"); await p.waitForTimeout(300); } else r.team.limit = "no #open-limit"; await p.close(); } // ---- ACCOUNT ---- { const p = await ctx.newPage(); p.on("console", (m) => { if (m.type() === "error") r.consoleErrors.push("account:" + m.text()); }); p.on("pageerror", (e) => r.consoleErrors.push("account:PAGEERROR:" + e.message)); await p.goto(BASE + "/account", { waitUntil: "load" }); await p.waitForTimeout(1500); r.account.filterBars = await p.locator(".filter-bar").count(); await p.screenshot({ path: path.join(OUT, "account-page.png") }); // 充值弹窗:点支付方式按钮 const payBtn = p.locator(".pay-method-btn").first(); if (await payBtn.count()) { await payBtn.click(); await p.waitForTimeout(450); r.account.topup = { modalShown: await p.locator(".modal-bg.show").count(), qr: await p.locator(".topup-qr").count(), topupModal: await p.locator(".topup-modal").count(), }; await p.screenshot({ path: path.join(OUT, "account-topup.png") }); await p.keyboard.press("Escape"); await p.waitForTimeout(400); r.account.topupClosedByEsc = (await p.locator(".modal-bg").count()) === 0; } else r.account.topup = "no .pay-method-btn"; await p.close(); } await browser.close(); console.log(JSON.stringify(r, null, 2)); fs.writeFileSync(path.join(OUT, "summary.json"), JSON.stringify(r, null, 2));