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(file, mocks) { const output = ts.transpileModule( fs .readFileSync( new URL( '../../resources/js/components/' + file, import.meta.url, ), 'utf8', ) .replaceAll('import.meta.env.VITE_MAP_TILE_URL', 'undefined'), { 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; } const polygon = (x, y) => ({ type: 'Polygon', coordinates: [ [ [x, y], [x + 1, y], [x + 1, y + 1], [x, y + 1], [x, y], ], ], }); const boundary = { geometry: polygon(30, -20), title: 'Província sintética', purpose: 'administrative_boundary', selected_territory: true, location_type: 'province', url: '/territorio', source_reference: 'Fonte sintética', source_date: '2026-01-01', }; const point = { geometry: { coordinates: [40, -10] }, properties: { id: 1, title: 'Investimento sintético', url: '/oportunidades/1', location: 'Local confirmado', }, }; const flatten = (node) => !node || typeof node !== 'object' ? [] : [ node, ...React.Children.toArray(node.props.children).flatMap(flatten), ]; await test('catalogue composes one map with the same opportunity data and separate administrative context, including no selected territory', () => { const Map = () => null, Info = () => null; const requests = []; const Component = load('catalogue-investment-map.tsx', { './interactive-investment-map': { default: Map }, './territory-geometry-preview': { TerritoryGeometryDetails: Info }, './use-territory-geometry': { useTerritoryGeometry: (id, endpoint, children) => { assert.equal(endpoint, undefined); assert.equal(children, true); requests.push(id); return { context: { areas: [boundary] }, error: '' }; }, }, }).default; const points = [point], areas = []; const tree = Component({ points, areas, territoryId: 'province-1' }); assert.equal(tree.type, Map); assert.equal(tree.props.points, points); assert.equal(tree.props.areas, areas); assert.deepEqual(tree.props.administrativeAreas, [boundary]); assert.equal(tree.props.layerControls, true); assert.equal(tree.props.administrativeInfo.type, Info); assert.deepEqual(requests, ['province-1']); assert.equal( Component({ points: [], areas }).props.administrativeInfo, undefined, ); }); await test('territorial request changes discard old boundaries immediately and ignore late responses; clearing selection removes context', async () => { const previousFetch = globalThis.fetch; const pending = [], memory = []; let cursor = 0, effect; const hooks = { useState(initial) { const index = cursor++; if (!(index in memory)) memory[index] = initial; return [memory[index], (value) => (memory[index] = value)]; }, useEffect(fn) { effect = fn; }, }; globalThis.fetch = (url, options) => new Promise((resolve) => pending.push({ url, options, resolve })); try { const { useTerritoryGeometry } = load('use-territory-geometry.ts', { react: hooks, }); const render = (id, children = false) => { cursor = 0; return useTerritoryGeometry(id, undefined, children); }; render('a'); const cleanup = effect(); cleanup(); render('b'); effect(); pending[0].resolve({ ok: true, json: async () => ({ areas: [boundary] }), }); await new Promise((resolve) => setImmediate(resolve)); assert.equal(render('b').context, null); pending[1].resolve({ ok: true, json: async () => ({ areas: [{ ...boundary, title: 'B' }] }), }); await new Promise((resolve) => setImmediate(resolve)); assert.equal(render('b').context.areas[0].title, 'B'); assert.equal(render('c').context, null); assert.equal(render(undefined).context, null); assert.equal(pending[0].options.signal.aborted, true); assert.equal(pending.length, 2); assert.equal(render('b', true).context, null); effect(); assert.match(pending[2].url, /include_children=1/); pending[2].resolve({ ok: true, json: async () => ({ areas: [boundary], child_coverage: { registered_count: 2 }, }), }); await new Promise((resolve) => setImmediate(resolve)); assert.equal( render('b', true).context.child_coverage.registered_count, 2, ); assert.equal(render('b', false).context, null); } finally { globalThis.fetch = previousFetch; } }); await test('Leaflet uses one instance, independent layers and selected boundary bounds; restoring hidden points from the list never creates positions', async () => { const previousDocument = globalThis.document, previousObserver = globalThis.ResizeObserver; let effect, overlayOptions, overlayEvents, fitted; const refs = [], instances = [], groups = []; globalThis.document = { createElement: () => ({ append() {}, textContent: '', style: {} }), }; globalThis.ResizeObserver = class { observe() {} disconnect() {} }; const instance = { setView() { return this; }, fitBounds(value) { fitted = value; }, on(_event, fn) { overlayEvents = fn; return this; }, addLayer(group) { group.visible = true; return this; }, invalidateSize() {}, remove() {}, }; const layer = () => ({ addTo(group) { group.items.push(this); return this; }, bindPopup() { return this; }, on() { return this; }, getLatLng() { return [ point.geometry.coordinates[1], point.geometry.coordinates[0], ]; }, openPopup() {}, }); const L = { map() { instances.push(instance); return instance; }, tileLayer: () => ({ on() { return this; }, addTo() {}, }), layerGroup() { const group = { items: [], addTo() { this.visible = true; return this; }, }; groups.push(group); return group; }, control: { scale: () => ({ addTo() {} }), layers(_base, overlays) { overlayOptions = overlays; return { addTo() {} }; }, }, geoJSON(geometry) { const item = layer(); const coords = geometry.coordinates[0]; item.getBounds = () => ({ isValid: () => true, getSouth: () => Math.min(...coords.map((c) => c[1])), getNorth: () => Math.max(...coords.map((c) => c[1])), getWest: () => Math.min(...coords.map((c) => c[0])), getEast: () => Math.max(...coords.map((c) => c[0])), }); return item; }, divIcon: (value) => value, marker: () => layer(), }; const hooks = { ...React, useMemo: (fn) => fn(), useState: (initial) => [initial, () => {}], useRef(initial) { const ref = { current: refs.length === 0 ? {} : initial }; refs.push(ref); return ref; }, useEffect(fn) { effect = fn; }, }; try { const Component = load('interactive-investment-map.tsx', { react: hooks, leaflet: L, 'leaflet/dist/leaflet.css': {}, }).default; const area = { ...boundary, title: 'Área sintética', purpose: 'investment_area', selected_territory: false, }; const tree = Component({ points: [point], areas: [area, boundary], administrativeAreas: [boundary], layerControls: true, }); const cleanup = effect(); await new Promise((resolve) => setImmediate(resolve)); assert.equal(instances.length, 1); assert.equal(groups[0].items.length, 1); assert.equal(groups[1].items.length, 1); assert.equal(groups[2].items.length, 1); // Same boundary from both APIs is not doubled. assert.deepEqual(fitted, [ [-20, 30], [-19, 31], ]); // No national parent/outside point overrides the selection. assert.equal(Object.keys(overlayOptions).length, 3); overlayEvents({ layer: groups[0], type: 'overlayremove' }); assert.equal(refs[4].current.points, false); const button = flatten(tree).find((n) => n.type === 'button'); button.props.onClick(); assert.equal(groups[0].visible, true); assert.equal(refs[4].current.points, true); assert( flatten(tree).some( (n) => n.type === 'a' && n.props.href === '/territorio', ), ); assert(flatten(tree).some((n) => n.type === 'details')); cleanup(); refs.length = 0; groups.length = 0; Component({ points: [], areas: [], administrativeAreas: [ { ...boundary, selected_territory: false }, { ...boundary, geometry: polygon(0, 0), selected_territory: false, location_type: 'country', title: 'País sintético', }, ], layerControls: true, }); const cleanupEmpty = effect(); await new Promise((resolve) => setImmediate(resolve)); assert.equal(groups[0].items.length, 0); assert.deepEqual(fitted, [ [-20, 30], [-19, 31], ]); cleanupEmpty(); } finally { globalThis.document = previousDocument; globalThis.ResizeObserver = previousObserver; } }); await test('administrative failure does not remove investment results or add another map', () => { const Map = () => null, Info = () => null; const Component = load('catalogue-investment-map.tsx', { './interactive-investment-map': { default: Map }, './territory-geometry-preview': { TerritoryGeometryDetails: Info }, './use-territory-geometry': { useTerritoryGeometry: () => ({ context: null, error: 'Limites indisponíveis', }), }, }).default; const points = [point]; const tree = Component({ points, areas: [], territoryId: 'a' }); assert.equal(tree.type, Map); assert.equal(tree.props.points, points); assert.equal(tree.props.administrativeAreas, undefined); assert(flatten(tree.props.notice).some((n) => n.props.role === 'alert')); }); await test('declared parcels and documented source locations are never labelled as administrative boundaries or investments', () => { const hooks = { ...React, useMemo: (fn) => fn(), useState: (value) => [value, () => {}], useRef: (value) => ({ current: value }), useEffect: () => {}, }; const Component = load('interactive-investment-map.tsx', { react: hooks, 'leaflet/dist/leaflet.css': {}, }).default; const parcel = renderToStaticMarkup( Component({ points: [], areas: [ { ...boundary, purpose: 'declared_parcel', title: 'Parcela sintética', }, ], contextOnly: true, mapKind: 'declared_parcel', }), ); assert.match(parcel, />Perímetro declarado pelo Promotor<\/h2>/); assert.match(parcel, /não é limite administrativo/); assert.doesNotMatch( parcel, />Contexto administrativo<\/h2>|limites administrativos disponíveis/, ); const documented = renderToStaticMarkup( Component({ points: [], areas: [], mapKind: 'documented_locations', layerControls: true, }), ); assert.match(documented, />Mapa dos locais documentados<\/h2>/); assert.match(documented, /Local documentado com coordenadas/); assert.doesNotMatch(documented, /Oportunidade com coordenadas confirmadas/); const administrative = renderToStaticMarkup( Component({ points: [], areas: [boundary], contextOnly: true }), ); assert.match(administrative, />Contexto administrativo<\/h2>/); }); await test('partnership catalogue has one contextual map and keeps the applied territory while filter edits are pending', () => { let draft; const hooks = { ...React, useState(initial) { draft ??= initial; return [ draft, (value) => { draft = value; }, ]; }, }; const Map = () => null; const Page = load('../pages/partnerships/catalogue.tsx', { react: hooks, '@inertiajs/react': { Link: 'a', router: { get() {} } }, '@/components/catalogue-investment-map': { default: Map }, './shared': { Frame: 'main', Field: 'label', control: '', button: '' }, }).default; const props = { types: {}, sectors: [], territories: [{ id: 'a', name: 'A', location_type: 'province' }], filters: { territory_id: 'a' }, mapPoints: [point], results: { data: [], total: 0, prev_page_url: null, next_page_url: null, }, }; let tree = Page(props); const maps = () => flatten(tree).filter((node) => node.type === Map); assert.equal(maps().length, 1); assert.equal(maps()[0].props.mapKind, 'documented_locations'); assert.equal(maps()[0].props.points, props.mapPoints); flatten(tree) .find((node) => node.type === 'select' && node.props.value === 'a') .props.onChange({ target: { value: 'b' } }); tree = Page(props); assert.equal(maps()[0].props.territoryId, 'a'); assert.equal(maps()[0].props.points, props.mapPoints); }); await test('administrative coverage identifies lower-level subdivisions and distinguishes absent geometry from absent territory', () => { const Details = load('territory-geometry-preview.tsx', { './interactive-investment-map': { default: () => null }, './use-territory-geometry': {}, }).TerritoryGeometryDetails; const html = renderToStaticMarkup( React.createElement(Details, { error: '', context: { selected_geometry_available: false, context_geometry_count: 1, truncated: true, child_coverage: { location_type: 'district', registered_count: 3, represented_count: 1, }, areas: [ { ...boundary, selected_territory: false, child_territory: true, }, ], }, }), ); assert.match(html, /1 de 3 referências acessíveis/); assert.match(html, /Subdivisão do território seleccionado/); assert.match(html, /não significa inexistência do território/); assert.match(html, /100 geometrias e 500 subdivisões/); assert.doesNotMatch(html, /apenas limites dos níveis superiores/); });