mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 13:09:22 -06:00
Dashboard Scene: Chore - Add missing unit test to PanelDataQueryTab (#87139)
* Create basic structure PanelDataQueriesTab unit test * Add more test cases to the PanelDataQueriesTab test * Update public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx
This commit is contained in:
parent
2eab915b80
commit
eae1cafebd
@ -286,6 +286,8 @@ export const Components = {
|
||||
queryInspectorButton: 'Query inspector button',
|
||||
queryHistoryButton: 'data-testid query-history-button',
|
||||
addQuery: 'data-testid query-tab-add-query',
|
||||
queryGroupTopSection: 'data-testid query group top section',
|
||||
addExpression: 'data-testid query-tab-add-expression',
|
||||
},
|
||||
QueryHistory: {
|
||||
queryText: 'Query text',
|
||||
|
@ -0,0 +1,346 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { of, map } from 'rxjs';
|
||||
|
||||
import {
|
||||
DataQuery,
|
||||
DataQueryRequest,
|
||||
DataSourceApi,
|
||||
DataSourceJsonData,
|
||||
DataSourceRef,
|
||||
FieldType,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
TimeRange,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
|
||||
import { DASHBOARD_DATASOURCE_PLUGIN_ID } from 'app/plugins/datasource/dashboard/types';
|
||||
import { DashboardDataDTO } from 'app/types';
|
||||
|
||||
import { transformSaveModelToScene } from '../../serialization/transformSaveModelToScene';
|
||||
import { DashboardModelCompatibilityWrapper } from '../../utils/DashboardModelCompatibilityWrapper';
|
||||
import { findVizPanelByKey } from '../../utils/utils';
|
||||
import { VizPanelManager } from '../VizPanelManager';
|
||||
import { testDashboard } from '../testfiles/testDashboard';
|
||||
|
||||
import { PanelDataQueriesTab, PanelDataQueriesTabRendered } from './PanelDataQueriesTab';
|
||||
|
||||
async function createModelMock() {
|
||||
const panelManager = setupVizPanelManger('panel-1');
|
||||
panelManager.activate();
|
||||
await Promise.resolve();
|
||||
const queryTabModel = new PanelDataQueriesTab(panelManager);
|
||||
|
||||
// mock queryRunner data state
|
||||
jest.spyOn(queryTabModel.queryRunner, 'state', 'get').mockReturnValue({
|
||||
...queryTabModel.queryRunner.state,
|
||||
data: {
|
||||
state: LoadingState.Done,
|
||||
series: [
|
||||
toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [100, 200, 300] },
|
||||
{ name: 'values', type: FieldType.number, values: [1, 2, 3] },
|
||||
],
|
||||
}),
|
||||
],
|
||||
timeRange: {} as TimeRange,
|
||||
},
|
||||
});
|
||||
|
||||
return queryTabModel;
|
||||
}
|
||||
const runRequestMock = jest.fn().mockImplementation((ds: DataSourceApi, request: DataQueryRequest) => {
|
||||
const result: PanelData = {
|
||||
state: LoadingState.Loading,
|
||||
series: [],
|
||||
timeRange: request.range,
|
||||
};
|
||||
|
||||
return of([]).pipe(
|
||||
map(() => {
|
||||
result.state = LoadingState.Done;
|
||||
result.series = [
|
||||
toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [100, 200, 300] },
|
||||
{ name: 'values', type: FieldType.number, values: [1, 2, 3] },
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
return result;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const ds1Mock: DataSourceApi = {
|
||||
meta: {
|
||||
id: 'grafana-testdata-datasource',
|
||||
},
|
||||
name: 'grafana-testdata-datasource',
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'gdev-testdata',
|
||||
getRef: () => {
|
||||
return { type: 'grafana-testdata-datasource', uid: 'gdev-testdata' };
|
||||
},
|
||||
} as DataSourceApi<DataQuery, DataSourceJsonData, {}>;
|
||||
|
||||
const ds2Mock: DataSourceApi = {
|
||||
meta: {
|
||||
id: 'grafana-prometheus-datasource',
|
||||
},
|
||||
name: 'grafana-prometheus-datasource',
|
||||
type: 'grafana-prometheus-datasource',
|
||||
uid: 'gdev-prometheus',
|
||||
getRef: () => {
|
||||
return { type: 'grafana-prometheus-datasource', uid: 'gdev-prometheus' };
|
||||
},
|
||||
} as DataSourceApi<DataQuery, DataSourceJsonData, {}>;
|
||||
|
||||
const ds3Mock: DataSourceApi = {
|
||||
meta: {
|
||||
id: DASHBOARD_DATASOURCE_PLUGIN_ID,
|
||||
},
|
||||
name: SHARED_DASHBOARD_QUERY,
|
||||
type: SHARED_DASHBOARD_QUERY,
|
||||
uid: SHARED_DASHBOARD_QUERY,
|
||||
getRef: () => {
|
||||
return { type: SHARED_DASHBOARD_QUERY, uid: SHARED_DASHBOARD_QUERY };
|
||||
},
|
||||
} as DataSourceApi<DataQuery, DataSourceJsonData, {}>;
|
||||
|
||||
const defaultDsMock: DataSourceApi = {
|
||||
meta: {
|
||||
id: 'grafana-testdata-datasource',
|
||||
},
|
||||
name: 'grafana-testdata-datasource',
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'gdev-testdata',
|
||||
getRef: () => {
|
||||
return { type: 'grafana-testdata-datasource', uid: 'gdev-testdata' };
|
||||
},
|
||||
} as DataSourceApi<DataQuery, DataSourceJsonData, {}>;
|
||||
|
||||
const instance1SettingsMock = {
|
||||
id: 1,
|
||||
uid: 'gdev-testdata',
|
||||
name: 'testDs1',
|
||||
type: 'grafana-testdata-datasource',
|
||||
meta: {
|
||||
id: 'grafana-testdata-datasource',
|
||||
info: {
|
||||
logos: {
|
||||
small: 'test-logo.png',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const instance2SettingsMock = {
|
||||
id: 1,
|
||||
uid: 'gdev-prometheus',
|
||||
name: 'testDs2',
|
||||
type: 'grafana-prometheus-datasource',
|
||||
meta: {
|
||||
id: 'grafana-prometheus-datasource',
|
||||
},
|
||||
};
|
||||
|
||||
// Mocking the build in Grafana data source to avoid annotations data layer errors.
|
||||
const grafanaDs = {
|
||||
id: 1,
|
||||
uid: '-- Grafana --',
|
||||
name: 'grafana',
|
||||
type: 'grafana',
|
||||
meta: {
|
||||
id: 'grafana',
|
||||
},
|
||||
};
|
||||
|
||||
// Mocking the build in Grafana data source to avoid annotations data layer errors.
|
||||
const MixedDs = {
|
||||
id: 5,
|
||||
uid: '-- Mixed --',
|
||||
name: 'Mixed',
|
||||
type: 'datasource',
|
||||
meta: {
|
||||
id: 'grafana',
|
||||
mixed: true,
|
||||
},
|
||||
};
|
||||
|
||||
const MixedDsSettingsMock = {
|
||||
id: 5,
|
||||
uid: '-- Mixed --',
|
||||
name: 'Mixed',
|
||||
type: 'datasource',
|
||||
meta: {
|
||||
id: 'grafana',
|
||||
mixed: true,
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getRunRequest: () => (ds: DataSourceApi, request: DataQueryRequest) => {
|
||||
return runRequestMock(ds, request);
|
||||
},
|
||||
getDataSourceSrv: () => ({
|
||||
get: async (ref: DataSourceRef) => {
|
||||
// Mocking the build in Grafana data source to avoid annotations data layer errors.
|
||||
if (ref.uid === '-- Grafana --') {
|
||||
return grafanaDs;
|
||||
}
|
||||
|
||||
if (ref.uid === 'gdev-testdata') {
|
||||
return ds1Mock;
|
||||
}
|
||||
|
||||
if (ref.uid === 'gdev-prometheus') {
|
||||
return ds2Mock;
|
||||
}
|
||||
|
||||
if (ref.uid === '-- Mixed --') {
|
||||
return MixedDs;
|
||||
}
|
||||
|
||||
if (ref.uid === SHARED_DASHBOARD_QUERY) {
|
||||
return ds3Mock;
|
||||
}
|
||||
|
||||
// if datasource is not found, return default datasource
|
||||
return defaultDsMock;
|
||||
},
|
||||
getInstanceSettings: (ref: DataSourceRef) => {
|
||||
if (ref.uid === 'gdev-testdata') {
|
||||
return instance1SettingsMock;
|
||||
}
|
||||
|
||||
if (ref.uid === 'gdev-prometheus') {
|
||||
return instance2SettingsMock;
|
||||
}
|
||||
|
||||
if (ref.uid === '-- Mixed --') {
|
||||
return MixedDsSettingsMock;
|
||||
}
|
||||
|
||||
// if datasource is not found, return default instance settings
|
||||
return instance1SettingsMock;
|
||||
},
|
||||
}),
|
||||
locationService: {
|
||||
partial: jest.fn(),
|
||||
getSearchObject: jest.fn().mockReturnValue({
|
||||
firstPanel: false,
|
||||
}),
|
||||
},
|
||||
config: {
|
||||
...jest.requireActual('@grafana/runtime').config,
|
||||
defaultDatasource: 'gdev-testdata',
|
||||
},
|
||||
}));
|
||||
describe('PanelDataQueriesModel', () => {
|
||||
it('can add a new query', async () => {
|
||||
const vizPanelManager = setupVizPanelManger('panel-1');
|
||||
vizPanelManager.activate();
|
||||
await Promise.resolve();
|
||||
|
||||
const model = new PanelDataQueriesTab(vizPanelManager);
|
||||
model.addQueryClick();
|
||||
expect(model.queryRunner.state.queries).toHaveLength(2);
|
||||
expect(model.queryRunner.state.queries[1].refId).toBe('B');
|
||||
expect(model.queryRunner.state.queries[1].hide).toBe(false);
|
||||
expect(model.queryRunner.state.queries[1].datasource).toEqual({
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'gdev-testdata',
|
||||
});
|
||||
});
|
||||
|
||||
it('can add a new query when datasource is mixed', async () => {
|
||||
const vizPanelManager = setupVizPanelManger('panel-7');
|
||||
vizPanelManager.activate();
|
||||
await Promise.resolve();
|
||||
|
||||
const model = new PanelDataQueriesTab(vizPanelManager);
|
||||
expect(vizPanelManager.state.datasource?.uid).toBe('-- Mixed --');
|
||||
expect(model.queryRunner.state.datasource?.uid).toBe('-- Mixed --');
|
||||
model.addQueryClick();
|
||||
|
||||
expect(model.queryRunner.state.queries).toHaveLength(2);
|
||||
expect(model.queryRunner.state.queries[1].refId).toBe('B');
|
||||
expect(model.queryRunner.state.queries[1].hide).toBe(false);
|
||||
expect(model.queryRunner.state.queries[1].datasource?.uid).toBe('gdev-testdata');
|
||||
});
|
||||
});
|
||||
|
||||
describe('PanelDataQueriesTab', () => {
|
||||
it('renders query group top section', async () => {
|
||||
const modelMock = await createModelMock();
|
||||
|
||||
render(<PanelDataQueriesTabRendered model={modelMock}></PanelDataQueriesTabRendered>);
|
||||
await screen.findByTestId(selectors.components.QueryTab.queryGroupTopSection);
|
||||
});
|
||||
|
||||
it('renders queries rows when queries are set', async () => {
|
||||
const modelMock = await createModelMock();
|
||||
render(<PanelDataQueriesTabRendered model={modelMock}></PanelDataQueriesTabRendered>);
|
||||
|
||||
await screen.findByTestId('query-editor-rows');
|
||||
expect(screen.getAllByTestId('query-editor-row')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('allow to add a new query when user clicks on add new', async () => {
|
||||
const modelMock = await createModelMock();
|
||||
jest.spyOn(modelMock, 'addQueryClick');
|
||||
jest.spyOn(modelMock, 'onQueriesChange');
|
||||
render(<PanelDataQueriesTabRendered model={modelMock}></PanelDataQueriesTabRendered>);
|
||||
|
||||
await screen.findByTestId(selectors.components.QueryTab.addQuery);
|
||||
await userEvent.click(screen.getByTestId(selectors.components.QueryTab.addQuery));
|
||||
|
||||
const expectedQueries = [
|
||||
{
|
||||
datasource: { type: 'grafana-testdata-datasource', uid: 'gdev-testdata' },
|
||||
refId: 'A',
|
||||
scenarioId: 'random_walk',
|
||||
seriesCount: 1,
|
||||
},
|
||||
{ datasource: { type: 'grafana-testdata-datasource', uid: 'gdev-testdata' }, hide: false, refId: 'B' },
|
||||
];
|
||||
|
||||
expect(modelMock.addQueryClick).toHaveBeenCalled();
|
||||
expect(modelMock.onQueriesChange).toHaveBeenCalledWith(expectedQueries);
|
||||
});
|
||||
|
||||
it('allow to remove a query when user clicks on remove', async () => {
|
||||
const modelMock = await createModelMock();
|
||||
jest.spyOn(modelMock, 'addQueryClick');
|
||||
jest.spyOn(modelMock, 'onQueriesChange');
|
||||
render(<PanelDataQueriesTabRendered model={modelMock}></PanelDataQueriesTabRendered>);
|
||||
|
||||
await screen.findByTestId('data-testid Remove query');
|
||||
await userEvent.click(screen.getByTestId('data-testid Remove query'));
|
||||
|
||||
expect(modelMock.onQueriesChange).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
|
||||
const setupVizPanelManger = (panelId: string) => {
|
||||
const scene = transformSaveModelToScene({ dashboard: testDashboard as unknown as DashboardDataDTO, meta: {} });
|
||||
const panel = findVizPanelByKey(scene, panelId)!;
|
||||
|
||||
const vizPanelManager = VizPanelManager.createFor(panel);
|
||||
|
||||
// The following happens on DahsboardScene activation. For the needs of this test this activation aint needed hence we hand-call it
|
||||
// @ts-expect-error
|
||||
getDashboardSrv().setCurrent(new DashboardModelCompatibilityWrapper(scene));
|
||||
|
||||
return vizPanelManager;
|
||||
};
|
@ -185,7 +185,7 @@ export class PanelDataQueriesTab extends SceneObjectBase<PanelDataQueriesTabStat
|
||||
}
|
||||
}
|
||||
|
||||
function PanelDataQueriesTabRendered({ model }: SceneComponentProps<PanelDataQueriesTab>) {
|
||||
export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<PanelDataQueriesTab>) {
|
||||
const { datasource, dsSettings } = model.panelManager.useState();
|
||||
const { data, queries } = model.panelManager.queryRunner.useState();
|
||||
|
||||
@ -232,7 +232,7 @@ function PanelDataQueriesTabRendered({ model }: SceneComponentProps<PanelDataQue
|
||||
icon="plus"
|
||||
onClick={model.onAddExpressionClick}
|
||||
variant="secondary"
|
||||
data-testid="query-tab-add-expression"
|
||||
data-testid={selectors.components.QueryTab.addExpression}
|
||||
>
|
||||
<span>Expression </span>
|
||||
</Button>
|
||||
|
@ -498,6 +498,99 @@ export const panelWithDataSourceNotFound = {
|
||||
type: 'timeseries',
|
||||
};
|
||||
|
||||
export const panelWithQueriesAndMixedDatasource = {
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: '-- Mixed --',
|
||||
},
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
color: {
|
||||
mode: 'palette-classic',
|
||||
},
|
||||
custom: {
|
||||
axisBorderShow: false,
|
||||
axisCenteredZero: false,
|
||||
axisColorMode: 'text',
|
||||
axisLabel: '',
|
||||
axisPlacement: 'auto',
|
||||
barAlignment: 0,
|
||||
drawStyle: 'line',
|
||||
fillOpacity: 0,
|
||||
gradientMode: 'none',
|
||||
hideFrom: {
|
||||
legend: false,
|
||||
tooltip: false,
|
||||
viz: false,
|
||||
},
|
||||
insertNulls: false,
|
||||
lineInterpolation: 'linear',
|
||||
lineWidth: 1,
|
||||
pointSize: 5,
|
||||
scaleDistribution: {
|
||||
type: 'linear',
|
||||
},
|
||||
showPoints: 'auto',
|
||||
spanNulls: false,
|
||||
stacking: {
|
||||
group: 'A',
|
||||
mode: 'none',
|
||||
},
|
||||
thresholdsStyle: {
|
||||
mode: 'off',
|
||||
},
|
||||
},
|
||||
mappings: [],
|
||||
thresholds: {
|
||||
mode: 'absolute',
|
||||
steps: [
|
||||
{
|
||||
color: 'green',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
value: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
overrides: [],
|
||||
},
|
||||
gridPos: {
|
||||
h: 8,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
id: 7,
|
||||
options: {
|
||||
legend: {
|
||||
calcs: [],
|
||||
displayMode: 'list',
|
||||
placement: 'bottom',
|
||||
showLegend: true,
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'single',
|
||||
sort: 'none',
|
||||
},
|
||||
},
|
||||
targets: [
|
||||
{
|
||||
datasource: {
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'gdev-testdata',
|
||||
},
|
||||
refId: 'A',
|
||||
scenarioId: 'random_walk',
|
||||
seriesCount: 1,
|
||||
},
|
||||
],
|
||||
title: 'Panel with just queries',
|
||||
type: 'timeseries',
|
||||
};
|
||||
|
||||
export const testDashboard = {
|
||||
annotations: {
|
||||
list: [
|
||||
@ -528,6 +621,7 @@ export const testDashboard = {
|
||||
panelWithDashboardQueryAndTransformations,
|
||||
panelWithNoDataSource,
|
||||
panelWithDataSourceNotFound,
|
||||
panelWithQueriesAndMixedDatasource,
|
||||
],
|
||||
refresh: '',
|
||||
schemaVersion: 39,
|
||||
|
@ -398,7 +398,7 @@ export function QueryGroupTopSection({
|
||||
const [isHelpOpen, setIsHelpOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div data-testid={selectors.components.QueryTab.queryGroupTopSection}>
|
||||
<div className={styles.dataSourceRow}>
|
||||
<InlineFormLabel htmlFor="data-source-picker" width={'auto'}>
|
||||
Data source
|
||||
|
Loading…
Reference in New Issue
Block a user