| import { describe, it, expect, beforeEach, vi } from 'vitest'; |
| import { createTestStore, setupOrchestratorMocks } from './test-helpers'; |
| import type { GenerationTask } from '../types'; |
| import { track } from '@/lib/telemetry'; |
|
|
| vi.mock('@/lib/llm/multi-agent-orchestrator', () => ({ MultiAgentOrchestrator: vi.fn() })); |
| setupOrchestratorMocks(); |
|
|
| function setActiveTask( |
| store: ReturnType<typeof createTestStore>, |
| projectId: string, |
| overrides?: Partial<GenerationTask>, |
| ) { |
| const tasks = new Map(store.getState().generationTasks); |
| tasks.set(projectId, { |
| projectId, |
| projectName: 'Test', |
| prompt: 'test', |
| model: 'gpt-4', |
| startedAt: Date.now(), |
| result: null, |
| paused: false, |
| pausedMessage: null, |
| orchestratorInstance: null, |
| persistedInstance: null, |
| ...overrides, |
| }); |
| store.setState({ generationTasks: tasks, generating: true }); |
| } |
|
|
| describe('project slice', () => { |
| let store: ReturnType<typeof createTestStore>; |
|
|
| beforeEach(() => { |
| store = createTestStore(); |
| }); |
|
|
| it('initProject populates fields', () => { |
| store.getState().initProject({ |
| id: 'proj-1', |
| name: 'Test Project', |
| settings: { runtime: 'react', previewEntryPoint: 'index.html' }, |
| }); |
| expect(store.getState().projectId).toBe('proj-1'); |
| expect(store.getState().projectName).toBe('Test Project'); |
| expect(store.getState().projectRuntime).toBe('react'); |
| expect(store.getState().entryPoint).toBe('index.html'); |
| }); |
|
|
| it('markDirty / markClean toggles isDirty', () => { |
| expect(store.getState().isDirty).toBe(false); |
| store.getState().markDirty(); |
| expect(store.getState().isDirty).toBe(true); |
| store.getState().markClean(); |
| expect(store.getState().isDirty).toBe(false); |
| }); |
|
|
| it('bumpRefreshTrigger increments', () => { |
| const before = store.getState().refreshTrigger; |
| store.getState().bumpRefreshTrigger(); |
| expect(store.getState().refreshTrigger).toBe(before + 1); |
| }); |
|
|
| it('updateProjectSettings updates runtime and bumps refresh', () => { |
| store.getState().initProject({ id: 'p', name: 'P', settings: { runtime: 'static' } }); |
| const before = store.getState().refreshTrigger; |
| store.getState().updateProjectSettings({ runtime: 'handlebars', previewEntryPoint: 'main.html' }); |
| expect(store.getState().projectRuntime).toBe('handlebars'); |
| expect(store.getState().entryPoint).toBe('main.html'); |
| expect(store.getState().refreshTrigger).toBe(before + 1); |
| }); |
|
|
| it('defaults to code mode', () => { |
| expect(store.getState().mode).toBe('code'); |
| }); |
|
|
| it('setMode sets the workspace mode (including interview)', () => { |
| store.getState().setMode('chat'); |
| expect(store.getState().mode).toBe('chat'); |
| store.getState().setMode('interview'); |
| expect(store.getState().mode).toBe('interview'); |
| }); |
|
|
| it('setMode tracks mode_switch only on an actual change', () => { |
| vi.mocked(track).mockClear(); |
| store.getState().setMode('chat'); |
| store.getState().setMode('interview'); |
| expect(track).toHaveBeenCalledWith('mode_switch', { from: 'chat', to: 'interview' }); |
|
|
| vi.mocked(track).mockClear(); |
| store.getState().setMode('interview'); |
| expect(track).not.toHaveBeenCalled(); |
| }); |
|
|
| it('setMode defers reset when generating', () => { |
| store.getState().initProject({ id: 'p', name: 'P' }); |
| |
| setActiveTask(store, 'p', { persistedInstance: { fake: true } as any }); |
| store.getState().setMode('chat'); |
| |
| const task = store.getState().generationTasks.get('p'); |
| expect(task?.persistedInstance).not.toBeNull(); |
| expect(store.getState().mode).toBe('chat'); |
| }); |
|
|
| it('setMode resets orchestrator when not generating', () => { |
| store.getState().initProject({ id: 'p', name: 'P' }); |
| |
| setActiveTask(store, 'p', { |
| result: 'completed', |
| persistedInstance: { fake: true } as any, |
| }); |
| store.setState({ generating: false }); |
|
|
| store.getState().setMode('chat'); |
|
|
| const task = store.getState().generationTasks.get('p'); |
| expect(task?.persistedInstance).toBeNull(); |
| }); |
|
|
| it('defaults to no active interview', () => { |
| expect(store.getState().activeInterview).toBeNull(); |
| }); |
|
|
| it('setActiveInterview sets and clears the active interview', () => { |
| store.getState().initProject({ id: 'p', name: 'P' }); |
| store.getState().setActiveInterview({ templateId: 'understand-company', title: 'Understand a company' }); |
| expect(store.getState().activeInterview).toEqual({ templateId: 'understand-company', title: 'Understand a company' }); |
| store.getState().setActiveInterview(null); |
| expect(store.getState().activeInterview).toBeNull(); |
| }); |
|
|
| it('resetProject clears the active interview', () => { |
| store.getState().initProject({ id: 'p', name: 'P' }); |
| store.getState().setActiveInterview({ templateId: 'plan-feature', title: 'Plan a feature' }); |
| store.getState().resetProject(); |
| expect(store.getState().activeInterview).toBeNull(); |
| }); |
|
|
| it('resetProject clears all project state', () => { |
| store.getState().initProject({ id: 'p', name: 'P', settings: { runtime: 'react' } }); |
| store.getState().markDirty(); |
| store.getState().resetProject(); |
| expect(store.getState().projectId).toBe(''); |
| expect(store.getState().isDirty).toBe(false); |
| expect(store.getState().projectRuntime).toBeUndefined(); |
| }); |
|
|
| it('resetProject is a no-op when generating', () => { |
| store.getState().initProject({ id: 'p', name: 'P' }); |
| |
| setActiveTask(store, 'p'); |
| store.getState().resetProject(); |
| expect(store.getState().projectId).toBe('p'); |
| }); |
| }); |
|
|