Files
grafana/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryEditor.test.tsx
Marcus Andersson 804c726413 PluginExtensions: Make the extensions registry reactive (#83085)
* feat: add a reactive extension registry

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: add hooks to work with the reactive registry

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: start using the reactive registry

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "command palette" extension point to use the hook

* feat: update the "alerting" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "explore" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "datasources config" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "panel menu" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "pyroscope datasource" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "user profile page" extension point to use the hooks

* chore: update betterer

* fix: update the hooks to not re-render unnecessarily

* chore: remove the old `createPluginExtensionRegistry` impementation

* chore: add "TODO" for `PanelMenuBehaviour` extension point

* feat: update the return value of the hooks to contain a `{ isLoading }` param

* tests: add more tests for the usePluginExtensions() hook

* fix: exclude the cloud-home-app from being non-awaited

* refactor: use uuidv4() for random ID generation (for the registry object)

* fix: linting issue

* feat: use the hooks for the new alerting extension point

* feat: use `useMemo()` for `AlertInstanceAction` extension point context

---------

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
2024-04-24 09:33:16 +02:00

140 lines
3.4 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { CoreApp, PluginType } from '@grafana/data';
import { setPluginExtensionsHook } from '@grafana/runtime';
import { PyroscopeDataSource } from '../datasource';
import { mockFetchPyroscopeDatasourceSettings } from '../datasource.test';
import { ProfileTypeMessage } from '../types';
import { Props, QueryEditor } from './QueryEditor';
describe('QueryEditor', () => {
beforeEach(() => {
setPluginExtensionsHook(() => ({ extensions: [], isLoading: false })); // No extensions
mockFetchPyroscopeDatasourceSettings();
});
it('should render without error', async () => {
setup();
expect(await screen.findByDisplayValue('process_cpu-cpu')).toBeDefined();
});
it('should render without error if empty profileTypes', async () => {
const ds = setupDs();
ds.getProfileTypes = jest.fn().mockResolvedValue([]);
setup({
props: {
datasource: ds,
query: {
queryType: 'both',
labelSelector: '',
profileTypeId: '',
refId: 'A',
maxNodes: 1000,
groupBy: [],
},
},
});
expect(await screen.findByPlaceholderText('No profile types found')).toBeDefined();
});
it('should render options', async () => {
setup();
await openOptions();
expect(screen.getByText(/Metric/)).toBeDefined();
expect(screen.getByText(/Profile/)).toBeDefined();
expect(screen.getByText(/Both/)).toBeDefined();
expect(screen.getByText(/Group by/)).toBeDefined();
});
it('should render correct options outside of explore', async () => {
setup({ props: { app: CoreApp.Dashboard } });
await openOptions();
expect(screen.getByText(/Metric/)).toBeDefined();
expect(screen.getByText(/Profile/)).toBeDefined();
expect(screen.queryAllByText(/Both/).length).toBe(0);
});
});
async function openOptions() {
const options = screen.getByText(/Options/);
expect(options).toBeDefined();
await userEvent.click(options);
}
function setupDs() {
const ds = new PyroscopeDataSource({
name: 'test',
uid: 'test',
type: PluginType.datasource,
access: 'proxy',
id: 1,
jsonData: {},
meta: {
name: '',
id: '',
type: PluginType.datasource,
baseUrl: '',
info: {
author: {
name: '',
},
description: '',
links: [],
logos: {
large: '',
small: '',
},
screenshots: [],
updated: '',
version: '',
},
module: '',
},
readOnly: false,
});
ds.getProfileTypes = jest.fn().mockResolvedValue([
{
label: 'process_cpu - cpu',
id: 'process_cpu:cpu',
},
{
label: 'memory',
id: 'memory:memory',
},
] as ProfileTypeMessage[]);
ds.getLabelNames = jest.fn().mockResolvedValue(['label_one']);
return ds;
}
function setup(options: { props: Partial<Props> } = { props: {} }) {
const onChange = jest.fn();
const utils = render(
<QueryEditor
query={{
queryType: 'both',
labelSelector: '',
profileTypeId: 'process_cpu:cpu',
refId: 'A',
maxNodes: 1000,
groupBy: [],
}}
datasource={setupDs()}
onChange={onChange}
onRunQuery={() => {}}
app={CoreApp.Explore}
{...options.props}
/>
);
return { ...utils, onChange };
}