import { expect, tap } from '@git.zone/tstest/tapbundle'; import { EInvoice } from '../../../ts/index.js'; tap.test('ENC-03: ISO-8859-1 Encoding - should handle ISO-8859-1 (Latin-1) encoded documents', async () => { console.log('Testing ISO-8859-1 (Latin-1) encoding support...\n'); // Test 1: Direct ISO-8859-1 encoding const testIso88591Direct = async () => { // Create ISO-8859-1 content with Latin-1 specific characters const xmlContent = ` ISO88591-TEST 2025-01-25 ISO-8859-1 Test: àáâãäåæçèéêëìíîïñòóôõöøùúûüý 380 EUR Société Générale Rue de la Paix Paris 75001 FR Müller & Associés Königstraße München 80331 DE 1 1 100.00 Test Item `; // Convert to ISO-8859-1 buffer const iso88591Buffer = Buffer.from(xmlContent, 'latin1'); try { // Try to load ISO-8859-1 content const invoice = await EInvoice.fromXml(iso88591Buffer.toString('latin1')); return { success: true, parsed: invoice.id === 'ISO88591-TEST' }; } catch (error) { // ISO-8859-1 might not be supported, which is acceptable return { success: false, error: error.message }; } }; const directResult = await testIso88591Direct(); console.log('Test 1 - Direct ISO-8859-1 encoding:'); console.log(` ${directResult.success ? 'Parsed successfully' : 'Not supported: ' + directResult.error}`); // Test 2: UTF-8 fallback for Latin-1 characters const testUtf8Fallback = async () => { const einvoice = new EInvoice(); einvoice.id = 'ISO88591-UTF8-TEST'; einvoice.date = Date.now(); einvoice.currency = 'EUR'; einvoice.subject = 'ISO-8859-1 characters: àéïöü'; einvoice.notes = ['French: crème brûlée', 'German: Müller & Söhne']; einvoice.from = { type: 'company', name: 'Société Française S.A.', description: 'French company with accented characters', address: { streetName: 'Rue de la Paix', houseNumber: '123', postalCode: '75001', city: 'Paris', country: 'FR' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'FR12345678901', registrationId: 'RCS Paris 123456789', registrationName: 'Registre du Commerce et des Sociétés' } }; einvoice.to = { type: 'company', name: 'Müller & Söhne GmbH', description: 'German company with umlauts', address: { streetName: 'Königstraße', houseNumber: '45', postalCode: '80331', city: 'München', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE987654321', registrationId: 'HRB 98765', registrationName: 'Handelsregister München' } }; einvoice.items = [{ position: 1, name: 'Spécialité française: crème brûlée', unitType: 'C62', unitQuantity: 10, unitNetPrice: 5.50, vatPercentage: 19 }]; // Export as UTF-8 (our default) const utf8Xml = await einvoice.toXmlString('ubl'); // Verify UTF-8 works correctly with Latin-1 characters const newInvoice = await EInvoice.fromXml(utf8Xml); const success = newInvoice.id === 'ISO88591-UTF8-TEST'; const charactersPreserved = utf8Xml.includes('Société Française') && utf8Xml.includes('Müller & Söhne') && utf8Xml.includes('crème brûlée') && utf8Xml.includes('München') && utf8Xml.includes('Königstraße'); return { success, charactersPreserved }; }; const fallbackResult = await testUtf8Fallback(); console.log('\nTest 2 - UTF-8 fallback for Latin-1 characters:'); console.log(` Invoice parsed: ${fallbackResult.success ? 'Yes' : 'No'}`); console.log(` Latin-1 chars preserved: ${fallbackResult.charactersPreserved ? 'Yes' : 'No'}`); // Test 3: Extended Latin-1 character range const testExtendedRange = async () => { const einvoice = new EInvoice(); // Test high Latin-1 characters (0x80-0xFF) const highChars = '¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ'; einvoice.id = 'ISO88591-RANGE-TEST'; einvoice.date = Date.now(); einvoice.currency = 'EUR'; einvoice.subject = `Latin-1 range test: ${highChars}`; einvoice.notes = [`Testing characters: ${highChars}`]; einvoice.from = { type: 'company', name: 'Test Company', description: 'Testing ISO-8859-1 character range', address: { streetName: 'Test Street', houseNumber: '1', postalCode: '12345', city: 'Test City', country: 'DE' }, status: 'active', foundedDate: { year: 2020, month: 1, day: 1 }, registrationDetails: { vatId: 'DE123456789', registrationId: 'HRB 12345', registrationName: 'Commercial Register' } }; einvoice.to = { type: 'person', name: 'Test', surname: 'Customer', salutation: 'Mr' as const, sex: 'male' as const, title: 'Doctor' as const, description: 'Test customer', address: { streetName: 'Customer Street', houseNumber: '2', postalCode: '54321', city: 'Customer City', country: 'DE' } }; einvoice.items = [{ position: 1, name: `Product with symbols: ${highChars.substring(0, 10)}`, unitType: 'C62', unitQuantity: 1, unitNetPrice: 100, vatPercentage: 19 }]; const xmlString = await einvoice.toXmlString('ubl'); // Check if characters are preserved (either directly or as entities) const preserved = highChars.split('').filter(char => { const charCode = char.charCodeAt(0); return xmlString.includes(char) || xmlString.includes(`&#${charCode};`) || xmlString.includes(`&#x${charCode.toString(16).toUpperCase()};`); }).length; const percentage = (preserved / highChars.length) * 100; return { preserved, total: highChars.length, percentage, success: percentage > 50 // At least 50% should be preserved }; }; const rangeResult = await testExtendedRange(); console.log('\nTest 3 - Extended Latin-1 character range (0x80-0xFF):'); console.log(` Characters preserved: ${rangeResult.preserved}/${rangeResult.total} (${rangeResult.percentage.toFixed(1)}%)`); // Test 4: Mixed encoding scenario const testMixedEncoding = async () => { // Test with a document that mixes ASCII and Latin-1 const mixedXml = ` MIXED-TEST 2025-01-25 Mixed ASCII and Latin-1: café, naïve, résumé 380 EUR ASCII Company Main Street New York 10001 US Café Société Avenue des Champs-Élysées Paris 75008 FR 1 1 100.00 Café au lait `; try { const invoice = await EInvoice.fromXml(mixedXml); return { success: true, parsed: invoice.id === 'MIXED-TEST' }; } catch (error) { return { success: false, error: error.message }; } }; const mixedResult = await testMixedEncoding(); console.log('\nTest 4 - Mixed ASCII/Latin-1 encoding:'); console.log(` ${mixedResult.success ? 'Parsed successfully' : 'Not supported: ' + mixedResult.error}`); // Summary console.log('\n=== ISO-8859-1 Encoding Test Summary ==='); console.log(`ISO-8859-1 Direct: ${directResult.success ? 'Supported' : 'Not supported (acceptable)'}`); console.log(`UTF-8 Fallback: ${fallbackResult.success ? 'Working' : 'Failed'}`); console.log(`Character Range: ${rangeResult.success ? 'Good coverage' : 'Limited coverage'}`); console.log(`Mixed Encoding: ${mixedResult.success ? 'Supported' : 'Not supported (acceptable)'}`); // The test passes if UTF-8 fallback works, since ISO-8859-1 support is optional expect(fallbackResult.success).toEqual(true); expect(fallbackResult.charactersPreserved).toEqual(true); console.log('\n✓ ISO-8859-1 encoding test completed'); }); tap.start();