import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { compileFunction } from 'node:vm'; import { createRequire } from 'node:module'; import { renderToStaticMarkup } from 'react-dom/server'; import ts from 'typescript'; const require = createRequire(import.meta.url); function load(file, mocks = {}) { const code = ts.transpileModule( fs.readFileSync(new URL(file, import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.ReactJSX, }, }, ).outputText; const module = { exports: {} }; compileFunction(code, ['require', 'module', 'exports'])( (key) => mocks[key] ?? require(key), module, module.exports, ); return module.exports; } const lib = load('../../resources/js/lib/company-preparation.ts'); const party = { id: 'SYNTHETIC-party', name: '', kind: 'person', source: null, roles: ['subscriber'], represented_id: null, cash: '10', in_kind: '0', paid: '2', share_count: '5', work: null, commitment: null, }; const facts = { form: 'quotas', proposed_name: null, seat: null, activities: null, regulated: 'unknown', currency: 'MZN', capital: '10', nominal_value: '2', management: null, binding: null, notes: null, parties: [party], }; const props = { case: { id: 'SYNTHETIC-case', title: 'SYNTHETIC dossier', version: 1 }, canWrite: true, historical: false, definition: { version: 'v1', forms: { individual: { title: 'Individual' }, quotas: { title: 'Quotas' }, anonymous: { title: 'Anónima' }, }, roles: { subscriber: 'Sócio', administrator: 'Administração', representative: 'Representante', beneficial_owner: 'Beneficiário', }, template_notice: 'Exemplares pendentes; ficha de preparação.', source: { title: 'SYNTHETIC source', edition: '2022', url: 'https://source.test/code.pdf', }, }, facts, review: { issues: ['SYNTHETIC pending'], totals: { subscribed: '10', unpaid: '8' }, note: 'SYNTHETIC declared', }, saved: null, sources: { case_proponent: { name: 'SYNTHETIC reused', kind: 'person', origin: 'Proponente do dossier', }, }, history: [], historyNext: null, }; function nodes(tree) { if (!tree || typeof tree !== 'object') return []; if (Array.isArray(tree)) return tree.flatMap(nodes); return [tree, ...nodes(tree.props?.children)]; } const named = (tree, name) => nodes(tree).find((n) => n.props?.name === name); const button = (tree, label) => nodes(tree).find((n) => n.type === 'button' && n.props.children === label); const tick = () => new Promise((resolve) => setImmediate(resolve)); function harness() { let cursor = 0, reject = false; const values = [], refs = [], visits = [], requests = []; class ValidationError extends Error { constructor(message) { super(message); this.fields = { 'facts.capital': 'SYNTHETIC amount error' }; } } const component = load( '../../resources/js/pages/investment/company-preparation.tsx', { react: { useState(initial) { const i = cursor++; if (!(i in values)) values[i] = structuredClone(initial); return [ values[i], (next) => { values[i] = typeof next === 'function' ? next(values[i]) : next; }, ]; }, useRef(initial) { const i = cursor++; if (!(i in values)) { values[i] = { current: initial }; refs.push(values[i]); } return values[i]; }, }, '@inertiajs/react': { Link: 'a', router: { visit: (...args) => visits.push(args) }, }, './shared': { Frame: 'main', card: '', input: '', button: '' }, '@/lib/company-preparation': lib, '@/lib/operation-key': { createOperationKey: () => 'SYNTHETIC-operation', }, '@/lib/profile-json': { ProfileValidationError: ValidationError, profileJson: async (...args) => { requests.push(args); if (args[2] === 'GET') return { ...props, case: { ...props.case, version: 4 }, facts: { ...facts, notes: 'SYNTHETIC concurrent edit', }, }; if (reject) throw new ValidationError('SYNTHETIC conflict'); return { saved: true }; }, }, }, ); return { render(extra = {}) { cursor = 0; const outer = component.default({ ...props, ...extra }); return outer.type(outer.props); }, refs, visits, requests, fail() { reject = true; }, }; } async function withData(data, fn) { const original = globalThis.FormData; globalThis.FormData = class { get(key) { return Object.hasOwn(data, key) ? data[key] : null; } }; try { return await fn(); } finally { globalThis.FormData = original; } } await test('preparation renders seven sections, escaped private names, source and explicit incomplete state', () => { const tree = harness().render(), html = renderToStaticMarkup(tree); assert.equal( nodes(tree).filter( (n) => n.type === 'h2' && /^[1-7]\./.test(n.props.children), ).length, 7, ); assert.match(html, /<SYNTHETIC>/); assert.match(html, /Exemplares pendentes/); assert.match(html, /SYNTHETIC pending/); assert.match(html, /https:\/\/source.test\/code.pdf/); assert.doesNotMatch(html, /name="(email|nuit|password)"/); }); await test('changing form hides only inapplicable capital and preserves amounts for later recovery', () => { const h = harness(); named(h.render(), 'form').props.onChange({ target: { value: 'individual' }, }); assert.equal(named(h.render(), 'capital'), undefined); named(h.render(), 'form').props.onChange({ target: { value: 'anonymous' }, }); assert.equal(named(h.render(), 'capital').props.value, '10'); assert.equal(named(h.render(), 'nominal_value').props.value, '2'); assert.equal(named(h.render(), `${party.id}.share_count`).props.value, '5'); }); await test('explicit reuse fills source identity while roles remain independently selectable', () => { const h = harness(); const source = nodes(h.render()).find( (n) => n.type === 'select' && n.props.children?.[0]?.props?.children === 'Informação nova desta preparação', ); source.props.onChange({ target: { value: 'case_proponent' } }); assert.equal( named(h.render(), `${party.id}.name`).props.value, 'SYNTHETIC reused', ); assert.equal(named(h.render(), `${party.id}.name`).props.readOnly, true); const checkboxes = nodes(h.render()).filter( (n) => n.type === 'input' && n.props.type === 'checkbox', ); assert.deepEqual( checkboxes.map((n) => n.props.checked), [true, false, false, false], ); checkboxes[1].props.onChange({ target: { checked: true } }); assert.deepEqual( nodes(h.render()) .filter((n) => n.type === 'input' && n.props.type === 'checkbox') .map((n) => n.props.checked), [true, true, false, false], ); }); await test('actual browser values are sent privately and hidden values survive without forged source snapshots', async () => { await withData( { proposed_name: 'SYNTHETIC autofill', [`${party.id}.cash`]: '7,25', capital: '', }, () => { const values = lib.preparationValues( {}, { ...facts, form: 'individual', parties: [ { ...party, source_snapshot: { reference: 'DO NOT POST' }, }, ], }, ); assert.equal(values.proposed_name, 'SYNTHETIC autofill'); assert.equal(values.capital, null); assert.equal(values.parties[0].cash, '7,25'); assert.equal(values.parties[0].share_count, '5'); assert.equal('source_snapshot' in values.parties[0], false); }, ); const h = harness(); h.render(); h.refs[0].current = {}; await withData({ notes: 'SYNTHETIC autofill' }, async () => { nodes(h.render()) .find((n) => n.type === 'form') .props.onSubmit({ preventDefault() {} }); await tick(); }); assert.equal( h.requests[0][0], '/investimentos/SYNTHETIC-case/preparar-empresa', ); assert.equal(h.requests[0][1].facts.notes, 'SYNTHETIC autofill'); assert.deepEqual(h.visits, [ [ '/investimentos/SYNTHETIC-case/preparar-empresa', { preserveState: false }, ], ]); }); await test('failed save retains private edits, consults current version without URL data and requires explicit reconciliation', async () => { const h = harness(); h.render(); h.refs[0].current = {}; h.fail(); await withData({ notes: 'SYNTHETIC unsaved' }, async () => { nodes(h.render()) .find((n) => n.type === 'form') .props.onSubmit({ preventDefault() {} }); await tick(); }); assert.equal(h.visits.length, 0); assert.equal(named(h.render(), 'notes').props.value, 'SYNTHETIC unsaved'); assert.match(renderToStaticMarkup(h.render()), /SYNTHETIC amount error/); await button( h.render(), 'Conferir edição actual sem perder estes dados', ).props.onClick(); assert.equal(h.requests[1][2], 'GET'); assert.equal(named(h.render(), 'notes').props.value, 'SYNTHETIC unsaved'); const recoveryHtml = renderToStaticMarkup(h.render()); assert.match(recoveryHtml, /SYNTHETIC concurrent edit/); assert.match(recoveryHtml, /Denominação proposta/); const recoverySection = nodes(h.render()).find( (n) => n.type === 'section' && nodes(n).some( (child) => child.type === 'h2' && child.props.children === 'Edição actualmente guardada no dossier', ), ); assert.doesNotMatch( renderToStaticMarkup(recoverySection), /source_snapshot|proposed_name|SYNTHETIC-party/, ); button( h.render(), 'Conferi: manter os meus dados para uma nova revisão', ).props.onClick(); assert.equal(named(h.render(), 'notes').props.value, 'SYNTHETIC unsaved'); await withData({}, async () => { nodes(h.render()) .find((n) => n.type === 'form') .props.onSubmit({ preventDefault() {} }); await tick(); }); assert.equal(h.requests[2][1].version, 4); }); await test('history and consultation keep all fields read-only and cannot submit a write', async () => { const h = harness(); const tree = h.render({ historical: true, saved: { revision: 1, at: 'SYNTHETIC-date' }, history: [{ id: 'old', revision: 1, at: 'SYNTHETIC-date' }], historyNext: 7, }); assert.equal( nodes(tree).find((n) => n.type === 'fieldset').props.disabled, true, ); nodes(tree) .find((n) => n.type === 'form') .props.onSubmit({ preventDefault() {} }); await tick(); assert.equal(h.requests.length, 0); assert.match(renderToStaticMarkup(tree), /preparar-empresa\?revision=old/); assert.match(renderToStaticMarkup(tree), /history_before=7/); const viewer = harness(); assert.equal( nodes(viewer.render({ canWrite: false })).find( (n) => n.type === 'fieldset', ).props.disabled, true, ); });