einvoice/test/suite/einvoice_encoding/test.enc-08.mixed-content.ts

260 lines
9.8 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import { EInvoice } from '../../../ts/index.js';
tap.test('ENC-08: Mixed Content - should handle mixed text and element content', async () => {
console.log('Testing XML mixed content handling...\n');
// Test 1: Pure element content (structured only)
const testPureElementContent = async () => {
const einvoice = new EInvoice();
einvoice.id = 'MIXED-ELEMENT-TEST';
einvoice.issueDate = new Date(2025, 0, 25);
einvoice.subject = 'Pure element content test';
einvoice.from = {
type: 'company',
name: 'Test Company',
description: 'Testing pure element content',
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: 'Test Product',
articleNumber: 'MIXED-001',
unitType: 'EA',
unitQuantity: 1,
unitNetPrice: 100,
vatPercentage: 19
}];
// Generate XML and verify structure
const xmlString = await einvoice.toXmlString('ubl');
// Check for proper element structure without mixed content
const hasProperStructure = xmlString.includes('<cbc:ID>MIXED-ELEMENT-TEST</cbc:ID>') &&
xmlString.includes('<cac:AccountingSupplierParty>') &&
xmlString.includes('<cac:Party>');
// Verify round-trip works
const newInvoice = new EInvoice();
await newInvoice.fromXmlString(xmlString);
const roundTripSuccess = (newInvoice.id === 'MIXED-ELEMENT-TEST' ||
newInvoice.invoiceId === 'MIXED-ELEMENT-TEST' ||
newInvoice.accountingDocId === 'MIXED-ELEMENT-TEST');
console.log(`Test 1 - Pure element content:`);
console.log(` Proper XML structure: ${hasProperStructure ? 'Yes' : 'No'}`);
console.log(` Round-trip successful: ${roundTripSuccess ? 'Yes' : 'No'}`);
return { hasProperStructure, roundTripSuccess };
};
// Test 2: Mixed content with text and elements
const testMixedContent = async () => {
// XML with mixed content (text + elements combined)
const mixedContentXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:ID>MIXED-CONTENT-TEST</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name>Company Name with Text
<Element>nested element</Element> and more text
</cbc:Name>
</cac:PartyName>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:Note>This is a note with <strong>emphasis</strong> and additional text</cbc:Note>
<cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
<cac:Item>
<cbc:Name>Test Item</cbc:Name>
<cbc:Description>Item description with
<detail>detailed info</detail>
and more descriptive text
</cbc:Description>
</cac:Item>
</cac:InvoiceLine>
</Invoice>`;
try {
const einvoice = new EInvoice();
await einvoice.fromXmlString(mixedContentXml);
// Check if mixed content is handled appropriately
const mixedContentHandled = einvoice.from?.name !== undefined &&
einvoice.items?.[0]?.name !== undefined;
console.log(`\nTest 2 - Mixed content parsing:`);
console.log(` Mixed content XML parsed: ${mixedContentHandled ? 'Yes' : 'No'}`);
console.log(` Supplier name extracted: ${einvoice.from?.name ? 'Yes' : 'No'}`);
console.log(` Item data extracted: ${einvoice.items?.[0]?.name ? 'Yes' : 'No'}`);
return { mixedContentHandled };
} catch (error) {
console.log(`\nTest 2 - Mixed content parsing:`);
console.log(` Mixed content parsing failed: ${error.message}`);
return { mixedContentHandled: false };
}
};
// Test 3: CDATA sections with mixed content
const testCDataMixedContent = async () => {
const cdataMixedXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:ID>CDATA-MIXED-TEST</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name><![CDATA[Company & Co. with <special> chars]]></cbc:Name>
</cac:PartyName>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:Note><![CDATA[HTML content: <b>bold</b> and <i>italic</i> text]]></cbc:Note>
<cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
<cac:Item>
<cbc:Name>CDATA Test Item</cbc:Name>
<cbc:Description><![CDATA[
Multi-line description
with <XML> markup preserved
and "special" characters & symbols
]]></cbc:Description>
</cac:Item>
</cac:InvoiceLine>
</Invoice>`;
try {
const einvoice = new EInvoice();
await einvoice.fromXmlString(cdataMixedXml);
const cdataHandled = einvoice.from?.name?.includes('&') &&
einvoice.from?.name?.includes('<') &&
einvoice.items?.[0]?.name === 'CDATA Test Item';
console.log(`\nTest 3 - CDATA mixed content:`);
console.log(` CDATA content preserved: ${cdataHandled ? 'Yes' : 'No'}`);
console.log(` Special characters handled: ${einvoice.from?.name?.includes('&') ? 'Yes' : 'No'}`);
return { cdataHandled };
} catch (error) {
console.log(`\nTest 3 - CDATA mixed content:`);
console.log(` CDATA parsing failed: ${error.message}`);
return { cdataHandled: false };
}
};
// Test 4: Whitespace handling in mixed content
const testWhitespaceHandling = async () => {
const whitespaceXml = `<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:ID>WHITESPACE-TEST</cbc:ID>
<cbc:IssueDate>2025-01-25</cbc:IssueDate>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name> Company Name </cbc:Name>
</cac:PartyName>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="EA">1</cbc:InvoicedQuantity>
<cac:Item>
<cbc:Name>
Test Item
</cbc:Name>
</cac:Item>
</cac:InvoiceLine>
</Invoice>`;
try {
const einvoice = new EInvoice();
await einvoice.fromXmlString(whitespaceXml);
// Check how whitespace is handled
const whitespacePreserved = einvoice.from?.name === ' Company Name ';
const whitespaceNormalized = einvoice.from?.name?.trim() === 'Company Name';
console.log(`\nTest 4 - Whitespace handling:`);
console.log(` Whitespace preserved: ${whitespacePreserved ? 'Yes' : 'No'}`);
console.log(` Whitespace normalized: ${whitespaceNormalized ? 'Yes' : 'No'}`);
console.log(` Company name value: "${einvoice.from?.name}"`);
return { whitespacePreserved, whitespaceNormalized };
} catch (error) {
console.log(`\nTest 4 - Whitespace handling:`);
console.log(` Whitespace test failed: ${error.message}`);
return { whitespacePreserved: false, whitespaceNormalized: false };
}
};
// Run all tests
const elementResult = await testPureElementContent();
const mixedResult = await testMixedContent();
const cdataResult = await testCDataMixedContent();
const whitespaceResult = await testWhitespaceHandling();
console.log(`\n=== XML Mixed Content Test Summary ===`);
console.log(`Pure element content: ${elementResult.hasProperStructure ? 'Working' : 'Issues'}`);
console.log(`Mixed content parsing: ${mixedResult.mixedContentHandled ? 'Working' : 'Issues'}`);
console.log(`CDATA mixed content: ${cdataResult.cdataHandled ? 'Working' : 'Issues'}`);
console.log(`Whitespace handling: ${whitespaceResult.whitespaceNormalized ? 'Working' : 'Issues'}`);
console.log(`Round-trip consistency: ${elementResult.roundTripSuccess ? 'Working' : 'Issues'}`);
// Test passes if basic element content and mixed content parsing work
expect(elementResult.hasProperStructure).toBeTrue();
expect(elementResult.roundTripSuccess).toBeTrue();
});
// Run the test
tap.start();