Files
Investidor-V2/tests/frontend/thematic-investment-map.test.mjs
T
2026-11-04 11:45:42 +02:00

341 lines
11 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 React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import ts from 'typescript';
const require = createRequire(import.meta.url);
const Map = () => React.createElement('div', { 'data-test-map': true });
function setup() {
let theme;
let mounted = false;
const source = fs.readFileSync(
new URL(
'../../resources/js/components/thematic-investment-map.tsx',
import.meta.url,
),
'utf8',
);
const output = ts.transpileModule(source, {
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'
? {
...React,
useEffect: (effect) => {
if (!mounted) {
mounted = true;
effect();
}
},
useState: (initial) => {
if (theme === undefined)
theme =
typeof initial === 'function'
? initial()
: initial;
return [
theme,
(next) => {
theme = next;
},
];
},
}
: name === './investment-map-indicators'
? { default: () => null }
: name === './catalogue-investment-map'
? { default: Map }
: require(name),
module,
module.exports,
);
return (...args) => {
module.exports.default(...args);
return module.exports.default(...args);
};
}
const flat = (node) =>
!node || typeof node !== 'object'
? []
: [node, ...React.Children.toArray(node.props.children).flatMap(flat)];
const proof = (category) => ({
id: category,
category,
statement: '<script>synthetic</script>',
source: 'Synthetic source',
locator: 'Table 4',
period: '2024',
scope: 'District A',
coverage: 'Partial coverage',
verified_on: '2026-10-01',
});
const records = [
{
id: 1,
title: 'Resource fixture',
url: '/fixture/1',
location: 'District A',
state: 'hypothesis',
limitations: 'No availability confirmed',
evidence: [proof('resource')],
},
{
id: 2,
title: 'Infrastructure fixture',
url: '/fixture/2',
location: 'Province B',
state: 'hypothesis',
limitations: null,
evidence: [proof('infrastructure')],
},
];
const points = [1, 3].map((id) => ({
geometry: { coordinates: [32, -25] },
properties: {
id,
title: 'Site ' + id,
url: '/fixture/' + id,
location: 'Confirmed opportunity site',
},
}));
const areas = [1, 2, 3].map((id) => ({
opportunity_id: id,
title: 'Area ' + id,
geometry: { type: 'Polygon', coordinates: [] },
}));
const props = {
points,
areas,
records,
territoryId: 'province-a',
truncated: false,
};
test('default preserves every opportunity and one map, with sourced thematic evidence and no new location claims', () => {
const Component = setup(),
tree = Component(props),
nodes = flat(tree);
const map = nodes.find((node) => node.type === Map);
assert.equal(nodes.filter((node) => node.type === Map).length, 1);
assert.equal(map.props.points, points);
assert.equal(map.props.areas, areas);
assert.equal(map.props.territoryId, 'province-a');
const html = renderToStaticMarkup(tree);
for (const text of [
'Synthetic source',
'Table 4',
'2024',
'District A',
'Partial coverage',
'2026-10-01',
'não representam a posição exacta',
'sem índice',
])
assert.ok(html.includes(text), text);
assert.match(html, /2 fichas com evidência temática pública/);
assert.match(html, /1 sem ponto público utilizável/);
assert.doesNotMatch(html, /<script>/);
});
test('choosing resources filters existing points and opportunity areas, retaining administrative identity and source context', () => {
const Component = setup();
flat(Component(props))
.find((node) => node.type === 'select')
.props.onChange({ target: { value: 'resource' } });
const tree = Component(props),
map = flat(tree).find((node) => node.type === Map);
assert.deepEqual(map.props.points, [points[0]]);
assert.deepEqual(map.props.areas, [areas[0]]);
assert.equal(map.props.territoryId, 'province-a');
const html = renderToStaticMarkup(tree);
assert.match(html, /Resource fixture/);
assert.doesNotMatch(html, /Infrastructure fixture/);
flat(tree)
.find((node) => node.type === 'select')
.props.onChange({ target: { value: 'all' } });
assert.equal(
flat(Component(props)).find((node) => node.type === Map).props.points,
points,
);
});
test('infrastructure without confirmed coordinates remains consultable without borrowing another opportunity point', () => {
const Component = setup();
flat(Component(props))
.find((node) => node.type === 'select')
.props.onChange({ target: { value: 'infrastructure' } });
const tree = Component(props),
map = flat(tree).find((node) => node.type === Map);
assert.deepEqual(map.props.points, []);
assert.deepEqual(map.props.areas, [areas[1]]);
assert.match(renderToStaticMarkup(tree), /Infrastructure fixture/);
assert.match(renderToStaticMarkup(tree), /Sem ponto público utilizável/);
assert.ok(
flat(tree).some(
(node) => node.type === 'a' && node.props.href === '/fixture/2',
),
);
});
test('changing query data never retains previous evidence; loading and incomplete coverage are explicit', () => {
const Component = setup();
flat(Component(props))
.find((node) => node.type === 'select')
.props.onChange({ target: { value: 'resource' } });
const loading = Component({
...props,
records: [],
points: null,
areas: [],
});
assert.equal(
flat(loading).find((node) => node.type === Map).props.points,
null,
);
assert.equal(
flat(loading).find((node) => node.type === 'select').props.disabled,
true,
);
assert.doesNotMatch(renderToStaticMarkup(loading), /Resource fixture/);
const html = renderToStaticMarkup(
Component({
...props,
points: [],
records: [],
areas: [],
truncated: true,
}),
);
assert.match(html, /Sem evidência pública local/);
assert.match(html, /A resposta está limitada/);
assert.match(html, /não significa ausência de recursos/);
});
test('theme selection survives reload in the tab and unavailable or invalid storage does not break the map', () => {
const previous = globalThis.window;
let saved = 'resource';
globalThis.window = {
sessionStorage: {
getItem: () => saved,
setItem: (key, value) => {
assert.equal(key, 'investmoz-map-theme-v1');
saved = value;
},
},
};
try {
const Component = setup();
let select = flat(Component(props)).find(
(node) => node.type === 'select',
);
assert.equal(select.props.value, 'resource');
select.props.onChange({ target: { value: 'infrastructure' } });
assert.equal(saved, 'infrastructure');
assert.equal(
flat(setup()(props)).find((node) => node.type === 'select').props
.value,
'infrastructure',
);
saved = 'invalid';
assert.equal(
flat(setup()(props)).find((node) => node.type === 'select').props
.value,
'all',
);
globalThis.window = {
sessionStorage: {
getItem: () => {
throw new Error('Storage unavailable');
},
setItem: () => {
throw new Error('Storage unavailable');
},
},
};
const WithoutStorage = setup();
select = flat(WithoutStorage(props)).find(
(node) => node.type === 'select',
);
assert.equal(select.props.value, 'all');
select.props.onChange({ target: { value: 'resource' } });
assert.equal(
flat(WithoutStorage(props)).find((node) => node.type === 'select')
.props.value,
'resource',
);
} finally {
if (previous === undefined) delete globalThis.window;
else globalThis.window = previous;
}
});
test('workforce and economic context preserve source categories and incompatible periods without inferred totals or borrowed points', () => {
const Component = setup();
const extra = [
{
...records[0],
id: 4,
title: 'Workforce evidence',
evidence: [{ ...proof('workforce'), source_category: 'workforce' }],
},
{
...records[1],
id: 5,
title: 'Economic context',
evidence: [
{
...proof('economic_potential'),
id: 'supply',
source_category: 'supply',
period: '2023',
},
{
...proof('economic_potential'),
id: 'demand',
source_category: 'demand',
period: '2026',
},
],
},
];
const data = { ...props, records: [...records, ...extra] };
flat(Component(data))
.find((node) => node.type === 'select')
.props.onChange({ target: { value: 'workforce' } });
let tree = Component(data);
assert.deepEqual(
flat(tree).find((node) => node.type === Map).props.points,
[],
);
let html = renderToStaticMarkup(tree);
assert.match(html, /Workforce evidence/);
assert.match(html, /Categoria de origem: Mão-de-obra/);
assert.match(html, /não mede trabalhadores disponíveis/);
flat(tree)
.find((node) => node.type === 'select')
.props.onChange({ target: { value: 'economic_potential' } });
tree = Component(data);
html = renderToStaticMarkup(tree);
assert.match(html, /Economic context/);
assert.match(html, /Categoria de origem: Oferta/);
assert.match(html, /Categoria de origem: Procura/);
assert.match(html, /2023/);
assert.match(html, /2026/);
assert.match(html, /sem índice, previsão de retorno ou viabilidade/);
assert.doesNotMatch(html, /Workforce evidence/);
assert.deepEqual(
flat(tree).find((node) => node.type === Map).props.points,
[],
);
});