test(CustomVariableEditor): Add unit tests

This commit is contained in:
grafakus
2025-11-25 18:21:29 +01:00
parent e0c28cfa4c
commit 009716a408
@@ -5,117 +5,219 @@ import { CustomVariable } from '@grafana/scenes';
import { CustomVariableEditor } from './CustomVariableEditor';
function setup(options: Partial<ConstructorParameters<typeof CustomVariable>[0]> = {}) {
return {
variable: new CustomVariable({
name: 'customVar',
...options,
}),
onRunQuery: jest.fn(),
};
}
function renderEditor(ui: React.ReactNode) {
const renderResult = render(ui);
const elements = {
formatButton: (label: string) => renderResult.queryByLabelText(label) as HTMLElement,
queryInput: () =>
renderResult.queryByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput
) as HTMLTextAreaElement,
multiValueCheckbox: () =>
renderResult.queryByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
) as HTMLInputElement,
allowCustomValueCheckbox: () =>
renderResult.queryByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsAllowCustomValueSwitch
) as HTMLInputElement,
includeAllCheckbox: () =>
renderResult.queryByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
) as HTMLInputElement,
customAllValueInput: () =>
renderResult.queryByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
) as HTMLInputElement,
};
return {
...renderResult,
elements,
actions: {
updateValuesInput(newQuery: string) {
fireEvent.change(elements.queryInput(), { target: { value: newQuery } });
fireEvent.blur(elements.queryInput());
},
changeValuesFormat(newFormat: 'csv' | 'json') {
const targetLabel = newFormat === 'json' ? 'Object values in a JSON array' : 'Values separated by comma';
fireEvent.click(elements.formatButton(targetLabel));
},
},
};
}
describe('CustomVariableEditor', () => {
it('should render the CustomVariableForm with correct initial values', () => {
const variable = new CustomVariable({
name: 'customVar',
query: 'test, test2',
value: 'test',
isMulti: true,
includeAll: true,
allValue: 'test',
describe('CSV values format', () => {
it('should render CustomVariableForm with the correct initial values', () => {
const { variable, onRunQuery } = setup({
query: 'test, test2',
value: 'test',
isMulti: true,
includeAll: true,
allowCustomValue: true,
allValue: 'all',
});
const { elements } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
expect(elements.queryInput().value).toBe('test, test2');
expect(elements.multiValueCheckbox().checked).toBe(true);
expect(elements.allowCustomValueCheckbox().checked).toBe(true);
expect(elements.includeAllCheckbox().checked).toBe(true);
expect(elements.customAllValueInput().value).toBe('all');
});
const onRunQuery = jest.fn();
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
it('should update the variable state when some input values change ("Multi-value", "Allow custom values" & "Include All option")', () => {
const { variable, onRunQuery } = setup({
query: 'test, test2',
value: 'test',
isMulti: false,
allowCustomValue: false,
includeAll: false,
});
const queryInput = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput
) as HTMLInputElement;
const allValueInput = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
) as HTMLInputElement;
const multiCheckbox = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
) as HTMLInputElement;
const includeAllCheckbox = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
) as HTMLInputElement;
const { elements } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
expect(queryInput.value).toBe('test, test2');
expect(allValueInput.value).toBe('test');
expect(multiCheckbox.checked).toBe(true);
expect(includeAllCheckbox.checked).toBe(true);
expect(elements.multiValueCheckbox().checked).toBe(false);
expect(elements.allowCustomValueCheckbox().checked).toBe(false);
expect(elements.includeAllCheckbox().checked).toBe(false);
// include-all-custom input appears after include-all checkbox is checked only
expect(elements.customAllValueInput()).not.toBeInTheDocument();
fireEvent.click(elements.multiValueCheckbox());
fireEvent.click(elements.allowCustomValueCheckbox());
fireEvent.click(elements.includeAllCheckbox());
expect(variable.state.isMulti).toBe(true);
expect(variable.state.allowCustomValue).toBe(true);
expect(variable.state.includeAll).toBe(true);
expect(elements.customAllValueInput()).toBeInTheDocument();
});
describe('when the values textarea loses focus after its value has changed', () => {
it('should update the query in the variable state and call the onRunQuery callback', async () => {
const { variable, onRunQuery } = setup({ query: 'test, test2', value: 'test' });
const { actions } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
actions.updateValuesInput('test3, test4');
expect(variable.state.query).toBe('test3, test4');
expect(onRunQuery).toHaveBeenCalled();
});
});
describe('when the "Custom all value" input loses focus after its value has changed', () => {
it('should update the variable state', () => {
const { variable, onRunQuery } = setup({
query: 'test, test2',
value: 'test',
isMulti: true,
includeAll: true,
});
const { elements } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
fireEvent.change(elements.customAllValueInput(), { target: { value: 'new custom all' } });
fireEvent.blur(elements.customAllValueInput());
expect(variable.state.allValue).toBe('new custom all');
});
});
});
it('should update the variable state when input values change', () => {
const variable = new CustomVariable({
name: 'customVar',
query: 'test, test2',
value: 'test',
describe('JSON values format', () => {
const initialQuery = `[
{"value":1,"text":"Development","aws":"dev","azure":"development"},
{"value":2,"text":"Production","aws":"prod","azure":"production"}
]`;
it('should render CustomVariableForm with the correct initial values', () => {
const { variable, onRunQuery } = setup({
valuesFormat: 'json',
query: initialQuery,
isMulti: true,
includeAll: true,
});
const { elements } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
expect(elements.queryInput().value).toBe(initialQuery);
expect(elements.multiValueCheckbox().checked).toBe(true);
expect(elements.allowCustomValueCheckbox()).not.toBeInTheDocument();
expect(elements.includeAllCheckbox().checked).toBe(true);
expect(elements.customAllValueInput()).not.toBeInTheDocument();
});
const onRunQuery = jest.fn();
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
describe('when the values textarea loses focus after its value has changed', () => {
describe('if the value is valid JSON', () => {
it('should update the query in the variable state and call the onRunQuery callback', async () => {
const { variable, onRunQuery } = setup({ valuesFormat: 'json', query: initialQuery });
const multiCheckbox = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch
);
const includeAllCheckbox = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch
);
const { actions } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
const allowCustomValueCheckbox = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsAllowCustomValueSwitch
);
actions.updateValuesInput('[]');
// It include-all-custom input appears after include-all checkbox is checked only
expect(() =>
getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput)
).toThrow('Unable to find an element');
expect(variable.state.query).toBe('[]');
expect(onRunQuery).toHaveBeenCalled();
});
});
fireEvent.click(allowCustomValueCheckbox);
describe('if the value is NOT valid JSON', () => {
it('should display a validation error message and neither update the query in the variable state nor call the onRunQuery callback', async () => {
const { variable, onRunQuery } = setup({ valuesFormat: 'json', query: initialQuery });
fireEvent.click(multiCheckbox);
const { actions, getByRole } = renderEditor(
<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />
);
fireEvent.click(includeAllCheckbox);
const allValueInput = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
);
actions.updateValuesInput('[x]');
expect(variable.state.isMulti).toBe(true);
expect(variable.state.includeAll).toBe(true);
expect(variable.state.allowCustomValue).toBe(false);
expect(allValueInput).toBeInTheDocument();
expect(getByRole('alert')).toHaveTextContent(`Unexpected token 'x', "[x]" is not valid JSON`);
expect(variable.state.query).toBe(initialQuery);
expect(onRunQuery).not.toHaveBeenCalled();
});
});
});
});
it('should call update query and re-run query when input loses focus', async () => {
const variable = new CustomVariable({
name: 'customVar',
query: 'test, test2',
value: 'test',
describe('when switching values format', () => {
it('should switch the visibility of the proper form inputs ("Allow custom values" and "Custom all value")', () => {
const { variable, onRunQuery } = setup({
valuesFormat: 'csv',
query: '',
isMulti: true,
includeAll: true,
allowCustomValue: true,
allValue: '',
});
const { elements, actions } = renderEditor(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
expect(elements.allowCustomValueCheckbox()).toBeInTheDocument();
expect(elements.customAllValueInput()).toBeInTheDocument();
actions.changeValuesFormat('json');
expect(elements.allowCustomValueCheckbox()).not.toBeInTheDocument();
expect(elements.customAllValueInput()).not.toBeInTheDocument();
actions.changeValuesFormat('csv');
expect(elements.allowCustomValueCheckbox()).toBeInTheDocument();
expect(elements.customAllValueInput()).toBeInTheDocument();
});
const onRunQuery = jest.fn();
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
const queryInput = getByTestId(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput);
fireEvent.change(queryInput, { target: { value: 'test3, test4' } });
fireEvent.blur(queryInput);
expect(onRunQuery).toHaveBeenCalled();
expect(variable.state.query).toBe('test3, test4');
});
it('should update the variable state when all-custom-value input loses focus', () => {
const variable = new CustomVariable({
name: 'customVar',
query: 'test, test2',
value: 'test',
isMulti: true,
includeAll: true,
});
const onRunQuery = jest.fn();
const { getByTestId } = render(<CustomVariableEditor variable={variable} onRunQuery={onRunQuery} />);
const allValueInput = getByTestId(
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
) as HTMLInputElement;
fireEvent.change(allValueInput, { target: { value: 'new custom all' } });
fireEvent.blur(allValueInput);
expect(variable.state.allValue).toBe('new custom all');
});
});