import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { AutodraftSettings } from '~/components/AutodraftSettings';
// Mock fetch
global.fetch = vi.fn();
describe('AutodraftSettings Component', () => {
const defaultProps = {
seasonId: 'season-123',
teamId: 'team-456',
isEnabled: false,
mode: 'next_pick' as const,
isMyTurn: false,
queueOnly: false,
onUpdate: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
(global.fetch as any).mockResolvedValue({
ok: true,
json: async () => ({ success: true }),
});
});
describe('Rendering', () => {
it('should render three autodraft state buttons', () => {
render();
expect(screen.getByRole('button', { name: 'Off' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Next Pick' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'All Picks' })).toBeInTheDocument();
});
it('should not show the queue-only toggle when autodraft is Off', () => {
render();
expect(screen.queryByRole('switch')).not.toBeInTheDocument();
expect(screen.queryByText('Only autodraft from queue')).not.toBeInTheDocument();
});
it('should show the queue-only toggle when autodraft is active', () => {
render();
expect(screen.getByRole('switch')).toBeInTheDocument();
expect(screen.getByText('Only autodraft from queue')).toBeInTheDocument();
});
it('should show disabled message when it is the user\'s turn', () => {
render();
expect(screen.getByText(/Autodraft settings are disabled during your pick/i)).toBeInTheDocument();
});
it('should mark Off as active when isEnabled=false', () => {
render();
const offButton = screen.getByRole('button', { name: 'Off' });
expect(offButton.className).toContain('bg-electric');
});
it('should mark Next Pick as active when isEnabled=true and mode=next_pick', () => {
render();
const nextPickButton = screen.getByRole('button', { name: 'Next Pick' });
expect(nextPickButton.className).toContain('bg-electric');
});
it('should mark All Picks as active when isEnabled=true and mode=while_on', () => {
render();
const allPicksButton = screen.getByRole('button', { name: 'All Picks' });
expect(allPicksButton.className).toContain('bg-electric');
});
});
describe('Button Group Interaction', () => {
it('should switch to Next Pick when that button is clicked', async () => {
const onUpdate = vi.fn();
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
'/api/autodraft/update',
expect.objectContaining({ method: 'POST' })
);
});
await waitFor(() => {
expect(onUpdate).toHaveBeenCalledWith(true, 'next_pick', false);
});
});
it('should switch to All Picks when that button is clicked', async () => {
const onUpdate = vi.fn();
render();
fireEvent.click(screen.getByRole('button', { name: 'All Picks' }));
await waitFor(() => {
expect(onUpdate).toHaveBeenCalledWith(true, 'while_on', false);
});
});
it('should switch to Off when that button is clicked from an active state', async () => {
const onUpdate = vi.fn();
render();
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
await waitFor(() => {
expect(onUpdate).toHaveBeenCalledWith(false, 'next_pick', false);
});
});
it('should disable all buttons when it is the user\'s turn', () => {
render();
expect(screen.getByRole('button', { name: 'Off' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Next Pick' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'All Picks' })).toBeDisabled();
});
it('should not fire API call when a button is clicked during user\'s turn', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
expect(global.fetch).not.toHaveBeenCalled();
});
it('should handle API errors by reverting state', async () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
(global.fetch as any).mockResolvedValueOnce({
ok: false,
json: async () => ({ error: 'Server error' }),
});
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
await waitFor(() => {
expect(consoleSpy).toHaveBeenCalled();
});
// Off button should still be active (reverted)
const offButton = screen.getByRole('button', { name: 'Off' });
expect(offButton.className).toContain('bg-electric');
consoleSpy.mockRestore();
});
it('should handle network errors by reverting state', async () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
(global.fetch as any).mockRejectedValueOnce(new Error('Network error'));
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
await waitFor(() => {
expect(consoleSpy).toHaveBeenCalled();
});
consoleSpy.mockRestore();
});
});
describe('Queue-Only Toggle', () => {
it('should send queueOnly=true when the toggle is switched on', async () => {
const onUpdate = vi.fn();
render();
fireEvent.click(screen.getByRole('switch'));
await waitFor(() => {
expect(onUpdate).toHaveBeenCalledWith(true, 'next_pick', true);
});
});
it('should send queueOnly=false when the toggle is switched off', async () => {
const onUpdate = vi.fn();
render();
fireEvent.click(screen.getByRole('switch'));
await waitFor(() => {
expect(onUpdate).toHaveBeenCalledWith(true, 'next_pick', false);
});
});
it('should disable the queue-only toggle when it is the user\'s turn', () => {
render();
expect(screen.getByRole('switch')).toBeDisabled();
});
});
describe('API Integration', () => {
it('should post correct formData when switching to Next Pick', async () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
'/api/autodraft/update',
expect.objectContaining({ method: 'POST', body: expect.any(FormData) })
);
});
const formData = (global.fetch as any).mock.calls[0][1].body as FormData;
expect(formData.get('seasonId')).toBe('season-123');
expect(formData.get('teamId')).toBe('team-456');
expect(formData.get('isEnabled')).toBe('true');
expect(formData.get('mode')).toBe('next_pick');
expect(formData.get('queueOnly')).toBe('false');
});
it('should post mode=while_on when switching to All Picks', async () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'All Picks' }));
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
});
const formData = (global.fetch as any).mock.calls[0][1].body as FormData;
expect(formData.get('mode')).toBe('while_on');
expect(formData.get('isEnabled')).toBe('true');
});
it('should post isEnabled=false when switching to Off', async () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
await waitFor(() => {
expect(global.fetch).toHaveBeenCalled();
});
const formData = (global.fetch as any).mock.calls[0][1].body as FormData;
expect(formData.get('isEnabled')).toBe('false');
});
it('should disable buttons during an in-flight API call', async () => {
let resolve: any;
(global.fetch as any).mockReturnValueOnce(new Promise((r) => { resolve = r; }));
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Off' })).toBeDisabled();
});
resolve({ ok: true, json: async () => ({ success: true }) });
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Off' })).not.toBeDisabled();
});
});
it('should only process the first click when buttons are clicked rapidly', async () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Next Pick' }));
fireEvent.click(screen.getByRole('button', { name: 'All Picks' }));
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledTimes(1);
});
});
});
});