218 lines
7.3 KiB
JavaScript
218 lines
7.3 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);
|
|
function load(path, mocks = {}) {
|
|
const source = fs.readFileSync(new URL(path, 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) => mocks[name] ?? require(name),
|
|
module,
|
|
module.exports,
|
|
);
|
|
return module.exports.default;
|
|
}
|
|
const Coverage = load(
|
|
'../../resources/js/components/investment-map-coverage.tsx',
|
|
);
|
|
const flat = (node) =>
|
|
!node || typeof node !== 'object'
|
|
? []
|
|
: [node, ...React.Children.toArray(node.props.children).flatMap(flat)];
|
|
const data = {
|
|
features: [
|
|
{
|
|
geometry: { coordinates: [35, -20] },
|
|
properties: {
|
|
id: 1,
|
|
title: 'Synthetic point',
|
|
url: '/synthetic/1',
|
|
source_reference: 'Synthetic source',
|
|
source_date: '2026-10-29',
|
|
},
|
|
},
|
|
{
|
|
geometry: { coordinates: [36, -21] },
|
|
properties: {
|
|
id: 1,
|
|
title: 'Synthetic unknown',
|
|
url: '/synthetic/2',
|
|
source_reference: ' ',
|
|
source_date: null,
|
|
},
|
|
},
|
|
],
|
|
areas: [
|
|
{
|
|
title: 'Synthetic area',
|
|
geometry: { type: 'Polygon', coordinates: [] },
|
|
url: '/synthetic/area',
|
|
source_reference: 'Synthetic area source',
|
|
source_date: '',
|
|
},
|
|
],
|
|
unlocated: [{ id: 3, title: 'Synthetic unlocated', url: '/synthetic/3' }],
|
|
truncated: true,
|
|
matched_count: 8,
|
|
};
|
|
void test('map coverage distinguishes returned counts, missing provenance and truncated coverage without changing data', () => {
|
|
const before = JSON.stringify(data);
|
|
const html = renderToStaticMarkup(
|
|
React.createElement(Coverage, {
|
|
points: data.features,
|
|
areas: data.areas,
|
|
unlocated: data.unlocated.length,
|
|
truncated: true,
|
|
}),
|
|
);
|
|
assert.match(
|
|
html,
|
|
/as contagens abaixo não representam o total do cadastro/,
|
|
);
|
|
assert.match(
|
|
html,
|
|
/<th scope="row">Marcadores de investimento<\/th><td>2<\/td><td>1<\/td><td>1<\/td>/,
|
|
);
|
|
assert.match(
|
|
html,
|
|
/<th scope="row">Áreas e limites<\/th><td>1<\/td><td>0<\/td><td>1<\/td>/,
|
|
);
|
|
assert.match(
|
|
html,
|
|
/Investimentos sem ponto público utilizável nesta consulta: 1/,
|
|
);
|
|
assert.match(html, /Investimentos com ponto nesta resposta: 1/);
|
|
assert.match(
|
|
html,
|
|
/Ausência de registos não significa ausência de investimento/,
|
|
);
|
|
assert.equal(JSON.stringify(data), before);
|
|
const empty = renderToStaticMarkup(
|
|
React.createElement(Coverage, {
|
|
points: [],
|
|
areas: [],
|
|
unlocated: 0,
|
|
truncated: false,
|
|
}),
|
|
);
|
|
assert.doesNotMatch(empty, /As fichas continuam disponíveis abaixo/);
|
|
assert.doesNotMatch(empty, /Resultados limitados/);
|
|
});
|
|
void test('one layered map preserves records, coverage, unlocated links and applied filters without a duplicate map or nonadministrative inference', async () => {
|
|
const previousFetch = globalThis.fetch;
|
|
let resolve;
|
|
const requests = [];
|
|
globalThis.fetch = (url, options) => {
|
|
requests.push({ url, options });
|
|
return new Promise((done) => {
|
|
resolve = done;
|
|
});
|
|
};
|
|
const memory = [];
|
|
let cursor = 0;
|
|
const effects = [];
|
|
const hooks = {
|
|
...React,
|
|
useState(initial) {
|
|
const index = cursor++;
|
|
if (!(index in memory)) memory[index] = initial;
|
|
return [
|
|
memory[index],
|
|
(value) => {
|
|
memory[index] =
|
|
typeof value === 'function'
|
|
? value(memory[index])
|
|
: value;
|
|
},
|
|
];
|
|
},
|
|
useEffect(callback) {
|
|
effects.push(callback);
|
|
},
|
|
};
|
|
const MapView = () => null;
|
|
const SearchState = () => null;
|
|
const Panel = load(
|
|
'../../resources/js/components/investment-map-panel.tsx',
|
|
{
|
|
react: hooks,
|
|
'@/components/search-state': { default: SearchState },
|
|
'./catalogue-investment-map': { default: MapView },
|
|
'./interactive-investment-map': { default: MapView },
|
|
'./investment-map-coverage': { default: Coverage },
|
|
},
|
|
);
|
|
const render = () => {
|
|
cursor = 0;
|
|
effects.length = 0;
|
|
return Panel({ filters: { province_id: 'synthetic-province' } });
|
|
};
|
|
let cleanup;
|
|
try {
|
|
assert.equal(
|
|
flat(render()).some((node) => node.type === Coverage),
|
|
false,
|
|
);
|
|
cleanup = effects[0]();
|
|
resolve({ ok: true, json: async () => data });
|
|
await new Promise((done) => setImmediate(done));
|
|
let tree = render();
|
|
assert.equal(
|
|
flat(tree).find((node) => node.type === SearchState).props.error,
|
|
'',
|
|
);
|
|
assert.equal(
|
|
flat(tree).find((node) => node.type === SearchState).props.total,
|
|
8,
|
|
);
|
|
const map = () => flat(tree).find((node) => node.type === MapView);
|
|
assert.equal(map().props.points.length, 2);
|
|
assert.equal(map().props.areas.length, 1);
|
|
assert.equal(
|
|
flat(tree).filter((node) => node.type === MapView).length,
|
|
1,
|
|
);
|
|
assert.equal(
|
|
flat(tree).filter((node) => node.type === 'input').length,
|
|
0,
|
|
);
|
|
assert.equal(map().props.territoryId, 'synthetic-province');
|
|
const html = renderToStaticMarkup(tree);
|
|
assert.match(html, /incluindo camadas ocultas/);
|
|
assert.match(html, /href="\/synthetic\/3"/);
|
|
assert.match(
|
|
html,
|
|
/<th scope="row">Marcadores de investimento<\/th><td>2<\/td>/,
|
|
);
|
|
tree = render();
|
|
assert.equal(map().props.points, data.features);
|
|
assert.equal(map().props.areas, data.areas);
|
|
assert.equal(requests.length, 1);
|
|
assert.match(requests[0].url, /province_id=synthetic-province/);
|
|
cursor = 0;
|
|
const changed = Panel({
|
|
filters: { geo_location_id: 'synthetic-park' },
|
|
});
|
|
const changedMap = flat(changed).find((node) => node.type === MapView);
|
|
assert.equal(changedMap.props.territoryId, undefined);
|
|
assert.equal(changedMap.props.points, null);
|
|
assert.deepEqual(changedMap.props.areas, []);
|
|
assert(!flat(changed).some((node) => node.type === Coverage));
|
|
} finally {
|
|
cleanup?.();
|
|
globalThis.fetch = previousFetch;
|
|
}
|
|
});
|