grafana/public/app/plugins/datasource/cloudwatch/components/QueryEditor.test.tsx
Torkel Ödegaard debf04eb2c
Modals: Be more consistent with Modal cancel button styling (#68302)
* Modals: Be more consistent with Modal cancel button styling

* Update docs

* Fix tests

* fixing tests
2023-05-12 09:21:07 +02:00

325 lines
12 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { QueryEditorProps } from '@grafana/data';
import { config } from '@grafana/runtime';
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource';
import {
validLogsQuery,
validMetricQueryBuilderQuery,
validMetricQueryCodeQuery,
validMetricSearchBuilderQuery,
validMetricSearchCodeQuery,
} from '../__mocks__/queries';
import { CloudWatchDatasource } from '../datasource';
import { CloudWatchQuery, CloudWatchJsonData, MetricEditorMode, MetricQueryType } from '../types';
import { QueryEditor } from './QueryEditor';
// the following three fields are added to legacy queries in the dashboard migrator
const migratedFields = {
statistic: 'Average',
metricEditorMode: MetricEditorMode.Builder,
metricQueryType: MetricQueryType.Query,
};
const props: QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData> = {
datasource: setupMockedDataSource().datasource,
onRunQuery: jest.fn(),
onChange: jest.fn(),
query: {} as CloudWatchQuery,
};
const FAKE_EDITOR_LABEL = 'FakeEditor';
jest.mock('./SQLCodeEditor', () => ({
SQLCodeEditor: ({ sql, onChange }: { sql: string; onChange: (val: string) => void }) => {
return (
<>
<label htmlFor="cloudwatch-fake-editor">{FAKE_EDITOR_LABEL}</label>
<input id="cloudwatch-fake-editor" value={sql} onChange={(e) => onChange(e.currentTarget.value)}></input>
</>
);
},
}));
export { SQLCodeEditor } from './SQLCodeEditor';
describe('QueryEditor should render right editor', () => {
describe('when using grafana 6.3.0 metric query', () => {
it('should render the metrics query editor', async () => {
const query = {
...migratedFields,
dimensions: {
InstanceId: 'i-123',
},
expression: '',
highResolution: false,
id: '',
metricName: 'CPUUtilization',
namespace: 'AWS/EC2',
period: '',
refId: 'A',
region: 'default',
returnData: false,
};
render(<QueryEditor {...props} query={query} />);
expect(await screen.findByText('Metric name')).toBeInTheDocument();
});
});
describe('when using grafana 7.0.0 style metrics query', () => {
it('should render the metrics query editor', async () => {
const query = {
...migratedFields,
alias: '',
apiMode: 'Metrics',
dimensions: {
InstanceId: 'i-123',
},
expression: '',
id: '',
logGroupNames: [],
matchExact: true,
metricName: 'CPUUtilization',
namespace: 'AWS/EC2',
period: '',
queryMode: 'Metrics',
refId: 'A',
region: 'ap-northeast-2',
statistics: 'Average',
} as CloudWatchQuery;
render(<QueryEditor {...props} query={query} />);
expect(await screen.findByText('Metric name')).toBeInTheDocument();
});
});
describe('when using grafana 7.0.0 style logs query', () => {
it('should render the logs query editor', async () => {
const query = {
...migratedFields,
alias: '',
apiMode: 'Logs',
dimensions: {
InstanceId: 'i-123',
},
expression: '',
id: '',
logGroupNames: [],
matchExact: true,
metricName: 'CPUUtilization',
namespace: 'AWS/EC2',
period: '',
queryMode: 'Logs',
refId: 'A',
region: 'ap-northeast-2',
statistic: 'Average',
} as CloudWatchQuery;
render(<QueryEditor {...props} query={query} />);
expect(await screen.findByText('Select log groups')).toBeInTheDocument();
});
});
describe('when using grafana query from curated ec2 dashboard', () => {
it('should render the metrics query editor', async () => {
const query = {
...migratedFields,
alias: 'Inbound',
dimensions: {
InstanceId: '*',
},
expression:
"SUM(REMOVE_EMPTY(SEARCH('{AWS/EC2,InstanceId} MetricName=\"NetworkIn\"', 'Sum', $period)))/$period",
id: '',
matchExact: true,
metricName: 'NetworkOut',
namespace: 'AWS/EC2',
period: '$period',
refId: 'B',
region: '$region',
statistic: 'Average',
} as CloudWatchQuery;
render(<QueryEditor {...props} query={query} />);
expect(await screen.findByText('Metric name')).toBeInTheDocument();
});
});
interface MonitoringBadgeScenario {
name: string;
query: CloudWatchQuery;
toggle: boolean;
}
describe('monitoring badge', () => {
let originalValue: boolean | undefined;
let datasourceMock: ReturnType<typeof setupMockedDataSource>;
beforeEach(() => {
datasourceMock = setupMockedDataSource();
datasourceMock.datasource.resources.isMonitoringAccount = jest.fn().mockResolvedValue(true);
datasourceMock.datasource.resources.getMetrics = jest.fn().mockResolvedValue([]);
datasourceMock.datasource.resources.getDimensionKeys = jest.fn().mockResolvedValue([]);
originalValue = config.featureToggles.cloudWatchCrossAccountQuerying;
});
afterEach(() => {
config.featureToggles.cloudWatchCrossAccountQuerying = originalValue;
});
describe('should be displayed when a monitoring account is returned and', () => {
const cases: MonitoringBadgeScenario[] = [
{ name: 'it is logs query and feature is enabled', query: validLogsQuery, toggle: true },
{
name: 'it is metric search builder query and feature is enabled',
query: validMetricSearchBuilderQuery,
toggle: true,
},
{
name: 'it is metric search code query and feature is enabled',
query: validMetricSearchCodeQuery,
toggle: true,
},
];
test.each(cases)('$name', async ({ query, toggle }) => {
config.featureToggles.cloudWatchCrossAccountQuerying = toggle;
render(<QueryEditor {...props} datasource={datasourceMock.datasource} query={query} />);
expect(await screen.findByText('Monitoring account')).toBeInTheDocument();
});
});
describe('should not be displayed when a monitoring account is returned and', () => {
const cases: MonitoringBadgeScenario[] = [
{
name: 'it is metric query builder query and toggle is enabled',
query: validMetricQueryBuilderQuery,
toggle: true,
},
{
name: 'it is metric query code query and toggle is not enabled',
query: validMetricQueryCodeQuery,
toggle: false,
},
{ name: 'it is logs query and feature is not enabled', query: validLogsQuery, toggle: false },
{
name: 'it is metric search builder query and feature is not enabled',
query: validMetricSearchBuilderQuery,
toggle: false,
},
{
name: 'it is metric search code query and feature is not enabled',
query: validMetricSearchCodeQuery,
toggle: false,
},
];
test.each(cases)('$name', async ({ query, toggle }) => {
config.featureToggles.cloudWatchCrossAccountQuerying = toggle;
render(<QueryEditor {...props} datasource={datasourceMock.datasource} query={query} />);
expect(await screen.findByText('Run queries')).toBeInTheDocument();
expect(screen.queryByText('Monitoring account')).toBeNull();
});
});
});
describe('QueryHeader', () => {
it('should display metric actions in header when metric query is used', async () => {
render(<QueryEditor {...props} query={validMetricQueryCodeQuery} />);
expect(await screen.findByText('CloudWatch Metrics')).toBeInTheDocument();
expect(screen.getByLabelText(/Region.*/)).toBeInTheDocument();
expect(screen.getByLabelText('Builder')).toBeInTheDocument();
expect(screen.getByLabelText('Code')).toBeInTheDocument();
expect(screen.getByText('Metric Query')).toBeInTheDocument();
});
it('should display metric actions in header when metric query is used', async () => {
render(<QueryEditor {...props} query={validLogsQuery} />);
expect(await screen.findByText('CloudWatch Logs')).toBeInTheDocument();
expect(screen.getByLabelText(/Region.*/)).toBeInTheDocument();
expect(screen.queryByLabelText('Builder')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Code')).not.toBeInTheDocument();
expect(screen.queryByText('Metric Query')).not.toBeInTheDocument();
});
});
describe('metrics editor should handle editor modes correctly', () => {
it('when metric query type is metric search and editor mode is builder', async () => {
render(<QueryEditor {...props} query={validMetricSearchBuilderQuery} />);
expect(await screen.findByText('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 () => {
render(<QueryEditor {...props} query={validMetricSearchCodeQuery} />);
expect(await screen.findByText('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 () => {
render(<QueryEditor {...props} query={validMetricQueryBuilderQuery} />);
expect(await screen.findByText('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 () => {
render(<QueryEditor {...props} query={validMetricQueryCodeQuery} />);
expect(await screen.findByText('Metric Query')).toBeInTheDocument();
const radio = screen.getByLabelText('Code');
expect(radio instanceof HTMLInputElement && radio.checked).toBeTruthy();
});
});
describe('confirm modal', () => {
it('should be shown when moving from code editor to builder when in sql mode', async () => {
const sqlQuery = 'SELECT * FROM test';
render(
<QueryEditor
{...props}
query={{ ...validMetricQueryCodeQuery, sqlExpression: sqlQuery }}
onChange={jest.fn()}
onRunQuery={jest.fn()}
/>
);
// the modal should not be shown unless the code editor is "dirty", so need to trigger a change
const codeEditorElement = screen.getByLabelText(FAKE_EDITOR_LABEL);
await userEvent.clear(codeEditorElement);
await userEvent.type(codeEditorElement, 'select * from ');
const builderElement = screen.getByLabelText('Builder');
expect(builderElement).toBeInTheDocument();
await userEvent.click(builderElement);
const modalTitleElem = screen.getByText('Are you sure?');
expect(modalTitleElem).toBeInTheDocument();
});
it('should not be shown when moving from builder to code when in sql mode', async () => {
render(
<QueryEditor {...props} query={validMetricQueryBuilderQuery} onChange={jest.fn()} onRunQuery={jest.fn()} />
);
const builderElement = screen.getByLabelText('Builder');
expect(builderElement).toBeInTheDocument();
await userEvent.click(builderElement);
expect(screen.queryByText('Are you sure?')).toBeNull();
});
it('should not be shown when moving from code to builder when in search mode', async () => {
render(<QueryEditor {...props} query={validMetricSearchCodeQuery} onChange={jest.fn()} onRunQuery={jest.fn()} />);
const builderElement = screen.getByLabelText('Builder');
expect(builderElement).toBeInTheDocument();
await userEvent.click(builderElement);
expect(screen.queryByText('Are you sure?')).toBeNull();
});
});
});