mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 17:52:24 +08:00

* GrafanaUI: add option to close DeleteButton on confirm click * add datasource readOnly info to frontend settings * move isTruthy utility type guard * add generic non-visualization table component * Add correlations settings page * add missing readOnly in mock * Fix typo * avoid reloading correlations after add/remove * use DeepPartial from rhf * validate source data source * fix validation logic * fix navmodel test * add missing readonly property * remove unused styles * handle multiple clicks on elements * better UX for loading states * fix remove handler * add glue icon
217 lines
6.5 KiB
TypeScript
217 lines
6.5 KiB
TypeScript
import { render, screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
|
|
import React from 'react';
|
|
import { of } from 'rxjs';
|
|
import { createFetchResponse } from 'test/helpers/createFetchResponse';
|
|
|
|
import { DataQueryRequest, DataSourceInstanceSettings, dateTime, PluginType } from '@grafana/data';
|
|
import { backendSrv } from 'app/core/services/backend_srv';
|
|
|
|
import { JaegerDatasource, JaegerJsonData } from '../datasource';
|
|
import { testResponse } from '../testResponse';
|
|
import { JaegerQuery } from '../types';
|
|
|
|
import SearchForm from './SearchForm';
|
|
|
|
jest.mock('@grafana/runtime', () => ({
|
|
...jest.requireActual('@grafana/runtime'),
|
|
getTemplateSrv: () => ({
|
|
replace: jest.fn(),
|
|
containsTemplate: (val: string): boolean => {
|
|
return val.includes('$');
|
|
},
|
|
}),
|
|
}));
|
|
|
|
describe('SearchForm', () => {
|
|
it('should call the `onChange` function on click of the Input', async () => {
|
|
const promise = Promise.resolve();
|
|
const handleOnChange = jest.fn(() => promise);
|
|
const query = {
|
|
...defaultQuery,
|
|
refId: '121314',
|
|
};
|
|
const ds = {
|
|
async metadataRequest(url: string, params?: Record<string, any>): Promise<any> {
|
|
if (url === '/api/services') {
|
|
return Promise.resolve(['jaeger-query', 'service2', 'service3']);
|
|
}
|
|
},
|
|
} as JaegerDatasource;
|
|
setupFetchMock({ data: [testResponse] });
|
|
|
|
render(<SearchForm datasource={ds} query={query} onChange={handleOnChange} />);
|
|
|
|
const asyncServiceSelect = await waitFor(() => screen.getByRole('combobox', { name: 'select-service-name' }));
|
|
expect(asyncServiceSelect).toBeInTheDocument();
|
|
|
|
await userEvent.click(asyncServiceSelect);
|
|
|
|
const jaegerService = await screen.findByText('jaeger-query');
|
|
expect(jaegerService).toBeInTheDocument();
|
|
});
|
|
|
|
it('should be able to select operation name if query.service exists', async () => {
|
|
const promise = Promise.resolve();
|
|
const handleOnChange = jest.fn(() => promise);
|
|
const query2 = {
|
|
...defaultQuery,
|
|
refId: '121314',
|
|
service: 'jaeger-query',
|
|
};
|
|
setupFetchMock({ data: [testResponse] });
|
|
|
|
render(<SearchForm datasource={{} as JaegerDatasource} query={query2} onChange={handleOnChange} />);
|
|
|
|
const asyncOperationSelect2 = await waitFor(() => screen.getByRole('combobox', { name: 'select-operation-name' }));
|
|
expect(asyncOperationSelect2).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('SearchForm', () => {
|
|
let user: UserEvent;
|
|
let query: JaegerQuery;
|
|
let ds: JaegerDatasource;
|
|
|
|
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 });
|
|
|
|
query = {
|
|
...defaultQuery,
|
|
refId: '121314',
|
|
service: 'jaeger-query',
|
|
};
|
|
|
|
ds = new JaegerDatasource(defaultSettings);
|
|
setupFetchMock({ data: [testResponse] });
|
|
|
|
jest.spyOn(ds, 'metadataRequest').mockImplementation(() => {
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve(['jaeger-query']);
|
|
}, 3000);
|
|
});
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it('should show loader if there is a delay fetching options', async () => {
|
|
const handleOnChange = jest.fn();
|
|
render(<SearchForm datasource={ds} query={query} onChange={handleOnChange} />);
|
|
|
|
const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-service-name' });
|
|
await user.click(asyncServiceSelect);
|
|
expect(screen.getByText('Loading options...')).toBeInTheDocument();
|
|
|
|
jest.advanceTimersByTime(3000);
|
|
await waitFor(() => expect(screen.queryByText('Loading options...')).not.toBeInTheDocument());
|
|
});
|
|
|
|
it('should filter the span dropdown when user types a search value', async () => {
|
|
render(<SearchForm datasource={ds} query={query} onChange={() => {}} />);
|
|
|
|
const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-service-name' });
|
|
expect(asyncServiceSelect).toBeInTheDocument();
|
|
await user.click(asyncServiceSelect);
|
|
jest.advanceTimersByTime(3000);
|
|
|
|
await user.type(asyncServiceSelect, 'j');
|
|
let option = await screen.findByText('jaeger-query');
|
|
expect(option).toBeDefined();
|
|
|
|
await user.type(asyncServiceSelect, 'c');
|
|
option = await screen.findByText('Hit enter to add');
|
|
expect(option).toBeDefined();
|
|
});
|
|
|
|
it('should add variable to select menu options', async () => {
|
|
query = {
|
|
...defaultQuery,
|
|
refId: '121314',
|
|
service: '$service',
|
|
operation: '$operation',
|
|
};
|
|
|
|
render(<SearchForm datasource={ds} query={query} onChange={() => {}} />);
|
|
|
|
const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-service-name' });
|
|
expect(asyncServiceSelect).toBeInTheDocument();
|
|
await user.click(asyncServiceSelect);
|
|
jest.advanceTimersByTime(3000);
|
|
|
|
await user.type(asyncServiceSelect, '$');
|
|
const serviceOption = await screen.findByText('$service');
|
|
expect(serviceOption).toBeDefined();
|
|
|
|
const asyncOperationSelect = screen.getByRole('combobox', { name: 'select-operation-name' });
|
|
expect(asyncOperationSelect).toBeInTheDocument();
|
|
await user.click(asyncOperationSelect);
|
|
jest.advanceTimersByTime(3000);
|
|
|
|
await user.type(asyncOperationSelect, '$');
|
|
const operationOption = await screen.findByText('$operation');
|
|
expect(operationOption).toBeDefined();
|
|
});
|
|
});
|
|
|
|
function setupFetchMock(response: any, mock?: any) {
|
|
const defaultMock = () => mock ?? of(createFetchResponse(response));
|
|
|
|
const fetchMock = jest.spyOn(backendSrv, 'fetch');
|
|
fetchMock.mockImplementation(defaultMock);
|
|
return fetchMock;
|
|
}
|
|
|
|
const defaultSettings: DataSourceInstanceSettings<JaegerJsonData> = {
|
|
id: 0,
|
|
uid: '0',
|
|
type: 'tracing',
|
|
name: 'jaeger',
|
|
url: 'http://grafana.com',
|
|
access: 'proxy',
|
|
meta: {
|
|
id: 'jaeger',
|
|
name: 'jaeger',
|
|
type: PluginType.datasource,
|
|
info: {} as any,
|
|
module: '',
|
|
baseUrl: '',
|
|
},
|
|
jsonData: {
|
|
nodeGraph: {
|
|
enabled: true,
|
|
},
|
|
},
|
|
readOnly: false,
|
|
};
|
|
|
|
const defaultQuery: DataQueryRequest<JaegerQuery> = {
|
|
requestId: '1',
|
|
dashboardId: 0,
|
|
interval: '0',
|
|
intervalMs: 10,
|
|
panelId: 0,
|
|
scopedVars: {},
|
|
range: {
|
|
from: dateTime().subtract(1, 'h'),
|
|
to: dateTime(),
|
|
raw: { from: '1h', to: 'now' },
|
|
},
|
|
timezone: 'browser',
|
|
app: 'explore',
|
|
startTime: 0,
|
|
targets: [
|
|
{
|
|
query: '12345',
|
|
refId: '1',
|
|
},
|
|
],
|
|
};
|