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-08: Mixed Content Encoding - should handle mixed content (text and elements) correctly', async (t) => { // ENC-08: Verify proper encoding of mixed content scenarios // This test ensures text nodes, elements, CDATA, and comments are properly encoded together const performanceTracker = new PerformanceTracker('ENC-08: Mixed Content'); const corpusLoader = new CorpusLoader(); t.test('Basic mixed content', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-BASIC-001 This invoice includes important payment terms: Net 30 days with 2% early payment discount. Please pay by 2025-02-25. Payment due in 30 days. If paid within 10 days: 2% discount If paid after 30 days: 1.5% interest Item includes 10 units of Widget A at €9.99 each. Total: €99.90 `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Verify mixed content is preserved expect(xmlString).toContain('This invoice includes'); expect(xmlString).toContain('important'); expect(xmlString).toContain('payment terms:'); expect(xmlString).toContain('Net 30 days'); expect(xmlString).toContain('with'); expect(xmlString).toContain('2%'); expect(xmlString).toContain('Please pay by'); expect(xmlString).toContain('2025-02-25'); // Verify nested mixed content expect(xmlString).toContain('If paid within'); expect(xmlString).toContain('10'); expect(xmlString).toContain('days:'); expect(xmlString).toContain('2%'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('basic-mixed', elapsed); }); t.test('Mixed content with special characters', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-SPECIAL-001 Price: 100.00 € (VAT 19% = 19.00 €) Total: 119.00 € for Müller & Söhne GmbH See contract §12.3 for terms & conditions. Payment < 30 days required. Contact: info@müller-söhne.de ≥ 100 items → 5% discount > 30 days → 1.5% interest Total = Price × Quantity × (1 + VAT%) `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Verify special characters in mixed content expect(xmlString).toContain('Price:'); expect(xmlString).toContain('€'); expect(xmlString).toContain('Müller & Söhne GmbH'); expect(xmlString).toContain('§12.3'); expect(xmlString).toContain('terms & conditions'); expect(xmlString).toContain('< 30 days'); expect(xmlString).toContain('info@müller-söhne.de'); expect(xmlString).toContain('≥ 100 items → 5% discount'); expect(xmlString).toContain('> 30 days → 1.5% interest'); expect(xmlString).toContain('×'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('special-mixed', elapsed); }); t.test('Mixed content with CDATA sections', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-CDATA-001 Regular text before CDATA. tags & special chars: < > & " ']]> Text after CDATA with nested element. HTML content example:

Invoice Details

Amount: €100.00

VAT: 19%

]]> End of description.
Formula: price * quantity 100) { discount = 5%; }]]> Applied to all items.
`; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Verify mixed content with CDATA is handled expect(xmlString).toContain('Regular text before CDATA'); expect(xmlString).toContain('Text after CDATA'); expect(xmlString).toContain('nested element'); // CDATA content should be preserved somehow if (xmlString.includes('CDATA')) { expect(xmlString).toContain(''); } else { // Or converted to escaped text expect(xmlString).toMatch(/<unescaped>|/); } const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('cdata-mixed', elapsed); }); t.test('Mixed content with comments', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-COMMENTS-001 Payment is due in 30 days. Early payment: 2% if paid within 10 days See attachment for details. invoice.pdf Contact : info@example.com Product: Widget Quantity: 10 Price: 9.99 `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Verify text content is preserved (comments may or may not be preserved) expect(xmlString).toContain('Payment is due in'); expect(xmlString).toContain('30'); expect(xmlString).toContain('days.'); expect(xmlString).toContain('Early payment: 2% if paid within 10 days'); expect(xmlString).toContain('See attachment'); expect(xmlString).toContain('for details.'); expect(xmlString).toContain('invoice.pdf'); expect(xmlString).toContain('Contact'); expect(xmlString).toContain('info@example.com'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('comments-mixed', elapsed); }); t.test('Whitespace preservation in mixed content', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-WHITESPACE-001 Text with multiple spaces and newlines should be preserved. Indented element More text with tabs between words. Leading spaces Net 30 Trailing spaces Middle spaces preserved. End with spaces Line 1 Line 2 Line 3 `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Whitespace handling varies by implementation expect(xmlString).toContain('Text with'); expect(xmlString).toContain('spaces'); expect(xmlString).toContain('Indented element'); expect(xmlString).toContain('More text with'); expect(xmlString).toContain('words'); // xml:space="preserve" should maintain whitespace if (xmlString.includes('xml:space="preserve"')) { expect(xmlString).toMatch(/Leading spaces|^\s+Leading/m); } const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('whitespace-mixed', elapsed); }); t.test('Deeply nested mixed content', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-NESTED-001 Level 1: Invoice for ABC Corp (Customer ID: C-12345)
Located at 123 Main St, New York, NY 10001
dated 2025-01-25.
Standard terms: Net 30 days from invoice date (2025-01-25) Special conditions: For orders > €1000: 5% discount
`; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Verify deeply nested structure is preserved expect(xmlString).toContain('Level 1: Invoice for'); expect(xmlString).toContain(''); expect(xmlString).toContain('ABC Corp'); expect(xmlString).toContain('(Customer ID:'); expect(xmlString).toContain('C-12345'); expect(xmlString).toContain('Located at'); expect(xmlString).toContain('123 Main St'); expect(xmlString).toContain('New York'); expect(xmlString).toContain('NY'); expect(xmlString).toContain('10001'); expect(xmlString).toContain('dated'); expect(xmlString).toContain('2025-01-25'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('nested-mixed', elapsed); }); t.test('International mixed content', async () => { const startTime = performance.now(); const xmlContent = ` 2.1 MIXED-INTL-001 Invoice for Müller GmbH from München. Total: €1.234,56 (inkl. 19% MwSt). 支付条款:30天内付款。 お支払い: 30日以内 Payment due in 30 days Zahlung fällig in 30 Tagen Paiement dû dans 30 jours Pago debido en 30 días Product: Book / Buch / Livre / / / كتاب Price: €25.00 per Stück `; const einvoice = new EInvoice(); await einvoice.loadFromString(xmlContent); const xmlString = einvoice.getXmlString(); // Verify international mixed content expect(xmlString).toContain('Müller GmbH'); expect(xmlString).toContain('München'); expect(xmlString).toContain('€1.234,56'); expect(xmlString).toContain('19% MwSt'); expect(xmlString).toContain('支付条款:'); expect(xmlString).toContain('30天内付款'); expect(xmlString).toContain('お支払い:'); expect(xmlString).toContain('30日以内'); expect(xmlString).toContain('Zahlung fällig in'); expect(xmlString).toContain('Tagen'); expect(xmlString).toContain('Paiement dû dans'); expect(xmlString).toContain('书'); expect(xmlString).toContain('本'); expect(xmlString).toContain('كتاب'); expect(xmlString).toContain('Stück'); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('intl-mixed', elapsed); }); t.test('Corpus mixed content analysis', async () => { const startTime = performance.now(); let processedCount = 0; let mixedContentCount = 0; const mixedContentExamples: string[] = []; const files = await corpusLoader.getAllFiles(); const xmlFiles = files.filter(f => f.endsWith('.xml')); // Sample corpus for mixed content patterns 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); let xmlString: string; if (Buffer.isBuffer(content)) { xmlString = content.toString('utf8'); } else { xmlString = content; } // Look for mixed content patterns // Pattern: text followed by element followed by text within same parent const mixedPattern = />([^<]+)<[^>]+>[^<]+<\/[^>]+>([^<]+) ex.includes('CDATA'))) { mixedContentExamples.push(`${file}: Contains CDATA sections`); } } processedCount++; } catch (error) { console.log(`Mixed content parsing issue in ${file}:`, error.message); } } console.log(`Mixed content corpus analysis (${processedCount} files):`); console.log(`- Files with mixed content patterns: ${mixedContentCount}`); if (mixedContentExamples.length > 0) { console.log('Mixed content examples:'); mixedContentExamples.forEach(ex => console.log(` ${ex}`)); } expect(processedCount).toBeGreaterThan(0); const elapsed = performance.now() - startTime; performanceTracker.addMeasurement('corpus-mixed', elapsed); }); // Print performance summary performanceTracker.printSummary(); // Performance assertions const avgTime = performanceTracker.getAverageTime(); expect(avgTime).toBeLessThan(150); // Mixed content operations may be slightly slower }); tap.start();