import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from '../plugins.js'; import { EInvoice } from '../../../ts/index.js'; import { CorpusLoader } from '../corpus.loader.js'; import { PerformanceTracker } from '../performance.tracker.js'; tap.test('ENC-05: Special Characters - should handle special and international characters correctly', async (t) => { // ENC-05: Verify handling of special characters across different languages and scripts // This test ensures proper support for international invoicing const performanceTracker = new PerformanceTracker('ENC-05: Special Characters'); const corpusLoader = new CorpusLoader(); t.test('European special characters', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 EU-SPECIAL-CHARS 2025-01-25 European chars test Åsa Öberg AB (Sweden) Østergade 42 København DK Müller & Schäfer GmbH Hauptstraße 15 Düsseldorf DE François Lefèvre f.lefevre@müller-schäfer.de Château Margaux (Bordeaux) Vin rouge, millésime 2015, cépage: Cabernet Sauvignon Prošek (Croatian dessert wine) Vino desertno, područje: Dalmacija Żubrówka (Polish vodka) Wódka żytnia z trawą żubrową `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Nordic characters expect(xmlString).toContain('Åsa Öberg'); expect(xmlString).toContain('Østergade'); expect(xmlString).toContain('København'); // German characters expect(xmlString).toContain('Müller & Schäfer'); expect(xmlString).toContain('Hauptstraße'); expect(xmlString).toContain('Düsseldorf'); expect(xmlString).toContain('müller-schäfer.de'); // French characters expect(xmlString).toContain('François Lefèvre'); expect(xmlString).toContain('Château Margaux'); expect(xmlString).toContain('millésime'); expect(xmlString).toContain('cépage'); // Croatian characters expect(xmlString).toContain('Prošek'); expect(xmlString).toContain('područje'); // Polish characters expect(xmlString).toContain('Żubrówka'); expect(xmlString).toContain('żytnia'); expect(xmlString).toContain('żubrową'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('european-chars', elapsed); }); t.test('Currency and monetary symbols', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 CURRENCY-SYMBOLS Currency symbols: € £ $ ¥ ₹ ₽ ₪ ₩ ₡ ₦ ₨ ₱ ₴ ₵ ₸ ₹ ₺ ₼ €1,234.56 £987.65 $2,345.67 ¥123,456 ₹98,765 false Discount (5% off orders > €500) 25.50 Accepted: € EUR, £ GBP, $ USD, ¥ JPY, ₹ INR `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Major currency symbols expect(xmlString).toContain('€'); // Euro expect(xmlString).toContain('£'); // Pound expect(xmlString).toContain('$'); // Dollar expect(xmlString).toContain('¥'); // Yen expect(xmlString).toContain('₹'); // Rupee expect(xmlString).toContain('₽'); // Ruble expect(xmlString).toContain('₪'); // Shekel expect(xmlString).toContain('₩'); // Won // Verify monetary formatting expect(xmlString).toContain('€1,234.56'); expect(xmlString).toContain('£987.65'); expect(xmlString).toContain('$2,345.67'); expect(xmlString).toContain('¥123,456'); expect(xmlString).toContain('₹98,765'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('currency-symbols', elapsed); }); t.test('Mathematical and technical symbols', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MATH-SYMBOLS Math symbols: ± × ÷ ≤ ≥ ≠ ≈ ∞ √ ∑ ∏ ∫ ∂ ∇ ∈ ∉ ⊂ ⊃ ∪ ∩ 100.00 95.00 Discount ≥ 10 units Precision tool ± 0.001mm Temperature range -40°C ≤ T ≤ +85°C Dimensions 10cm × 5cm × 2cm √2 ≈ 1.414, π ≈ 3.14159, e ≈ 2.71828 Formula Area = πr² (where r = radius) `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Mathematical operators expect(xmlString).toContain('±'); // Plus-minus expect(xmlString).toContain('×'); // Multiplication expect(xmlString).toContain('÷'); // Division expect(xmlString).toContain('≤'); // Less than or equal expect(xmlString).toContain('≥'); // Greater than or equal expect(xmlString).toContain('≠'); // Not equal expect(xmlString).toContain('≈'); // Approximately expect(xmlString).toContain('∞'); // Infinity expect(xmlString).toContain('√'); // Square root expect(xmlString).toContain('π'); // Pi expect(xmlString).toContain('°'); // Degree const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('math-symbols', elapsed); }); t.test('Asian scripts and characters', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 ASIAN-SCRIPTS 株式会社山田商事 (Yamada Trading Co., Ltd.) 東京都千代田区丸の内1-1-1 東京 JP 北京科技有限公司 (Beijing Tech Co., Ltd.) 北京市朝阳区建国路88号 北京 CN 전자제품 (Electronics) 최신 스마트폰 모델 कंप्यूटर उपकरण नवीनतम लैपटॉप मॉडल ซอฟต์แวร์คอมพิวเตอร์ โปรแกรมสำนักงาน `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Japanese (Kanji, Hiragana, Katakana) expect(xmlString).toContain('株式会社山田商事'); expect(xmlString).toContain('東京都千代田区丸の内'); // Chinese (Simplified) expect(xmlString).toContain('北京科技有限公司'); expect(xmlString).toContain('北京市朝阳区建国路'); // Korean (Hangul) expect(xmlString).toContain('전자제품'); expect(xmlString).toContain('최신 스마트폰 모델'); // Hindi (Devanagari) expect(xmlString).toContain('कंप्यूटर उपकरण'); expect(xmlString).toContain('नवीनतम लैपटॉप मॉडल'); // Thai expect(xmlString).toContain('ซอฟต์แวร์คอมพิวเตอร์'); expect(xmlString).toContain('โปรแกรมสำนักงาน'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('asian-scripts', elapsed); }); t.test('Arabic and RTL scripts', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 RTL-SCRIPTS شركة التقنية المحدودة شارع الملك فهد الرياض SA חברת הטכנולוגיה בע"מ רחוב דיזנגוף 123 תל אביב IL الدفع: 30 يومًا صافي منتج إلكتروني جهاز كمبيوتر محمول מוצר אלקטרוני מחשב נייד מתקדם `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Arabic expect(xmlString).toContain('شركة التقنية المحدودة'); expect(xmlString).toContain('شارع الملك فهد'); expect(xmlString).toContain('الرياض'); expect(xmlString).toContain('الدفع: 30 يومًا صافي'); expect(xmlString).toContain('منتج إلكتروني'); // Hebrew expect(xmlString).toContain('חברת הטכנולוגיה בע"מ'); expect(xmlString).toContain('רחוב דיזנגוף'); expect(xmlString).toContain('תל אביב'); expect(xmlString).toContain('מוצר אלקטרוני'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('rtl-scripts', elapsed); }); t.test('Emoji and emoticons', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 EMOJI-TEST Thank you for your order! 😊 🎉 🚀 Payment methods: 💳 💰 🏦 Premium Package 🌟 Includes: 📱 💻 🖱️ ⌨️ 🎧 Express Shipping 🚚💨 Delivery: 📦 → 🏠 (1-2 days) Customer Support 24/7 ☎️ Contact: 📧 📞 💬 `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Common emojis expect(xmlString).toContain('😊'); // Smiling face expect(xmlString).toContain('🎉'); // Party expect(xmlString).toContain('🚀'); // Rocket expect(xmlString).toContain('💳'); // Credit card expect(xmlString).toContain('💰'); // Money bag expect(xmlString).toContain('🏦'); // Bank expect(xmlString).toContain('🌟'); // Star expect(xmlString).toContain('📱'); // Phone expect(xmlString).toContain('💻'); // Laptop expect(xmlString).toContain('🚚'); // Truck expect(xmlString).toContain('📦'); // Package expect(xmlString).toContain('🏠'); // House expect(xmlString).toContain('☎️'); // Phone expect(xmlString).toContain('📧'); // Email expect(xmlString).toContain('💬'); // Chat const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('emoji', elapsed); }); t.test('Corpus special character validation', async () => { const startTime = performance.now(); let processedCount = 0; let specialCharCount = 0; const specialCharFiles: string[] = []; const files = await corpusLoader.getAllFiles(); const xmlFiles = files.filter(f => f.endsWith('.xml')); // Check sample for special characters const sampleSize = Math.min(60, xmlFiles.length); const sample = xmlFiles.slice(0, sampleSize); for (const file of sample) { try { const content = await corpusLoader.readFile(file); const einvoice = new EInvoice(); if (typeof content === 'string') { await einvoice.loadFromString(content); } else { await einvoice.loadFromBuffer(content); } const xmlString = einvoice.getXmlString(); // Check for non-ASCII characters if (/[^\x00-\x7F]/.test(xmlString)) { specialCharCount++; // Check for specific character ranges if (/[À-ÿ]/.test(xmlString)) { specialCharFiles.push(`${file} (Latin Extended)`); } else if (/[Ā-ſ]/.test(xmlString)) { specialCharFiles.push(`${file} (Latin Extended-A)`); } else if (/[\u0400-\u04FF]/.test(xmlString)) { specialCharFiles.push(`${file} (Cyrillic)`); } else if (/[\u4E00-\u9FFF]/.test(xmlString)) { specialCharFiles.push(`${file} (CJK)`); } else if (/[\u0600-\u06FF]/.test(xmlString)) { specialCharFiles.push(`${file} (Arabic)`); } } processedCount++; } catch (error) { console.log(`Special char issue in ${file}:`, error.message); } } console.log(`Special character corpus test: ${specialCharCount}/${processedCount} files contain special characters`); if (specialCharFiles.length > 0) { console.log('Sample files with special characters:', specialCharFiles.slice(0, 5)); } expect(processedCount).toBeGreaterThan(0); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('corpus-special', elapsed); }); t.test('Zero-width and invisible characters', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 INVISIBLE-CHARS Zero​-width​space (U+200B) Non‌breaking‍zero‌width‍joiner Soft­hyphen­test Left‐to‐right‏mark and right‐to‐left‎mark `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // These characters might be preserved or stripped // Check that the text is still readable expect(xmlString).toMatch(/Zero.*width.*space/); expect(xmlString).toMatch(/Non.*breaking.*zero.*width.*joiner/); expect(xmlString).toMatch(/Soft.*hyphen.*test/); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('invisible-chars', elapsed); }); // Print performance summary performanceTracker.printSummary(); // Performance assertions const avgTime = performanceTracker.getAverageTime(); expect(avgTime).toBeLessThan(150); // Special character operations should be reasonably fast }); tap.start();