mirror of
https://github.com/grafana/grafana.git
synced 2025-09-16 09:12:45 +08:00

* Repeating: Refactoring and unification * Update * update * fix e2e * fixes * Update * adjust e2e test --------- Co-authored-by: Sergej-Vlasov <sergej.s.vlasov@gmail.com>
220 lines
6.7 KiB
TypeScript
220 lines
6.7 KiB
TypeScript
import { VariableRefresh } from '@grafana/data';
|
|
import { FetchError } from '@grafana/runtime';
|
|
import {
|
|
DeepPartial,
|
|
EmbeddedScene,
|
|
SceneDeactivationHandler,
|
|
SceneGridLayout,
|
|
SceneGridRow,
|
|
SceneObject,
|
|
SceneTimeRange,
|
|
SceneVariableSet,
|
|
TestVariable,
|
|
VizPanel,
|
|
} from '@grafana/scenes';
|
|
import { DashboardLoaderSrv, setDashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
|
|
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants';
|
|
import { DashboardDTO } from 'app/types/dashboard';
|
|
|
|
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
|
import { DashboardGridItem, RepeatDirection } from '../scene/layout-default/DashboardGridItem';
|
|
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
|
|
import { RowRepeaterBehavior } from '../scene/layout-default/RowRepeaterBehavior';
|
|
|
|
export function setupLoadDashboardMock(rsp: DeepPartial<DashboardDTO>, spy?: jest.Mock) {
|
|
const loadDashboardMock = (spy || jest.fn()).mockResolvedValue(rsp);
|
|
const loadSnapshotMock = (spy || jest.fn()).mockResolvedValue(rsp);
|
|
// disabling type checks since this is a test util
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
setDashboardLoaderSrv({
|
|
loadDashboard: loadDashboardMock,
|
|
loadSnapshot: loadSnapshotMock,
|
|
} as unknown as DashboardLoaderSrv);
|
|
return loadDashboardMock;
|
|
}
|
|
export function setupLoadDashboardMockReject(rsp: DeepPartial<FetchError>, spy?: jest.Mock) {
|
|
const loadDashboardMock = (spy || jest.fn()).mockRejectedValue(rsp);
|
|
// disabling type checks since this is a test util
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
setDashboardLoaderSrv({
|
|
loadDashboard: loadDashboardMock,
|
|
} as unknown as DashboardLoaderSrv);
|
|
return loadDashboardMock;
|
|
}
|
|
|
|
export function setupLoadDashboardRuntimeErrorMock() {
|
|
// disabling type checks since this is a test util
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
setDashboardLoaderSrv({
|
|
loadDashboard: () => {
|
|
throw new Error('Runtime error');
|
|
},
|
|
} as unknown as DashboardLoaderSrv);
|
|
}
|
|
|
|
export function mockResizeObserver() {
|
|
window.ResizeObserver = class ResizeObserver {
|
|
constructor(callback: ResizeObserverCallback) {
|
|
setTimeout(() => {
|
|
callback(
|
|
[
|
|
// disabling type checks since this is a test util
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
{
|
|
contentRect: {
|
|
x: 1,
|
|
y: 2,
|
|
width: 500,
|
|
height: 500,
|
|
top: 100,
|
|
bottom: 0,
|
|
left: 100,
|
|
right: 0,
|
|
},
|
|
} as ResizeObserverEntry,
|
|
],
|
|
this
|
|
);
|
|
});
|
|
}
|
|
observe() {}
|
|
disconnect() {}
|
|
unobserve() {}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Useful from tests to simulate mounting a full scene. Children are activated before parents to simulate the real order
|
|
* of React mount order and useEffect ordering.
|
|
*
|
|
*/
|
|
export function activateFullSceneTree(scene: SceneObject): SceneDeactivationHandler {
|
|
const deactivationHandlers: SceneDeactivationHandler[] = [];
|
|
|
|
// Important that variables are activated before other children
|
|
if (scene.state.$variables) {
|
|
deactivationHandlers.push(activateFullSceneTree(scene.state.$variables));
|
|
}
|
|
|
|
scene.forEachChild((child) => {
|
|
// For query runners which by default use the container width for maxDataPoints calculation we are setting a width.
|
|
// In real life this is done by the React component when VizPanel is rendered.
|
|
if ('setContainerWidth' in child) {
|
|
// @ts-expect-error
|
|
child.setContainerWidth(500);
|
|
}
|
|
deactivationHandlers.push(activateFullSceneTree(child));
|
|
});
|
|
|
|
deactivationHandlers.push(scene.activate());
|
|
|
|
return () => {
|
|
for (const handler of deactivationHandlers) {
|
|
handler();
|
|
}
|
|
};
|
|
}
|
|
|
|
interface SceneOptions {
|
|
variableQueryTime: number;
|
|
maxPerRow?: number;
|
|
itemHeight?: number;
|
|
repeatDirection?: RepeatDirection;
|
|
x?: number;
|
|
y?: number;
|
|
numberOfOptions?: number;
|
|
usePanelRepeater?: boolean;
|
|
useRowRepeater?: boolean;
|
|
throwError?: string;
|
|
variableRefresh?: VariableRefresh;
|
|
}
|
|
|
|
export function buildPanelRepeaterScene(options: SceneOptions, source?: VizPanel) {
|
|
const defaults = { usePanelRepeater: true, ...options };
|
|
|
|
const withRepeat = new DashboardGridItem({
|
|
variableName: 'server',
|
|
repeatedPanels: [],
|
|
repeatDirection: options.repeatDirection,
|
|
maxPerRow: options.maxPerRow,
|
|
itemHeight: options.itemHeight,
|
|
body:
|
|
source ??
|
|
new VizPanel({
|
|
title: 'Panel $server',
|
|
pluginId: 'timeseries',
|
|
key: 'panel-1',
|
|
}),
|
|
x: options.x || 0,
|
|
y: options.y || 0,
|
|
});
|
|
|
|
const withoutRepeat = new DashboardGridItem({
|
|
x: 0,
|
|
y: 0,
|
|
width: 10,
|
|
height: 10,
|
|
body: new VizPanel({
|
|
title: 'Panel $server',
|
|
pluginId: 'timeseries',
|
|
titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })],
|
|
}),
|
|
});
|
|
|
|
const row = new SceneGridRow({
|
|
$behaviors: defaults.useRowRepeater ? [new RowRepeaterBehavior({ variableName: 'handler' })] : [],
|
|
children: [defaults.usePanelRepeater ? withRepeat : withoutRepeat],
|
|
});
|
|
|
|
const panelRepeatVariable = new TestVariable({
|
|
name: 'server',
|
|
query: 'A.*',
|
|
value: ALL_VARIABLE_VALUE,
|
|
text: ALL_VARIABLE_TEXT,
|
|
isMulti: true,
|
|
includeAll: true,
|
|
delayMs: options.variableQueryTime,
|
|
optionsToReturn: [
|
|
{ label: 'A', value: '1' },
|
|
{ label: 'B', value: '2' },
|
|
{ label: 'C', value: '3' },
|
|
{ label: 'D', value: '4' },
|
|
{ label: 'E', value: '5' },
|
|
].slice(0, options.numberOfOptions),
|
|
throwError: defaults.throwError,
|
|
refresh: options.variableRefresh,
|
|
});
|
|
|
|
const rowRepeatVariable = new TestVariable({
|
|
name: 'handler',
|
|
query: 'A.*',
|
|
value: ALL_VARIABLE_VALUE,
|
|
text: ALL_VARIABLE_TEXT,
|
|
isMulti: true,
|
|
includeAll: true,
|
|
delayMs: options.variableQueryTime,
|
|
optionsToReturn: [
|
|
{ label: 'AA', value: '11' },
|
|
{ label: 'BB', value: '22' },
|
|
{ label: 'CC', value: '33' },
|
|
{ label: 'DD', value: '44' },
|
|
{ label: 'EE', value: '55' },
|
|
].slice(0, options.numberOfOptions),
|
|
throwError: defaults.throwError,
|
|
});
|
|
|
|
const scene = new EmbeddedScene({
|
|
$timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }),
|
|
$variables: new SceneVariableSet({
|
|
variables: [panelRepeatVariable, rowRepeatVariable],
|
|
}),
|
|
body: new DefaultGridLayoutManager({
|
|
grid: new SceneGridLayout({
|
|
children: [row],
|
|
}),
|
|
}),
|
|
});
|
|
|
|
return { scene, repeater: withRepeat, row, variable: panelRepeatVariable };
|
|
}
|