@link-assistant/web-capture 1.12.0 → 2.0.1

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.
Files changed (119) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -1
  3. package/bin/web-capture.js +5 -7
  4. package/package.json +32 -18
  5. package/src/archive.js +1 -2
  6. package/src/binary-download.js +13 -0
  7. package/src/docx.js +14 -11
  8. package/src/fetch.js +1 -2
  9. package/src/figures.js +2 -12
  10. package/src/gdocs-images.js +0 -1
  11. package/src/gdocs.js +0 -1
  12. package/src/github.js +0 -1
  13. package/src/lib.js +1 -2
  14. package/src/localize-images.js +2 -12
  15. package/src/markdown.js +0 -1
  16. package/src/shared-dialog.js +0 -1
  17. package/src/stream.js +0 -1
  18. package/src/transport.js +23 -5
  19. package/src/txt.js +0 -1
  20. package/.changeset/README.md +0 -8
  21. package/.changeset/config.json +0 -11
  22. package/.dockerignore +0 -6
  23. package/.jscpd.json +0 -19
  24. package/.lenv +0 -8
  25. package/.prettierignore +0 -8
  26. package/.prettierrc +0 -10
  27. package/Dockerfile +0 -52
  28. package/babel.config.cjs +0 -13
  29. package/docker-compose.yml +0 -11
  30. package/eslint.config.js +0 -112
  31. package/examples/js/capture_cache.js +0 -12
  32. package/examples/js/engine_comparison.js +0 -39
  33. package/examples/js/html_download.js +0 -23
  34. package/examples/js/image_download.js +0 -36
  35. package/examples/js/markdown_download.js +0 -23
  36. package/examples/js/playwright_example.js +0 -23
  37. package/examples/python/html_download.py +0 -15
  38. package/examples/python/image_download.py +0 -20
  39. package/examples/python/markdown_download.py +0 -15
  40. package/examples/python/playwright_example.py +0 -22
  41. package/jest.config.mjs +0 -20
  42. package/tests/ci/dependency-freshness.test.js +0 -74
  43. package/tests/ci/detect-code-changes.test.js +0 -174
  44. package/tests/ci/docker-install.test.js +0 -31
  45. package/tests/ci/rust-dockerfile.test.js +0 -54
  46. package/tests/ci/workflow-policy.test.js +0 -79
  47. package/tests/e2e/docker.test.js +0 -186
  48. package/tests/e2e/process.test.js +0 -215
  49. package/tests/fixtures/bold-coalesce-and-leakage.html +0 -27
  50. package/tests/fixtures/deepwiki/deepwiki-full-page.png +0 -0
  51. package/tests/fixtures/deepwiki/index.md +0 -1500
  52. package/tests/fixtures/deepwiki/part-1.md +0 -776
  53. package/tests/fixtures/habr-articles.js +0 -45
  54. package/tests/fixtures/heading-numbering.html +0 -19
  55. package/tests/fixtures/list-item-with-br.html +0 -8
  56. package/tests/fixtures/ol-continuous-numbering.html +0 -12
  57. package/tests/fixtures/paragraph-vs-line-break.html +0 -11
  58. package/tests/helpers/nock.js +0 -10
  59. package/tests/helpers/search-fixture.cjs +0 -17
  60. package/tests/helpers/temporary-directory.js +0 -12
  61. package/tests/integration/api-endpoints.test.js +0 -418
  62. package/tests/integration/archive-default-layout.test.js +0 -37
  63. package/tests/integration/browser-engines.test.js +0 -128
  64. package/tests/integration/browser-packaged-path.test.js +0 -45
  65. package/tests/integration/deepwiki-download.test.js +0 -144
  66. package/tests/integration/deepwiki-rendered-page.mock.test.js +0 -121
  67. package/tests/integration/formalai-contract.test.js +0 -214
  68. package/tests/integration/gdocs-public-doc.test.js +0 -315
  69. package/tests/integration/github-readme.test.js +0 -137
  70. package/tests/integration/google-drive-image.test.js +0 -246
  71. package/tests/integration/habr-article.test.js +0 -337
  72. package/tests/integration/search-endpoint.test.js +0 -88
  73. package/tests/integration/stackoverflow-download.test.js +0 -173
  74. package/tests/integration/wikipedia-download.test.js +0 -142
  75. package/tests/jest.setup.mjs +0 -7
  76. package/tests/mock/index.test.js +0 -225
  77. package/tests/unit/animation.test.js +0 -33
  78. package/tests/unit/batch.test.js +0 -114
  79. package/tests/unit/bold-coalesce-and-leakage.test.js +0 -54
  80. package/tests/unit/browser-launch-options.test.js +0 -42
  81. package/tests/unit/browser.test.js +0 -172
  82. package/tests/unit/cache.test.js +0 -182
  83. package/tests/unit/cli.test.js +0 -399
  84. package/tests/unit/convertGoogleDriveUrl.test.js +0 -89
  85. package/tests/unit/convertRelativeUrls.test.js +0 -34
  86. package/tests/unit/extract-images.test.js +0 -259
  87. package/tests/unit/fetch-html.test.js +0 -54
  88. package/tests/unit/figures.test.js +0 -74
  89. package/tests/unit/gdocs-export-headers.test.js +0 -28
  90. package/tests/unit/gdocs-preprocess.test.js +0 -36
  91. package/tests/unit/gdocs.test.js +0 -1490
  92. package/tests/unit/github.test.js +0 -156
  93. package/tests/unit/heading-image.test.js +0 -49
  94. package/tests/unit/heading-numbering.test.js +0 -28
  95. package/tests/unit/html2md-br-in-list-item.test.js +0 -23
  96. package/tests/unit/html2md-ol-numbering.test.js +0 -20
  97. package/tests/unit/html2md.test.js +0 -695
  98. package/tests/unit/image-mode-defaults.test.js +0 -54
  99. package/tests/unit/kreuzberg.test.js +0 -249
  100. package/tests/unit/latex.test.js +0 -123
  101. package/tests/unit/localize-images.test.js +0 -114
  102. package/tests/unit/markdown.test.js +0 -36
  103. package/tests/unit/metadata.test.js +0 -140
  104. package/tests/unit/package-runtime-dependencies.test.js +0 -74
  105. package/tests/unit/paragraph-vs-line-break.test.js +0 -56
  106. package/tests/unit/postprocess.test.js +0 -110
  107. package/tests/unit/proxy-headers.test.js +0 -100
  108. package/tests/unit/publish-verification.test.js +0 -57
  109. package/tests/unit/retry.test.js +0 -111
  110. package/tests/unit/search.test.js +0 -339
  111. package/tests/unit/shared-dialog.test.js +0 -173
  112. package/tests/unit/stackoverflow.test.js +0 -32
  113. package/tests/unit/stream.test.js +0 -85
  114. package/tests/unit/verify.test.js +0 -182
  115. package/tests/xpaste/xpaste-screenshot-markdown.mock.test.js +0 -375
  116. package/tests/xpaste/xpaste.e2e.test.js +0 -36
  117. package/tests/xpaste/xpaste.integration.test.js +0 -41
  118. package/tests/xpaste/xpaste.mock.test.js +0 -261
  119. package/tests/xpaste/xpaste.unit.test.js +0 -119
@@ -1,259 +0,0 @@
1
- import { createHash } from 'crypto';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import os from 'os';
5
- import {
6
- extractAndSaveImages,
7
- extractBase64ToBuffers,
8
- stripBase64Images,
9
- hasBase64Images,
10
- } from '../../src/extract-images.js';
11
-
12
- describe('extract-images module', () => {
13
- let tmpDir;
14
-
15
- beforeEach(() => {
16
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'extract-images-test-'));
17
- });
18
-
19
- afterEach(() => {
20
- fs.rmSync(tmpDir, { recursive: true, force: true });
21
- });
22
-
23
- // 1x1 red PNG pixel as base64
24
- const TINY_PNG =
25
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==';
26
- // 1x1 white JPEG as base64
27
- const TINY_JPEG =
28
- '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAP///wAAAf/bAEMA/9sAQwD/2wBDAf///wAAAf/bAEMA/9sAQwD/2Q==';
29
-
30
- function contentHash(base64Data) {
31
- const buf = Buffer.from(base64Data, 'base64');
32
- return createHash('sha256').update(buf).digest('hex').slice(0, 8);
33
- }
34
-
35
- const PNG_HASH = contentHash(TINY_PNG);
36
- const JPEG_HASH = contentHash(TINY_JPEG);
37
-
38
- describe('extractAndSaveImages', () => {
39
- it('extracts a single PNG image from markdown', () => {
40
- const md = `# Hello\n\n![test](data:image/png;base64,${TINY_PNG})\n\nEnd.`;
41
- const result = extractAndSaveImages(md, tmpDir);
42
-
43
- expect(result.extracted).toBe(1);
44
- expect(result.markdown).toContain(
45
- `![test](images/image-${PNG_HASH}.png)`
46
- );
47
- expect(result.markdown).not.toContain('data:image');
48
-
49
- const imgPath = path.join(tmpDir, 'images', `image-${PNG_HASH}.png`);
50
- expect(fs.existsSync(imgPath)).toBe(true);
51
- const buf = fs.readFileSync(imgPath);
52
- expect(buf.length).toBeGreaterThan(0);
53
- // Verify PNG magic bytes
54
- expect(buf[0]).toBe(0x89);
55
- expect(buf[1]).toBe(0x50); // P
56
- });
57
-
58
- it('extracts multiple images with content-hash filenames', () => {
59
- const md = [
60
- `![a](data:image/png;base64,${TINY_PNG})`,
61
- `![b](data:image/jpeg;base64,${TINY_JPEG})`,
62
- ].join('\n');
63
-
64
- const result = extractAndSaveImages(md, tmpDir);
65
- expect(result.extracted).toBe(2);
66
- expect(result.markdown).toContain(`images/image-${PNG_HASH}.png`);
67
- expect(result.markdown).toContain(`images/image-${JPEG_HASH}.jpg`);
68
- });
69
-
70
- it('produces stable filenames for duplicate images', () => {
71
- const md = [
72
- `![a](data:image/png;base64,${TINY_PNG})`,
73
- `![b](data:image/png;base64,${TINY_PNG})`,
74
- ].join('\n');
75
-
76
- const result = extractAndSaveImages(md, tmpDir);
77
- expect(result.extracted).toBe(2);
78
- // Both should produce the same hash-based filename
79
- const matches = result.markdown.match(
80
- new RegExp(`image-${PNG_HASH}\\.png`, 'g')
81
- );
82
- expect(matches).toHaveLength(2);
83
- });
84
-
85
- it('handles JPEG mime type correctly', () => {
86
- const md = `![photo](data:image/jpeg;base64,${TINY_JPEG})`;
87
- const result = extractAndSaveImages(md, tmpDir);
88
-
89
- expect(result.extracted).toBe(1);
90
- expect(result.markdown).toContain(`images/image-${JPEG_HASH}.jpg`);
91
- });
92
-
93
- it('uses custom imagesDir', () => {
94
- const md = `![img](data:image/png;base64,${TINY_PNG})`;
95
- const result = extractAndSaveImages(md, tmpDir, {
96
- imagesDir: 'my-images',
97
- });
98
-
99
- expect(result.extracted).toBe(1);
100
- expect(result.markdown).toContain(`my-images/image-${PNG_HASH}.png`);
101
- expect(
102
- fs.existsSync(path.join(tmpDir, 'my-images', `image-${PNG_HASH}.png`))
103
- ).toBe(true);
104
- });
105
-
106
- it('returns markdown unchanged when no base64 images', () => {
107
- const md = '# No images\n\nJust text.';
108
- const result = extractAndSaveImages(md, tmpDir);
109
-
110
- expect(result.extracted).toBe(0);
111
- expect(result.markdown).toBe(md);
112
- expect(fs.existsSync(path.join(tmpDir, 'images'))).toBe(false);
113
- });
114
-
115
- it('preserves non-base64 image references', () => {
116
- const md =
117
- '![remote](https://example.com/img.png)\n![local](images/existing.png)';
118
- const result = extractAndSaveImages(md, tmpDir);
119
-
120
- expect(result.extracted).toBe(0);
121
- expect(result.markdown).toBe(md);
122
- });
123
-
124
- it('preserves alt text during extraction', () => {
125
- const md = `![A descriptive alt text](data:image/png;base64,${TINY_PNG})`;
126
- const result = extractAndSaveImages(md, tmpDir);
127
-
128
- expect(result.markdown).toContain(
129
- `![A descriptive alt text](images/image-${PNG_HASH}.png)`
130
- );
131
- });
132
-
133
- it('extracts a base64 image even when followed by an empty markdown title', () => {
134
- // Google Docs HTML exports embed `<img title="" alt="" src="...">`, and
135
- // the html2md pipeline can render that as `![](src "")`. The base64
136
- // extractor must still pull the image out cleanly so the rendered
137
- // markdown does not carry multi-megabyte data URIs.
138
- const md = `Hello.\n\n![](data:image/png;base64,${TINY_PNG} "")\n\nEnd.\n`;
139
- const result = extractAndSaveImages(md, tmpDir);
140
- expect(result.extracted).toBe(1);
141
- expect(result.markdown).toMatch(/images\/image-[a-f0-9]+\.png/);
142
- expect(result.markdown).not.toContain('data:image');
143
- });
144
-
145
- it('extracts a base64 image with a non-empty markdown title', () => {
146
- const md = `Hello.\n\n![alt](data:image/png;base64,${TINY_PNG} "caption")\n\nEnd.\n`;
147
- const result = extractAndSaveImages(md, tmpDir);
148
- expect(result.extracted).toBe(1);
149
- expect(result.markdown).toMatch(/images\/image-[a-f0-9]+\.png/);
150
- expect(result.markdown).not.toContain('data:image');
151
- });
152
-
153
- it('handles SVG data URIs', () => {
154
- const svgContent =
155
- '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"><rect fill="red" width="1" height="1"/></svg>';
156
- const svgBase64 = Buffer.from(svgContent).toString('base64');
157
- const svgHash = contentHash(svgBase64);
158
- const md = `![icon](data:image/svg+xml;base64,${svgBase64})`;
159
- const result = extractAndSaveImages(md, tmpDir);
160
-
161
- expect(result.extracted).toBe(1);
162
- expect(result.markdown).toContain(`images/image-${svgHash}.svg`);
163
-
164
- const imgPath = path.join(tmpDir, 'images', `image-${svgHash}.svg`);
165
- expect(fs.existsSync(imgPath)).toBe(true);
166
- const content = fs.readFileSync(imgPath, 'utf-8');
167
- expect(content).toContain('<svg');
168
- });
169
- });
170
-
171
- describe('extractBase64ToBuffers', () => {
172
- it('extracts base64 images to in-memory buffers', () => {
173
- const md = `# Hello\n\n![test](data:image/png;base64,${TINY_PNG})\n\nEnd.`;
174
- const result = extractBase64ToBuffers(md);
175
-
176
- expect(result.images).toHaveLength(1);
177
- expect(result.images[0].filename).toMatch(/^image-[0-9a-f]{8}\.png$/);
178
- expect(result.images[0].buffer.length).toBeGreaterThan(0);
179
- expect(result.markdown).toContain(`images/${result.images[0].filename}`);
180
- expect(result.markdown).not.toContain('data:image');
181
- });
182
-
183
- it('uses custom imagesDir prefix', () => {
184
- const md = `![img](data:image/png;base64,${TINY_PNG})`;
185
- const result = extractBase64ToBuffers(md, 'assets');
186
-
187
- expect(result.markdown).toContain('assets/image-');
188
- });
189
-
190
- it('does not write files to disk', () => {
191
- const md = `![img](data:image/png;base64,${TINY_PNG})`;
192
- extractBase64ToBuffers(md);
193
- expect(fs.existsSync(path.join(tmpDir, 'images'))).toBe(false);
194
- });
195
- });
196
-
197
- describe('stripBase64Images', () => {
198
- it('strips base64 images and leaves alt text placeholder', () => {
199
- const md = `![my image](data:image/png;base64,${TINY_PNG})`;
200
- const result = stripBase64Images(md);
201
-
202
- expect(result.stripped).toBe(1);
203
- expect(result.markdown).toBe('*[image: my image]*');
204
- expect(result.markdown).not.toContain('data:image');
205
- });
206
-
207
- it('leaves a visible placeholder when stripping a base64 image with empty alt', () => {
208
- // Google Docs HTML exports emit `<img alt="" src="data:image/png;base64,...">`,
209
- // which renders as `![](data:...)`. If stripping drops the markdown image
210
- // entirely, every image in the doc vanishes from the output with no
211
- // indication that anything was lost. Leave a visible placeholder so the
212
- // reader can see that an image was here.
213
- const md = `Hi.\n\n![](data:image/png;base64,${TINY_PNG})\n\nBye.\n`;
214
- const result = stripBase64Images(md);
215
-
216
- expect(result.stripped).toBe(1);
217
- expect(result.markdown).not.toMatch(/data:image/);
218
- expect(result.markdown).toMatch(/!\[|\[image/);
219
- });
220
-
221
- it('preserves remote image URLs', () => {
222
- const md = '![remote](https://example.com/img.png)';
223
- const result = stripBase64Images(md);
224
-
225
- expect(result.stripped).toBe(0);
226
- expect(result.markdown).toBe(md);
227
- });
228
-
229
- it('strips multiple base64 images', () => {
230
- const md = [
231
- `![a](data:image/png;base64,${TINY_PNG})`,
232
- `![b](data:image/jpeg;base64,${TINY_JPEG})`,
233
- '![c](https://example.com/img.png)',
234
- ].join('\n');
235
- const result = stripBase64Images(md);
236
-
237
- expect(result.stripped).toBe(2);
238
- expect(result.markdown).toContain('*[image: a]*');
239
- expect(result.markdown).toContain('*[image: b]*');
240
- expect(result.markdown).toContain('https://example.com/img.png');
241
- });
242
- });
243
-
244
- describe('hasBase64Images', () => {
245
- it('returns true when markdown has base64 images', () => {
246
- expect(hasBase64Images(`![x](data:image/png;base64,${TINY_PNG})`)).toBe(
247
- true
248
- );
249
- });
250
-
251
- it('returns false when no base64 images', () => {
252
- expect(hasBase64Images('![x](https://example.com/img.png)')).toBe(false);
253
- });
254
-
255
- it('returns false for empty string', () => {
256
- expect(hasBase64Images('')).toBe(false);
257
- });
258
- });
259
- });
@@ -1,54 +0,0 @@
1
- import nock from '../helpers/nock.js';
2
- import { jest } from '@jest/globals';
3
- import { fetchHtml, fetchHtmlReceipt } from '../../src/lib.js';
4
-
5
- afterEach(() => {
6
- nock.cleanAll();
7
- });
8
-
9
- describe('fetchHtml', () => {
10
- it('requests identity encoding for document HTML fetches', async () => {
11
- nock('https://example.com', {
12
- reqheaders: {
13
- 'accept-encoding': 'identity',
14
- },
15
- })
16
- .get('/article')
17
- .reply(200, '<html><body>ok</body></html>', {
18
- 'content-type': 'text/html; charset=utf-8',
19
- });
20
-
21
- await expect(fetchHtml('https://example.com/article')).resolves.toContain(
22
- 'ok'
23
- );
24
- });
25
-
26
- it('returns an exact-byte receipt through an injectable transport', async () => {
27
- const body = Buffer.from([0x00, 0xff, 0x41]);
28
- const transport = jest.fn(async ({ url, signal }) => ({
29
- body,
30
- finalUrl: `${url}/final`,
31
- status: 206,
32
- headers: { 'content-type': 'application/octet-stream', etag: 'v1' },
33
- diagnostics: { outcome: 'response' },
34
- signal,
35
- }));
36
- const controller = new globalThis.AbortController();
37
-
38
- const receipt = await fetchHtmlReceipt('https://example.com/bytes', {
39
- transport,
40
- signal: controller.signal,
41
- });
42
-
43
- expect(Buffer.from(receipt.body)).toEqual(body);
44
- expect(receipt).toMatchObject({
45
- finalUrl: 'https://example.com/bytes/final',
46
- status: 206,
47
- headers: { 'content-type': 'application/octet-stream', etag: 'v1' },
48
- diagnostics: { outcome: 'response' },
49
- });
50
- expect(transport).toHaveBeenCalledWith(
51
- expect.objectContaining({ signal: controller.signal })
52
- );
53
- });
54
- });
@@ -1,74 +0,0 @@
1
- import { extractFigures } from '../../src/figures.js';
2
-
3
- describe('figures module', () => {
4
- describe('extractFigures', () => {
5
- it('extracts figures with captions', () => {
6
- const html = `
7
- <figure>
8
- <img src="https://example.com/fig1.png" alt="Graph">
9
- <figcaption>Figure 1. The relationship graph</figcaption>
10
- </figure>
11
- `;
12
- const figures = extractFigures(html, 'https://example.com');
13
- expect(figures).toHaveLength(1);
14
- expect(figures[0].figureNum).toBe(1);
15
- expect(figures[0].src).toBe('https://example.com/fig1.png');
16
- expect(figures[0].caption).toContain('Figure 1');
17
- });
18
-
19
- it('handles Russian figure captions', () => {
20
- const html = `
21
- <figure>
22
- <img src="https://example.com/fig.png" alt="">
23
- <figcaption>Рис. 5. Описание</figcaption>
24
- </figure>
25
- `;
26
- const figures = extractFigures(html, 'https://example.com');
27
- expect(figures[0].figureNum).toBe(5);
28
- });
29
-
30
- it('uses sequential numbering when no caption number', () => {
31
- const html = `
32
- <figure>
33
- <img src="https://example.com/a.png" alt="">
34
- <figcaption>Some caption without number</figcaption>
35
- </figure>
36
- <figure>
37
- <img src="https://example.com/b.png" alt="">
38
- </figure>
39
- `;
40
- const figures = extractFigures(html, 'https://example.com');
41
- expect(figures).toHaveLength(2);
42
- expect(figures[0].figureNum).toBe(1);
43
- expect(figures[1].figureNum).toBe(2);
44
- });
45
-
46
- it('skips SVG images', () => {
47
- const html = `
48
- <figure>
49
- <img src="https://example.com/icon.svg" alt="">
50
- </figure>
51
- `;
52
- const figures = extractFigures(html, 'https://example.com');
53
- expect(figures).toHaveLength(0);
54
- });
55
-
56
- it('resolves relative URLs', () => {
57
- const html = `
58
- <figure>
59
- <img src="/images/fig.png" alt="">
60
- </figure>
61
- `;
62
- const figures = extractFigures(html, 'https://example.com');
63
- expect(figures[0].src).toBe('https://example.com/images/fig.png');
64
- });
65
-
66
- it('returns empty array for no figures', () => {
67
- const figures = extractFigures(
68
- '<div>no figures</div>',
69
- 'https://example.com'
70
- );
71
- expect(figures).toHaveLength(0);
72
- });
73
- });
74
- });
@@ -1,28 +0,0 @@
1
- import nock from '../helpers/nock.js';
2
- import { fetchGoogleDocAsMarkdown } from '../../src/gdocs.js';
3
-
4
- afterEach(() => {
5
- nock.cleanAll();
6
- });
7
-
8
- describe('Google Docs public export headers', () => {
9
- it('requests identity encoding for public export fetches', async () => {
10
- nock('https://docs.google.com', {
11
- reqheaders: {
12
- 'accept-encoding': 'identity',
13
- },
14
- })
15
- .get('/document/d/export-doc/export')
16
- .query({ format: 'html' })
17
- .reply(200, '<html><body><h1>Export Doc</h1></body></html>', {
18
- 'content-type': 'text/html; charset=utf-8',
19
- });
20
-
21
- const result = await fetchGoogleDocAsMarkdown(
22
- 'https://docs.google.com/document/d/export-doc/edit'
23
- );
24
-
25
- expect(result.markdown).toContain('Export Doc');
26
- expect(nock.isDone()).toBe(true);
27
- });
28
- });
@@ -1,36 +0,0 @@
1
- import {
2
- normalizeGoogleDocsExportMarkdown,
3
- preprocessGoogleDocsExportHtml,
4
- } from '../../src/gdocs.js';
5
- import { convertHtmlToMarkdown } from '../../src/lib.js';
6
-
7
- describe('preprocessGoogleDocsExportHtml', () => {
8
- it('keeps multi-paragraph table cells on one GFM row (issue #110)', () => {
9
- const html = `
10
- <table>
11
- <tr><td><p>Feature</p></td><td><p>Notes</p></td></tr>
12
- <tr>
13
- <td>
14
- <p>First paragraph.</p>
15
- <p>Second paragraph.</p>
16
- <p>Third paragraph.</p>
17
- </td>
18
- <td><p>x</p></td>
19
- </tr>
20
- </table>
21
- `;
22
-
23
- const preprocessed = preprocessGoogleDocsExportHtml(html);
24
- const markdown = normalizeGoogleDocsExportMarkdown(
25
- convertHtmlToMarkdown(preprocessed.html)
26
- );
27
-
28
- expect(preprocessed.html).toContain(
29
- '<td>First paragraph.<br><br>Second paragraph.<br><br>Third paragraph.</td>'
30
- );
31
- expect(markdown).toContain(
32
- '| First paragraph.<br><br>Second paragraph.<br><br>Third paragraph. | x |'
33
- );
34
- expect(markdown).not.toContain('First paragraph. \n');
35
- });
36
- });