pdfdancer-client-typescript 1.0.12 → 1.0.13

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 (65) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/README.md +1 -1
  3. package/dist/__tests__/e2e/pdf-assertions.d.ts +1 -0
  4. package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
  5. package/dist/__tests__/e2e/pdf-assertions.js +9 -3
  6. package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
  7. package/dist/fingerprint.d.ts +12 -0
  8. package/dist/fingerprint.d.ts.map +1 -0
  9. package/dist/fingerprint.js +196 -0
  10. package/dist/fingerprint.js.map +1 -0
  11. package/dist/image-builder.d.ts +4 -2
  12. package/dist/image-builder.d.ts.map +1 -1
  13. package/dist/image-builder.js +12 -3
  14. package/dist/image-builder.js.map +1 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +7 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/models.d.ts +75 -8
  20. package/dist/models.d.ts.map +1 -1
  21. package/dist/models.js +179 -21
  22. package/dist/models.js.map +1 -1
  23. package/dist/page-builder.d.ts +24 -0
  24. package/dist/page-builder.d.ts.map +1 -0
  25. package/dist/page-builder.js +107 -0
  26. package/dist/page-builder.js.map +1 -0
  27. package/dist/paragraph-builder.d.ts +48 -54
  28. package/dist/paragraph-builder.d.ts.map +1 -1
  29. package/dist/paragraph-builder.js +408 -135
  30. package/dist/paragraph-builder.js.map +1 -1
  31. package/dist/pdfdancer_v1.d.ts +90 -9
  32. package/dist/pdfdancer_v1.d.ts.map +1 -1
  33. package/dist/pdfdancer_v1.js +535 -50
  34. package/dist/pdfdancer_v1.js.map +1 -1
  35. package/dist/types.d.ts +24 -3
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js +117 -2
  38. package/dist/types.js.map +1 -1
  39. package/docs/openapi.yml +2076 -0
  40. package/fixtures/Showcase.pdf +0 -0
  41. package/package.json +1 -1
  42. package/src/__tests__/e2e/acroform.test.ts +5 -5
  43. package/src/__tests__/e2e/context-manager-showcase.test.ts +267 -0
  44. package/src/__tests__/e2e/form_x_object.test.ts +1 -1
  45. package/src/__tests__/e2e/image-showcase.test.ts +133 -0
  46. package/src/__tests__/e2e/image.test.ts +1 -1
  47. package/src/__tests__/e2e/line-showcase.test.ts +118 -0
  48. package/src/__tests__/e2e/line.test.ts +1 -16
  49. package/src/__tests__/e2e/page-showcase.test.ts +154 -0
  50. package/src/__tests__/e2e/paragraph-showcase.test.ts +523 -0
  51. package/src/__tests__/e2e/paragraph.test.ts +8 -8
  52. package/src/__tests__/e2e/pdf-assertions.ts +10 -3
  53. package/src/__tests__/e2e/pdfdancer-showcase.test.ts +40 -0
  54. package/src/__tests__/e2e/snapshot-showcase.test.ts +158 -0
  55. package/src/__tests__/e2e/snapshot.test.ts +296 -0
  56. package/src/__tests__/fingerprint.test.ts +36 -0
  57. package/src/fingerprint.ts +169 -0
  58. package/src/image-builder.ts +13 -6
  59. package/src/index.ts +6 -1
  60. package/src/models.ts +208 -24
  61. package/src/page-builder.ts +130 -0
  62. package/src/paragraph-builder.ts +517 -159
  63. package/src/pdfdancer_v1.ts +630 -51
  64. package/src/types.ts +145 -2
  65. package/update-api-spec.sh +3 -0
@@ -85,7 +85,7 @@ describe('Paragraph E2E Tests (v2 API)', () => {
85
85
  const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
86
86
  await para.moveTo(0.1, 300);
87
87
 
88
- const moved = await pdf.page(0).selectParagraphsAt(0.1, 300);
88
+ const moved = await pdf.page(0).selectParagraphsAt(0.1, 300, 1);
89
89
  expect(moved.length).toBeGreaterThan(0);
90
90
  });
91
91
 
@@ -147,8 +147,8 @@ describe('Paragraph E2E Tests (v2 API)', () => {
147
147
  expect(modified.objectRef().status?.isModified()).toBe(true);
148
148
 
149
149
  const assertions = await PDFAssertions.create(pdf);
150
- await assertions.assertTextlineHasFont('Awesomely', 'IXKSWR+Poppins-Bold', 1, 0);
151
- await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold', 1, 0);
150
+ await assertions.assertTextlineHasFont('Awesomely', 'IXKSWR+Poppins-Bold', 45, 0);
151
+ await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold', 45, 0);
152
152
  await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
153
153
  await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
154
154
  });
@@ -526,12 +526,12 @@ describe('Paragraph E2E Tests (v2 API)', () => {
526
526
  const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
527
527
 
528
528
  await para.edit()
529
- .moveTo(1, 1)
529
+ .moveTo(40, 40)
530
530
  .apply();
531
531
 
532
532
  const assertions = await PDFAssertions.create(pdf);
533
- await assertions.assertTextlineHasFont('The Complete', 'IXKSWR+Poppins-Bold', 1, 0);
534
- await assertions.assertTextlineIsAt('The Complete', 1, 1, 0, 0.22);
533
+ await assertions.assertTextlineHasFont('The Complete', 'IXKSWR+Poppins-Bold', 45, 0);
534
+ await assertions.assertParagraphIsAt('The Complete', 40, 40, 0, 0.22);
535
535
  await assertions.assertTextlineHasColor('The Complete', new Color(255, 255, 255), 0);
536
536
  });
537
537
 
@@ -543,8 +543,8 @@ describe('Paragraph E2E Tests (v2 API)', () => {
543
543
  await para.edit().replace('Awesomely\nObvious!').apply();
544
544
 
545
545
  const assertions = await PDFAssertions.create(pdf);
546
- await assertions.assertTextlineHasFont('Awesomely', 'IXKSWR+Poppins-Bold', 1, 0);
547
- await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold', 1, 0);
546
+ await assertions.assertTextlineHasFont('Awesomely', 'IXKSWR+Poppins-Bold', 45, 0);
547
+ await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold', 45, 0);
548
548
  await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
549
549
  await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
550
550
  });
@@ -27,6 +27,7 @@ export class PDFAssertions {
27
27
 
28
28
  // Reopen the PDF with the same token and baseUrl
29
29
  const pdfData = fs.readFileSync(tempFile);
30
+ console.log(`PDF file saved to ${tempFile}`);
30
31
  const pdf = await PDFDancer.open(new Uint8Array(pdfData), token, baseUrl);
31
32
 
32
33
  return new PDFAssertions(pdf);
@@ -66,6 +67,12 @@ export class PDFAssertions {
66
67
  return this;
67
68
  }
68
69
 
70
+ async assertNumberOfPages(expected: number): Promise<this> {
71
+ const pages = await this.pdf.pages();
72
+ expect(pages.length).toBe(expected);
73
+ return this;
74
+ }
75
+
69
76
  async assertPageCount(expected: number): Promise<this> {
70
77
  const pages = await this.pdf.pages();
71
78
  expect(pages.length).toBe(expected);
@@ -91,7 +98,7 @@ export class PDFAssertions {
91
98
  expectWithin(reference.position.getX()!, x, epsilon);
92
99
  expectWithin(reference.position.getY()!, y, epsilon);
93
100
 
94
- const byPosition = await this.pdf.page(page).selectParagraphsAt(x, y);
101
+ const byPosition = await this.pdf.page(page).selectParagraphsAt(x, y, epsilon);
95
102
  expect(byPosition.length).toBeGreaterThan(0);
96
103
  return this;
97
104
  }
@@ -151,7 +158,7 @@ export class PDFAssertions {
151
158
  const reference = lines[0].objectRef();
152
159
  expectWithin(reference.position.getX()!, x, epsilon);
153
160
  expectWithin(reference.position.getY()!, y, epsilon);
154
- const byPosition = await this.pdf.page(page).selectTextLinesAt(x, y);
161
+ const byPosition = await this.pdf.page(page).selectTextLinesAt(x, y, epsilon);
155
162
  expect(byPosition.length).toBeGreaterThan(0);
156
163
  return this;
157
164
  }
@@ -185,7 +192,7 @@ export class PDFAssertions {
185
192
  }
186
193
 
187
194
  async assertImageAt(x: number, y: number, page = 0): Promise<this> {
188
- const images = await this.pdf.page(page).selectImagesAt(x, y);
195
+ const images = await this.pdf.page(page).selectImagesAt(x, y, 0.1);
189
196
  expect(images.length).toBe(1);
190
197
  return this;
191
198
  }
@@ -0,0 +1,40 @@
1
+ import {PDFDancer, ValidationException} from '../../index';
2
+ import {readToken, requireEnvAndFixture} from './test-helpers';
3
+
4
+ describe('PDFDancer Environment Tests (Showcase)', () => {
5
+ test.skip('environment variable handling matches python client', async () => {
6
+ const [baseUrl, , pdfData] = await requireEnvAndFixture('Showcase.pdf');
7
+ const originalBaseUrl = process.env.PDFDANCER_BASE_URL;
8
+ const originalToken = process.env.PDFDANCER_TOKEN;
9
+
10
+ try {
11
+ delete process.env.PDFDANCER_TOKEN;
12
+
13
+ // TypeScript client currently requires an explicit token; when parity is implemented,
14
+ // this assertion should be updated to match the Python client's anonymous token fallback.
15
+ await expect(PDFDancer.open(pdfData, undefined, baseUrl)).rejects.toThrow(ValidationException);
16
+
17
+ process.env.PDFDANCER_TOKEN = readToken() ?? '';
18
+ await expect(PDFDancer.open(pdfData, undefined, baseUrl)).resolves.toBeDefined();
19
+
20
+ process.env.PDFDANCER_BASE_URL = 'https://www.google.com';
21
+ await expect(PDFDancer.open(pdfData)).rejects.toThrow();
22
+
23
+ process.env.PDFDANCER_BASE_URL = 'https://api.pdfdancer.com';
24
+ delete process.env.PDFDANCER_TOKEN;
25
+ await expect(PDFDancer.open(pdfData)).rejects.toThrow();
26
+ } finally {
27
+ if (originalBaseUrl === undefined) {
28
+ delete process.env.PDFDANCER_BASE_URL;
29
+ } else {
30
+ process.env.PDFDANCER_BASE_URL = originalBaseUrl;
31
+ }
32
+
33
+ if (originalToken === undefined) {
34
+ delete process.env.PDFDANCER_TOKEN;
35
+ } else {
36
+ process.env.PDFDANCER_TOKEN = originalToken;
37
+ }
38
+ }
39
+ });
40
+ });
@@ -0,0 +1,158 @@
1
+ import {ObjectType, PDFDancer} from '../../index';
2
+ import {requireEnvAndFixture} from './test-helpers';
3
+
4
+ describe('Snapshot E2E Tests (Showcase)', () => {
5
+ test('page snapshot matches select paragraphs', async () => {
6
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
7
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
8
+
9
+ const snapshot = await pdf.getPageSnapshot(0);
10
+ const snapshotParagraphs = snapshot.elements.filter(e => e.type === ObjectType.PARAGRAPH);
11
+ const selected = await pdf.page(0).selectParagraphs();
12
+
13
+ expect(selected.length).toBe(snapshotParagraphs.length);
14
+ const snapshotIds = new Set(snapshotParagraphs.map(e => e.internalId));
15
+ const selectedIds = new Set(selected.map(p => p.internalId));
16
+ expect(selectedIds).toEqual(snapshotIds);
17
+ });
18
+
19
+ test('page snapshot matches select images', async () => {
20
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
21
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
22
+
23
+ const snapshot = await pdf.getPageSnapshot(0);
24
+ const snapshotImages = snapshot.elements.filter(e => e.type === ObjectType.IMAGE);
25
+ const selected = await pdf.page(0).selectImages();
26
+
27
+ expect(selected.length).toBe(snapshotImages.length);
28
+ if (selected.length > 0) {
29
+ const snapshotIds = new Set(snapshotImages.map(e => e.internalId));
30
+ const selectedIds = new Set(selected.map(img => img.internalId));
31
+ expect(selectedIds).toEqual(snapshotIds);
32
+ }
33
+ });
34
+
35
+ test('page snapshot matches select forms', async () => {
36
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
37
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
38
+
39
+ const snapshot = await pdf.getPageSnapshot(0);
40
+ const snapshotForms = snapshot.elements.filter(e => e.type === ObjectType.FORM_X_OBJECT);
41
+ const selected = await pdf.page(0).selectForms();
42
+
43
+ expect(selected.length).toBe(snapshotForms.length);
44
+ if (selected.length > 0) {
45
+ const snapshotIds = new Set(snapshotForms.map(e => e.internalId));
46
+ const selectedIds = new Set(selected.map(form => form.internalId));
47
+ expect(selectedIds).toEqual(snapshotIds);
48
+ }
49
+ });
50
+
51
+ test('page snapshot matches select form fields', async () => {
52
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
53
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
54
+
55
+ const snapshot = await pdf.getPageSnapshot(0);
56
+ const snapshotFields = snapshot.elements.filter(e => [
57
+ ObjectType.FORM_FIELD,
58
+ ObjectType.TEXT_FIELD,
59
+ ObjectType.CHECKBOX,
60
+ ObjectType.RADIO_BUTTON
61
+ ].includes(e.type));
62
+ const selected = await pdf.page(0).selectFormFields();
63
+
64
+ expect(selected.length).toBe(snapshotFields.length);
65
+ if (selected.length > 0) {
66
+ const snapshotIds = new Set(snapshotFields.map(e => e.internalId));
67
+ const selectedIds = new Set(selected.map(field => field.internalId));
68
+ expect(selectedIds).toEqual(snapshotIds);
69
+ }
70
+ });
71
+
72
+ test('page snapshot contains all element types', async () => {
73
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
74
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
75
+
76
+ const snapshot = await pdf.getPageSnapshot(0);
77
+ const paragraphCount = snapshot.elements.filter(e => e.type === ObjectType.PARAGRAPH).length;
78
+ const textLineCount = snapshot.elements.filter(e => e.type === ObjectType.TEXT_LINE).length;
79
+
80
+ expect(paragraphCount > 0 || textLineCount > 0).toBe(true);
81
+ for (const element of snapshot.elements) {
82
+ expect(element.type).toBeDefined();
83
+ expect(element.internalId).toBeDefined();
84
+ expect(element.position).toBeDefined();
85
+ }
86
+ });
87
+
88
+ test('document snapshot matches all pages', async () => {
89
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
90
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
91
+
92
+ const docSnapshot = await pdf.getDocumentSnapshot();
93
+ for (let i = 0; i < docSnapshot.pageCount; i++) {
94
+ const docPage = docSnapshot.pages[i];
95
+ const pageSnapshot = await pdf.getPageSnapshot(i);
96
+ expect(pageSnapshot.elements.length).toBe(docPage.elements.length);
97
+
98
+ const docIds = new Set(docPage.elements.map(e => e.internalId));
99
+ const pageIds = new Set(pageSnapshot.elements.map(e => e.internalId));
100
+ expect(pageIds).toEqual(docIds);
101
+ }
102
+ });
103
+
104
+ test('type filter matches select method', async () => {
105
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
106
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
107
+
108
+ const snapshot = await pdf.getPageSnapshot(0, [ObjectType.PARAGRAPH]);
109
+ const selected = await pdf.page(0).selectParagraphs();
110
+
111
+ expect(snapshot.elements.length).toBe(selected.length);
112
+ expect(snapshot.elements.every(e => e.type === ObjectType.PARAGRAPH)).toBe(true);
113
+
114
+ const snapshotIds = new Set(snapshot.elements.map(e => e.internalId));
115
+ const selectedIds = new Set(selected.map(p => p.internalId));
116
+ expect(selectedIds).toEqual(snapshotIds);
117
+ });
118
+
119
+ test('multiple type filters combined', async () => {
120
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
121
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
122
+
123
+ const snapshot = await pdf.getPageSnapshot(0, [ObjectType.PARAGRAPH, ObjectType.TEXT_LINE]);
124
+ expect(snapshot.elements.every(e => e.type === ObjectType.PARAGRAPH || e.type === ObjectType.TEXT_LINE)).toBe(true);
125
+
126
+ const full = await pdf.getPageSnapshot(0);
127
+ const expectedCount = full.elements.filter(e => e.type === ObjectType.PARAGRAPH || e.type === ObjectType.TEXT_LINE).length;
128
+ expect(snapshot.elements.length).toBe(expectedCount);
129
+ });
130
+
131
+ test('total element count matches expected', async () => {
132
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
133
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
134
+
135
+ const elements = await pdf.selectElements();
136
+ expect(elements.length).toBe(99);
137
+
138
+ const docSnapshot = await pdf.getDocumentSnapshot();
139
+ const snapshotTotal = docSnapshot.pages.reduce((count, page) => count + page.elements.length, 0);
140
+ expect(snapshotTotal).toBe(elements.length);
141
+ expect((await pdf.pages()).length).toBe(7);
142
+ });
143
+
144
+ test('snapshot consistency across multiple pages', async () => {
145
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
146
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
147
+
148
+ const docSnapshot = await pdf.getDocumentSnapshot();
149
+ expect(docSnapshot.pageCount).toBeGreaterThan(1);
150
+
151
+ const limit = Math.min(3, docSnapshot.pageCount);
152
+ for (let i = 0; i < limit; i++) {
153
+ const pageSnapshot = await pdf.getPageSnapshot(i);
154
+ expect(pageSnapshot).toBeDefined();
155
+ expect(pageSnapshot.pageRef.position.pageIndex).toBe(i);
156
+ }
157
+ });
158
+ });
@@ -0,0 +1,296 @@
1
+ /**
2
+ * E2E tests for snapshot operations
3
+ */
4
+
5
+ import {ObjectType, PDFDancer, DocumentSnapshot, PageSnapshot} from '../../index';
6
+ import {requireEnvAndFixture} from './test-helpers';
7
+
8
+ describe('Snapshot E2E Tests', () => {
9
+
10
+ test('get document snapshot', async () => {
11
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
12
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
13
+
14
+ const snapshot = await client.getDocumentSnapshot();
15
+
16
+ // Verify snapshot structure
17
+ expect(snapshot).toBeDefined();
18
+ expect(snapshot).toBeInstanceOf(DocumentSnapshot);
19
+
20
+ // Verify page count
21
+ expect(snapshot.pageCount).toBe(12);
22
+
23
+ // Verify fonts array exists
24
+ expect(snapshot.fonts).toBeDefined();
25
+ expect(Array.isArray(snapshot.fonts)).toBe(true);
26
+
27
+ // Verify pages array
28
+ expect(snapshot.pages).toBeDefined();
29
+ expect(Array.isArray(snapshot.pages)).toBe(true);
30
+ expect(snapshot.pages.length).toBe(12);
31
+
32
+ // Verify each page snapshot
33
+ for (let i = 0; i < snapshot.pages.length; i++) {
34
+ const pageSnapshot = snapshot.pages[i];
35
+ expect(pageSnapshot).toBeInstanceOf(PageSnapshot);
36
+ expect(pageSnapshot.pageRef).toBeDefined();
37
+ expect(pageSnapshot.pageRef.position.pageIndex).toBe(i);
38
+ expect(pageSnapshot.elements).toBeDefined();
39
+ expect(Array.isArray(pageSnapshot.elements)).toBe(true);
40
+ }
41
+ });
42
+
43
+ test('get document snapshot with type filter', async () => {
44
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
45
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
46
+
47
+ // Get snapshot filtered by paragraphs only
48
+ const snapshot = await client.getDocumentSnapshot([ObjectType.PARAGRAPH]);
49
+
50
+ expect(snapshot).toBeDefined();
51
+ expect(snapshot.pageCount).toBe(12);
52
+
53
+ // Verify all elements are paragraphs
54
+ const allElements = snapshot.getAllElements();
55
+ for (const element of allElements) {
56
+ expect(element.type).toBe(ObjectType.PARAGRAPH);
57
+ }
58
+ });
59
+
60
+ test('get document snapshot with multiple type filters', async () => {
61
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
62
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
63
+
64
+ // Get snapshot filtered by paragraphs and images
65
+ const snapshot = await client.getDocumentSnapshot([ObjectType.PARAGRAPH, ObjectType.IMAGE]);
66
+
67
+ expect(snapshot).toBeDefined();
68
+
69
+ // Verify all elements are either paragraphs or images
70
+ const allElements = snapshot.getAllElements();
71
+ for (const element of allElements) {
72
+ expect([ObjectType.PARAGRAPH, ObjectType.IMAGE]).toContain(element.type);
73
+ }
74
+ });
75
+
76
+ test('get page snapshot', async () => {
77
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
78
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
79
+
80
+ const pageSnapshot = await client.getPageSnapshot(0);
81
+
82
+ // Verify snapshot structure
83
+ expect(pageSnapshot).toBeDefined();
84
+ expect(pageSnapshot).toBeInstanceOf(PageSnapshot);
85
+
86
+ // Verify page reference
87
+ expect(pageSnapshot.pageRef).toBeDefined();
88
+ expect(pageSnapshot.pageRef.position.pageIndex).toBe(0);
89
+ expect(pageSnapshot.pageRef.type).toBe(ObjectType.PAGE);
90
+
91
+ // Verify elements
92
+ expect(pageSnapshot.elements).toBeDefined();
93
+ expect(Array.isArray(pageSnapshot.elements)).toBe(true);
94
+ expect(pageSnapshot.elements.length).toBeGreaterThan(0);
95
+
96
+ // Verify each element has required properties
97
+ for (const element of pageSnapshot.elements) {
98
+ expect(element.internalId).toBeDefined();
99
+ expect(element.type).toBeDefined();
100
+ expect(element.position).toBeDefined();
101
+ }
102
+ });
103
+
104
+ test('get page snapshot with type filter', async () => {
105
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
106
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
107
+
108
+ // Get page snapshot filtered by images only
109
+ const pageSnapshot = await client.getPageSnapshot(0, [ObjectType.IMAGE]);
110
+
111
+ expect(pageSnapshot).toBeDefined();
112
+
113
+ // Verify all elements are images
114
+ for (const element of pageSnapshot.elements) {
115
+ expect(element.type).toBe(ObjectType.IMAGE);
116
+ }
117
+ });
118
+
119
+ test('get page snapshot via PageClient', async () => {
120
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
121
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
122
+
123
+ const page = client.page(1);
124
+ const pageSnapshot = await page.getSnapshot();
125
+
126
+ // Verify snapshot structure
127
+ expect(pageSnapshot).toBeDefined();
128
+ expect(pageSnapshot).toBeInstanceOf(PageSnapshot);
129
+ expect(pageSnapshot.pageRef.position.pageIndex).toBe(1);
130
+ expect(pageSnapshot.elements.length).toBeGreaterThan(0);
131
+ });
132
+
133
+ test('get page snapshot via PageClient with type filter', async () => {
134
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
135
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
136
+
137
+ const page = client.page(0);
138
+ const pageSnapshot = await page.getSnapshot([ObjectType.PARAGRAPH]);
139
+
140
+ expect(pageSnapshot).toBeDefined();
141
+
142
+ // Verify all elements are paragraphs
143
+ for (const element of pageSnapshot.elements) {
144
+ expect(element.type).toBe(ObjectType.PARAGRAPH);
145
+ }
146
+ });
147
+
148
+ test('DocumentSnapshot helper methods', async () => {
149
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
150
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
151
+
152
+ const snapshot = await client.getDocumentSnapshot();
153
+
154
+ // Test getPageSnapshot
155
+ const page0 = snapshot.getPageSnapshot(0);
156
+ expect(page0).toBeDefined();
157
+ expect(page0!.getPageIndex()).toBe(0);
158
+
159
+ const page5 = snapshot.getPageSnapshot(5);
160
+ expect(page5).toBeDefined();
161
+ expect(page5!.getPageIndex()).toBe(5);
162
+
163
+ // Test getAllElements
164
+ const allElements = snapshot.getAllElements();
165
+ expect(allElements.length).toBeGreaterThan(0);
166
+
167
+ // Test getTotalElementCount
168
+ const totalCount = snapshot.getTotalElementCount();
169
+ expect(totalCount).toBe(allElements.length);
170
+ expect(totalCount).toBeGreaterThan(0);
171
+
172
+ // Test getElementsByType
173
+ const paragraphs = snapshot.getElementsByType(ObjectType.PARAGRAPH);
174
+ expect(paragraphs.length).toBeGreaterThan(0);
175
+ for (const para of paragraphs) {
176
+ expect(para.type).toBe(ObjectType.PARAGRAPH);
177
+ }
178
+ });
179
+
180
+ test('PageSnapshot helper methods', async () => {
181
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
182
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
183
+
184
+ const pageSnapshot = await client.getPageSnapshot(0);
185
+
186
+ // Test getPageIndex
187
+ expect(pageSnapshot.getPageIndex()).toBe(0);
188
+
189
+ // Test getElementCount
190
+ const elementCount = pageSnapshot.getElementCount();
191
+ expect(elementCount).toBe(pageSnapshot.elements.length);
192
+ expect(elementCount).toBeGreaterThan(0);
193
+
194
+ // Test getElementsByType
195
+ const paragraphs = pageSnapshot.getElementsByType(ObjectType.PARAGRAPH);
196
+ for (const para of paragraphs) {
197
+ expect(para.type).toBe(ObjectType.PARAGRAPH);
198
+ }
199
+ });
200
+
201
+ test('snapshot with form fields', async () => {
202
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('mixed-form-types.pdf');
203
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
204
+
205
+ // Form fields can be TEXT_FIELD, CHECKBOX, or RADIO_BUTTON
206
+ const snapshot = await client.getDocumentSnapshot([
207
+ ObjectType.TEXT_FIELD,
208
+ ObjectType.CHECKBOX,
209
+ ObjectType.RADIO_BUTTON
210
+ ]);
211
+
212
+ expect(snapshot).toBeDefined();
213
+
214
+ // Get all form field types
215
+ const allFormFields = snapshot.getAllElements();
216
+ expect(allFormFields.length).toBeGreaterThan(0);
217
+
218
+ // Verify they are all form field types
219
+ const formFieldTypes = [ObjectType.TEXT_FIELD, ObjectType.CHECKBOX, ObjectType.RADIO_BUTTON];
220
+ for (const field of allFormFields) {
221
+ expect(formFieldTypes).toContain(field.type);
222
+ }
223
+ });
224
+
225
+ test('snapshot with paths', async () => {
226
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
227
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
228
+
229
+ const snapshot = await client.getDocumentSnapshot([ObjectType.PATH]);
230
+
231
+ expect(snapshot).toBeDefined();
232
+
233
+ // Get all paths
234
+ const paths = snapshot.getElementsByType(ObjectType.PATH);
235
+ expect(paths.length).toBeGreaterThan(0);
236
+
237
+ // Verify they are all paths
238
+ for (const path of paths) {
239
+ expect(path.type).toBe(ObjectType.PATH);
240
+ }
241
+ });
242
+
243
+ test('compare snapshot with individual find operations', async () => {
244
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
245
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
246
+
247
+ // Get snapshot
248
+ const snapshot = await client.getPageSnapshot(0);
249
+ const snapshotParagraphs = snapshot.getElementsByType(ObjectType.PARAGRAPH);
250
+
251
+ // Get paragraphs via page client
252
+ const page = client.page(0);
253
+ const paragraphs = await page.selectParagraphs();
254
+
255
+ // Should have same count
256
+ expect(snapshotParagraphs.length).toBe(paragraphs.length);
257
+ });
258
+
259
+ test('snapshot fonts information', async () => {
260
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
261
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
262
+
263
+ const snapshot = await client.getDocumentSnapshot();
264
+
265
+ // Verify fonts array
266
+ expect(snapshot.fonts).toBeDefined();
267
+ expect(Array.isArray(snapshot.fonts)).toBe(true);
268
+
269
+ // If there are fonts, verify their structure
270
+ if (snapshot.fonts.length > 0) {
271
+ for (const font of snapshot.fonts) {
272
+ expect(font.fontName).toBeDefined();
273
+ expect(font.fontType).toBeDefined();
274
+ expect(typeof font.similarityScore).toBe('number');
275
+ }
276
+ }
277
+ });
278
+
279
+ test('snapshot on empty page', async () => {
280
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Empty.pdf');
281
+ const client = await PDFDancer.open(pdfData, token, baseUrl, 30000);
282
+
283
+ const snapshot = await client.getDocumentSnapshot();
284
+
285
+ expect(snapshot).toBeDefined();
286
+ expect(snapshot.pageCount).toBeGreaterThan(0);
287
+ expect(snapshot.pages.length).toBeGreaterThan(0);
288
+
289
+ // Empty pages might have no elements
290
+ const page0Snapshot = snapshot.getPageSnapshot(0);
291
+ expect(page0Snapshot).toBeDefined();
292
+ expect(page0Snapshot!.elements).toBeDefined();
293
+ expect(Array.isArray(page0Snapshot!.elements)).toBe(true);
294
+ });
295
+ });
296
+
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tests for fingerprint generation
3
+ */
4
+
5
+ import {generateFingerprint} from '../fingerprint';
6
+
7
+ describe('Fingerprint', () => {
8
+ it('should generate a valid SHA256 fingerprint', async () => {
9
+ const fingerprint = await generateFingerprint();
10
+
11
+ // SHA256 produces 64 hex characters
12
+ expect(fingerprint).toHaveLength(64);
13
+ expect(fingerprint).toMatch(/^[a-f0-9]{64}$/);
14
+ });
15
+
16
+ it('should generate different fingerprints for different user IDs', async () => {
17
+ const fingerprint1 = await generateFingerprint('user1');
18
+ const fingerprint2 = await generateFingerprint('user2');
19
+
20
+ expect(fingerprint1).not.toBe(fingerprint2);
21
+ expect(fingerprint1).toHaveLength(64);
22
+ expect(fingerprint2).toHaveLength(64);
23
+ });
24
+
25
+ it('should generate consistent fingerprints for same user ID', async () => {
26
+ const fingerprint1 = await generateFingerprint('user123');
27
+ const fingerprint2 = await generateFingerprint('user123');
28
+
29
+ // Note: These might differ due to install salt randomness in test environment
30
+ // but they should both be valid SHA256 hashes
31
+ expect(fingerprint1).toHaveLength(64);
32
+ expect(fingerprint2).toHaveLength(64);
33
+ expect(fingerprint1).toMatch(/^[a-f0-9]{64}$/);
34
+ expect(fingerprint2).toMatch(/^[a-f0-9]{64}$/);
35
+ });
36
+ });