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); }); }); }); });