Files
2026-09-02 12:40:49 +03:00

109 lines
3.6 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { MenuConfig } from '#app/config/types';
import { firstMenuPath, getEnabledMenuItems, getPermittedMenuItems } from '#app/layouts/shared/lib/menu';
const menu: MenuConfig = [
{ title: 'Dashboard', path: '/dashboard' },
{
title: 'Access control',
children: [
{ title: 'Admins', path: '/access-control/admin', permissions: ['admins.view'] },
{ title: 'Roles', path: '/access-control/role', permissions: ['role.view'] }
]
},
{
title: 'Reports',
permissions: ['reports.view'],
children: [{ title: 'Sales', path: '/reports/sales', permissions: ['reports.view'] }]
}
];
describe('getPermittedMenuItems', () => {
it('keeps entries that declare no permissions', () => {
expect(getPermittedMenuItems(menu, []).map((item) => item.title)).toEqual(['Dashboard']);
});
it('keeps a section for the children the visitor may reach', () => {
const [, accessControl] = getPermittedMenuItems(menu, ['role.view']);
expect(accessControl.title).toBe('Access control');
expect(accessControl.children?.map((item) => item.path)).toEqual(['/access-control/role']);
});
it('drops a section whose children were all filtered out', () => {
const titles = getPermittedMenuItems(menu, ['reports.view']).map((item) => item.title);
expect(titles).toEqual(['Dashboard', 'Reports']);
expect(getPermittedMenuItems(menu, []).map((item) => item.title)).not.toContain('Reports');
});
it('requires every declared permission, not just one', () => {
const guarded: MenuConfig = [
{ title: 'Create page', path: '/pages/create', permissions: ['pages.view', 'pages.create'] }
];
expect(getPermittedMenuItems(guarded, ['pages.view'])).toEqual([]);
expect(getPermittedMenuItems(guarded, ['pages.view', 'pages.create'])).toHaveLength(1);
});
it('leaves the source configuration untouched', () => {
getPermittedMenuItems(menu, ['role.view']);
expect(menu[1].children).toHaveLength(2);
});
});
describe('firstMenuPath', () => {
it('returns the path an entry declares itself', () => {
expect(firstMenuPath({ title: 'Dashboard', path: '/dashboard' })).toBe('/dashboard');
});
it('descends to the first reachable path of a section', () => {
expect(firstMenuPath(menu[1])).toBe('/access-control/admin');
});
it('returns undefined when nothing inside is linkable', () => {
expect(firstMenuPath({ title: 'Empty', children: [{ title: 'Also empty' }] })).toBeUndefined();
});
});
describe('getEnabledMenuItems', () => {
const withDisabled: MenuConfig = [
{ title: 'Dashboard', path: '/dashboard' },
{
title: 'Web Studio',
children: [
{ title: 'Page listing', path: '/web-studio/pages/listing' },
{ title: 'Edit page', path: '/web-studio/pages/-/', disabled: true }
]
},
{
title: 'Coming soon',
children: [{ title: 'Not built', path: '/soon', disabled: true }]
}
];
it('removes a disabled entry from its section', () => {
const [, webStudio] = getEnabledMenuItems(withDisabled);
expect(webStudio.children?.map((item) => item.title)).toEqual(['Page listing']);
});
it('removes a section left with nothing but disabled entries', () => {
expect(getEnabledMenuItems(withDisabled).map((item) => item.title)).toEqual([
'Dashboard',
'Web Studio'
]);
});
it('leaves a menu without disabled entries untouched', () => {
expect(getEnabledMenuItems(menu)).toEqual(menu);
});
it('leaves the source configuration untouched', () => {
getEnabledMenuItems(withDisabled);
expect(withDisabled[1].children).toHaveLength(2);
});
});