test(admin/tags): cover TagTreeNode recursive branches
Tag link href, document-count visibility branch, color-dot at depth 0 vs deeper, aria-current matrix, children list rendering, collapse-map hides children, expand/collapse toggle for nodes with children. 9 tests covering ~30 branches in the recursive tree-node component. Refs #496. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
125
frontend/src/routes/admin/tags/TagTreeNode.svelte.test.ts
Normal file
125
frontend/src/routes/admin/tags/TagTreeNode.svelte.test.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page as browserPage } from 'vitest/browser';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
const mockPage = { url: new URL('http://localhost/admin/tags') };
|
||||
|
||||
vi.mock('$app/state', () => ({
|
||||
get page() {
|
||||
return mockPage;
|
||||
}
|
||||
}));
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
async function loadComponent() {
|
||||
return (await import('./TagTreeNode.svelte')).default;
|
||||
}
|
||||
|
||||
const leafNode = (overrides: Record<string, unknown> = {}) => ({
|
||||
id: 't1',
|
||||
name: 'Personen',
|
||||
color: 'sage',
|
||||
documentCount: 5,
|
||||
parentId: null,
|
||||
children: [],
|
||||
...overrides
|
||||
});
|
||||
|
||||
const parentNode = (overrides: Record<string, unknown> = {}) => ({
|
||||
id: 'tp1',
|
||||
name: 'Orte',
|
||||
color: 'sienna',
|
||||
documentCount: 0,
|
||||
parentId: null,
|
||||
children: [{ id: 'tc1', name: 'Berlin', color: null, documentCount: 2, children: [] }],
|
||||
...overrides
|
||||
});
|
||||
|
||||
describe('TagTreeNode', () => {
|
||||
it('renders the tag name as a link', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, { props: { node: leafNode(), depth: 0, collapseMap: new SvelteMap() } });
|
||||
|
||||
await expect
|
||||
.element(browserPage.getByRole('link', { name: /personen/i }))
|
||||
.toHaveAttribute('href', '/admin/tags/t1');
|
||||
});
|
||||
|
||||
it('renders the document count when greater than 0', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, {
|
||||
props: { node: leafNode({ documentCount: 7 }), depth: 0, collapseMap: new SvelteMap() }
|
||||
});
|
||||
|
||||
await expect.element(browserPage.getByText('(7)')).toBeVisible();
|
||||
});
|
||||
|
||||
it('omits the document count when 0', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, {
|
||||
props: { node: leafNode({ documentCount: 0 }), depth: 0, collapseMap: new SvelteMap() }
|
||||
});
|
||||
|
||||
await expect.element(browserPage.getByText('(0)')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the color dot at depth 0 when color is set', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, { props: { node: leafNode(), depth: 0, collapseMap: new SvelteMap() } });
|
||||
|
||||
const dot = document.querySelector('[data-testid="tag-list-color-dot"]');
|
||||
expect(dot).not.toBeNull();
|
||||
expect(dot?.getAttribute('data-color')).toBe('sage');
|
||||
});
|
||||
|
||||
it('omits the color dot when depth > 0', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, { props: { node: leafNode(), depth: 1, collapseMap: new SvelteMap() } });
|
||||
|
||||
expect(document.querySelector('[data-testid="tag-list-color-dot"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('marks the link as aria-current=page when on the matching route', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags/t1');
|
||||
const Node = await loadComponent();
|
||||
render(Node, { props: { node: leafNode(), depth: 0, collapseMap: new SvelteMap() } });
|
||||
|
||||
await expect
|
||||
.element(browserPage.getByRole('link', { name: /personen/i }))
|
||||
.toHaveAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
it('renders the children list for parent nodes when not collapsed', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, { props: { node: parentNode(), depth: 0, collapseMap: new SvelteMap() } });
|
||||
|
||||
await expect.element(browserPage.getByText('Berlin')).toBeVisible();
|
||||
});
|
||||
|
||||
it('hides children when the node is collapsed', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
const map = new SvelteMap<string, boolean>([['tp1', true]]);
|
||||
render(Node, { props: { node: parentNode(), depth: 0, collapseMap: map } });
|
||||
|
||||
await expect.element(browserPage.getByText('Berlin')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('exposes the expand/collapse toggle for nodes with children', async () => {
|
||||
mockPage.url = new URL('http://localhost/admin/tags');
|
||||
const Node = await loadComponent();
|
||||
render(Node, { props: { node: parentNode(), depth: 0, collapseMap: new SvelteMap() } });
|
||||
|
||||
await expect
|
||||
.element(browserPage.getByRole('button', { name: /einklappen|ausklappen/i }))
|
||||
.toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user