test: complete WP4-07 visual and performance budgets
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 2m34s
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 2m34s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
||||
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { readWp5RemoteGate, validateWp407FrozenInputs, validateWp5FinalManifest } from "./lib/wp4-07-gate.mjs";
|
||||
@@ -12,6 +13,25 @@ function findFiles(directory, name) {
|
||||
});
|
||||
}
|
||||
|
||||
function readBaselineMerges() {
|
||||
const log = spawnSync("git", ["log", "--merges", "--format=%H%x09%s", "HEAD"], { encoding: "utf8", timeout: 30_000 });
|
||||
if ((log.status ?? 1) !== 0) throw new Error("WP4_07_BASELINE_MERGES_UNREADABLE");
|
||||
return log.stdout.trim().split(/\r?\n/).filter(Boolean).map((line) => {
|
||||
const [sha, ...subject] = line.split("\t");
|
||||
return { sha, subject: subject.join("\t") };
|
||||
}).filter((entry) => entry.subject.startsWith("merge: integrate WP5-"));
|
||||
}
|
||||
|
||||
function readGitState() {
|
||||
const commitResult = spawnSync("git", ["rev-parse", "HEAD"], { encoding: "utf8", timeout: 30_000 });
|
||||
const statusResult = spawnSync("git", ["status", "--porcelain"], { encoding: "utf8", timeout: 30_000 });
|
||||
if ((commitResult.status ?? 1) !== 0 || (statusResult.status ?? 1) !== 0) throw new Error("WP4_07_GIT_STATE_UNREADABLE");
|
||||
return {
|
||||
commit: commitResult.stdout.trim(),
|
||||
worktree_under_test: statusResult.stdout.trim() ? "uncommitted implementation" : "clean committed implementation",
|
||||
};
|
||||
}
|
||||
|
||||
const layer = process.argv[2];
|
||||
if (!['visual', 'performance'].includes(layer)) {
|
||||
console.error("Usage: node scripts/run-wp4-07-layer.mjs <visual|performance>");
|
||||
@@ -31,28 +51,38 @@ try {
|
||||
missing_remote_tasks: gate.missing_tasks,
|
||||
observed_remote_heads: gate.heads,
|
||||
observed_task_shas: gate.task_shas,
|
||||
required_final_branch: gate.required_final_branch,
|
||||
required_final_branches: gate.required_final_branches,
|
||||
status: "red",
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
const manifest = validateWp5FinalManifest(process.env.DADA_WP4_07_FINAL_ASSET_MANIFEST);
|
||||
if (!process.env.DADA_WP4_07_APP_URL || !process.env.DADA_WP4_07_PROJECT_ID) throw new Error("WP4_07_REAL_EDITOR_INPUT_REQUIRED");
|
||||
const runId = process.env.DADA_TDD_RUN_ID ?? `wp4-07-${layer}-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
|
||||
const runDirectory = resolve("artifacts", "tdd", runId);
|
||||
const casesDirectory = resolve(runDirectory, "cases");
|
||||
const releaseDirectory = resolve(runDirectory, "real-release");
|
||||
mkdirSync(casesDirectory, { recursive: true });
|
||||
const environment = {
|
||||
let environment = {
|
||||
...process.env,
|
||||
DADA_PLAYWRIGHT_OUTPUT_DIR: resolve(runDirectory, "playwright-output"),
|
||||
DADA_WP4_07_EVIDENCE_DIR: casesDirectory,
|
||||
DADA_WP4_07_HARNESS_MODE: "real",
|
||||
DADA_WP4_07_HARNESS_MODE: "real_archive",
|
||||
DADA_WP4_07_LAYER: layer,
|
||||
DADA_WP5_03_RUN_DIRECTORY: releaseDirectory,
|
||||
};
|
||||
const build = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", "pnpm --filter @dada/shared-contracts build && pnpm --filter @dada/static-sticker-catalog build && pnpm --filter @dada/template-registry build && pnpm --filter @dada/asset-renderer build"], { encoding: "utf8", env: environment, maxBuffer: 64 * 1024 * 1024 });
|
||||
const build = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", "pnpm --filter @dada/shared-contracts build && pnpm --filter @dada/static-sticker-catalog build && pnpm --filter @dada/template-registry build && pnpm --filter @dada/asset-renderer build && pnpm --filter @dada/asset-compiler build"], { encoding: "utf8", env: environment, maxBuffer: 64 * 1024 * 1024 });
|
||||
if (build.stdout) process.stdout.write(build.stdout);
|
||||
if (build.stderr) process.stderr.write(build.stderr);
|
||||
if ((build.status ?? 1) !== 0) process.exit(build.status ?? 1);
|
||||
let manifestPath = process.env.DADA_WP4_07_FINAL_ASSET_MANIFEST;
|
||||
if (!manifestPath) {
|
||||
const compile = spawnSync(process.execPath, ["scripts/compile-wp5-03-assets.mjs"], { encoding: "utf8", env: environment, maxBuffer: 64 * 1024 * 1024 });
|
||||
if (compile.stdout) process.stdout.write(compile.stdout);
|
||||
if (compile.stderr) process.stderr.write(compile.stderr);
|
||||
if ((compile.status ?? 1) !== 0) process.exit(compile.status ?? 1);
|
||||
manifestPath = resolve(releaseDirectory, "cases", "TDD-WP5-WHITE-001-p0a-allowlist", "manifest.json");
|
||||
}
|
||||
const manifest = validateWp5FinalManifest(manifestPath);
|
||||
environment = { ...environment, DADA_WP4_07_FINAL_ASSET_MANIFEST: resolve(manifestPath) };
|
||||
const playwright = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", "pnpm exec playwright test --config playwright.wp4-07.config.ts"], { encoding: "utf8", env: environment, maxBuffer: 64 * 1024 * 1024 });
|
||||
if (playwright.stdout) process.stdout.write(playwright.stdout);
|
||||
if (playwright.stderr) process.stderr.write(playwright.stderr);
|
||||
@@ -61,6 +91,8 @@ try {
|
||||
const familyNeedle = layer === "visual" ? "editor-export-evidence" : "budget-without-dilution";
|
||||
const traces = findFiles(environment.DADA_PLAYWRIGHT_OUTPUT_DIR, "trace.zip");
|
||||
for (const browser of ["chrome", "edge"]) {
|
||||
const evidenceTrace = resolve(caseDirectory, browser, "trace.zip");
|
||||
if (existsSync(evidenceTrace)) continue;
|
||||
const trace = traces.find((path) => path.toLowerCase().includes(familyNeedle) && path.toLowerCase().includes(browser));
|
||||
if (!trace) throw new Error(`WP4_07_${layer.toUpperCase()}_${browser.toUpperCase()}_TRACE_REQUIRED`);
|
||||
mkdirSync(resolve(caseDirectory, browser), { recursive: true });
|
||||
@@ -70,7 +102,67 @@ try {
|
||||
if (aggregator.stdout) process.stdout.write(aggregator.stdout);
|
||||
if (aggregator.stderr) process.stderr.write(aggregator.stderr);
|
||||
if ((aggregator.status ?? 1) !== 0) process.exit(aggregator.status ?? 1);
|
||||
console.log(JSON.stringify({ layer, manifest, run_id: runId, status: "automated_green" }, null, 2));
|
||||
writeFileSync(resolve(caseDirectory, "release-inputs.json"), `${JSON.stringify({
|
||||
baseline_merge_commits: readBaselineMerges(),
|
||||
final_manifest: manifest,
|
||||
remote_terminal_shas: gate.terminal_branch_shas,
|
||||
source_task_shas: gate.task_shas,
|
||||
status: "passed",
|
||||
}, null, 2)}\n`);
|
||||
const git = readGitState();
|
||||
const manifestSha = createHash("sha256").update(readFileSync("tasks.manifest.json")).digest("hex").toUpperCase();
|
||||
const visual = layer === "visual";
|
||||
const evidenceRefs = visual
|
||||
? [
|
||||
"pixel-diff.json", "layout-boxes.json", "manual-review.json", "release-inputs.json",
|
||||
"chrome/asset-sources.json", "chrome/editor.png", "chrome/canvas.png", "chrome/export-dialog.png", "chrome/layout-boxes.json", "chrome/trace.zip",
|
||||
"edge/asset-sources.json", "edge/editor.png", "edge/canvas.png", "edge/export-dialog.png", "edge/layout-boxes.json", "edge/trace.zip",
|
||||
]
|
||||
: [
|
||||
"performance.json", "memory.json", "dom-count.json", "environment.json", "release-inputs.json",
|
||||
"chrome/asset-sources.json", "chrome/performance-raw.json", "chrome/trace.zip",
|
||||
"edge/asset-sources.json", "edge/performance-raw.json", "edge/trace.zip",
|
||||
];
|
||||
const missingEvidence = evidenceRefs.filter((path) => !existsSync(resolve(caseDirectory, path)));
|
||||
const manualReviewPassed = !visual
|
||||
|| JSON.parse(readFileSync(resolve(caseDirectory, "manual-review.json"), "utf8")).status === "passed";
|
||||
const status = missingEvidence.length > 0
|
||||
? "failed"
|
||||
: manualReviewPassed ? "passed" : "automated_green_pending_manual";
|
||||
const result = {
|
||||
acceptance_criteria: visual ? ["AC-19", "AC-23", "AC-32"] : ["AC-27", "AC-32"],
|
||||
automation: visual ? ["automated", "manual_review"] : ["automated"],
|
||||
commit: git.commit,
|
||||
evidence_refs: evidenceRefs,
|
||||
fixture_ids: ["FX-CANVAS-50"],
|
||||
green_assertions: visual
|
||||
? ["Chrome/Edge visual and layout differences remain within the fixed section 10.1 thresholds", "known substitutions receive manual review"]
|
||||
: ["all fixed section 10.2 budgets pass in Chrome and Edge", "export failure leaves the project and latest export unchanged"],
|
||||
layer: visual ? ["VIS-PERF", "MANUAL"] : ["VIS-PERF"],
|
||||
manifest: { path: "tasks.manifest.json", sha256: manifestSha },
|
||||
missing_evidence: missingEvidence,
|
||||
phase: "green",
|
||||
red_reason: visual ? "Chrome/Edge 白名单结构或导出漂移" : "50 元素、自动保存、资源面板或导出超过预算",
|
||||
release_gate: ["work_package:WP-4", "release:P0-A"],
|
||||
requirements: visual ? ["NFR-02"] : ["NFR-03"],
|
||||
run_id: runId,
|
||||
status,
|
||||
task_id: "TASK-WP4-07",
|
||||
test_id: visual ? "TDD-WP4-VIS-001-browser-diff" : "TDD-WP4-PERF-001-budget",
|
||||
work_package: "WP-4",
|
||||
worktree_under_test: git.worktree_under_test,
|
||||
};
|
||||
writeFileSync(resolve(caseDirectory, "result.json"), `${JSON.stringify(result, null, 2)}\n`);
|
||||
writeFileSync(resolve(runDirectory, "evidence.json"), `${JSON.stringify({
|
||||
cases: [{ missing_evidence: missingEvidence, status, test_id: result.test_id }],
|
||||
commit: git.commit,
|
||||
phase: "green",
|
||||
run_id: runId,
|
||||
status,
|
||||
task_id: "TASK-WP4-07",
|
||||
}, null, 2)}\n`);
|
||||
console.log(JSON.stringify({ layer, manifest, run_id: runId, status }, null, 2));
|
||||
if (status === "failed") process.exit(1);
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify({ code: error instanceof Error ? error.message : String(error), layer, status: "failed" }, null, 2));
|
||||
process.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user