feat: 建立 DOCX 标准样式探针
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
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",
|
||||
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",
|
||||
breakBefore: "auto",
|
||||
breakAfter: "auto",
|
||||
breakInside: "auto",
|
||||
display: "block"
|
||||
};
|
||||
|
||||
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)
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user