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'; 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); }); 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); });