QG6 完成QA治理矩阵与报告规范
这个提交包含在:
@@ -1,3 +1,4 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { expect, test, type Browser, type Page } from "@playwright/test";
|
||||
@@ -30,14 +31,41 @@ type PreparedRoutes = {
|
||||
|
||||
const ROOT_DIR = path.resolve(process.cwd(), "..");
|
||||
const MATRIX_PATH = path.join(ROOT_DIR, "docs/trialrun/project-wide-flow-matrix.csv");
|
||||
const SCREENSHOT_DIR = path.join(ROOT_DIR, "output/playwright/project-wide-visual");
|
||||
const ARTIFACT_DIR = path.join(ROOT_DIR, "artifacts/qa-flow/project-wide-visual");
|
||||
const ISSUE_ID = process.env.QA_ISSUE_ID?.trim() || "project-wide-visual";
|
||||
const BASE_URL = process.env.E2E_BASE_URL?.trim() || null;
|
||||
const SCREENSHOT_DIR = resolveRootPath(
|
||||
process.env.QA_VISUAL_OUTPUT_DIR,
|
||||
path.join(ROOT_DIR, "output/playwright/project-wide-visual"),
|
||||
);
|
||||
const ARTIFACT_DIR = resolveRootPath(
|
||||
process.env.QA_VISUAL_ARTIFACT_DIR,
|
||||
path.join(ROOT_DIR, "artifacts/qa-flow/project-wide-visual"),
|
||||
);
|
||||
const WECOM_UA =
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wxwork/4.1.20 MicroMessenger/8.0.1 Language/zh_CN";
|
||||
const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
|
||||
|
||||
const findings: VisualFinding[] = [];
|
||||
|
||||
function resolveRootPath(value: string | undefined, fallback: string) {
|
||||
if (!value?.trim()) {
|
||||
return fallback;
|
||||
}
|
||||
return path.isAbsolute(value) ? value : path.join(ROOT_DIR, value);
|
||||
}
|
||||
|
||||
function currentCommit() {
|
||||
try {
|
||||
return execSync("git rev-parse --short HEAD", {
|
||||
cwd: ROOT_DIR,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
}).trim();
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
function outputPath(dir: string, fileName: string) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
return path.join(dir, fileName);
|
||||
@@ -357,7 +385,7 @@ async function auditPlans(browser: Browser, plans: Array<{ route: string; domain
|
||||
for (const { user, plans: userPlans } of plansByUser.values()) {
|
||||
const context = viewport === "mobile-wecom"
|
||||
? await newWecomContext(browser)
|
||||
: await browser.newContext({ viewport: { width: 1366, height: 900 }, serviceWorkers: "block" });
|
||||
: await browser.newContext({ viewport: { width: 1440, height: 960 }, serviceWorkers: "block" });
|
||||
try {
|
||||
const page = await context.newPage();
|
||||
await loginForVisual(page, user);
|
||||
@@ -382,7 +410,15 @@ test.describe("项目级全页面视觉巡检", () => {
|
||||
|
||||
const reportPath = outputPath(ARTIFACT_DIR, "report.json");
|
||||
writeFileSync(reportPath, JSON.stringify({
|
||||
issueId: ISSUE_ID,
|
||||
baseURL: BASE_URL,
|
||||
commit: currentCommit(),
|
||||
checkedAt: new Date().toISOString(),
|
||||
status: findings.some((finding) => finding.severity === "P1") ? "FAIL" : "PASS",
|
||||
matrix: {
|
||||
pc: "1440x960",
|
||||
mobileWeCom: "390x844",
|
||||
},
|
||||
pcRoutes: pcPlans.map((item) => item.route),
|
||||
mobileRoutes: mobilePlans.map((item) => item.route),
|
||||
findings,
|
||||
|
||||
在新工单中引用
屏蔽一个用户