187 lines
5.9 KiB
JavaScript
187 lines
5.9 KiB
JavaScript
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 nodes(tree) {
|
|
if (!tree || typeof tree !== 'object') return [];
|
|
if (Array.isArray(tree)) return tree.flatMap(nodes);
|
|
return [tree, ...nodes(tree.props?.children)];
|
|
}
|
|
function harness() {
|
|
const values = [],
|
|
requests = [];
|
|
let cursor = 0,
|
|
effect;
|
|
const hooks = {
|
|
useState(initial) {
|
|
const index = cursor++;
|
|
if (!(index in values)) values[index] = initial;
|
|
return [
|
|
values[index],
|
|
(value) => {
|
|
values[index] =
|
|
typeof value === 'function'
|
|
? value(values[index])
|
|
: value;
|
|
},
|
|
];
|
|
},
|
|
useEffect(callback) {
|
|
effect = callback;
|
|
},
|
|
};
|
|
const output = ts.transpileModule(
|
|
fs.readFileSync(
|
|
new URL(
|
|
'../../resources/js/components/desk-service-history.tsx',
|
|
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) =>
|
|
name === 'react'
|
|
? hooks
|
|
: name === '@/lib/portal-api'
|
|
? {
|
|
portalRequest: (url, options) =>
|
|
new Promise((resolve, reject) =>
|
|
requests.push({
|
|
url,
|
|
options,
|
|
resolve,
|
|
reject,
|
|
}),
|
|
),
|
|
}
|
|
: require(name),
|
|
module,
|
|
module.exports,
|
|
);
|
|
return {
|
|
requests,
|
|
start: () => effect(),
|
|
render(code = 'project-change') {
|
|
cursor = 0;
|
|
return module.exports.default({
|
|
code,
|
|
formatField: (field) =>
|
|
field.label +
|
|
(field.required ? ' — Obrigatório' : ' — Opcional'),
|
|
});
|
|
},
|
|
};
|
|
}
|
|
const version = {
|
|
id: 'v1',
|
|
version: 1,
|
|
status: 'published',
|
|
author_id: 21,
|
|
published_by: 22,
|
|
published_at: null,
|
|
reason: 'Synthetic publication reason',
|
|
source: { identifier: 'Synthetic source', edition: 'Synthetic edition' },
|
|
definition: {
|
|
title: 'Old form',
|
|
theme: 'Theme',
|
|
profile: 'Investor',
|
|
entity: 'Internal team',
|
|
stage: 'E10',
|
|
result: 'Internal preparation',
|
|
fields: {
|
|
title: { label: 'Original question', type: 'text', required: true },
|
|
},
|
|
},
|
|
payload: 'PRIVATE-ANSWER',
|
|
};
|
|
const flush = () => new Promise((resolve) => setImmediate(resolve));
|
|
|
|
await test('history renders immutable definitions and provenance without applicant answers, then paginates using the same authorised endpoint', async () => {
|
|
const h = harness();
|
|
h.render();
|
|
h.start();
|
|
assert.equal(
|
|
h.requests[0].url,
|
|
'/management/balcao/servicos/project-change/versoes?page=1',
|
|
);
|
|
h.requests[0].resolve({ page: 1, pages: 2, total: 11, data: [version] });
|
|
await flush();
|
|
const tree = h.render(),
|
|
html = renderToStaticMarkup(tree);
|
|
assert.match(html, /Original question — Obrigatório/);
|
|
assert.match(html, /Synthetic publication reason/);
|
|
assert.match(html, /Synthetic source · Synthetic edition/);
|
|
assert.doesNotMatch(html, /PRIVATE-ANSWER|<input|<form|<textarea/);
|
|
nodes(tree)
|
|
.find(
|
|
(node) =>
|
|
node.type === 'button' && node.props.children === 'Seguinte',
|
|
)
|
|
.props.onClick();
|
|
assert.match(renderToStaticMarkup(h.render()), /A recuperar/);
|
|
h.start();
|
|
assert.equal(
|
|
h.requests[1].url,
|
|
'/management/balcao/servicos/project-change/versoes?page=2',
|
|
);
|
|
h.requests[1].resolve({
|
|
page: 2,
|
|
pages: 2,
|
|
total: 11,
|
|
data: [
|
|
{
|
|
...version,
|
|
id: 'older',
|
|
version: 0,
|
|
definition: { ...version.definition, title: 'Previous page' },
|
|
},
|
|
],
|
|
});
|
|
await flush();
|
|
assert.match(renderToStaticMarkup(h.render()), /Previous page/);
|
|
});
|
|
|
|
await test('failed history differs from an empty history and stale service responses never replace the selected service', async () => {
|
|
const h = harness();
|
|
h.render();
|
|
const cleanup = h.start();
|
|
cleanup();
|
|
h.render('project-follow-up');
|
|
h.start();
|
|
h.requests[0].resolve({ page: 1, pages: 1, total: 1, data: [version] });
|
|
await flush();
|
|
assert.doesNotMatch(
|
|
renderToStaticMarkup(h.render('project-follow-up')),
|
|
/Original question/,
|
|
);
|
|
h.requests[1].reject(new Error('Network unavailable'));
|
|
await flush();
|
|
const failed = h.render('project-follow-up');
|
|
assert.match(renderToStaticMarkup(failed), /Tentar novamente/);
|
|
assert.doesNotMatch(renderToStaticMarkup(failed), /Ainda não existem/);
|
|
nodes(failed)
|
|
.find((node) => node.type === 'button')
|
|
.props.onClick();
|
|
h.render('project-follow-up');
|
|
h.start();
|
|
h.requests[2].resolve({ page: 1, pages: 1, total: 0, data: [] });
|
|
await flush();
|
|
assert.match(
|
|
renderToStaticMarkup(h.render('project-follow-up')),
|
|
/Ainda não existem/,
|
|
);
|
|
});
|