mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 17:13:42 +00:00
test: add hook tests for useDashboard and useCases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
159ac0a26c
commit
6ede0d93ed
2 changed files with 342 additions and 0 deletions
291
frontend/src/hooks/__tests__/useCases.test.ts
Normal file
291
frontend/src/hooks/__tests__/useCases.test.ts
Normal 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))
|
||||||
|
})
|
||||||
|
})
|
||||||
51
frontend/src/hooks/__tests__/useDashboard.test.ts
Normal file
51
frontend/src/hooks/__tests__/useDashboard.test.ts
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue