2025-10-21 23:22:17 -07:00
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
2025-10-24 21:20:19 -07:00
|
|
|
import { AutodraftSettings } from '~/components/AutodraftSettings';
|
2025-10-21 23:22:17 -07:00
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
|
onUpdate: vi.fn(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
|
|
|
|
(global.fetch as any).mockResolvedValue({
|
|
|
|
|
ok: true,
|
|
|
|
|
json: async () => ({ success: true }),
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('Rendering', () => {
|
|
|
|
|
it('should render the autodraft switch', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} />);
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText('Autodraft')).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByRole('switch')).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not show mode options when autodraft is disabled', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
|
|
|
|
|
|
|
|
|
expect(screen.queryByText('Next Pick')).not.toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByText('While On')).not.toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should show mode options when autodraft is enabled', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} />);
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText('Next Pick')).toBeInTheDocument();
|
|
|
|
|
expect(screen.getByText('While On')).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should show disabled message when it is user turn', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isMyTurn={true} />);
|
|
|
|
|
|
|
|
|
|
expect(screen.getByText(/Autodraft settings are disabled during your pick/i)).toBeInTheDocument();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('Switch Toggle', () => {
|
|
|
|
|
it('should enable autodraft when switch is toggled on', async () => {
|
|
|
|
|
const onUpdate = vi.fn();
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
|
|
|
'/api/autodraft/update',
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
method: 'POST',
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(onUpdate).toHaveBeenCalledWith(true, 'next_pick');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should disable autodraft when switch is toggled off', async () => {
|
|
|
|
|
const onUpdate = vi.fn();
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} onUpdate={onUpdate} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(global.fetch).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(onUpdate).toHaveBeenCalledWith(false, 'next_pick');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should be disabled when it is user turn', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isMyTurn={true} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
expect(switchElement).toBeDisabled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle API errors gracefully', async () => {
|
|
|
|
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
|
|
|
(global.fetch as any).mockResolvedValueOnce({
|
|
|
|
|
ok: false,
|
|
|
|
|
json: async () => ({ error: 'Server error' }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Switch should revert to original state
|
|
|
|
|
expect(switchElement).not.toBeChecked();
|
|
|
|
|
|
|
|
|
|
consoleSpy.mockRestore();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('Mode Selection', () => {
|
|
|
|
|
it('should select next_pick mode by default', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" />);
|
|
|
|
|
|
|
|
|
|
const nextPickRadio = screen.getByRole('radio', { name: /next pick/i });
|
|
|
|
|
expect(nextPickRadio).toBeChecked();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should select while_on mode when specified', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="while_on" />);
|
|
|
|
|
|
|
|
|
|
const whileOnRadio = screen.getByRole('radio', { name: /while on/i });
|
|
|
|
|
expect(whileOnRadio).toBeChecked();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should change mode when radio button is clicked', async () => {
|
|
|
|
|
const onUpdate = vi.fn();
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" onUpdate={onUpdate} />);
|
|
|
|
|
|
|
|
|
|
const whileOnRadio = screen.getByRole('radio', { name: /while on/i });
|
|
|
|
|
fireEvent.click(whileOnRadio);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(global.fetch).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(onUpdate).toHaveBeenCalledWith(true, 'while_on');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should disable mode selection when it is user turn', () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} isMyTurn={true} />);
|
|
|
|
|
|
|
|
|
|
const nextPickRadio = screen.getByRole('radio', { name: /next pick/i });
|
|
|
|
|
const whileOnRadio = screen.getByRole('radio', { name: /while on/i });
|
|
|
|
|
|
|
|
|
|
expect(nextPickRadio).toBeDisabled();
|
|
|
|
|
expect(whileOnRadio).toBeDisabled();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('API Integration', () => {
|
|
|
|
|
it('should send correct data when enabling autodraft', async () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
|
|
|
'/api/autodraft/update',
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: expect.any(FormData),
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const fetchCall = (global.fetch as any).mock.calls[0];
|
|
|
|
|
const formData = fetchCall[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');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should send correct data when changing mode', async () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" />);
|
|
|
|
|
|
|
|
|
|
const whileOnRadio = screen.getByRole('radio', { name: /while on/i });
|
|
|
|
|
fireEvent.click(whileOnRadio);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(global.fetch).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const fetchCall = (global.fetch as any).mock.calls[0];
|
|
|
|
|
const formData = fetchCall[1].body as FormData;
|
|
|
|
|
|
|
|
|
|
expect(formData.get('mode')).toBe('while_on');
|
|
|
|
|
expect(formData.get('isEnabled')).toBe('true');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should show loading state during API call', async () => {
|
|
|
|
|
let resolvePromise: any;
|
|
|
|
|
const fetchPromise = new Promise((resolve) => {
|
|
|
|
|
resolvePromise = resolve;
|
|
|
|
|
});
|
|
|
|
|
(global.fetch as any).mockReturnValueOnce(fetchPromise);
|
|
|
|
|
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
// Switch should be disabled during update
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(switchElement).toBeDisabled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Resolve the promise
|
|
|
|
|
resolvePromise({ ok: true, json: async () => ({ success: true }) });
|
|
|
|
|
|
|
|
|
|
// Switch should be enabled again
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(switchElement).not.toBeDisabled();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('Edge Cases', () => {
|
|
|
|
|
it('should handle rapid toggle clicks', async () => {
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
|
|
|
|
|
// Click multiple times rapidly
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
// Should only process the first click while updating
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle network errors', async () => {
|
|
|
|
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
|
|
|
(global.fetch as any).mockRejectedValueOnce(new Error('Network error'));
|
|
|
|
|
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
consoleSpy.mockRestore();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should maintain state consistency on error', async () => {
|
|
|
|
|
(global.fetch as any).mockResolvedValueOnce({
|
|
|
|
|
ok: false,
|
|
|
|
|
json: async () => ({ error: 'Server error' }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
|
|
|
|
|
|
|
|
|
const switchElement = screen.getByRole('switch');
|
|
|
|
|
expect(switchElement).not.toBeChecked();
|
|
|
|
|
|
|
|
|
|
fireEvent.click(switchElement);
|
|
|
|
|
|
|
|
|
|
// Should revert to original state on error
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(switchElement).not.toBeChecked();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|