add tests
This commit is contained in:
+98
@@ -117,4 +117,102 @@ describe('EmptyTransformationsMessage', () => {
|
||||
expect(screen.getByTestId(selectors.components.Transforms.addTransformationButton)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SQL card disabled state', () => {
|
||||
beforeEach(() => {
|
||||
config.featureToggles.transformationsEmptyPlaceholder = true;
|
||||
config.featureToggles.sqlExpressions = true;
|
||||
});
|
||||
|
||||
// Helper to check if the info icon button is present (rendered when disabled)
|
||||
const getInfoIconButton = (container: HTMLElement) => {
|
||||
// The IconButton for info renders as a button with an SVG icon
|
||||
// When disabled, there are 2 buttons: the card button and the info icon button
|
||||
const buttons = container.querySelectorAll('button');
|
||||
return buttons.length > 1 ? buttons[1] : null;
|
||||
};
|
||||
|
||||
it('should show disabled SQL card with info icon when isSqlApplicable is false', () => {
|
||||
render(
|
||||
<EmptyTransformationsMessage
|
||||
onShowPicker={onShowPicker}
|
||||
onGoToQueries={onGoToQueries}
|
||||
onAddTransformation={onAddTransformation}
|
||||
isSqlApplicable={false}
|
||||
/>
|
||||
);
|
||||
|
||||
const sqlCard = screen.getByTestId('go-to-queries-button');
|
||||
// Should show info icon button when disabled (2 buttons total)
|
||||
expect(getInfoIconButton(sqlCard)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not call onGoToQueries when SQL card is disabled and clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<EmptyTransformationsMessage
|
||||
onShowPicker={onShowPicker}
|
||||
onGoToQueries={onGoToQueries}
|
||||
onAddTransformation={onAddTransformation}
|
||||
isSqlApplicable={false}
|
||||
/>
|
||||
);
|
||||
|
||||
const sqlCard = screen.getByTestId('go-to-queries-button');
|
||||
const button = sqlCard.querySelector('button');
|
||||
await user.click(button!);
|
||||
|
||||
// onGoToQueries should NOT be called when disabled
|
||||
expect(onGoToQueries).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call onGoToQueries when SQL card is enabled and clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<EmptyTransformationsMessage
|
||||
onShowPicker={onShowPicker}
|
||||
onGoToQueries={onGoToQueries}
|
||||
onAddTransformation={onAddTransformation}
|
||||
isSqlApplicable={true}
|
||||
/>
|
||||
);
|
||||
|
||||
const sqlCard = screen.getByTestId('go-to-queries-button');
|
||||
const button = sqlCard.querySelector('button');
|
||||
await user.click(button!);
|
||||
|
||||
expect(onGoToQueries).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not show info icon when SQL card is enabled', () => {
|
||||
render(
|
||||
<EmptyTransformationsMessage
|
||||
onShowPicker={onShowPicker}
|
||||
onGoToQueries={onGoToQueries}
|
||||
onAddTransformation={onAddTransformation}
|
||||
isSqlApplicable={true}
|
||||
/>
|
||||
);
|
||||
|
||||
const sqlCard = screen.getByTestId('go-to-queries-button');
|
||||
// Should NOT show info icon button when enabled (only 1 button)
|
||||
expect(getInfoIconButton(sqlCard)).toBeNull();
|
||||
});
|
||||
|
||||
it('should default to enabled when isSqlApplicable is not provided', () => {
|
||||
render(
|
||||
<EmptyTransformationsMessage
|
||||
onShowPicker={onShowPicker}
|
||||
onGoToQueries={onGoToQueries}
|
||||
onAddTransformation={onAddTransformation}
|
||||
/>
|
||||
);
|
||||
|
||||
const sqlCard = screen.getByTestId('go-to-queries-button');
|
||||
// Should NOT show info icon button when default (enabled)
|
||||
expect(getInfoIconButton(sqlCard)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+173
-2
@@ -11,9 +11,11 @@ import {
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { DataSourceSrv, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { SceneDataTransformer, SceneQueryRunner } from '@grafana/scenes';
|
||||
import config from 'app/core/config';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { ExpressionDatasourceUID } from 'app/features/expressions/types';
|
||||
import { getStandardTransformers } from 'app/features/transformers/standardTransformers';
|
||||
import { DashboardDataDTO } from 'app/types/dashboard';
|
||||
|
||||
@@ -24,14 +26,40 @@ import { testDashboard } from '../testfiles/testDashboard';
|
||||
|
||||
import { PanelDataTransformationsTab, PanelDataTransformationsTabRendered } from './PanelDataTransformationsTab';
|
||||
|
||||
// Mock getDataSourceSrv
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
const actual = jest.requireActual('@grafana/runtime');
|
||||
return {
|
||||
...actual,
|
||||
getDataSourceSrv: jest.fn(() => ({
|
||||
getInstanceSettings: jest.fn(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
const getDataSourceSrvMock = getDataSourceSrv as jest.MockedFunction<typeof getDataSourceSrv>;
|
||||
|
||||
// Helper to create DataSourceSrv mock with custom getInstanceSettings
|
||||
const createMockDataSourceSrv = (
|
||||
getInstanceSettingsFn: (ref: { uid?: string; type?: string } | undefined) => unknown
|
||||
): DataSourceSrv =>
|
||||
({
|
||||
get: jest.fn(),
|
||||
getList: jest.fn(),
|
||||
getInstanceSettings: getInstanceSettingsFn,
|
||||
reload: jest.fn(),
|
||||
registerRuntimeDataSource: jest.fn(),
|
||||
}) as unknown as DataSourceSrv;
|
||||
|
||||
function createModelMock(
|
||||
panelData: PanelData,
|
||||
transformations?: DataTransformerConfig[],
|
||||
onChangeTransformationsMock?: Function
|
||||
onChangeTransformationsMock?: Function,
|
||||
queries: Array<{ refId: string; datasource?: { uid?: string; type?: string } }> = []
|
||||
) {
|
||||
return {
|
||||
getDataTransformer: () => new SceneDataTransformer({ data: panelData, transformations: transformations || [] }),
|
||||
getQueryRunner: () => new SceneQueryRunner({ queries: [], data: panelData }),
|
||||
getQueryRunner: () => new SceneQueryRunner({ queries, data: panelData }),
|
||||
onChangeTransformations: onChangeTransformationsMock,
|
||||
} as unknown as PanelDataTransformationsTab;
|
||||
}
|
||||
@@ -208,6 +236,149 @@ describe('PanelDataTransformationsTab', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('SQL Expression applicability', () => {
|
||||
standardTransformersRegistry.setInit(getStandardTransformers);
|
||||
|
||||
let originalTransformationsToggle: boolean | undefined;
|
||||
let originalSqlToggle: boolean | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
originalTransformationsToggle = config.featureToggles.transformationsEmptyPlaceholder;
|
||||
originalSqlToggle = config.featureToggles.sqlExpressions;
|
||||
config.featureToggles.transformationsEmptyPlaceholder = true;
|
||||
config.featureToggles.sqlExpressions = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.featureToggles.transformationsEmptyPlaceholder = originalTransformationsToggle;
|
||||
config.featureToggles.sqlExpressions = originalSqlToggle;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
// Helper to check if the info icon button is present (rendered when disabled)
|
||||
// The IconButton for info renders as a button with an SVG icon
|
||||
// When disabled, there are 2 buttons: the card button and the info icon button
|
||||
const getInfoIconButton = (container: HTMLElement) => {
|
||||
const buttons = container.querySelectorAll('button');
|
||||
return buttons.length > 1 ? buttons[1] : null;
|
||||
};
|
||||
|
||||
it('should enable SQL card when datasource is a backend datasource', async () => {
|
||||
getDataSourceSrvMock.mockReturnValue(
|
||||
createMockDataSourceSrv(() => ({
|
||||
uid: 'prometheus',
|
||||
name: 'Prometheus',
|
||||
meta: { backend: true },
|
||||
}))
|
||||
);
|
||||
|
||||
const modelMock = createModelMock(mockData, [], undefined, [
|
||||
{ refId: 'A', datasource: { uid: 'prometheus', type: 'prometheus' } },
|
||||
]);
|
||||
|
||||
render(<PanelDataTransformationsTabRendered model={modelMock} />);
|
||||
|
||||
const sqlCard = await screen.findByTestId('go-to-queries-button');
|
||||
// Card should NOT have disabled background styling - check it's clickable
|
||||
expect(sqlCard).toBeInTheDocument();
|
||||
// The card should not show the disabled info icon button
|
||||
expect(getInfoIconButton(sqlCard)).toBeNull();
|
||||
});
|
||||
|
||||
it('should disable SQL card when datasource is frontend-only', async () => {
|
||||
getDataSourceSrvMock.mockReturnValue(
|
||||
createMockDataSourceSrv(() => ({
|
||||
uid: 'googlesheets',
|
||||
name: 'Google Sheets',
|
||||
meta: { backend: false, isBackend: false },
|
||||
}))
|
||||
);
|
||||
|
||||
const modelMock = createModelMock(mockData, [], undefined, [
|
||||
{ refId: 'A', datasource: { uid: 'googlesheets', type: 'grafana-googlesheets-datasource' } },
|
||||
]);
|
||||
|
||||
render(<PanelDataTransformationsTabRendered model={modelMock} />);
|
||||
|
||||
const sqlCard = await screen.findByTestId('go-to-queries-button');
|
||||
// Card should show the disabled info icon button
|
||||
expect(getInfoIconButton(sqlCard)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable SQL card when datasource settings cannot be found', async () => {
|
||||
// Return undefined for getInstanceSettings - simulating unknown datasource
|
||||
getDataSourceSrvMock.mockReturnValue(createMockDataSourceSrv(() => undefined));
|
||||
|
||||
const modelMock = createModelMock(mockData, [], undefined, [
|
||||
{ refId: 'A', datasource: { uid: 'unknown-ds', type: 'unknown' } },
|
||||
]);
|
||||
|
||||
render(<PanelDataTransformationsTabRendered model={modelMock} />);
|
||||
|
||||
const sqlCard = await screen.findByTestId('go-to-queries-button');
|
||||
// Card should NOT be disabled when we can't determine datasource type
|
||||
expect(getInfoIconButton(sqlCard)).toBeNull();
|
||||
});
|
||||
|
||||
it('should skip expression queries when checking SQL applicability', async () => {
|
||||
getDataSourceSrvMock.mockReturnValue(
|
||||
createMockDataSourceSrv((ref) => {
|
||||
if (ref?.uid === ExpressionDatasourceUID) {
|
||||
// Expression datasource - this should be skipped
|
||||
return { uid: ExpressionDatasourceUID, name: 'Expression', meta: { backend: false } };
|
||||
}
|
||||
// Backend datasource
|
||||
return { uid: 'prometheus', name: 'Prometheus', meta: { backend: true } };
|
||||
})
|
||||
);
|
||||
|
||||
const modelMock = createModelMock(mockData, [], undefined, [
|
||||
{ refId: 'A', datasource: { uid: 'prometheus', type: 'prometheus' } },
|
||||
{ refId: 'B', datasource: { uid: ExpressionDatasourceUID, type: '__expr__' } }, // Expression query
|
||||
]);
|
||||
|
||||
render(<PanelDataTransformationsTabRendered model={modelMock} />);
|
||||
|
||||
const sqlCard = await screen.findByTestId('go-to-queries-button');
|
||||
// Should still be enabled because expression queries are skipped
|
||||
expect(getInfoIconButton(sqlCard)).toBeNull();
|
||||
});
|
||||
|
||||
it('should disable SQL card if any non-expression query uses frontend-only datasource', async () => {
|
||||
getDataSourceSrvMock.mockReturnValue(
|
||||
createMockDataSourceSrv((ref) => {
|
||||
if (ref?.uid === 'googlesheets') {
|
||||
return { uid: 'googlesheets', name: 'Google Sheets', meta: { backend: false, isBackend: false } };
|
||||
}
|
||||
return { uid: 'prometheus', name: 'Prometheus', meta: { backend: true } };
|
||||
})
|
||||
);
|
||||
|
||||
const modelMock = createModelMock(mockData, [], undefined, [
|
||||
{ refId: 'A', datasource: { uid: 'prometheus', type: 'prometheus' } },
|
||||
{ refId: 'B', datasource: { uid: 'googlesheets', type: 'grafana-googlesheets-datasource' } },
|
||||
]);
|
||||
|
||||
render(<PanelDataTransformationsTabRendered model={modelMock} />);
|
||||
|
||||
const sqlCard = await screen.findByTestId('go-to-queries-button');
|
||||
// Card should be disabled because one datasource is frontend-only
|
||||
expect(getInfoIconButton(sqlCard)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable SQL card when there are no queries', async () => {
|
||||
getDataSourceSrvMock.mockReturnValue(createMockDataSourceSrv(() => undefined));
|
||||
|
||||
const modelMock = createModelMock(mockData, [], undefined, []);
|
||||
|
||||
render(<PanelDataTransformationsTabRendered model={modelMock} />);
|
||||
|
||||
const sqlCard = await screen.findByTestId('go-to-queries-button');
|
||||
// Card should be enabled when there are no queries
|
||||
expect(getInfoIconButton(sqlCard)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
function setupTabScene(panelId: string) {
|
||||
const scene = transformSaveModelToScene({ dashboard: testDashboard as unknown as DashboardDataDTO, meta: {} });
|
||||
const panel = findVizPanelByKey(scene, panelId)!;
|
||||
|
||||
Reference in New Issue
Block a user