import { describe, expect, it } from "vitest";
import { zipSync } from "fflate";
import { defaultExportConfig } from "@md-to-pdf/core";
import { inspectDocxAcceptance } from "../src/index.js";
const encoder = new TextEncoder();
const word =
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
const relationships =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
const packageRelationships =
"http://schemas.openxmlformats.org/package/2006/relationships";
const math =
"http://schemas.openxmlformats.org/officeDocument/2006/math";
const drawing =
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
const png = Uint8Array.of(
0x89,
0x50,
0x4e,
0x47,
0x0d,
0x0a,
0x1a,
0x0a,
0,
0,
0,
0
);
function xml(value: string) {
return encoder.encode(value);
}
function createAcceptanceDocx(
overrides: {
altChunk?: boolean;
duplicateText?: boolean;
internalMarker?: boolean;
invalidPropertyOrder?: boolean;
coverPageNumber?: boolean;
bodyTitlePage?: boolean;
footerAlignment?: "left" | "center" | "right";
evenAndOddHeaders?: boolean;
logicalOuterFooter?: boolean;
nativeOuterFooter?: boolean;
} = {}
) {
return zipSync({
"[Content_Types].xml": xml(
`
整体 HTML
") } : {}) }); } const expectation = { id: "unit-fixture", page: { widthTwips: 11906, heightTwips: 16838, orientation: "portrait" as const, marginsTwips: { top: 907, right: 907, bottom: 907, left: 907 } }, requiredText: ["可编辑正文", "原生表格"], requiredFootnoteText: ["脚注"], minimumParagraphs: 3, minimumTextRuns: 2, minimumTables: 1, minimumNumberedParagraphs: 1, minimumHyperlinks: 1, minimumFootnoteReferences: 1, minimumMathObjects: 1, minimumPngImages: 1, requiredImageAltText: ["普通图片"], requiredStyleIds: ["Normal", "Heading1", "SourceCode", "Table"], requiredParagraphStyleIds: ["Heading1"], requirePageField: true, minimumSections: 2, requireFirstSectionWithoutHeaderFooter: true, finalPageNumberStart: 1, requireSectionPagesField: true, minimumFullWidthTables: 1, maximumTextOccurrences: { 可编辑正文: 1 }, forbidInternalMarkers: true, pageDecorations: { exportConfig: { header: defaultExportConfig.header, footer: defaultExportConfig.footer }, semanticDocument: { schemaVersion: 1 as const, titlePolicy: { metadataTitle: "suppress" as const, firstBodyHeading: "keep" as const }, regions: [ { kind: "cover" as const, nodes: [ { kind: "text" as const, role: "project-report-title" as const, text: "封面" } ], section: { headerFooter: "none" as const, pageNumber: "hidden" as const, breakAfter: "next-page" as const, followingPageNumberStart: 1 } } ] } } }; describe("DOCX 自动验收器", () => { it("验证纸张、可编辑结构、关系、PNG 和字段", () => { const report = inspectDocxAcceptance( createAcceptanceDocx(), expectation ); expect(report.page.orientation).toBe("portrait"); expect(report.structure).toMatchObject({ tableCount: 1, numberedParagraphCount: 1, hyperlinkCount: 1, footnoteReferenceCount: 1, mathObjectCount: 1, pngImageCount: 1, pageFieldCount: 1, sectionPageFieldCount: 1, sectionCount: 2, fullWidthTableCount: 1, internalMarkerCount: 0, altChunkCount: 0 }); expect(report.checks).toEqual( expect.objectContaining({ editableStructure: true, pngMedia: true, noAltChunk: true }) ); expect(report.pageDecorations).toEqual({ hasCover: true, usesDifferentFirstPage: false, usesEvenAndOddPages: false, bodyPageNumberStart: 1, headerReferenceTypes: [], footerReferenceTypes: ["default"], totalPageField: "SECTIONPAGES" }); }); it("拒绝通过 altChunk 嵌入的正文 HTML", () => { expect(() => inspectDocxAcceptance( createAcceptanceDocx({ altChunk: true }), expectation ) ).toThrow("altChunk"); }); it("拒绝重复文本和未清理的内部标记", () => { expect(() => inspectDocxAcceptance( createAcceptanceDocx({ duplicateText: true }), expectation ) ).toThrow("最多允许 1 次"); expect(() => inspectDocxAcceptance( createAcceptanceDocx({ internalMarker: true }), expectation ) ).toThrow("internal-markers"); }); it("拒绝 WordprocessingML 属性子节点乱序", () => { expect(() => inspectDocxAcceptance( createAcceptanceDocx({ invalidPropertyOrder: true }), expectation ) ).toThrow("w:pPr 子节点顺序无效"); }); it("拒绝封面节残留页码和正文节错误首页设置", () => { expect(() => inspectDocxAcceptance( createAcceptanceDocx({ coverPageNumber: true }), expectation ) ).toThrow("封面节不得包含 w:pgNumType"); expect(() => inspectDocxAcceptance( createAcceptanceDocx({ bodyTitlePage: true }), expectation ) ).toThrow("body-title-page"); }); it("拒绝页码位置和奇偶页设置偏离导出配置", () => { expect(() => inspectDocxAcceptance( createAcceptanceDocx({ footerAlignment: "right" }), expectation ) ).toThrow("footer-default-alignment"); expect(() => inspectDocxAcceptance( createAcceptanceDocx({ evenAndOddHeaders: true }), expectation ) ).toThrow("even-and-odd-setting"); }); it("封面重启页码时要求原生奇偶页脚表达外侧页码", () => { const outerExpectation = { ...expectation, pageDecorations: { ...expectation.pageDecorations, exportConfig: { ...expectation.pageDecorations.exportConfig, footer: { ...expectation.pageDecorations.exportConfig.footer, alignment: "outer" as const } } } }; const report = inspectDocxAcceptance( createAcceptanceDocx({ evenAndOddHeaders: true, nativeOuterFooter: true, footerAlignment: "left" }), outerExpectation ); expect(report.pageDecorations).toMatchObject({ hasCover: true, usesEvenAndOddPages: true, footerReferenceTypes: ["default", "even"] }); expect(() => inspectDocxAcceptance(createAcceptanceDocx(), outerExpectation) ).toThrow("body-page-number-start"); }); });