fix: 重建DOCX视觉一致性门禁
This commit is contained in:
@@ -5,6 +5,7 @@ import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
createPdfVisualDiffReport,
|
||||
getBlockingVisualDiffIssues,
|
||||
renderPdfVisualDiffHtml,
|
||||
serializePdfVisualDiffJson,
|
||||
type PdfDocumentSnapshot,
|
||||
@@ -30,6 +31,40 @@ function raster(color: string): PdfPageRaster {
|
||||
};
|
||||
}
|
||||
|
||||
function flowRaster(color: string): PdfPageRaster {
|
||||
const canvas = createCanvas(40, 50);
|
||||
const context = canvas.getContext("2d");
|
||||
context.fillStyle = "#ffffff";
|
||||
context.fillRect(0, 0, 40, 50);
|
||||
context.fillStyle = color;
|
||||
context.fillRect(34, 5, 6, 40);
|
||||
const png = Uint8Array.from(canvas.toBuffer("image/png"));
|
||||
return {
|
||||
widthPx: 40,
|
||||
heightPx: 50,
|
||||
dpi: 144,
|
||||
sha256: createHash("sha256").update(png).digest("hex"),
|
||||
png,
|
||||
};
|
||||
}
|
||||
|
||||
function blockRaster(color: string): PdfPageRaster {
|
||||
const canvas = createCanvas(40, 50);
|
||||
const context = canvas.getContext("2d");
|
||||
context.fillStyle = "#ffffff";
|
||||
context.fillRect(0, 0, 40, 50);
|
||||
context.fillStyle = color;
|
||||
context.fillRect(8, 12, 20, 10);
|
||||
const png = Uint8Array.from(canvas.toBuffer("image/png"));
|
||||
return {
|
||||
widthPx: 40,
|
||||
heightPx: 50,
|
||||
dpi: 144,
|
||||
sha256: createHash("sha256").update(png).digest("hex"),
|
||||
png,
|
||||
};
|
||||
}
|
||||
|
||||
function snapshot(label: string, pageRaster: PdfPageRaster): PdfDocumentSnapshot {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
@@ -82,7 +117,7 @@ function flowSnapshot(
|
||||
items: [],
|
||||
lines: [...lines],
|
||||
contentText: lines.map((line) => line.normalizedText).join(""),
|
||||
raster: raster(color),
|
||||
raster: flowRaster(color),
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -387,7 +422,7 @@ describe("PDF 视觉差异报告", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("整段跨页同时发生换行变化时仍保持严格失败", async () => {
|
||||
it("整段跨页同时发生换行变化时由语义块门禁失败", async () => {
|
||||
const baseline = flowSnapshot(
|
||||
"baseline",
|
||||
[[contentLine("甲", 8)], [contentLine("乙丙", 8)]],
|
||||
@@ -411,7 +446,7 @@ describe("PDF 视觉差异报告", () => {
|
||||
expect(report.status).toBe("failed");
|
||||
expect(report.basic.bodyFlow).toMatchObject({ detected: true, legal: false });
|
||||
expect(report.pages.every(
|
||||
(page) => page.rasterComparisonMode === "strict",
|
||||
(page) => page.rasterComparisonMode === "body-block",
|
||||
)).toBe(true);
|
||||
expect(report.issues.map((issue) => issue.code)).toContain(
|
||||
"TEXT_BLOCK_LINE_COUNT_MISMATCH",
|
||||
@@ -498,4 +533,78 @@ describe("PDF 视觉差异报告", () => {
|
||||
"PAGE_NUMBER_VISIBILITY_MISMATCH",
|
||||
);
|
||||
});
|
||||
|
||||
it("正文整页差异只作诊断,但语义块局部视觉变化必须失败", async () => {
|
||||
const baseline = flowSnapshot(
|
||||
"baseline",
|
||||
[[contentLine("甲", 8)]],
|
||||
"#111111",
|
||||
);
|
||||
const candidate = flowSnapshot(
|
||||
"candidate",
|
||||
[[contentLine("甲", 8)]],
|
||||
"#111111",
|
||||
);
|
||||
baseline.pages[0]!.raster = blockRaster("#111111");
|
||||
candidate.pages[0]!.raster = blockRaster("#cc0000");
|
||||
const report = await createPdfVisualDiffReport(baseline, candidate, {
|
||||
expectedEditableText: "甲",
|
||||
expectedEditableParagraphs: [flowParagraphs[0]!],
|
||||
pageSemantics: bodySemantics(1),
|
||||
});
|
||||
|
||||
expect(report.status).toBe("failed");
|
||||
expect(report.pages[0]?.rasterComparisonMode).toBe("body-block");
|
||||
expect(report.issues.map((issue) => issue.code)).toContain(
|
||||
"BODY_PAGE_RASTER_DIFFERENCE",
|
||||
);
|
||||
expect(report.issues.map((issue) => issue.code)).toContain(
|
||||
"SEMANTIC_BLOCK_RASTER_MISMATCH",
|
||||
);
|
||||
});
|
||||
|
||||
it("缺少语义块证据时不得把正文整页栅格自动降级", async () => {
|
||||
const report = await createPdfVisualDiffReport(
|
||||
snapshot("baseline", raster("#111111")),
|
||||
snapshot("candidate", raster("#cc0000")),
|
||||
{ pageSemantics: bodySemantics(1) },
|
||||
);
|
||||
|
||||
expect(report.status).toBe("failed");
|
||||
expect(report.pages[0]?.rasterComparisonMode).toBe("strict");
|
||||
expect(report.issues.map((issue) => issue.code)).toContain(
|
||||
"CHANGED_PIXEL_RATIO_EXCEEDED",
|
||||
);
|
||||
});
|
||||
|
||||
it("所有 failure 级视觉问题都必须进入发布阻断集合", () => {
|
||||
const failure = {
|
||||
code: "INK_IOU_BELOW_THRESHOLD" as const,
|
||||
severity: "failure" as const,
|
||||
message: "墨迹 IoU 未达到门限",
|
||||
};
|
||||
const warning = {
|
||||
code: "BODY_FLOW_RASTER_DIFFERENCE" as const,
|
||||
severity: "warning" as const,
|
||||
message: "正文自然分页差异",
|
||||
};
|
||||
|
||||
expect(getBlockingVisualDiffIssues({ issues: [failure, warning] })).toEqual([
|
||||
failure,
|
||||
]);
|
||||
});
|
||||
|
||||
it("正文自然分页 warning 不阻断发布", () => {
|
||||
expect(
|
||||
getBlockingVisualDiffIssues({
|
||||
issues: [
|
||||
{
|
||||
code: "BODY_FLOW_RASTER_DIFFERENCE",
|
||||
severity: "warning",
|
||||
message: "正文自然分页差异",
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user