import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { createRequire } from 'node:module'; import { compileFunction } from 'node:vm'; import { renderToStaticMarkup } from 'react-dom/server'; import ts from 'typescript'; const require = createRequire(import.meta.url); function load(path, mocks) { const output = ts.transpileModule( fs.readFileSync(new URL(path, import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.ReactJSX, }, }, ).outputText; const module = { exports: {} }; compileFunction(output, ['require', 'module', 'exports'])( (name) => mocks[name] ?? require(name), module, module.exports, ); return module.exports; } function nodes(tree) { if (!tree || typeof tree !== 'object') return []; if (Array.isArray(tree)) return tree.flatMap(nodes); return [tree, ...nodes(tree.props?.children)]; } const emptyProps = { today: '2026-11-02', territories: [], projects: [], courses: [], occupations: [], skills: [], draftsAvailable: true, }; class ApiError extends Error { constructor(status) { super('Synthetic error'); this.status = status; this.errors = {}; } } function setup(request, props = emptyProps, renderPage = false) { const states = []; let cursor = 0, form; const Snapshot = () => null; const saved = [], dirty = []; const module = load('../../resources/js/pages/education/index.tsx', { react: { useState(initial) { const index = cursor++; if (!(index in states)) states[index] = typeof initial === 'function' ? initial() : initial; return [ states[index], (value) => { states[index] = typeof value === 'function' ? value(states[index]) : value; }, ]; }, useEffect(fn) { if (!renderPage) fn(); }, useCallback(fn) { return fn; }, }, '@inertiajs/react': { Link: 'a', router: {}, useForm(initial) { if (renderPage) return { data: initial, errors: {}, processing: false, isDirty: false, }; form ??= { data: { ...initial }, defaults: { ...initial }, errors: {}, processing: false, get isDirty() { return ( JSON.stringify(this.data) !== JSON.stringify(this.defaults) ); }, setData(key, value) { this.data = { ...this.data, [key]: value }; }, setDefaults(value) { this.defaults = { ...value }; }, clearErrors() { this.errors = {}; }, setError(errors) { this.errors = errors; }, post() { throw new Error('Draft must not use legacy create'); }, }; return form; }, }, '@/lib/portal-api': { portalRequest: request, PortalApiError: ApiError, newSubmissionKey: () => 'synthetic-stable-key', }, '@/components/education-submitted-snapshot': { __esModule: true, default: Snapshot, }, '../management/shared': { Management: 'main', Field: 'label', control: '', button: '', Pagination: () => null, }, '@/components/investment-territory': { TerritoryFields: 'territory-fields', }, }); return { page() { cursor = 0; return module.default(props); }, render() { cursor = 0; return module.Intake({ kind: 'demands', props, onSaved: (item) => saved.push(item), onDirty: (kind, value) => dirty.push([kind, value]), }); }, get form() { return form; }, saved, dirty, Snapshot, }; } const saveButton = (tree) => nodes(tree).find( (node) => node.type === 'button' && node.props.type === 'button', ); const flush = () => new Promise((resolve) => setImmediate(resolve)); await test('the full education page renders the private draft list and both intake forms', () => { const component = setup( async () => {}, { ...emptyProps, filters: {}, providers: [], demands: { data: [] }, capacities: { data: [] }, indicators: { demand_records: 0, capacity_records: 0, expired_demands: 0, pending_demands: 0, }, ownDrafts: { data: [ { id: 'qa-draft', kind: 'demands', state: 'draft', version_no: 1, label: 'Synthetic qualification', updated_at: '2026-11-02T12:00:00Z', }, ], }, }, true, ); const html = renderToStaticMarkup(component.page()); assert.ok(html.includes('Os meus rascunhos e submissões')); assert.ok(html.includes('/management/education?draft=qa-draft')); assert.ok(html.includes('Synthetic qualification')); assert.ok(html.includes('Declarar procura de trabalhadores')); assert.ok(html.includes('Declarar vagas de formação')); }); await test('incomplete save bypasses native required fields, keeps edits, and submits the saved identity with current values', async () => { const requests = []; const component = setup(async (url, options) => { const data = JSON.parse(options.body); requests.push({ url, method: options.method, data }); return { id: 'qa-draft', kind: 'demands', version_no: data.version_no + 1, state: url.endsWith('/submit') ? 'submitted' : 'draft', payload: data.payload, references: {}, record_id: null, updated_at: '2026-11-02T12:00:00Z', }; }); let tree = component.render(); component.form.setData('employer', 'Synthetic incomplete employer'); tree = component.render(); assert.equal(saveButton(tree).props.type, 'button'); saveButton(tree).props.onClick(); await flush(); assert.equal(requests[0].method, 'POST'); assert.equal(requests[0].data.payload.qualification, ''); assert.equal(component.form.data.employer, 'Synthetic incomplete employer'); assert.equal(component.form.isDirty, false); tree = component.render(); component.form.setData('quantity', '12'); tree = component.render(); saveButton(tree).props.onClick(); await flush(); assert.equal(requests[1].method, 'PUT'); assert.equal(requests[1].data.version_no, 1); assert.equal(requests[1].data.client_key, requests[0].data.client_key); tree = component.render(); nodes(tree) .find((node) => node.type === 'form') .props.onSubmit({ preventDefault() {} }); await flush(); assert.equal( requests[2].url, '/management/education/drafts/demands/qa-draft/submit', ); assert.equal(requests[2].data.version_no, 2); assert.equal(requests[2].data.payload.quantity, '12'); tree = component.render(); assert.equal(tree.type, component.Snapshot); assert.equal(tree.props.draft.state, 'submitted'); assert.equal(component.saved.length, 3); }); await test('recovery preserves blank dates and reference IDs, and a conflict does not overwrite current edits', async () => { const component = setup( async () => { throw new ApiError(409); }, { ...emptyProps, selectedDraft: { id: 'recovered', kind: 'demands', version_no: 4, state: 'draft', payload: { employer: 'Old', reference_date: null, geo_location_id: 'retired-location', }, references: null, }, }, ); let tree = component.render(); assert.equal(component.form.data.reference_date, ''); assert.equal(component.form.data.geo_location_id, 'retired-location'); component.form.setData('employer', 'Deliberate current edit'); tree = component.render(); saveButton(tree).props.onClick(); await flush(); tree = component.render(); assert.equal(component.form.data.employer, 'Deliberate current edit'); assert.equal(component.form.isDirty, true); assert.ok( nodes(tree).some( (node) => node.props?.role === 'alert' && String(node.props.children).includes('noutra sessão'), ), ); assert.equal(component.saved.length, 0); }); await test('recovered territorial path remains stable while unrelated fields are edited', () => { const territories = [ { id: 'p', name: 'Province', parent_id: null, location_type: 'province', }, { id: 'd', name: 'District', parent_id: 'p', location_type: 'district', }, ]; const component = setup(async () => {}, { ...emptyProps, territories, selectedDraft: { id: 'qa', kind: 'demands', version_no: 1, state: 'draft', payload: { geo_location_id: 'd' }, }, }); let tree = component.render(); const initial = nodes(tree).find((node) => node.type === 'territory-fields') .props.initial; assert.deepEqual( initial.map((item) => item.id), ['p', 'd'], ); component.form.setData('employer', 'New text'); tree = component.render(); assert.equal( nodes(tree).find((node) => node.type === 'territory-fields').props .initial, initial, ); }); await test('submitted view uses preserved reference names and escapes source text without another submission form', () => { const Snapshot = load( '../../resources/js/components/education-submitted-snapshot.tsx', { '@inertiajs/react': { Link: 'a' } }, ).default; const html = renderToStaticMarkup( Snapshot({ draft: { kind: 'demands', record_id: 'qa-record', payload: { quantity: 20, source: '' }, references: { project_id: { name: 'Original project name' }, territory_path: [{ name: 'Original district' }], }, }, }), ); assert.ok(html.includes('Original project name')); assert.ok(html.includes('Original district')); assert.ok(html.includes('<script>')); assert.ok(!html.includes('