Files
Investidor-V2/tests/frontend/proactive-supply-review.test.mjs
T

406 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(file, mocks) {
const code = ts.transpileModule(
fs.readFileSync(new URL(file, import.meta.url), 'utf8'),
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2022,
jsx: ts.JsxEmit.ReactJSX,
},
},
).outputText;
const module = { exports: {} };
compileFunction(code, ['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 source = 'a'.repeat(64),
observation = 'b'.repeat(64),
duplicate = 'c'.repeat(64);
const module = load(
'../../resources/js/pages/management/proactive-supply-review.tsx',
{
'@inertiajs/react': { Link: 'a' },
'./shared': { Management: 'main', button: '', control: '' },
},
);
const props = {
installed: true,
filters: {
status: 'repeated',
source_hash: source,
q: '100% & algodão',
page: 2,
},
recordId: 35,
total: 26,
per_page: 25,
limitations: 'Historical partial source',
status: 'Administrative observations',
sources: [
{
sha256: source,
name: 'SYNTHETIC source.xlsx',
period: '2018–2020',
limitations: 'Historical, not current capacity',
imported_at: '2026-10-28',
receipt: { contact: 'PRIVATE NEVER DISPLAY' },
},
],
coverage: {
observations: 50,
distinct_observations: 30,
repeated: 20,
unmapped: 10,
ambiguous: 4,
territory_unavailable: 1,
identity_conflict: 2,
unclassified: 3,
unnamed: 2,
},
rows: [
{
id: observation,
name: '<SYNTHETIC company>',
province: 'Original province',
district: 'Original district',
territory_id: null,
territory_resolution: 'ambiguous',
canonical_territory: null,
canonical_type: null,
cae: '',
activity: '',
sector: '',
source_date: 'Serial Excel original: 45000',
sheet: 'Original sheet',
row_number: 4,
source_hash: source,
identity_conflict: true,
duplicate_of: duplicate,
source_name: 'SYNTHETIC source.xlsx',
source_period: '2018–2020',
imported_at: '2026-10-28',
entity_key: 'PRIVATE NEVER DISPLAY',
},
],
};
await test('private review preserves source lineage and distinguishes observations, repetitions, ambiguity and unavailable dates', () => {
const tree = module.default(props);
const html = renderToStaticMarkup(tree);
assert.match(html, /&lt;SYNTHETIC company&gt;/);
assert.match(html, /Serial Excel original: 45000/);
assert.match(html, /Original sheet, linha 4/);
assert.match(html, /não são números de empresas únicas/);
assert.match(html, /podem sobrepor-se/);
assert.match(
html,
/Várias correspondências possíveis, sem escolha automática/,
);
assert.doesNotMatch(html, /PRIVATE NEVER DISPLAY/);
const form = nodes(tree).find((node) => node.type === 'form');
assert.equal(form.props.method, 'get');
assert.equal(
form.props.action,
'/minhas-oportunidades/oferta-interna/revisao',
);
assert.equal(form.props.onSubmit, undefined);
const links = nodes(tree).filter((node) => node.type === 'a');
const root = new URL(
links.find(
(node) =>
node.props.children === 'Conferir a observação de referência',
).props.href,
'https://portal.invalid',
);
assert.equal(root.searchParams.get('observation_id'), duplicate);
assert.equal(root.searchParams.get('status'), 'all');
assert.equal(
root.searchParams.has('source_hash'),
false,
'A repeated sighting can point to an earlier original source.',
);
const previous = new URL(
links.find((node) => node.props.children === 'Anterior').props.href,
'https://portal.invalid',
);
assert.equal(previous.searchParams.get('q'), '100% & algodão');
assert.equal(previous.searchParams.get('record_id'), '35');
assert.equal(previous.searchParams.get('source_hash'), source);
assert.equal(previous.searchParams.get('page'), '1');
assert.ok(
links.some(
(node) =>
node.props.children === 'Ligação para recuperar esta linha' &&
node.props.href.includes(observation),
),
);
});
await test('missing or empty coverage never means no businesses, and exact-row filters can be removed without losing the source context', () => {
const html = renderToStaticMarkup(
module.default({
...props,
installed: false,
rows: [],
total: null,
coverage: null,
sources: [],
filters: { ...props.filters, observation_id: observation },
errors: { source_hash: 'SYNTHETIC source unavailable' },
}),
);
assert.match(html, /base interna ainda não está carregada/);
assert.match(html, /SYNTHETIC source unavailable/);
assert.doesNotMatch(
html,
/Página 2|0 empresas|Cobertura documental da origem/,
);
const url = module.supplyReviewUrl(
{ ...props.filters, observation_id: null, page: 1 },
35,
);
assert.equal(
new URL(url, 'https://portal.invalid').searchParams.has(
'observation_id',
),
false,
);
assert.equal(
new URL(url, 'https://portal.invalid').searchParams.get('source_hash'),
source,
);
});
function browserHarness() {
let cursor = 0;
const state = [],
effects = [],
requests = [];
const hooks = {
useState(initial) {
const index = cursor++;
if (!(index in state)) state[index] = initial;
return [
state[index],
(value) => {
state[index] =
typeof value === 'function'
? value(state[index])
: value;
},
];
},
useEffect(fn) {
effects.push(fn);
},
};
const Browser = load('../../resources/js/components/proactive-supply.tsx', {
react: hooks,
'@inertiajs/react': { Link: 'a' },
'@/pages/management/shared': {
button: '',
control: '',
Field: 'label',
},
'@/lib/operation-key': { createOperationKey: () => 'qa' },
}).ProactiveSupplyBrowser;
const previousFetch = globalThis.fetch;
globalThis.fetch = (url, options) =>
new Promise((resolve, reject) =>
requests.push({
url,
options,
fail: reject,
finish(data) {
resolve({ ok: true, json: async () => data });
},
}),
);
return {
render(next = {}) {
cursor = 0;
effects.length = 0;
return Browser({
value: { evidence: [], supply_comparisons: [] },
onChange() {},
territoryId: 'area-a',
recordId: 1,
...next,
});
},
start: () => effects[1](),
resetTerritory: () => effects[0](),
requests,
restore: () => {
globalThis.fetch = previousFetch;
},
};
}
const result = (title) => ({
installed: true,
rows: [
{
id: title,
name: title,
province: 'P',
district: 'D',
cae: '1',
activity: 'QA',
sector: 'QA',
source_date: '2020',
sheet: 'QA',
row_number: 2,
source_hash: source,
source_name: 'QA source',
source_period: '2020',
identity_conflict: false,
},
],
total: 1,
limitations: 'Historical',
status: 'Historical',
});
const flush = () => new Promise((resolve) => setImmediate(resolve));
await test('a late supply response cannot revive rows from a previous territory', async () => {
const h = browserHarness();
try {
let tree = h.render();
nodes(tree)
.find(
(node) =>
node.type === 'button' &&
node.props.children === 'Consultar oferta da localização',
)
.props.onClick();
h.render();
const abort = h.start();
h.render({ territoryId: 'area-b' });
abort();
h.resetTerritory();
h.requests[0].finish(result('PRIVATE OLD AREA'));
await flush();
assert.doesNotMatch(
renderToStaticMarkup(h.render({ territoryId: 'area-b' })),
/PRIVATE OLD AREA/,
);
assert.equal(h.requests[0].options.signal.aborted, true);
} finally {
h.restore();
}
});
await test('changing the record hides prior supply results until the response scoped to that record arrives', async () => {
const h = browserHarness();
try {
let tree = h.render();
nodes(tree)
.find(
(node) =>
node.type === 'button' &&
node.props.children === 'Consultar oferta da localização',
)
.props.onClick();
h.render();
const abort = h.start();
h.requests[0].finish(result('PRIVATE OLD RECORD'));
await flush();
assert.match(renderToStaticMarkup(h.render()), /PRIVATE OLD RECORD/);
assert.doesNotMatch(
renderToStaticMarkup(h.render({ recordId: 2 })),
/PRIVATE OLD RECORD/,
);
abort();
h.start();
assert.equal(
new URL(
h.requests[1].url,
'https://portal.invalid',
).searchParams.get('record_id'),
'2',
);
h.requests[1].finish(result('CURRENT RECORD RESULT'));
await flush();
const html = renderToStaticMarkup(h.render({ recordId: 2 }));
assert.match(html, /CURRENT RECORD RESULT/);
assert.doesNotMatch(html, /PRIVATE OLD RECORD/);
} finally {
h.restore();
}
});
await test('filter navigation resets native inputs to the applied query and keeps the record context', () => {
const first = nodes(module.default(props)).find(
(node) => node.type === 'form',
);
const nextProps = {
...props,
filters: { status: 'all', page: 1, observation_id: duplicate },
};
const next = nodes(module.default(nextProps)).find(
(node) => node.type === 'form',
);
assert.notEqual(first.key, next.key);
assert.equal(
nodes(next).find((node) => node.props?.name === 'source_hash').props
.defaultValue,
'',
);
assert.equal(
nodes(next).find((node) => node.props?.name === 'q').props.defaultValue,
'',
);
assert.equal(
nodes(next).find((node) => node.props?.name === 'record_id').props
.value,
35,
);
});
await test('a late failure from an aborted request cannot clear the current record results', async () => {
const h = browserHarness();
try {
nodes(h.render())
.find(
(node) =>
node.type === 'button' &&
node.props.children === 'Consultar oferta da localização',
)
.props.onClick();
h.render();
const abort = h.start();
h.render({ recordId: 2 });
abort();
h.start();
h.requests[1].finish(result('CURRENT RESULT MUST REMAIN'));
await flush();
h.requests[0].fail(
new Error('OLD ERROR MUST NOT REPLACE CURRENT RESULT'),
);
await flush();
const html = renderToStaticMarkup(h.render({ recordId: 2 }));
assert.match(html, /CURRENT RESULT MUST REMAIN/);
assert.doesNotMatch(html, /OLD ERROR MUST NOT/);
} finally {
h.restore();
}
});