Files
gridpilot.gg/apps/website/components/leagues/LeagueMembers.test.tsx
2025-12-20 12:22:48 +01:00

130 lines
3.8 KiB
TypeScript

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<any[]>>();
const mockGetLeagueMembers = vi.fn<(leagueId: string) => any[]>();
const mockFindByIds = vi.fn<(ids: string[]) => Promise<DriverDTO[]>>();
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(<LeagueMembers leagueId={leagueId} showActions={false} />);
// 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(<LeagueMembers leagueId={leagueId} showActions={false} />);
await waitFor(() => {
expect(screen.queryByText('Loading members...')).not.toBeInTheDocument();
});
expect(screen.getByText('No members found')).toBeInTheDocument();
});
});