Files
Investidor-V2/tests/frontend/partnership-agreement-history.test.mjs
T

285 lines
9.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { harness, props, text } from './helpers/partnership-room.mjs';
import fs from 'node:fs';
import { createRequire } from 'node:module';
import { compileFunction } from 'node:vm';
import ts from 'typescript';
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
test('a legacy instrument without a confirmation map remains readable without inventing pending confirmations', () => {
for (const confirmations of [undefined, null, 'SYNTHETIC malformed map']) {
const initial = props();
initial.room.agreement.confirmations = confirmations;
const h = harness(initial);
assert.match(text(h.tree), /SYNTHETIC instrument/);
assert.match(text(h.tree), /confirmações.*(não.*registad|estrutura)/i);
assert.doesNotMatch(text(h.tree), /Confirmações: Pendentes/);
}
});
const require = createRequire(import.meta.url);
const modules = {};
const shared = fs.readFileSync(
'resources/js/pages/partnerships/shared.tsx',
'utf8',
);
const labels = compileFunction(
`return (${shared.match(/export const labels: Record<string, string> = (\{[\s\S]*?\n\});/)[1]});`,
[],
)();
function load(file) {
const code = ts.transpileModule(fs.readFileSync(file, 'utf8'), {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
jsx: ts.JsxEmit.ReactJSX,
},
}).outputText;
const module = { exports: {} };
compileFunction(code, ['require', 'module', 'exports'])(
(name) => {
if (Object.hasOwn(modules, name)) return modules[name];
if (name === './shared')
return {
labels,
Frame: ({ title, children }) =>
React.createElement(
'main',
{},
React.createElement('h1', {}, title),
children,
),
};
if (name === '@inertiajs/react')
return {
Link: ({ children, ...attributes }) =>
React.createElement('a', attributes, children),
};
return require(name);
},
module,
module.exports,
);
return module.exports;
}
modules['@/lib/partnership-agreement'] = load(
'resources/js/lib/partnership-agreement.ts',
);
modules['@/components/partnership-agreement-instrument'] = load(
'resources/js/components/partnership-agreement-instrument.tsx',
);
const Instrument =
modules['@/components/partnership-agreement-instrument'].default;
const Page = load(
'resources/js/pages/partnerships/agreement-history.tsx',
).default;
const instrument = () => ({
title: 'SYNTHETIC frozen title',
terms: 'SYNTHETIC frozen terms',
confirmations: {},
legal_signature_status: 'not_verified',
});
const snapshot = () => ({
availability: 'available',
version: 2,
status: 'agreement_recorded',
instrument_availability: 'available',
instrument: instrument(),
instrument_partnership_version: 4,
instrument_interest_version: 3,
});
const entry = () => ({
version: 2,
restricted: false,
action: 'record',
actor_name: 'SYNTHETIC actor',
occurred_at: '2026-11-03',
reason: 'SYNTHETIC reason',
confirmation_party: null,
partnership_version: 4,
interest_version: 3,
previous: { availability: 'not_recorded' },
after: snapshot(),
});
const history = () => ({
room_id: 1,
entries: [entry()],
first_url: '/parcerias/salas/1/historico',
next_url: null,
older_page: false,
room_href: '/parcerias/salas/1',
dashboard_href: '/parcerias/painel',
});
const render = (p) => renderToStaticMarkup(React.createElement(Page, p));
test('the stored empty array is an empty confirmation map while nonempty lists stay unrecognized', () => {
const value = instrument();
value.confirmations = [];
assert.match(
renderToStaticMarkup(React.createElement(Instrument, { value })),
/Confirmações: Pendentes/,
);
value.confirmations = [{ actor_id: 1 }];
assert.match(
renderToStaticMarkup(React.createElement(Instrument, { value })),
/não têm uma estrutura reconhecida/,
);
});
test('invalid instrument fields never become React children or fabricated confirmations', () => {
for (const value of [
false,
['SYNTHETIC invalid'],
{
title: { secret: 'SYNTHETIC PRIVATE' },
terms: ['SYNTHETIC PRIVATE'],
confirmations: null,
},
]) {
const html = renderToStaticMarkup(
React.createElement(Instrument, { value }),
);
assert.doesNotMatch(
html,
/SYNTHETIC PRIVATE|\[object Object\]|Pendentes/,
);
assert.match(html, /estrutura reconhecida|Título não registado/);
}
});
test('incomplete and unknown confirmation references do not impersonate a party', () => {
const initial = props();
initial.room.agreement.confirmations = {
proponent: { actor_id: 'wrong', date: {}, reason: [] },
unknown: { actor_id: 2 },
};
const h = harness(initial);
assert.match(text(h.tree), /Registo de confirmação incompleto/);
assert.match(text(h.tree), /não substituem a confirmação de cada parte/);
assert.ok(h.button('Confirmar pela minha parte'));
assert.equal(h.button('Actualizar confirmação da minha parte'), undefined);
assert.doesNotMatch(text(h.tree), /\[object Object\]/);
});
test('history renders frozen before and after instruments with their decision context and no write actions', () => {
const p = history();
p.entries[0].previous = snapshot();
p.entries[0].previous.instrument.title = 'SYNTHETIC previous title';
p.entries[0].after.instrument.confirmations = {
candidate: {
actor_id: 2,
actor_name: 'SYNTHETIC candidate',
reason: 'SYNTHETIC confirmation reason',
date: '2026-11-03',
},
};
p.entries[0].confirmation_party = 'candidate';
const html = render(p);
for (const fragment of [
'SYNTHETIC previous title',
'SYNTHETIC frozen title',
'SYNTHETIC candidate',
'SYNTHETIC confirmation reason',
'Contexto da decisão: proposta 4',
'candidatura 3',
'não verificada',
'Parte que confirmou: Candidata',
])
assert.ok(html.includes(fragment), fragment);
assert.doesNotMatch(html, /<form|<button|Registar versão do acordo/);
});
test('restricted and unknown history states never render supplied private fields', () => {
for (const availability of [
'restricted',
'scope_unknown',
'not_recorded',
'malformed',
]) {
const p = history();
p.entries[0].restricted = true;
p.entries[0].reason = 'SYNTHETIC HIDDEN reason';
p.entries[0].actor_name = 'SYNTHETIC HIDDEN actor';
p.entries[0].previous = {
...snapshot(),
availability,
instrument: { title: 'SYNTHETIC HIDDEN instrument' },
};
p.entries[0].after = {
...snapshot(),
instrument_availability: availability,
instrument: { title: 'SYNTHETIC HIDDEN instrument' },
};
const html = render(p);
assert.doesNotMatch(html, /SYNTHETIC HIDDEN/);
assert.match(html, /Conteúdo reservado/);
}
});
test('legacy absence is explicit and history never fills it with a current instrument', () => {
const p = history();
p.entries[0].after = { availability: 'not_recorded' };
const html = render(p);
assert.match(html, /Não foi preenchida com a versão actual/);
assert.doesNotMatch(html, /SYNTHETIC frozen/);
});
test('history navigation uses the scoped server URLs and distinguishes older pages', () => {
const p = history();
p.older_page = true;
p.next_url = '/parcerias/salas/1/historico?before=SYNTHETIC-token';
const html = render(p);
for (const fragment of [
'Voltar à sala',
'Versões mais recentes',
'Consultar versões anteriores',
'before=SYNTHETIC-token',
'href="/parcerias/painel"',
])
assert.ok(html.includes(fragment));
assert.doesNotMatch(html, /<button/);
});
test('an empty history explains the absence without creating a historical instrument', () => {
const p = history();
p.entries = [];
const html = render(p);
assert.match(html, /Ainda não existem alterações do acordo guardadas/);
assert.doesNotMatch(
html,
/Consultar versões anteriores|Antes da alteração/,
);
});
test('stored conditions and reasons are escaped and cannot add links or markup', () => {
const p = history();
p.entries[0].reason = '<script>SYNTHETIC</script>';
p.entries[0].after.instrument.terms =
'<a href="https://example.invalid">SYNTHETIC</a>';
const html = render(p);
assert.ok(html.includes('&lt;script&gt;SYNTHETIC&lt;/script&gt;'));
assert.ok(html.includes('&lt;a href='));
assert.doesNotMatch(html, /<script>|<a href="https:\/\/example.invalid/);
});
test('the room history link shares the existing leave guard without erasing an unsent reason', () => {
const h = harness(props());
h.change('agreement', 'Motivo da ação', 'SYNTHETIC unsent reason');
const link = h
.all()
.find(
(node) =>
node.type === 'a' &&
node.props.href ===
`/parcerias/salas/${h.props.room.id}/historico`,
);
assert.ok(link);
assert.equal(h.leave(), true);
assert.equal(
h.field('agreement', 'Motivo da ação').props.value,
'SYNTHETIC unsent reason',
);
assert.equal(h.sent.length, 0);
});