import { act, fireEvent, render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import { ReplaySubject } from 'rxjs';
import { TimeSrvStub } from 'test/specs/helpers';
import {
dateTime,
EventBusSrv,
getDefaultTimeRange,
LoadingState,
PanelData,
PanelPlugin,
TimeRange,
} from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { getTimeSrv, TimeSrv, setTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { PanelQueryRunner } from '../../../query/state/PanelQueryRunner';
import { PanelModel } from '../../state';
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
import { PanelEditorTableView, Props } from './PanelEditorTableView';
jest.mock('../../utils/panel', () => ({
applyPanelTimeOverrides: jest.fn((panel, timeRange) => ({
...timeRange,
})),
}));
jest.mock('app/features/panel/components/PanelRenderer', () => ({
PanelRenderer: jest.fn(() =>
PanelRenderer Mock
),
}));
function setupTestContext(options: Partial = {}) {
const mockStore = configureMockStore();
const subject: ReplaySubject = new ReplaySubject();
const panelQueryRunner = {
getData: () => subject,
run: () => {
subject.next({ state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() });
},
} as unknown as PanelQueryRunner;
const defaults = {
panel: new PanelModel({
id: 123,
hasTitle: jest.fn(),
replaceVariables: jest.fn(),
events: new EventBusSrv(),
getQueryRunner: () => panelQueryRunner,
getOptions: jest.fn(),
getDisplayTitle: jest.fn(),
runAllPanelQueries: jest.fn(),
}),
dashboard: createDashboardModelFixture({
id: 1,
uid: 'super-unique-id',
// panelInitialized: jest.fn(),
// events: new EventBusSrv(),
panels: [],
}),
plugin: {
meta: { skipDataQuery: false },
panel: TestPanelComponent,
} as unknown as PanelPlugin,
isViewing: false,
isEditing: true,
isInView: false,
width: 100,
height: 100,
onInstanceStateChange: () => {},
};
// Set up the mock store with the defaults
const store = mockStore({ dashboard: defaults.dashboard });
const timeSrv = getTimeSrv();
const props = { ...defaults, ...options };
const { rerender } = render(
);
return { rerender, props, subject, store, timeSrv };
}
describe('PanelEditorTableView', () => {
beforeAll(() => {
// Mock the timeSrv singleton
const timeSrvMock2 = new TimeSrvStub() as unknown as TimeSrv;
setTimeSrv(timeSrvMock2);
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should render', async () => {
const { rerender, props, subject, store } = setupTestContext({});
// only render the panel when loading is done
act(() => {
subject.next({ state: LoadingState.Loading, series: [], timeRange: getDefaultTimeRange() });
subject.next({ state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() });
});
const newProps = { ...props, isInView: true };
rerender(
);
expect(screen.getByText(/PanelRenderer Mock/i)).toBeInTheDocument();
});
it('should run all panel queries if time changes', async () => {
const { rerender, props, subject, store, timeSrv } = setupTestContext({});
const timeRangeUpdated = {
from: dateTime([2019, 1, 11, 12, 0]),
to: dateTime([2019, 1, 11, 18, 0]),
} as unknown as TimeRange;
// only render the panel when loading is done
act(() => {
subject.next({ state: LoadingState.Loading, series: [], timeRange: getDefaultTimeRange() });
subject.next({ state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() });
});
const newProps = { ...props, isInView: true };
rerender(
);
expect(screen.getByText(/PanelRenderer Mock/i)).toBeInTheDocument();
// updating the global time range
act(() => {
timeSrv.setTime(timeRangeUpdated);
props.panel.refresh();
});
// panel queries should have the updated time range
expect(props.panel.runAllPanelQueries).toHaveBeenNthCalledWith(1, {
dashboardTimezone: '',
dashboardUID: props.dashboard.uid,
timeData: timeRangeUpdated,
width: 100,
});
// update global time second time
const timeRangeUpdated2 = {
from: dateTime([2018, 1, 11, 12, 0]),
to: dateTime([2018, 1, 11, 18, 0]),
} as unknown as TimeRange;
act(() => {
timeSrv.setTime(timeRangeUpdated2);
props.panel.refresh();
});
// panel queries should have the updated time range
expect(props.panel.runAllPanelQueries).toHaveBeenLastCalledWith({
dashboardTimezone: '',
dashboardUID: props.dashboard.uid,
timeData: timeRangeUpdated2,
width: 100,
});
});
it('should render an error', async () => {
const { rerender, props, subject, store } = setupTestContext({});
// only render the panel when loading is done
act(() => {
subject.next({ state: LoadingState.Loading, series: [], timeRange: getDefaultTimeRange() });
subject.next({
state: LoadingState.Error,
series: [],
errors: [{ message: 'boom!' }],
timeRange: getDefaultTimeRange(),
});
});
const newProps = { ...props, isInView: true };
rerender(
);
const button = screen.getByRole('button', { name: selectors.components.Panels.Panel.headerCornerInfo('error') });
expect(button).toBeInTheDocument();
await act(async () => {
fireEvent.focus(button);
});
expect(await screen.findByText('boom!')).toBeInTheDocument();
});
it('should render a description for multiple errors', async () => {
const { rerender, props, subject, store } = setupTestContext({});
// only render the panel when loading is done
act(() => {
subject.next({ state: LoadingState.Loading, series: [], timeRange: getDefaultTimeRange() });
subject.next({
state: LoadingState.Error,
series: [],
errors: [{ message: 'boom 1!' }, { message: 'boom 2!' }],
timeRange: getDefaultTimeRange(),
});
});
const newProps = { ...props, isInView: true };
rerender(
);
const button = screen.getByRole('button', { name: selectors.components.Panels.Panel.headerCornerInfo('error') });
expect(button).toBeInTheDocument();
await act(async () => {
fireEvent.focus(button);
});
expect(await screen.findByText('Multiple errors found. Click for more details')).toBeInTheDocument();
});
});
const TestPanelComponent = () => Plugin Panel to Render
;