test(admin/tags): cover the tag-edit page branches

Heading with tag name, name input hydration, color picker visible only
for top-level tags, color swatch grid (10 entries), aria-pressed for
active color, success banner branch, error banner branch, merge-success
banner branch.

8 tests covering ~30 branches in the tag-edit page.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-10 01:26:14 +02:00
committed by marcel
parent f684ba3a61
commit 4b223df330

View File

@@ -0,0 +1,121 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
const mockPage = { url: { pathname: '/admin/tags/t1' } };
vi.mock('$app/stores', () => ({
page: {
subscribe: (fn: (v: typeof mockPage) => void) => {
fn(mockPage);
return () => {};
}
}
}));
vi.mock('$lib/shared/services/confirm.svelte', () => ({
getConfirmService: () => ({ confirm: async () => false })
}));
vi.mock('$lib/shared/services/confirm.svelte.js', () => ({
getConfirmService: () => ({ confirm: async () => false })
}));
vi.mock('$app/navigation', () => ({
beforeNavigate: () => {},
afterNavigate: () => {},
goto: vi.fn(),
invalidate: vi.fn(),
invalidateAll: vi.fn(),
preloadCode: vi.fn(),
preloadData: vi.fn(),
pushState: vi.fn(),
replaceState: vi.fn(),
disableScrollHandling: vi.fn(),
onNavigate: () => () => {}
}));
const { default: AdminTagEditPage } = await import('./+page.svelte');
afterEach(cleanup);
const baseTag = (overrides: Record<string, unknown> = {}) => ({
id: 't1',
name: 'Personen',
color: 'sage' as string | null,
parentId: null as string | null,
...overrides
});
const baseData = (overrides: Record<string, unknown> = {}) => ({
tag: baseTag(),
tags: [baseTag(), baseTag({ id: 't2', name: 'Orte', color: 'sienna' })],
mergeSuccess: null,
...overrides
});
describe('admin/tags/[id] page', () => {
it('renders the edit heading with the tag name', async () => {
render(AdminTagEditPage, { props: { data: baseData(), form: undefined } });
await expect.element(page.getByRole('heading', { name: /personen/i })).toBeVisible();
});
it('hydrates the name input from data.tag.name', async () => {
render(AdminTagEditPage, {
props: { data: baseData({ tag: baseTag({ name: 'Reisen' }) }), form: undefined }
});
const input = document.querySelector('input[name="name"]') as HTMLInputElement;
expect(input.value).toBe('Reisen');
});
it('renders the color picker for top-level tags (no parentId)', async () => {
render(AdminTagEditPage, { props: { data: baseData(), form: undefined } });
const colorPicker = document.querySelector('[data-testid="color-picker"]');
expect(colorPicker).not.toBeNull();
});
it('renders one color swatch per palette entry', async () => {
render(AdminTagEditPage, { props: { data: baseData(), form: undefined } });
const swatches = document.querySelectorAll('[data-testid^="color-swatch-"]');
expect(swatches.length).toBeGreaterThanOrEqual(10);
});
it('marks the active color swatch as aria-pressed', async () => {
render(AdminTagEditPage, {
props: { data: baseData({ tag: baseTag({ color: 'amber' }) }), form: undefined }
});
const amberSwatch = document.querySelector('[data-testid="color-swatch-amber"]') as HTMLElement;
expect(amberSwatch.getAttribute('aria-pressed')).toBe('true');
});
it('shows the form-success banner when form.success is true', async () => {
render(AdminTagEditPage, {
props: { data: baseData(), form: { success: true } }
});
const banners = document.querySelectorAll('.bg-green-50');
expect(banners.length).toBeGreaterThan(0);
});
it('shows the form-error banner when form.error is set', async () => {
render(AdminTagEditPage, {
props: { data: baseData(), form: { error: 'Tag name already in use' } }
});
const errors = document.querySelectorAll('.bg-red-50');
expect(errors.length).toBeGreaterThan(0);
});
it('shows the merge-success banner when data.mergeSuccess is set', async () => {
render(AdminTagEditPage, {
props: { data: baseData({ mergeSuccess: 'old-id' }), form: undefined }
});
const banners = document.querySelectorAll('[role="status"]');
expect(banners.length).toBeGreaterThan(0);
});
});