grafana/public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.test.tsx
Andre Pereira 541a03f33b
Tempo: TraceQL Configurable static fields (#65284)
* TraceQL - configurable static fields for new UI

* TraceQL - filter out static fields from Tags section. Added tooltip to static fields

* Add more units to duration validation. Improve duration field tooltip with accepted units

* Better control of delete button on SearchField

* Move new config behind feature toggle

* Special title for intrinsic "name"

* Fix tests

* Move static fields not in the datasource to the Tags section

* Start using the useAsync hook in the Tempo TraceQL configuration page to retrieve the tags and datasource

* Fix tests

* Fix test. Use useAsync to retrieve options in SearchField

* Remove ability to set a default value in filter configuration.
Removed type from filter, dynamic filters are now any filters not present in the datasource config

* Updated the static filters tooltip

* Replace useState + useEffect with useMemo for scopedTag
2023-03-31 10:35:37 +01:00

189 lines
5.9 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { FetchError } from '@grafana/runtime';
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource';
import SearchField from './SearchField';
const getOptionsV2 = jest.fn().mockImplementation(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{
value: 'customer',
label: 'customer',
type: 'string',
},
{
value: 'driver',
label: 'driver',
type: 'string',
},
]);
}, 1000);
});
});
jest.mock('../language_provider', () => {
return jest.fn().mockImplementation(() => {
return { getOptionsV2 };
});
});
describe('SearchField', () => {
let user: ReturnType<typeof userEvent.setup>;
beforeEach(() => {
jest.useFakeTimers();
// Need to use delay: null here to work with fakeTimers
// see https://github.com/testing-library/user-event/issues/833
user = userEvent.setup({ delay: null });
});
afterEach(() => {
jest.useRealTimers();
});
it('should not render tag if hideTag is true', () => {
const updateFilter = jest.fn((val) => {
return val;
});
const filter: TraceqlFilter = { id: 'test1', valueType: 'string', tag: 'test-tag' };
const { container } = renderSearchField(updateFilter, filter, [], true);
expect(container.querySelector(`input[aria-label="select test1 tag"]`)).not.toBeInTheDocument();
expect(container.querySelector(`input[aria-label="select test1 operator"]`)).toBeInTheDocument();
expect(container.querySelector(`input[aria-label="select test1 value"]`)).toBeInTheDocument();
});
it('should update operator when new value is selected in operator input', async () => {
const updateFilter = jest.fn((val) => {
return val;
});
const filter: TraceqlFilter = { id: 'test1', operator: '=', valueType: 'string', tag: 'test-tag' };
const { container } = renderSearchField(updateFilter, filter);
const select = await container.querySelector(`input[aria-label="select test1 operator"]`);
expect(select).not.toBeNull();
expect(select).toBeInTheDocument();
if (select) {
await user.click(select);
jest.advanceTimersByTime(1000);
const largerThanOp = await screen.findByText('!=');
await user.click(largerThanOp);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, operator: '!=' });
}
});
it('should update value when new value is selected in value input', async () => {
const updateFilter = jest.fn((val) => {
return val;
});
const filter: TraceqlFilter = {
id: 'test1',
value: 'old',
valueType: 'string',
tag: 'test-tag',
};
const { container } = renderSearchField(updateFilter, filter);
const select = await container.querySelector(`input[aria-label="select test1 value"]`);
expect(select).not.toBeNull();
expect(select).toBeInTheDocument();
if (select) {
// Add first value
await user.click(select);
jest.advanceTimersByTime(1000);
const driverVal = await screen.findByText('driver');
await user.click(driverVal);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, value: ['driver'] });
// Add a second value
await user.click(select);
jest.advanceTimersByTime(1000);
const customerVal = await screen.findByText('customer');
await user.click(customerVal);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, value: ['driver', 'customer'] });
// Remove the first value
const firstValRemove = await screen.findByLabelText('Remove driver');
await user.click(firstValRemove);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, value: ['customer'] });
}
});
it('should update tag when new value is selected in tag input', async () => {
const updateFilter = jest.fn((val) => {
return val;
});
const filter: TraceqlFilter = {
id: 'test1',
valueType: 'string',
};
const { container } = renderSearchField(updateFilter, filter, ['tag1', 'tag22', 'tag33']);
const select = await container.querySelector(`input[aria-label="select test1 tag"]`);
expect(select).not.toBeNull();
expect(select).toBeInTheDocument();
if (select) {
// Select tag22 as the tag
await user.click(select);
jest.advanceTimersByTime(1000);
const tag22 = await screen.findByText('tag22');
await user.click(tag22);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, tag: 'tag22' });
// Select tag1 as the tag
await user.click(select);
jest.advanceTimersByTime(1000);
const tag1 = await screen.findByText('tag1');
await user.click(tag1);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, tag: 'tag1' });
// Remove the tag
const tagRemove = await screen.findByLabelText('select-clear-value');
await user.click(tagRemove);
expect(updateFilter).toHaveBeenCalledWith({ ...filter, value: undefined });
}
});
});
const renderSearchField = (
updateFilter: (f: TraceqlFilter) => void,
filter: TraceqlFilter,
tags?: string[],
hideTag?: boolean
) => {
const datasource: TempoDatasource = {
search: {
filters: [
{
id: 'service-name',
tag: 'service.name',
operator: '=',
scope: TraceqlSearchScope.Resource,
},
{ id: 'span-name', type: 'static', tag: 'name', operator: '=', scope: TraceqlSearchScope.Span },
],
},
} as TempoDatasource;
return render(
<SearchField
datasource={datasource}
updateFilter={updateFilter}
filter={filter}
setError={function (error: FetchError): void {
throw error;
}}
tags={tags || []}
hideTag={hideTag}
/>
);
};