From 35017d91c4f39af8c619f5c45b3442015d8c9391 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 25 May 2026 17:41:50 +0200 Subject: [PATCH] feat(themen): add /themen server load function + tests Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/routes/themen/+page.server.ts | 12 ++++ .../src/routes/themen/page.server.spec.ts | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 frontend/src/routes/themen/+page.server.ts create mode 100644 frontend/src/routes/themen/page.server.spec.ts diff --git a/frontend/src/routes/themen/+page.server.ts b/frontend/src/routes/themen/+page.server.ts new file mode 100644 index 00000000..d5d3891c --- /dev/null +++ b/frontend/src/routes/themen/+page.server.ts @@ -0,0 +1,12 @@ +import { error } from '@sveltejs/kit'; +import { createApiClient } from '$lib/shared/api.server'; +import type { components } from '$lib/generated/api'; + +type TagTreeNodeDTO = components['schemas']['TagTreeNodeDTO']; + +export async function load({ fetch }: Parameters[0]) { + const api = createApiClient(fetch); + const result = await api.GET('/api/tags/tree'); + if (!result.response.ok) throw error(500, 'Themen konnten nicht geladen werden.'); + return { tree: (result.data ?? []) as TagTreeNodeDTO[] }; +} diff --git a/frontend/src/routes/themen/page.server.spec.ts b/frontend/src/routes/themen/page.server.spec.ts new file mode 100644 index 00000000..338c4b40 --- /dev/null +++ b/frontend/src/routes/themen/page.server.spec.ts @@ -0,0 +1,60 @@ +import { describe, expect, it, vi, beforeEach } from 'vitest'; + +vi.mock('$lib/shared/api.server', () => ({ + createApiClient: vi.fn(), + extractErrorCode: (e: unknown) => (e as { code?: string } | undefined)?.code +})); + +import { createApiClient } from '$lib/shared/api.server'; + +beforeEach(() => vi.clearAllMocks()); + +function mockApiGet(ok: boolean, data: unknown) { + vi.mocked(createApiClient).mockReturnValue({ + GET: vi.fn().mockResolvedValue({ response: { ok }, data }) + } as ReturnType); +} + +const makeTag = (name: string, documentCount = 0) => ({ + id: 'id-' + name, + name, + documentCount, + children: [] +}); + +describe('/themen +page.server load', () => { + function makeLoadEvent() { + return { + fetch: vi.fn() as unknown as typeof fetch, + request: new Request('http://localhost/themen'), + url: new URL('http://localhost/themen') + }; + } + + it('returns tag tree when API succeeds', async () => { + const tree = [makeTag('Briefe', 5), makeTag('Fotos', 3)]; + mockApiGet(true, tree); + + const { load } = await import('./+page.server'); + const result = await load(makeLoadEvent()); + + expect(result.tree).toEqual(tree); + }); + + it('returns empty array when API returns empty list', async () => { + mockApiGet(true, []); + + const { load } = await import('./+page.server'); + const result = await load(makeLoadEvent()); + + expect(result.tree).toEqual([]); + }); + + it('throws 500 when API call fails', async () => { + mockApiGet(false, null); + + const { load } = await import('./+page.server'); + + await expect(load(makeLoadEvent())).rejects.toMatchObject({ status: 500 }); + }); +});