Files
grafana/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryLinkExtension.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

132 lines
3.7 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { PluginType, rangeUtil, PluginExtensionLink, PluginExtensionTypes } from '@grafana/data';
import { usePluginLinkExtensions } from '@grafana/runtime';
import { PyroscopeDataSource } from '../datasource';
import { mockFetchPyroscopeDatasourceSettings } from '../datasource.test';
import { Props, PyroscopeQueryLinkExtensions, resetPyroscopeQueryLinkExtensionsFetches } from './QueryLinkExtension';
// Constants copied from `QueryLinkExtension.tsx`
const EXTENSION_POINT_ID = 'plugins/grafana-pyroscope-datasource/query-links';
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
usePluginLinkExtensions: jest.fn(),
getTemplateSrv: () => {
return {
replace: (query: string): string => {
return query.replace(/\$var/g, 'interpolated');
},
};
},
}));
const usePluginLinkExtensionsMock = jest.mocked(usePluginLinkExtensions);
const defaultPyroscopeDataSourceSettings = {
uid: 'default-pyroscope',
url: 'http://pyroscope',
basicAuthUser: 'pyroscope_user',
};
describe('PyroscopeQueryLinkExtensions', () => {
const EXPECTED_BUTTON_LABEL = 'Profiles App';
const DEFAULT_EXTENSION_PATH = 'a/mock-path-app/fake-path';
function createExtension(overrides?: Partial<PluginExtensionLink>) {
return {
...{
description: 'unremarkable-description',
extensionPointId: EXTENSION_POINT_ID,
title: EXPECTED_BUTTON_LABEL,
path: DEFAULT_EXTENSION_PATH,
type: PluginExtensionTypes.link,
category: 'unremarkable-category',
icon: 'heart',
onClick() {},
pluginId: 'mock-path-app',
id: `${Date.now()}}`,
},
...overrides,
} as PluginExtensionLink;
}
beforeEach(() => {
resetPyroscopeQueryLinkExtensionsFetches();
mockFetchPyroscopeDatasourceSettings(defaultPyroscopeDataSourceSettings);
usePluginLinkExtensionsMock.mockRestore();
usePluginLinkExtensionsMock.mockReturnValue({ extensions: [], isLoading: false }); // Unless stated otherwise, no extensions
});
it('should render if extension present', async () => {
usePluginLinkExtensionsMock.mockReturnValue({ extensions: [createExtension()], isLoading: false }); // Default extension
await act(setup);
expect(await screen.findAllByText(EXPECTED_BUTTON_LABEL)).toBeDefined();
});
it('Should not render if no extension present', async () => {
await act(setup);
expect(screen.queryByText(EXPECTED_BUTTON_LABEL)).toBeNull();
});
});
function setupDs() {
const ds = new PyroscopeDataSource({
...defaultPyroscopeDataSourceSettings,
name: '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,
});
return ds;
}
async function setup(options: { props: Partial<Props> } = { props: {} }) {
const utils = render(
<PyroscopeQueryLinkExtensions
query={{
queryType: 'both',
labelSelector: '',
profileTypeId: 'process_cpu:cpu',
refId: 'A',
maxNodes: 1000,
groupBy: [],
}}
datasource={setupDs()}
range={rangeUtil.convertRawToRange({ from: 'now-1h', to: 'now' })}
{...options.props}
/>
);
return { ...utils };
}