import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import LeagueMembers from './LeagueMembers'; import type { DriverDTO } from '@/lib/types/generated/DriverDTO'; // Stub global driver stats helper used by LeagueMembers sorting/rendering (globalThis as any).getDriverStats = (driverId: string) => ({ driverId, rating: driverId === 'driver-1' ? 2500 : 2000, overallRank: driverId === 'driver-1' ? 1 : 2, wins: driverId === 'driver-1' ? 10 : 5, }); // Mock effective driver id so we can assert the "(You)" label vi.mock('@/hooks/useEffectiveDriverId', () => { return { useEffectiveDriverId: () => 'driver-1', }; }); // Mock services hook to inject stub leagueMembershipService and driverService const mockFetchLeagueMemberships = vi.fn<[], Promise>(); const mockGetLeagueMembers = vi.fn<(leagueId: string) => any[]>(); const mockFindByIds = vi.fn<(ids: string[]) => Promise>(); vi.mock('@/lib/services/ServiceProvider', () => { return { useServices: () => ({ leagueMembershipService: { fetchLeagueMemberships: mockFetchLeagueMemberships, getLeagueMembers: mockGetLeagueMembers, }, driverService: { findByIds: mockFindByIds, }, }), }; }); describe('LeagueMembers', () => { beforeEach(() => { mockFetchLeagueMemberships.mockReset(); mockGetLeagueMembers.mockReset(); mockFindByIds.mockReset(); }); it('loads memberships via services and renders driver rows', async () => { const leagueId = 'league-1'; const memberships = [ { id: 'm1', leagueId, driverId: 'driver-1', role: 'owner', status: 'active', joinedAt: '2024-01-01T00:00:00.000Z', }, { id: 'm2', leagueId, driverId: 'driver-2', role: 'member', status: 'active', joinedAt: '2024-01-02T00:00:00.000Z', }, ]; const drivers: DriverDTO[] = [ { id: 'driver-1', iracingId: 'ir-1', name: 'Driver One', country: 'DE', }, { id: 'driver-2', iracingId: 'ir-2', name: 'Driver Two', country: 'US', }, ]; mockFetchLeagueMemberships.mockResolvedValue(memberships); mockGetLeagueMembers.mockReturnValue(memberships); mockFindByIds.mockResolvedValue(drivers); render(); // Loading state first expect(screen.getByText('Loading members...')).toBeInTheDocument(); // Wait for data to be rendered await waitFor(() => { expect(screen.queryByText('Loading members...')).not.toBeInTheDocument(); }); // Services should have been called with expected arguments expect(mockFetchLeagueMemberships).toHaveBeenCalledWith(leagueId); expect(mockGetLeagueMembers).toHaveBeenCalledWith(leagueId); expect(mockFindByIds).toHaveBeenCalledTimes(1); expect(mockFindByIds).toHaveBeenCalledWith(['driver-1', 'driver-2']); // Driver rows should be rendered using DTO names expect(screen.getByText('Driver One')).toBeInTheDocument(); expect(screen.getByText('Driver Two')).toBeInTheDocument(); // Current user marker should appear for effective driver id expect(screen.getByText('(You)')).toBeInTheDocument(); }); it('handles empty membership list gracefully', async () => { const leagueId = 'league-empty'; mockFetchLeagueMemberships.mockResolvedValue([]); mockGetLeagueMembers.mockReturnValue([]); mockFindByIds.mockResolvedValue([]); render(); await waitFor(() => { expect(screen.queryByText('Loading members...')).not.toBeInTheDocument(); }); expect(screen.getByText('No members found')).toBeInTheDocument(); }); });