199 lines
9.3 KiB
JavaScript
199 lines
9.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
catalogueDetailHref,
|
|
catalogueReturnHref,
|
|
} from '../../resources/js/lib/catalogue-context.ts';
|
|
import {
|
|
portalNavigation,
|
|
legacyPortalNavigation,
|
|
currentNavigationGroup,
|
|
currentNavigationItem,
|
|
canShowLegacyManagement,
|
|
} from '../../resources/js/lib/portal-navigation.ts';
|
|
|
|
void test('public and authenticated navigation share groups without revealing management', () => {
|
|
const publicGroups = portalNavigation({
|
|
authenticated: false,
|
|
manager: false,
|
|
});
|
|
const investor = portalNavigation({ authenticated: true, manager: false });
|
|
assert.deepEqual(publicGroups.slice(0, 3), investor.slice(0, 3));
|
|
assert.deepEqual(
|
|
publicGroups.map((g) => g.id),
|
|
['discovery', 'realization', 'support', 'work', 'account'],
|
|
);
|
|
const hrefs = JSON.stringify(investor);
|
|
assert.equal(hrefs.includes('/management/'), false);
|
|
assert.equal(JSON.stringify(publicGroups).includes('/login'), true);
|
|
assert.equal(hrefs.includes('/investidor/profile'), true);
|
|
assert.equal(hrefs.includes('/balcao-virtual'), true);
|
|
});
|
|
|
|
void test('management and optional modules follow permissions and feature flags', () => {
|
|
const groups = portalNavigation({
|
|
authenticated: true,
|
|
manager: true,
|
|
businesses: true,
|
|
education: true,
|
|
ai: true,
|
|
});
|
|
assert.ok(groups.some((group) => group.id === 'work'));
|
|
assert.equal(JSON.stringify(groups).includes('/investidor/negocios'), true);
|
|
assert.equal(JSON.stringify(groups).includes('/management/ai'), true);
|
|
assert.equal(
|
|
JSON.stringify(
|
|
portalNavigation({
|
|
authenticated: false,
|
|
manager: false,
|
|
education: false,
|
|
}),
|
|
).includes('/educacao'),
|
|
false,
|
|
);
|
|
});
|
|
|
|
void test('detail URLs and filtered catalogue pages retain the current group', () => {
|
|
// 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',
|
|
'/zonas/fixture',
|
|
'/oportunidades/fixture',
|
|
])
|
|
assert.equal(currentNavigationGroup(groups, url), 'explore');
|
|
assert.equal(
|
|
currentNavigationGroup(
|
|
groups,
|
|
'/balcao-virtual/dossiers/fixture?phase=market',
|
|
),
|
|
'support',
|
|
);
|
|
assert.equal(
|
|
currentNavigationGroup(groups, '/investidor/profile'),
|
|
'account',
|
|
);
|
|
assert.equal(
|
|
currentNavigationGroup(groups, '/management/crm/leads/fixture'),
|
|
'management',
|
|
);
|
|
assert.equal(currentNavigationGroup(groups, '/unknown'), null);
|
|
assert.equal(
|
|
currentNavigationItem(groups, '/carteira?q=QA&kind=project'),
|
|
'projects',
|
|
);
|
|
assert.equal(
|
|
currentNavigationItem(groups, '/zonas?category=zee&q=QA'),
|
|
'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'),
|
|
'discovery',
|
|
);
|
|
assert.equal(
|
|
currentNavigationGroup(groups, '/balcao-virtual/dossiers/fixture'),
|
|
'support',
|
|
);
|
|
assert.equal(currentNavigationGroup(groups, '/management/crm/leads'), null);
|
|
});
|
|
|
|
void test('discovery keeps four entry points and catalogue destinations without duplicates', () => {
|
|
const groups = portalNavigation({
|
|
authenticated: false,
|
|
manager: false,
|
|
businesses: true,
|
|
education: true,
|
|
});
|
|
const links = [];
|
|
const collect = (items) =>
|
|
items.forEach((item) => {
|
|
if (item.href) links.push(item.href);
|
|
if (item.children) collect(item.children);
|
|
});
|
|
collect(groups);
|
|
assert.equal(new Set(links).size, links.length);
|
|
for (const href of [
|
|
'/investimentos/iniciar',
|
|
'/oportunidades',
|
|
'/sectores',
|
|
'/oportunidades?view=map',
|
|
'/carteira',
|
|
'/zonas',
|
|
'/negocios',
|
|
'/educacao',
|
|
'/documentos',
|
|
'/legislacao',
|
|
])
|
|
assert.ok(links.includes(href), href);
|
|
});
|
|
|
|
void test('catalogue detail and return preserve the search inside the local catalogue', () => {
|
|
const query = 'q=turismo+em+Inhambane&kind=cluster&province_id=fixture';
|
|
const href = catalogueDetailHref('/carteira/fixture', query);
|
|
assert.equal(catalogueReturnHref('/carteira', href), '/carteira?' + query);
|
|
assert.equal(
|
|
catalogueReturnHref(
|
|
'/zonas',
|
|
'/zonas/fixture?catalogue_query=https%3A%2F%2Foutside.invalid',
|
|
),
|
|
'/zonas?https%3A%2F%2Foutside.invalid=',
|
|
);
|
|
});
|
|
|
|
const destinations=(items)=>items.flatMap(item=>[...(item.href?[item.href]:[]),...destinations(item.children??[])]);
|
|
void test('both menu consumers respect the existing scoped server decision rather than generic manager status',()=>{
|
|
assert.equal(canShowLegacyManagement({user:{id:1},canManagePortal:true,legacyManagementAccess:false}),false);
|
|
assert.equal(canShowLegacyManagement({user:{id:1},canManagePortal:true,legacyManagementAccess:true}),true);
|
|
assert.equal(canShowLegacyManagement({user:null,canManagePortal:true,legacyManagementAccess:true}),false);
|
|
assert.equal(canShowLegacyManagement({user:{id:1},canManagePortal:true}),true);
|
|
});
|
|
void test('work entry is discoverable without exposing private capabilities to an anonymous visitor',()=>{
|
|
const groups=portalNavigation({authenticated:false,manager:true,contentManagementHref:'/gestao-conteudos',contentManagementAccess:{opportunities:{index:'/management/opportunities',create:'/management/opportunities/create'}},scopedAdministration:[{label:'Private QA administration',href:'/management/secret'}]});
|
|
assert.deepEqual(destinations(groups.find(g=>g.id==='work').children),['/acesso-gestao']);
|
|
assert.equal(JSON.stringify(groups).includes('/management/'),false);
|
|
assert.equal(JSON.stringify(groups).includes('/gestao-conteudos'),false);
|
|
});
|
|
void test('work area exposes existing content capability even when the old dashboard flag has no menu object',()=>{
|
|
const groups=portalNavigation({authenticated:true,manager:true,opportunityManagement:null,contentManagementHref:'/gestao-conteudos',contentManagementAccess:{opportunities:{index:'/management/opportunities',create:'/management/opportunities/create'}}});
|
|
const work=groups.find(g=>g.id==='work');
|
|
assert.equal(work.label,'Área de trabalho');
|
|
assert.ok(work.children.some(i=>i.label==='Preparar análise e ideia'&&i.href==='/management/opportunities/create?analysis=proactive'));
|
|
assert.ok(work.children.some(i=>i.href==='/gestao-conteudos'));
|
|
assert.ok(work.children.some(i=>i.href==='/management/opportunities'));
|
|
});
|
|
void test('every previously authorised management/profile destination survives the regrouping exactly once',()=>{
|
|
const access={authenticated:true,manager:true,businesses:true,education:true,ai:true,contentManagementHref:'/gestao-conteudos',profileAccess:{contexts:[],mine:'/minhas-oportunidades',create:'/minhas-oportunidades/create',request:'/perfil/promotor',review:null,audit:'/perfis/auditoria',administration:'/perfis/gestao'},scopedAdministration:[{label:'Referências económicas',href:'/management/economic-references/lot-01'}]};
|
|
const old=destinations(legacyPortalNavigation(access).filter(g=>['management','profile-opportunities'].includes(g.id)));
|
|
const groups=portalNavigation(access);const after=destinations(groups);
|
|
for(const href of old)assert.equal(after.filter(x=>x===href).length,1,href);
|
|
assert.equal(after.length,new Set(after).size);
|
|
assert.ok(destinations(groups.find(g=>g.id==='work').children).includes('/management/economic-references/lot-01'));
|
|
assert.equal(after.filter(href=>href==='/management/profile-install').length,1,'profile installation already present in the old authorised sidebar must survive');
|
|
});
|
|
void test('ordinary authenticated account has an explanation entry and does not acquire creation or management links',()=>{
|
|
const groups=portalNavigation({authenticated:true,manager:false});
|
|
assert.deepEqual(destinations(groups.find(g=>g.id==='work').children),['/acesso-gestao']);
|
|
assert.equal(JSON.stringify(groups).includes('analysis=proactive'),false);
|
|
assert.equal(JSON.stringify(groups).includes('/management/'),false);
|
|
});
|
|
void test('direct analysis and ordinary creation preserve distinct selection state in the same work group',()=>{
|
|
const access={authenticated:true,manager:false,profileAccess:{contexts:[],mine:'/minhas-oportunidades',create:'/minhas-oportunidades/create',request:'/perfil/promotor',review:null,administration:null}};
|
|
const groups=portalNavigation(access);
|
|
assert.equal(currentNavigationGroup(groups,'/minhas-oportunidades/create?analysis=proactive'),'work');
|
|
assert.equal(currentNavigationItem(groups,'/minhas-oportunidades/create?analysis=proactive'),'prepare-analysis');
|
|
assert.equal(currentNavigationItem(groups,'/minhas-oportunidades/create'),'new-my-opportunity');
|
|
});
|