Spaces:
Sleeping
Sleeping
| // 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(<SldEditPanel {...defaultProps} />); | |
| expect(screen.getByText(/click a breaker .* or a load \/ generator/i)).toBeInTheDocument(); | |
| }); | |
| it('renders one row per pending change with baseline→target direction', () => { | |
| render(<SldEditPanel | |
| {...defaultProps} | |
| pendingChanges={[ | |
| { switchId: 'SW_A', baselineOpen: false, targetOpen: true }, | |
| { switchId: 'SW_B', baselineOpen: true, targetOpen: false }, | |
| ]} | |
| />); | |
| 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(<SldEditPanel {...defaultProps} />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| onReset={onReset} | |
| onSimulate={onSimulate} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| combinedWithActionId="disco_LINE_42" | |
| />); | |
| expect(screen.getByText(/combined with disco_LINE_42/i)).toBeInTheDocument(); | |
| }); | |
| it('disables Simulate when busy', () => { | |
| render(<SldEditPanel | |
| {...defaultProps} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| busy | |
| />); | |
| 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(<SldEditPanel {...defaultProps} onClose={onClose} />); | |
| 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(<SldEditPanel {...noClose} />); | |
| expect(screen.queryByTestId('sld-edit-close')).toBeNull(); | |
| }); | |
| it('focuses a single switch when its row is clicked', () => { | |
| const onFocus = vi.fn(); | |
| render(<SldEditPanel | |
| {...defaultProps} | |
| onFocus={onFocus} | |
| pendingChanges={[ | |
| { switchId: 'SW_A', baselineOpen: false, targetOpen: true }, | |
| { switchId: 'SW_B', baselineOpen: true, targetOpen: false }, | |
| ]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| focusedSwitchId="SW_A" | |
| onFocus={onFocus} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| onRemove={onRemove} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| onRemoveMany={onRemoveMany} | |
| pendingChanges={[ | |
| { switchId: 'SW_A', baselineOpen: false, targetOpen: true }, | |
| { switchId: 'SW_B', baselineOpen: true, targetOpen: false }, | |
| { switchId: 'SW_C', baselineOpen: false, targetOpen: true }, | |
| ]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| />); | |
| expect(screen.queryByTestId('sld-edit-remove-selected')).toBeNull(); | |
| }); | |
| it('lists staged injection retunes with baseline→target MW', () => { | |
| render(<SldEditPanel | |
| {...defaultProps} | |
| injectionChanges={[ | |
| { equipmentId: 'GEN_A', kind: 'generator', baselineP: 120, targetP: 90 }, | |
| { equipmentId: 'LOAD_A', kind: 'load', baselineP: 50, targetP: 30 }, | |
| ]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| onInjectionRemove={onInjectionRemove} | |
| injectionChanges={[{ equipmentId: 'GEN_A', kind: 'generator', baselineP: 120, targetP: 90 }]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| onFocus={onFocus} | |
| injectionChanges={[{ equipmentId: 'GEN_A', kind: 'generator', baselineP: 120, targetP: 90 }]} | |
| />); | |
| 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(<SldEditPanel | |
| {...defaultProps} | |
| pendingChanges={[{ switchId: 'SW_A', baselineOpen: false, targetOpen: true }]} | |
| injectionChanges={[ | |
| { equipmentId: 'GEN_A', kind: 'generator', baselineP: 120, targetP: 90 }, | |
| { equipmentId: 'LOAD_A', kind: 'load', baselineP: 50, targetP: 30 }, | |
| ]} | |
| />); | |
| 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(); | |
| }); | |
| }); | |