mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00

🔄 Dynamic Processing Strategies - Adaptive routing: Same tool uses different backend endpoints based on file analysis - Combined vs separate processing: Intelligently chooses between merge operations and individual file processing - Cross-format workflows: Enable complex conversions like "mixed files → PDF" that other tools can't handle ⚙️ Format-Specific Intelligence Each conversion type gets tailored options: - HTML/ZIP → PDF: Zoom controls (0.1-3.0 increments) with live preview - Email → PDF: Attachment handling, size limits, recipient control - PDF → PDF/A: Digital signature detection with warnings - Images → PDF: Smart combining vs individual file options File Architecture Core Implementation: ├── Convert.tsx # Main stepped workflow UI ├── ConvertSettings.tsx # Centralized settings with smart detection ├── GroupedFormatDropdown.tsx # Enhanced format selector with grouping ├── useConvertParameters.ts # Smart detection & parameter management ├── useConvertOperation.ts # Multi-strategy processing logic └── Settings Components: ├── ConvertFromWebSettings.tsx # HTML zoom controls ├── ConvertFromEmailSettings.tsx # Email attachment options ├── ConvertToPdfaSettings.tsx # PDF/A with signature detection ├── ConvertFromImageSettings.tsx # Image PDF options └── ConvertToImageSettings.tsx # PDF to image options Utility Layer Utils & Services: ├── convertUtils.ts # Format detection & endpoint routing ├── fileResponseUtils.ts # Generic API response handling └── setupTests.ts # Enhanced test environment with crypto mocks Testing & Quality Comprehensive Test Coverage Test Suite: ├── useConvertParameters.test.ts # Parameter logic & smart detection ├── useConvertParametersAutoDetection.test.ts # File type analysis ├── ConvertIntegration.test.tsx # End-to-end conversion workflows ├── ConvertSmartDetectionIntegration.test.tsx # Mixed file scenarios ├── ConvertE2E.spec.ts # Playwright browser tests ├── convertUtils.test.ts # Utility function validation └── fileResponseUtils.test.ts # API response handling Advanced Test Features - Crypto API mocking: Proper test environment for file hashing - File.arrayBuffer() polyfills: Complete browser API simulation - Multi-file scenario testing: Complex batch processing validation - CI/CD integration: Vitest runs in GitHub Actions with proper artifacts --------- Co-authored-by: Connor Yoh <connor@stirlingpdf.com> Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
147 lines
5.1 KiB
TypeScript
147 lines
5.1 KiB
TypeScript
/**
|
|
* Unit tests for file response utility functions
|
|
*/
|
|
|
|
import { describe, test, expect } from 'vitest';
|
|
import { getFilenameFromHeaders, createFileFromApiResponse } from './fileResponseUtils';
|
|
|
|
describe('fileResponseUtils', () => {
|
|
|
|
describe('getFilenameFromHeaders', () => {
|
|
|
|
test('should extract filename from content-disposition header', () => {
|
|
const contentDisposition = 'attachment; filename="document.pdf"';
|
|
const filename = getFilenameFromHeaders(contentDisposition);
|
|
|
|
expect(filename).toBe('document.pdf');
|
|
});
|
|
|
|
test('should extract filename without quotes', () => {
|
|
const contentDisposition = 'attachment; filename=document.pdf';
|
|
const filename = getFilenameFromHeaders(contentDisposition);
|
|
|
|
expect(filename).toBe('document.pdf');
|
|
});
|
|
|
|
test('should handle single quotes', () => {
|
|
const contentDisposition = "attachment; filename='document.pdf'";
|
|
const filename = getFilenameFromHeaders(contentDisposition);
|
|
|
|
expect(filename).toBe('document.pdf');
|
|
});
|
|
|
|
test('should return null for malformed header', () => {
|
|
const contentDisposition = 'attachment; invalid=format';
|
|
const filename = getFilenameFromHeaders(contentDisposition);
|
|
|
|
expect(filename).toBe(null);
|
|
});
|
|
|
|
test('should return null for empty header', () => {
|
|
const filename = getFilenameFromHeaders('');
|
|
|
|
expect(filename).toBe(null);
|
|
});
|
|
|
|
test('should return null for undefined header', () => {
|
|
const filename = getFilenameFromHeaders();
|
|
|
|
expect(filename).toBe(null);
|
|
});
|
|
|
|
test('should handle complex filenames with spaces and special chars', () => {
|
|
const contentDisposition = 'attachment; filename="My Document (1).pdf"';
|
|
const filename = getFilenameFromHeaders(contentDisposition);
|
|
|
|
expect(filename).toBe('My Document (1).pdf');
|
|
});
|
|
|
|
test('should handle filename with extension when downloadHtml is enabled', () => {
|
|
const contentDisposition = 'attachment; filename="email_content.html"';
|
|
const filename = getFilenameFromHeaders(contentDisposition);
|
|
|
|
expect(filename).toBe('email_content.html');
|
|
});
|
|
});
|
|
|
|
describe('createFileFromApiResponse', () => {
|
|
|
|
test('should create file using header filename when available', () => {
|
|
const responseData = new Uint8Array([1, 2, 3, 4]);
|
|
const headers = {
|
|
'content-type': 'application/pdf',
|
|
'content-disposition': 'attachment; filename="server_filename.pdf"'
|
|
};
|
|
const fallbackFilename = 'fallback.pdf';
|
|
|
|
const file = createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
|
|
expect(file.name).toBe('server_filename.pdf');
|
|
expect(file.type).toBe('application/pdf');
|
|
expect(file.size).toBe(4);
|
|
});
|
|
|
|
test('should use fallback filename when no header filename', () => {
|
|
const responseData = new Uint8Array([1, 2, 3, 4]);
|
|
const headers = {
|
|
'content-type': 'application/pdf'
|
|
};
|
|
const fallbackFilename = 'converted_file.pdf';
|
|
|
|
const file = createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
|
|
expect(file.name).toBe('converted_file.pdf');
|
|
expect(file.type).toBe('application/pdf');
|
|
});
|
|
|
|
test('should handle HTML response when downloadHtml is enabled', () => {
|
|
const responseData = '<html><body>Test</body></html>';
|
|
const headers = {
|
|
'content-type': 'text/html',
|
|
'content-disposition': 'attachment; filename="email_content.html"'
|
|
};
|
|
const fallbackFilename = 'fallback.pdf';
|
|
|
|
const file = createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
|
|
expect(file.name).toBe('email_content.html');
|
|
expect(file.type).toBe('text/html');
|
|
});
|
|
|
|
test('should handle ZIP response', () => {
|
|
const responseData = new Uint8Array([80, 75, 3, 4]); // ZIP file signature
|
|
const headers = {
|
|
'content-type': 'application/zip',
|
|
'content-disposition': 'attachment; filename="converted_files.zip"'
|
|
};
|
|
const fallbackFilename = 'fallback.pdf';
|
|
|
|
const file = createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
|
|
expect(file.name).toBe('converted_files.zip');
|
|
expect(file.type).toBe('application/zip');
|
|
});
|
|
|
|
test('should use default content-type when none provided', () => {
|
|
const responseData = new Uint8Array([1, 2, 3, 4]);
|
|
const headers = {};
|
|
const fallbackFilename = 'test.bin';
|
|
|
|
const file = createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
|
|
expect(file.name).toBe('test.bin');
|
|
expect(file.type).toBe('application/octet-stream');
|
|
});
|
|
|
|
test('should handle null/undefined headers gracefully', () => {
|
|
const responseData = new Uint8Array([1, 2, 3, 4]);
|
|
const headers = null;
|
|
const fallbackFilename = 'test.bin';
|
|
|
|
const file = createFileFromApiResponse(responseData, headers, fallbackFilename);
|
|
|
|
expect(file.name).toBe('test.bin');
|
|
expect(file.type).toBe('application/octet-stream');
|
|
});
|
|
});
|
|
}); |