mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 16:03:41 +00:00
test: add hook tests for useNotifications and useReports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e67fe73da7
commit
36ffe43f18
2 changed files with 273 additions and 0 deletions
123
frontend/src/hooks/__tests__/useNotifications.test.ts
Normal file
123
frontend/src/hooks/__tests__/useNotifications.test.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
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 {
|
||||
mockNotification,
|
||||
mockNotificationRead,
|
||||
mockNotificationList,
|
||||
} from '@/test/mocks/data'
|
||||
import { useNotifications } from '@/hooks/useNotifications'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useNotifications
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useNotifications', () => {
|
||||
it('returns notifications list and unread count', async () => {
|
||||
const { result } = renderHookWithProviders(() => useNotifications())
|
||||
|
||||
// Initially loading
|
||||
expect(result.current.loading).toBe(true)
|
||||
expect(result.current.notifications).toEqual([])
|
||||
expect(result.current.unreadCount).toBe(0)
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false))
|
||||
|
||||
expect(result.current.notifications).toEqual(mockNotificationList.items)
|
||||
expect(result.current.notifications).toHaveLength(2)
|
||||
expect(result.current.unreadCount).toBe(1)
|
||||
expect(result.current.notifications[0].is_read).toBe(false)
|
||||
expect(result.current.notifications[1].is_read).toBe(true)
|
||||
})
|
||||
|
||||
it('handles empty notifications', async () => {
|
||||
server.use(
|
||||
http.get('/api/notifications', () => {
|
||||
return HttpResponse.json({ items: [], unread_count: 0 })
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useNotifications())
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false))
|
||||
|
||||
expect(result.current.notifications).toEqual([])
|
||||
expect(result.current.unreadCount).toBe(0)
|
||||
})
|
||||
|
||||
it('handles loading state', async () => {
|
||||
const { result } = renderHookWithProviders(() => useNotifications())
|
||||
|
||||
// Should start in loading state
|
||||
expect(result.current.loading).toBe(true)
|
||||
expect(result.current.notifications).toEqual([])
|
||||
expect(result.current.unreadCount).toBe(0)
|
||||
|
||||
// After loading completes
|
||||
await waitFor(() => expect(result.current.loading).toBe(false))
|
||||
|
||||
expect(result.current.notifications).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('markAsRead mutation works', async () => {
|
||||
let capturedId: number | null = null
|
||||
|
||||
server.use(
|
||||
http.put('/api/notifications/:id/read', ({ params }) => {
|
||||
capturedId = Number(params.id)
|
||||
return HttpResponse.json({
|
||||
...mockNotification,
|
||||
id: capturedId,
|
||||
is_read: true,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useNotifications())
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false))
|
||||
|
||||
await act(async () => {
|
||||
result.current.markAsRead(1)
|
||||
})
|
||||
|
||||
expect(capturedId).toBe(1)
|
||||
})
|
||||
|
||||
it('markAllAsRead mutation works', async () => {
|
||||
let markAllCalled = false
|
||||
|
||||
server.use(
|
||||
http.put('/api/notifications/read-all', () => {
|
||||
markAllCalled = true
|
||||
return HttpResponse.json({ marked_read: 1 })
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useNotifications())
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false))
|
||||
|
||||
await act(async () => {
|
||||
result.current.markAllAsRead()
|
||||
})
|
||||
|
||||
expect(markAllCalled).toBe(true)
|
||||
})
|
||||
|
||||
it('exposes refresh function', async () => {
|
||||
const { result } = renderHookWithProviders(() => useNotifications())
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false))
|
||||
|
||||
// refresh should be a function
|
||||
expect(typeof result.current.refresh).toBe('function')
|
||||
|
||||
// calling refresh should not throw
|
||||
await act(async () => {
|
||||
result.current.refresh()
|
||||
})
|
||||
})
|
||||
})
|
||||
150
frontend/src/hooks/__tests__/useReports.test.ts
Normal file
150
frontend/src/hooks/__tests__/useReports.test.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
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 { mockReportMeta } from '@/test/mocks/data'
|
||||
import { useReports, useGenerateReport, useDeleteReports } from '@/hooks/useReports'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useReports
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useReports', () => {
|
||||
it('fetches report list (success)', async () => {
|
||||
const { result } = renderHookWithProviders(() => useReports())
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual({
|
||||
items: [mockReportMeta],
|
||||
total: 1,
|
||||
})
|
||||
expect(result.current.data!.items).toHaveLength(1)
|
||||
expect(result.current.data!.items[0].jahr).toBe(2026)
|
||||
expect(result.current.data!.items[0].kw).toBe(6)
|
||||
})
|
||||
|
||||
it('handles empty list', async () => {
|
||||
server.use(
|
||||
http.get('/api/reports/list', () => {
|
||||
return HttpResponse.json({ items: [], total: 0 })
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useReports())
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual({ items: [], total: 0 })
|
||||
expect(result.current.data!.items).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useGenerateReport
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useGenerateReport', () => {
|
||||
it('generates report (success)', async () => {
|
||||
const newReport = {
|
||||
...mockReportMeta,
|
||||
id: 2,
|
||||
kw: 7,
|
||||
report_date: '2026-02-16',
|
||||
generated_at: '2026-02-17T08:00:00Z',
|
||||
}
|
||||
|
||||
server.use(
|
||||
http.post('/api/reports/generate', () => {
|
||||
return HttpResponse.json(newReport)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useGenerateReport())
|
||||
|
||||
let response: unknown
|
||||
await act(async () => {
|
||||
response = await result.current.mutateAsync({ jahr: 2026, kw: 7 })
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect((response as { id: number }).id).toBe(2)
|
||||
expect((response as { kw: number }).kw).toBe(7)
|
||||
expect((response as { jahr: number }).jahr).toBe(2026)
|
||||
})
|
||||
|
||||
it('handles error', async () => {
|
||||
server.use(
|
||||
http.post('/api/reports/generate', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Report generation failed' },
|
||||
{ status: 500 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useGenerateReport())
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ jahr: 2026, kw: 99 })
|
||||
} catch {
|
||||
// expected
|
||||
}
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useDeleteReports
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useDeleteReports', () => {
|
||||
it('deletes reports (success)', async () => {
|
||||
let capturedBody: unknown = null
|
||||
|
||||
server.use(
|
||||
http.delete('/api/reports/delete', async ({ request }) => {
|
||||
capturedBody = await request.json()
|
||||
return HttpResponse.json({ deleted: 2 })
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useDeleteReports())
|
||||
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync([1, 2])
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(capturedBody).toEqual([1, 2])
|
||||
})
|
||||
|
||||
it('handles error', async () => {
|
||||
server.use(
|
||||
http.delete('/api/reports/delete', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Forbidden' },
|
||||
{ status: 403 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useDeleteReports())
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync([999])
|
||||
} catch {
|
||||
// expected
|
||||
}
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue