import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import { QueryClientProvider } from '@tanstack/react-query'; import { queryClient } from '../../src/lib/queryClient'; import Games from '../../src/routes/games'; import * as useGamesModule from '../../src/hooks/useGames'; // Mock the useGames hooks vi.spyOn(useGamesModule, 'useGames'); vi.spyOn(useGamesModule, 'useCreateGame'); vi.spyOn(useGamesModule, 'useUpdateGame'); vi.spyOn(useGamesModule, 'useDeleteGame'); const mockGames = [ { id: '1', title: 'The Legend of Zelda', slug: 'zelda-game', createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z', description: null, }, { id: '2', title: 'Super Mario Bros', slug: 'mario-game', createdAt: '2026-01-02T00:00:00Z', updatedAt: '2026-01-02T00:00:00Z', description: null, }, ]; describe('Games Page', () => { beforeEach(() => { vi.clearAllMocks(); // Default mocks vi.mocked(useGamesModule.useGames).mockReturnValue({ data: mockGames, isLoading: false, error: null, } as any); vi.mocked(useGamesModule.useCreateGame).mockReturnValue({ mutateAsync: vi.fn(), isPending: false, } as any); vi.mocked(useGamesModule.useUpdateGame).mockReturnValue({ mutateAsync: vi.fn(), isPending: false, } as any); vi.mocked(useGamesModule.useDeleteGame).mockReturnValue({ mutateAsync: vi.fn(), isPending: false, } as any); }); it('should render empty state when no games', () => { vi.mocked(useGamesModule.useGames).mockReturnValue({ data: [], isLoading: false, error: null, } as any); render( ); expect(screen.getByText(/no games found/i)).toBeInTheDocument(); }); it('should render loading state', () => { vi.mocked(useGamesModule.useGames).mockReturnValue({ data: undefined, isLoading: true, error: null, } as any); render( ); expect(screen.getByText(/loading games/i)).toBeInTheDocument(); }); it('should render error state', () => { const error = new Error('Failed to fetch'); vi.mocked(useGamesModule.useGames).mockReturnValue({ data: undefined, isLoading: false, error, } as any); render( ); expect(screen.getByText(/error/i)).toBeInTheDocument(); expect(screen.getByText(/failed to fetch/i)).toBeInTheDocument(); }); it('should render table with games', () => { render( ); expect(screen.getByText('The Legend of Zelda')).toBeInTheDocument(); expect(screen.getByText('Super Mario Bros')).toBeInTheDocument(); }); it('should render "Add Game" button', () => { render( ); expect(screen.getByRole('button', { name: /add game/i })).toBeInTheDocument(); }); it('should open form when "Add Game" is clicked', async () => { const user = await userEvent.setup(); render( ); const addButton = screen.getByRole('button', { name: /add game/i }); await user.click(addButton); await waitFor(() => { expect(screen.getByText(/create game/i)).toBeInTheDocument(); }); }); it('should open form for editing when edit button is clicked', async () => { const user = await userEvent.setup(); render( ); const editButtons = screen.getAllByRole('button', { name: /edit/i }); await user.click(editButtons[0]); await waitFor(() => { expect(screen.getByText(/edit game/i)).toBeInTheDocument(); }); }); it('should show delete confirmation when delete is clicked', async () => { const user = await userEvent.setup(); render( ); const deleteButtons = screen.getAllByRole('button', { name: /delete/i }); await user.click(deleteButtons[0]); await waitFor(() => { expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument(); }); }); it('should call delete mutation when confirmed', async () => { const user = await userEvent.setup(); const deleteAsync = vi.fn().mockResolvedValue(undefined); vi.mocked(useGamesModule.useDeleteGame).mockReturnValue({ mutateAsync: deleteAsync, isPending: false, } as any); render( ); const deleteButtons = screen.getAllByRole('button', { name: /delete/i }); await user.click(deleteButtons[0]); const confirmButton = await screen.findByRole('button', { name: /confirm/i }); await user.click(confirmButton); await waitFor(() => { expect(deleteAsync).toHaveBeenCalledWith('1'); }); }); it('should display table headers', () => { render( ); expect(screen.getByText('Title')).toBeInTheDocument(); expect(screen.getByText('Slug')).toBeInTheDocument(); expect(screen.getByText('Created')).toBeInTheDocument(); expect(screen.getByText('Actions')).toBeInTheDocument(); }); });