406 lines
13 KiB
JavaScript
406 lines
13 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 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, /<SYNTHETIC company>/);
|
||
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();
|
||
}
|
||
});
|