/** * Tests de la sección "Análisis de Impacto" del GlobalSettingsPanel. * * Los mocks usan vi.hoisted() para que currentProcess sea una referencia * estable entre renders (evita el loop infinito del useEffect de sync). */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, fireEvent, act } from '@testing-library/react' import { TooltipProvider } from '@/components/ui/tooltip' // ─── Mocks con referencia estable ───────────────────────────────────────────── const processStoreMock = vi.hoisted(() => ({ currentProcess: { id: 'p1', name: 'Proceso test', clientName: 'Cliente A', bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, createdAt: 0, updatedAt: 0, }, updateProcess: vi.fn(), subscribe: vi.fn(), })) vi.mock('@/store/process-store', () => ({ useProcessStore: () => processStoreMock, })) vi.mock('@/store/simulation-store', () => ({ useSimulationStore: () => ({ resultActual: null, resultAutomated: null, invalidateResults: vi.fn(), }), })) import { GlobalSettingsPanel } from '@/features/workspace/GlobalSettingsPanel' // ─── Helper ─────────────────────────────────────────────────────────────────── function renderPanel() { return render( ) } // ─── Tests ──────────────────────────────────────────────────────────────────── beforeEach(() => { processStoreMock.updateProcess.mockReset() processStoreMock.updateProcess.mockResolvedValue(undefined) // Restaurar defaults del proceso processStoreMock.currentProcess.annualFrequency = 1000 processStoreMock.currentProcess.analysisHorizonYears = 3 processStoreMock.currentProcess.automationInvestment = 0 }) describe('GlobalSettingsPanel — sección Análisis de Impacto', () => { it('renderiza los 3 campos de impacto con sus labels correctos', () => { renderPanel() expect(screen.getByLabelText(/Frecuencia anual/i)).toBeInTheDocument() expect(screen.getByText(/Horizonte de análisis/i)).toBeInTheDocument() expect(screen.getByLabelText(/Inversión en automatización/i)).toBeInTheDocument() }) it('frecuencia anual muestra el default 1000 del proceso', () => { renderPanel() const freqInput = screen.getByLabelText(/Frecuencia anual/i) as HTMLInputElement expect(freqInput.value).toBe('1000') }) it('editar frecuencia y guardar incluye el nuevo valor en updateProcess', async () => { renderPanel() const freqInput = screen.getByLabelText(/Frecuencia anual/i) await act(async () => { fireEvent.change(freqInput, { target: { value: '5000' } }) }) // Después de cambiar, el botón debería mostrar "Guardar configuración" const guardarBtn = await screen.findByRole('button', { name: /Guardar/i }) await act(async () => { fireEvent.click(guardarBtn) }) expect(processStoreMock.updateProcess).toHaveBeenCalledOnce() const calledWith = processStoreMock.updateProcess.mock.calls[0][0] expect(calledWith.annualFrequency).toBe(5000) }) it('horizonte de análisis se muestra en texto (default 3 años)', () => { renderPanel() expect(screen.getByText('3 años')).toBeInTheDocument() }) it('guardar incluye horizonte actual en updateProcess', async () => { renderPanel() const freqInput = screen.getByLabelText(/Frecuencia anual/i) await act(async () => { fireEvent.change(freqInput, { target: { value: '1200' } }) }) const guardarBtn = await screen.findByRole('button', { name: /Guardar/i }) await act(async () => { fireEvent.click(guardarBtn) }) const calledWith = processStoreMock.updateProcess.mock.calls[0][0] expect(calledWith.analysisHorizonYears).toBe(3) expect(calledWith.analysisHorizonYears).toBeGreaterThanOrEqual(1) expect(calledWith.analysisHorizonYears).toBeLessThanOrEqual(10) }) it('editar inversión en automatización y guardar la incluye correctamente', async () => { renderPanel() const investInput = screen.getByLabelText(/Inversión en automatización/i) await act(async () => { fireEvent.change(investInput, { target: { value: '75000' } }) }) const guardarBtn = await screen.findByRole('button', { name: /Guardar/i }) await act(async () => { fireEvent.click(guardarBtn) }) const calledWith = processStoreMock.updateProcess.mock.calls[0][0] expect(calledWith.automationInvestment).toBe(75000) }) it('sección "ANÁLISIS DE IMPACTO" aparece con su label de sección', () => { renderPanel() expect(screen.getByText(/Análisis de Impacto/i)).toBeInTheDocument() }) })