test: add hook tests for useDashboard and useCases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-02-26 21:19:20 +00:00
parent 159ac0a26c
commit 6ede0d93ed
2 changed files with 342 additions and 0 deletions

View file

@ -0,0 +1,291 @@
import { describe, it, expect } from 'vitest'
import { waitFor, act } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import { renderHookWithProviders } from '@/test/utils'
import {
mockCase,
mockCaseListResponse,
mockEmptyCaseList,
} from '@/test/mocks/data'
import {
useCases,
usePendingIcdCases,
useCaseUpdate,
useKvnrUpdate,
useIcdUpdate,
} from '@/hooks/useCases'
import type { CaseFilters } from '@/hooks/useCases'
// ---------------------------------------------------------------------------
// useCases
// ---------------------------------------------------------------------------
describe('useCases', () => {
const defaultFilters: CaseFilters = { page: 1, per_page: 20 }
it('fetches cases with default filters (success)', async () => {
const { result } = renderHookWithProviders(() => useCases(defaultFilters))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toEqual(mockCaseListResponse)
expect(result.current.data!.items).toHaveLength(2)
expect(result.current.data!.total).toBe(2)
})
it('passes filter params to the API (search, jahr, fallgruppe, has_icd)', async () => {
const capturedParams: Record<string, string | null> = {}
server.use(
http.get('/api/cases/', ({ request }) => {
const url = new URL(request.url)
capturedParams.search = url.searchParams.get('search')
capturedParams.jahr = url.searchParams.get('jahr')
capturedParams.fallgruppe = url.searchParams.get('fallgruppe')
capturedParams.has_icd = url.searchParams.get('has_icd')
capturedParams.page = url.searchParams.get('page')
capturedParams.per_page = url.searchParams.get('per_page')
return HttpResponse.json(mockCaseListResponse)
}),
)
const filters: CaseFilters = {
page: 2,
per_page: 10,
search: 'Meier',
jahr: 2026,
fallgruppe: 'onko',
has_icd: 'true',
}
const { result } = renderHookWithProviders(() => useCases(filters))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(capturedParams.search).toBe('Meier')
expect(capturedParams.jahr).toBe('2026')
expect(capturedParams.fallgruppe).toBe('onko')
expect(capturedParams.has_icd).toBe('true')
expect(capturedParams.page).toBe('2')
expect(capturedParams.per_page).toBe('10')
})
it('does not send optional params when they are undefined', async () => {
const capturedParams: Record<string, string | null> = {}
server.use(
http.get('/api/cases/', ({ request }) => {
const url = new URL(request.url)
capturedParams.search = url.searchParams.get('search')
capturedParams.jahr = url.searchParams.get('jahr')
capturedParams.fallgruppe = url.searchParams.get('fallgruppe')
capturedParams.has_icd = url.searchParams.get('has_icd')
return HttpResponse.json(mockCaseListResponse)
}),
)
const { result } = renderHookWithProviders(() => useCases(defaultFilters))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(capturedParams.search).toBeNull()
expect(capturedParams.jahr).toBeNull()
expect(capturedParams.fallgruppe).toBeNull()
expect(capturedParams.has_icd).toBeNull()
})
it('respects enabled: false (fetchStatus === "idle")', () => {
const { result } = renderHookWithProviders(() =>
useCases(defaultFilters, { enabled: false }),
)
expect(result.current.fetchStatus).toBe('idle')
expect(result.current.data).toBeUndefined()
})
it('handles empty response', async () => {
server.use(
http.get('/api/cases/', () => {
return HttpResponse.json(mockEmptyCaseList)
}),
)
const { result } = renderHookWithProviders(() => useCases(defaultFilters))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data!.items).toHaveLength(0)
expect(result.current.data!.total).toBe(0)
})
it('handles server error (500)', async () => {
server.use(
http.get('/api/cases/', () => {
return new HttpResponse(null, { status: 500 })
}),
)
const { result } = renderHookWithProviders(() => useCases(defaultFilters))
await waitFor(() => expect(result.current.isError).toBe(true))
expect(result.current.data).toBeUndefined()
})
})
// ---------------------------------------------------------------------------
// usePendingIcdCases
// ---------------------------------------------------------------------------
describe('usePendingIcdCases', () => {
it('fetches pending ICD cases (success)', async () => {
const { result } = renderHookWithProviders(() => usePendingIcdCases(1, 20))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data!.items).toHaveLength(1)
expect(result.current.data!.items[0].icd).toBeNull()
expect(result.current.data!.total).toBe(1)
})
it('respects enabled: false', () => {
const { result } = renderHookWithProviders(() =>
usePendingIcdCases(1, 20, { enabled: false }),
)
expect(result.current.fetchStatus).toBe('idle')
expect(result.current.data).toBeUndefined()
})
})
// ---------------------------------------------------------------------------
// useCaseUpdate
// ---------------------------------------------------------------------------
describe('useCaseUpdate', () => {
it('updates a case and returns the result', async () => {
const { result } = renderHookWithProviders(() => useCaseUpdate())
let updated: unknown
await act(async () => {
updated = await result.current.mutateAsync({
id: 1,
data: { kommentar: 'Neuer Kommentar' },
})
})
expect((updated as { id: number }).id).toBe(1)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
})
it('handles error (403 Forbidden)', async () => {
server.use(
http.put('/api/cases/:id', () => {
return HttpResponse.json(
{ detail: 'Forbidden' },
{ status: 403 },
)
}),
)
const { result } = renderHookWithProviders(() => useCaseUpdate())
await act(async () => {
try {
await result.current.mutateAsync({ id: 1, data: { kommentar: 'x' } })
} catch {
// expected
}
})
await waitFor(() => expect(result.current.isError).toBe(true))
})
})
// ---------------------------------------------------------------------------
// useKvnrUpdate
// ---------------------------------------------------------------------------
describe('useKvnrUpdate', () => {
it('updates KVNR and returns the updated case', async () => {
const { result } = renderHookWithProviders(() => useKvnrUpdate())
let updated: unknown
await act(async () => {
updated = await result.current.mutateAsync({
id: 1,
kvnr: 'C111222333',
})
})
expect((updated as { kvnr: string }).kvnr).toBe('C111222333')
await waitFor(() => expect(result.current.isSuccess).toBe(true))
})
it('handles null KVNR', async () => {
server.use(
http.put('/api/cases/:id/kvnr', ({ params }) => {
const id = Number(params.id)
return HttpResponse.json({ ...mockCase, id, kvnr: null })
}),
)
const { result } = renderHookWithProviders(() => useKvnrUpdate())
let updated: unknown
await act(async () => {
updated = await result.current.mutateAsync({
id: 1,
kvnr: null,
})
})
expect((updated as { kvnr: string | null }).kvnr).toBeNull()
await waitFor(() => expect(result.current.isSuccess).toBe(true))
})
})
// ---------------------------------------------------------------------------
// useIcdUpdate
// ---------------------------------------------------------------------------
describe('useIcdUpdate', () => {
it('updates ICD and returns the updated case', async () => {
const { result } = renderHookWithProviders(() => useIcdUpdate())
let updated: unknown
await act(async () => {
updated = await result.current.mutateAsync({
id: 1,
icd: 'C61',
})
})
expect((updated as { icd: string }).icd).toBe('C61')
await waitFor(() => expect(result.current.isSuccess).toBe(true))
})
it('handles error (422 Unprocessable Entity)', async () => {
server.use(
http.put('/api/cases/:id/icd', () => {
return HttpResponse.json(
{ detail: 'Invalid ICD code' },
{ status: 422 },
)
}),
)
const { result } = renderHookWithProviders(() => useIcdUpdate())
await act(async () => {
try {
await result.current.mutateAsync({ id: 1, icd: 'INVALID' })
} catch {
// expected
}
})
await waitFor(() => expect(result.current.isError).toBe(true))
})
})

View file

@ -0,0 +1,51 @@
import { describe, it, expect } from 'vitest'
import { waitFor } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import { renderHookWithProviders } from '@/test/utils'
import { mockDashboardResponse } from '@/test/mocks/data'
import { useDashboard } from '@/hooks/useDashboard'
describe('useDashboard', () => {
it('fetches dashboard data for the given year', async () => {
const { result } = renderHookWithProviders(() => useDashboard(2026))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toEqual(mockDashboardResponse)
expect(result.current.data!.kpis.total_cases).toBe(142)
expect(result.current.data!.weekly).toHaveLength(6)
})
it('includes the year parameter in the API call', async () => {
let capturedJahr: string | null = null
server.use(
http.get('/api/reports/dashboard', ({ request }) => {
const url = new URL(request.url)
capturedJahr = url.searchParams.get('jahr')
return HttpResponse.json(mockDashboardResponse)
}),
)
const { result } = renderHookWithProviders(() => useDashboard(2025))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(capturedJahr).toBe('2025')
})
it('handles server error (500)', async () => {
server.use(
http.get('/api/reports/dashboard', () => {
return new HttpResponse(null, { status: 500 })
}),
)
const { result } = renderHookWithProviders(() => useDashboard(2026))
await waitFor(() => expect(result.current.isError).toBe(true))
expect(result.current.data).toBeUndefined()
})
})