Files
Investidor-V2/tests/frontend/opportunity-curation.test.mjs
T

134 lines
7.5 KiB
JavaScript

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 ts from 'typescript';
const require = createRequire(import.meta.url);
function nodes(tree) {
if (Array.isArray(tree)) return tree.flatMap(nodes);
if (!tree || typeof tree !== 'object') return [];
return [tree, ...nodes(tree.props?.children)];
}
function text(tree) {
if (typeof tree === 'string') return tree;
if (Array.isArray(tree)) return tree.map(text).join('');
return tree?.props ? text(tree.props.children) : '';
}
function harness() {
const memory = [], effects = [], calls = [], reloads = [];
let cursor = 0, pending = [], dirty = false, serial = 0, handler;
let props = { record: { id: 1, title: 'QA first', featured: false }, etag: 'a'.repeat(64), history: { data: [], current_page: 1 }, basePath: '/management/opportunities' };
const code = ts.transpileModule(fs.readFileSync(new URL('../../resources/js/pages/management/opportunity-curation.tsx', import.meta.url), 'utf8'), {
compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX, target: ts.ScriptTarget.ES2022 },
}).outputText;
const module = { exports: {} };
const mocks = {
react: {
useState(initial) {
const i = cursor++;
if (!(i in memory)) memory[i] = typeof initial === 'function' ? initial() : initial;
return [memory[i], (value) => {
const next = typeof value === 'function' ? value(memory[i]) : value;
if (!Object.is(next, memory[i])) { memory[i] = next; dirty = true; }
}];
},
useRef(initial) { const i = cursor++; memory[i] ??= { current: initial }; return memory[i]; },
useEffect(effect, dependencies) {
const i = cursor++;
if (!effects[i] || dependencies.some((value, index) => value !== effects[i][index])) {
effects[i] = dependencies; pending.push(effect);
}
},
},
'@inertiajs/react': { Link: 'a', router: { reload: (options) => reloads.push(options) } },
'@/lib/operation-key': { createOperationKey: () => `aaaaaaaa-aaaa-4aaa-8aaa-${String(++serial).padStart(12, '0')}` },
'@/lib/profile-json': {
ProfileValidationError: class extends Error {},
profileJson: (url, data) => { calls.push({ url, data: structuredClone(data) }); return handler(url, data); },
},
'./shared': { Management: 'main', button: '', control: '' },
};
compileFunction(code, ['require', 'module', 'exports'])((name) => mocks[name] ?? require(name), module, module.exports);
let tree;
function render() {
do { dirty = false; cursor = 0; pending = []; tree = module.exports.default(props); for (const effect of pending) effect(); } while (dirty);
return tree;
}
return {
calls, reloads, render,
setHandler(fn) { handler = fn; },
setProps(next) { props = next; return render(); },
button(label) { return nodes(render()).find(node => node.type === 'button' && text(node) === label); },
input(type) { return nodes(render()).find(node => node.type === type); },
get props() { return props; },
};
}
test('editing rationale invalidates both the preview and its confirmation without submitting', async () => {
const h = harness();
h.setHandler(async () => ({ hash: 'b'.repeat(64), title: 'QA first', before: false, featured: true, reason: 'Rationale for QA editorial selection' }));
h.input('textarea').props.onChange({ target: { value: 'Rationale for QA editorial selection' } });
await h.button('Conferir prévia').props.onClick();
assert.equal(h.button('Confirmar decisão de destaque').props.disabled, true);
h.input('input').props.onChange({ target: { checked: true } });
assert.equal(h.button('Confirmar decisão de destaque').props.disabled, false);
h.input('textarea').props.onChange({ target: { value: 'Changed editorial rationale must be reviewed again' } });
assert.equal(h.button('Confirmar decisão de destaque'), undefined);
assert.equal(h.calls.length, 1);
assert.equal(h.calls[0].data.featured, true);
});
test('in-flight preview is single; uncertain confirmation keeps the same intent for recovery', async () => {
const h = harness();
let release;
h.setHandler(() => new Promise(resolve => { release = resolve; }));
h.input('textarea').props.onChange({ target: { value: 'Rationale for QA editorial selection' } });
const first = h.button('Conferir prévia').props.onClick();
await h.button('Conferir prévia').props.onClick();
assert.equal(h.calls.length, 1);
assert.equal(h.input('fieldset').props.disabled, true);
release({ hash: 'b'.repeat(64), title: 'QA first', before: false, featured: true, reason: 'Rationale for QA editorial selection' });
await first;
h.input('input').props.onChange({ target: { checked: true } });
h.setHandler(async () => { throw new Error('Response lost'); });
await h.button('Confirmar decisão de destaque').props.onClick();
const uncertain = h.calls.at(-1).data;
h.setHandler(async () => ({ replayed: true, featured: true, etag: 'c'.repeat(64) }));
await h.button('Confirmar decisão de destaque').props.onClick();
assert.deepEqual(h.calls.at(-1).data, uncertain);
assert.equal(h.reloads.length, 1);
assert.deepEqual(h.reloads[0].only, ['record', 'etag', 'history']);
assert.match(text(h.render()), /Decisão já registada/);
});
test('a delayed preview cannot be adopted after another record and version arrive', async () => {
const h = harness();
let release;
h.setHandler(() => new Promise(resolve => { release = resolve; }));
h.input('textarea').props.onChange({ target: { value: 'Rationale for QA editorial selection' } });
const request = h.button('Conferir prévia').props.onClick();
h.setProps({ ...h.props, record: { id: 2, title: 'QA second', featured: false }, etag: 'd'.repeat(64) });
release({ hash: 'b'.repeat(64), title: 'QA first', before: false, featured: true, reason: 'Old rationale' });
await request;
assert.equal(h.button('Confirmar decisão de destaque'), undefined);
assert.match(text(h.render()), /QA second/);
assert.doesNotMatch(text(h.render()), /Old rationale/);
assert.equal(h.input('textarea').props.value, '');
});
test('changing only the history page retains the unsubmitted rationale and reviewed decision', async () => {
const h = harness();
h.setHandler(async () => ({ hash: 'b'.repeat(64), title: 'QA first', before: false, featured: true, reason: 'Reviewed QA rationale retained during paging' }));
h.input('textarea').props.onChange({ target: { value: 'Reviewed QA rationale retained during paging' } });
await h.button('Conferir prévia').props.onClick();
h.input('input').props.onChange({ target: { checked: true } });
h.setProps({ ...h.props, history: { data: [], current_page: 2, prev_page_url: '/management/opportunities/1/destaque?page=1' } });
assert.equal(h.input('textarea').props.value, 'Reviewed QA rationale retained during paging');
assert.equal(h.button('Confirmar decisão de destaque').props.disabled, false);
const link = nodes(h.render()).find(node => node.type === 'a' && text(node) === 'Anterior');
assert.equal(link.props.preserveState, true);
assert.equal(link.props.preserveScroll, true);
});