Files
MorphDoc/packages/docx-theme-engine/tests/probe.test.ts
T
SkyJourney 2c5c1bd317 release: 发布 v0.6.1 DOCX 视觉一致性修复
新增通用 CSS 到 OOXML 翻译修复,统一字体、字距、精确行距、段落、列表、表格、引用、代码块与行内代码连续性,不引入按主题 ID 分支。

新增 MdTP Mono 并统一 Serif、Sans、Mono 三字体包的 Chromium 与 DOCX 使用链;字体声明、嵌入部件和 Word/WPS 实际采用均进入硬门禁。

重建封面整页及正文语义块视觉差分,14 套主题、纵横两个方向、五组页边距共 140 个真实场景全部通过,阻断失败和诊断失败均为零。

源码服务、Docker Web API 与实际安装 Desktop 的 red-briefing 导出均包含 5 个字体部件;Word/WPS 原生渲染和逐页复核通过。修复 Docker 构建上下文与运行层复用软链接,并完善 v0.6.1 版本、发行说明和发布归集。

验证:npm test(116 个文件、616 项测试)、npm run typecheck、npm run build、git diff --check 全部通过。Desktop 安装器与 ZIP、Docker v0.6.1 镜像已生成;Windows 产物仍为未签名内部发行。
2026-08-04 10:30:44 +08:00

137 lines
3.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
DOCX_STYLE_SLOT_NAMES,
collectDocxStyleSlotSnapshots,
collectDocxThemeStyleSnapshot,
createDocxStyleProbeMarkup,
listDocxStyleProbeSlots,
readDocxComputedStyle,
validateDocxStyleProbeMarkup,
type DocxComputedStyle,
type DocxStyleProbeRoot
} from "../src/index.js";
const computedStyle: DocxComputedStyle = {
fontFamily: '"Microsoft YaHei", sans-serif',
fontSize: "16px",
fontWeight: "400",
fontStyle: "normal",
color: "rgb(17, 17, 17)",
backgroundColor: "rgba(0, 0, 0, 0)",
lineHeight: "28px",
letterSpacing: "normal",
textAlign: "start",
direction: "ltr",
textIndent: "32px",
textDecorationLine: "none",
marginTop: "0px",
marginRight: "0px",
marginBottom: "12px",
marginLeft: "0px",
paddingTop: "0px",
paddingRight: "0px",
paddingBottom: "0px",
paddingLeft: "0px",
borderTop: "0px none rgb(17, 17, 17)",
borderRight: "0px none rgb(17, 17, 17)",
borderBottom: "0px none rgb(17, 17, 17)",
borderLeft: "0px none rgb(17, 17, 17)",
width: "640px",
maxWidth: "none",
minWidth: "0px",
minHeight: "0px",
height: "28px",
breakBefore: "auto",
breakAfter: "auto",
breakInside: "auto",
display: "block",
flexDirection: "row",
alignItems: "normal",
justifyContent: "normal",
outline: "0px none rgb(17, 17, 17)",
outlineOffset: "0px"
};
function createRoot(matchedSlots: readonly string[]): DocxStyleProbeRoot {
const selectors = new Set(
matchedSlots.map(
(slot) => `[data-docx-slot="${slot}"]`
)
);
return {
querySelector(selector) {
return selectors.has(selector) ? { selector } : null;
}
};
}
describe("DOCX 标准样式探针", () => {
it("每个标准槽位在探针中恰好出现一次", () => {
const markup = createDocxStyleProbeMarkup();
expect(() => validateDocxStyleProbeMarkup(markup)).not.toThrow();
const slots = listDocxStyleProbeSlots(markup);
expect(slots).toHaveLength(DOCX_STYLE_SLOT_NAMES.length);
expect(new Set(slots)).toEqual(
new Set(DOCX_STYLE_SLOT_NAMES)
);
expect(markup).toMatch(
/^<article id="write"[^>]*>\s*<h1 data-docx-slot="heading-1">/u
);
expect(markup).toContain('<code class="language-javascript"');
expect(markup).not.toContain('<code class="hljs ');
});
it("拒绝缺失和重复槽位的探针", () => {
const markup = createDocxStyleProbeMarkup()
.replace('data-docx-slot="paragraph"', "")
.replace(
"</article>",
'<p data-docx-slot="strong">重复</p></article>'
);
expect(() => validateDocxStyleProbeMarkup(markup)).toThrow(
/重复槽位:strong;缺少槽位:paragraph/u
);
});
});
describe("DOCX 计算样式采集", () => {
it("按稳定槽位顺序采集并保留未命中状态", () => {
const slots = collectDocxStyleSlotSnapshots(
createRoot(["document", "paragraph"]),
() => readDocxComputedStyle(computedStyle)
);
expect(slots).toHaveLength(DOCX_STYLE_SLOT_NAMES.length);
expect(slots[0]).toMatchObject({
slot: "document",
matched: true,
computed: { fontSize: "16px" }
});
expect(
slots.find((entry) => entry.slot === "paragraph")
).toMatchObject({ matched: true });
expect(
slots.find((entry) => entry.slot === "heading-1")
).toEqual({ slot: "heading-1", matched: false });
});
it("生成可校验且与平台实现无关的主题快照", () => {
const snapshot = collectDocxThemeStyleSnapshot({
root: createRoot(DOCX_STYLE_SLOT_NAMES),
readComputedStyle: () =>
readDocxComputedStyle(computedStyle),
themeId: "external-clean",
themeFingerprint: "c".repeat(64),
viewport: {
widthPx: 794,
heightPx: 1123,
deviceScaleFactor: 1
},
rootFontSizePx: 16
});
expect(snapshot.slots).toHaveLength(
DOCX_STYLE_SLOT_NAMES.length
);
expect(snapshot.slots.every((slot) => slot.matched)).toBe(true);
});
});