mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
* Modals: Be more consistent with Modal cancel button styling * Update docs * Fix tests * fixing tests
325 lines
12 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|