grafana/public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor.test.tsx
Erik Sundell bab78a9e64
CloudWatch: Add support for AWS Metric Insights (#42487)
* add support for code editor and builder

* refactor cloudwatch migration

* Add tooltip to editor field (#56)

* add tooltip

* add old tooltips

* Bug bash feedback fixes (#58)

* make ASC the default option

* update sql preview whenever sql changes

* don't allow queries without aggregation

* set default value for aggregation

* use new input field

* cleanup

* pr feedback

* prevent unnecessary rerenders

* use frame error instead of main error

* remove not used snapshot

* Use dimension filter in schema picker  (#63)

* use dimension key filter in group by and schema labels

* add dimension filter also to code editor

* add tests

* fix build error

* fix strict error

* remove debug code

* fix annotation editor (#64)

* fix annotation editor

* fix broken test

* revert annotation backend change

* PR feedback (#67)

* pr feedback

* removed dimension filter from group by

* add spacing between common fields and rest

* do not generate deep link for metric queries (#70)

* update docs (#69)

Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>

* fix lint problem caused by merge conflict

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
2021-11-30 10:53:31 +01:00

216 lines
7.6 KiB
TypeScript

import React from 'react';
import renderer from 'react-test-renderer';
import { render, screen, waitFor } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { DataSourceInstanceSettings } from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { MetricsQueryEditor, normalizeQuery, Props } from './MetricsQueryEditor';
import { CloudWatchDatasource } from '../datasource';
import { CustomVariableModel, initialVariableModelState } from '../../../../features/variables/types';
import { CloudWatchJsonData, CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from '../types';
const setup = () => {
const instanceSettings = {
jsonData: { defaultRegion: 'us-east-1' },
} as DataSourceInstanceSettings<CloudWatchJsonData>;
const templateSrv = new TemplateSrv();
const variable: CustomVariableModel = {
...initialVariableModelState,
id: 'var3',
index: 0,
name: 'var3',
options: [
{ selected: true, value: 'var3-foo', text: 'var3-foo' },
{ selected: false, value: 'var3-bar', text: 'var3-bar' },
{ selected: true, value: 'var3-baz', text: 'var3-baz' },
],
current: { selected: true, value: ['var3-foo', 'var3-baz'], text: 'var3-foo + var3-baz' },
multi: true,
includeAll: false,
query: '',
type: 'custom',
};
templateSrv.init([variable]);
const datasource = new CloudWatchDatasource(instanceSettings, templateSrv as any, {} as any);
datasource.metricFindQuery = async () => [{ value: 'test', label: 'test', text: 'test' }];
datasource.getNamespaces = jest.fn().mockResolvedValue([]);
datasource.getMetrics = jest.fn().mockResolvedValue([]);
datasource.getRegions = jest.fn().mockResolvedValue([]);
datasource.getDimensionKeys = jest.fn().mockResolvedValue([]);
const props: Props = {
query: {
queryMode: 'Metrics',
refId: '',
id: '',
region: 'us-east-1',
namespace: 'ec2',
metricName: 'CPUUtilization',
dimensions: { somekey: 'somevalue' },
statistic: '',
period: '',
expression: '',
alias: '',
matchExact: true,
metricQueryType: MetricQueryType.Search,
metricEditorMode: MetricEditorMode.Builder,
},
datasource,
history: [],
onChange: jest.fn(),
onRunQuery: jest.fn(),
};
return props;
};
describe('QueryEditor', () => {
it('should render component', async () => {
const { act } = renderer;
await act(async () => {
const props = setup();
const tree = renderer.create(<MetricsQueryEditor {...props} />).toJSON();
expect(tree).toMatchSnapshot();
});
});
it('normalizes query on mount', async () => {
const { act } = renderer;
const props = setup();
// This does not actually even conform to the prop type but this happens on initialisation somehow
props.query = {
queryMode: 'Metrics',
apiMode: 'Metrics',
refId: '',
expression: '',
matchExact: true,
metricQueryType: MetricQueryType.Search,
metricEditorMode: MetricEditorMode.Builder,
} as any;
await act(async () => {
renderer.create(<MetricsQueryEditor {...props} />);
});
expect((props.onChange as jest.Mock).mock.calls[0][0]).toEqual({
namespace: '',
metricName: '',
expression: '',
sqlExpression: '',
dimensions: {},
region: 'default',
id: '',
alias: '',
statistic: 'Average',
period: '',
queryMode: 'Metrics',
apiMode: 'Metrics',
refId: '',
matchExact: true,
metricQueryType: MetricQueryType.Search,
metricEditorMode: MetricEditorMode.Builder,
});
});
describe('should use correct default values', () => {
it('should normalize query with default values', () => {
expect(normalizeQuery({ refId: '42' } as any)).toEqual({
namespace: '',
metricName: '',
expression: '',
sqlExpression: '',
dimensions: {},
region: 'default',
id: '',
alias: '',
statistic: 'Average',
matchExact: true,
period: '',
queryMode: 'Metrics',
refId: '42',
metricQueryType: MetricQueryType.Search,
metricEditorMode: MetricEditorMode.Builder,
});
});
});
describe('should handle editor modes correctly', () => {
it('when metric query type is metric search and editor mode is builder', async () => {
await act(async () => {
const props = setup();
render(<MetricsQueryEditor {...props} />);
expect(screen.getByText('Metric Search')).toBeInTheDocument();
const radio = screen.getByLabelText('Builder');
expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
});
});
it('when metric query type is metric search and editor mode is raw', async () => {
await act(async () => {
const props = setup();
(props.query as CloudWatchMetricsQuery).metricEditorMode = MetricEditorMode.Code;
render(<MetricsQueryEditor {...props} />);
expect(screen.getByText('Metric Search')).toBeInTheDocument();
const radio = screen.getByLabelText('Code');
expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
});
});
it('when metric query type is metric query and editor mode is builder', async () => {
await act(async () => {
const props = setup();
(props.query as CloudWatchMetricsQuery).metricQueryType = MetricQueryType.Query;
(props.query as CloudWatchMetricsQuery).metricEditorMode = MetricEditorMode.Builder;
render(<MetricsQueryEditor {...props} />);
expect(screen.getByText('Metric Query')).toBeInTheDocument();
const radio = screen.getByLabelText('Builder');
expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
});
});
it('when metric query type is metric query and editor mode is raw', async () => {
await act(async () => {
const props = setup();
(props.query as CloudWatchMetricsQuery).metricQueryType = MetricQueryType.Query;
(props.query as CloudWatchMetricsQuery).metricEditorMode = MetricEditorMode.Code;
render(<MetricsQueryEditor {...props} />);
expect(screen.getByText('Metric Query')).toBeInTheDocument();
const radio = screen.getByLabelText('Code');
expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
});
});
});
describe('should handle expression options correctly', () => {
it('should display match exact switch', () => {
const props = setup();
render(<MetricsQueryEditor {...props} />);
expect(screen.getByText('Match exact')).toBeInTheDocument();
});
it('shoud display wildcard option in dimension value dropdown', async () => {
const props = setup();
props.datasource.getDimensionValues = jest.fn().mockResolvedValue([[{ label: 'dimVal1', value: 'dimVal1' }]]);
(props.query as CloudWatchMetricsQuery).metricQueryType = MetricQueryType.Search;
(props.query as CloudWatchMetricsQuery).metricEditorMode = MetricEditorMode.Builder;
(props.query as CloudWatchMetricsQuery).dimensions = { instanceId: 'instance-123' };
render(<MetricsQueryEditor {...props} />);
expect(screen.getByText('Match exact')).toBeInTheDocument();
const valueElement = screen.getByText('instance-123');
expect(valueElement).toBeInTheDocument();
expect(screen.queryByText('*')).toBeNull();
act(async () => {
await valueElement.click();
await waitFor(() => {
expect(screen.getByText('*')).toBeInTheDocument();
});
});
});
});
});