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'; // 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(); vi.mock('@/lib/services/ServiceProvider', () => ({ useServices: () => ({ leagueStewardingService: { getProtestDetailViewModel: mockGetProtestDetailViewModel, }, protestService: { applyPenalty: vi.fn(), requestDefense: vi.fn(), }, leagueMembershipService: { fetchLeagueMemberships: mockFetchLeagueMemberships, getMembership: mockGetMembership, }, }), })); 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(); 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(); }); });