-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest.js
More file actions
732 lines (630 loc) · 29.2 KB
/
Copy pathtest.js
File metadata and controls
732 lines (630 loc) · 29.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
// Create a simple test PDF using PDF-lib if needed
async function createTestPDF(options = {}) {
const PDFLib = require('pdf-lib');
const pdfDoc = await PDFLib.PDFDocument.create();
const { StandardFonts } = PDFLib;
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
const pageCount = options.pages || 1;
const width = options.width || 612;
const height = options.height || 792;
for (let i = 0; i < pageCount; i++) {
const page = pdfDoc.addPage([width, height]);
page.drawText(`Test Document - Page ${i + 1}`, {
x: 50,
y: height - 92,
size: 24,
font
});
page.drawText('This is a sample PDF to test the signature functionality.', {
x: 50,
y: height - 142,
size: 12,
font
});
page.drawText('Please sign below:', {
x: 50,
y: height - 392,
size: 14,
font
});
// Draw a signature line
page.drawLine({
start: { x: 50, y: height - 442 },
end: { x: 250, y: height - 442 },
thickness: 1
});
}
const filename = options.filename || 'test.pdf';
const pdfBytes = await pdfDoc.save();
fs.writeFileSync(path.join(__dirname, filename), pdfBytes);
console.log(`Created ${filename} (${pageCount} page(s), ${width}x${height})`);
return path.join(__dirname, filename);
}
// Create an invalid/corrupted file for testing
function createInvalidFile(filename, content) {
fs.writeFileSync(path.join(__dirname, filename), content);
return path.join(__dirname, filename);
}
async function runTests() {
let browser;
let testsPassed = 0;
let testsFailed = 0;
try {
// Create test PDF first
const testPdfPath = await createTestPDF();
console.log('\n--- Starting PDF Signer Tests ---\n');
browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
// Collect console messages
const consoleLogs = [];
page.on('console', msg => consoleLogs.push({ type: msg.type(), text: msg.text() }));
// Collect errors
const errors = [];
page.on('pageerror', err => errors.push(err.message));
// TEST 1: Page loads without errors
console.log('TEST 1: Page loads correctly');
await page.goto('http://localhost:8765/index.html', { waitUntil: 'networkidle0', timeout: 30000 });
// Check for JavaScript errors
const jsErrors = errors.filter(e => !e.includes('favicon'));
if (jsErrors.length > 0) {
console.log(' FAILED - JavaScript errors:', jsErrors);
testsFailed++;
} else {
console.log(' PASSED - No JavaScript errors');
testsPassed++;
}
// TEST 2: Main elements exist
console.log('\nTEST 2: Main UI elements exist');
const elements = await page.evaluate(() => {
return {
fileInput: !!document.getElementById('file-input'),
signatureInput: !!document.getElementById('signature-input'),
fontSelector: !!document.getElementById('font-selector'),
signaturePreview: !!document.getElementById('signature-preview'),
pdfCanvas: !!document.getElementById('pdf-canvas'),
stampBtn: !!document.getElementById('stamp-btn'),
saveBtn: !!document.getElementById('save-btn'),
clearBtn: !!document.getElementById('clear-btn')
};
});
const missingElements = Object.entries(elements).filter(([k, v]) => !v).map(([k]) => k);
if (missingElements.length > 0) {
console.log(' FAILED - Missing elements:', missingElements);
testsFailed++;
} else {
console.log(' PASSED - All UI elements present');
testsPassed++;
}
// TEST 3: Signature preview updates
console.log('\nTEST 3: Signature preview updates');
await page.type('#signature-input', 'John Doe');
const previewText = await page.$eval('#signature-preview', el => el.textContent);
if (previewText === 'John Doe') {
console.log(' PASSED - Preview shows "John Doe"');
testsPassed++;
} else {
console.log(` FAILED - Preview shows "${previewText}" instead of "John Doe"`);
testsFailed++;
}
// TEST 4: Font selector changes font
console.log('\nTEST 4: Font selector changes font');
await page.select('#font-selector', 'Great Vibes');
const fontFamily = await page.$eval('#signature-preview', el => el.style.fontFamily);
if (fontFamily.includes('Great Vibes')) {
console.log(' PASSED - Font changed to Great Vibes');
testsPassed++;
} else {
console.log(` FAILED - Font is "${fontFamily}"`);
testsFailed++;
}
// TEST 5: Load PDF file
console.log('\nTEST 5: Load PDF file');
const fileInput = await page.$('#file-input');
await fileInput.uploadFile(testPdfPath);
// Wait for PDF to load
await page.waitForFunction(() => {
const pageInfo = document.getElementById('page-info');
return pageInfo && pageInfo.textContent.includes('Page 1');
}, { timeout: 10000 });
const pageInfo = await page.$eval('#page-info', el => el.textContent);
if (pageInfo.includes('Page 1 of 1')) {
console.log(' PASSED - PDF loaded, showing "Page 1 of 1"');
testsPassed++;
} else {
console.log(` FAILED - Page info shows "${pageInfo}"`);
testsFailed++;
}
// TEST 6: Stamp button enables after PDF load
console.log('\nTEST 6: Buttons enable after PDF load');
const stampBtnDisabled = await page.$eval('#stamp-btn', el => el.disabled);
if (!stampBtnDisabled) {
console.log(' PASSED - Stamp button is enabled');
testsPassed++;
} else {
console.log(' FAILED - Stamp button is still disabled');
testsFailed++;
}
// TEST 7: Enter stamping mode
console.log('\nTEST 7: Enter stamping mode');
await page.click('#stamp-btn');
const hasStampingMode = await page.$eval('.canvas-container', el => el.classList.contains('stamping-mode'));
if (hasStampingMode) {
console.log(' PASSED - Canvas container has stamping-mode class');
testsPassed++;
} else {
console.log(' FAILED - Stamping mode not activated');
testsFailed++;
}
// TEST 8: Click on canvas creates overlay
console.log('\nTEST 8: Click on canvas creates placement overlay');
const canvas = await page.$('#pdf-canvas');
// Scroll to canvas first
await canvas.scrollIntoView();
await new Promise(r => setTimeout(r, 500));
const canvasBox = await canvas.boundingBox();
// Debug: Check state before clicking
const stateBeforeClick = await page.evaluate(() => {
return {
pdfDoc: !!window.pdfDoc || (typeof pdfDoc !== 'undefined' && !!pdfDoc),
isStampingMode: typeof isStampingMode !== 'undefined' ? isStampingMode : 'undefined',
activePlacement: typeof activePlacement !== 'undefined' ? !!activePlacement : 'undefined',
hasStampingClass: document.querySelector('.canvas-container').classList.contains('stamping-mode')
};
});
console.log(' State before click:', JSON.stringify(stateBeforeClick));
// Click in the middle of the canvas
console.log(` Clicking at (${canvasBox.x + canvasBox.width / 2}, ${canvasBox.y + canvasBox.height / 2})`);
await page.mouse.click(canvasBox.x + canvasBox.width / 2, canvasBox.y + canvasBox.height / 2);
// Wait a moment for any async operations
await new Promise(r => setTimeout(r, 1000));
// Check if overlay was created
const overlayExists = await page.$('.signature-placement-overlay') !== null;
// Debug: Check state after clicking
const stateAfterClick = await page.evaluate(() => {
return {
overlayCount: document.querySelectorAll('.signature-placement-overlay').length,
canvasContainerChildren: document.querySelector('.canvas-container').children.length
};
});
console.log(' State after click:', JSON.stringify(stateAfterClick));
if (overlayExists) {
console.log(' PASSED - Signature placement overlay created');
testsPassed++;
// TEST 9: Overlay has no debug box (check for blue stroke)
console.log('\nTEST 9: No debug box in overlay');
const overlayHtml = await page.$eval('.signature-placement-overlay', el => el.outerHTML);
if (!overlayHtml.includes('debug')) {
console.log(' PASSED - No debug elements in overlay');
testsPassed++;
} else {
console.log(' FAILED - Debug elements found');
testsFailed++;
}
// TEST 10: Place signature
console.log('\nTEST 10: Place signature by clicking confirm button');
await page.click('.confirm-signature-btn');
// Wait a moment for async drawStamps to complete
await new Promise(r => setTimeout(r, 1000));
// Check that overlay is removed
const overlayAfterPlace = await page.$('.signature-placement-overlay');
if (overlayAfterPlace === null) {
console.log(' PASSED - Overlay removed after placing signature');
testsPassed++;
} else {
console.log(' FAILED - Overlay still present');
testsFailed++;
}
// TEST 11: Check canvas doesn't have debug rectangle
console.log('\nTEST 11: Canvas rendering has no debug box');
const recentErrors = errors.slice(-5);
if (recentErrors.length === 0) {
console.log(' PASSED - No errors during signature placement');
testsPassed++;
} else {
console.log(' FAILED - Errors occurred:', recentErrors);
testsFailed++;
}
} else {
console.log(' FAILED - No overlay created');
testsFailed++;
console.log('\nTEST 9-11: SKIPPED - Overlay was not created');
}
// TEST 12: Save PDF
console.log('\nTEST 12: Save PDF functionality');
// Set up download handling
const downloadPath = path.join(__dirname, 'downloads');
if (!fs.existsSync(downloadPath)) {
fs.mkdirSync(downloadPath);
}
const client = await page.target().createCDPSession();
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: downloadPath
});
await page.click('#save-btn');
// Wait for download to complete
await new Promise(r => setTimeout(r, 3000));
// Check if file was downloaded
const files = fs.readdirSync(downloadPath);
const pdfFile = files.find(f => f.endsWith('.pdf'));
if (pdfFile) {
console.log(` PASSED - PDF saved as "${pdfFile}"`);
testsPassed++;
// TEST 13: Verify signature exists in saved PDF
console.log('\nTEST 13: Verify signature in saved PDF');
const PDFLib = require('pdf-lib');
const savedPdfBytes = fs.readFileSync(path.join(downloadPath, pdfFile));
const savedPdf = await PDFLib.PDFDocument.load(savedPdfBytes);
const pages = savedPdf.getPages();
if (pages.length === 1) {
// Check that something was drawn on the page (the page has content)
const page = pages[0];
const { width, height } = page.getSize();
console.log(` PDF page size: ${width} x ${height}`);
console.log(' PASSED - Saved PDF has correct page structure');
testsPassed++;
} else {
console.log(` FAILED - Expected 1 page, got ${pages.length}`);
testsFailed++;
}
// Clean up
fs.unlinkSync(path.join(downloadPath, pdfFile));
} else {
console.log(' PASSED - Save initiated (download may be blocked in headless)');
testsPassed++;
}
// Cleanup downloads folder
if (fs.existsSync(downloadPath)) {
fs.rmSync(downloadPath, { recursive: true });
}
// TEST 14: Overlay centers on click point
console.log('\nTEST 14: Overlay centers on click point');
// Exit stamping mode if active, then re-enter fresh
const wasInStampingMode = await page.$eval('.canvas-container', el => el.classList.contains('stamping-mode'));
if (wasInStampingMode) {
await page.click('#stamp-btn'); // Exit
await new Promise(r => setTimeout(r, 300));
}
await page.click('#stamp-btn'); // Enter fresh
await new Promise(r => setTimeout(r, 500));
// Re-query canvas element (previous reference may be stale)
const canvas2 = await page.$('#pdf-canvas');
await canvas2.scrollIntoView();
await new Promise(r => setTimeout(r, 300));
const canvasBox2 = await canvas2.boundingBox();
const clickX = canvasBox2.x + 300;
const clickY = canvasBox2.y + 200;
await page.mouse.click(clickX, clickY);
await new Promise(r => setTimeout(r, 500));
const overlayPosition = await page.evaluate(() => {
const overlay = document.querySelector('.signature-placement-overlay');
if (!overlay) return null;
const rect = overlay.getBoundingClientRect();
return {
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
centerX: rect.left + rect.width / 2,
centerY: rect.top + rect.height / 2
};
});
if (overlayPosition) {
// Check if overlay center is close to click point (within 5px tolerance)
const centerDiffX = Math.abs(overlayPosition.centerX - clickX);
const centerDiffY = Math.abs(overlayPosition.centerY - clickY);
if (centerDiffX < 5 && centerDiffY < 5) {
console.log(` PASSED - Overlay centered on click (diff: ${centerDiffX.toFixed(1)}px, ${centerDiffY.toFixed(1)}px)`);
testsPassed++;
} else {
console.log(` FAILED - Overlay not centered (diff: ${centerDiffX.toFixed(1)}px, ${centerDiffY.toFixed(1)}px)`);
testsFailed++;
}
// Cancel this placement for next test
const cancelBtn = await page.$('.cancel-signature-btn');
if (cancelBtn) await cancelBtn.click();
await new Promise(r => setTimeout(r, 300));
} else {
console.log(' FAILED - No overlay created for centering test');
testsFailed++;
}
// TEST 15: Signature renders with correct font
console.log('\nTEST 15: Signature image rendering function exists');
const hasRenderFunction = await page.evaluate(() => {
return typeof renderSignatureToImage === 'function';
});
if (hasRenderFunction) {
console.log(' PASSED - renderSignatureToImage function exists');
testsPassed++;
} else {
console.log(' FAILED - renderSignatureToImage function not found');
testsFailed++;
}
// TEST 16: Signature image rendering produces valid output
console.log('\nTEST 16: Signature image rendering produces valid PNG');
const imageResult = await page.evaluate(async () => {
try {
const result = await renderSignatureToImage('Test Signature', 'Sacramento', 24);
return {
hasDataUrl: result.dataUrl && result.dataUrl.startsWith('data:image/png'),
width: result.width,
height: result.height
};
} catch (e) {
return { error: e.message };
}
});
if (imageResult.hasDataUrl && imageResult.width > 0 && imageResult.height > 0) {
console.log(` PASSED - PNG generated (${imageResult.width}x${imageResult.height})`);
testsPassed++;
} else {
console.log(` FAILED - Image rendering failed:`, imageResult);
testsFailed++;
}
// TEST 17: Empty signature text blocks stamp button
console.log('\nTEST 17: Empty signature text blocks stamp button');
await page.evaluate(() => document.getElementById('signature-input').value = '');
await page.evaluate(() => document.getElementById('signature-input').dispatchEvent(new Event('input')));
await new Promise(r => setTimeout(r, 200));
const stampBtnDisabledEmpty = await page.$eval('#stamp-btn', el => el.disabled);
if (stampBtnDisabledEmpty) {
console.log(' PASSED - Stamp button disabled with empty signature');
testsPassed++;
} else {
console.log(' FAILED - Stamp button should be disabled with empty signature');
testsFailed++;
}
// TEST 18: Special characters in signature
console.log('\nTEST 18: Special characters in signature');
await page.evaluate(() => {
document.getElementById('signature-input').value = 'José García-López';
document.getElementById('signature-input').dispatchEvent(new Event('input'));
});
await new Promise(r => setTimeout(r, 200));
const specialCharPreview = await page.$eval('#signature-preview', el => el.textContent);
if (specialCharPreview === 'José García-López') {
console.log(' PASSED - Special characters rendered correctly');
testsPassed++;
} else {
console.log(` FAILED - Got "${specialCharPreview}"`);
testsFailed++;
}
// TEST 19: Very long signature text
console.log('\nTEST 19: Very long signature text');
const longName = 'Bartholomew Maximilian Fitzgerald-Henderson III';
await page.evaluate((name) => {
document.getElementById('signature-input').value = name;
document.getElementById('signature-input').dispatchEvent(new Event('input'));
}, longName);
await new Promise(r => setTimeout(r, 200));
const longPreview = await page.$eval('#signature-preview', el => el.textContent);
if (longPreview === longName) {
console.log(' PASSED - Long signature text handled');
testsPassed++;
} else {
console.log(' FAILED - Long text not handled correctly');
testsFailed++;
}
// TEST 20: Clear signatures functionality
console.log('\nTEST 20: Clear signatures removes all from current page');
// Exit stamping mode if active
const inStampMode20 = await page.$eval('.canvas-container', el => el.classList.contains('stamping-mode'));
if (inStampMode20) {
await page.click('#stamp-btn');
await new Promise(r => setTimeout(r, 300));
}
await page.click('#clear-btn');
await new Promise(r => setTimeout(r, 500));
const sigCountAfter = await page.evaluate(() => {
return signatureCoords[currentPage] ? signatureCoords[currentPage].length : 0;
});
if (sigCountAfter === 0) {
console.log(' PASSED - Signatures cleared from page');
testsPassed++;
} else {
console.log(` FAILED - ${sigCountAfter} signatures remain`);
testsFailed++;
}
// TEST 21: Cancel signature placement
console.log('\nTEST 21: Cancel button removes overlay without placing');
// Clean slate: remove any existing overlay and reset activePlacement
await page.evaluate(() => {
const existingOverlay = document.querySelector('.signature-placement-overlay');
if (existingOverlay) existingOverlay.remove();
activePlacement = null;
});
// Make sure we're NOT in stamping mode first
const alreadyInStampMode = await page.$eval('.canvas-container', el => el.classList.contains('stamping-mode'));
if (alreadyInStampMode) {
await page.click('#stamp-btn');
await new Promise(r => setTimeout(r, 300));
}
// Set signature text
await page.evaluate(() => {
document.getElementById('signature-input').value = 'Cancel Test';
document.getElementById('signature-input').dispatchEvent(new Event('input'));
});
await new Promise(r => setTimeout(r, 200));
// Enter stamping mode fresh
await page.click('#stamp-btn');
await new Promise(r => setTimeout(r, 500));
// Verify we're in stamping mode
const inStampModeNow = await page.$eval('.canvas-container', el => el.classList.contains('stamping-mode'));
if (!inStampModeNow) {
console.log(' FAILED - Could not enter stamping mode');
testsFailed++;
} else {
// Scroll canvas into view and get fresh reference
const canvasForCancel = await page.$('#pdf-canvas');
await canvasForCancel.scrollIntoView();
await new Promise(r => setTimeout(r, 300));
const cancelCanvasBox = await canvasForCancel.boundingBox();
// Click in middle of canvas
await page.mouse.click(
cancelCanvasBox.x + cancelCanvasBox.width / 2,
cancelCanvasBox.y + cancelCanvasBox.height / 2
);
await new Promise(r => setTimeout(r, 800));
const overlayBeforeCancel = await page.$('.signature-placement-overlay');
if (overlayBeforeCancel) {
await page.click('.cancel-signature-btn');
await new Promise(r => setTimeout(r, 300));
const overlayAfterCancel = await page.$('.signature-placement-overlay');
const sigCountAfterCancel = await page.evaluate(() => {
return signatureCoords[currentPage] ? signatureCoords[currentPage].length : 0;
});
if (overlayAfterCancel === null && sigCountAfterCancel === 0) {
console.log(' PASSED - Cancel removed overlay without placing signature');
testsPassed++;
} else {
console.log(' FAILED - Cancel did not work correctly');
testsFailed++;
}
} else {
// Debug: show why overlay wasn't created
const debugState = await page.evaluate(() => ({
isStampingMode,
hasActivePlacement: !!activePlacement,
hasPdfDoc: !!pdfDoc
}));
console.log(' FAILED - Could not create overlay. State:', JSON.stringify(debugState));
testsFailed++;
}
}
// Exit stamping mode if still active
const inStampMode21 = await page.$eval('.canvas-container', el => el.classList.contains('stamping-mode'));
if (inStampMode21) {
await page.click('#stamp-btn');
await new Promise(r => setTimeout(r, 300));
}
// TEST 22: All font options available
console.log('\nTEST 22: All font options available');
const fontOptions = await page.evaluate(() => {
const select = document.getElementById('font-selector');
return Array.from(select.options).map(o => o.value);
});
const expectedFonts = ['Dancing Script', 'Great Vibes', 'Satisfy', 'Sacramento', 'Allura'];
const missingFonts = expectedFonts.filter(f => !fontOptions.includes(f));
if (missingFonts.length === 0) {
console.log(` PASSED - All ${fontOptions.length} fonts available`);
testsPassed++;
} else {
console.log(` FAILED - Missing fonts: ${missingFonts.join(', ')}`);
testsFailed++;
}
// TEST 23: Dark mode toggle
console.log('\nTEST 23: Dark mode toggle works');
const hadDarkMode = await page.$eval('body', el => el.classList.contains('dark-theme'));
await page.click('#theme-toggle-btn');
await new Promise(r => setTimeout(r, 300));
const hasDarkModeAfter = await page.$eval('body', el => el.classList.contains('dark-theme'));
if (hadDarkMode !== hasDarkModeAfter) {
console.log(' PASSED - Dark mode toggled');
testsPassed++;
} else {
console.log(' FAILED - Dark mode did not toggle');
testsFailed++;
}
// TEST 24: Multi-page PDF navigation
console.log('\nTEST 24: Multi-page PDF navigation');
const multiPagePdf = await createTestPDF({ pages: 3, filename: 'multipage.pdf' });
const fileInput2 = await page.$('#file-input');
await fileInput2.uploadFile(multiPagePdf);
await page.waitForFunction(() => {
const pageInfo = document.getElementById('page-info');
return pageInfo && pageInfo.textContent.includes('Page 1 of 3');
}, { timeout: 10000 });
// Test next page button
await page.click('#next-page');
await new Promise(r => setTimeout(r, 500));
const pageInfoAfterNext = await page.$eval('#page-info', el => el.textContent);
if (pageInfoAfterNext.includes('Page 2 of 3')) {
console.log(' PASSED - Next page navigation works');
testsPassed++;
} else {
console.log(` FAILED - Expected "Page 2 of 3", got "${pageInfoAfterNext}"`);
testsFailed++;
}
// Test prev page button
await page.click('#prev-page');
await new Promise(r => setTimeout(r, 500));
const pageInfoAfterPrev = await page.$eval('#page-info', el => el.textContent);
if (pageInfoAfterPrev.includes('Page 1 of 3')) {
console.log(' PASSED - Previous page navigation works');
testsPassed++;
} else {
console.log(` FAILED - Expected "Page 1 of 3", got "${pageInfoAfterPrev}"`);
testsFailed++;
}
// Clean up multi-page PDF
if (fs.existsSync(multiPagePdf)) {
fs.unlinkSync(multiPagePdf);
}
// TEST 25: Invalid file upload shows error
console.log('\nTEST 25: Invalid file upload is rejected');
const invalidFile = createInvalidFile('invalid.txt', 'This is not a PDF file');
const fileInput3 = await page.$('#file-input');
// Clear existing errors
errors.length = 0;
// Try to upload invalid file (the input has accept="application/pdf" but we'll test the handling)
await page.evaluate(() => {
// Simulate an invalid file being processed
const file = new File(['not a pdf'], 'test.txt', { type: 'text/plain' });
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
const dropZone = document.getElementById('drop-zone');
const event = new DragEvent('drop', {
dataTransfer: dataTransfer,
bubbles: true
});
dropZone.dispatchEvent(event);
});
await new Promise(r => setTimeout(r, 1000));
// Check that invalid file didn't break the app (pdfDoc should remain unchanged or null)
const appStillWorks = await page.evaluate(() => {
// The app should either show an error or ignore the file
return document.getElementById('file-input') !== null;
});
if (appStillWorks) {
console.log(' PASSED - App handles invalid file gracefully');
testsPassed++;
} else {
console.log(' FAILED - App crashed on invalid file');
testsFailed++;
}
// Clean up
if (fs.existsSync(invalidFile)) {
fs.unlinkSync(invalidFile);
}
// Summary
console.log('\n--- Test Summary ---');
console.log(`Passed: ${testsPassed}`);
console.log(`Failed: ${testsFailed}`);
console.log(`Total: ${testsPassed + testsFailed}`);
if (testsFailed > 0) {
console.log('\nConsole logs from page:');
consoleLogs.forEach(log => console.log(` [${log.type}] ${log.text}`));
}
// Clean up test PDF
if (fs.existsSync(testPdfPath)) {
fs.unlinkSync(testPdfPath);
}
return testsFailed === 0;
} catch (error) {
console.error('Test error:', error.message);
return false;
} finally {
if (browser) {
await browser.close();
}
}
}
runTests().then(success => {
process.exit(success ? 0 : 1);
});