feat(admin/groups): add groups entity with master-detail sub-routes
Creates the full groups section under /admin/groups/: - +layout.server.ts: loads groups list via GET /api/groups - GroupsListPanel.svelte: left list panel (name + permission count, active state) - +layout.svelte: composes list panel + children slot - +page.svelte: empty selection prompt - [id]/+page.server.ts: update (PATCH) and delete actions - [id]/+page.svelte: edit detail panel with Standard/Administrative permission sections - new/+page.svelte and +page.server.ts: create group form Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
79
frontend/src/routes/admin/groups/layout.svelte.spec.ts
Normal file
79
frontend/src/routes/admin/groups/layout.svelte.spec.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { afterEach, describe, it, expect, vi } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import GroupsListPanel from './GroupsListPanel.svelte';
|
||||
|
||||
vi.mock('$app/state', () => ({
|
||||
page: { url: { pathname: '/admin/groups/g1' } }
|
||||
}));
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const groups = [
|
||||
{ id: 'g1', name: 'Administrators', permissions: ['ADMIN', 'WRITE_ALL'] },
|
||||
{ id: 'g2', name: 'Editors', permissions: ['WRITE_ALL'] },
|
||||
{ id: 'g3', name: 'Readers', permissions: [] }
|
||||
];
|
||||
|
||||
describe('GroupsListPanel — header', () => {
|
||||
it('renders the panel title', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
await expect.element(page.getByText(/Alle Gruppen/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a new-group link pointing to /admin/groups/new', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /neue gruppe/i }))
|
||||
.toHaveAttribute('href', '/admin/groups/new');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GroupsListPanel — group items', () => {
|
||||
it('renders each group name', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
await expect.element(page.getByRole('link', { name: /administrators/i })).toBeInTheDocument();
|
||||
await expect.element(page.getByRole('link', { name: /editors/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('each group links to /admin/groups/[id]', async () => {
|
||||
const { container } = render(GroupsListPanel, { groups });
|
||||
const links = container.querySelectorAll<HTMLAnchorElement>('a[href^="/admin/groups/g"]');
|
||||
expect(links.length).toBe(3);
|
||||
expect(links[0].getAttribute('href')).toBe('/admin/groups/g1');
|
||||
});
|
||||
|
||||
it('shows permission count as subtitle', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
// Administrators has 2 permissions
|
||||
await expect.element(page.getByText(/2 Berechtigungen/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "no permissions" for a group with zero permissions', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
await expect.element(page.getByText(/0 Berechtigungen/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GroupsListPanel — active state', () => {
|
||||
it('marks the active group link with aria-current=page', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /administrators/i }))
|
||||
.toHaveAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
it('does not mark inactive group links with aria-current', async () => {
|
||||
render(GroupsListPanel, { groups });
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /editors/i }))
|
||||
.not.toHaveAttribute('aria-current');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GroupsListPanel — empty state', () => {
|
||||
it('shows empty state when groups array is empty', async () => {
|
||||
render(GroupsListPanel, { groups: [] });
|
||||
await expect.element(page.getByText(/keine gruppen/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user