@bendyline/squisq-formats 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-532L4D5D.js +21 -0
- package/dist/chunk-532L4D5D.js.map +1 -0
- package/dist/chunk-67KIJHV2.js +21 -0
- package/dist/chunk-67KIJHV2.js.map +1 -0
- package/dist/chunk-KAK4V57E.js +387 -0
- package/dist/chunk-KAK4V57E.js.map +1 -0
- package/dist/chunk-MQHCXI56.js +1035 -0
- package/dist/chunk-MQHCXI56.js.map +1 -0
- package/dist/chunk-TBPD5PCU.js +1136 -0
- package/dist/chunk-TBPD5PCU.js.map +1 -0
- package/dist/chunk-ULLIPBEJ.js +271 -0
- package/dist/chunk-ULLIPBEJ.js.map +1 -0
- package/dist/docx/index.d.ts +108 -0
- package/dist/docx/index.js +14 -0
- package/dist/docx/index.js.map +1 -0
- package/dist/html/index.d.ts +166 -0
- package/dist/html/index.js +17 -0
- package/dist/html/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/ooxml/index.d.ts +351 -0
- package/dist/ooxml/index.js +99 -0
- package/dist/ooxml/index.js.map +1 -0
- package/dist/pdf/index.d.ts +130 -0
- package/dist/pdf/index.js +15 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/pptx/index.d.ts +58 -0
- package/dist/pptx/index.js +13 -0
- package/dist/pptx/index.js.map +1 -0
- package/dist/xlsx/index.d.ts +58 -0
- package/dist/xlsx/index.js +13 -0
- package/dist/xlsx/index.js.map +1 -0
- package/package.json +85 -0
- package/src/__tests__/docxExport.test.ts +457 -0
- package/src/__tests__/docxImport.test.ts +410 -0
- package/src/__tests__/html.test.ts +295 -0
- package/src/__tests__/ooxml.test.ts +197 -0
- package/src/__tests__/pdfExport.test.ts +322 -0
- package/src/__tests__/pdfImport.test.ts +290 -0
- package/src/__tests__/roundTrip.test.ts +201 -0
- package/src/docx/export.ts +978 -0
- package/src/docx/import.ts +909 -0
- package/src/docx/index.ts +26 -0
- package/src/docx/styles.ts +145 -0
- package/src/html/htmlTemplate.ts +307 -0
- package/src/html/imageUtils.ts +66 -0
- package/src/html/index.ts +168 -0
- package/src/index.ts +51 -0
- package/src/ooxml/index.ts +87 -0
- package/src/ooxml/namespaces.ts +160 -0
- package/src/ooxml/reader.ts +218 -0
- package/src/ooxml/types.ts +104 -0
- package/src/ooxml/writer.ts +321 -0
- package/src/ooxml/xmlUtils.ts +123 -0
- package/src/pdf/export.ts +1029 -0
- package/src/pdf/import.ts +835 -0
- package/src/pdf/index.ts +29 -0
- package/src/pdf/styles.ts +180 -0
- package/src/pptx/index.ts +78 -0
- package/src/xlsx/index.ts +78 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for DOCX export: markdownDocToDocx, docToDocx.
|
|
3
|
+
*
|
|
4
|
+
* Exports a MarkdownDocument to .docx, then unzips and inspects
|
|
5
|
+
* the OOXML structure to verify correctness.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import JSZip from 'jszip';
|
|
10
|
+
import type {
|
|
11
|
+
MarkdownDocument,
|
|
12
|
+
MarkdownHeading,
|
|
13
|
+
MarkdownList,
|
|
14
|
+
MarkdownListItem,
|
|
15
|
+
MarkdownTable,
|
|
16
|
+
MarkdownTableRow,
|
|
17
|
+
MarkdownTableCell,
|
|
18
|
+
MarkdownCodeBlock,
|
|
19
|
+
MarkdownBlockquote,
|
|
20
|
+
MarkdownThematicBreak,
|
|
21
|
+
MarkdownStrong,
|
|
22
|
+
MarkdownEmphasis,
|
|
23
|
+
MarkdownInlineCode,
|
|
24
|
+
MarkdownLink,
|
|
25
|
+
} from '@bendyline/squisq/markdown';
|
|
26
|
+
|
|
27
|
+
import { markdownDocToDocx, docToDocx } from '../docx/export';
|
|
28
|
+
|
|
29
|
+
// ============================================
|
|
30
|
+
// Helpers
|
|
31
|
+
// ============================================
|
|
32
|
+
|
|
33
|
+
async function exportAndParse(
|
|
34
|
+
doc: MarkdownDocument,
|
|
35
|
+
options?: Parameters<typeof markdownDocToDocx>[1],
|
|
36
|
+
) {
|
|
37
|
+
const buffer = await markdownDocToDocx(doc, options);
|
|
38
|
+
expect(buffer).toBeInstanceOf(ArrayBuffer);
|
|
39
|
+
expect(buffer.byteLength).toBeGreaterThan(0);
|
|
40
|
+
|
|
41
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
42
|
+
return zip;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function getDocumentXml(zip: JSZip): Promise<Document> {
|
|
46
|
+
const text = await zip.file('word/document.xml')!.async('text');
|
|
47
|
+
return new DOMParser().parseFromString(text, 'application/xml');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _getBodyParagraphs(doc: Document): Element[] {
|
|
51
|
+
const body = doc.getElementsByTagName('w:body')[0];
|
|
52
|
+
return Array.from(body.children).filter((el) => el.localName === 'p');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================
|
|
56
|
+
// Basic Structure
|
|
57
|
+
// ============================================
|
|
58
|
+
|
|
59
|
+
describe('markdownDocToDocx', () => {
|
|
60
|
+
it('produces a valid zip with required parts', async () => {
|
|
61
|
+
const doc: MarkdownDocument = {
|
|
62
|
+
type: 'document',
|
|
63
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Hello' }] }],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const zip = await exportAndParse(doc);
|
|
67
|
+
|
|
68
|
+
expect(zip.file('[Content_Types].xml')).not.toBeNull();
|
|
69
|
+
expect(zip.file('_rels/.rels')).not.toBeNull();
|
|
70
|
+
expect(zip.file('word/document.xml')).not.toBeNull();
|
|
71
|
+
expect(zip.file('word/styles.xml')).not.toBeNull();
|
|
72
|
+
expect(zip.file('word/settings.xml')).not.toBeNull();
|
|
73
|
+
expect(zip.file('word/fontTable.xml')).not.toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('exports an empty document', async () => {
|
|
77
|
+
const doc: MarkdownDocument = { type: 'document', children: [] };
|
|
78
|
+
const zip = await exportAndParse(doc);
|
|
79
|
+
const xmlDoc = await getDocumentXml(zip);
|
|
80
|
+
const body = xmlDoc.getElementsByTagName('w:body')[0];
|
|
81
|
+
expect(body).toBeDefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ============================================
|
|
85
|
+
// Headings
|
|
86
|
+
// ============================================
|
|
87
|
+
|
|
88
|
+
it('exports headings with correct styles', async () => {
|
|
89
|
+
const doc: MarkdownDocument = {
|
|
90
|
+
type: 'document',
|
|
91
|
+
children: [
|
|
92
|
+
{
|
|
93
|
+
type: 'heading',
|
|
94
|
+
depth: 1,
|
|
95
|
+
children: [{ type: 'text', value: 'Title' }],
|
|
96
|
+
} satisfies MarkdownHeading,
|
|
97
|
+
{
|
|
98
|
+
type: 'heading',
|
|
99
|
+
depth: 2,
|
|
100
|
+
children: [{ type: 'text', value: 'Subtitle' }],
|
|
101
|
+
} satisfies MarkdownHeading,
|
|
102
|
+
{
|
|
103
|
+
type: 'heading',
|
|
104
|
+
depth: 3,
|
|
105
|
+
children: [{ type: 'text', value: 'Section' }],
|
|
106
|
+
} satisfies MarkdownHeading,
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const zip = await exportAndParse(doc);
|
|
111
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
112
|
+
|
|
113
|
+
// Verify heading styles are present
|
|
114
|
+
expect(xmlText).toContain('w:val="Heading1"');
|
|
115
|
+
expect(xmlText).toContain('w:val="Heading2"');
|
|
116
|
+
expect(xmlText).toContain('w:val="Heading3"');
|
|
117
|
+
|
|
118
|
+
// Verify heading text
|
|
119
|
+
expect(xmlText).toContain('Title');
|
|
120
|
+
expect(xmlText).toContain('Subtitle');
|
|
121
|
+
expect(xmlText).toContain('Section');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// ============================================
|
|
125
|
+
// Inline Formatting
|
|
126
|
+
// ============================================
|
|
127
|
+
|
|
128
|
+
it('exports bold text with w:b element', async () => {
|
|
129
|
+
const doc: MarkdownDocument = {
|
|
130
|
+
type: 'document',
|
|
131
|
+
children: [
|
|
132
|
+
{
|
|
133
|
+
type: 'paragraph',
|
|
134
|
+
children: [
|
|
135
|
+
{
|
|
136
|
+
type: 'strong',
|
|
137
|
+
children: [{ type: 'text', value: 'bold text' }],
|
|
138
|
+
} satisfies MarkdownStrong,
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const zip = await exportAndParse(doc);
|
|
145
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
146
|
+
expect(xmlText).toContain('<w:b/>');
|
|
147
|
+
expect(xmlText).toContain('bold text');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('exports italic text with w:i element', async () => {
|
|
151
|
+
const doc: MarkdownDocument = {
|
|
152
|
+
type: 'document',
|
|
153
|
+
children: [
|
|
154
|
+
{
|
|
155
|
+
type: 'paragraph',
|
|
156
|
+
children: [
|
|
157
|
+
{
|
|
158
|
+
type: 'emphasis',
|
|
159
|
+
children: [{ type: 'text', value: 'italic text' }],
|
|
160
|
+
} satisfies MarkdownEmphasis,
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const zip = await exportAndParse(doc);
|
|
167
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
168
|
+
expect(xmlText).toContain('<w:i/>');
|
|
169
|
+
expect(xmlText).toContain('italic text');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('exports inline code with monospace font', async () => {
|
|
173
|
+
const doc: MarkdownDocument = {
|
|
174
|
+
type: 'document',
|
|
175
|
+
children: [
|
|
176
|
+
{
|
|
177
|
+
type: 'paragraph',
|
|
178
|
+
children: [{ type: 'inlineCode', value: 'console.log()' } satisfies MarkdownInlineCode],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const zip = await exportAndParse(doc);
|
|
184
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
185
|
+
expect(xmlText).toContain('Consolas');
|
|
186
|
+
expect(xmlText).toContain('console.log()');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// ============================================
|
|
190
|
+
// Lists
|
|
191
|
+
// ============================================
|
|
192
|
+
|
|
193
|
+
it('exports unordered list with numbering', async () => {
|
|
194
|
+
const doc: MarkdownDocument = {
|
|
195
|
+
type: 'document',
|
|
196
|
+
children: [
|
|
197
|
+
{
|
|
198
|
+
type: 'list',
|
|
199
|
+
ordered: false,
|
|
200
|
+
children: [
|
|
201
|
+
{
|
|
202
|
+
type: 'listItem',
|
|
203
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Item 1' }] }],
|
|
204
|
+
} satisfies MarkdownListItem,
|
|
205
|
+
{
|
|
206
|
+
type: 'listItem',
|
|
207
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Item 2' }] }],
|
|
208
|
+
} satisfies MarkdownListItem,
|
|
209
|
+
],
|
|
210
|
+
} satisfies MarkdownList,
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const zip = await exportAndParse(doc);
|
|
215
|
+
|
|
216
|
+
// Should have numbering.xml
|
|
217
|
+
expect(zip.file('word/numbering.xml')).not.toBeNull();
|
|
218
|
+
|
|
219
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
220
|
+
expect(xmlText).toContain('Item 1');
|
|
221
|
+
expect(xmlText).toContain('Item 2');
|
|
222
|
+
expect(xmlText).toContain('w:numId');
|
|
223
|
+
|
|
224
|
+
// Numbering should be bullet type
|
|
225
|
+
const numXml = await zip.file('word/numbering.xml')!.async('text');
|
|
226
|
+
expect(numXml).toContain('w:val="bullet"');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('exports ordered list with decimal numbering', async () => {
|
|
230
|
+
const doc: MarkdownDocument = {
|
|
231
|
+
type: 'document',
|
|
232
|
+
children: [
|
|
233
|
+
{
|
|
234
|
+
type: 'list',
|
|
235
|
+
ordered: true,
|
|
236
|
+
children: [
|
|
237
|
+
{
|
|
238
|
+
type: 'listItem',
|
|
239
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: 'First' }] }],
|
|
240
|
+
} satisfies MarkdownListItem,
|
|
241
|
+
],
|
|
242
|
+
} satisfies MarkdownList,
|
|
243
|
+
],
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const zip = await exportAndParse(doc);
|
|
247
|
+
const numXml = await zip.file('word/numbering.xml')!.async('text');
|
|
248
|
+
expect(numXml).toContain('w:val="decimal"');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// ============================================
|
|
252
|
+
// Tables
|
|
253
|
+
// ============================================
|
|
254
|
+
|
|
255
|
+
it('exports tables with rows and cells', async () => {
|
|
256
|
+
const doc: MarkdownDocument = {
|
|
257
|
+
type: 'document',
|
|
258
|
+
children: [
|
|
259
|
+
{
|
|
260
|
+
type: 'table',
|
|
261
|
+
children: [
|
|
262
|
+
{
|
|
263
|
+
type: 'tableRow',
|
|
264
|
+
children: [
|
|
265
|
+
{
|
|
266
|
+
type: 'tableCell',
|
|
267
|
+
isHeader: true,
|
|
268
|
+
children: [{ type: 'text', value: 'Name' }],
|
|
269
|
+
} satisfies MarkdownTableCell,
|
|
270
|
+
{
|
|
271
|
+
type: 'tableCell',
|
|
272
|
+
isHeader: true,
|
|
273
|
+
children: [{ type: 'text', value: 'Value' }],
|
|
274
|
+
} satisfies MarkdownTableCell,
|
|
275
|
+
],
|
|
276
|
+
} satisfies MarkdownTableRow,
|
|
277
|
+
{
|
|
278
|
+
type: 'tableRow',
|
|
279
|
+
children: [
|
|
280
|
+
{
|
|
281
|
+
type: 'tableCell',
|
|
282
|
+
children: [{ type: 'text', value: 'A' }],
|
|
283
|
+
} satisfies MarkdownTableCell,
|
|
284
|
+
{
|
|
285
|
+
type: 'tableCell',
|
|
286
|
+
children: [{ type: 'text', value: '1' }],
|
|
287
|
+
} satisfies MarkdownTableCell,
|
|
288
|
+
],
|
|
289
|
+
} satisfies MarkdownTableRow,
|
|
290
|
+
],
|
|
291
|
+
} satisfies MarkdownTable,
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const zip = await exportAndParse(doc);
|
|
296
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
297
|
+
expect(xmlText).toContain('<w:tbl>');
|
|
298
|
+
expect(xmlText).toContain('<w:tr>');
|
|
299
|
+
expect(xmlText).toContain('<w:tc>');
|
|
300
|
+
expect(xmlText).toContain('Name');
|
|
301
|
+
expect(xmlText).toContain('Value');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// ============================================
|
|
305
|
+
// Code Blocks
|
|
306
|
+
// ============================================
|
|
307
|
+
|
|
308
|
+
it('exports code blocks with code styling', async () => {
|
|
309
|
+
const doc: MarkdownDocument = {
|
|
310
|
+
type: 'document',
|
|
311
|
+
children: [
|
|
312
|
+
{
|
|
313
|
+
type: 'code',
|
|
314
|
+
value: 'const x = 1;\nconst y = 2;',
|
|
315
|
+
} satisfies MarkdownCodeBlock,
|
|
316
|
+
],
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const zip = await exportAndParse(doc);
|
|
320
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
321
|
+
expect(xmlText).toContain('const x = 1;');
|
|
322
|
+
expect(xmlText).toContain('const y = 2;');
|
|
323
|
+
expect(xmlText).toContain('Consolas');
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// ============================================
|
|
327
|
+
// Blockquotes
|
|
328
|
+
// ============================================
|
|
329
|
+
|
|
330
|
+
it('exports blockquotes with quote style', async () => {
|
|
331
|
+
const doc: MarkdownDocument = {
|
|
332
|
+
type: 'document',
|
|
333
|
+
children: [
|
|
334
|
+
{
|
|
335
|
+
type: 'blockquote',
|
|
336
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: 'A wise quote' }] }],
|
|
337
|
+
} satisfies MarkdownBlockquote,
|
|
338
|
+
],
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const zip = await exportAndParse(doc);
|
|
342
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
343
|
+
expect(xmlText).toContain('Quote');
|
|
344
|
+
expect(xmlText).toContain('A wise quote');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// ============================================
|
|
348
|
+
// Hyperlinks
|
|
349
|
+
// ============================================
|
|
350
|
+
|
|
351
|
+
it('exports hyperlinks with relationship', async () => {
|
|
352
|
+
const doc: MarkdownDocument = {
|
|
353
|
+
type: 'document',
|
|
354
|
+
children: [
|
|
355
|
+
{
|
|
356
|
+
type: 'paragraph',
|
|
357
|
+
children: [
|
|
358
|
+
{
|
|
359
|
+
type: 'link',
|
|
360
|
+
url: 'https://example.com',
|
|
361
|
+
children: [{ type: 'text', value: 'click here' }],
|
|
362
|
+
} satisfies MarkdownLink,
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const zip = await exportAndParse(doc);
|
|
369
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
370
|
+
expect(xmlText).toContain('<w:hyperlink');
|
|
371
|
+
expect(xmlText).toContain('click here');
|
|
372
|
+
|
|
373
|
+
// Check relationships
|
|
374
|
+
const relsText = await zip.file('word/_rels/document.xml.rels')!.async('text');
|
|
375
|
+
expect(relsText).toContain('https://example.com');
|
|
376
|
+
expect(relsText).toContain('TargetMode="External"');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// ============================================
|
|
380
|
+
// Thematic Break
|
|
381
|
+
// ============================================
|
|
382
|
+
|
|
383
|
+
it('exports thematic breaks', async () => {
|
|
384
|
+
const doc: MarkdownDocument = {
|
|
385
|
+
type: 'document',
|
|
386
|
+
children: [{ type: 'thematicBreak' } satisfies MarkdownThematicBreak],
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const zip = await exportAndParse(doc);
|
|
390
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
391
|
+
expect(xmlText).toContain('w:pBdr');
|
|
392
|
+
expect(xmlText).toContain('w:bottom');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// ============================================
|
|
396
|
+
// Core Properties
|
|
397
|
+
// ============================================
|
|
398
|
+
|
|
399
|
+
it('includes core properties when options are provided', async () => {
|
|
400
|
+
const doc: MarkdownDocument = {
|
|
401
|
+
type: 'document',
|
|
402
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: 'test' }] }],
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const zip = await exportAndParse(doc, {
|
|
406
|
+
title: 'My Document',
|
|
407
|
+
author: 'Test Author',
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const coreXml = await zip.file('docProps/core.xml')?.async('text');
|
|
411
|
+
expect(coreXml).toBeDefined();
|
|
412
|
+
expect(coreXml).toContain('My Document');
|
|
413
|
+
expect(coreXml).toContain('Test Author');
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// ============================================
|
|
418
|
+
// docToDocx convenience wrapper
|
|
419
|
+
// ============================================
|
|
420
|
+
|
|
421
|
+
describe('docToDocx', () => {
|
|
422
|
+
it('converts a Doc to docx', async () => {
|
|
423
|
+
const doc = {
|
|
424
|
+
articleId: 'test',
|
|
425
|
+
duration: 10,
|
|
426
|
+
blocks: [
|
|
427
|
+
{
|
|
428
|
+
id: 'block-1',
|
|
429
|
+
startTime: 0,
|
|
430
|
+
duration: 5,
|
|
431
|
+
audioSegment: 0,
|
|
432
|
+
sourceHeading: {
|
|
433
|
+
type: 'heading' as const,
|
|
434
|
+
depth: 1 as const,
|
|
435
|
+
children: [{ type: 'text' as const, value: 'Test Heading' }],
|
|
436
|
+
},
|
|
437
|
+
contents: [
|
|
438
|
+
{
|
|
439
|
+
type: 'paragraph' as const,
|
|
440
|
+
children: [{ type: 'text' as const, value: 'Body text' }],
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
audio: { segments: [] },
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const buffer = await docToDocx(doc);
|
|
449
|
+
expect(buffer).toBeInstanceOf(ArrayBuffer);
|
|
450
|
+
expect(buffer.byteLength).toBeGreaterThan(0);
|
|
451
|
+
|
|
452
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
453
|
+
const xmlText = await zip.file('word/document.xml')!.async('text');
|
|
454
|
+
expect(xmlText).toContain('Test Heading');
|
|
455
|
+
expect(xmlText).toContain('Body text');
|
|
456
|
+
});
|
|
457
|
+
});
|