Files
MorphDoc/packages/docx-theme-engine/tests/contracts.test.ts
T
SkyJourney 58f87cc19f feat: 完成 DOCX R4 元素级视觉门禁
建立 Chromium、Word 与 WPS 的可编辑内容、段落几何、页眉页脚、封面、媒体和逐页视觉比较链路。

支持封面独立分节、正文页码重启、主题页边距与横向纸张,并将自然分页差异降为诊断项。

修正代码块内边距和折叠表格单元格边框映射,14 套主题生产矩阵与六组布局视觉矩阵通过。
2026-08-02 03:27:11 +08:00

172 lines
4.6 KiB
TypeScript

import { themeManifestSchema } from "@md-to-pdf/core";
import { describe, expect, it } from "vitest";
import {
DOCX_STYLE_SLOTS,
docxThemeStyleSnapshotSchema,
docxThemeTokenSetSchema,
normalizeDocxThemeMappingConfig
} from "../src/index.js";
function createManifest(docxStyle?: unknown) {
return themeManifestSchema.parse({
manifestVersion: 1,
id: "external-clean",
name: "External Clean",
version: "1.0.0",
description: "外部主题",
author: "Tester",
license: "MIT",
entry: "theme.css",
domPreset: "generic",
defaultFontSize: "16px",
supportedFeatures: ["table"],
category: "general",
compatibleProfiles: [],
...(docxStyle ? { docxStyle } : {}),
bundled: false
});
}
describe("DOCX 主题映射配置", () => {
it("没有 DOCX 声明的外部主题默认使用自动映射", () => {
expect(
normalizeDocxThemeMappingConfig(createManifest())
).toEqual({
mode: "auto",
basePreset: "general",
overrides: {},
legacyPreset: false
});
});
it("兼容现有 preset 与扁平覆盖", () => {
const result = normalizeDocxThemeMappingConfig(
createManifest({
preset: "technical",
body: { sizePt: 11 }
})
);
expect(result).toMatchObject({
mode: "auto-with-overrides",
basePreset: "technical",
overrides: { body: { sizePt: 11 } },
legacyPreset: true
});
});
it("新式 overrides 优先于旧式扁平覆盖", () => {
const result = normalizeDocxThemeMappingConfig(
createManifest({
mode: "auto-with-overrides",
basePreset: "formal",
body: { sizePt: 11, color: "#111111" },
overrides: { body: { sizePt: 12 } }
})
);
expect(result.overrides.body?.sizePt).toBe(12);
expect(result.overrides.body?.color).toBe("#111111");
});
});
describe("DOCX 主题引擎契约", () => {
it("样式槽位名称与选择器保持唯一", () => {
expect(new Set(DOCX_STYLE_SLOTS.map((slot) => slot.name)).size)
.toBe(DOCX_STYLE_SLOTS.length);
expect(
new Set(DOCX_STYLE_SLOTS.map((slot) => slot.selector)).size
).toBe(DOCX_STYLE_SLOTS.length);
});
it("拒绝重复或状态不一致的样式快照", () => {
const computed = {
fontFamily: "Arial",
fontSize: "16px",
fontWeight: "400",
fontStyle: "normal",
color: "rgb(0, 0, 0)",
backgroundColor: "rgba(0, 0, 0, 0)",
lineHeight: "24px",
letterSpacing: "normal",
textAlign: "start",
direction: "ltr",
textIndent: "0px",
textDecorationLine: "none",
marginTop: "0px",
marginRight: "0px",
marginBottom: "0px",
marginLeft: "0px",
paddingTop: "0px",
paddingRight: "0px",
paddingBottom: "0px",
paddingLeft: "0px",
borderTop: "0px none rgb(0, 0, 0)",
borderRight: "0px none rgb(0, 0, 0)",
borderBottom: "0px none rgb(0, 0, 0)",
borderLeft: "0px none rgb(0, 0, 0)",
width: "100px",
maxWidth: "none",
minWidth: "0px",
minHeight: "0px",
height: "24px",
breakBefore: "auto",
breakAfter: "auto",
breakInside: "auto",
display: "block",
flexDirection: "row",
alignItems: "normal",
justifyContent: "normal",
outline: "0px none rgb(0, 0, 0)",
outlineOffset: "0px"
};
const result = docxThemeStyleSnapshotSchema.safeParse({
schemaVersion: 1,
themeId: "external-clean",
themeFingerprint: "a".repeat(64),
viewport: {
widthPx: 794,
heightPx: 1123,
deviceScaleFactor: 1
},
rootFontSizePx: 16,
slots: [
{ slot: "paragraph", matched: true, computed },
{ slot: "paragraph", matched: false }
]
});
expect(result.success).toBe(false);
});
it("接受带来源、置信度和诊断的样式令牌", () => {
expect(
docxThemeTokenSetSchema.parse({
schemaVersion: 1,
themeId: "external-clean",
themeFingerprint: "b".repeat(64),
mode: "auto",
basePreset: "general",
slots: [
{
slot: "paragraph",
source: "computed-css",
confidence: "exact",
style: {
fontCandidates: ["Arial", "sans-serif"],
fontSizePt: 12,
color: "#111111"
}
}
],
diagnostics: [
{
severity: "warning",
code: "font-fallback-required",
message: "首选字体不可用",
slot: "paragraph",
property: "font-family"
}
]
}).slots
).toHaveLength(1);
});
});