From df28ab723d71dfadbb4d5c15a728bb82a0e95736 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Fri, 27 Feb 2026 21:56:28 -0800 Subject: [PATCH] test: rewrite AutodraftSettings tests for three-state button group UI The component was redesigned from a switch + radio buttons to Off/Next Pick/All Picks buttons with a separate queue-only Switch toggle. Updated 17 stale tests and added 5 new tests covering the queue-only toggle and the All Picks/Off button interactions. Co-Authored-By: Claude Sonnet 4.6 --- .../__tests__/AutodraftSettings.test.tsx | 374 +++++++++--------- 1 file changed, 184 insertions(+), 190 deletions(-) diff --git a/app/components/__tests__/AutodraftSettings.test.tsx b/app/components/__tests__/AutodraftSettings.test.tsx index 7ac0158..09cee7a 100644 --- a/app/components/__tests__/AutodraftSettings.test.tsx +++ b/app/components/__tests__/AutodraftSettings.test.tsx @@ -25,239 +25,142 @@ describe('AutodraftSettings Component', () => { }); describe('Rendering', () => { - it('should render the autodraft switch', () => { + it('should render three autodraft state buttons', () => { render(); - - expect(screen.getByText('Autodraft')).toBeInTheDocument(); - expect(screen.getByRole('switch')).toBeInTheDocument(); + + 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 mode options when autodraft is disabled', () => { + it('should not show the queue-only toggle when autodraft is Off', () => { render(); - - expect(screen.queryByText('Next Pick')).not.toBeInTheDocument(); - expect(screen.queryByText('While On')).not.toBeInTheDocument(); + + expect(screen.queryByRole('switch')).not.toBeInTheDocument(); + expect(screen.queryByText('Only autodraft from queue')).not.toBeInTheDocument(); }); - it('should show mode options when autodraft is enabled', () => { + it('should show the queue-only toggle when autodraft is active', () => { render(); - - expect(screen.getByText('Next Pick')).toBeInTheDocument(); - expect(screen.getByText('While On')).toBeInTheDocument(); + + expect(screen.getByRole('switch')).toBeInTheDocument(); + expect(screen.getByText('Only autodraft from queue')).toBeInTheDocument(); }); - it('should show disabled message when it is user turn', () => { + 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('Switch Toggle', () => { - it('should enable autodraft when switch is toggled on', async () => { + describe('Button Group Interaction', () => { + it('should switch to Next Pick when that button is clicked', async () => { const onUpdate = vi.fn(); - render(); - - const switchElement = screen.getByRole('switch'); - fireEvent.click(switchElement); + render(); + + fireEvent.click(screen.getByRole('button', { name: 'Next Pick' })); await waitFor(() => { expect(global.fetch).toHaveBeenCalledWith( '/api/autodraft/update', - expect.objectContaining({ - method: 'POST', - }) + expect.objectContaining({ method: 'POST' }) ); }); await waitFor(() => { - expect(onUpdate).toHaveBeenCalledWith(true, 'next_pick'); + expect(onUpdate).toHaveBeenCalledWith(true, 'next_pick', false); }); }); - it('should disable autodraft when switch is toggled off', async () => { + 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(); - - const switchElement = screen.getByRole('switch'); - fireEvent.click(switchElement); + + fireEvent.click(screen.getByRole('button', { name: 'Off' })); await waitFor(() => { - expect(global.fetch).toHaveBeenCalled(); - }); - - await waitFor(() => { - expect(onUpdate).toHaveBeenCalledWith(false, 'next_pick'); + expect(onUpdate).toHaveBeenCalledWith(false, 'next_pick', false); }); }); - it('should be disabled when it is user turn', () => { + it('should disable all buttons when it is the user\'s turn', () => { render(); - - const switchElement = screen.getByRole('switch'); - expect(switchElement).toBeDisabled(); + + 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 handle API errors gracefully', async () => { + 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(); - - const switchElement = screen.getByRole('switch'); - fireEvent.click(switchElement); + render(); + + fireEvent.click(screen.getByRole('button', { name: 'Next Pick' })); await waitFor(() => { expect(consoleSpy).toHaveBeenCalled(); }); - // Switch should revert to original state - expect(switchElement).not.toBeChecked(); + // Off button should still be active (reverted) + const offButton = screen.getByRole('button', { name: 'Off' }); + expect(offButton.className).toContain('bg-electric'); consoleSpy.mockRestore(); }); - }); - describe('Mode Selection', () => { - it('should select next_pick mode by default', () => { - render(); - - const nextPickRadio = screen.getByRole('radio', { name: /next pick/i }); - expect(nextPickRadio).toBeChecked(); - }); - - it('should select while_on mode when specified', () => { - render(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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(); - - 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 () => { + 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(); - - const switchElement = screen.getByRole('switch'); - fireEvent.click(switchElement); + + fireEvent.click(screen.getByRole('button', { name: 'Next Pick' })); await waitFor(() => { expect(consoleSpy).toHaveBeenCalled(); @@ -265,23 +168,114 @@ describe('AutodraftSettings Component', () => { consoleSpy.mockRestore(); }); + }); - it('should maintain state consistency on error', async () => { - (global.fetch as any).mockResolvedValueOnce({ - ok: false, - json: async () => ({ error: 'Server error' }), + 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) }) + ); }); - render(); - - const switchElement = screen.getByRole('switch'); - expect(switchElement).not.toBeChecked(); - - fireEvent.click(switchElement); + 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' })); - // Should revert to original state on error await waitFor(() => { - expect(switchElement).not.toBeChecked(); + 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); }); }); });