Files
flyer-crawler.projectium.com/src/hooks/queries/useLeaderboardQuery.test.tsx
Torben Sorensen c78323275b
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 2m28s
more unit tests - done for now
2026-01-29 16:21:48 -08:00

194 lines
6.1 KiB
TypeScript

// src/hooks/queries/useLeaderboardQuery.test.tsx
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { ReactNode } from 'react';
import { useLeaderboardQuery } from './useLeaderboardQuery';
import * as apiClient from '../../services/apiClient';
import type { LeaderboardUser } from '../../types';
vi.mock('../../services/apiClient');
const mockedApiClient = vi.mocked(apiClient);
describe('useLeaderboardQuery', () => {
let queryClient: QueryClient;
const wrapper = ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
beforeEach(() => {
vi.resetAllMocks();
queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
},
});
});
it('should fetch leaderboard successfully', async () => {
const mockLeaderboard: LeaderboardUser[] = [
{
user_id: 'user-123',
full_name: 'Top Scorer',
avatar_url: 'https://example.com/avatar1.png',
points: 1500,
rank: '1',
},
{
user_id: 'user-456',
full_name: 'Second Place',
avatar_url: null,
points: 1200,
rank: '2',
},
];
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: true, data: mockLeaderboard }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(mockedApiClient.fetchLeaderboard).toHaveBeenCalledWith(10);
expect(result.current.data).toEqual(mockLeaderboard);
});
it('should fetch leaderboard with custom limit', async () => {
const mockLeaderboard: LeaderboardUser[] = [
{
user_id: 'user-789',
full_name: 'Champion',
avatar_url: 'https://example.com/avatar.png',
points: 2000,
rank: '1',
},
];
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: true, data: mockLeaderboard }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(5), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(mockedApiClient.fetchLeaderboard).toHaveBeenCalledWith(5);
expect(result.current.data).toEqual(mockLeaderboard);
});
it('should handle API error with error message', async () => {
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: false,
status: 401,
json: () => Promise.resolve({ message: 'Authentication required' }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.error?.message).toBe('Authentication required');
});
it('should handle API error without message', async () => {
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: false,
status: 500,
json: () => Promise.reject(new Error('Parse error')),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.error?.message).toBe('Request failed with status 500');
});
it('should use fallback message when error.message is empty', async () => {
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: false,
status: 500,
json: () => Promise.resolve({ message: '' }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.error?.message).toBe('Failed to fetch leaderboard');
});
it('should return empty array for no users on leaderboard', async () => {
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: true, data: [] }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toEqual([]);
});
it('should return empty array when success is false', async () => {
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: false, data: null }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toEqual([]);
});
it('should return empty array when data is not an array', async () => {
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: true, data: { invalid: 'data' } }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toEqual([]);
});
it('should not fetch when disabled', () => {
renderHook(() => useLeaderboardQuery(10, false), { wrapper });
expect(mockedApiClient.fetchLeaderboard).not.toHaveBeenCalled();
});
it('should handle users with null full_name and avatar_url', async () => {
const mockLeaderboard: LeaderboardUser[] = [
{
user_id: 'user-anon',
full_name: null,
avatar_url: null,
points: 100,
rank: '1',
},
];
mockedApiClient.fetchLeaderboard.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: true, data: mockLeaderboard }),
} as Response);
const { result } = renderHook(() => useLeaderboardQuery(), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toEqual(mockLeaderboard);
expect(result.current.data?.[0].full_name).toBeNull();
expect(result.current.data?.[0].avatar_url).toBeNull();
});
});