mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Add a test
This commit is contained in:
parent
e093075c73
commit
bf1f8d8b3a
@ -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';
|
||||
|
@ -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,
|
||||
|
@ -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 [];
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
import { BASE_URL } from './query';
|
||||
import { getTestQueryList } from './testdata/testQueryList';
|
||||
|
||||
export const mockData = {
|
||||
all: {
|
||||
url: BASE_URL,
|
||||
response: getTestQueryList(),
|
||||
},
|
||||
};
|
@ -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 {
|
||||
|
122
packages/grafana-runtime/src/services/queryLibrary/api/testdata/testQueryList.ts
vendored
Normal file
122
packages/grafana-runtime/src/services/queryLibrary/api/testdata/testQueryList.ts
vendored
Normal 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"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -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,
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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' });
|
||||
|
@ -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 });
|
||||
}),
|
||||
|
83
public/app/features/explore/spec/queryLibrary.test.tsx
Normal file
83
public/app/features/explore/spec/queryLibrary.test.tsx
Normal 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');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user