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();