@link-assistant/web-capture 2.0.0 → 2.0.2

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 (117) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/bin/web-capture.js +2 -5
  3. package/package.json +36 -23
  4. package/src/archive.js +0 -1
  5. package/src/binary-download.js +0 -1
  6. package/src/docx.js +13 -10
  7. package/src/fetch.js +0 -1
  8. package/src/gdocs-images.js +0 -1
  9. package/src/gdocs.js +0 -1
  10. package/src/github.js +0 -1
  11. package/src/lib.js +0 -1
  12. package/src/markdown.js +0 -1
  13. package/src/shared-dialog.js +0 -1
  14. package/src/stream.js +0 -1
  15. package/src/transport.js +23 -5
  16. package/src/txt.js +0 -1
  17. package/.changeset/README.md +0 -8
  18. package/.changeset/config.json +0 -11
  19. package/.dockerignore +0 -6
  20. package/.jscpd.json +0 -19
  21. package/.lenv +0 -8
  22. package/.prettierignore +0 -8
  23. package/.prettierrc +0 -10
  24. package/Dockerfile +0 -52
  25. package/docker-compose.yml +0 -11
  26. package/eslint.config.js +0 -112
  27. package/examples/js/capture_cache.js +0 -12
  28. package/examples/js/engine_comparison.js +0 -39
  29. package/examples/js/html_download.js +0 -23
  30. package/examples/js/image_download.js +0 -36
  31. package/examples/js/markdown_download.js +0 -23
  32. package/examples/js/playwright_example.js +0 -23
  33. package/examples/python/capture_client.py +0 -20
  34. package/examples/python/html_download.py +0 -3
  35. package/examples/python/image_download.py +0 -16
  36. package/examples/python/markdown_download.py +0 -3
  37. package/examples/python/playwright_example.py +0 -22
  38. package/jest.config.mjs +0 -17
  39. package/tests/ci/dependency-freshness.test.js +0 -74
  40. package/tests/ci/detect-code-changes.test.js +0 -187
  41. package/tests/ci/docker-install.test.js +0 -31
  42. package/tests/ci/rust-dockerfile.test.js +0 -54
  43. package/tests/ci/workflow-policy.test.js +0 -79
  44. package/tests/e2e/docker.test.js +0 -186
  45. package/tests/e2e/process.test.js +0 -215
  46. package/tests/fixtures/bold-coalesce-and-leakage.html +0 -27
  47. package/tests/fixtures/deepwiki/deepwiki-full-page.png +0 -0
  48. package/tests/fixtures/deepwiki/index.md +0 -1500
  49. package/tests/fixtures/deepwiki/part-1.md +0 -776
  50. package/tests/fixtures/habr-articles.js +0 -45
  51. package/tests/fixtures/heading-numbering.html +0 -19
  52. package/tests/fixtures/list-item-with-br.html +0 -8
  53. package/tests/fixtures/ol-continuous-numbering.html +0 -12
  54. package/tests/fixtures/paragraph-vs-line-break.html +0 -11
  55. package/tests/helpers/nock.js +0 -10
  56. package/tests/helpers/screenshot.js +0 -10
  57. package/tests/helpers/search-fixture.cjs +0 -17
  58. package/tests/helpers/temporary-directory.js +0 -12
  59. package/tests/integration/api-endpoints.test.js +0 -418
  60. package/tests/integration/archive-default-layout.test.js +0 -37
  61. package/tests/integration/browser-engines.test.js +0 -80
  62. package/tests/integration/browser-packaged-path.test.js +0 -45
  63. package/tests/integration/deepwiki-download.test.js +0 -144
  64. package/tests/integration/deepwiki-rendered-page.mock.test.js +0 -121
  65. package/tests/integration/formalai-contract.test.js +0 -214
  66. package/tests/integration/gdocs-public-doc.test.js +0 -315
  67. package/tests/integration/github-readme.test.js +0 -133
  68. package/tests/integration/google-drive-image.test.js +0 -246
  69. package/tests/integration/habr-article.test.js +0 -337
  70. package/tests/integration/search-endpoint.test.js +0 -88
  71. package/tests/integration/stackoverflow-download.test.js +0 -173
  72. package/tests/integration/wikipedia-download.test.js +0 -134
  73. package/tests/jest.setup.mjs +0 -7
  74. package/tests/mock/index.test.js +0 -225
  75. package/tests/unit/animation.test.js +0 -33
  76. package/tests/unit/batch.test.js +0 -114
  77. package/tests/unit/bold-coalesce-and-leakage.test.js +0 -54
  78. package/tests/unit/browser-launch-options.test.js +0 -42
  79. package/tests/unit/browser.test.js +0 -172
  80. package/tests/unit/cache.test.js +0 -182
  81. package/tests/unit/cli.test.js +0 -399
  82. package/tests/unit/convertGoogleDriveUrl.test.js +0 -89
  83. package/tests/unit/convertRelativeUrls.test.js +0 -34
  84. package/tests/unit/extract-images.test.js +0 -259
  85. package/tests/unit/fetch-html.test.js +0 -54
  86. package/tests/unit/figures.test.js +0 -74
  87. package/tests/unit/gdocs-export-headers.test.js +0 -28
  88. package/tests/unit/gdocs-preprocess.test.js +0 -36
  89. package/tests/unit/gdocs.test.js +0 -1490
  90. package/tests/unit/github.test.js +0 -156
  91. package/tests/unit/heading-image.test.js +0 -49
  92. package/tests/unit/heading-numbering.test.js +0 -28
  93. package/tests/unit/html2md-br-in-list-item.test.js +0 -23
  94. package/tests/unit/html2md-ol-numbering.test.js +0 -20
  95. package/tests/unit/html2md.test.js +0 -695
  96. package/tests/unit/image-mode-defaults.test.js +0 -54
  97. package/tests/unit/kreuzberg.test.js +0 -249
  98. package/tests/unit/latex.test.js +0 -123
  99. package/tests/unit/localize-images.test.js +0 -114
  100. package/tests/unit/markdown.test.js +0 -36
  101. package/tests/unit/metadata.test.js +0 -140
  102. package/tests/unit/package-runtime-dependencies.test.js +0 -74
  103. package/tests/unit/paragraph-vs-line-break.test.js +0 -56
  104. package/tests/unit/postprocess.test.js +0 -110
  105. package/tests/unit/proxy-headers.test.js +0 -100
  106. package/tests/unit/publish-verification.test.js +0 -54
  107. package/tests/unit/retry.test.js +0 -111
  108. package/tests/unit/search.test.js +0 -339
  109. package/tests/unit/shared-dialog.test.js +0 -173
  110. package/tests/unit/stackoverflow.test.js +0 -32
  111. package/tests/unit/stream.test.js +0 -83
  112. package/tests/unit/verify.test.js +0 -182
  113. package/tests/xpaste/xpaste-screenshot-markdown.mock.test.js +0 -375
  114. package/tests/xpaste/xpaste.e2e.test.js +0 -36
  115. package/tests/xpaste/xpaste.integration.test.js +0 -41
  116. package/tests/xpaste/xpaste.mock.test.js +0 -261
  117. package/tests/xpaste/xpaste.unit.test.js +0 -119
@@ -1,182 +0,0 @@
1
- import {
2
- normalizeText,
3
- normalizeCode,
4
- verifyMarkdownContent,
5
- } from '../../src/verify.js';
6
-
7
- describe('verify module', () => {
8
- describe('normalizeText', () => {
9
- it('lowercases text', () => {
10
- expect(normalizeText('Hello World')).toBe('hello world');
11
- });
12
-
13
- it('normalizes unicode spaces', () => {
14
- expect(normalizeText('hello\u00A0world')).toBe('hello world');
15
- });
16
-
17
- it('normalizes curly quotes', () => {
18
- expect(normalizeText('\u201Chello\u201D')).toBe('"hello"');
19
- });
20
-
21
- it('removes LaTeX delimiters', () => {
22
- expect(normalizeText('$x + y$')).toBe('x + y');
23
- expect(normalizeText('$$x + y$$')).toBe('x + y');
24
- });
25
-
26
- it('normalizes arrows', () => {
27
- expect(normalizeText('\u2192')).toBe('->');
28
- expect(normalizeText('\\to')).toBe('->');
29
- });
30
-
31
- it('removes \\displaystyle', () => {
32
- expect(normalizeText('\\displaystyle x')).toBe('x');
33
- });
34
-
35
- it('extracts text from \\text{}', () => {
36
- expect(normalizeText('\\text{hello}')).toBe('hello');
37
- });
38
- });
39
-
40
- describe('normalizeCode', () => {
41
- it('lowercases and collapses whitespace', () => {
42
- expect(normalizeCode(' Hello World ')).toBe('hello world');
43
- });
44
-
45
- it('normalizes multiplication sign', () => {
46
- expect(normalizeCode('2\u00D73')).toBe('2x3');
47
- });
48
- });
49
-
50
- describe('verifyMarkdownContent', () => {
51
- it('verifies title presence', () => {
52
- const webContent = {
53
- title: 'My Article',
54
- headings: [],
55
- paragraphs: [],
56
- codeBlocks: [],
57
- formulas: [],
58
- blockquoteFormulas: [],
59
- listItems: [],
60
- links: [],
61
- figures: [],
62
- };
63
- const markdown = '# My Article\n\nSome content here.';
64
- const result = verifyMarkdownContent(webContent, markdown);
65
- expect(result.missing.title).toBe(false);
66
- expect(result.passedChecks).toBeGreaterThan(0);
67
- });
68
-
69
- it('detects missing title', () => {
70
- const webContent = {
71
- title: 'Missing Title',
72
- headings: [],
73
- paragraphs: [],
74
- codeBlocks: [],
75
- formulas: [],
76
- blockquoteFormulas: [],
77
- listItems: [],
78
- links: [],
79
- figures: [],
80
- };
81
- const markdown = '# Different Title\n\nContent.';
82
- const result = verifyMarkdownContent(webContent, markdown);
83
- expect(result.missing.title).toBe(true);
84
- });
85
-
86
- it('verifies headings', () => {
87
- const webContent = {
88
- title: '',
89
- headings: [
90
- { level: 'h2', text: 'Introduction' },
91
- { level: 'h3', text: 'Background' },
92
- ],
93
- paragraphs: [],
94
- codeBlocks: [],
95
- formulas: [],
96
- blockquoteFormulas: [],
97
- listItems: [],
98
- links: [],
99
- figures: [],
100
- };
101
- const markdown = '## Introduction\n\n### Background\n\nText.';
102
- const result = verifyMarkdownContent(webContent, markdown);
103
- expect(result.missing.headings).toHaveLength(0);
104
- });
105
-
106
- it('detects missing headings', () => {
107
- const webContent = {
108
- title: '',
109
- headings: [{ level: 'h2', text: 'Unique Heading XYZ' }],
110
- paragraphs: [],
111
- codeBlocks: [],
112
- formulas: [],
113
- blockquoteFormulas: [],
114
- listItems: [],
115
- links: [],
116
- figures: [],
117
- };
118
- const markdown = '## Different Heading\n\nText.';
119
- const result = verifyMarkdownContent(webContent, markdown);
120
- expect(result.missing.headings).toContain('Unique Heading XYZ');
121
- });
122
-
123
- it('verifies code blocks with fuzzy matching', () => {
124
- const webContent = {
125
- title: '',
126
- headings: [],
127
- paragraphs: [],
128
- codeBlocks: [
129
- 'function hello() {\n console.log("hello");\n return true;\n}',
130
- ],
131
- formulas: [],
132
- blockquoteFormulas: [],
133
- listItems: [],
134
- links: [],
135
- figures: [],
136
- };
137
- const markdown =
138
- '```javascript\nfunction hello() {\n console.log("hello");\n return true;\n}\n```';
139
- const result = verifyMarkdownContent(webContent, markdown);
140
- expect(result.missing.codeBlocks).toHaveLength(0);
141
- });
142
-
143
- it('calculates pass rate', () => {
144
- const webContent = {
145
- title: 'Test',
146
- headings: [{ level: 'h2', text: 'Found' }],
147
- paragraphs: [],
148
- codeBlocks: [],
149
- formulas: [],
150
- blockquoteFormulas: [],
151
- listItems: [],
152
- links: [],
153
- figures: [],
154
- };
155
- const markdown = '# Test\n\n## Found\n\nContent.';
156
- const result = verifyMarkdownContent(webContent, markdown);
157
- expect(result.passRate).toBe(1.0);
158
- expect(result.success).toBe(true);
159
- });
160
-
161
- it('checks figure images when hasLocalImages is set', () => {
162
- const webContent = {
163
- title: '',
164
- headings: [],
165
- paragraphs: [],
166
- codeBlocks: [],
167
- formulas: [],
168
- blockquoteFormulas: [],
169
- listItems: [],
170
- links: [],
171
- figures: [1, 2, 3],
172
- };
173
- const markdown =
174
- '![Figure 1](images/figure-1.png)\n![Figure 2](images/figure-2.png)\n![Figure 3](images/figure-3.png)';
175
- const result = verifyMarkdownContent(webContent, markdown, {
176
- hasLocalImages: true,
177
- expectedFigures: 3,
178
- });
179
- expect(result.missing.images).toBe(0);
180
- });
181
- });
182
- });
@@ -1,375 +0,0 @@
1
- /**
2
- * Integration tests to verify that markdown extraction from xpaste.pro
3
- * includes ALL text visible in screenshots of the same page
4
- *
5
- * This ensures 100% coverage of visual content extraction as required in issue #15
6
- */
7
-
8
- import request from 'supertest';
9
- import nock from '../helpers/nock.js';
10
- import fs from 'fs';
11
- import path from 'path';
12
- import { fileURLToPath } from 'url';
13
-
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
-
16
- let app;
17
-
18
- beforeAll(async () => {
19
- app = (await import('../../src/index.js')).app;
20
- });
21
-
22
- describe('xpaste.pro screenshot-to-markdown content matching', () => {
23
- beforeEach(() => {
24
- nock.cleanAll();
25
- });
26
-
27
- describe('GET /markdown for t4q0Lsp0', () => {
28
- const testUrl = 'https://xpaste.pro/p/t4q0Lsp0';
29
-
30
- // Load the actual HTML page content that was saved
31
- const htmlPath = path.join(
32
- __dirname,
33
- '../../../tests/xpaste/data/t4q0Lsp0-page.html'
34
- );
35
- const rawTextPath = path.join(
36
- __dirname,
37
- '../../../tests/xpaste/data/t4q0Lsp0-actual-content.txt'
38
- );
39
- let testHtml;
40
- let rawText;
41
-
42
- beforeAll(() => {
43
- if (fs.existsSync(htmlPath)) {
44
- testHtml = fs.readFileSync(htmlPath, 'utf-8');
45
- }
46
- if (fs.existsSync(rawTextPath)) {
47
- rawText = fs.readFileSync(rawTextPath, 'utf-8');
48
- }
49
- });
50
-
51
- beforeEach(() => {
52
- if (rawText) {
53
- nock('https://xpaste.pro')
54
- .persist()
55
- .get('/p/t4q0Lsp0/raw')
56
- .reply(200, rawText, {
57
- 'content-type': 'text/plain; charset=utf-8',
58
- });
59
- }
60
- });
61
-
62
- it('should include all header/metadata text visible in screenshot', async () => {
63
- if (!testHtml) {
64
- console.warn('Skipping test - HTML file not found');
65
- return;
66
- }
67
-
68
- nock('https://xpaste.pro')
69
- .get('/p/t4q0Lsp0')
70
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
71
-
72
- const response = await request(app)
73
- .get('/markdown')
74
- .query({ url: testUrl });
75
-
76
- expect(response.status).toBe(200);
77
- expect(response.type).toBe('text/markdown');
78
-
79
- // Verify page title/branding (top of screenshot)
80
- expect(response.text).toContain('Xpaste');
81
- expect(response.text).toContain('xPaste');
82
-
83
- // Verify metadata section (visible in screenshot)
84
- expect(response.text).toContain('Формат');
85
- expect(response.text).toContain('text');
86
- expect(response.text).toContain('Время создания');
87
- expect(response.text).toContain('07.07.2021');
88
- expect(response.text).toContain('12:48 UTC');
89
- expect(response.text).toContain('Будет удалена');
90
- expect(response.text).toContain('05.07.2031');
91
- });
92
-
93
- it('should include all SQL query content visible in screenshot', async () => {
94
- if (!testHtml) {
95
- console.warn('Skipping test - HTML file not found');
96
- return;
97
- }
98
-
99
- nock('https://xpaste.pro')
100
- .get('/p/t4q0Lsp0')
101
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
102
-
103
- const response = await request(app)
104
- .get('/markdown')
105
- .query({ url: testUrl });
106
-
107
- expect(response.status).toBe(200);
108
-
109
- // Verify all 4 SQL queries visible in screenshot are present
110
-
111
- // Query #1 metadata (lines 1-8 in screenshot)
112
- expect(response.text).toContain('# 1');
113
- expect(response.text).toContain('Time: 210707 15:39:36');
114
- expect(response.text).toMatch(
115
- /User@Host:\s+1703313381\[1703313381\]\s+@\s+\[136\.243\.53\.188\]/
116
- );
117
- expect(response.text).toContain('Id: 1138102510');
118
- expect(response.text).toContain('Schema: xxxxxx');
119
- expect(response.text).toContain('Query_time: 2.182754');
120
- expect(response.text).toContain('Rows_examined: 324036');
121
-
122
- // Query #2 metadata (lines 19-28 in screenshot)
123
- expect(response.text).toContain('# 2');
124
- expect(response.text).toContain('Time: 210707 15:38:11');
125
- expect(response.text).toContain('Id: 1138100591');
126
- expect(response.text).toContain('Query_time: 9.259469');
127
- expect(response.text).toContain('DELETE FROM phpbb_post_revisions');
128
- expect(response.text).toContain('WHERE post_id = 10472158');
129
-
130
- // Query #3 metadata (lines 30-46 in screenshot)
131
- expect(response.text).toContain('# 3');
132
- expect(response.text).toContain('Time: 210707 15:32:18');
133
- expect(response.text).toContain('Id: 1138094492');
134
- expect(response.text).toContain('Query_time: 1.113014');
135
- expect(response.text).toContain('Rows_examined: 521597');
136
-
137
- // Query #4 metadata (lines 48-64 in screenshot)
138
- expect(response.text).toContain('# 4');
139
- expect(response.text).toContain('Time: 210707 15:27:56');
140
- expect(response.text).toContain('Id: 1138089558');
141
- expect(response.text).toContain('Query_time: 4.682828');
142
- expect(response.text).toContain('Rows_examined: 2457916');
143
-
144
- // Verify SQL statements contain key elements
145
- expect(response.text).toContain('SELECT f.*, t.*, p.*, u.*');
146
- expect(response.text).toContain('phpbb_posts');
147
- expect(response.text).toContain('phpbb_users');
148
- expect(response.text).toContain('phpbb_topics');
149
- expect(response.text).toContain('phpbb_forums');
150
- });
151
-
152
- it('should include all footer text visible in screenshot', async () => {
153
- if (!testHtml) {
154
- console.warn('Skipping test - HTML file not found');
155
- return;
156
- }
157
-
158
- nock('https://xpaste.pro')
159
- .get('/p/t4q0Lsp0')
160
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
161
-
162
- const response = await request(app)
163
- .get('/markdown')
164
- .query({ url: testUrl });
165
-
166
- expect(response.status).toBe(200);
167
-
168
- // Verify footer elements (bottom of screenshot)
169
- expect(response.text).toContain('xPaste');
170
- expect(response.text).toContain('Упакуем пароль или код в');
171
- expect(response.text).toMatch(/cсылку для передачи/); // Note: 'c' can be different Unicode char
172
- expect(response.text).toContain('Сделано в');
173
- expect(response.text).toContain('Southbridge');
174
- expect(response.text).toContain('Справка');
175
- expect(response.text).toContain('Политика конфиденциальности');
176
- });
177
-
178
- it('should include language selector links visible in screenshot', async () => {
179
- if (!testHtml) {
180
- console.warn('Skipping test - HTML file not found');
181
- return;
182
- }
183
-
184
- nock('https://xpaste.pro')
185
- .get('/p/t4q0Lsp0')
186
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
187
-
188
- const response = await request(app)
189
- .get('/markdown')
190
- .query({ url: testUrl });
191
-
192
- expect(response.status).toBe(200);
193
-
194
- // Verify language links (top right of screenshot)
195
- expect(response.text).toContain('Ru');
196
- expect(response.text).toContain('En');
197
- });
198
-
199
- it('should include RAW link visible in screenshot', async () => {
200
- if (!testHtml) {
201
- console.warn('Skipping test - HTML file not found');
202
- return;
203
- }
204
-
205
- nock('https://xpaste.pro')
206
- .get('/p/t4q0Lsp0')
207
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
208
-
209
- const response = await request(app)
210
- .get('/markdown')
211
- .query({ url: testUrl });
212
-
213
- expect(response.status).toBe(200);
214
-
215
- // Verify RAW button (visible in screenshot)
216
- expect(response.text).toContain('RAW');
217
- });
218
-
219
- it('should embed the raw text file as a markdown text block', async () => {
220
- if (!testHtml || !rawText) {
221
- console.warn('Skipping test - fixture files not found');
222
- return;
223
- }
224
-
225
- nock('https://xpaste.pro')
226
- .get('/p/t4q0Lsp0')
227
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
228
-
229
- const response = await request(app)
230
- .get('/markdown')
231
- .query({ url: testUrl });
232
-
233
- expect(response.status).toBe(200);
234
- expect(response.type).toBe('text/markdown');
235
- expect(response.text).toContain('## xpaste-pro-t4q0Lsp0.txt');
236
- expect(response.text).toContain('```text');
237
- expect(response.text).toContain(
238
- rawText.split('\n')[0].replace(/\r/g, '')
239
- );
240
- expect(response.text).toContain('DELETE FROM phpbb_post_revisions');
241
- });
242
-
243
- it('should verify correct element ordering - heading and languages at top', async () => {
244
- if (!testHtml) {
245
- console.warn('Skipping test - HTML file not found');
246
- return;
247
- }
248
-
249
- nock('https://xpaste.pro')
250
- .get('/p/t4q0Lsp0')
251
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
252
-
253
- const response = await request(app)
254
- .get('/markdown')
255
- .query({ url: testUrl });
256
-
257
- expect(response.status).toBe(200);
258
-
259
- const lines = response.text.split('\n');
260
-
261
- // Find line numbers of key elements
262
- const headingLine = lines.findIndex((l) => l.includes('Упакуем пароль'));
263
- const languageLine = lines.findIndex(
264
- (l) => l.includes('[Ru]') || l.includes('[En]')
265
- );
266
- const formatLine = lines.findIndex((l) => l.includes('Формат:'));
267
- const firstQueryLine = lines.findIndex(
268
- (l) => l.includes('# 1') && l.includes('#')
269
- );
270
-
271
- // Verify heading comes before metadata (as shown in screenshot)
272
- expect(headingLine).toBeGreaterThan(0);
273
- expect(headingLine).toBeLessThan(formatLine);
274
-
275
- // Verify languages come before metadata (as shown in screenshot)
276
- expect(languageLine).toBeGreaterThan(0);
277
- expect(languageLine).toBeLessThan(formatLine);
278
-
279
- // Verify heading comes before main content
280
- expect(headingLine).toBeLessThan(firstQueryLine);
281
-
282
- // Verify metadata comes before main content
283
- expect(formatLine).toBeLessThan(firstQueryLine);
284
-
285
- console.log(
286
- `✓ Element ordering verified: heading (line ${headingLine + 1}) -> languages (line ${languageLine + 1}) -> metadata (line ${formatLine + 1}) -> content (line ${firstQueryLine + 1})`
287
- );
288
- });
289
-
290
- it('should verify markdown extraction matches screenshot - comprehensive check', async () => {
291
- if (!testHtml) {
292
- console.warn('Skipping test - HTML file not found');
293
- return;
294
- }
295
-
296
- nock('https://xpaste.pro')
297
- .get('/p/t4q0Lsp0')
298
- .reply(200, testHtml, { 'content-type': 'text/html; charset=utf-8' });
299
-
300
- const response = await request(app)
301
- .get('/markdown')
302
- .query({ url: testUrl });
303
-
304
- expect(response.status).toBe(200);
305
-
306
- // Define all key text elements visible in the screenshot
307
- const requiredElements = [
308
- // Header/branding
309
- 'Xpaste',
310
-
311
- // Metadata section
312
- 'Формат:',
313
- 'text',
314
- 'Время создания:',
315
- '07.07.2021, 12:48 UTC',
316
- 'Будет удалена:',
317
- '05.07.2031, 12:48 UTC',
318
-
319
- // Query #1
320
- '# 1',
321
- 'Time: 210707 15:39:36',
322
- 'User@Host: 1703313381[1703313381]',
323
- '[136.243.53.188]',
324
- 'Id: 1138102510',
325
- 'Query_time: 2.182754',
326
-
327
- // Query #2
328
- '# 2',
329
- 'Time: 210707 15:38:11',
330
- 'Id: 1138100591',
331
- 'DELETE FROM phpbb_post_revisions',
332
-
333
- // Query #3
334
- '# 3',
335
- 'Time: 210707 15:32:18',
336
- 'Id: 1138094492',
337
-
338
- // Query #4
339
- '# 4',
340
- 'Time: 210707 15:27:56',
341
- 'Id: 1138089558',
342
-
343
- // Footer
344
- 'Упакуем пароль',
345
- 'Southbridge',
346
- 'Справка',
347
- 'Политика конфиденциальности',
348
-
349
- // Language links
350
- 'Ru',
351
- 'En',
352
-
353
- // RAW button
354
- 'RAW',
355
- ];
356
-
357
- // Verify ALL elements are present
358
- const missingElements = [];
359
- requiredElements.forEach((element) => {
360
- if (!response.text.includes(element)) {
361
- missingElements.push(element);
362
- }
363
- });
364
-
365
- expect(missingElements).toEqual([]);
366
-
367
- // Log success with coverage stats
368
- if (missingElements.length === 0) {
369
- console.log(
370
- `✓ All ${requiredElements.length} screenshot text elements found in markdown`
371
- );
372
- }
373
- });
374
- });
375
- });
@@ -1,36 +0,0 @@
1
- import getPort from 'get-port';
2
- import nock from '../helpers/nock.js';
3
- import { app } from '../../src/index.js';
4
-
5
- describe('xpaste.pro HTTP e2e', () => {
6
- let server;
7
-
8
- afterEach(async () => {
9
- nock.cleanAll();
10
- if (server) {
11
- await new Promise((resolve) => server.close(resolve));
12
- server = null;
13
- }
14
- });
15
-
16
- it('serves raw paste text through the real HTTP server', async () => {
17
- const pasteText = 'console.log("xpaste fixture");\n';
18
- nock('https://xpaste.pro')
19
- .get('/p/js123/raw')
20
- .reply(200, pasteText, { 'content-type': 'text/plain; charset=utf-8' });
21
-
22
- const port = await getPort();
23
- server = app.listen(port);
24
-
25
- const response = await fetch(
26
- `http://127.0.0.1:${port}/txt?url=${encodeURIComponent('https://xpaste.pro/p/js123')}`
27
- );
28
-
29
- expect(response.status).toBe(200);
30
- expect(response.headers.get('content-type')).toMatch(/text\/plain/);
31
- expect(response.headers.get('content-disposition')).toContain(
32
- 'xpaste-pro-js123.txt'
33
- );
34
- expect(await response.text()).toBe(pasteText);
35
- });
36
- });
@@ -1,41 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { convertHtmlToMarkdownEnhanced } from '../../src/lib.js';
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const DATA_DIR = path.join(__dirname, '../../../tests/xpaste/data');
8
-
9
- describe('xpaste.pro fixture integration', () => {
10
- it('converts the captured page fixture with the same visual ordering as the screenshot', () => {
11
- const html = fs.readFileSync(
12
- path.join(DATA_DIR, 't4q0Lsp0-page.html'),
13
- 'utf-8'
14
- );
15
- const { markdown } = convertHtmlToMarkdownEnhanced(
16
- html,
17
- 'https://xpaste.pro/p/t4q0Lsp0',
18
- {
19
- extractMetadata: false,
20
- postProcess: false,
21
- }
22
- );
23
-
24
- const headingIndex = markdown.indexOf('Упакуем пароль');
25
- const languageIndexes = ['[Ru]', '[En]']
26
- .map((label) => markdown.indexOf(label))
27
- .filter((index) => index >= 0);
28
- const languageIndex = Math.min(...languageIndexes);
29
- const formatIndex = markdown.indexOf('Формат:');
30
- const firstQueryIndex = markdown.indexOf('# 1');
31
-
32
- expect(headingIndex).toBeGreaterThanOrEqual(0);
33
- expect(languageIndexes).not.toHaveLength(0);
34
- expect(languageIndex).toBeGreaterThanOrEqual(0);
35
- expect(formatIndex).toBeGreaterThanOrEqual(0);
36
- expect(firstQueryIndex).toBeGreaterThanOrEqual(0);
37
- expect(headingIndex).toBeLessThan(formatIndex);
38
- expect(languageIndex).toBeLessThan(formatIndex);
39
- expect(formatIndex).toBeLessThan(firstQueryIndex);
40
- });
41
- });