* -------------------------------------------------------------------------
* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*/
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { observer } from 'mobx-react-lite';
import { runInAction } from 'mobx';
import StaticLineChart from '../StaticLineChart';
import { Session } from '../../entity/session';
import { MemorySession } from '../../entity/memorySession';
import { staticOpMemoryGraphGet } from '../../utils/RequestUtils';
import { useTranslation } from 'react-i18next';
import '@testing-library/jest-dom';
import { LineChart } from '../LineChart';
jest.mock('mobx-react-lite', () => ({
observer: (component: React.ComponentType) => component,
}));
jest.mock('../../utils/RequestUtils', () => ({
staticOpMemoryGraphGet: jest.fn(),
}));
jest.mock('react-i18next', () => ({
useTranslation: jest.fn(),
}));
jest.mock('@insight/lib/components', () => ({
Select: ({ value, onChange, options }) => (
<select
data-testid="select-graphId"
value={value}
onChange={(e) => onChange(e.target.value)}
>
{options.map((option: any) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
),
Spin: ({ spinning, children }) => (
<div data-testid="spin" data-spinning={spinning}>
{children}
</div>
),
}));
jest.mock('../Common', () => ({
Label: ({ name }) => <label data-testid="label">{name}</label>,
}));
jest.mock('../LineChart', () => ({
LineChart: ({ onSelectionChanged, graph, isStatic }) => (
<div
data-testid="line-chart"
data-is-static={isStatic}
data-graph-columns={graph?.columns?.join(',')}
onClick={() => onSelectionChanged && onSelectionChanged(0, 10)}
>
LineChart Mock
</div>
),
}));
jest.mock('../../utils/styleUtils', () => ({
SearchBox: ({ children }) => <div data-testid="search-box">{children}</div>,
}));
jest.mock('@insight/lib/utils', () => ({
StyledEmpty: ({ style, translation }) => (
<div data-testid="styled-empty" style={style}>
StyledEmpty Mock - {translation?.('test')}
</div>
),
customConsole: {
error: jest.fn(),
},
}));
const createMockSession = (overrides?: Partial<Session>): Session => ({
compareRank: {
isCompare: false,
},
isAllMemoryCompletedSwitch: false,
...overrides,
});
const createMockMemorySession = (overrides?: Partial<MemorySession>): MemorySession => ({
memoryGraphId: 'graph-1',
memoryGraphIdList: ['graph-1', 'graph-2', 'graph-3'],
selectedRankId: 'rank-1',
staticSelectedRange: undefined,
searchEventOperatorName: '',
current: 1,
pageSize: 10,
getSelectedRankValue: jest.fn(),
...overrides,
});
const mockStaticOperatorCurve = {
legends: ['legend1', 'legend2'],
lines: [
['100', '200', '300'],
['150', '250', '350'],
],
};
describe('StaticLineChart', () => {
const defaultProps = {
session: createMockSession(),
memorySession: createMockMemorySession(),
isDark: false,
};
beforeEach(() => {
jest.clearAllMocks();
(useTranslation as jest.Mock).mockReturnValue({
t: (key: string) => `translated-${key}`,
});
(staticOpMemoryGraphGet as jest.Mock).mockResolvedValue(mockStaticOperatorCurve);
(defaultProps.memorySession.getSelectedRankValue as jest.Mock).mockReturnValue({
rankInfo: { rankId: 'rank-1' },
dbPath: '/test/db/path',
});
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should render without crashing', () => {
render(<StaticLineChart {...defaultProps} />);
expect(screen.getByTestId('search-box')).toBeInTheDocument();
});
it('should render search criteria section with label and select', () => {
render(<StaticLineChart {...defaultProps} />);
expect(screen.getByTestId('label')).toHaveTextContent('translated-searchCriteria.GraphId');
expect(screen.getByTestId('select-graphId')).toBeInTheDocument();
});
it('should render select with correct options from memorySession', () => {
defaultProps.memorySession.memoryGraphIdList = ['option1', 'option2'];
render(<StaticLineChart {...defaultProps} />);
const select = screen.getByTestId('select-graphId');
expect(select).toHaveValue('option1');
});
it('should update memorySession when graph ID is changed', () => {
jest.spyOn(require('mobx'), 'runInAction');
render(<StaticLineChart {...defaultProps} />);
const select = screen.getByTestId('select-graphId');
fireEvent.change(select, { target: { value: 'graph-2' } });
expect(runInAction).toHaveBeenCalled();
});
it('should show loading spinner when fetching static curve data', async () => {
(staticOpMemoryGraphGet as jest.Mock).mockImplementation(() =>
new Promise(resolve => setTimeout(() => resolve(mockStaticOperatorCurve), 100)),
);
render(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('spin')).toHaveAttribute('data-spinning', 'true');
});
});
it('should render LineChart when staticLineChartData is available', async () => {
render(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('line-chart')).toBeInTheDocument();
});
const lineChart = screen.getByTestId('line-chart');
expect(lineChart).toHaveAttribute('data-is-static', 'true');
});
it('should render StyledEmpty when no staticLineChartData is available', () => {
defaultProps.memorySession.rankCondition = { options: [], value: undefined };
render(<StaticLineChart {...defaultProps} />);
expect(screen.getByTestId('styled-empty')).toBeInTheDocument();
});
it('should update static selected range when LineChart selection changes', async () => {
jest.spyOn(require('mobx'), 'runInAction');
render(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('line-chart')).toBeInTheDocument();
});
const lineChart = screen.getByTestId('line-chart');
fireEvent.click(lineChart);
expect(runInAction).toHaveBeenCalled();
});
it('should reset data when selectedRankId is empty', () => {
const memorySession = createMockMemorySession({
selectedRankId: '',
});
render(<StaticLineChart {...defaultProps} memorySession={memorySession} />);
expect(staticOpMemoryGraphGet).not.toHaveBeenCalled();
});
it('should fetch data when selectedRankId changes', async () => {
const { rerender } = render(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
expect(staticOpMemoryGraphGet).toHaveBeenCalledTimes(1);
});
(defaultProps.memorySession.getSelectedRankValue as jest.Mock).mockReturnValue({
rankInfo: { rankId: 'rank-2' },
dbPath: '/test/db/path',
});
rerender(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
expect(staticOpMemoryGraphGet).toHaveBeenCalledTimes(1);
});
});
it('should pass correct isCompare parameter in compare mode', async () => {
const session = createMockSession({
compareRank: { isCompare: true },
});
jest.spyOn(require('mobx'), 'runInAction');
render(<StaticLineChart {...defaultProps} session={session} />);
await waitFor(() => {
expect(staticOpMemoryGraphGet).toHaveBeenCalledWith({
rankId: 'rank-1',
dbPath: '/test/db/path',
graphId: '',
isCompare: true,
});
});
});
it('should transform API response to LineChart data format', async () => {
render(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
const lineChart = screen.getByTestId('line-chart');
expect(lineChart).toHaveAttribute('data-graph-columns', 'translated-legend1,translated-legend2');
});
});
it('should apply correct CSS classes to container', () => {
const { container } = render(<StaticLineChart {...defaultProps} />);
expect(container.querySelector('.mb-30')).toBeInTheDocument();
});
it('should handle empty memoryGraphIdList gracefully', () => {
defaultProps.memorySession.memoryGraphIdList = [];
render(<StaticLineChart {...defaultProps} />);
const select = screen.getByTestId('select-graphId');
expect(select).toBeInTheDocument();
});
it('should validate selection range and reset if invalid', async () => {
await waitFor(() => {
render(<StaticLineChart {...defaultProps} />);
});
const lineChart = screen.getByTestId('line-chart');
fireEvent.click(lineChart);
});
it('should use translation function for all text', () => {
render(<StaticLineChart {...defaultProps} />);
expect(screen.getByTestId('label')).toHaveTextContent('translated-searchCriteria.GraphId');
});
it('should cleanup properly when unmounted', async () => {
const { unmount } = render(<StaticLineChart {...defaultProps} />);
await waitFor(() => {
expect(screen.getByTestId('line-chart')).toBeInTheDocument();
});
unmount();
expect(screen.queryByTestId('line-chart')).not.toBeInTheDocument();
});
});