import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { RaceDetailInteractive } from './RaceDetailInteractive'; import type { RaceDetailsViewModel } from '@/lib/view-models/RaceDetailsViewModel'; // Mocks for Next.js navigation const mockPush = vi.fn(); const mockBack = vi.fn(); vi.mock('next/navigation', () => ({ useRouter: () => ({ push: mockPush, back: mockBack, }), useParams: () => ({ id: 'race-123' }), })); // Mock effective driver id hook vi.mock('@/hooks/useEffectiveDriverId', () => ({ useEffectiveDriverId: () => 'driver-1', })); // Mock sponsor mode hook to avoid rendering heavy sponsor card vi.mock('@/components/sponsors/SponsorInsightsCard', () => ({ __esModule: true, default: () =>
, MetricBuilders: { views: vi.fn(() => ({ label: 'Views', value: '100' })), engagement: vi.fn(() => ({ label: 'Engagement', value: '50%' })), reach: vi.fn(() => ({ label: 'Reach', value: '1000' })), }, SlotTemplates: { race: vi.fn(() => []), }, useSponsorMode: () => false, })); // Mock the new DI hooks const mockGetRaceDetails = vi.fn(); const mockReopenRace = vi.fn(); const mockFetchLeagueMemberships = vi.fn(); const mockGetMembership = vi.fn(); // Mock race detail hook vi.mock('@/hooks/race/useRaceDetail', () => ({ useRaceDetail: (raceId: string, driverId: string) => ({ data: mockGetRaceDetails.mock.results[0]?.value || null, isLoading: false, isError: false, isSuccess: !!mockGetRaceDetails.mock.results[0]?.value, refetch: vi.fn(), retry: vi.fn(), }), })); // Mock reopen race hook vi.mock('@/hooks/race/useReopenRace', () => ({ useReopenRace: () => ({ mutateAsync: mockReopenRace, mutate: mockReopenRace, isPending: false, isLoading: false, }), })); // Mock league membership service static method vi.mock('@/lib/services/leagues/LeagueMembershipService', () => ({ LeagueMembershipService: { getMembership: mockGetMembership, fetchLeagueMemberships: mockFetchLeagueMemberships, setLeagueMemberships: vi.fn(), clearLeagueMemberships: vi.fn(), getCachedMembershipsIterator: vi.fn(() => [][Symbol.iterator]()), getAllMembershipsForDriver: vi.fn(() => []), getLeagueMembers: vi.fn(() => []), }, })); // Mock league membership hook (if used by component) vi.mock('@/hooks/league/useLeagueMemberships', () => ({ useLeagueMemberships: (leagueId: string, currentUserId: string) => ({ data: mockFetchLeagueMemberships.mock.results[0]?.value || null, isLoading: false, isError: false, isSuccess: !!mockFetchLeagueMemberships.mock.results[0]?.value, refetch: vi.fn(), }), })); // Mock the useLeagueMembership hook that the component imports vi.mock('@/hooks/useLeagueMembershipService', () => ({ useLeagueMembership: (leagueId: string, driverId: string) => ({ data: mockGetMembership.mock.results[0]?.value || null, isLoading: false, isError: false, isSuccess: !!mockGetMembership.mock.results[0]?.value, refetch: vi.fn(), }), })); // We'll use the actual hooks but they will use the mocked services // The hooks are already mocked above via the service mocks // Mock league membership utility to control admin vs non-admin behavior const mockIsOwnerOrAdmin = vi.fn(); vi.mock('@/lib/utilities/LeagueMembershipUtility', () => ({ LeagueMembershipUtility: { isOwnerOrAdmin: (...args: unknown[]) => mockIsOwnerOrAdmin(...args), }, })); const renderWithQueryClient = (ui: React.ReactElement) => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); return render(