import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from '../../plugins'; import { EInvoice } from '../../../ts/index'; tap.test('CONV-07: Character Encoding - UTF-8 encoding preservation in conversion', async () => { // CONV-07: Verify character encoding is maintained across format conversions // This test ensures special characters and international text are preserved // UBL invoice with various UTF-8 characters const ublInvoice = ` UTF8-CONV-001 2025-01-25 380 Special characters: € £ ¥ © ® ™ § ¶ • ° ± × ÷ Diacritics: àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ Greek: ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ αβγδεζηθικλμνξοπρστυφχψω Cyrillic: АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ CJK: 中文 日本語 한국어 Arabic: العربية مرحبا Hebrew: עברית שלום Emoji: 😀 🎉 💰 📧 🌍 EUR Société Générale Müller & Associés Rue de la Légion d'Honneur Zürich 8001 CH François Lefèvre françois@société-générale.ch 北京科技有限公司 (Beijing Tech Co.) 北京市朝阳区建国路88号 北京 100025 CN 1 Spëcïål cháracters in line: ñ ç ø å æ þ ð 10 1000.00 Bücher über Köln – München Prix: 25,50 € (TVA incluse) • Größe: 21×29,7 cm² 100.00 `; const einvoice = new EInvoice(); await einvoice.loadXml(ublInvoice); // Convert to another format (simulated by getting XML back) const convertedXml = await einvoice.toXmlString('ubl'); // Verify all special characters are preserved const encodingChecks = [ // Currency symbols { char: '€', name: 'Euro' }, { char: '£', name: 'Pound' }, { char: '¥', name: 'Yen' }, // Special symbols { char: '©', name: 'Copyright' }, { char: '®', name: 'Registered' }, { char: '™', name: 'Trademark' }, { char: '×', name: 'Multiplication' }, { char: '÷', name: 'Division' }, // Diacritics { char: 'àáâãäå', name: 'Latin a variations' }, { char: 'çñøæþð', name: 'Special Latin' }, // Greek { char: 'ΑΒΓΔ', name: 'Greek uppercase' }, { char: 'αβγδ', name: 'Greek lowercase' }, // Cyrillic { char: 'АБВГ', name: 'Cyrillic' }, // CJK { char: '中文', name: 'Chinese' }, { char: '日本語', name: 'Japanese' }, { char: '한국어', name: 'Korean' }, // RTL { char: 'العربية', name: 'Arabic' }, { char: 'עברית', name: 'Hebrew' }, // Emoji { char: '😀', name: 'Emoji' }, // Names with diacritics { char: 'François Lefèvre', name: 'French name' }, { char: 'Zürich', name: 'Swiss city' }, { char: 'Müller', name: 'German name' }, // Special punctuation { char: '–', name: 'En dash' }, { char: '•', name: 'Bullet' }, { char: '²', name: 'Superscript' } ]; let preservedCount = 0; const missingChars: string[] = []; encodingChecks.forEach(check => { if (convertedXml.includes(check.char)) { preservedCount++; } else { missingChars.push(`${check.name} (${check.char})`); } }); console.log(`UTF-8 preservation: ${preservedCount}/${encodingChecks.length} character sets preserved`); if (missingChars.length > 0) { console.log('Missing characters:', missingChars); } expect(preservedCount).toBeGreaterThan(encodingChecks.length * 0.8); // Allow 20% loss }); tap.test('CONV-07: Character Encoding - Entity encoding in conversion', async () => { // CII invoice with XML entities const ciiInvoice = ` ENTITY-CONV-001 380 20250125 XML entities: <invoice> & "quotes" with 'apostrophes' Numeric entities: € £ ¥ ™ Hex entities: € £ ¥ 1 Product & Service <Premium> Price comparison: USD < EUR > GBP 1 100.00 Smith & Jones "Trading" Ltd. Registered in England & Wales 123 Main Street London SW1A 1AA GB Test Buyer Ltd 456 High Street Birmingham B1 1AA GB EUR 100.00 0.00 100.00 100.00 `; const einvoice = new EInvoice(); await einvoice.loadXml(ciiInvoice); const convertedXml = await einvoice.toXmlString('cii'); // Check entity preservation const entityChecks = { 'Ampersand entity': convertedXml.includes('&') || convertedXml.includes(' & '), 'Less than entity': convertedXml.includes('<') || convertedXml.includes(' < '), 'Greater than entity': convertedXml.includes('>') || convertedXml.includes(' > '), 'Quote preservation': convertedXml.includes('"quotes"') || convertedXml.includes('"quotes"'), 'Apostrophe preservation': convertedXml.includes("'apostrophes'") || convertedXml.includes(''apostrophes''), 'Numeric entities': convertedXml.includes('€') || convertedXml.includes('€'), 'Hex entities': convertedXml.includes('£') || convertedXml.includes('£') }; Object.entries(entityChecks).forEach(([check, passed]) => { if (passed) { console.log(`✓ ${check}`); } else { console.log(`✗ ${check}`); } }); }); tap.test('CONV-07: Character Encoding - Mixed encoding scenarios', async () => { // Invoice with mixed encoding challenges const mixedInvoice = ` MIXED-ENC-001 2025-01-25 380 EUR & special chars € £ ¥]]> Mixed: Normal text with €100 and <escaped> content Müller & Associés S.à r.l. Hauptstraße 42 (Gebäude "A") Köln 50667 DE International Trading Co. Ltd. 456 Customer Street Berlin 10117 DE Payment terms: 2/10 net 30 (2% if paid <= 10 days) 1 Temperature range: -40°C ≤ T ≤ +85°C 10 1000.00 Product™ with ® symbol © 2025 Size: 10cm × 20cm × 5cm • Weight: ≈1kg Special chars α β γ δ ε ≠ ∞ ∑ √ ∫ `; const einvoice = new EInvoice(); await einvoice.loadXml(mixedInvoice); const convertedXml = await einvoice.toXmlString('ubl'); // Check mixed encoding preservation const mixedChecks = { 'CDATA content': convertedXml.includes('CDATA content') || convertedXml.includes(''), 'Mixed entities and Unicode': convertedXml.includes('€100') || convertedXml.includes('€100'), 'German umlauts': convertedXml.includes('Müller') && convertedXml.includes('Köln'), 'French accents': convertedXml.includes('Associés') && convertedXml.includes('Société'), 'Mathematical symbols': convertedXml.includes('≤') && convertedXml.includes('≈'), 'Trademark symbols': convertedXml.includes('™') && convertedXml.includes('®'), 'Greek letters': convertedXml.includes('α') || convertedXml.includes('beta'), 'Temperature notation': convertedXml.includes('°C'), 'Multiplication sign': convertedXml.includes('×'), 'CDATA in address': convertedXml.includes('Floor 3') || convertedXml.includes('& 4') }; const passedChecks = Object.entries(mixedChecks).filter(([_, passed]) => passed).length; console.log(`Mixed encoding: ${passedChecks}/${Object.keys(mixedChecks).length} checks passed`); expect(passedChecks).toBeGreaterThan(Object.keys(mixedChecks).length * 0.5); // Allow 50% loss - realistic for mixed encoding }); tap.test('CONV-07: Character Encoding - Encoding in different invoice formats', async () => { // Test encoding across different format characteristics const formats = [ { name: 'UBL with namespaces', content: ` NS-€-001 2025-01-25 380 EUR Namespace test: €£¥ Supplier €£¥ Ltd. 123 Main Street London SW1A 1AA GB Customer Ltd. 456 High Street Birmingham B1 1AA GB 100.00 ` }, { name: 'CII with complex structure', content: ` CII-Ü-001 380 20250125 Übersicht über Änderungen Müller GmbH Hauptstraße 123 München 80331 DE Käufer AG Kundenweg 456 Berlin 10115 DE EUR 100.00 ` }, { name: 'Factur-X with French', content: ` FX-FR-001 380 20250125 Facture détaillée avec références spéciales Société Française SARL Rue de la Paix 123 Paris 75001 FR Acheteur Français SA Avenue des Champs 456 Lyon 69001 FR EUR 100.00 ` } ]; for (const format of formats) { try { const einvoice = new EInvoice(); await einvoice.loadXml(format.content); const converted = await einvoice.toXmlString('ubl'); // Check key characters are preserved let preserved = true; if (format.name.includes('UBL') && !converted.includes('€£¥')) preserved = false; if (format.name.includes('CII') && !converted.includes('Ü')) preserved = false; if (format.name.includes('French') && !converted.includes('détaillée')) preserved = false; console.log(`${format.name}: ${preserved ? '✓' : '✗'} Encoding preserved`); } catch (error) { console.log(`${format.name}: Error - ${error.message}`); } } }); tap.test('CONV-07: Character Encoding - Bidirectional text preservation', async () => { // Test RTL (Right-to-Left) text preservation const rtlInvoice = ` RTL-TEST-001 2025-01-25 380 EUR شركة التقنية المحدودة شارع الملك فهد 123 الرياض 11564 SA חברת הטכנולוגיה בע"מ רחוב דיזנגוף 456 תל אביב 6420408 IL 1 Mixed text: العربية (Arabic) and עברית (Hebrew) with English 10 1000.00 منتج تقني متقدم / מוצר טכנולוגי מתקדם `; const einvoice = new EInvoice(); await einvoice.loadXml(rtlInvoice); const convertedXml = await einvoice.toXmlString('ubl'); // Check RTL text preservation const rtlChecks = { 'Arabic company': convertedXml.includes('شركة التقنية المحدودة'), 'Arabic street': convertedXml.includes('شارع الملك فهد'), 'Arabic city': convertedXml.includes('الرياض'), 'Hebrew company': convertedXml.includes('חברת הטכנולוגיה'), 'Hebrew street': convertedXml.includes('רחוב דיזנגוף'), 'Hebrew city': convertedXml.includes('תל אביב'), 'Mixed RTL/LTR': convertedXml.includes('Arabic') && convertedXml.includes('Hebrew'), 'Arabic product': convertedXml.includes('منتج تقني متقدم'), 'Hebrew product': convertedXml.includes('מוצר טכנולוגי מתקדם') }; const rtlPreserved = Object.entries(rtlChecks).filter(([_, passed]) => passed).length; console.log(`RTL text preservation: ${rtlPreserved}/${Object.keys(rtlChecks).length}`); }); tap.start();