The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/dashboard-scene/settings/variables/utils.test.ts

346 lines
11 KiB

import { DataSourceApi } from '@grafana/data';
import { config, setTemplateSrv, TemplateSrv } from '@grafana/runtime';
import {
CustomVariable,
ConstantVariable,
IntervalVariable,
QueryVariable,
DataSourceVariable,
AdHocFiltersVariable,
GroupByVariable,
TextBoxVariable,
SceneVariableSet,
} from '@grafana/scenes';
import { DataQuery, DataSourceJsonData, VariableType } from '@grafana/schema';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
import { DASHBOARD_DATASOURCE_PLUGIN_ID } from 'app/plugins/datasource/dashboard/types';
import { AdHocFiltersVariableEditor } from './editors/AdHocFiltersVariableEditor';
import { ConstantVariableEditor } from './editors/ConstantVariableEditor';
import { CustomVariableEditor } from './editors/CustomVariableEditor';
import { DataSourceVariableEditor } from './editors/DataSourceVariableEditor';
import { GroupByVariableEditor } from './editors/GroupByVariableEditor';
import { IntervalVariableEditor } from './editors/IntervalVariableEditor';
import { QueryVariableEditor } from './editors/QueryVariableEditor';
import { TextBoxVariableEditor } from './editors/TextBoxVariableEditor';
import {
isEditableVariableType,
EDITABLE_VARIABLES,
EDITABLE_VARIABLES_SELECT_ORDER,
getVariableTypeSelectOptions,
getVariableEditor,
getVariableScene,
hasVariableOptions,
EditableVariableType,
getDefinition,
getOptionDataSourceTypes,
getNextAvailableId,
getVariableDefault,
} from './utils';
const templateSrv = {
getAdhocFilters: jest.fn().mockReturnValue([{ key: 'origKey', operator: '=', value: '' }]),
} as unknown as TemplateSrv;
const dsMock: 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, {}>;
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => ({
get: async () => dsMock,
getList: () => {
return [
{
name: 'DataSourceInstance1',
uid: 'ds1',
meta: {
name: 'ds1',
id: 'dsTestDataSource',
},
},
];
},
}),
}));
describe('isEditableVariableType', () => {
it('should return true for editable variable types', () => {
const editableTypes: VariableType[] = [
'custom',
'query',
'constant',
'interval',
'datasource',
'adhoc',
'groupby',
'textbox',
];
editableTypes.forEach((type) => {
expect(isEditableVariableType(type)).toBe(true);
});
});
it('should return false for non-editable variable types', () => {
const nonEditableTypes: VariableType[] = ['system'];
nonEditableTypes.forEach((type) => {
expect(isEditableVariableType(type)).toBe(false);
});
});
});
describe('getVariableTypeSelectOptions', () => {
describe('when groupByVariable is enabled', () => {
beforeAll(() => {
config.featureToggles.groupByVariable = true;
});
afterAll(() => {
config.featureToggles.groupByVariable = false;
});
it('should contain all editable variable types', () => {
const options = getVariableTypeSelectOptions();
expect(options).toHaveLength(Object.keys(EDITABLE_VARIABLES).length);
EDITABLE_VARIABLES_SELECT_ORDER.forEach((type) => {
expect(EDITABLE_VARIABLES).toHaveProperty(type);
});
});
it('should return an array of selectable values for editable variable types', () => {
const options = getVariableTypeSelectOptions();
expect(options).toHaveLength(8);
options.forEach((option, index) => {
const editableType = EDITABLE_VARIABLES_SELECT_ORDER[index];
const variableTypeConfig = EDITABLE_VARIABLES[editableType];
expect(option.value).toBe(editableType);
expect(option.label).toBe(variableTypeConfig.name);
expect(option.description).toBe(variableTypeConfig.description);
});
});
});
describe('when groupByVariable is disabled', () => {
it('should contain all editable variable types except groupby', () => {
const options = getVariableTypeSelectOptions();
expect(options).toHaveLength(Object.keys(EDITABLE_VARIABLES).length - 1);
EDITABLE_VARIABLES_SELECT_ORDER.forEach((type) => {
expect(EDITABLE_VARIABLES).toHaveProperty(type);
});
});
it('should return an array of selectable values for editable variable types', () => {
const options = getVariableTypeSelectOptions();
expect(options).toHaveLength(7);
options.forEach((option, index) => {
const editableType = EDITABLE_VARIABLES_SELECT_ORDER[index];
const variableTypeConfig = EDITABLE_VARIABLES[editableType];
expect(option.value).toBe(editableType);
expect(option.label).toBe(variableTypeConfig.name);
expect(option.description).toBe(variableTypeConfig.description);
});
});
});
});
describe('getVariableEditor', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it.each(Object.keys(EDITABLE_VARIABLES) as EditableVariableType[])(
'should define an editor for variable type "%s"',
(type) => {
const editor = getVariableEditor(type);
expect(editor).toBeDefined();
}
);
it.each([
['custom', CustomVariableEditor],
['query', QueryVariableEditor],
['constant', ConstantVariableEditor],
['interval', IntervalVariableEditor],
['datasource', DataSourceVariableEditor],
['adhoc', AdHocFiltersVariableEditor],
['groupby', GroupByVariableEditor],
['textbox', TextBoxVariableEditor],
])('should return the correct editor for variable type "%s"', (type, ExpectedVariableEditor) => {
expect(getVariableEditor(type as EditableVariableType)).toBe(ExpectedVariableEditor);
});
});
describe('getVariableScene', () => {
beforeAll(() => {
setTemplateSrv(templateSrv);
});
it.each(Object.keys(EDITABLE_VARIABLES) as EditableVariableType[])(
'should define a scene object for every variable type',
(type) => {
const variable = getVariableScene(type, { name: 'foo' });
expect(variable).toBeDefined();
}
);
it.each([
['custom', CustomVariable],
['query', QueryVariable],
['constant', ConstantVariable],
['interval', IntervalVariable],
['datasource', DataSourceVariable],
['adhoc', AdHocFiltersVariable],
['groupby', GroupByVariable],
['textbox', TextBoxVariable],
])('should return the scene variable instance for the given editable variable type', (type, instanceType) => {
const initialState = { name: 'MyVariable' };
const sceneVariable = getVariableScene(type as EditableVariableType, initialState);
expect(sceneVariable).toBeInstanceOf(instanceType);
expect(sceneVariable.state.name).toBe(initialState.name);
});
});
describe('hasVariableOptions', () => {
it('should return true for scene variables with options property', () => {
const variableWithOptions = new CustomVariable({
name: 'MyVariable',
options: [{ value: 'option1', label: 'Option 1' }],
});
expect(hasVariableOptions(variableWithOptions)).toBe(true);
});
it('should return false for scene variables without options property', () => {
const variableWithoutOptions = new ConstantVariable({ name: 'MyVariable' });
expect(hasVariableOptions(variableWithoutOptions)).toBe(false);
});
});
describe('getDefinition', () => {
it('returns the correct definition for QueryVariable when definition is defined', () => {
const model = new QueryVariable({
name: 'custom0',
query: '',
definition: 'legacy ABC query definition',
});
expect(getDefinition(model)).toBe('legacy ABC query definition');
});
it('returns the correct definition for QueryVariable when definition is not defined', () => {
const model = new QueryVariable({
name: 'custom0',
query: 'ABC query',
definition: '',
});
expect(getDefinition(model)).toBe('ABC query');
});
it('returns the correct definition for DataSourceVariable', () => {
const model = new DataSourceVariable({
name: 'ds0',
pluginId: 'datasource-plugin',
value: 'datasource-value',
});
expect(getDefinition(model)).toBe('datasource-plugin');
});
it('returns the correct definition for CustomVariable', () => {
const model = new CustomVariable({
name: 'custom0',
query: 'Custom, A, B, C',
});
expect(getDefinition(model)).toBe('Custom, A, B, C');
});
it('returns the correct definition for IntervalVariable', () => {
const model = new IntervalVariable({
name: 'interval0',
intervals: ['1m', '5m', '15m', '30m', '1h', '6h', '12h', '1d'],
});
expect(getDefinition(model)).toBe('1m,5m,15m,30m,1h,6h,12h,1d');
});
it('returns the correct definition for TextBoxVariable', () => {
const model = new TextBoxVariable({
name: 'textbox0',
value: 'TextBox Value',
});
expect(getDefinition(model)).toBe('TextBox Value');
});
it('returns the correct definition for ConstantVariable', () => {
const model = new ConstantVariable({
name: 'constant0',
value: 'Constant Value',
});
expect(getDefinition(model)).toBe('Constant Value');
});
});
describe('getOptionDataSourceTypes', () => {
it('should return all data source types when no data source types are specified', () => {
const optionTypes = getOptionDataSourceTypes();
expect(optionTypes).toHaveLength(2);
// in the old code we always had an empty option
expect(optionTypes[0].value).toBe('');
expect(optionTypes[1].label).toBe('ds1');
});
});
describe('getNextAvailableId', () => {
it('should return the initial ID for an empty array', () => {
const sceneVariables = new SceneVariableSet({
variables: [],
});
expect(getNextAvailableId('query', sceneVariables.state.variables)).toBe('query0');
});
it('should return a non-conflicting ID for a non-empty array', () => {
const variable = new QueryVariable({
name: 'query0',
label: 'test-label',
description: 'test-desc',
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-std', type: 'fake-std' },
query: 'query',
includeAll: true,
allValue: 'test-all',
isMulti: true,
});
const sceneVariables = new SceneVariableSet({
variables: [variable],
});
expect(getNextAvailableId('query', sceneVariables.state.variables)).toBe('query1');
});
});
describe('getVariableDefault', () => {
it('should return a QueryVariable instance with the correct name', () => {
const sceneVariables = new SceneVariableSet({
variables: [],
});
const defaultVariable = getVariableDefault(sceneVariables.state.variables);
expect(defaultVariable).toBeInstanceOf(QueryVariable);
expect(defaultVariable.state.name).toBe('query0');
});
});