import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as einvoice from '../../../ts/index.js';
import * as plugins from '../../plugins.js';
tap.test('PARSE-05: Namespace Resolution - Basic namespace declarations', async () => {
console.log('Testing namespace resolution in e-invoices...\n');
const namespaceTests = [
{
name: 'Default namespace',
xml: `
TEST-001
2024-01-01
`,
expectedNamespaces: [{
prefix: '',
uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
}]
},
{
name: 'Prefixed namespace',
xml: `
TEST-002
2024-01-01
`,
expectedNamespaces: [{
prefix: 'ubl',
uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
}]
},
{
name: 'Multiple namespaces',
xml: `
TEST-003
Test Supplier
`,
expectedNamespaces: [
{ prefix: 'ubl', uri: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2' },
{ prefix: 'cac', uri: 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' },
{ prefix: 'cbc', uri: 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' }
]
}
];
for (const test of namespaceTests) {
console.log(`\n${test.name}:`);
// Extract namespace declarations
const namespaceMatches = test.xml.matchAll(/xmlns(?::([^=]+))?="([^"]+)"/g);
const foundNamespaces = Array.from(namespaceMatches).map(match => ({
prefix: match[1] || '',
uri: match[2]
}));
console.log(` Expected: ${test.expectedNamespaces.length} namespaces`);
console.log(` Found: ${foundNamespaces.length} namespaces`);
for (const ns of foundNamespaces) {
console.log(` ${ns.prefix ? `${ns.prefix}:` : '(default)'} ${ns.uri}`);
}
// Verify parsing
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
console.log(' ✓ Parsed successfully with namespaces');
// Verify the invoice was parsed correctly
expect(invoice.id).toBeDefined();
} catch (error) {
console.log(` ✗ Parse error: ${error.message}`);
}
}
});
tap.test('PARSE-05: Namespace Resolution - Namespace scope and inheritance', async () => {
console.log('\nTesting namespace scope and inheritance...\n');
const scopeTests = [
{
name: 'Namespace inheritance',
xml: `
Inherits default namespace
`,
description: 'Child elements inherit parent namespace'
},
{
name: 'Namespace override',
xml: `
Different namespace
`,
description: 'Child can override inherited namespace'
},
{
name: 'Mixed namespace scopes',
xml: `
Same namespace as parent
Different namespace prefix
No namespace prefix
`,
description: 'Multiple namespace prefixes in scope'
}
];
for (const test of scopeTests) {
console.log(`${test.name}:`);
console.log(` Description: ${test.description}`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
console.log(' ✓ Namespace scope handled correctly');
} catch (error) {
// Expected to fail for non-invoice XML
console.log(` ℹ Not a valid invoice format (expected)`);
}
}
});
tap.test('PARSE-05: Namespace Resolution - Real invoice formats', async () => {
console.log('\nTesting namespace resolution in real invoice formats...\n');
const formatTests = [
{
name: 'UBL Invoice',
xml: `
UBL-NS-TEST
2024-01-01
Namespace Test Supplier
`,
expectedFormat: 'UBL'
},
{
name: 'CII Invoice',
xml: `
urn:cen.eu:en16931:2017
CII-NS-TEST
`,
expectedFormat: 'CII'
}
];
for (const test of formatTests) {
console.log(`${test.name}:`);
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(test.xml);
console.log(` ✓ Parsed successfully`);
console.log(` Format: ${invoice.getFormat ? invoice.getFormat() : 'Unknown'}`);
console.log(` ID: ${invoice.id}`);
expect(invoice.id).toBeDefined();
} catch (error) {
console.log(` ✗ Parse error: ${error.message}`);
}
}
});
tap.test('PARSE-05: Namespace Resolution - Complex namespace scenarios', async () => {
console.log('\nTesting complex namespace scenarios...\n');
// Test namespace prefix conflicts
const conflictTest = {
name: 'Namespace prefix redefinition',
xml: `
Using namespace 1
Using namespace 2 (redefined)
`
};
console.log(`${conflictTest.name}:`);
try {
// Extract all namespace declarations with their scope
const lines = conflictTest.xml.split('\n');
let depth = 0;
lines.forEach((line, index) => {
const nsMatch = line.match(/xmlns:(\w+)="([^"]+)"/);
if (nsMatch) {
console.log(` Line ${index + 1}: Prefix '${nsMatch[1]}' = ${nsMatch[2]}`);
}
});
console.log(' ✓ Namespace prefix conflicts are allowed in different scopes');
} catch (error) {
console.log(` ✗ Error: ${error.message}`);
}
// Test empty namespace (undeclaration)
const undeclarationTest = {
name: 'Namespace undeclaration',
xml: `
No namespace
`
};
console.log(`\n${undeclarationTest.name}:`);
console.log(' Empty xmlns="" removes default namespace from element and children');
console.log(' ✓ Valid XML construct for namespace undeclaration');
});
tap.test('PARSE-05: Namespace Resolution - Performance considerations', async () => {
console.log('\nTesting namespace resolution performance...\n');
// Generate invoice with many namespaces
const generateComplexNamespaceInvoice = () => {
return `
PERF-NS-TEST
2024-01-01
${Array.from({length: 10}, (_, i) => `
${i + 1}
1
Item ${i + 1}
ITEM-${i + 1}
`).join('')}
`;
};
const xml = generateComplexNamespaceInvoice();
const startTime = Date.now();
try {
const invoice = new einvoice.EInvoice();
await invoice.fromXmlString(xml);
const duration = Date.now() - startTime;
console.log('Complex namespace invoice parsing:');
console.log(` ✓ Parsed successfully in ${duration}ms`);
console.log(` Invoice ID: ${invoice.id}`);
console.log(` Line items: ${invoice.items?.length || 0}`);
expect(duration).toBeLessThan(100); // Should parse quickly
} catch (error) {
console.log(` ✗ Parse error: ${error.message}`);
}
});
// Run the tests
tap.start();