379 lines
12 KiB
TypeScript
379 lines
12 KiB
TypeScript
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, VariableHide, VariableType } from '@grafana/schema';
|
|
import { SHARED_DASHBOARD_QUERY, DASHBOARD_DATASOURCE_PLUGIN_ID } from 'app/plugins/datasource/dashboard/constants';
|
|
|
|
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,
|
|
isSceneVariableInstance,
|
|
} 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('isSceneVariableInstance', () => {
|
|
it.each([
|
|
CustomVariable,
|
|
QueryVariable,
|
|
ConstantVariable,
|
|
IntervalVariable,
|
|
DataSourceVariable,
|
|
AdHocFiltersVariable,
|
|
GroupByVariable,
|
|
TextBoxVariable,
|
|
])('should return true for scene variable instances %s', (instanceType) => {
|
|
const variable = new instanceType({ name: 'MyVariable' });
|
|
expect(isSceneVariableInstance(variable)).toBe(true);
|
|
});
|
|
|
|
it('should return false for non-scene variable instances', () => {
|
|
const variable = {
|
|
name: 'MyVariable',
|
|
type: 'query',
|
|
};
|
|
expect(variable).not.toBeInstanceOf(QueryVariable);
|
|
});
|
|
});
|
|
|
|
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],
|
|
['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);
|
|
expect(sceneVariable.state.hide).toBe(undefined);
|
|
});
|
|
|
|
it('should return the scene variable instance for the constant editable variable type', () => {
|
|
const initialState = { name: 'MyVariable' };
|
|
const sceneVariable = getVariableScene('constant' as EditableVariableType, initialState);
|
|
expect(sceneVariable).toBeInstanceOf(ConstantVariable);
|
|
expect(sceneVariable.state.name).toBe(initialState.name);
|
|
expect(sceneVariable.state.hide).toBe(VariableHide.hideVariable);
|
|
});
|
|
});
|
|
|
|
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');
|
|
});
|
|
});
|