// round2 现状截图基线:本地真实栈(5174 -> 18080 Java),登录后逐页截图 + 收集 pageerror/console error。 // 用法:cd admin-frontend-vue && node scripts/round2-shots.mjs import { chromium } from '@playwright/test' import { mkdirSync } from 'node:fs' const BASE = process.env.BASE_URL || 'http://localhost:5174/admin-vue' const OUT = 'test-results/round2-baseline' mkdirSync(OUT, { recursive: true }) const PAGES = [ ['account-users', 'account/users'], ['account-menus', 'account/menus'], ['account-groups', 'account/groups'], ['asin-registry', 'asin-center/registry'], ['asin-invalid', 'asin-center/invalid'], ['asin-query', 'asin-center/query'], ['asin-categories', 'asin-center/categories'], ['asin-skip-price', 'asin-center/skip-price'], ['shop-keys', 'shop-center/keys'], ['shop-shops', 'shop-center/shops'], ['shop-data-tasks', 'shop-center/data-tasks'], ['records-history', 'records/history'], ['records-software-version', 'records/software-version'], ['records-digital-human-version', 'records/digital-human-version'], ['records-image-video-tasks', 'records/image-video-tasks'], ] const browser = await chromium.launch({ channel: 'msedge', headless: true }) const context = await browser.newContext({ viewport: { width: 1440, height: 900 }, locale: 'zh-CN' }) const page = await context.newPage() const problems = new Map() // page -> string[] page.on('pageerror', (e) => { const key = page.url() const list = problems.get(key) || [] list.push(`pageerror: ${e.message}`) problems.set(key, list) }) page.on('console', (m) => { if (m.type() === 'error') { const key = page.url() const list = problems.get(key) || [] list.push(`console.error: ${m.text().slice(0, 300)}`) problems.set(key, list) } }) // 登录(真实 UI 表单) async function ensureLoggedIn() { await page.goto(`${BASE}/`, { waitUntil: 'networkidle' }) await page.waitForTimeout(600) const loginVisible = await page.locator('#loginUsername').isVisible().catch(() => false) if (loginVisible) { await page.locator('#loginUsername').fill('admin') await page.locator('#loginPassword').fill('admin123') await page.locator('.btn-login').click() // 等离开登录页(进入后台) await page.waitForURL(/admin-vue\/(account|asin|shop|records)/, { timeout: 20000 }).catch(() => {}) await page.waitForTimeout(1200) } } await ensureLoggedIn() console.log('logged-in url:', page.url()) const report = [] for (const [name, path] of PAGES) { const url = `${BASE}/${path}` try { await page.goto(url, { waitUntil: 'domcontentloaded' }) // 等页头或内容出现(不同页标题不同,抓第一个标题/内容容器) await page.locator('.page-heading').first().waitFor({ state: 'visible', timeout: 12000 }).catch(() => {}) await page.waitForLoadState('networkidle').catch(() => {}) await page.waitForTimeout(500) await page.screenshot({ path: `${OUT}/${name}.jpg`, type: 'jpeg', quality: 82 }) report.push(`ok ${name} ${url}`) } catch (e) { report.push(`FAIL ${name} ${url} :: ${String(e).slice(0, 200)}`) } } console.log('\n=== screenshots ===') console.log(report.join('\n')) console.log('\n=== page errors (运行时异常,供排查) ===') let anyError = false for (const [url, list] of problems) { const clean = url.replace(BASE, '') if (list.length) { anyError = true console.log(`-- ${clean}`) for (const l of [...new Set(list)]) console.log(' ', l) } } if (!anyError) console.log('(无 pageerror/console.error)') await browser.close()