import test from 'node:test'; import assert from 'node:assert/strict'; import { harness, props, text } from './helpers/education-review.mjs'; const form = 'education-review'; function historyPage(page = 1) { return { data: [], total: 24, current_page: page, last_page: 2, prev_page_url: page === 2 ? '/management/education/demands/one?scope=review&history_page=1' : null, next_page_url: page === 1 ? '/management/education/demands/one?scope=review&history_page=2' : null, }; } test('history paging keeps the prepared review and does not ask to discard it', () => { const h = harness(props({ history: historyPage() })); h.change(form, reason, 'SYNTHETIC reasoning still being prepared.'); const next = h.all().find(node => node.type === 'a' && text(node) === 'Seguinte'); assert.equal(next.props.preserveState, true); assert.equal(next.props.preserveScroll, true); assert.match(next.props.href, /scope=review&history_page=2$/); assert.equal(h.leave(next.props.preserveState), false); assert.equal(h.confirmCalls, 0); h.render({ ...h.props, history: historyPage(2) }); assert.equal(h.field(form, reason).props.value, 'SYNTHETIC reasoning still being prepared.'); const previous = h.all().find(node => node.type === 'a' && text(node) === 'Anterior'); assert.equal(previous.props.preserveState, true); assert.equal(previous.props.preserveScroll, true); assert.equal(h.sent.length, 0); assert.equal(h.leave(), true); // Leaving the declaration still protects unsaved work. }); test('history paging keeps an uncertain attempt recoverable under the same command key', () => { const h = harness(props({ history: historyPage() })); const request = prepared(h); h.fail(request); h.render({ ...h.props, history: historyPage(2) }); const retry = h.submit(form); assert.deepEqual(retry.data, request.data); assert.equal(h.sent.length, 2); // One explicit retry; paging itself sends no decision. }); test('history paging cannot silently approve a newer declaration version', () => { const h = harness(props({ history: historyPage() })); h.change(form, reason, 'SYNTHETIC review prepared against version one.'); h.render({ ...h.props, history: historyPage(2), declaration: { ...h.props.declaration, version_no: 2, quantity: 31 }, }); h.submit(form); assert.equal(h.sent.length, 0); assert.equal(h.field(form, reason).props.value, 'SYNTHETIC review prepared against version one.'); assert.match(text(h.tree), /A declaração mudou/); h.click('Confirmar a leitura desta versão'); const request = h.submit(form); assert.equal(request.data.version_no, 2); }); test('history paging with a different actor remounts instead of disclosing the prepared reason', () => { const h = harness(props({ history: historyPage() })); h.change(form, reason, 'SYNTHETIC private reasoning of the previous actor.'); h.render({ ...h.props, actorId: 3, history: historyPage(2), declaration: { ...h.props.declaration, review_context: { actor_id: 3, commands: [] } }, }); assert.equal(h.field(form, reason).props.value, ''); assert.equal(h.sent.length, 0); }); test('the private detail renders preserved reasons, unavailable entries and contextual history pagination', () => { const h = harness( props({ history: { data: [ { id: 'one', version: 3, actor: 'SYNTHETIC reviewer', occurred_at: '2026-11-03', available: true, status: 'rejected', reason: 'SYNTHETIC preserved reason', previous_reason: 'SYNTHETIC older reason', }, { id: 'two', version: 2, actor: null, occurred_at: '2026-11-02', available: false, status: null, reason: null, previous_reason: null, }, ], total: 12, current_page: 1, last_page: 2, prev_page_url: null, next_page_url: '/management/education/demands/one?scope=mine&history_page=2', }, }), ); assert.match(text(h.tree), /Registos de revisão disponíveis/); assert.match(text(h.tree), /SYNTHETIC preserved reason/); assert.match( text(h.tree), /Fundamento anterior capturado: SYNTHETIC older reason/, ); assert.match(text(h.tree), /Fundamento indisponível para leitura/); assert.ok(h.tree); }); test('the submitter sees the declaration without being asked to review it', () => { const initial = props(); initial.declaration.can_review = false; initial.declaration.review_unavailable = 'self'; const h = harness(initial); assert.equal(h.button('Guardar decisão'), undefined); assert.match(text(h.tree), /outro gestor/); }); test('a newer declaration cannot silently replace the version on which the review was prepared', () => { const h = harness(); h.change( form, 'Justificação e evidência da revisão', 'SYNTHETIC prepared reason with evidence.', ); h.render({ ...h.props, declaration: { ...h.props.declaration, version_no: 2, quantity: 30 }, }); h.submit(form); assert.equal(h.sent.length, 0); }); const reason = 'Justificação e evidência da revisão'; function prepared(h) { h.change(form, reason, 'SYNTHETIC independent review with evidence.'); return h.submit(form); } function accepted(h, request = h.sent.at(-1), patch = {}) { return { ...h.props, declaration: { ...h.props.declaration, version_no: request.data.version_no + 1, status: request.data.status, review_reason: request.data.review_reason, review_context: { actor_id: h.props.actorId, commands: [ { key: request.data.client_key, version: request.data.version_no + 1, }, ], }, }, ...patch, }; } test('explicit comparison adopts the current version without discarding the prepared decision', () => { const h = harness(); h.change(form, reason, 'SYNTHETIC prepared reason with evidence.'); h.render({ ...h.props, declaration: { ...h.props.declaration, version_no: 2, quantity: 30 }, }); assert.match(text(h.tree), /Analisada: 5.*Actual: 30/); h.click('Confirmar a leitura desta versão'); const request = h.submit(form); assert.equal(request.data.version_no, 2); assert.equal( request.data.review_reason, 'SYNTHETIC prepared reason with evidence.', ); }); test('confirmation clears only sent event values and preserves later edits', () => { const h = harness(), request = prepared(h); h.change(form, reason, 'SYNTHETIC later reason with evidence.'); h.change(form, 'Decisão interna', 'rejected'); h.success(accepted(h, request), request); assert.equal( h.field(form, reason).props.value, 'SYNTHETIC later reason with evidence.', ); assert.equal(h.field(form, 'Decisão interna').props.value, 'rejected'); const next = h.submit(form); assert.equal(next.data.version_no, 2); assert.notEqual(next.data.client_key, request.data.client_key); }); test('an uncertain result retries exactly the original version, decision and key', () => { const h = harness(), request = prepared(h); h.fail(request); h.change(form, reason, 'SYNTHETIC later reason with evidence.'); const retry = h.submit(form); assert.deepEqual(retry.data, request.data); h.success(accepted(h, retry), retry); assert.equal( h.field(form, reason).props.value, 'SYNTHETIC later reason with evidence.', ); }); test('a fresh authorised read confirms the receipt without sending another decision', () => { const h = harness(), request = prepared(h); h.fail(request, 500); h.click('Actualizar dados da declaração'); assert.deepEqual(h.reloads[0].only, [ 'declaration', 'references', 'today', 'actorId', 'kind', 'history', ]); h.refreshed(accepted(h, request)); assert.equal(h.sent.length, 1); assert.equal(h.field(form, reason).props.value, ''); assert.equal(h.leave(), false); assert.match(text(h.tree), /Revisão registada/); }); test('another actor cannot acknowledge or erase a local pending review', () => { const h = harness(), request = prepared(h); request.options.onSuccess({ props: accepted(h, request, { actorId: 3 }) }); request.form.processing = false; h.render(); assert.equal( h.field(form, reason).props.value, 'SYNTHETIC independent review with evidence.', ); assert.deepEqual(h.submit(form).data, request.data); }); test('an obsolete callback cannot alter the draft of the next account', () => { const h = harness(), request = prepared(h); h.render({ ...h.props, actorId: 3, declaration: { ...h.props.declaration, review_context: { actor_id: 3, commands: [] }, }, }); h.change(form, reason, 'SYNTHETIC next account review.'); request.options.onSuccess({ props: accepted(h, request, { actorId: 1 }) }); request.form.processing = false; h.render(); assert.equal( h.field(form, reason).props.value, 'SYNTHETIC next account review.', ); }); test('a later decision remains current when an earlier receipt is acknowledged', () => { const h = harness(), request = prepared(h); h.fail(request); const next = accepted(h, request); next.declaration.version_no = 3; next.declaration.status = 'rejected'; h.render(next); assert.match(text(h.tree), /A declaração mudou/); assert.equal(h.sent.length, 1); h.submit(form); assert.equal(h.sent.length, 1); h.click('Confirmar a leitura desta versão'); const fresh = h.submit(form); assert.equal(fresh.data.version_no, 3); assert.notEqual(fresh.data.client_key, request.data.client_key); }); test('a competing newer version without the receipt requires explicit adoption before a new decision', () => { const h = harness(), request = prepared(h); h.fail(request, 409); h.click('Actualizar declaração'); h.refreshed({ ...h.props, declaration: { ...h.props.declaration, version_no: 2, quantity: 20 }, }); h.click('Confirmar a leitura desta versão'); const next = h.submit(form); assert.equal(next.data.version_no, 2); assert.notEqual(next.data.client_key, request.data.client_key); }); test('expired declarations retain prepared text without a new decision form', () => { const h = harness(), request = prepared(h); h.fail(request, 422); h.render({ ...h.props, declaration: { ...h.props.declaration, can_review: false, review_unavailable: 'expired', }, }); assert.equal(h.button('Guardar decisão'), undefined); assert.equal(h.button('Recuperar a mesma revisão'), undefined); assert.match(text(h.tree), /Fundamento preparado nesta página/); assert.match(text(h.tree), /SYNTHETIC independent review/); }); test('validation preserves the draft and a decision-only change has one leave guard', () => { const h = harness(); h.change(form, 'Decisão interna', 'rejected'); assert.equal(h.leave(), true); assert.equal(h.confirmCalls, 1); const request = prepared(h); h.validation({ review_reason: 'SYNTHETIC validation' }, request); assert.equal( h.field(form, reason).props.value, 'SYNTHETIC independent review with evidence.', ); const next = h.submit(form); assert.notEqual(next.data.client_key, request.data.client_key); });