pdfdancer-client-typescript 1.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 (52) hide show
  1. package/.eslintrc.js +19 -0
  2. package/README.md +267 -0
  3. package/dist/__tests__/e2e/test-helpers.d.ts +32 -0
  4. package/dist/__tests__/e2e/test-helpers.d.ts.map +1 -0
  5. package/dist/__tests__/e2e/test-helpers.js +157 -0
  6. package/dist/__tests__/e2e/test-helpers.js.map +1 -0
  7. package/dist/client-v1.d.ts +169 -0
  8. package/dist/client-v1.d.ts.map +1 -0
  9. package/dist/client-v1.js +558 -0
  10. package/dist/client-v1.js.map +1 -0
  11. package/dist/exceptions.d.ts +43 -0
  12. package/dist/exceptions.d.ts.map +1 -0
  13. package/dist/exceptions.js +66 -0
  14. package/dist/exceptions.js.map +1 -0
  15. package/dist/index.d.ts +13 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +33 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/models.d.ts +216 -0
  20. package/dist/models.d.ts.map +1 -0
  21. package/dist/models.js +427 -0
  22. package/dist/models.js.map +1 -0
  23. package/dist/paragraph-builder.d.ts +67 -0
  24. package/dist/paragraph-builder.d.ts.map +1 -0
  25. package/dist/paragraph-builder.js +160 -0
  26. package/dist/paragraph-builder.js.map +1 -0
  27. package/example.ts +103 -0
  28. package/fixtures/DancingScript-Regular.ttf +0 -0
  29. package/fixtures/JetBrainsMono-Regular.ttf +0 -0
  30. package/fixtures/ObviouslyAwesome.pdf +0 -0
  31. package/fixtures/README.md +25 -0
  32. package/fixtures/basic-paths.pdf +0 -0
  33. package/fixtures/logo-80.png +0 -0
  34. package/fixtures/mixed-form-types.pdf +0 -0
  35. package/jest.config.js +26 -0
  36. package/package.json +38 -0
  37. package/scripts/release.js +91 -0
  38. package/scripts/test-release.js +59 -0
  39. package/src/__tests__/client-v1.test.ts +111 -0
  40. package/src/__tests__/e2e/form.test.ts +51 -0
  41. package/src/__tests__/e2e/image.test.ts +108 -0
  42. package/src/__tests__/e2e/line.test.ts +134 -0
  43. package/src/__tests__/e2e/page.test.ts +45 -0
  44. package/src/__tests__/e2e/paragraph.test.ts +210 -0
  45. package/src/__tests__/e2e/path.test.ts +72 -0
  46. package/src/__tests__/e2e/test-helpers.ts +132 -0
  47. package/src/client-v1.ts +673 -0
  48. package/src/exceptions.ts +67 -0
  49. package/src/index.ts +34 -0
  50. package/src/models.ts +476 -0
  51. package/src/paragraph-builder.ts +184 -0
  52. package/tsconfig.json +20 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * E2E tests for text line operations
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import { ClientV1, Position } from '../../index';
7
+ import { requireEnvAndFixture, createTempPath } from './test-helpers';
8
+
9
+ describe('Line E2E Tests', () => {
10
+ // Tests should fail properly if environment is not configured
11
+
12
+ test('find lines by position', async () => {
13
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
14
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
15
+ await client.init();
16
+
17
+ const lines = await client.findTextLines();
18
+ expect(lines).toHaveLength(340);
19
+
20
+ const first = lines[0];
21
+ expect(first.internalId).toBe('LINE_000001');
22
+ expect(first.position).toBeDefined();
23
+ expect(first.position.boundingRect?.x).toBeCloseTo(326, 1);
24
+ expect(first.position.boundingRect?.y).toBeCloseTo(706, 1);
25
+
26
+ const last = lines[lines.length - 1];
27
+ expect(last.internalId).toBe('LINE_000340');
28
+ expect(last.position).toBeDefined();
29
+ expect(last.position.boundingRect?.x).toBeCloseTo(548, 2);
30
+ expect(last.position.boundingRect?.y).toBeCloseTo(35, 2);
31
+ });
32
+
33
+ test('find lines by text', async () => {
34
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
35
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
36
+ await client.init();
37
+
38
+ const pos = Position.fromPageIndex(0);
39
+ pos.textStartsWith = 'the complete';
40
+ const lines = await client.findTextLines(pos);
41
+ expect(lines).toHaveLength(1);
42
+
43
+ const line = lines[0];
44
+ expect(line.internalId).toBe('LINE_000002');
45
+ expect(line.position).toBeDefined();
46
+ expect(line.position.boundingRect?.x).toBeCloseTo(54, 1);
47
+ expect(line.position.boundingRect?.y).toBeCloseTo(606, 2);
48
+ });
49
+
50
+ test('delete line', async () => {
51
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
52
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
53
+ await client.init();
54
+
55
+ const pos = Position.fromPageIndex(0);
56
+ pos.textStartsWith = 'The Complete';
57
+ const ref = (await client.findTextLines(pos))[0];
58
+ expect(await client.delete(ref)).toBe(true);
59
+
60
+ const pos2 = Position.fromPageIndex(0);
61
+ pos2.textStartsWith = 'The Complete';
62
+ expect(await client.findTextLines(pos2)).toHaveLength(0);
63
+
64
+ // Save PDF to verify operation (Node.js environment)
65
+ const outPath = createTempPath('deleteLine.pdf');
66
+ const outputPdfData = await client.getPdfFile();
67
+ fs.writeFileSync(outPath, outputPdfData);
68
+ expect(fs.existsSync(outPath)).toBe(true);
69
+ expect(fs.statSync(outPath).size).toBeGreaterThan(0);
70
+
71
+ // Cleanup
72
+ fs.unlinkSync(outPath);
73
+ });
74
+
75
+ test('move line', async () => {
76
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
77
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
78
+ await client.init();
79
+
80
+ const pos3 = Position.fromPageIndex(0);
81
+ pos3.textStartsWith = 'The Complete';
82
+ const ref = (await client.findTextLines(pos3))[0];
83
+ const newPos = ref.position.copy();
84
+ newPos.moveX(100);
85
+ expect(await client.move(ref, newPos)).toBe(true);
86
+
87
+ const ref2 = (await client.findParagraphs(newPos))[0];
88
+ expect(ref2).toBeDefined();
89
+
90
+ // Save PDF to verify operation
91
+ const outPath = createTempPath('moveLine.pdf');
92
+ const outputPdfData = await client.getPdfFile();
93
+ fs.writeFileSync(outPath, outputPdfData);
94
+ expect(fs.existsSync(outPath)).toBe(true);
95
+ expect(fs.statSync(outPath).size).toBeGreaterThan(0);
96
+
97
+ // Cleanup
98
+ fs.unlinkSync(outPath);
99
+ });
100
+
101
+ test('modify line', async () => {
102
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
103
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
104
+ await client.init();
105
+
106
+ const pos4 = Position.fromPageIndex(0);
107
+ pos4.textStartsWith = 'The Complete';
108
+ const ref = (await client.findTextLines(pos4))[0];
109
+ expect(await client.modifyTextLine(ref, ' replaced ')).toBe(true);
110
+
111
+ // Save PDF to verify operation
112
+ const outPath = createTempPath('modifyLine.pdf');
113
+ const outputPdfData = await client.getPdfFile();
114
+ fs.writeFileSync(outPath, outputPdfData);
115
+ expect(fs.existsSync(outPath)).toBe(true);
116
+ expect(fs.statSync(outPath).size).toBeGreaterThan(0);
117
+
118
+ // Verify the text was replaced
119
+ const pos5 = Position.fromPageIndex(0);
120
+ pos5.textStartsWith = 'The Complete';
121
+ expect(await client.findTextLines(pos5)).toHaveLength(0);
122
+
123
+ const pos6 = Position.fromPageIndex(0);
124
+ pos6.textStartsWith = ' replaced ';
125
+ expect(await client.findTextLines(pos6)).not.toHaveLength(0);
126
+
127
+ const pos7 = Position.fromPageIndex(0);
128
+ pos7.textStartsWith = ' replaced ';
129
+ expect(await client.findParagraphs(pos7)).not.toHaveLength(0);
130
+
131
+ // Cleanup
132
+ fs.unlinkSync(outPath);
133
+ });
134
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * E2E tests for page operations
3
+ */
4
+
5
+ import { ClientV1, ObjectType } from '../../index';
6
+ import { requireEnvAndFixture } from './test-helpers';
7
+
8
+ describe('Page E2E Tests', () => {
9
+ // Tests should fail properly if environment is not configured
10
+
11
+ test('get pages', async () => {
12
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
13
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
14
+ await client.init();
15
+
16
+ const pages = await client.getPages();
17
+ expect(pages).toBeDefined();
18
+ expect(pages[0].type).toBe(ObjectType.PAGE);
19
+ expect(pages).toHaveLength(12);
20
+ });
21
+
22
+ test('get page', async () => {
23
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
24
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
25
+ await client.init();
26
+
27
+ const page = await client.getPage(2);
28
+ expect(page).toBeDefined();
29
+ expect(page!.position.pageIndex).toBe(2);
30
+ expect(page!.internalId).toBeDefined();
31
+ });
32
+
33
+ test('delete page', async () => {
34
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
35
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
36
+ await client.init();
37
+
38
+ const page3 = await client.getPage(3);
39
+ expect(page3).toBeDefined();
40
+ expect(await client.deletePage(page3!)).toBe(true);
41
+
42
+ const newPages = await client.getPages();
43
+ expect(newPages).toHaveLength(11);
44
+ });
45
+ });
@@ -0,0 +1,210 @@
1
+ /**
2
+ * E2E tests for paragraph operations
3
+ */
4
+
5
+ import { ClientV1, Position, Font, Color, FontNotFoundException } from '../../index';
6
+ import { requireEnvAndFixture, readFontFixture } from './test-helpers';
7
+
8
+ describe('Paragraph E2E Tests', () => {
9
+ // Tests should fail properly if environment is not configured
10
+
11
+ test('find paragraphs by position', async () => {
12
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
13
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
14
+ await client.init();
15
+
16
+ const paras = await client.findParagraphs();
17
+ expect(paras).toHaveLength(172);
18
+
19
+ const parasPage0 = await client.findParagraphs(Position.fromPageIndex(0));
20
+ expect(parasPage0).toHaveLength(2);
21
+
22
+ const first = parasPage0[0];
23
+ expect(first.internalId).toBe('PARAGRAPH_000003');
24
+ expect(first.position).toBeDefined();
25
+ expect(first.position.boundingRect?.x).toBeCloseTo(326, 1);
26
+ expect(first.position.boundingRect?.y).toBeCloseTo(706, 1);
27
+
28
+ const last = parasPage0[parasPage0.length - 1];
29
+ expect(last.internalId).toBe('PARAGRAPH_000004');
30
+ expect(last.position).toBeDefined();
31
+ expect(last.position.boundingRect?.x).toBeCloseTo(54, 1);
32
+ expect(last.position.boundingRect?.y).toBeCloseTo(496, 2);
33
+ });
34
+
35
+ test('find paragraphs by text', async () => {
36
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
37
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
38
+ await client.init();
39
+
40
+ const pos = Position.fromPageIndex(0);
41
+ pos.textStartsWith = 'The Complete';
42
+ const paras = await client.findParagraphs(pos);
43
+ expect(paras).toHaveLength(1);
44
+
45
+ const p = paras[0];
46
+ expect(p.internalId).toBe('PARAGRAPH_000004');
47
+ expect(p.position).toBeDefined();
48
+ expect(p.position.boundingRect?.x).toBeCloseTo(54, 1);
49
+ expect(p.position.boundingRect?.y).toBeCloseTo(496, 2);
50
+ });
51
+
52
+ test('delete paragraph', async () => {
53
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
54
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
55
+ await client.init();
56
+
57
+ const posDel = Position.fromPageIndex(0);
58
+ posDel.textStartsWith = 'The Complete';
59
+ const ref = (await client.findParagraphs(posDel))[0];
60
+ expect(await client.delete(ref)).toBe(true);
61
+
62
+ const posDel2 = Position.fromPageIndex(0);
63
+ posDel2.textStartsWith = 'The Complete';
64
+ expect(await client.findParagraphs(posDel2)).toHaveLength(0);
65
+ });
66
+
67
+ test('move paragraph', async () => {
68
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
69
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
70
+ await client.init();
71
+
72
+ const posMove = Position.fromPageIndex(0);
73
+ posMove.textStartsWith = 'The Complete';
74
+ const ref = (await client.findParagraphs(posMove))[0];
75
+ const newPos = Position.onPageCoordinates(0, 0.1, 300);
76
+ expect(await client.move(ref, newPos)).toBe(true);
77
+
78
+ const ref2 = (await client.findParagraphs(newPos))[0];
79
+ expect(ref2).toBeDefined();
80
+ });
81
+
82
+ async function assertNewParagraphExists(client: ClientV1): Promise<void> {
83
+ // Validate via find_text_lines for text starting with 'Awesomely'
84
+ const pos = Position.fromPageIndex(0);
85
+ pos.textStartsWith = 'Awesomely';
86
+ const lines = await client.findTextLines(pos);
87
+ expect(lines.length).toBeGreaterThanOrEqual(1);
88
+ }
89
+
90
+ test('modify paragraph', async () => {
91
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
92
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
93
+ await client.init();
94
+
95
+ const posMod = Position.fromPageIndex(0);
96
+ posMod.textStartsWith = 'The Complete';
97
+ const ref = (await client.findParagraphs(posMod))[0];
98
+
99
+ const newParagraph = client.paragraphBuilder()
100
+ .fromString('Awesomely\\nObvious!')
101
+ .withFont(new Font('Helvetica', 14))
102
+ .withLineSpacing(0.7)
103
+ .withPosition(Position.onPageCoordinates(0, 300.1, 500))
104
+ .build();
105
+
106
+ expect(await client.modifyParagraph(ref, newParagraph)).toBe(true);
107
+ await assertNewParagraphExists(client);
108
+ });
109
+
110
+ test('modify paragraph simple', async () => {
111
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
112
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
113
+ await client.init();
114
+
115
+ const posMod2 = Position.fromPageIndex(0);
116
+ posMod2.textStartsWith = 'The Complete';
117
+ const ref = (await client.findParagraphs(posMod2))[0];
118
+ expect(await client.modifyParagraph(ref, 'Awesomely\\nObvious!')).toBe(true);
119
+ await assertNewParagraphExists(client);
120
+ });
121
+
122
+ test('add paragraph with custom font - expect not found', async () => {
123
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
124
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
125
+ await client.init();
126
+
127
+ const pb = client.paragraphBuilder()
128
+ .fromString('Awesomely\\nObvious!')
129
+ .withFont(new Font('Roboto', 14))
130
+ .withLineSpacing(0.7)
131
+ .withPosition(Position.onPageCoordinates(0, 300.1, 500));
132
+
133
+ await expect(client.addParagraph(pb.build())).rejects.toThrow('Font not found');
134
+ });
135
+
136
+ test('add paragraph with custom font - Roboto-Regular', async () => {
137
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
138
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
139
+ await client.init();
140
+
141
+ const pb = client.paragraphBuilder()
142
+ .fromString('Awesomely\\nObvious!')
143
+ .withFont(new Font('Roboto-Regular', 14))
144
+ .withLineSpacing(0.7)
145
+ .withPosition(Position.onPageCoordinates(0, 300.1, 500));
146
+
147
+ expect(await client.addParagraph(pb.build())).toBe(true);
148
+ await assertNewParagraphExists(client);
149
+ });
150
+
151
+ test('add paragraph with found font', async () => {
152
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
153
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
154
+ await client.init();
155
+
156
+ const fonts = await client.findFonts('Roboto', 14);
157
+ expect(fonts.length).toBeGreaterThan(0);
158
+ expect(fonts[0].name).toBe('Roboto-Regular');
159
+
160
+ const roboto = fonts[0];
161
+ const pb = client.paragraphBuilder()
162
+ .fromString('Awesomely\\nObvious!')
163
+ .withFont(roboto)
164
+ .withLineSpacing(0.7)
165
+ .withPosition(Position.onPageCoordinates(0, 300.1, 500));
166
+
167
+ expect(await client.addParagraph(pb.build())).toBe(true);
168
+ await assertNewParagraphExists(client);
169
+ });
170
+
171
+ test('add paragraph with Asimovian font', async () => {
172
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
173
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
174
+ await client.init();
175
+
176
+ const fonts = await client.findFonts('Asimovian', 14);
177
+ expect(fonts.length).toBeGreaterThan(0);
178
+ expect(fonts[0].name).toBe('Asimovian-Regular');
179
+
180
+ const asimovian = fonts[0];
181
+ const pb = client.paragraphBuilder()
182
+ .fromString('Awesomely\\nObvious!')
183
+ .withFont(asimovian)
184
+ .withLineSpacing(0.7)
185
+ .withPosition(Position.onPageCoordinates(0, 300.1, 500));
186
+
187
+ expect(await client.addParagraph(pb.build())).toBe(true);
188
+ await assertNewParagraphExists(client);
189
+ });
190
+
191
+ test('add paragraph with custom TTF font', async () => {
192
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
193
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
194
+ await client.init();
195
+
196
+ // Use DancingScript-Regular.ttf from fixtures directory
197
+ const ttfData = readFontFixture('DancingScript-Regular.ttf');
198
+
199
+ const pb = client.paragraphBuilder()
200
+ .fromString('Awesomely\\nObvious!')
201
+ .withLineSpacing(1.8)
202
+ .withColor(new Color(0, 0, 255))
203
+ .withPosition(Position.onPageCoordinates(0, 300.1, 500));
204
+
205
+ await pb.withFontFile(ttfData, 24);
206
+
207
+ expect(await client.addParagraph(pb.build())).toBe(true);
208
+ await assertNewParagraphExists(client);
209
+ });
210
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * E2E tests for path operations
3
+ */
4
+
5
+ import { ClientV1, Position, ObjectType } from '../../index';
6
+ import { requireEnvAndFixture } from './test-helpers';
7
+
8
+ describe('Path E2E Tests', () => {
9
+ // Remove the misleading beforeAll - tests should fail properly if not configured
10
+
11
+ test('find paths', async () => {
12
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
13
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
14
+ await client.init();
15
+
16
+ const paths = await client.findPaths();
17
+ expect(paths).toHaveLength(9);
18
+ expect(paths[0].type).toBe(ObjectType.PATH);
19
+
20
+ const p1 = paths[0];
21
+ expect(p1).toBeDefined();
22
+ expect(p1.internalId).toBe('PATH_000001');
23
+ expect(p1.position.boundingRect?.x).toBeCloseTo(80, 1);
24
+ expect(p1.position.boundingRect?.y).toBeCloseTo(720, 1);
25
+ });
26
+
27
+ test('find paths by position', async () => {
28
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
29
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
30
+ await client.init();
31
+
32
+ const paths = await client.findPaths(Position.onPageCoordinates(0, 80, 720));
33
+ expect(paths).toHaveLength(1);
34
+ expect(paths[0].internalId).toBe('PATH_000001');
35
+ });
36
+
37
+ test('delete path', async () => {
38
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
39
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
40
+ await client.init();
41
+
42
+ let paths = await client.findPaths(Position.onPageCoordinates(0, 80, 720));
43
+ expect(paths).toHaveLength(1);
44
+ expect(paths[0].internalId).toBe('PATH_000001');
45
+ expect(await client.delete(paths[0])).toBe(true);
46
+
47
+ expect(await client.findPaths(Position.onPageCoordinates(0, 80, 720))).toHaveLength(0);
48
+ expect(await client.findPaths()).toHaveLength(8);
49
+ });
50
+
51
+ test('move path', async () => {
52
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
53
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
54
+ await client.init();
55
+
56
+ let paths = await client.findPaths(Position.onPageCoordinates(0, 80, 720));
57
+ const ref = paths[0];
58
+ const pos = ref.position;
59
+ expect(pos.boundingRect?.x).toBeCloseTo(80, 1);
60
+ expect(pos.boundingRect?.y).toBeCloseTo(720, 1);
61
+
62
+ expect(await client.move(ref, Position.onPageCoordinates(0, 50.1, 100))).toBe(true);
63
+
64
+ expect(await client.findPaths(Position.onPageCoordinates(0, 80, 720))).toHaveLength(0);
65
+
66
+ paths = await client.findPaths(Position.onPageCoordinates(0, 50.1, 100));
67
+ const movedRef = paths[0];
68
+ const newPos = movedRef.position;
69
+ expect(newPos.boundingRect?.x).toBeCloseTo(50.1, 0.05);
70
+ expect(newPos.boundingRect?.y).toBeCloseTo(100, 0.05);
71
+ });
72
+ });
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Helper functions for e2e tests
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+
8
+ /**
9
+ * Get the base URL from environment variable or default
10
+ */
11
+ export function getBaseUrl(): string {
12
+ return process.env.PDFDANCER_BASE_URL || 'http://localhost:8080';
13
+ }
14
+
15
+ /**
16
+ * Read authentication token from environment or token files
17
+ */
18
+ export function readToken(): string | null {
19
+ const token = process.env.PDFDANCER_TOKEN;
20
+ if (token) {
21
+ return token.trim();
22
+ }
23
+
24
+ // Try common token files in project root
25
+ const projectRoot = path.resolve(__dirname, '../../..');
26
+ const candidates = findFiles(projectRoot, 'jwt-token-*.txt');
27
+
28
+ for (const file of candidates) {
29
+ try {
30
+ return fs.readFileSync(file, 'utf-8').trim();
31
+ } catch {
32
+ continue;
33
+ }
34
+ }
35
+
36
+ return null;
37
+ }
38
+
39
+ /**
40
+ * Check if server is up and running
41
+ */
42
+ export async function serverUp(baseUrl: string): Promise<boolean> {
43
+ try {
44
+ const response = await fetch(`${baseUrl}/ping`, {
45
+ signal: AbortSignal.timeout(3000)
46
+ });
47
+ const text = await response.text();
48
+ return response.status === 200 && text.includes('Pong');
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Helper to find files matching a pattern
56
+ */
57
+ function findFiles(dir: string, pattern: string): string[] {
58
+ try {
59
+ const files = fs.readdirSync(dir);
60
+ const regex = new RegExp(pattern.replace('*', '.*'));
61
+ return files
62
+ .filter(file => regex.test(file))
63
+ .map(file => path.join(dir, file));
64
+ } catch {
65
+ return [];
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Require environment variables and PDF fixture for testing
71
+ */
72
+ export async function requireEnvAndFixture(pdfFilename: string): Promise<[string, string, Uint8Array]> {
73
+ const baseUrl = getBaseUrl();
74
+ const token = readToken();
75
+
76
+ if (!await serverUp(baseUrl)) {
77
+ throw new Error(`PDFDancer server not reachable at ${baseUrl}; set PDFDANCER_BASE_URL or start server`);
78
+ }
79
+
80
+ if (!token) {
81
+ throw new Error('PDFDANCER_TOKEN not set and no token file found; set env or place jwt-token-*.txt in repo');
82
+ }
83
+
84
+ // Look for PDF fixture file
85
+ const fixturesDir = path.resolve(__dirname, '../../../fixtures');
86
+ const pdfPath = path.join(fixturesDir, pdfFilename);
87
+
88
+ if (!fs.existsSync(pdfPath)) {
89
+ throw new Error(`${pdfFilename} fixture not found at ${pdfPath}`);
90
+ }
91
+
92
+ const pdfData = fs.readFileSync(pdfPath);
93
+ return [baseUrl, token, new Uint8Array(pdfData)];
94
+ }
95
+
96
+ /**
97
+ * Helper to create temporary file path (for Node.js environment)
98
+ */
99
+ export function createTempPath(filename: string): string {
100
+ const tmpDir = process.env.TMPDIR || '/tmp';
101
+ return path.join(tmpDir, filename);
102
+ }
103
+
104
+ /**
105
+ * Helper to read image file for tests
106
+ */
107
+ export function readImageFixture(filename: string): Uint8Array {
108
+ const fixturesDir = path.resolve(__dirname, '../../../fixtures');
109
+ const imagePath = path.join(fixturesDir, filename);
110
+
111
+ if (!fs.existsSync(imagePath)) {
112
+ throw new Error(`Image fixture not found: ${filename}`);
113
+ }
114
+
115
+ const imageData = fs.readFileSync(imagePath);
116
+ return new Uint8Array(imageData);
117
+ }
118
+
119
+ /**
120
+ * Helper to read font file for tests
121
+ */
122
+ export function readFontFixture(filename: string): Uint8Array {
123
+ const fixturesDir = path.resolve(__dirname, '../../../fixtures');
124
+ const fontPath = path.join(fixturesDir, filename);
125
+
126
+ if (!fs.existsSync(fontPath)) {
127
+ throw new Error(`Font fixture not found: ${filename}`);
128
+ }
129
+
130
+ const fontData = fs.readFileSync(fontPath);
131
+ return new Uint8Array(fontData);
132
+ }