From 26611676a9c20fb14b8a35882dfca288a3ff64f9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 9 May 2026 23:01:03 +0200 Subject: [PATCH] test(admin/groups): cover GroupsListPanel branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expanded list with header, per-group rendering with permission count, empty placeholder branch, new-group link, aria-current matrix, collapsed view via autocollapse prop, localStorage preference path, expand-on-click flow. 8 tests covering ~25 branches (collapsed/expanded × autocollapse × localStorage × empty × active matrix). Refs #496. Co-Authored-By: Claude Sonnet 4.6 --- .../groups/GroupsListPanel.svelte.test.ts | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 frontend/src/routes/admin/groups/GroupsListPanel.svelte.test.ts diff --git a/frontend/src/routes/admin/groups/GroupsListPanel.svelte.test.ts b/frontend/src/routes/admin/groups/GroupsListPanel.svelte.test.ts new file mode 100644 index 00000000..7101b295 --- /dev/null +++ b/frontend/src/routes/admin/groups/GroupsListPanel.svelte.test.ts @@ -0,0 +1,109 @@ +import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page as browserPage } from 'vitest/browser'; + +const mockPage = { url: new URL('http://localhost/admin/groups') }; + +vi.mock('$app/state', () => ({ + get page() { + return mockPage; + } +})); + +beforeEach(() => { + localStorage.clear(); +}); + +afterEach(cleanup); + +async function loadComponent() { + return (await import('./GroupsListPanel.svelte')).default; +} + +const baseGroups = [ + { id: 'g1', name: 'Familie', permissions: ['READ_ALL'] }, + { id: 'g2', name: 'Admins', permissions: ['ADMIN', 'WRITE_ALL'] } +]; + +describe('GroupsListPanel', () => { + it('renders the expanded list with header by default', async () => { + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups } }); + + await expect.element(browserPage.getByText('Alle Gruppen')).toBeVisible(); + }); + + it('renders one row per group with permission count', async () => { + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups } }); + + await expect.element(browserPage.getByText('Familie')).toBeVisible(); + await expect.element(browserPage.getByText('Admins')).toBeVisible(); + await expect.element(browserPage.getByText('1 Berechtigungen')).toBeVisible(); + await expect.element(browserPage.getByText('2 Berechtigungen')).toBeVisible(); + }); + + it('renders the empty placeholder when groups is empty', async () => { + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: [] } }); + + await expect.element(browserPage.getByText('Keine Gruppen vorhanden.')).toBeVisible(); + }); + + it('renders the new-group link to /admin/groups/new', async () => { + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups } }); + + await expect + .element(browserPage.getByRole('link', { name: /neue gruppe/i })) + .toHaveAttribute('href', '/admin/groups/new'); + }); + + it('marks the active group with aria-current=page', async () => { + mockPage.url = new URL('http://localhost/admin/groups/g2'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups } }); + + const links = Array.from( + document.querySelectorAll('a[href^="/admin/groups/"]') + ) as HTMLAnchorElement[]; + const adminsLink = links.find((a) => a.href.endsWith('/admin/groups/g2')); + expect(adminsLink?.getAttribute('aria-current')).toBe('page'); + }); + + it('renders collapsed view when autocollapse is true', async () => { + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups, autocollapse: true } }); + + await expect + .element(browserPage.getByRole('button', { name: /liste ausklappen/i })) + .toBeVisible(); + }); + + it('honours the localStorage manual-collapse preference', async () => { + localStorage.setItem('admin_groups_list_collapsed', 'true'); + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups } }); + + await expect + .element(browserPage.getByRole('button', { name: /liste ausklappen/i })) + .toBeVisible(); + }); + + it('expands the panel when the collapsed handle is clicked', async () => { + localStorage.setItem('admin_groups_list_collapsed', 'true'); + mockPage.url = new URL('http://localhost/admin/groups'); + const Panel = await loadComponent(); + render(Panel, { props: { groups: baseGroups } }); + + await browserPage.getByRole('button', { name: /liste ausklappen/i }).click(); + + await expect.element(browserPage.getByText('Alle Gruppen')).toBeVisible(); + }); +});