Files
Investidor-V2/tests/frontend/business-point-publication.test.mjs
T

427 lines
14 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 { createElement } 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(file, import.meta.url), 'utf8'),
{
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 locations = load('../../resources/js/lib/business-locations.ts');
const summaryModule = load(
'../../resources/js/components/business-point-summary.tsx',
{ '@/lib/business-locations': locations },
);
const Summary = summaryModule.default;
const sourceModule = load(
'../../resources/js/components/similar-businesses.tsx',
{ '@inertiajs/react': { Link: 'a' }, '@/lib/portal-api': {} },
);
const paths = [
{
province_id: 'qa-province',
district_id: 'qa-one',
admin_post_id: '',
locality_id: '',
},
{
province_id: 'qa-province',
district_id: 'qa-two',
admin_post_id: '',
locality_id: '',
},
];
const payload = {
title: 'QA private preparation',
wizard_version: '2',
wizard_step: '5',
geography_scope: 'multiple',
locations_json: JSON.stringify(paths),
location_unknown: 'no',
disclosure_precision: 'exact',
location_point_enabled: 'yes',
location_latitude: '-26.0001',
location_longitude: '32.0001',
location_point_source_mode: 'listing',
source_reference: '<QA source>',
source_date: '2026-10-02',
location_point_source_reference: 'Other private source',
location_point_source_date: '',
location_point_share_source: 'no',
};
function nodes(tree) {
if (!tree || typeof tree !== 'object') return [];
if (Array.isArray(tree)) return tree.flatMap(nodes);
return [tree, ...nodes(tree.props?.children)];
}
function harness(initial = payload) {
let cursor = 0;
const state = [],
requests = [];
const hooks = {
useState(initial) {
const index = cursor++;
if (!(index in state))
state[index] =
typeof initial === 'function' ? initial() : initial;
return [
state[index],
(value) => {
state[index] =
typeof value === 'function'
? value(state[index])
: value;
},
];
},
useRef(initial) {
const index = cursor++;
if (!(index in state)) state[index] = { current: initial };
return state[index];
},
useEffect() {},
useCallback(fn) {
return fn;
},
};
const active = {
geography_scope: ['specific', 'multiple'],
disclosure_precision: ['exact'],
location_unknown: ['no'],
location_point_enabled: ['yes'],
};
const schema = {
geography_scope: {
step: 5,
pt: 'Geographic scope',
en: 'Scope',
type: 'select',
max: 30,
options: {
multiple: 'Multiple',
specific: 'Specific',
national: 'National',
digital: 'Digital',
},
},
location_point_territory: {
step: 5,
pt: 'Primary site',
en: 'Primary site',
type: 'point_territory',
max: 150,
when: { ...active, geography_scope: ['multiple'] },
},
location_latitude: {
step: 5,
pt: 'Latitude',
en: 'Latitude',
type: 'decimal',
max: 16,
when: active,
},
location_longitude: {
step: 5,
pt: 'Longitude',
en: 'Longitude',
type: 'decimal',
max: 16,
when: active,
},
location_point_source_mode: {
step: 5,
pt: 'Coordinate source',
en: 'Coordinate source',
type: 'select',
max: 20,
options: { listing: 'Existing source', custom: 'Different source' },
when: active,
},
location_point_source_reference: {
step: 5,
pt: 'Point source',
en: 'Point source',
type: 'textarea',
max: 2000,
when: { ...active, location_point_source_mode: ['custom'] },
},
};
const record = {
id: 'qa-listing',
code: 'MNE-QA',
modality: 'company_sale',
classification: 'restricted',
version_no: 1,
payload: initial,
territory_paths: [
['QA province', 'QA one'],
['QA province', 'QA two'],
],
};
const Wizard = load(
'../../resources/js/pages/investment/business-wizard.tsx',
{
react: hooks,
'@inertiajs/react': { router: { visit() {}, reload() {} } },
'@/components/country-select': { default: () => null },
'@/components/business-point-summary': summaryModule,
'./shared': {
Frame: 'div',
Field: ({ label, children }) =>
createElement('label', null, label, children),
input: 'input',
button: 'button',
card: 'card',
api: async (url, body) => {
requests.push({ url, body });
return { ...record, payload: body.payload, version_no: 2 };
},
},
'./business-types': {
businessModalities: { company_sale: 'Sale' },
},
'./business-objects': { default: () => null },
'../../lib/business-locations': locations,
},
).default;
return {
render() {
cursor = 0;
return Wizard({
countries: [],
record,
formSchema: schema,
modalityFormType: 'company_sale',
modalities: ['company_sale'],
objects: [],
objectKinds: [],
sectors: [],
});
},
requests,
};
}
await test('the existing wizard requires an explicit primary path and keeps the selected path distinct from the first site', async () => {
const h = harness();
let tree = h.render();
const chooser = nodes(tree).find(
(node) =>
node.type === 'select' &&
nodes(node).some(
(child) =>
child.type === 'option' &&
child.props.value ===
locations.businessPointTerritoryKey(paths[1]),
),
);
assert.equal(chooser.props.value, '');
chooser.props.onChange({
target: { value: locations.businessPointTerritoryKey(paths[1]) },
});
tree = h.render();
const html = renderToStaticMarkup(tree);
assert.match(html, /QA two/);
assert.match(html, /Latitude \*/);
assert.match(html, /não publica o ponto/);
assert.match(html, /mantida no processo reservado/);
const save = nodes(tree).find(
(node) =>
node.type === 'button' && node.props.children === 'Guardar e sair',
);
await save.props.onClick();
assert.equal(
h.requests[0].url,
'/api/v1/existing-business/listings/qa-listing',
);
assert.equal(
h.requests[0].body.payload.location_point_territory,
locations.businessPointTerritoryKey(paths[1]),
);
assert.equal(h.requests[0].body.version_no, 1);
assert.equal(h.requests[0].body.payload.location_latitude, '-26.0001');
});
await test('switching source reuses existing fields and switching to national hides the supplement while preserving the private draft', async () => {
const h = harness();
let tree = h.render();
assert.equal(
nodes(tree).some((node) => node.type === 'textarea'),
false,
);
const source = nodes(tree).find(
(node) => node.type === 'select' && node.props.value === 'listing',
);
source.props.onChange({ target: { value: 'custom' } });
tree = h.render();
assert.equal(
nodes(tree).find((node) => node.type === 'textarea').props.value,
'Other private source',
);
const scope = nodes(tree).find(
(node) => node.type === 'select' && node.props.value === 'multiple',
);
scope.props.onChange({ target: { value: 'national' } });
tree = h.render();
assert.doesNotMatch(
renderToStaticMarkup(tree),
/Ponto em preparação|Other private source|Latitude WGS84/,
);
assert.equal(
nodes(tree).some((node) => node.type === 'textarea'),
false,
);
await nodes(tree)
.find(
(node) =>
node.type === 'button' &&
node.props.children === 'Guardar e sair',
)
.props.onClick();
assert.equal(h.requests[0].body.payload.location_latitude, '-26.0001');
assert.equal(
h.requests[0].body.payload.location_point_source_reference,
'Other private source',
);
assert.equal(
h.requests[0].body.payload.location_point_source_mode,
'custom',
);
assert.equal(h.requests[0].body.payload.geography_scope, 'national');
});
await test('stale point path stays visible for reconciliation instead of selecting another site', () => {
const h = harness({
...payload,
location_point_territory: 'qa-removed-site',
});
const html = renderToStaticMarkup(h.render());
assert.match(html, /Selecção guardada fora das localizações actuais/);
assert.match(html, /value="qa-removed-site" selected/);
});
await test('an incomplete earlier location does not change the index or name of a later selectable site', () => {
const h = harness({
...payload,
locations_json: JSON.stringify([
locations.emptyBusinessLocation(),
paths[1],
]),
});
const option = nodes(h.render()).find(
(node) =>
node.type === 'option' &&
node.props.value === locations.businessPointTerritoryKey(paths[1]),
);
const html = renderToStaticMarkup(option);
assert.match(html, /Localização 2/);
assert.match(html, /QA two/);
assert.doesNotMatch(html, /QA one/);
assert.equal(
locations.businessPointTerritoryLabel(
{
...payload,
location_point_territory: locations.businessPointTerritoryKey(
paths[1],
),
},
[['QA one'], ['QA two']],
),
'QA two',
);
});
await test('summary distinguishes registered, prepared and published data, escapes the reused source and reports an unavailable supplement', () => {
const registered = {
id: 'qa-existing',
is_public: false,
precision_level: 'exact',
has_geometry: true,
latitude: -27,
longitude: 33,
};
let html = renderToStaticMarkup(
Summary({ payload, registered, available: false }),
);
assert.match(html, /Latitude -27; longitude/);
assert.match(html, /Ponto em preparação/);
assert.match(html, /&lt;QA source&gt;/);
assert.match(html, /ainda não está instalado/);
assert.match(html, /substitui o primário cadastrado/);
html = renderToStaticMarkup(
Summary({ payload, registered, published: true }),
);
assert.match(html, /Ponto declarado na publicação/);
assert.match(html, /não reescreve o histórico/);
assert.equal(
Summary({ payload: { ...payload, location_unknown: 'yes' } }),
null,
);
html = renderToStaticMarkup(
Summary({
payload: { ...payload, location_point_enabled: 'no' },
registered,
}),
);
assert.match(
html,
/cadastro existente é conservado|cadastro\s+existente é conservado/,
);
assert.doesNotMatch(html, /-26.0001|&lt;QA source&gt;/);
});
await test('public source dates remain calendar dates in a western timezone and unknown dates are not replaced by the registration time', () => {
const previous = process.env.TZ;
try {
process.env.TZ = 'Pacific/Honolulu';
const html = renderToStaticMarkup(
sourceModule.BusinessLocationSource({
record: {
location_source_reference: '<Public source>',
location_source_date: '2026-10-02',
location_recorded_at: '2026-11-02T10:00:00Z',
},
showRecorded: true,
}),
);
assert.match(html, /&lt;Public source&gt;/);
assert.ok(
html.includes(
new Date('2026-10-02T00:00:00Z').toLocaleDateString('pt-MZ', {
timeZone: 'UTC',
}),
),
);
const unknown = renderToStaticMarkup(
sourceModule.BusinessLocationSource({
record: { location_recorded_at: '2026-11-02T10:00:00Z' },
}),
);
assert.match(unknown, /Data da fonte: desconhecida/);
assert.doesNotMatch(unknown, /2026/);
} finally {
if (previous === undefined) delete process.env.TZ;
else process.env.TZ = previous;
}
});