// Copyright (c) 2025-2026, RTE (https://www.rte-france.com) // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, // you can obtain one at http://mozilla.org/MPL/2.0/. // SPDX-License-Identifier: MPL-2.0 // @vitest-environment jsdom import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import SldEditPanel from './SldEditPanel'; const defaultProps = { pendingChanges: [], onReset: vi.fn(), onSimulate: vi.fn(), onClose: vi.fn(), }; describe('SldEditPanel', () => { it('shows the empty-state hint when there are no pending changes', () => { render(); expect(screen.getByText(/click a breaker .* or a load \/ generator/i)).toBeInTheDocument(); }); it('renders one row per pending change with baseline→target direction', () => { render(); const a = screen.getByTestId('sld-edit-change-SW_A'); const b = screen.getByTestId('sld-edit-change-SW_B'); expect(a.textContent).toMatch(/fermé.*ouvert/); expect(b.textContent).toMatch(/ouvert.*fermé/); }); it('disables both action buttons when no changes are staged', () => { render(); expect(screen.getByTestId('sld-edit-reset')).toBeDisabled(); expect(screen.getByTestId('sld-edit-simulate')).toBeDisabled(); }); it('enables and fires Reset / Simulate when changes exist', () => { const onReset = vi.fn(); const onSimulate = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-reset')); fireEvent.click(screen.getByTestId('sld-edit-simulate')); expect(onReset).toHaveBeenCalledTimes(1); expect(onSimulate).toHaveBeenCalledTimes(1); }); it('shows a combined-with badge when combinedWithActionId is provided', () => { render(); expect(screen.getByText(/combined with disco_LINE_42/i)).toBeInTheDocument(); }); it('disables Simulate when busy', () => { render(); const sim = screen.getByTestId('sld-edit-simulate'); expect(sim).toBeDisabled(); expect(sim.textContent).toMatch(/simulating/i); }); it('fires onClose when the ✕ is clicked', () => { const onClose = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-close')); expect(onClose).toHaveBeenCalledTimes(1); }); it('omits the exit ✕ when no onClose is provided (implicit edit mode)', () => { const { onClose, ...noClose } = defaultProps; void onClose; render(); expect(screen.queryByTestId('sld-edit-close')).toBeNull(); }); it('focuses a single switch when its row is clicked', () => { const onFocus = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-focus-SW_A')); expect(onFocus).toHaveBeenCalledWith('SW_A'); }); it('clears focus when clicking the already-focused row', () => { const onFocus = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-focus-SW_A')); expect(onFocus).toHaveBeenCalledWith(null); }); it('removes a single maneuver via its × button', () => { const onRemove = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-remove-SW_A')); expect(onRemove).toHaveBeenCalledWith('SW_A'); }); it('removes a block of selected maneuvers', () => { const onRemoveMany = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-select-SW_A')); fireEvent.click(screen.getByTestId('sld-edit-select-SW_C')); fireEvent.click(screen.getByTestId('sld-edit-remove-selected')); expect(onRemoveMany).toHaveBeenCalledTimes(1); expect(new Set(onRemoveMany.mock.calls[0][0])).toEqual(new Set(['SW_A', 'SW_C'])); }); it('hides the block-remove button when nothing is selected', () => { render(); expect(screen.queryByTestId('sld-edit-remove-selected')).toBeNull(); }); it('lists staged injection retunes with baseline→target MW', () => { render(); const gen = screen.getByTestId('sld-edit-injection-GEN_A'); expect(gen.textContent).toMatch(/120\.0 → 90\.0 MW/); const load = screen.getByTestId('sld-edit-injection-LOAD_A'); expect(load.textContent).toMatch(/50\.0 → 30\.0 MW/); // Simulate is enabled with only injection changes (no switch toggles). expect(screen.getByTestId('sld-edit-simulate')).not.toBeDisabled(); }); it('removes a staged injection retune via its × button', () => { const onInjectionRemove = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-injection-remove-GEN_A')); expect(onInjectionRemove).toHaveBeenCalledWith('GEN_A'); }); it('focuses an injection row when its label is clicked', () => { const onFocus = vi.fn(); render(); fireEvent.click(screen.getByTestId('sld-edit-injection-focus-GEN_A')); expect(onFocus).toHaveBeenCalledWith('GEN_A'); }); it('lists switch toggles AND injection retunes together in one combined action', () => { render(); expect(screen.getByTestId('sld-edit-change-SW_A')).toBeInTheDocument(); expect(screen.getByTestId('sld-edit-injection-GEN_A')).toBeInTheDocument(); expect(screen.getByTestId('sld-edit-injection-LOAD_A')).toBeInTheDocument(); // One Simulate button drives the whole combined action. expect(screen.getByTestId('sld-edit-simulate')).not.toBeDisabled(); }); });