import test from 'node:test'; import assert from 'node:assert/strict'; import JSZip from 'jszip'; import { mkdir, writeFile } from 'node:fs/promises'; import { createProjectDossierReport, createProjectDossierPrintHtml, generateProjectDossierDocx, generateProjectDossierPackage, } from '../../resources/js/lib/project-dossier-report.ts'; import { inheritCompatibleFields } from '../../resources/js/lib/context-inheritance.ts'; void test('selected second opportunity survives actual Word and ZIP generation and the native print document', async () => { const snapshot = JSON.stringify({ version: 1, context_revision: 12, consumer_opportunity: 'opportunity:309', zone: { title: 'Parque Manica', version: 3, href: '/parques/manica' }, opportunity_choices: [ { opportunity: { key: 'opportunity:309', title: 'Segunda oportunidade Manica', }, }, ], land_preparation: { need: 'yes' }, path: { steps: [ { code: 'S10', title: 'Terra Manica', applicability: 'applicable', state: 'preparing', }, { code: 'S13', title: 'Financiamento', applicability: 'not_needed_for_preparation', reason: 'Sem financiamento externo neste ambito', }, ], }, }); const report = createProjectDossierReport( { project_title: 'QA segunda oportunidade', context_applicability_snapshot: snapshot, }, { selectedSections: new Set(['sources']) }, ); const html = await createProjectDossierPrintHtml(report, [], false); assert.match(html, /Terra Manica/); assert.match(html, /Sem financiamento externo neste ambito/); assert.match(html, /opportunity:309/); assert.match(html, /Segunda oportunidade Manica/); assert.match(html, /Parque Manica/); assert.doesNotMatch(html, /Sofala/); const docx = await generateProjectDossierDocx(report, [], true); const bytes = await docx.arrayBuffer(); const word = await JSZip.loadAsync(bytes); const xml = await word.file('word/document.xml').async('string'); assert.match(xml, /opportunity:309/); assert.match(xml, /Parque Manica/); assert.doesNotMatch(xml, /Sofala/); const bundle = await generateProjectDossierPackage(docx, [], true); const zip = await JSZip.loadAsync(await bundle.arrayBuffer()); const inner = Object.keys(zip.files).find((name) => name.endsWith('.docx')); assert.ok(inner); assert.deepEqual( await zip.file(inner).async('uint8array'), new Uint8Array(bytes), ); await mkdir('outputs/multi-opportunity-fix-20261023/treatment-exports', { recursive: true, }); await writeFile( 'outputs/multi-opportunity-fix-20261023/treatment-exports/second.docx', new Uint8Array(bytes), ); await writeFile( 'outputs/multi-opportunity-fix-20261023/treatment-exports/second.zip', new Uint8Array(await bundle.arrayBuffer()), ); await writeFile( 'outputs/multi-opportunity-fix-20261023/treatment-exports/second-print.html', html, ); }); void test('new common contact reuse preserves a deliberate contact and follows inherited contact only', () => { const source = { email: 'qa@example.invalid', phone: '+258840001234' }; const a = inheritCompatibleFields( 'dossier:qa:land', { email: 'edited@example.invalid' }, source, '', ); assert.equal(a.patch.phone, source.phone); assert.equal(a.patch.email, undefined); const b = inheritCompatibleFields( 'dossier:qa:land', { email: 'edited@example.invalid', phone: source.phone }, { ...source, phone: '+258840005678' }, a.metadata, ); assert.equal(b.patch.phone, '+258840005678'); assert.equal(b.patch.email, undefined); });