Files
QOffice/src/io/wordOfficeImport.test.ts
T
Курнат Андрей 8517bd6391 Preserve QWord tabs in DOCX
2026-06-01 12:40:37 +03:00

211 lines
7.9 KiB
TypeScript

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 text color and highlight from OOXML", async () => {
const blob = await exportDocxBlob(
"Color.docx",
'<p><span style="color: #0f5fae; background-color: #fff2cc;">Color fragment</span></p>'
);
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",
'<p style="text-align: center;">Centered</p><p align="right">Right</p><p style="text-align: justify;">Justified</p>'
);
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", '<p><span style="font-size: 18pt;">Large text</span></p>');
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", '<p><span style="font-family: Arial;">Arial text</span></p>');
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", "<p><s>Deleted text</s></p>");
const file = new File([blob], "Strike.docx", {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
const imported = await importDocxFile(file);
expect(imported.html).toContain("<s>Deleted text</s>");
expect(htmlToWordBlocks(imported.html)).toEqual([
{
type: "paragraph",
text: "Deleted text",
runs: [{ text: "Deleted text", strike: true }]
}
]);
});
it("imports DOCX line breaks from OOXML", async () => {
const blob = await exportDocxBlob("LineBreak.docx", "<p>Line 1<br>Line 2</p>");
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<br>Line 2");
expect(htmlToWordBlocks(imported.html)).toEqual([
{
type: "paragraph",
text: "Line 1 Line 2",
runs: [{ text: "Line 1\nLine 2" }]
}
]);
});
it("imports DOCX tabs from OOXML", async () => {
const blob = await exportDocxBlob("Tab.docx", '<p>Before<span data-qoffice-tab="true" style="white-space: pre;">\t</span>After</p>');
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", '<p>Open <a href="https://example.com/report"><strong>report</strong></a>.</p>');
const file = new File([blob], "Hyperlink.docx", {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
const imported = await importDocxFile(file);
expect(imported.html).toContain('<a href="https://example.com/report">');
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 simple DOCX tables from OOXML", async () => {
const blob = await exportDocxBlob(
"Table.docx",
"<p>Before table</p><table><tr><th>Stage</th><th>Owner</th></tr><tr><td>Plan</td><td>Anna</td></tr></table><p>After table</p>"
);
const file = new File([blob], "Table.docx", {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
const imported = await importDocxFile(file);
expect(imported.html).toContain("<table>");
expect(imported.html).toContain("<td><p>Stage</p></td>");
expect(imported.html).toContain("<td><p>Anna</p></td>");
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 styled DOCX content when the document also contains images", async () => {
const blob = await exportDocxBlob(
"Mixed.docx",
`<p><span style="color: #0f5fae;">Color fragment</span></p><img src="${tinyPngDataUri}" alt="Logo" width="32" height="32"><table><tr><td>Stage</td><td>Done</td></tr></table>`
);
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("<table>");
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"]]
});
});
});