import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const here = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(here, "../../.."); const designRoot = "file:///Users/maidong/Desktop/zyc/qiyuan_gitea/AirShelf/电商AI平台"; const targetRoot = "http://127.0.0.1:5173/exact"; const manifest = JSON.parse(fs.readFileSync(path.join(here, "pages.json"), "utf8")); function arg(name, fallback = "") { const index = process.argv.indexOf(`--${name}`); return index >= 0 ? process.argv[index + 1] : fallback; } function asUrl(root, value) { if (value.startsWith("http://") || value.startsWith("https://") || value.startsWith("file://")) return value; return `${root}/${value}`; } const viewport = arg("viewport", "1440x900"); const only = arg("only", ""); const pages = only ? manifest.filter((item) => item.name.includes(only)) : manifest; const results = []; for (const item of pages) { const source = asUrl(designRoot, item.source); const target = item.target || asUrl(targetRoot, item.targetPath || item.source); const args = [ path.join(here, "compare-page.mjs"), "--name", item.name, "--source", source, "--target", target, "--viewport", viewport, "--clear-target-storage" ]; console.log(`\n[visual-parity] ${item.name}`); const run = spawnSync(process.execPath, args, { cwd: here, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); if (run.stdout) process.stdout.write(run.stdout); if (run.stderr) process.stderr.write(run.stderr); const reportFile = path.join(repoRoot, "core/qa/visual-parity/output", `${item.name}.report.json`); let report = null; if (fs.existsSync(reportFile)) { report = JSON.parse(fs.readFileSync(reportFile, "utf8")); } results.push({ name: item.name, ok: run.status === 0 && report?.pass === true, status: run.status, diffPixels: report?.diffPixels ?? null, diffRatio: report?.diffRatio ?? null, note: item.note || "" }); } const failed = results.filter((item) => !item.ok); const summary = { viewport, total: results.length, passed: results.length - failed.length, failed: failed.length, results }; const summaryPath = path.join(repoRoot, "core/qa/visual-parity/output/all.report.json"); fs.mkdirSync(path.dirname(summaryPath), { recursive: true }); fs.writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`); console.log("\n[visual-parity] summary"); console.table(results.map((item) => ({ page: item.name, ok: item.ok, diffPixels: item.diffPixels, diffRatio: item.diffRatio }))); if (failed.length) { process.exitCode = 1; }