From 6e439def0245b71d2579dbf0f5fd7a91a55abfc8 Mon Sep 17 00:00:00 2001 From: nala Date: Tue, 20 Oct 2026 15:12:17 +0200 Subject: [PATCH] Reconciliar verificacoes com navegacao e relatorios aprovados --- resources/js/lib/portal-api.ts | 2 +- tests/frontend/portal-api.test.mjs | 15 +++++++++++++++ tests/frontend/portal-navigation.test.mjs | 17 ++++++++++++++--- tests/frontend/project-dossier-report.test.mjs | 14 +++++++++----- tests/frontend/simulator-entry.test.mjs | 6 +++--- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/resources/js/lib/portal-api.ts b/resources/js/lib/portal-api.ts index 3773f36d..5ad64e14 100644 --- a/resources/js/lib/portal-api.ts +++ b/resources/js/lib/portal-api.ts @@ -114,7 +114,7 @@ export async function portalRequest( const headers = new Headers(options.headers); headers.set('Accept', 'application/json'); if (options.method && !['GET', 'HEAD'].includes(options.method.toUpperCase())) { - const token = document.cookie.split('; ').find(value => value.startsWith('XSRF-TOKEN='))?.slice(11); + const token = typeof document === 'undefined' ? undefined : document.cookie.split('; ').find(value => value.startsWith('XSRF-TOKEN='))?.slice(11); if (token) headers.set('X-XSRF-TOKEN', decodeURIComponent(token)); } const response = await fetch(url, { diff --git a/tests/frontend/portal-api.test.mjs b/tests/frontend/portal-api.test.mjs index 5b3e340c..ac1d42ad 100644 --- a/tests/frontend/portal-api.test.mjs +++ b/tests/frontend/portal-api.test.mjs @@ -144,6 +144,21 @@ await test('fetch accepts a real server confirmation', async (t) => { assert.equal(response.data.reference, 'IR-SERVER-001'); }); +await test('browser writes preserve the CSRF cookie header', async (t) => { + const previous = Object.getOwnPropertyDescriptor(globalThis, 'document'); + Object.defineProperty(globalThis, 'document', { configurable: true, value: { cookie: 'other=x; XSRF-TOKEN=qa%2Ftoken' } }); + try { + t.mock.method(globalThis, 'fetch', async (_url, options) => { + assert.equal(options.headers.get('X-XSRF-TOKEN'), 'qa/token'); + return new Response('{"success":true}', { status: 201 }); + }); + await portalRequest('/api/v1/interest-requests', { method: 'POST' }); + } finally { + if (previous) Object.defineProperty(globalThis, 'document', previous); + else delete globalThis.document; + } +}); + for (const status of [422, 429, 500]) { await test(`HTTP ${status} never becomes a successful confirmation`, async (t) => { t.mock.method( diff --git a/tests/frontend/portal-navigation.test.mjs b/tests/frontend/portal-navigation.test.mjs index b5b68c71..a67365ed 100644 --- a/tests/frontend/portal-navigation.test.mjs +++ b/tests/frontend/portal-navigation.test.mjs @@ -2,6 +2,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; import { portalNavigation, + legacyPortalNavigation, currentNavigationGroup, currentNavigationItem, } from '../../resources/js/lib/portal-navigation.ts'; @@ -15,7 +16,7 @@ void test('public and authenticated navigation share groups without revealing ma assert.deepEqual(publicGroups.slice(0, 3), investor.slice(0, 3)); assert.deepEqual( publicGroups.map((g) => g.id), - ['explore', 'prepare', 'support', 'partnerships', 'account'], + ['discovery', 'realization', 'support', 'account'], ); const hrefs = JSON.stringify(investor); assert.equal(hrefs.includes('/management/'), false); @@ -32,7 +33,7 @@ void test('management and optional modules follow permissions and feature flags' education: true, ai: true, }); - assert.equal(groups.at(-1).id, 'management'); + assert.ok(groups.some((group) => group.id === 'management')); assert.equal(JSON.stringify(groups).includes('/investidor/negocios'), true); assert.equal(JSON.stringify(groups).includes('/management/ai'), true); assert.equal( @@ -48,7 +49,9 @@ void test('management and optional modules follow permissions and feature flags' }); void test('detail URLs and filtered catalogue pages retain the current group', () => { - const groups = portalNavigation({ authenticated: true, manager: true }); + // Preserve the retained catalogue's detail/filter matching independently + // of the reconciled shell's new grouping names. + const groups = legacyPortalNavigation({ authenticated: true, manager: true }); for (const url of [ '/carteira/project/fixture', '/zonas?category=zee', @@ -81,3 +84,11 @@ void test('detail URLs and filtered catalogue pages retain the current group', ( 'zee', ); }); + +void test('reconciled navigation selects discovery, realization and private support correctly', () => { + const groups = portalNavigation({ authenticated: false, manager: false }); + assert.equal(currentNavigationGroup(groups, '/oportunidades/fixture'), 'discovery'); + assert.equal(currentNavigationGroup(groups, '/simulador-fiscal'), 'realization'); + assert.equal(currentNavigationGroup(groups, '/balcao-virtual/dossiers/fixture'), 'support'); + assert.equal(currentNavigationGroup(groups, '/management/crm/leads'), null); +}); diff --git a/tests/frontend/project-dossier-report.test.mjs b/tests/frontend/project-dossier-report.test.mjs index 69e4ffdb..b57cd798 100644 --- a/tests/frontend/project-dossier-report.test.mjs +++ b/tests/frontend/project-dossier-report.test.mjs @@ -114,15 +114,19 @@ void test('business plan values in the dossier equal the existing simulator fore const finance = report.sections.find((section) => section.id === 'finance'); const row = (field) => finance.rows.find((item) => item.field === field).value; + const numeric = (value) => { + assert.match(value, /MZN$/); + return value.replace(/\s|MZN/g, '').replace(',', '.'); + }; assert.equal( - row('Facturação no horizonte'), - `${expected.totalRevenue} MZN`, + numeric(row('Facturação no horizonte')), + expected.totalRevenue, ); assert.equal( - row('Resultado antes de impostos'), - `${expected.totalProfitBeforeTax} MZN`, + numeric(row('Resultado antes de impostos')), + expected.totalProfitBeforeTax, ); - assert.equal(row('Caixa final projectada'), `${expected.endingCash} MZN`); + assert.equal(numeric(row('Caixa final projectada')), expected.endingCash); assert.equal(row('Reconciliação do financiamento'), expected.fundingStatus); }); diff --git a/tests/frontend/simulator-entry.test.mjs b/tests/frontend/simulator-entry.test.mjs index a81b848b..0e6899ea 100644 --- a/tests/frontend/simulator-entry.test.mjs +++ b/tests/frontend/simulator-entry.test.mjs @@ -26,8 +26,8 @@ void test('portal home hero exposes a direct public simulator link', () => { void test('completed simulator result keeps public interest submission independent', () => { assert.match(simulator, /href="\/manifestar-interesse"/); assert.match( - simulator, - /Manifestar interesse sem partilhar\s+esta simulação/, + simulator.replace(/\s+/g, ' '), + /Manifestar interesse sem partilhar esta simulação/, ); - assert.match(simulator, /sem\s+partilhar os dados desta simulação/); + assert.match(simulator.replace(/\s+/g, ' '), /sem partilhar os dados desta simulação/); });