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); }); });