Files
Investidor-V2/tests/frontend/investment-map-indicators.test.mjs
T

222 lines
7.1 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 source = fs.readFileSync(
new URL(
'../../resources/js/components/investment-map-indicators.tsx',
import.meta.url,
),
'utf8',
);
const compiled = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2022,
jsx: ts.JsxEmit.ReactJSX,
},
}).outputText;
const module = { exports: {} };
compileFunction(compiled, ['require', 'module', 'exports'])(
require,
module,
module.exports,
);
const Component = module.exports.default;
const filterIndicators = module.exports.filterMapIndicators;
const row = {
id: '1',
zone_id: 'park-a',
zone_name: 'Synthetic park',
zone_version: 3,
url: '/zonas/park-a',
theme: 'workforce',
indicator: 'employment',
label: 'Synthetic declared jobs',
value: '0',
unit: 'pessoas',
period_start: '2025-01-01',
period_end: '2025-12-31',
scope: 'Park A only',
population: 'Declared establishments',
methodology: 'Documentary register',
source_reference: '<script>synthetic source</script>',
source_date: '2026-01-15',
knowledge: 'documented',
missing_metadata: [],
};
const context = {
state: 'available',
records: [
row,
{
...row,
id: '2',
theme: 'economic_potential',
indicator: 'production',
label: 'Synthetic production',
value: '12.3400',
unit: 'toneladas',
period_start: '2024-01-01',
period_end: '2024-12-31',
},
],
zones_consulted: 1,
zone_limit: 30,
record_limit: 200,
truncated: false,
};
const render = (data, theme = 'all') =>
renderToStaticMarkup(
React.createElement(Component, { context: data, theme }),
);
test('quantitative observations keep zero, decimal precision, units, periods, scope, methodology and public source', () => {
const html = render(context);
for (const value of [
'0',
'12.3400',
'pessoas',
'toneladas',
'2025-01-01',
'2024-01-01',
'Park A only',
'Declared establishments',
'Documentary register',
'2026-01-15',
'Versão pública 3',
])
assert.ok(html.includes(value), value);
assert.match(html, /Sem somas, conversões de unidade ou moeda/);
assert.match(html, /Emprego não mede trabalhadores disponíveis/);
assert.match(html, /não constituem um índice de potencial/);
assert.doesNotMatch(html, /<script>/);
assert.match(html, /href="\/zonas\/park-a"/);
});
test('themes separate labour and economic observations without inventing totals or losing metadata', () => {
assert.match(render(context, 'workforce'), /pessoas/);
assert.doesNotMatch(render(context, 'workforce'), /toneladas/);
assert.match(render(context, 'economic_potential'), /12.3400/);
assert.doesNotMatch(
render(context, 'economic_potential'),
/Synthetic declared jobs/,
);
assert.equal(render(context, 'resource'), '');
});
test('unknown values, disputes, missing metadata, incomplete coverage and excluded scope remain explicit', () => {
const html = render({
...context,
truncated: true,
records: [
{
...row,
value: null,
unit: null,
knowledge: 'unknown',
missing_metadata: ['unit', 'population'],
},
{ ...row, id: '3', knowledge: 'disputed' },
],
});
assert.match(html, /Por determinar/);
assert.match(html, /Em divergência/);
assert.match(html, /Informação em falta: unidade, população\/base/);
assert.match(html, /limitada a 30 fichas e 200 observações/);
assert.match(
render({ ...context, records: [] }),
/Valor desconhecido não equivale a zero/,
);
assert.match(
render({ ...context, state: 'not_included' }),
/Não foi feita uma medição quantitativa/,
);
assert.match(
render({ ...context, state: 'unavailable' }),
/indisponíveis nesta versão/,
);
assert.match(render(null), /A carregar indicadores/);
assert.match(
render({ ...context, state: 'error' }),
/Não foi possível consultar os indicadores/,
);
});
test('indicator and inclusive temporal overlap filters preserve exact source values without mutation', () => {
const records = [
row,
{
...row,
id: 'prior',
period_start: '2024-01-01',
period_end: '2024-12-31',
},
{
...row,
id: 'production',
indicator: 'production',
value: '1234567890123.456789',
},
];
const before = JSON.stringify(records);
assert.deepEqual(
filterIndicators(records, 'employment', '2025-12-31', '2026-01-01').map(
(item) => item.id,
),
['1'],
);
assert.deepEqual(
filterIndicators(records, 'all', '', '2024-01-01').map(
(item) => item.id,
),
['prior'],
);
assert.equal(
filterIndicators(records, 'production', '2025-01-01', '').at(0).value,
'1234567890123.456789',
);
assert.equal(
filterIndicators(records, 'all', '2026-01-01', '2025-01-01').length,
0,
);
assert.equal(JSON.stringify(records), before);
});
test('missing, partial, invalid and inverted source periods are visible unfiltered but never inferred from source date', () => {
const records = [
{ ...row, id: 'missing', period_start: null },
{ ...row, id: 'partial', period_start: '2025' },
{ ...row, id: 'invalid', period_start: '2025-02-30' },
{ ...row, id: 'inverted', period_start: '2026-01-01' },
{ ...row, id: 'unknown', value: null, knowledge: 'unknown' },
{ ...row, id: 'disputed', knowledge: 'disputed' },
];
assert.equal(filterIndicators(records, 'all', '', '').length, 6);
assert.deepEqual(
filterIndicators(records, 'all', '2025-01-01', '2025-12-31').map(
(item) => item.id,
),
['unknown', 'disputed'],
);
assert.equal(
filterIndicators([row], 'all', '2026-01-15', '2026-01-15').length,
0,
);
});
test('quantitative filters are labelled, scoped and explain source date and coverage limits', () => {
const html = render(context);
assert.match(html, /Filtrar observações do contexto territorial/);
assert.match(html, /Período de referência — desde/);
assert.match(html, /Período de referência — até/);
assert.match(html, /A data da fonte não substitui o período de referência/);
assert.match(html, /não alteram oportunidades ou limites de cobertura/);
assert.match(html, /Limpar filtros dos indicadores/);
});