// 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
// This file is part of Co-Study4Grid a Power Grid Study tool Assistant Interface to help solve contigencies for a grid state under study.
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/vitest';
import ComputedPairsTable, { type ComputedPairEntry } from './ComputedPairsTable';
describe('ComputedPairsTable', () => {
const basePair: ComputedPairEntry = {
id: 'act1+act2',
action1: 'act1',
action2: 'act2',
betas: [1.0, 0.9],
estimated_max_rho: 0.75,
estimated_max_rho_line: 'L3',
is_suspect: false,
isSimulated: false,
simulated_max_rho: null,
simulated_max_rho_line: null,
simData: null,
};
const defaultProps = {
computedPairsList: [basePair],
monitoringFactor: 0.95,
simulating: false,
onSimulate: vi.fn(),
};
it('renders table headers', () => {
render();
expect(screen.getByText('Action 1')).toBeInTheDocument();
expect(screen.getByText('Action 2')).toBeInTheDocument();
expect(screen.getByText('Betas')).toBeInTheDocument();
expect(screen.getByText('Max Loading (Est.)')).toBeInTheDocument();
});
it('renders a pair row with action names and estimated max rho', () => {
render();
expect(screen.getByText('act1')).toBeInTheDocument();
expect(screen.getByText('act2')).toBeInTheDocument();
expect(screen.getByText('75.0%')).toBeInTheDocument();
});
it('renders betas as formatted values', () => {
render();
expect(screen.getByText('1.00, 0.90')).toBeInTheDocument();
});
it('shows "Not simulated" when pair is not simulated', () => {
render();
expect(screen.getByText('Not simulated')).toBeInTheDocument();
});
it('shows Simulate button for unsimulated pairs', () => {
render();
expect(screen.getByText('Simulate')).toBeInTheDocument();
});
it('calls onSimulate when Simulate button is clicked', () => {
const onSimulate = vi.fn();
render();
fireEvent.click(screen.getByText('Simulate'));
expect(onSimulate).toHaveBeenCalledWith('act1+act2');
});
it('shows Re-Simulate button for already simulated pairs', () => {
const simulatedPair: ComputedPairEntry = {
...basePair,
isSimulated: true,
simulated_max_rho: 0.72,
simulated_max_rho_line: 'L3',
simData: { max_rho: 0.72, max_rho_line: 'L3', is_rho_reduction: true },
};
render();
expect(screen.getByText('Re-Simulate')).toBeInTheDocument();
expect(screen.getByText('72.0%')).toBeInTheDocument();
});
it('shows empty state when no pairs exist', () => {
render();
expect(screen.getByText(/No computed combinations found/)).toBeInTheDocument();
expect(screen.getByText(/Explore Pairs/)).toBeInTheDocument();
});
it('colors estimated rho green when below monitoring factor', () => {
const greenPair: ComputedPairEntry = { ...basePair, estimated_max_rho: 0.80 };
render();
const badge = screen.getByText('80.0%');
expect(badge).toBeInTheDocument();
});
it('colors estimated rho red when above monitoring factor', () => {
const redPair: ComputedPairEntry = { ...basePair, estimated_max_rho: 1.05 };
render();
const badge = screen.getByText('105.0%');
expect(badge).toBeInTheDocument();
});
it('shows suspect warning for islanded estimation', () => {
const suspectPair: ComputedPairEntry = { ...basePair, is_suspect: true };
render();
expect(screen.getByTitle('Estimation suspect due to islanding')).toBeInTheDocument();
});
it('disables Simulate button when simulating', () => {
render();
const button = screen.getByText('⌛');
expect(button).toBeDisabled();
});
it('shows "—" for pair with null estimated_max_rho', () => {
const noPair: ComputedPairEntry = { ...basePair, estimated_max_rho: null };
render();
expect(screen.getByText('—')).toBeInTheDocument();
});
it('shows a "target: X% on LINE" subtitle when the target overload differs from the estimated max line', () => {
const focusedPair: ComputedPairEntry = {
...basePair,
estimated_max_rho: 0.82,
estimated_max_rho_line: 'LOUHAL31PYMON',
target_max_rho: 0.20,
target_max_rho_line: 'BEON L31CPVAN',
};
render();
expect(screen.getByText(/target:.*20\.0%.*BEON L31CPVAN/)).toBeInTheDocument();
});
it('does not render target subtitle when target line equals estimated line', () => {
const matchingPair: ComputedPairEntry = {
...basePair,
estimated_max_rho: 0.82,
estimated_max_rho_line: 'L3',
target_max_rho: 0.82,
target_max_rho_line: 'L3',
};
render();
expect(screen.queryByText(/target:/)).not.toBeInTheDocument();
});
it('shows islanding icon for simulated islanded pair', () => {
const islandedPair: ComputedPairEntry = {
...basePair,
isSimulated: true,
simulated_max_rho: 0.70,
simulated_max_rho_line: 'L1',
simData: { max_rho: 0.70, max_rho_line: 'L1', is_rho_reduction: true, is_islanded: true },
};
render();
expect(screen.getByTitle('Islanding detected')).toBeInTheDocument();
});
});