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(`${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();