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.
- package/.github/workflows/ci.yml +1 -1
- package/README.md +1 -1
- package/dist/__tests__/e2e/pdf-assertions.d.ts +1 -0
- package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
- package/dist/__tests__/e2e/pdf-assertions.js +9 -3
- package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
- package/dist/fingerprint.d.ts +12 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +196 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/image-builder.d.ts +4 -2
- package/dist/image-builder.d.ts.map +1 -1
- package/dist/image-builder.js +12 -3
- package/dist/image-builder.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +75 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +179 -21
- package/dist/models.js.map +1 -1
- package/dist/page-builder.d.ts +24 -0
- package/dist/page-builder.d.ts.map +1 -0
- package/dist/page-builder.js +107 -0
- package/dist/page-builder.js.map +1 -0
- package/dist/paragraph-builder.d.ts +48 -54
- package/dist/paragraph-builder.d.ts.map +1 -1
- package/dist/paragraph-builder.js +408 -135
- package/dist/paragraph-builder.js.map +1 -1
- package/dist/pdfdancer_v1.d.ts +90 -9
- package/dist/pdfdancer_v1.d.ts.map +1 -1
- package/dist/pdfdancer_v1.js +535 -50
- package/dist/pdfdancer_v1.js.map +1 -1
- package/dist/types.d.ts +24 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +117 -2
- package/dist/types.js.map +1 -1
- package/docs/openapi.yml +2076 -0
- package/fixtures/Showcase.pdf +0 -0
- package/package.json +1 -1
- package/src/__tests__/e2e/acroform.test.ts +5 -5
- package/src/__tests__/e2e/context-manager-showcase.test.ts +267 -0
- package/src/__tests__/e2e/form_x_object.test.ts +1 -1
- package/src/__tests__/e2e/image-showcase.test.ts +133 -0
- package/src/__tests__/e2e/image.test.ts +1 -1
- package/src/__tests__/e2e/line-showcase.test.ts +118 -0
- package/src/__tests__/e2e/line.test.ts +1 -16
- package/src/__tests__/e2e/page-showcase.test.ts +154 -0
- package/src/__tests__/e2e/paragraph-showcase.test.ts +523 -0
- package/src/__tests__/e2e/paragraph.test.ts +8 -8
- package/src/__tests__/e2e/pdf-assertions.ts +10 -3
- package/src/__tests__/e2e/pdfdancer-showcase.test.ts +40 -0
- package/src/__tests__/e2e/snapshot-showcase.test.ts +158 -0
- package/src/__tests__/e2e/snapshot.test.ts +296 -0
- package/src/__tests__/fingerprint.test.ts +36 -0
- package/src/fingerprint.ts +169 -0
- package/src/image-builder.ts +13 -6
- package/src/index.ts +6 -1
- package/src/models.ts +208 -24
- package/src/page-builder.ts +130 -0
- package/src/paragraph-builder.ts +517 -159
- package/src/pdfdancer_v1.ts +630 -51
- package/src/types.ts +145 -2
- 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',
|
|
151
|
-
await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold',
|
|
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(
|
|
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',
|
|
534
|
-
await assertions.
|
|
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',
|
|
547
|
-
await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold',
|
|
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
|
+
});
|