import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as smartpreview from '../ts_web/index.ts'; // Test data - minimal PDF as Uint8Array for browser testing const createMinimalPdfBuffer = (): Uint8Array => { const pdfContent = `%PDF-1.4 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj 3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >> endobj 4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj 5 0 obj << /Length 44 >> stream BT /F1 12 Tf 72 720 Td (Hello World) Tj ET endstream endobj xref 0 6 0000000000 65535 f 0000000010 00000 n 0000000079 00000 n 0000000136 00000 n 0000000273 00000 n 0000000362 00000 n trailer << /Size 6 /Root 1 0 R >> startxref 456 %%EOF`; return new TextEncoder().encode(pdfContent); }; // Create a mock File object for testing const createMockPdfFile = (): File => { const buffer = createMinimalPdfBuffer(); return new File([buffer], 'test.pdf', { type: 'application/pdf' }); }; tap.test('should check browser compatibility', async () => { const compatibility = smartpreview.SmartPreview.getBrowserCompatibility(); expect(compatibility).toHaveProperty('fileApi'); expect(compatibility).toHaveProperty('webWorkers'); expect(compatibility).toHaveProperty('offscreenCanvas'); expect(compatibility).toHaveProperty('isSupported'); expect(typeof compatibility.fileApi).toEqual('boolean'); expect(typeof compatibility.webWorkers).toEqual('boolean'); expect(typeof compatibility.offscreenCanvas).toEqual('boolean'); expect(typeof compatibility.isSupported).toEqual('boolean'); }); tap.test('should create SmartPreview instance', async () => { const preview = new smartpreview.SmartPreview(); expect(preview).toBeInstanceOf(smartpreview.SmartPreview); }); tap.test('should return supported formats', async () => { const preview = new smartpreview.SmartPreview(); const formats = preview.getSupportedFormats(); expect(formats).toContain('pdf'); expect(preview.isFormatSupported('pdf')).toEqual(true); expect(preview.isFormatSupported('jpg')).toEqual(false); }); tap.test('should throw error when not initialized', async () => { const preview = new smartpreview.SmartPreview(); const testFile = createMockPdfFile(); try { await preview.generatePreview(testFile); expect(true).toEqual(false); // Should not reach here } catch (error) { expect(error).toBeInstanceOf(smartpreview.PreviewError); expect(error.errorType).toEqual('PROCESSING_FAILED'); } }); tap.test('should validate input', async () => { const preview = new smartpreview.SmartPreview(); try { await preview.generatePreview(null as any); expect(true).toEqual(false); // Should not reach here } catch (error) { expect(error).toBeInstanceOf(smartpreview.PreviewError); expect(error.errorType).toEqual('PROCESSING_FAILED'); } }); tap.test('should handle initialization', async () => { const preview = new smartpreview.SmartPreview(); try { await preview.init(); expect(true).toEqual(true); // If we get here, init succeeded } catch (error) { // Expected if browser APIs are not fully available in test environment expect(error).toBeInstanceOf(smartpreview.PreviewError); } finally { await preview.cleanup(); } }); tap.test('should create PreviewError correctly', async () => { const error = new smartpreview.PreviewError('INVALID_INPUT', 'Test error message'); expect(error).toBeInstanceOf(Error); expect(error).toBeInstanceOf(smartpreview.PreviewError); expect(error.errorType).toEqual('INVALID_INPUT'); expect(error.message).toEqual('Test error message'); expect(error.name).toEqual('PreviewError'); }); tap.test('should handle different input types', async () => { // Test with File const file = createMockPdfFile(); expect(file).toBeInstanceOf(File); expect(file.type).toEqual('application/pdf'); // Test with ArrayBuffer const buffer = createMinimalPdfBuffer(); const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); expect(arrayBuffer).toBeInstanceOf(ArrayBuffer); // Test with Uint8Array expect(buffer).toBeInstanceOf(Uint8Array); }); tap.test('should provide download functionality methods', async () => { const preview = new smartpreview.SmartPreview(); // These methods should exist expect(typeof preview.createDownloadLink).toEqual('function'); expect(typeof preview.downloadPreview).toEqual('function'); expect(typeof preview.generatePreviewFromFile).toEqual('function'); expect(typeof preview.generatePreviewFromUrl).toEqual('function'); }); tap.test('should create instance via factory method', async () => { try { const preview = await smartpreview.SmartPreview.create(); expect(preview).toBeInstanceOf(smartpreview.SmartPreview); await preview.cleanup(); } catch (error) { // Expected if browser APIs are not fully available expect(error).toBeInstanceOf(smartpreview.PreviewError); } }); // Performance tests for measuring conversion times in browser tap.test('should measure browser initialization time', async () => { const startTime = performance.now(); const preview = new smartpreview.SmartPreview(); try { await preview.init(); const initTime = performance.now() - startTime; console.log(`Browser initialization time: ${initTime.toFixed(2)}ms`); // Browser initialization should be reasonably fast (under 10 seconds due to worker setup) expect(initTime).toBeLessThan(10000); await preview.cleanup(); } catch (error) { // Expected if browser APIs are not fully available expect(error).toBeInstanceOf(smartpreview.PreviewError); } }); tap.test('should measure browser PDF conversion time', async () => { const preview = new smartpreview.SmartPreview(); const testFile = createMockPdfFile(); try { await preview.init(); const startTime = performance.now(); const result = await preview.generatePreview(testFile, { quality: 80, width: 800, height: 600, generateDataUrl: true }); const conversionTime = performance.now() - startTime; console.log(`Browser PDF conversion time: ${conversionTime.toFixed(2)}ms`); console.log(`Generated preview size: ${result.size} bytes`); console.log(`Dimensions: ${result.dimensions.width}x${result.dimensions.height}`); console.log(`Data URL length: ${result.dataUrl.length} characters`); // Browser conversion should complete within reasonable time (under 15 seconds due to worker overhead) expect(conversionTime).toBeLessThan(15000); expect(result.size).toBeGreaterThan(0); expect(result.dataUrl.length).toBeGreaterThan(0); await preview.cleanup(); } catch (error) { // Expected if browser APIs are not fully available expect(error).toBeInstanceOf(smartpreview.PreviewError); } }); tap.test('should measure browser worker timeout handling', async () => { const preview = new smartpreview.SmartPreview(); const testFile = createMockPdfFile(); try { await preview.init(); const startTime = performance.now(); const result = await preview.generatePreview(testFile, { quality: 60, width: 400, height: 300, timeout: 5000, // 5 second timeout generateDataUrl: false }); const conversionTime = performance.now() - startTime; console.log(`Browser conversion with timeout: ${conversionTime.toFixed(2)}ms`); console.log(`Completed within timeout: ${conversionTime < 5000 ? 'Yes' : 'No'}`); expect(conversionTime).toBeLessThan(5000); // Should complete within timeout expect(result.size).toBeGreaterThan(0); await preview.cleanup(); } catch (error) { // Could be timeout error or browser API unavailable if (error instanceof smartpreview.PreviewError && error.errorType === 'WORKER_TIMEOUT') { console.log('Worker timeout occurred as expected for performance test'); } else { expect(error).toBeInstanceOf(smartpreview.PreviewError); } } }); tap.test('should measure different input type processing times', async () => { const preview = new smartpreview.SmartPreview(); try { await preview.init(); // Test File input const file = createMockPdfFile(); const fileStartTime = performance.now(); try { await preview.generatePreview(file, { quality: 70, width: 300, height: 200 }); const fileTime = performance.now() - fileStartTime; console.log(`File input processing time: ${fileTime.toFixed(2)}ms`); } catch (error) { console.log('File input test skipped due to browser limitations'); } // Test ArrayBuffer input const buffer = createMinimalPdfBuffer(); const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); const bufferStartTime = performance.now(); try { await preview.generatePreview(arrayBuffer, { quality: 70, width: 300, height: 200 }); const bufferTime = performance.now() - bufferStartTime; console.log(`ArrayBuffer input processing time: ${bufferTime.toFixed(2)}ms`); } catch (error) { console.log('ArrayBuffer input test skipped due to browser limitations'); } // Test Uint8Array input const uint8StartTime = performance.now(); try { await preview.generatePreview(buffer, { quality: 70, width: 300, height: 200 }); const uint8Time = performance.now() - uint8StartTime; console.log(`Uint8Array input processing time: ${uint8Time.toFixed(2)}ms`); } catch (error) { console.log('Uint8Array input test skipped due to browser limitations'); } await preview.cleanup(); } catch (error) { // Expected if browser APIs are not fully available expect(error).toBeInstanceOf(smartpreview.PreviewError); } }); tap.test('should measure progress callback overhead', async () => { const preview = new smartpreview.SmartPreview(); const testFile = createMockPdfFile(); const progressCalls: Array<{progress: number, stage: string, timestamp: number}> = []; try { await preview.init(); const startTime = performance.now(); await preview.generatePreview(testFile, { quality: 80, width: 600, height: 400, onProgress: (progress, stage) => { progressCalls.push({ progress, stage, timestamp: performance.now() - startTime }); } }); const totalTime = performance.now() - startTime; console.log(`Total conversion time with progress tracking: ${totalTime.toFixed(2)}ms`); console.log(`Progress callbacks received: ${progressCalls.length}`); if (progressCalls.length > 0) { console.log('Progress timeline:'); progressCalls.forEach((call, index) => { console.log(` ${index + 1}. ${call.stage}: ${call.progress}% at ${call.timestamp.toFixed(2)}ms`); }); } expect(totalTime).toBeGreaterThan(0); await preview.cleanup(); } catch (error) { // Expected if browser APIs are not fully available expect(error).toBeInstanceOf(smartpreview.PreviewError); } }); export default tap.start();