diff --git a/frontend/src/routes/admin/tags/TagsListPanel.svelte.test.ts b/frontend/src/routes/admin/tags/TagsListPanel.svelte.test.ts new file mode 100644 index 00000000..4e298519 --- /dev/null +++ b/frontend/src/routes/admin/tags/TagsListPanel.svelte.test.ts @@ -0,0 +1,79 @@ +import { describe, it, expect, afterEach, beforeEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import TagsListPanel from './TagsListPanel.svelte'; + +beforeEach(() => { + localStorage.clear(); +}); + +afterEach(cleanup); + +const baseTree = [ + { id: 't1', name: 'Personen', parentId: null, color: 'sage', children: [] }, + { id: 't2', name: 'Orte', parentId: null, color: 'sienna', children: [] } +]; + +describe('TagsListPanel', () => { + it('renders the expanded panel with tree and label', async () => { + render(TagsListPanel, { props: { tree: baseTree } }); + + await expect.element(page.getByRole('tree', { name: /schlagwörter/i })).toBeVisible(); + }); + + it('renders the empty placeholder when tree is empty', async () => { + render(TagsListPanel, { props: { tree: [] } }); + + await expect.element(page.getByText('Keine Schlagworte vorhanden.')).toBeVisible(); + }); + + it('renders one tree node per top-level tag', async () => { + render(TagsListPanel, { props: { tree: baseTree } }); + + await expect.element(page.getByText('Personen')).toBeVisible(); + await expect.element(page.getByText('Orte')).toBeVisible(); + }); + + it('renders the collapse button in expanded view', async () => { + render(TagsListPanel, { props: { tree: baseTree } }); + + await expect.element(page.getByRole('button', { name: /liste einklappen/i })).toBeVisible(); + }); + + it('renders collapsed view when autocollapse is true', async () => { + render(TagsListPanel, { props: { tree: baseTree, autocollapse: true } }); + + await expect.element(page.getByRole('button', { name: /liste ausklappen/i })).toBeVisible(); + }); + + it('honours the localStorage manual-collapse preference', async () => { + localStorage.setItem('admin_tags_list_collapsed', 'true'); + render(TagsListPanel, { props: { tree: baseTree } }); + + await expect.element(page.getByRole('button', { name: /liste ausklappen/i })).toBeVisible(); + }); + + it('expands the panel when the collapsed handle is clicked', async () => { + localStorage.setItem('admin_tags_list_collapsed', 'true'); + render(TagsListPanel, { props: { tree: baseTree } }); + + await page.getByRole('button', { name: /liste ausklappen/i }).click(); + + await expect.element(page.getByRole('tree')).toBeVisible(); + }); + + it('reads the collapse map from localStorage on mount', async () => { + localStorage.setItem('admin_tags_tree_state', JSON.stringify({ t1: true })); + render(TagsListPanel, { props: { tree: baseTree } }); + + // Just verify it doesn't crash on JSON parse + await expect.element(page.getByRole('tree')).toBeVisible(); + }); + + it('handles malformed localStorage tree state gracefully', async () => { + localStorage.setItem('admin_tags_tree_state', 'not-valid-json{'); + render(TagsListPanel, { props: { tree: baseTree } }); + + await expect.element(page.getByRole('tree')).toBeVisible(); + }); +});