130 lines
3.8 KiB
TypeScript
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();
|
|
});
|
|
});
|