groupBy per panel action tests

This commit is contained in:
Victor Marin
2025-11-05 15:24:14 +02:00
parent 361fa3582b
commit da84148dc5
3 changed files with 226 additions and 13 deletions
+2 -2
View File
@@ -296,8 +296,8 @@
"@grafana/plugin-ui": "^0.10.10",
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "^6.42.1",
"@grafana/scenes-react": "^6.42.1",
"@grafana/scenes": "6.43.0--canary.1278.19036459049.0",
"@grafana/scenes-react": "6.43.0--canary.1278.19036459049.0",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
@@ -0,0 +1,213 @@
import { of } from 'rxjs';
import { GroupByVariable, SceneQueryRunner, SceneVariableSet, VizPanel } from '@grafana/scenes';
import { DashboardScene } from './DashboardScene';
import { PanelGroupByAction } from './PanelGroupByAction';
import { DefaultGridLayoutManager } from './layout-default/DefaultGridLayoutManager';
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => {
return {
get: jest.fn().mockResolvedValue({}),
getInstanceSettings: jest.fn().mockResolvedValue({ uid: 'ds1' }),
};
},
getPluginImportUtils: () => ({
getPanelPluginFromCache: jest.fn(() => undefined),
}),
}));
describe('PanelGroupByAction', () => {
describe('Initialization', () => {
it('should create PanelGroupByAction instance', () => {
const action = new PanelGroupByAction();
expect(action).toBeDefined();
expect(action).toBeInstanceOf(PanelGroupByAction);
});
it('should find GroupByVariable after activation', () => {
const { action, variable } = buildTestScene();
// Mock validateAndUpdate to avoid hanging
jest.spyOn(variable, 'validateAndUpdate').mockReturnValue(
of({
origin: variable,
state: variable.state,
})
);
const deactivate = action.activate();
const groupByVar = action.getGroupByVariable();
expect(groupByVar).toBeDefined();
expect(groupByVar).toBeInstanceOf(GroupByVariable);
expect(groupByVar?.state.name).toBe('A');
deactivate();
});
describe('GroupBy variable integration', () => {
it('should access variable through scene graph', () => {
const { action, variable } = buildTestScene();
jest.spyOn(variable, 'validateAndUpdate').mockReturnValue(
of({
origin: variable,
state: variable.state,
})
);
const deactivate = action.activate();
const foundVariable = action.getGroupByVariable();
expect(foundVariable).toBe(variable);
expect(foundVariable?.state.type).toBe('groupby');
deactivate();
});
it('should handle missing GroupByVariable gracefully', () => {
const action = new PanelGroupByAction();
const panel = new VizPanel({
title: 'Panel A',
pluginId: 'table',
key: 'panel-1',
headerActions: [action],
$data: new SceneQueryRunner({
datasource: { uid: 'my-uid' },
queries: [{ query: 'QueryA', refId: 'A' }],
}),
});
new DashboardScene({
uid: 'dash-1',
$variables: new SceneVariableSet({
variables: [], // No GroupByVariable
}),
body: DefaultGridLayoutManager.fromVizPanels([panel]),
});
const deactivate = action.activate();
expect(action.getGroupByVariable()).toBeUndefined();
deactivate();
});
});
describe('Panel parent requirement', () => {
it('should throw error if parent is not VizPanel', () => {
const action = new PanelGroupByAction();
expect(() => {
action.activate();
}).toThrow('PanelGroupByAction can be used only for VizPanel');
});
});
describe('getGroupByOptions', () => {
it('should return empty array when variable has no options', async () => {
const { action, variable } = buildTestScene();
jest.spyOn(variable, 'validateAndUpdate').mockReturnValue(
of({
origin: variable,
state: {
...variable.state,
options: [],
},
})
);
const deactivate = action.activate();
const options = await action.getGroupByOptions();
expect(options).toEqual([]);
deactivate();
});
it('should filter options based on applicability', async () => {
const { action, variable, panel } = buildTestScene();
variable.setState({
options: [
{ label: 'field1', value: 'field1' },
{ label: 'field2', value: 'field2' },
{ label: 'field3', value: 'field3' },
],
});
const queryRunner = panel.state.$data as SceneQueryRunner;
queryRunner.setState({
data: {
state: 'Done',
series: [],
timeRange: {} as any,
request: {
targets: [{ refId: 'A', datasource: { uid: 'test-uid', type: 'prometheus' } }],
},
},
});
jest.spyOn(variable, 'validateAndUpdate').mockReturnValue(
of({
origin: variable,
state: variable.state,
})
);
jest.spyOn(variable, 'getGroupByApplicabilityForQueries').mockResolvedValue([
{ key: 'field1', applicable: true },
{ key: 'field2', applicable: false },
{ key: 'field3', applicable: true },
]);
const deactivate = action.activate();
const options = await action.getGroupByOptions();
expect(options).toHaveLength(2);
expect(options).toEqual([
{ label: 'field1', value: 'field1' },
{ label: 'field3', value: 'field3' },
]);
deactivate();
});
});
});
function buildTestScene() {
const variable = new GroupByVariable({
name: 'A',
label: 'A',
description: 'A',
type: 'groupby',
value: 'Text',
});
const action = new PanelGroupByAction();
const panel = new VizPanel({
title: 'Panel A',
pluginId: 'table',
key: 'panel-12',
headerActions: [action],
$data: new SceneQueryRunner({
datasource: { uid: 'my-uid' },
queries: [{ query: 'QueryA', refId: 'A' }],
}),
});
const dashboard = new DashboardScene({
uid: 'A',
$variables: new SceneVariableSet({
variables: [variable],
}),
body: DefaultGridLayoutManager.fromVizPanels([panel]),
});
return { action, panel, dashboard, variable };
}
+11 -11
View File
@@ -3555,11 +3555,11 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/scenes-react@npm:^6.42.1":
version: 6.42.1
resolution: "@grafana/scenes-react@npm:6.42.1"
"@grafana/scenes-react@npm:6.43.0--canary.1278.19036459049.0":
version: 6.43.0--canary.1278.19036459049.0
resolution: "@grafana/scenes-react@npm:6.43.0--canary.1278.19036459049.0"
dependencies:
"@grafana/scenes": "npm:6.42.1"
"@grafana/scenes": "npm:6.43.0--canary.1278.19036459049.0"
lru-cache: "npm:^10.2.2"
react-use: "npm:^17.4.0"
peerDependencies:
@@ -3571,7 +3571,7 @@ __metadata:
react: ^18.0.0
react-dom: ^18.0.0
react-router-dom: ^6.28.0
checksum: 10/190289561d343a7a2d9d036f9b23abb063cee20f3a3e9e9275d8f11ef2e0df9fbd256e4687bf5a7b8f59aa79ae2bbc05bbb009876f582ebaeed97a0f52492907
checksum: 10/46d746bcbd83f903672d44f0dbc97fe2c400865c93847e6debeec554ba7c587995219a91868d24984dd35b2700590b6ff14dd523f28b2d69b64ce2e81feb4694
languageName: node
linkType: hard
@@ -3601,9 +3601,9 @@ __metadata:
languageName: node
linkType: hard
"@grafana/scenes@npm:6.42.1, @grafana/scenes@npm:^6.42.1":
version: 6.42.1
resolution: "@grafana/scenes@npm:6.42.1"
"@grafana/scenes@npm:6.43.0--canary.1278.19036459049.0":
version: 6.43.0--canary.1278.19036459049.0
resolution: "@grafana/scenes@npm:6.43.0--canary.1278.19036459049.0"
dependencies:
"@floating-ui/react": "npm:^0.26.16"
"@leeoniya/ufuzzy": "npm:^1.0.16"
@@ -3623,7 +3623,7 @@ __metadata:
react: ^18.0.0
react-dom: ^18.0.0
react-router-dom: ^6.28.0
checksum: 10/f1f455f823c01324942fef9feb256b04cb209d486a43e6dd683e984abb88f078d8e2a9d85e31619e37c9c3ee4356c97707d258a608f9b64ae8df097813bf178f
checksum: 10/6cb9d0bb259afd4ee4c15db5668e70300eb0ef68f90c1823a4106c487ba6836a94271af58c4604506cc142fe62b703f932815441aa313081f90589d34300ff66
languageName: node
linkType: hard
@@ -18865,8 +18865,8 @@ __metadata:
"@grafana/plugin-ui": "npm:^0.10.10"
"@grafana/prometheus": "workspace:*"
"@grafana/runtime": "workspace:*"
"@grafana/scenes": "npm:^6.42.1"
"@grafana/scenes-react": "npm:^6.42.1"
"@grafana/scenes": "npm:6.43.0--canary.1278.19036459049.0"
"@grafana/scenes-react": "npm:6.43.0--canary.1278.19036459049.0"
"@grafana/schema": "workspace:*"
"@grafana/sql": "workspace:*"
"@grafana/test-utils": "workspace:*"