The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.test.tsx

191 lines
6.3 KiB

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { DataSourceInstanceSettings, MetricFindValue } from '@grafana/data/src';
import { PrometheusDatasource } from '../../datasource';
import { PromOptions } from '../../types';
import {
formatPrometheusLabelFilters,
formatPrometheusLabelFiltersToString,
MetricSelect,
Props,
} from './MetricSelect';
const instanceSettings = {
url: 'proxied',
id: 1,
user: 'test',
password: 'mupp',
jsonData: { httpMethod: 'GET' },
} as unknown as DataSourceInstanceSettings<PromOptions>;
const dataSourceMock = new PrometheusDatasource(instanceSettings);
const mockValues = [{ label: 'random_metric' }, { label: 'unique_metric' }, { label: 'more_unique_metric' }];
// Mock metricFindQuery which will call backend API
//@ts-ignore
dataSourceMock.metricFindQuery = jest.fn((query: string) => {
// Use the label values regex to get the values inside the label_values function call
const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
const queryValueArray = query.match(labelValuesRegex) as RegExpMatchArray;
const queryValueRaw = queryValueArray[1] as string;
// Remove the wrapping regex
const queryValue = queryValueRaw.substring(queryValueRaw.indexOf('".*') + 3, queryValueRaw.indexOf('.*"'));
// Run the regex that we'd pass into prometheus API against the strings in the test
return Promise.resolve(
mockValues
.filter((value) => value.label.match(queryValue))
.map((result) => {
return {
text: result.label,
};
}) as MetricFindValue[]
);
});
const props: Props = {
labelsFilters: [],
datasource: dataSourceMock,
query: {
metric: '',
labels: [],
operations: [],
},
onChange: jest.fn(),
onGetMetrics: jest.fn().mockResolvedValue(mockValues),
metricLookupDisabled: false,
};
describe('MetricSelect', () => {
it('shows all metric options', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
await waitFor(() => expect(screen.getByText('random_metric')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('unique_metric')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('more_unique_metric')).toBeInTheDocument());
await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(3));
});
it('truncates list of metrics to 1000', async () => {
const manyMockValues = [...Array(1001).keys()].map((idx: number) => {
return { label: 'random_metric' + idx };
});
props.onGetMetrics = jest.fn().mockResolvedValue(manyMockValues);
render(<MetricSelect {...props} />);
await openMetricSelect();
await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(1000));
});
it('shows option to set custom value when typing', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'custom value');
await waitFor(() => expect(screen.getByText('custom value')).toBeInTheDocument());
});
it('shows searched options when typing', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'unique');
await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(3));
});
it('searches on split words', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'more unique');
await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(2));
});
it('searches on multiple split words', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'more unique metric');
await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(2));
});
it('highlights matching string', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'more');
await waitFor(() => expect(document.querySelectorAll('mark')).toHaveLength(1));
});
it('highlights multiple matching strings in 1 input row', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'more metric');
await waitFor(() => expect(document.querySelectorAll('mark')).toHaveLength(2));
});
it('highlights multiple matching strings in multiple input rows', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'unique metric');
await waitFor(() => expect(document.querySelectorAll('mark')).toHaveLength(4));
});
it('does not highlight matching string in create option', async () => {
render(<MetricSelect {...props} />);
await openMetricSelect();
const input = screen.getByRole('combobox');
await userEvent.type(input, 'new');
await waitFor(() => expect(document.querySelector('mark')).not.toBeInTheDocument());
});
it('label filters properly join', () => {
const query = formatPrometheusLabelFilters([
{
value: 'value',
label: 'label',
op: '=',
},
{
value: 'value2',
label: 'label2',
op: '=',
},
]);
query.forEach((label) => {
expect(label.includes(',', 0));
});
});
it('label filter creation', () => {
const labels = [
{
value: 'value',
label: 'label',
op: '=',
},
{
value: 'value2',
label: 'label2',
op: '=',
},
];
const queryString = formatPrometheusLabelFiltersToString('query', labels);
queryString.split(',').forEach((queryChunk) => {
expect(queryChunk.length).toBeGreaterThan(1); // must be longer then ','
});
});
});
async function openMetricSelect() {
const select = screen.getByText('Select metric').parentElement!;
await userEvent.click(select);
}