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:
121
frontend/src/routes/admin/tags/[id]/page.svelte.test.ts
Normal file
121
frontend/src/routes/admin/tags/[id]/page.svelte.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user