Files
nuzlocke-tracker/frontend/src/hooks/useBosses.test.tsx
Julian Tabel 0d2f419c6a Add unit tests for frontend utilities and hooks
82 tests covering download.ts and all React Query hooks. API modules are
mocked with vi.mock; mutation tests spy on queryClient.invalidateQueries
to verify cache invalidation. Conditional queries (null id) are verified
to stay idle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 13:47:55 +01:00

119 lines
4.1 KiB
TypeScript

import { QueryClientProvider } from '@tanstack/react-query'
import { renderHook, waitFor, act } from '@testing-library/react'
import { createTestQueryClient } from '../test/utils'
import {
useGameBosses,
useBossResults,
useCreateBossResult,
useDeleteBossResult,
} from './useBosses'
vi.mock('../api/bosses')
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))
import { getGameBosses, getBossResults, createBossResult, deleteBossResult } from '../api/bosses'
function createWrapper() {
const queryClient = createTestQueryClient()
const wrapper = ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
return { queryClient, wrapper }
}
describe('useGameBosses', () => {
it('is disabled when gameId is null', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useGameBosses(null), { wrapper })
expect(result.current.fetchStatus).toBe('idle')
expect(getGameBosses).not.toHaveBeenCalled()
})
it('fetches bosses for a given game', async () => {
const bosses = [{ id: 1, name: 'Brock' }]
vi.mocked(getGameBosses).mockResolvedValue(bosses as never)
const { wrapper } = createWrapper()
const { result } = renderHook(() => useGameBosses(1), { wrapper })
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(getGameBosses).toHaveBeenCalledWith(1, undefined)
expect(result.current.data).toEqual(bosses)
})
it('passes the all flag to the API', async () => {
vi.mocked(getGameBosses).mockResolvedValue([] as never)
const { wrapper } = createWrapper()
renderHook(() => useGameBosses(2, true), { wrapper })
await waitFor(() => expect(getGameBosses).toHaveBeenCalledWith(2, true))
})
})
describe('useBossResults', () => {
it('fetches boss results for a given run', async () => {
vi.mocked(getBossResults).mockResolvedValue([] as never)
const { wrapper } = createWrapper()
const { result } = renderHook(() => useBossResults(10), { wrapper })
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(getBossResults).toHaveBeenCalledWith(10)
})
})
describe('useCreateBossResult', () => {
it('calls createBossResult with the run id and input', async () => {
vi.mocked(createBossResult).mockResolvedValue({} as never)
const { wrapper } = createWrapper()
const { result } = renderHook(() => useCreateBossResult(5), { wrapper })
const input = { bossId: 1, won: true }
await act(async () => {
await result.current.mutateAsync(input as never)
})
expect(createBossResult).toHaveBeenCalledWith(5, input)
})
it('invalidates boss results for the run on success', async () => {
vi.mocked(createBossResult).mockResolvedValue({} as never)
const { queryClient, wrapper } = createWrapper()
const spy = vi.spyOn(queryClient, 'invalidateQueries')
const { result } = renderHook(() => useCreateBossResult(5), { wrapper })
await act(async () => {
await result.current.mutateAsync({} as never)
})
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 5, 'boss-results'] })
})
})
describe('useDeleteBossResult', () => {
it('calls deleteBossResult with the run id and result id', async () => {
vi.mocked(deleteBossResult).mockResolvedValue(undefined as never)
const { wrapper } = createWrapper()
const { result } = renderHook(() => useDeleteBossResult(5), { wrapper })
await act(async () => {
await result.current.mutateAsync(99)
})
expect(deleteBossResult).toHaveBeenCalledWith(5, 99)
})
it('invalidates boss results for the run on success', async () => {
vi.mocked(deleteBossResult).mockResolvedValue(undefined as never)
const { queryClient, wrapper } = createWrapper()
const spy = vi.spyOn(queryClient, 'invalidateQueries')
const { result } = renderHook(() => useDeleteBossResult(5), { wrapper })
await act(async () => {
await result.current.mutateAsync(99)
})
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 5, 'boss-results'] })
})
})