diff --git a/frontend/src/routes/admin/EntityNav.svelte.test.ts b/frontend/src/routes/admin/EntityNav.svelte.test.ts new file mode 100644 index 00000000..d4c6fbd9 --- /dev/null +++ b/frontend/src/routes/admin/EntityNav.svelte.test.ts @@ -0,0 +1,89 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page as browserPage } from 'vitest/browser'; + +const mockPage = { url: new URL('http://localhost/admin/users') }; + +vi.mock('$app/state', () => ({ + get page() { + return mockPage; + } +})); + +afterEach(cleanup); + +async function loadComponent() { + return (await import('./EntityNav.svelte')).default; +} + +const baseProps = (overrides: Record = {}) => ({ + userCount: 5, + groupCount: 3, + tagCount: 12, + inviteCount: 1, + canManageUsers: true, + canManageTags: true, + canManagePermissions: true, + canRunMaintenance: true, + ...overrides +}); + +describe('EntityNav', () => { + it('renders all sections when all permissions are granted', async () => { + mockPage.url = new URL('http://localhost/admin/users'); + const EntityNav = await loadComponent(); + render(EntityNav, { props: baseProps() }); + + const links = document.querySelectorAll('a[href^="/admin/"]'); + // Sidebar renders: users, groups, invites, tags, system, ocr — 6 links + expect(links.length).toBeGreaterThanOrEqual(6); + }); + + it('hides users / invites links when canManageUsers is false', async () => { + mockPage.url = new URL('http://localhost/admin/groups'); + const EntityNav = await loadComponent(); + render(EntityNav, { props: baseProps({ canManageUsers: false }) }); + + const userLinks = document.querySelectorAll('a[href="/admin/users"]'); + const inviteLinks = document.querySelectorAll('a[href="/admin/invites"]'); + expect(userLinks.length).toBe(0); + expect(inviteLinks.length).toBe(0); + }); + + it('hides the groups link when canManagePermissions is false', async () => { + mockPage.url = new URL('http://localhost/admin/users'); + const EntityNav = await loadComponent(); + render(EntityNav, { props: baseProps({ canManagePermissions: false }) }); + + const groupLinks = document.querySelectorAll('a[href="/admin/groups"]'); + expect(groupLinks.length).toBe(0); + }); + + it('hides the tags link when canManageTags is false', async () => { + mockPage.url = new URL('http://localhost/admin/users'); + const EntityNav = await loadComponent(); + render(EntityNav, { props: baseProps({ canManageTags: false }) }); + + const tagLinks = document.querySelectorAll('a[href="/admin/tags"]'); + expect(tagLinks.length).toBe(0); + }); + + it('hides the system and ocr links when canRunMaintenance is false', async () => { + mockPage.url = new URL('http://localhost/admin/users'); + const EntityNav = await loadComponent(); + render(EntityNav, { props: baseProps({ canRunMaintenance: false }) }); + + const systemLinks = document.querySelectorAll('a[href="/admin/system"]'); + const ocrLinks = document.querySelectorAll('a[href="/admin/ocr"]'); + expect(systemLinks.length).toBe(0); + expect(ocrLinks.length).toBe(0); + }); + + it('does not render the flyout panel by default', async () => { + mockPage.url = new URL('http://localhost/admin/users'); + const EntityNav = await loadComponent(); + render(EntityNav, { props: baseProps() }); + + await expect.element(browserPage.getByRole('dialog')).not.toBeInTheDocument(); + }); +});