import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import ProtestReviewPage from './page'; // Mock useEffectiveDriverId vi.mock('@/hooks/useEffectiveDriverId', () => ({ useEffectiveDriverId: () => 'driver-1', })); // Mocks for Next.js navigation const mockPush = vi.fn(); vi.mock('next/navigation', () => ({ useRouter: () => ({ push: mockPush, }), useParams: () => ({ id: 'league-1', protestId: 'protest-1' }), })); // Mock effective driver id hook vi.mock('@/hooks/useEffectiveDriverId', () => ({ useEffectiveDriverId: () => 'driver-1', })); const mockGetProtestDetailViewModel = vi.fn(); const mockFetchLeagueMemberships = vi.fn(); const mockGetMembership = vi.fn(); // Mock useLeagueAdminStatus hook vi.mock('@/hooks/league/useLeagueAdminStatus', () => ({ useLeagueAdminStatus: (leagueId: string, driverId: string) => ({ data: mockGetMembership.mock.results[0]?.value ? (mockGetMembership.mock.results[0].value.role === 'admin' || mockGetMembership.mock.results[0].value.role === 'owner') : false, isLoading: false, isError: false, isSuccess: true, refetch: vi.fn(), }), })); // Mock useProtestDetail hook vi.mock('@/hooks/league/useProtestDetail', () => ({ useProtestDetail: (leagueId: string, protestId: string, enabled: boolean = true) => ({ data: mockGetProtestDetailViewModel.mock.results[0]?.value || null, isLoading: false, isError: false, isSuccess: !!mockGetProtestDetailViewModel.mock.results[0]?.value, refetch: vi.fn(), retry: vi.fn(), }), })); // Mock useInject for protest service vi.mock('@/lib/di/hooks/useInject', () => ({ useInject: (token: symbol) => { if (token.toString().includes('PROTEST_SERVICE_TOKEN')) { return { applyPenalty: vi.fn(), requestDefense: vi.fn(), }; } return {}; }, })); // Mock the static LeagueMembershipService for LeagueRoleUtility 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(() => []), }, })); const mockIsLeagueAdminOrHigherRole = vi.fn(); vi.mock('@/lib/utilities/LeagueRoleUtility', () => ({ LeagueRoleUtility: { isLeagueAdminOrHigherRole: (...args: unknown[]) => mockIsLeagueAdminOrHigherRole(...args), }, })); describe('ProtestReviewPage', () => { beforeEach(() => { mockPush.mockReset(); mockGetProtestDetailViewModel.mockReset(); mockFetchLeagueMemberships.mockReset(); mockGetMembership.mockReset(); mockIsLeagueAdminOrHigherRole.mockReset(); // Set up default mock implementations mockFetchLeagueMemberships.mockResolvedValue(undefined); mockGetMembership.mockReturnValue({ role: 'admin' }); mockIsLeagueAdminOrHigherRole.mockReturnValue(true); }); it('loads protest detail via LeagueStewardingService view model method', async () => { mockGetProtestDetailViewModel.mockResolvedValue({ protest: { id: 'protest-1', raceId: 'race-1', protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', description: 'desc', submittedAt: '2023-10-01T10:00:00Z', status: 'pending', incident: { lap: 1 }, }, race: { id: 'race-1', name: 'Test Race', formattedDate: '10/1/2023', }, protestingDriver: { id: 'driver-1', name: 'Driver 1' }, accusedDriver: { id: 'driver-2', name: 'Driver 2' }, penaltyTypes: [ { type: 'time_penalty', label: 'Time Penalty', description: 'Add seconds to race result', requiresValue: true, valueLabel: 'seconds', defaultValue: 5, }, ], defaultReasons: { upheld: 'Upheld reason', dismissed: 'Dismissed reason' }, initialPenaltyType: 'time_penalty', initialPenaltyValue: 5, }); render(); await waitFor(() => { expect(mockGetProtestDetailViewModel).toHaveBeenCalledWith('league-1', 'protest-1'); }); expect(await screen.findByText('Protest Review')).toBeInTheDocument(); }); });