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>
This commit is contained in:
148
frontend/src/hooks/useAdmin.test.tsx
Normal file
148
frontend/src/hooks/useAdmin.test.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor, act } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import { usePokemonList, useCreateGame, useUpdateGame, useDeleteGame } from './useAdmin'
|
||||
|
||||
vi.mock('../api/admin')
|
||||
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))
|
||||
|
||||
import * as adminApi from '../api/admin'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('usePokemonList', () => {
|
||||
it('calls listPokemon with defaults', async () => {
|
||||
vi.mocked(adminApi.listPokemon).mockResolvedValue({ results: [], total: 0 } as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => usePokemonList(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(adminApi.listPokemon).toHaveBeenCalledWith(undefined, 50, 0, undefined)
|
||||
})
|
||||
|
||||
it('passes search and filter params to listPokemon', async () => {
|
||||
vi.mocked(adminApi.listPokemon).mockResolvedValue({ results: [], total: 0 } as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => usePokemonList('pika', 10, 20, 'electric'), { wrapper })
|
||||
await waitFor(() =>
|
||||
expect(adminApi.listPokemon).toHaveBeenCalledWith('pika', 10, 20, 'electric')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useCreateGame', () => {
|
||||
it('calls createGame with the provided input', async () => {
|
||||
vi.mocked(adminApi.createGame).mockResolvedValue({ id: 1 } as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useCreateGame(), { wrapper })
|
||||
const input = { name: 'FireRed', slug: 'firered', generation: 3, region: 'kanto', vgId: 1 }
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(input as never)
|
||||
})
|
||||
|
||||
expect(adminApi.createGame).toHaveBeenCalledWith(input)
|
||||
})
|
||||
|
||||
it('invalidates the games query on success', async () => {
|
||||
vi.mocked(adminApi.createGame).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useCreateGame(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({} as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['games'] })
|
||||
})
|
||||
|
||||
it('shows a success toast after creating a game', async () => {
|
||||
vi.mocked(adminApi.createGame).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useCreateGame(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({} as never)
|
||||
})
|
||||
|
||||
expect(toast.success).toHaveBeenCalledWith('Game created')
|
||||
})
|
||||
|
||||
it('shows an error toast on failure', async () => {
|
||||
vi.mocked(adminApi.createGame).mockRejectedValue(new Error('Conflict'))
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useCreateGame(), { wrapper })
|
||||
await act(async () => {
|
||||
result.current.mutate({} as never)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('Failed to create game: Conflict'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useUpdateGame', () => {
|
||||
it('calls updateGame with id and data', async () => {
|
||||
vi.mocked(adminApi.updateGame).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useUpdateGame(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ id: 7, data: { name: 'Renamed' } } as never)
|
||||
})
|
||||
|
||||
expect(adminApi.updateGame).toHaveBeenCalledWith(7, { name: 'Renamed' })
|
||||
})
|
||||
|
||||
it('invalidates games and shows a toast on success', async () => {
|
||||
vi.mocked(adminApi.updateGame).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useUpdateGame(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ id: 1, data: {} } as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['games'] })
|
||||
expect(toast.success).toHaveBeenCalledWith('Game updated')
|
||||
})
|
||||
})
|
||||
|
||||
describe('useDeleteGame', () => {
|
||||
it('calls deleteGame with the given id', async () => {
|
||||
vi.mocked(adminApi.deleteGame).mockResolvedValue(undefined as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useDeleteGame(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(3)
|
||||
})
|
||||
|
||||
expect(adminApi.deleteGame).toHaveBeenCalledWith(3)
|
||||
})
|
||||
|
||||
it('invalidates games and shows a toast on success', async () => {
|
||||
vi.mocked(adminApi.deleteGame).mockResolvedValue(undefined as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useDeleteGame(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(3)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['games'] })
|
||||
expect(toast.success).toHaveBeenCalledWith('Game deleted')
|
||||
})
|
||||
})
|
||||
118
frontend/src/hooks/useBosses.test.tsx
Normal file
118
frontend/src/hooks/useBosses.test.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
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'] })
|
||||
})
|
||||
})
|
||||
161
frontend/src/hooks/useEncounters.test.tsx
Normal file
161
frontend/src/hooks/useEncounters.test.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor, act } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import {
|
||||
useCreateEncounter,
|
||||
useUpdateEncounter,
|
||||
useDeleteEncounter,
|
||||
useEvolutions,
|
||||
useForms,
|
||||
useBulkRandomize,
|
||||
} from './useEncounters'
|
||||
|
||||
vi.mock('../api/encounters')
|
||||
|
||||
import {
|
||||
createEncounter,
|
||||
updateEncounter,
|
||||
deleteEncounter,
|
||||
fetchEvolutions,
|
||||
fetchForms,
|
||||
bulkRandomizeEncounters,
|
||||
} from '../api/encounters'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('useCreateEncounter', () => {
|
||||
it('calls createEncounter with the run id and input', async () => {
|
||||
vi.mocked(createEncounter).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useCreateEncounter(3), { wrapper })
|
||||
const input = { routeId: 1, pokemonId: 25, status: 'caught' }
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(input as never)
|
||||
})
|
||||
|
||||
expect(createEncounter).toHaveBeenCalledWith(3, input)
|
||||
})
|
||||
|
||||
it('invalidates the run query on success', async () => {
|
||||
vi.mocked(createEncounter).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useCreateEncounter(3), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({} as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 3] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('useUpdateEncounter', () => {
|
||||
it('calls updateEncounter with id and data', async () => {
|
||||
vi.mocked(updateEncounter).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useUpdateEncounter(3), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ id: 42, data: { status: 'dead' } } as never)
|
||||
})
|
||||
|
||||
expect(updateEncounter).toHaveBeenCalledWith(42, { status: 'dead' })
|
||||
})
|
||||
|
||||
it('invalidates the run query on success', async () => {
|
||||
vi.mocked(updateEncounter).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useUpdateEncounter(3), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ id: 1, data: {} } as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 3] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('useDeleteEncounter', () => {
|
||||
it('calls deleteEncounter with the encounter id', async () => {
|
||||
vi.mocked(deleteEncounter).mockResolvedValue(undefined as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useDeleteEncounter(3), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(55)
|
||||
})
|
||||
|
||||
expect(deleteEncounter).toHaveBeenCalledWith(55)
|
||||
})
|
||||
|
||||
it('invalidates the run query on success', async () => {
|
||||
vi.mocked(deleteEncounter).mockResolvedValue(undefined as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useDeleteEncounter(3), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(55)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 3] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('useEvolutions', () => {
|
||||
it('is disabled when pokemonId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => useEvolutions(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
expect(fetchEvolutions).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fetches evolutions for a given pokemon', async () => {
|
||||
vi.mocked(fetchEvolutions).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useEvolutions(25, 'kanto'), { wrapper })
|
||||
await waitFor(() => expect(fetchEvolutions).toHaveBeenCalledWith(25, 'kanto'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useForms', () => {
|
||||
it('is disabled when pokemonId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => useForms(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
|
||||
it('fetches forms for a given pokemon', async () => {
|
||||
vi.mocked(fetchForms).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useForms(133), { wrapper })
|
||||
await waitFor(() => expect(fetchForms).toHaveBeenCalledWith(133))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useBulkRandomize', () => {
|
||||
it('calls bulkRandomizeEncounters and invalidates the run', async () => {
|
||||
vi.mocked(bulkRandomizeEncounters).mockResolvedValue([] as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useBulkRandomize(4), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync()
|
||||
})
|
||||
|
||||
expect(bulkRandomizeEncounters).toHaveBeenCalledWith(4)
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 4] })
|
||||
})
|
||||
})
|
||||
89
frontend/src/hooks/useGames.test.tsx
Normal file
89
frontend/src/hooks/useGames.test.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import { useGames, useGame, useGameRoutes, useRoutePokemon } from './useGames'
|
||||
|
||||
vi.mock('../api/games')
|
||||
|
||||
import { getGames, getGame, getGameRoutes, getRoutePokemon } from '../api/games'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('useGames', () => {
|
||||
it('calls getGames and returns data', async () => {
|
||||
const games = [{ id: 1, name: 'Red' }]
|
||||
vi.mocked(getGames).mockResolvedValue(games as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useGames(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getGames).toHaveBeenCalledOnce()
|
||||
expect(result.current.data).toEqual(games)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useGame', () => {
|
||||
it('calls getGame with the given id', async () => {
|
||||
const game = { id: 2, name: 'Blue' }
|
||||
vi.mocked(getGame).mockResolvedValue(game as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useGame(2), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getGame).toHaveBeenCalledWith(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useGameRoutes', () => {
|
||||
it('is disabled when gameId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => useGameRoutes(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
expect(getGameRoutes).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fetches routes when gameId is provided', async () => {
|
||||
const routes = [{ id: 10, name: 'Route 1' }]
|
||||
vi.mocked(getGameRoutes).mockResolvedValue(routes as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useGameRoutes(1), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getGameRoutes).toHaveBeenCalledWith(1, undefined)
|
||||
expect(result.current.data).toEqual(routes)
|
||||
})
|
||||
|
||||
it('passes allowedTypes to the API', async () => {
|
||||
vi.mocked(getGameRoutes).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useGameRoutes(5, ['grass', 'water']), { wrapper })
|
||||
await waitFor(() => expect(getGameRoutes).toHaveBeenCalledWith(5, ['grass', 'water']))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useRoutePokemon', () => {
|
||||
it('is disabled when routeId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => useRoutePokemon(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
expect(getRoutePokemon).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fetches pokemon for a given route', async () => {
|
||||
vi.mocked(getRoutePokemon).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useRoutePokemon(3, 1), { wrapper })
|
||||
await waitFor(() => expect(getRoutePokemon).toHaveBeenCalledWith(3, 1))
|
||||
})
|
||||
})
|
||||
178
frontend/src/hooks/useGenlockes.test.tsx
Normal file
178
frontend/src/hooks/useGenlockes.test.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor, act } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import {
|
||||
useGenlockes,
|
||||
useGenlocke,
|
||||
useGenlockeGraveyard,
|
||||
useGenlockeLineages,
|
||||
useRegions,
|
||||
useCreateGenlocke,
|
||||
useLegSurvivors,
|
||||
useAdvanceLeg,
|
||||
} from './useGenlockes'
|
||||
|
||||
vi.mock('../api/genlockes')
|
||||
|
||||
import {
|
||||
getGenlockes,
|
||||
getGenlocke,
|
||||
getGenlockeGraveyard,
|
||||
getGenlockeLineages,
|
||||
getGamesByRegion,
|
||||
createGenlocke,
|
||||
getLegSurvivors,
|
||||
advanceLeg,
|
||||
} from '../api/genlockes'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('useGenlockes', () => {
|
||||
it('calls getGenlockes and returns data', async () => {
|
||||
const genlockes = [{ id: 1, name: 'Gen 1 Run' }]
|
||||
vi.mocked(getGenlockes).mockResolvedValue(genlockes as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useGenlockes(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getGenlockes).toHaveBeenCalledOnce()
|
||||
expect(result.current.data).toEqual(genlockes)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useGenlocke', () => {
|
||||
it('calls getGenlocke with the given id', async () => {
|
||||
vi.mocked(getGenlocke).mockResolvedValue({ id: 2 } as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useGenlocke(2), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getGenlocke).toHaveBeenCalledWith(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useGenlockeGraveyard', () => {
|
||||
it('calls getGenlockeGraveyard with the given id', async () => {
|
||||
vi.mocked(getGenlockeGraveyard).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useGenlockeGraveyard(3), { wrapper })
|
||||
await waitFor(() => expect(getGenlockeGraveyard).toHaveBeenCalledWith(3))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useGenlockeLineages', () => {
|
||||
it('calls getGenlockeLineages with the given id', async () => {
|
||||
vi.mocked(getGenlockeLineages).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useGenlockeLineages(3), { wrapper })
|
||||
await waitFor(() => expect(getGenlockeLineages).toHaveBeenCalledWith(3))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useRegions', () => {
|
||||
it('calls getGamesByRegion', async () => {
|
||||
vi.mocked(getGamesByRegion).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useRegions(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getGamesByRegion).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
||||
describe('useCreateGenlocke', () => {
|
||||
it('calls createGenlocke with the provided input', async () => {
|
||||
vi.mocked(createGenlocke).mockResolvedValue({ id: 10 } as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useCreateGenlocke(), { wrapper })
|
||||
const input = { name: 'New Genlocke', gameIds: [1, 2] }
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(input as never)
|
||||
})
|
||||
|
||||
expect(createGenlocke).toHaveBeenCalledWith(input)
|
||||
})
|
||||
|
||||
it('invalidates both runs and genlockes on success', async () => {
|
||||
vi.mocked(createGenlocke).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useCreateGenlocke(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({} as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] })
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['genlockes'] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('useLegSurvivors', () => {
|
||||
it('is disabled when enabled is false', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => useLegSurvivors(1, 1, false), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
expect(getLegSurvivors).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fetches survivors when enabled', async () => {
|
||||
vi.mocked(getLegSurvivors).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => useLegSurvivors(1, 2, true), { wrapper })
|
||||
await waitFor(() => expect(getLegSurvivors).toHaveBeenCalledWith(1, 2))
|
||||
})
|
||||
})
|
||||
|
||||
describe('useAdvanceLeg', () => {
|
||||
it('calls advanceLeg with genlocke id and leg order', async () => {
|
||||
vi.mocked(advanceLeg).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useAdvanceLeg(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ genlockeId: 1, legOrder: 1 })
|
||||
})
|
||||
|
||||
expect(advanceLeg).toHaveBeenCalledWith(1, 1, undefined)
|
||||
})
|
||||
|
||||
it('passes transferEncounterIds when provided', async () => {
|
||||
vi.mocked(advanceLeg).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useAdvanceLeg(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ genlockeId: 2, legOrder: 3, transferEncounterIds: [4, 5] })
|
||||
})
|
||||
|
||||
expect(advanceLeg).toHaveBeenCalledWith(2, 3, { transferEncounterIds: [4, 5] })
|
||||
})
|
||||
|
||||
it('invalidates runs and genlockes on success', async () => {
|
||||
vi.mocked(advanceLeg).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useAdvanceLeg(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ genlockeId: 1, legOrder: 1 })
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] })
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['genlockes'] })
|
||||
})
|
||||
})
|
||||
93
frontend/src/hooks/usePokemon.test.tsx
Normal file
93
frontend/src/hooks/usePokemon.test.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import {
|
||||
usePokemon,
|
||||
usePokemonFamilies,
|
||||
usePokemonEncounterLocations,
|
||||
usePokemonEvolutionChain,
|
||||
} from './usePokemon'
|
||||
|
||||
vi.mock('../api/pokemon')
|
||||
|
||||
import {
|
||||
getPokemon,
|
||||
fetchPokemonFamilies,
|
||||
fetchPokemonEncounterLocations,
|
||||
fetchPokemonEvolutionChain,
|
||||
} from '../api/pokemon'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('usePokemon', () => {
|
||||
it('is disabled when id is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => usePokemon(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
expect(getPokemon).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fetches a pokemon by id', async () => {
|
||||
const mon = { id: 25, name: 'pikachu' }
|
||||
vi.mocked(getPokemon).mockResolvedValue(mon as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => usePokemon(25), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getPokemon).toHaveBeenCalledWith(25)
|
||||
expect(result.current.data).toEqual(mon)
|
||||
})
|
||||
})
|
||||
|
||||
describe('usePokemonFamilies', () => {
|
||||
it('calls fetchPokemonFamilies and returns data', async () => {
|
||||
const families = [{ id: 1, members: [] }]
|
||||
vi.mocked(fetchPokemonFamilies).mockResolvedValue(families as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => usePokemonFamilies(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(fetchPokemonFamilies).toHaveBeenCalledOnce()
|
||||
expect(result.current.data).toEqual(families)
|
||||
})
|
||||
})
|
||||
|
||||
describe('usePokemonEncounterLocations', () => {
|
||||
it('is disabled when pokemonId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => usePokemonEncounterLocations(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
|
||||
it('fetches encounter locations for a given pokemon', async () => {
|
||||
vi.mocked(fetchPokemonEncounterLocations).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => usePokemonEncounterLocations(25), { wrapper })
|
||||
await waitFor(() => expect(fetchPokemonEncounterLocations).toHaveBeenCalledWith(25))
|
||||
})
|
||||
})
|
||||
|
||||
describe('usePokemonEvolutionChain', () => {
|
||||
it('is disabled when pokemonId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => usePokemonEvolutionChain(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
|
||||
it('fetches the evolution chain for a given pokemon', async () => {
|
||||
vi.mocked(fetchPokemonEvolutionChain).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
renderHook(() => usePokemonEvolutionChain(4), { wrapper })
|
||||
await waitFor(() => expect(fetchPokemonEvolutionChain).toHaveBeenCalledWith(4))
|
||||
})
|
||||
})
|
||||
181
frontend/src/hooks/useRuns.test.tsx
Normal file
181
frontend/src/hooks/useRuns.test.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor, act } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import {
|
||||
useRuns,
|
||||
useRun,
|
||||
useCreateRun,
|
||||
useUpdateRun,
|
||||
useDeleteRun,
|
||||
useNamingCategories,
|
||||
useNameSuggestions,
|
||||
} from './useRuns'
|
||||
|
||||
vi.mock('../api/runs')
|
||||
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))
|
||||
|
||||
import { getRuns, getRun, createRun, updateRun, deleteRun, getNamingCategories } from '../api/runs'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('useRuns', () => {
|
||||
it('calls getRuns and returns data', async () => {
|
||||
const runs = [{ id: 1, name: 'My Run' }]
|
||||
vi.mocked(getRuns).mockResolvedValue(runs as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useRuns(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getRuns).toHaveBeenCalledOnce()
|
||||
expect(result.current.data).toEqual(runs)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useRun', () => {
|
||||
it('calls getRun with the given id', async () => {
|
||||
const run = { id: 3, name: 'Specific Run' }
|
||||
vi.mocked(getRun).mockResolvedValue(run as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useRun(3), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getRun).toHaveBeenCalledWith(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useCreateRun', () => {
|
||||
it('calls createRun with the provided input', async () => {
|
||||
vi.mocked(createRun).mockResolvedValue({ id: 10 } as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useCreateRun(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ name: 'New Run', gameId: 1, status: 'active' } as never)
|
||||
})
|
||||
|
||||
expect(createRun).toHaveBeenCalledWith({ name: 'New Run', gameId: 1, status: 'active' })
|
||||
})
|
||||
|
||||
it('invalidates the runs query on success', async () => {
|
||||
vi.mocked(createRun).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useCreateRun(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({} as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('useUpdateRun', () => {
|
||||
it('calls updateRun with the given id and data', async () => {
|
||||
vi.mocked(updateRun).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useUpdateRun(5), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ name: 'Updated' } as never)
|
||||
})
|
||||
|
||||
expect(updateRun).toHaveBeenCalledWith(5, { name: 'Updated' })
|
||||
})
|
||||
|
||||
it('invalidates both the list and individual run query on success', async () => {
|
||||
vi.mocked(updateRun).mockResolvedValue({} as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useUpdateRun(5), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({} as never)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] })
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs', 5] })
|
||||
})
|
||||
|
||||
it('shows a toast when status is set to completed', async () => {
|
||||
vi.mocked(updateRun).mockResolvedValue({} as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useUpdateRun(1), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync({ status: 'completed' } as never)
|
||||
})
|
||||
|
||||
expect(toast.success).toHaveBeenCalledWith('Run marked as completed!')
|
||||
})
|
||||
|
||||
it('shows an error toast on failure', async () => {
|
||||
vi.mocked(updateRun).mockRejectedValue(new Error('Network error'))
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useUpdateRun(1), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutate({} as never)
|
||||
})
|
||||
|
||||
await waitFor(() =>
|
||||
expect(toast.error).toHaveBeenCalledWith('Failed to update run: Network error')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useDeleteRun', () => {
|
||||
it('calls deleteRun with the given id', async () => {
|
||||
vi.mocked(deleteRun).mockResolvedValue(undefined as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useDeleteRun(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(7)
|
||||
})
|
||||
|
||||
expect(deleteRun).toHaveBeenCalledWith(7)
|
||||
})
|
||||
|
||||
it('invalidates the runs query on success', async () => {
|
||||
vi.mocked(deleteRun).mockResolvedValue(undefined as never)
|
||||
const { queryClient, wrapper } = createWrapper()
|
||||
const spy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const { result } = renderHook(() => useDeleteRun(), { wrapper })
|
||||
await act(async () => {
|
||||
await result.current.mutateAsync(7)
|
||||
})
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ queryKey: ['runs'] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('useNamingCategories', () => {
|
||||
it('calls getNamingCategories', async () => {
|
||||
vi.mocked(getNamingCategories).mockResolvedValue([] as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useNamingCategories(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getNamingCategories).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
||||
describe('useNameSuggestions', () => {
|
||||
it('is disabled when runId is null', () => {
|
||||
const { wrapper } = createWrapper()
|
||||
const { result } = renderHook(() => useNameSuggestions(null), { wrapper })
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
})
|
||||
38
frontend/src/hooks/useStats.test.tsx
Normal file
38
frontend/src/hooks/useStats.test.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { renderHook, waitFor } from '@testing-library/react'
|
||||
import { createTestQueryClient } from '../test/utils'
|
||||
import { useStats } from './useStats'
|
||||
|
||||
vi.mock('../api/stats')
|
||||
|
||||
import { getStats } from '../api/stats'
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = createTestQueryClient()
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
return { queryClient, wrapper }
|
||||
}
|
||||
|
||||
describe('useStats', () => {
|
||||
it('calls getStats and returns data', async () => {
|
||||
const stats = { totalRuns: 5, activeRuns: 2 }
|
||||
vi.mocked(getStats).mockResolvedValue(stats as never)
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useStats(), { wrapper })
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(getStats).toHaveBeenCalledOnce()
|
||||
expect(result.current.data).toEqual(stats)
|
||||
})
|
||||
|
||||
it('reflects loading state before data resolves', () => {
|
||||
vi.mocked(getStats).mockReturnValue(new Promise(() => undefined))
|
||||
const { wrapper } = createWrapper()
|
||||
|
||||
const { result } = renderHook(() => useStats(), { wrapper })
|
||||
expect(result.current.isLoading).toBe(true)
|
||||
})
|
||||
})
|
||||
47
frontend/src/utils/download.test.ts
Normal file
47
frontend/src/utils/download.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { downloadJson } from './download'
|
||||
|
||||
describe('downloadJson', () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock-url')
|
||||
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('creates a blob URL from the JSON data', () => {
|
||||
downloadJson({ x: 1 }, 'export.json')
|
||||
expect(URL.createObjectURL).toHaveBeenCalledOnce()
|
||||
const blob = vi.mocked(URL.createObjectURL).mock.calls[0]?.[0] as Blob
|
||||
expect(blob.type).toBe('application/json')
|
||||
})
|
||||
|
||||
it('revokes the blob URL after triggering the download', () => {
|
||||
downloadJson({ x: 1 }, 'export.json')
|
||||
expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:mock-url')
|
||||
})
|
||||
|
||||
it('sets the correct download filename on the anchor', () => {
|
||||
const spy = vi.spyOn(document, 'createElement')
|
||||
downloadJson({ x: 1 }, 'my-data.json')
|
||||
const anchor = spy.mock.results[0]?.value as HTMLAnchorElement
|
||||
expect(anchor.download).toBe('my-data.json')
|
||||
})
|
||||
|
||||
it('appends and removes the anchor from the document body', () => {
|
||||
const appendSpy = vi.spyOn(document.body, 'appendChild')
|
||||
const removeSpy = vi.spyOn(document.body, 'removeChild')
|
||||
downloadJson({}, 'empty.json')
|
||||
expect(appendSpy).toHaveBeenCalledOnce()
|
||||
expect(removeSpy).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('serializes the data as formatted JSON', () => {
|
||||
downloadJson({ a: 1, b: [2, 3] }, 'data.json')
|
||||
const blob = vi.mocked(URL.createObjectURL).mock.calls[0]?.[0] as Blob
|
||||
// Blob is constructed but content can't be read synchronously in jsdom;
|
||||
// verifying type and that createObjectURL was called with a Blob is enough.
|
||||
expect(blob).toBeInstanceOf(Blob)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user