Add a test

This commit is contained in:
Piotr Jamroz 2024-04-25 22:45:17 +02:00
parent e093075c73
commit bf1f8d8b3a
15 changed files with 253 additions and 12 deletions

View File

@ -26,6 +26,7 @@ export {
type DataResponse,
type TestingStatus,
} from './utils/queryResponse';
export * from './services/queryLibrary';
export { PanelRenderer, type PanelRendererProps } from './components/PanelRenderer';
export { PanelDataErrorView, type PanelDataErrorViewProps } from './components/PanelDataErrorView';
export { toDataQueryError } from './utils/toDataQueryError';

View File

@ -2,7 +2,7 @@ import { createApi } from '@reduxjs/toolkit/query/react';
import { QueryTemplate } from '@grafana/data';
import { fromApiResponse } from './mappers';
import { convertDataQueryResponseToQueryTemplates } from './mappers';
import { baseQuery } from './query';
export const createQueryLibraryApi = () => {
@ -11,7 +11,7 @@ export const createQueryLibraryApi = () => {
endpoints: (builder) => ({
allQueryTemplates: builder.query<QueryTemplate[], void>({
query: () => undefined,
transformResponse: fromApiResponse,
transformResponse: convertDataQueryResponseToQueryTemplates,
}),
}),
reducerPath: undefined,

View File

@ -2,7 +2,7 @@ import { QueryTemplate } from '@grafana/data';
import { DataQuerySpecResponse, DataQueryTarget } from './types';
export const fromApiResponse = (result: DataQuerySpecResponse): QueryTemplate[] => {
export const convertDataQueryResponseToQueryTemplates = (result: DataQuerySpecResponse): QueryTemplate[] => {
if (!result.items) {
return [];
}

View File

@ -0,0 +1,9 @@
import { BASE_URL } from './query';
import { getTestQueryList } from './testdata/testQueryList';
export const mockData = {
all: {
url: BASE_URL,
response: getTestQueryList(),
},
};

View File

@ -5,7 +5,7 @@ import { getBackendSrv, isFetchError } from '../../backendSrv';
import { DataQuerySpecResponse } from './types';
const BASE_URL = '/apis/peakq.grafana.app/v0alpha1/namespaces/default/querytemplates/';
export const BASE_URL = '/apis/peakq.grafana.app/v0alpha1/namespaces/default/querytemplates/';
export const baseQuery: BaseQueryFn<void, DataQuerySpecResponse, Error> = async () => {
try {

View File

@ -0,0 +1,122 @@
export const getTestQueryList = () => ({
kind: 'QueryTemplateList',
apiVersion: 'peakq.grafana.app/v0alpha1',
metadata: {
resourceVersion: '1783293408052252672',
remainingItemCount: 0,
},
items: [
{
kind: 'QueryTemplate',
apiVersion: 'peakq.grafana.app/v0alpha1',
metadata: {
name: 'AElastic2nkf9',
generateName: 'AElastic',
namespace: 'default',
uid: '65327fce-c545-489d-ada5-16f909453d12',
resourceVersion: '1783293341664808960',
creationTimestamp: '2024-04-25T20:32:58Z',
},
spec: {
title: 'Elastic Query Template',
targets: [
{
variables: {},
properties: {
refId: 'A',
datasource: {
type: 'elasticsearch',
uid: 'elastic-uid',
},
alias: '',
metrics: [
{
id: '1',
type: 'count',
},
],
bucketAggs: [
{
field: '@timestamp',
id: '2',
settings: {
interval: 'auto',
},
type: 'date_histogram',
},
],
timeField: '@timestamp',
query: 'test:test ',
},
},
],
},
},
{
kind: 'QueryTemplate',
apiVersion: 'peakq.grafana.app/v0alpha1',
metadata: {
name: 'ALoki296tj',
generateName: 'ALoki',
namespace: 'default',
uid: '3e71de65-efa7-40e3-8f23-124212cca455',
resourceVersion: '1783214217151647744',
creationTimestamp: '2024-04-25T11:05:55Z',
},
spec: {
title: 'Loki Query Template',
vars: [
{
key: '__value',
defaultValues: [''],
valueListDefinition: {
customValues: '',
},
},
],
targets: [
{
variables: {
__value: [
{
path: '$.datasource.jsonData.derivedFields.0.url',
position: {
start: 0,
end: 14,
},
format: 'raw',
},
{
path: '$.datasource.jsonData.derivedFields.1.url',
position: {
start: 0,
end: 14,
},
format: 'raw',
},
{
path: '$.datasource.jsonData.derivedFields.2.url',
position: {
start: 0,
end: 14,
},
format: 'raw',
},
],
},
properties: {
refId: 'A',
datasource: {
type: 'loki',
uid: 'loki-uid',
},
queryType: 'range',
editorMode: 'code',
expr: '{test="test"}',
},
},
],
},
},
],
});

View File

@ -2,6 +2,7 @@ import { ApiProvider } from '@reduxjs/toolkit/dist/query/react';
import React, { PropsWithChildren } from 'react';
import { createQueryLibraryApi } from './api/factory';
import { mockData } from './api/mocks';
const api = createQueryLibraryApi();
@ -10,3 +11,7 @@ export const { useAllQueryTemplatesQuery } = api;
export const QueryLibraryApiProvider = ({ children }: PropsWithChildren) => {
return <ApiProvider api={api}>{children}</ApiProvider>;
};
export const QueryLibraryMocks = {
data: mockData,
};

View File

@ -40,6 +40,7 @@ export function QueriesDrawerDropdown({ variant }: Props) {
icon="book"
variant={drawerOpened ? 'active' : 'canvas'}
onClick={() => setDrawerOpened(!drawerOpened)}
aria-label={selectedTab}
>
{variant === 'full' ? selectedTab : undefined}
</ToolbarButton>

View File

@ -1,6 +1,6 @@
import React from 'react';
import { QueryLibraryApiProvider } from '@grafana/runtime/src/services/queryLibrary';
import { QueryLibraryApiProvider } from '@grafana/runtime';
import { QueryTemplatesList } from './QueryTemplatesList';

View File

@ -1,7 +1,7 @@
import React from 'react';
import { QueryTemplate } from '@grafana/data';
import { useAllQueryTemplatesQuery } from '@grafana/runtime/src/services/queryLibrary';
import { useAllQueryTemplatesQuery } from '@grafana/runtime';
import { EmptyState, Spinner } from '@grafana/ui';
import { getDatasourceSrv } from '../../plugins/datasource_srv';
@ -24,7 +24,7 @@ export function QueryTemplatesList() {
return <Spinner />;
}
if (!data) {
if (!data || data.length === 0) {
return (
<EmptyState message={`Query Library`} variant="not-found">
<p>

View File

@ -21,19 +21,21 @@ export function QueryDescriptionCell(props: CellProps<QueryTemplateRow>) {
return <div>No queries</div>;
}
const query = props.row.original.query;
const description = props.row.original.description;
const dsName = datasourceApi?.name || '';
return (
<>
<div aria-label={`Query template for ${dsName}: ${description}`}>
<p className={styles.header}>
<img
className={styles.logo}
src={datasourceApi?.meta.info.logos.small || 'public/img/icn-datasource.svg'}
alt={datasourceApi?.meta.info.description}
/>
{datasourceApi?.name}
{dsName}
</p>
<p className={cx(styles.mainText, styles.singleLine)}>{datasourceApi?.getQueryDisplayText?.(query)}</p>
<p className={cx(styles.otherText, styles.singleLine)}>{props.row.original.description}</p>
</>
<p className={cx(styles.otherText, styles.singleLine)}>{description}</p>
</div>
);
}

View File

@ -22,6 +22,15 @@ export const assertQueryHistory = async (expectedQueryTexts: string[]) => {
});
};
export const assertQueryLibraryTemplateExists = async (datasource: string, description: string) => {
const selector = withinQueryHistory();
const cell = selector.getByRole('cell', {
name: new RegExp(`query template for ${datasource.toLowerCase()}: ${description.toLowerCase()}`, 'i'),
});
expect(cell).toBeInTheDocument();
};
export const assertQueryHistoryIsEmpty = async () => {
const selector = withinQueryHistory();
const queryTexts = selector.queryAllByLabelText('Query text');

View File

@ -32,6 +32,12 @@ export const openQueryHistory = async () => {
expect(await screen.findByPlaceholderText('Search queries')).toBeInTheDocument();
};
export const openQueryLibrary = async () => {
const explore = withinExplore('left');
const button = explore.getByRole('button', { name: 'Query library' });
await userEvent.click(button);
};
export const closeQueryHistory = async () => {
const selector = withinQueryHistory();
const closeButton = selector.getByRole('button', { name: 'Close query history' });

View File

@ -28,6 +28,7 @@ import {
getDataSourceSrv,
getEchoSrv,
setLocationService,
QueryLibraryMocks,
} from '@grafana/runtime';
import { DataSourceRef } from '@grafana/schema';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
@ -70,12 +71,14 @@ export function setupExplore(options?: SetupOptions): {
datasourceRequest: jest.fn().mockRejectedValue(undefined),
delete: jest.fn().mockRejectedValue(undefined),
fetch: jest.fn().mockImplementation((req) => {
const data: Record<string, object | number> = {};
let data: Record<string, string | object | number> = {};
if (req.url.startsWith('/api/datasources/correlations') && req.method === 'GET') {
data.correlations = [];
data.totalCount = 0;
} else if (req.url.startsWith('/api/query-history') && req.method === 'GET') {
data.result = options?.queryHistory || {};
} else if (req.url.startsWith(QueryLibraryMocks.data.all.url)) {
data = QueryLibraryMocks.data.all.response;
}
return of({ data });
}),

View File

@ -0,0 +1,83 @@
import React from 'react';
import { Props } from 'react-virtualized-auto-sizer';
import { EventBusSrv } from '@grafana/data';
import { config } from '@grafana/runtime';
import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
import { assertQueryLibraryTemplateExists } from './helper/assert';
import { openQueryLibrary } from './helper/interactions';
import { setupExplore, waitForExplore } from './helper/setup';
const reportInteractionMock = jest.fn();
const testEventBus = new EventBusSrv();
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
reportInteraction: (...args: object[]) => {
reportInteractionMock(...args);
},
getAppEvents: () => testEventBus,
}));
jest.mock('app/core/core', () => ({
contextSrv: {
hasPermission: () => true,
isSignedIn: true,
getValidIntervals: (defaultIntervals: string[]) => defaultIntervals,
},
}));
jest.mock('app/core/services/PreferencesService', () => ({
PreferencesService: function () {
return {
patch: jest.fn(),
load: jest.fn().mockResolvedValue({
queryHistory: {
homeTab: 'query',
},
}),
};
},
}));
jest.mock('../hooks/useExplorePageTitle', () => ({
useExplorePageTitle: jest.fn(),
}));
jest.mock('react-virtualized-auto-sizer', () => {
return {
__esModule: true,
default(props: Props) {
return <div>{props.children({ height: 1, scaledHeight: 1, scaledWidth: 1000, width: 1000 })}</div>;
},
};
});
// const server = setupServer(...testing.serverHandlers);
describe('QueryLibrary', () => {
silenceConsoleOutput();
beforeAll(() => {
config.featureToggles.queryLibrary = true;
// server.listen();
});
afterEach(() => {
// server.resetHandlers();
});
afterAll(() => {
// server.close();
});
it('Load query templates', async () => {
setupExplore();
await waitForExplore();
await openQueryLibrary();
await assertQueryLibraryTemplateExists('loki', 'Loki Query Template');
await assertQueryLibraryTemplateExists('elastic', 'Elastic Query Template');
});
});