import { describe, expect, it } from "vitest"; import { exportDocxBlob, htmlToWordBlocks, importDocxFile } from "./wordOffice"; const tinyPngBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="; const tinyPngDataUri = `data:image/png;base64,${tinyPngBase64}`; describe("wordOffice DOCX import", () => { it("imports DOCX Heading 3-6 as h3-h6", async () => { const blob = await exportDocxBlob("Headings.docx", "

Quality gate

Scenario

Step
Detail
"); const file = new File([blob], "Headings.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("

Quality gate

"); expect(imported.html).toContain("

Scenario

"); expect(imported.html).toContain("
Step
"); expect(imported.html).toContain("
Detail
"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "heading", level: 3, text: "Quality gate", runs: [{ text: "Quality gate" }] }, { type: "heading", level: 4, text: "Scenario", runs: [{ text: "Scenario" }] }, { type: "heading", level: 5, text: "Step", runs: [{ text: "Step" }] }, { type: "heading", level: 6, text: "Detail", runs: [{ text: "Detail" }] } ]); }); it("imports DOCX Quote style as blockquote", async () => { const blob = await exportDocxBlob("Quote.docx", "
Quoted fragment
"); const file = new File([blob], "Quote.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("
Quoted fragment
"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Quoted fragment", runs: [{ text: "Quoted fragment" }], quote: true } ]); }); it("imports DOCX text color and highlight from OOXML", async () => { const blob = await exportDocxBlob( "Color.docx", '

Color fragment

' ); const file = new File([blob], "Color.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("color: #0F5FAE;"); expect(imported.html).toContain("background-color: #FFF2CC;"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Color fragment", runs: [{ text: "Color fragment", color: "0F5FAE", highlightColor: "FFF2CC" }] } ]); }); it("imports DOCX paragraph alignment from OOXML", async () => { const blob = await exportDocxBlob( "Alignment.docx", '

Centered

Right

Justified

' ); const file = new File([blob], "Alignment.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain('style="text-align: center;"'); expect(imported.html).toContain('style="text-align: right;"'); expect(imported.html).toContain('style="text-align: justify;"'); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Centered", runs: [{ text: "Centered" }], alignment: "center" }, { type: "paragraph", text: "Right", runs: [{ text: "Right" }], alignment: "right" }, { type: "paragraph", text: "Justified", runs: [{ text: "Justified" }], alignment: "both" } ]); }); it("imports DOCX font size from OOXML", async () => { const blob = await exportDocxBlob("FontSize.docx", '

Large text

'); const file = new File([blob], "FontSize.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("font-size: 18pt;"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Large text", runs: [{ text: "Large text", fontSize: 18 }] } ]); }); it("imports DOCX font family from OOXML", async () => { const blob = await exportDocxBlob("FontFamily.docx", '

Arial text

'); const file = new File([blob], "FontFamily.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("font-family: 'Arial';"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Arial text", runs: [{ text: "Arial text", fontFamily: "Arial" }] } ]); }); it("imports DOCX strikethrough from OOXML", async () => { const blob = await exportDocxBlob("Strike.docx", "

Deleted text

"); const file = new File([blob], "Strike.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("Deleted text"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Deleted text", runs: [{ text: "Deleted text", strike: true }] } ]); }); it("imports DOCX subscript and superscript from OOXML", async () => { const blob = await exportDocxBlob("Indexes.docx", "

H2O m2

"); const file = new File([blob], "Indexes.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("2"); expect(imported.html).toContain("2"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "H2O m2", runs: [ { text: "H" }, { text: "2", subscript: true }, { text: "O m" }, { text: "2", superscript: true } ] } ]); }); it("imports DOCX line breaks from OOXML", async () => { const blob = await exportDocxBlob("LineBreak.docx", "

Line 1
Line 2

"); const file = new File([blob], "LineBreak.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("Line 1
Line 2"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Line 1 Line 2", runs: [{ text: "Line 1\nLine 2" }] } ]); }); it("imports DOCX page breaks from OOXML", async () => { const blob = await exportDocxBlob("PageBreak.docx", '

BeforeAfter

'); const file = new File([blob], "PageBreak.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain('data-qoffice-page-break="true"'); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Before After", runs: [{ text: "Before\fAfter" }] } ]); }); it("imports DOCX tabs from OOXML", async () => { const blob = await exportDocxBlob("Tab.docx", '

Before\tAfter

'); const file = new File([blob], "Tab.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain('data-qoffice-tab="true"'); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Before After", runs: [{ text: "Before\tAfter" }] } ]); }); it("imports DOCX external hyperlinks from OOXML relationships", async () => { const blob = await exportDocxBlob("Hyperlink.docx", '

Open report.

'); const file = new File([blob], "Hyperlink.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain(''); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Open report.", runs: [ { text: "Open " }, { text: "report", bold: true, href: "https://example.com/report" }, { text: "." } ] } ]); }); it("imports DOCX internal hyperlink anchors from OOXML", async () => { const blob = await exportDocxBlob("InternalHyperlink.docx", '

Open section.

'); const file = new File([blob], "InternalHyperlink.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain(''); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Open section.", runs: [{ text: "Open " }, { text: "section", bold: true, href: "#Section1" }, { text: "." }] } ]); }); it("imports DOCX bookmarks as HTML ids", async () => { const blob = await exportDocxBlob("Bookmark.docx", '

Section

Open section

'); const file = new File([blob], "Bookmark.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain('

Section

'); expect(imported.html).toContain('Open section'); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "heading", level: 1, text: "Section", runs: [{ text: "Section" }], bookmark: "Section1" }, { type: "paragraph", text: "Open section", runs: [{ text: "Open section", href: "#Section1" }] } ]); }); it("imports simple DOCX tables from OOXML", async () => { const blob = await exportDocxBlob( "Table.docx", "

Before table

StageOwner
PlanAnna

After table

" ); const file = new File([blob], "Table.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain(""); expect(imported.html).toContain(""); expect(imported.html).toContain(""); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Before table", runs: [{ text: "Before table" }] }, { type: "table", rows: [ ["Stage", "Owner"], ["Plan", "Anna"] ] }, { type: "paragraph", text: "After table", runs: [{ text: "After table" }] } ]); }); it("imports DOCX bullet and numbered lists from OOXML numbering", async () => { const blob = await exportDocxBlob( "Lists.docx", '
  1. Step one
  2. Step two
' ); const file = new File([blob], "Lists.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("

Stage

Anna

StageDone
` ); const file = new File([blob], "Mixed.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); const blocks = htmlToWordBlocks(imported.html); expect(imported.html).toContain("color: #0F5FAE;"); expect(imported.html).toContain(""); expect(imported.html).toContain(`src="${tinyPngDataUri}"`); expect(blocks[0]).toEqual({ type: "paragraph", text: "Color fragment", runs: [{ text: "Color fragment", color: "0F5FAE" }] }); expect(blocks[1]).toMatchObject({ type: "image", src: tinyPngDataUri, alt: "Logo", width: 32, height: 32 }); expect(blocks[2]).toEqual({ type: "table", rows: [["Stage", "Done"]] }); }); it("imports DOCX paragraph indentation from OOXML", async () => { const blob = await exportDocxBlob( "Indent.docx", '

Indented

Hanging

' ); const file = new File([blob], "Indent.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); const imported = await importDocxFile(file); expect(imported.html).toContain("margin-left: 36pt; text-indent: 18pt;"); expect(imported.html).toContain("margin-left: 36pt; text-indent: -12pt;"); expect(htmlToWordBlocks(imported.html)).toEqual([ { type: "paragraph", text: "Indented", runs: [{ text: "Indented" }], indent: { left: 720, firstLine: 360 } }, { type: "paragraph", text: "Hanging", runs: [{ text: "Hanging" }], indent: { left: 720, hanging: 240 } } ]); }); });