/** * Tests for useWizardState hook */ import { renderHook, act } from '@testing-library/react'; import { useWizardState } from '@/components/projects/wizard/useWizardState'; import { WIZARD_STEPS } from '@/components/projects/wizard/constants'; describe('useWizardState', () => { describe('initial state', () => { it('starts at step 1', () => { const { result } = renderHook(() => useWizardState()); expect(result.current.state.step).toBe(1); }); it('has empty form fields', () => { const { result } = renderHook(() => useWizardState()); expect(result.current.state.projectName).toBe(''); expect(result.current.state.description).toBe(''); expect(result.current.state.repoUrl).toBe(''); }); it('has null selections', () => { const { result } = renderHook(() => useWizardState()); expect(result.current.state.complexity).toBeNull(); expect(result.current.state.clientMode).toBeNull(); expect(result.current.state.autonomyLevel).toBeNull(); }); it('is not in script mode', () => { const { result } = renderHook(() => useWizardState()); expect(result.current.isScriptMode).toBe(false); }); }); describe('updateState', () => { it('updates project name', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test Project' }); }); expect(result.current.state.projectName).toBe('Test Project'); }); it('updates multiple fields at once', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test', description: 'A test project', }); }); expect(result.current.state.projectName).toBe('Test'); expect(result.current.state.description).toBe('A test project'); }); }); describe('resetState', () => { it('resets to initial state', () => { const { result } = renderHook(() => useWizardState()); // Make some changes act(() => { result.current.updateState({ projectName: 'Test', complexity: 'medium', step: 3, }); }); // Reset act(() => { result.current.resetState(); }); expect(result.current.state.projectName).toBe(''); expect(result.current.state.complexity).toBeNull(); expect(result.current.state.step).toBe(1); }); }); describe('canProceed', () => { it('requires project name at least 3 chars for step 1', () => { const { result } = renderHook(() => useWizardState()); expect(result.current.canProceed).toBe(false); act(() => { result.current.updateState({ projectName: 'AB' }); }); expect(result.current.canProceed).toBe(false); act(() => { result.current.updateState({ projectName: 'ABC' }); }); expect(result.current.canProceed).toBe(true); }); it('requires complexity selection for step 2', () => { const { result } = renderHook(() => useWizardState()); // Move to step 2 act(() => { result.current.updateState({ projectName: 'Test', step: 2 }); }); expect(result.current.canProceed).toBe(false); act(() => { result.current.updateState({ complexity: 'medium' }); }); expect(result.current.canProceed).toBe(true); }); it('requires client mode selection for step 3 (non-script)', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test', complexity: 'medium', step: 3, }); }); expect(result.current.canProceed).toBe(false); act(() => { result.current.updateState({ clientMode: 'technical' }); }); expect(result.current.canProceed).toBe(true); }); it('requires autonomy level for step 4 (non-script)', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test', complexity: 'medium', clientMode: 'auto', step: 4, }); }); expect(result.current.canProceed).toBe(false); act(() => { result.current.updateState({ autonomyLevel: 'milestone' }); }); expect(result.current.canProceed).toBe(true); }); it('always allows proceeding from step 5 (agent chat)', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ step: 5 }); }); expect(result.current.canProceed).toBe(true); }); }); describe('navigation', () => { it('goNext increments step', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test Project' }); }); act(() => { result.current.goNext(); }); expect(result.current.state.step).toBe(2); }); it('goBack decrements step', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test', step: 3 }); }); act(() => { result.current.goBack(); }); expect(result.current.state.step).toBe(2); }); it('goBack does nothing at step 1', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.goBack(); }); expect(result.current.state.step).toBe(1); }); it('does not proceed when canProceed is false', () => { const { result } = renderHook(() => useWizardState()); // Project name too short act(() => { result.current.updateState({ projectName: 'AB' }); }); act(() => { result.current.goNext(); }); expect(result.current.state.step).toBe(1); }); }); describe('script mode', () => { it('sets isScriptMode when complexity is script', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ complexity: 'script' }); }); expect(result.current.isScriptMode).toBe(true); }); it('skips from step 2 to step 5 for scripts', () => { const { result } = renderHook(() => useWizardState()); // Set up step 2 with script complexity act(() => { result.current.updateState({ projectName: 'Test Script', step: 2, complexity: 'script', }); }); // Go next should skip to step 5 act(() => { result.current.goNext(); }); expect(result.current.state.step).toBe(WIZARD_STEPS.AGENT_CHAT); }); it('auto-sets clientMode and autonomyLevel for scripts', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test Script', step: 2, complexity: 'script', }); }); act(() => { result.current.goNext(); }); expect(result.current.state.clientMode).toBe('auto'); expect(result.current.state.autonomyLevel).toBe('autonomous'); }); it('goBack from step 5 goes to step 2 for scripts', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test Script', complexity: 'script', step: 5, }); }); act(() => { result.current.goBack(); }); expect(result.current.state.step).toBe(WIZARD_STEPS.COMPLEXITY); }); }); describe('getProjectData', () => { it('generates correct project data', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'My Test Project', description: 'A description', repoUrl: 'https://github.com/test/repo', complexity: 'medium', clientMode: 'technical', autonomyLevel: 'milestone', }); }); const data = result.current.getProjectData(); expect(data.name).toBe('My Test Project'); expect(data.slug).toBe('my-test-project'); expect(data.description).toBe('A description'); expect(data.autonomy_level).toBe('milestone'); expect(data.settings.complexity).toBe('medium'); expect(data.settings.client_mode).toBe('technical'); expect(data.settings.repo_url).toBe('https://github.com/test/repo'); }); it('generates URL-safe slug', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'My Project! With Special @#$ Characters', }); }); const data = result.current.getProjectData(); expect(data.slug).toBe('my-project-with-special-characters'); }); it('excludes empty repoUrl from settings', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test Project', repoUrl: '', }); }); const data = result.current.getProjectData(); expect(data.settings.repo_url).toBeUndefined(); }); it('uses defaults for null values', () => { const { result } = renderHook(() => useWizardState()); act(() => { result.current.updateState({ projectName: 'Test Project', }); }); const data = result.current.getProjectData(); expect(data.autonomy_level).toBe('milestone'); expect(data.settings.complexity).toBe('medium'); expect(data.settings.client_mode).toBe('auto'); }); }); });