123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- import { getDefaultNormalizer, render, RenderResult, SelectorMatcherOptions, waitFor } from '@testing-library/react';
- import userEvent from '@testing-library/user-event';
- import React from 'react';
- import { Provider } from 'react-redux';
- import { MemoryRouter } from 'react-router-dom';
- import { PluginErrorCode, PluginSignatureStatus, PluginType, dateTimeFormatTimeAgo } from '@grafana/data';
- import { selectors } from '@grafana/e2e-selectors';
- import { config } from '@grafana/runtime';
- import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
- import { configureStore } from 'app/store/configureStore';
- import { mockPluginApis, getCatalogPluginMock, getPluginsStateMock, mockUserPermissions } from '../__mocks__';
- import * as api from '../api';
- import { usePluginConfig } from '../hooks/usePluginConfig';
- import { fetchRemotePlugins } from '../state/actions';
- import {
- CatalogPlugin,
- CatalogPluginDetails,
- PluginTabIds,
- PluginTabLabels,
- ReducerState,
- RequestStatus,
- } from '../types';
- import PluginDetailsPage from './PluginDetails';
- jest.mock('@grafana/runtime', () => {
- const original = jest.requireActual('@grafana/runtime');
- const mockedRuntime = { ...original };
- mockedRuntime.config.buildInfo.version = 'v8.1.0';
- return mockedRuntime;
- });
- jest.mock('../hooks/usePluginConfig.tsx', () => ({
- usePluginConfig: jest.fn(() => ({
- value: {
- meta: {},
- },
- })),
- }));
- jest.mock('../helpers.ts', () => ({
- ...jest.requireActual('../helpers.ts'),
- updatePanels: jest.fn(),
- }));
- const renderPluginDetails = (
- pluginOverride: Partial<CatalogPlugin>,
- {
- pageId,
- pluginsStateOverride,
- }: {
- pageId?: PluginTabIds;
- pluginsStateOverride?: ReducerState;
- } = {}
- ): RenderResult => {
- const plugin = getCatalogPluginMock(pluginOverride);
- const { id } = plugin;
- const props = getRouteComponentProps({
- match: { params: { pluginId: id }, isExact: true, url: '', path: '' },
- queryParams: { page: pageId },
- location: {
- hash: '',
- pathname: `/plugins/${id}`,
- search: pageId ? `?page=${pageId}` : '',
- state: undefined,
- },
- });
- const store = configureStore({
- plugins: pluginsStateOverride || getPluginsStateMock([plugin]),
- });
- return render(
- <MemoryRouter>
- <Provider store={store}>
- <PluginDetailsPage {...props} />
- </Provider>
- </MemoryRouter>
- );
- };
- describe('Plugin details page', () => {
- const id = 'my-plugin';
- const originalWindowLocation = window.location;
- let dateNow: any;
- beforeAll(() => {
- dateNow = jest.spyOn(Date, 'now').mockImplementation(() => 1609470000000); // 2021-01-01 04:00:00
- // Enabling / disabling the plugin is currently reloading the page to propagate the changes
- Object.defineProperty(window, 'location', {
- configurable: true,
- value: { reload: jest.fn() },
- });
- });
- afterEach(() => {
- jest.clearAllMocks();
- config.pluginAdminExternalManageEnabled = false;
- config.licenseInfo.enabledFeatures = {};
- });
- afterAll(() => {
- dateNow.mockRestore();
- Object.defineProperty(window, 'location', { configurable: true, value: originalWindowLocation });
- });
- describe('viewed as user with grafana admin permissions', () => {
- beforeAll(() => {
- mockUserPermissions({
- isAdmin: true,
- isDataSourceEditor: true,
- isOrgAdmin: true,
- });
- });
- // We are doing this very basic test to see if the API fetching and data-munging is working correctly from a high-level.
- it('(SMOKE TEST) - should fetch and merge the remote and local plugin API responses correctly ', async () => {
- const id = 'smoke-test-plugin';
- mockPluginApis({
- remote: { slug: id },
- local: { id },
- });
- const props = getRouteComponentProps({
- match: { params: { pluginId: id }, isExact: true, url: '', path: '' },
- queryParams: {},
- location: {
- hash: '',
- pathname: `/plugins/${id}`,
- search: '',
- state: undefined,
- },
- });
- const store = configureStore();
- const { queryByText } = render(
- <MemoryRouter>
- <Provider store={store}>
- <PluginDetailsPage {...props} />
- </Provider>
- ,
- </MemoryRouter>
- );
- await waitFor(() => expect(queryByText(/licensed under the apache 2.0 license/i)).toBeInTheDocument());
- });
- it('should display an overview (plugin readme) by default', async () => {
- const { queryByText } = renderPluginDetails({ id });
- await waitFor(() => expect(queryByText(/licensed under the apache 2.0 license/i)).toBeInTheDocument());
- });
- it('should display an app config page by default for installed app plugins', async () => {
- const name = 'Akumuli';
- // @ts-ignore
- usePluginConfig.mockReturnValue({
- value: {
- meta: {
- type: PluginType.app,
- enabled: false,
- pinned: false,
- jsonData: {},
- },
- configPages: [
- {
- title: 'Config',
- icon: 'cog',
- id: 'configPage',
- body: function ConfigPage() {
- return <div>Custom Config Page!</div>;
- },
- },
- ],
- },
- });
- const { queryByText } = renderPluginDetails({
- name,
- isInstalled: true,
- type: PluginType.app,
- });
- await waitFor(() => expect(queryByText(/custom config page/i)).toBeInTheDocument());
- });
- it('should display the number of downloads in the header', async () => {
- // depending on what locale you have the Intl.NumberFormat will return a format that contains
- // whitespaces. In that case we don't want testing library to remove whitespaces.
- const downloads = 24324;
- const options: SelectorMatcherOptions = { normalizer: getDefaultNormalizer({ collapseWhitespace: false }) };
- const expected = new Intl.NumberFormat().format(downloads);
- const { queryByText } = renderPluginDetails({ id, downloads });
- await waitFor(() => expect(queryByText(expected, options)).toBeInTheDocument());
- });
- it('should display the installed version if a plugin is installed', async () => {
- const installedVersion = '1.3.443';
- const { queryByText } = renderPluginDetails({ id, installedVersion });
- await waitFor(() => expect(queryByText(installedVersion)).toBeInTheDocument());
- });
- it('should display the latest compatible version in the header if a plugin is not installed', async () => {
- const details: CatalogPluginDetails = {
- links: [],
- versions: [
- { version: '1.3.0', createdAt: '', isCompatible: false, grafanaDependency: '>=9.0.0' },
- { version: '1.2.0', createdAt: '', isCompatible: false, grafanaDependency: '>=8.3.0' },
- { version: '1.1.1', createdAt: '', isCompatible: true, grafanaDependency: '>=8.0.0' },
- { version: '1.1.0', createdAt: '', isCompatible: true, grafanaDependency: '>=8.0.0' },
- { version: '1.0.0', createdAt: '', isCompatible: true, grafanaDependency: '>=7.0.0' },
- ],
- };
- const { queryByText } = renderPluginDetails({ id, details });
- await waitFor(() => expect(queryByText('1.1.1')).toBeInTheDocument());
- await waitFor(() => expect(queryByText(/>=8.0.0/i)).toBeInTheDocument());
- });
- it('should display description in the header', async () => {
- const description = 'This is my description';
- const { queryByText } = renderPluginDetails({ id, description });
- await waitFor(() => expect(queryByText(description)).toBeInTheDocument());
- });
- it('should display a "Signed" badge if the plugin signature is verified', async () => {
- const { queryByText } = renderPluginDetails({ id, signature: PluginSignatureStatus.valid });
- await waitFor(() => expect(queryByText('Signed')).toBeInTheDocument());
- });
- it('should display a "Missing signature" badge if the plugin signature is missing', async () => {
- const { queryByText } = renderPluginDetails({ id, signature: PluginSignatureStatus.missing });
- await waitFor(() => expect(queryByText('Missing signature')).toBeInTheDocument());
- });
- it('should display a "Modified signature" badge if the plugin signature is modified', async () => {
- const { queryByText } = renderPluginDetails({ id, signature: PluginSignatureStatus.modified });
- await waitFor(() => expect(queryByText('Modified signature')).toBeInTheDocument());
- });
- it('should display a "Invalid signature" badge if the plugin signature is invalid', async () => {
- const { queryByText } = renderPluginDetails({ id, signature: PluginSignatureStatus.invalid });
- await waitFor(() => expect(queryByText('Invalid signature')).toBeInTheDocument());
- });
- it('should display version history if the plugin is published', async () => {
- const versions = [
- {
- version: '1.2.0',
- createdAt: '2018-04-06T20:23:41.000Z',
- isCompatible: false,
- grafanaDependency: '>=8.3.0',
- },
- {
- version: '1.1.0',
- createdAt: '2017-04-06T20:23:41.000Z',
- isCompatible: true,
- grafanaDependency: '>=8.0.0',
- },
- {
- version: '1.0.0',
- createdAt: '2016-04-06T20:23:41.000Z',
- isCompatible: true,
- grafanaDependency: '>=7.0.0',
- },
- ];
- const { queryByText, getByRole } = renderPluginDetails(
- {
- id,
- details: {
- links: [],
- versions,
- },
- },
- { pageId: PluginTabIds.VERSIONS }
- );
- // Check if version information is available
- await waitFor(() => expect(queryByText(/version history/i)).toBeInTheDocument());
- // Check the column headers
- expect(getByRole('columnheader', { name: /version/i })).toBeInTheDocument();
- expect(getByRole('columnheader', { name: /last updated/i })).toBeInTheDocument();
- // Check the data
- for (const version of versions) {
- expect(getByRole('cell', { name: new RegExp(version.version, 'i') })).toBeInTheDocument();
- expect(
- getByRole('cell', { name: new RegExp(dateTimeFormatTimeAgo(version.createdAt), 'i') })
- ).toBeInTheDocument();
- // Check the latest compatible version
- expect(queryByText('1.1.0 (latest compatible version)')).toBeInTheDocument();
- }
- });
- it("should display an install button for a plugin that isn't installed", async () => {
- const { queryByRole } = renderPluginDetails({ id, isInstalled: false });
- await waitFor(() => expect(queryByRole('button', { name: /^install/i })).toBeInTheDocument());
- // Does not display "uninstall" button
- expect(queryByRole('button', { name: /uninstall/i })).not.toBeInTheDocument();
- });
- it('should display an uninstall button for an already installed plugin', async () => {
- const { queryByRole } = renderPluginDetails({ id, isInstalled: true });
- await waitFor(() => expect(queryByRole('button', { name: /uninstall/i })).toBeInTheDocument());
- // Does not display "install" button
- expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
- });
- it('should display update and uninstall buttons for a plugin with update', async () => {
- const { queryByRole } = renderPluginDetails({ id, isInstalled: true, hasUpdate: true });
- // Displays an "update" button
- await waitFor(() => expect(queryByRole('button', { name: /update/i })).toBeInTheDocument());
- expect(queryByRole('button', { name: /uninstall/i })).toBeInTheDocument();
- // Does not display "install" button
- expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
- });
- it('should display an install button for enterprise plugins if license is valid', async () => {
- config.licenseInfo.enabledFeatures = { 'enterprise.plugins': true };
- const { queryByRole } = renderPluginDetails({ id, isInstalled: false, isEnterprise: true });
- await waitFor(() => expect(queryByRole('button', { name: /install/i })).toBeInTheDocument());
- });
- it('should not display install button for enterprise plugins if license is invalid', async () => {
- config.licenseInfo.enabledFeatures = {};
- const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: true, isEnterprise: true });
- await waitFor(() => expect(queryByRole('button', { name: /install/i })).not.toBeInTheDocument());
- expect(queryByText(/no valid Grafana Enterprise license detected/i)).toBeInTheDocument();
- expect(queryByRole('link', { name: /learn more/i })).toBeInTheDocument();
- });
- it('should not display install / uninstall buttons for core plugins', async () => {
- const { queryByRole } = renderPluginDetails({ id, isInstalled: true, isCore: true });
- await waitFor(() => expect(queryByRole('button', { name: /update/i })).not.toBeInTheDocument());
- await waitFor(() => expect(queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument());
- });
- it('should not display install / uninstall buttons for disabled plugins', async () => {
- const { queryByRole } = renderPluginDetails({ id, isInstalled: true, isDisabled: true });
- await waitFor(() => expect(queryByRole('button', { name: /update/i })).not.toBeInTheDocument());
- await waitFor(() => expect(queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument());
- });
- it('should not display install / uninstall buttons for renderer plugins', async () => {
- const { queryByRole } = renderPluginDetails({ id, type: PluginType.renderer });
- await waitFor(() => expect(queryByRole('button', { name: /update/i })).not.toBeInTheDocument());
- await waitFor(() => expect(queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument());
- });
- it('should display install link with `config.pluginAdminExternalManageEnabled` set to true', async () => {
- config.pluginAdminExternalManageEnabled = true;
- const { queryByRole } = renderPluginDetails({ id, isInstalled: false });
- await waitFor(() => expect(queryByRole('link', { name: /install via grafana.com/i })).toBeInTheDocument());
- });
- it('should display uninstall link for an installed plugin with `config.pluginAdminExternalManageEnabled` set to true', async () => {
- config.pluginAdminExternalManageEnabled = true;
- const { queryByRole } = renderPluginDetails({ id, isInstalled: true });
- await waitFor(() => expect(queryByRole('link', { name: /uninstall via grafana.com/i })).toBeInTheDocument());
- });
- it('should display update and uninstall links for a plugin with an available update and `config.pluginAdminExternalManageEnabled` set to true', async () => {
- config.pluginAdminExternalManageEnabled = true;
- const { queryByRole } = renderPluginDetails({ id, isInstalled: true, hasUpdate: true });
- await waitFor(() => expect(queryByRole('link', { name: /update via grafana.com/i })).toBeInTheDocument());
- expect(queryByRole('link', { name: /uninstall via grafana.com/i })).toBeInTheDocument();
- });
- it('should display alert with information about why the plugin is disabled', async () => {
- const { queryByLabelText } = renderPluginDetails({
- id,
- isInstalled: true,
- isDisabled: true,
- error: PluginErrorCode.modifiedSignature,
- });
- await waitFor(() => expect(queryByLabelText(selectors.pages.PluginPage.disabledInfo)).toBeInTheDocument());
- });
- it('should display grafana dependencies for a plugin if they are available', async () => {
- const { queryByText } = renderPluginDetails({
- id,
- details: {
- pluginDependencies: [],
- grafanaDependency: '>=8.0.0',
- links: [],
- },
- });
- // Wait for the dependencies part to be loaded
- await waitFor(() => expect(queryByText(/dependencies:/i)).toBeInTheDocument());
- expect(queryByText('Grafana >=8.0.0')).toBeInTheDocument();
- });
- it('should show a confirm modal when trying to uninstall a plugin', async () => {
- // @ts-ignore
- api.uninstallPlugin = jest.fn();
- const { queryByText, getByRole } = renderPluginDetails({
- id,
- name: 'Akumuli',
- isInstalled: true,
- details: {
- pluginDependencies: [],
- grafanaDependency: '>=8.0.0',
- links: [],
- versions: [
- {
- version: '1.0.0',
- createdAt: '',
- isCompatible: true,
- grafanaDependency: '>=8.0.0',
- },
- ],
- },
- });
- // Wait for the install controls to be loaded
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- // Open the confirmation modal
- await userEvent.click(getByRole('button', { name: /uninstall/i }));
- expect(queryByText('Uninstall Akumuli')).toBeInTheDocument();
- expect(queryByText('Are you sure you want to uninstall this plugin?')).toBeInTheDocument();
- expect(api.uninstallPlugin).toHaveBeenCalledTimes(0);
- // Confirm the uninstall
- await userEvent.click(getByRole('button', { name: /confirm/i }));
- expect(api.uninstallPlugin).toHaveBeenCalledTimes(1);
- expect(api.uninstallPlugin).toHaveBeenCalledWith(id);
- // Check if the modal disappeared
- expect(queryByText('Uninstall Akumuli')).not.toBeInTheDocument();
- });
- it('should not display the install / uninstall / update buttons if the GCOM api is not available', async () => {
- let rendered: RenderResult;
- const plugin = getCatalogPluginMock({ id });
- const state = getPluginsStateMock([plugin]);
- // Mock the store like if the remote plugins request was rejected
- const pluginsStateOverride = {
- ...state,
- requests: {
- ...state.requests,
- [fetchRemotePlugins.typePrefix]: {
- status: RequestStatus.Rejected,
- },
- },
- };
- // Does not show an Install button
- rendered = renderPluginDetails({ id }, { pluginsStateOverride });
- await waitFor(() => expect(rendered.queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument());
- rendered.unmount();
- // Does not show a Uninstall button
- rendered = renderPluginDetails({ id, isInstalled: true }, { pluginsStateOverride });
- await waitFor(() => expect(rendered.queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument());
- rendered.unmount();
- // Does not show an Update button
- rendered = renderPluginDetails({ id, isInstalled: true, hasUpdate: true }, { pluginsStateOverride });
- await waitFor(() => expect(rendered.queryByRole('button', { name: /update/i })).not.toBeInTheDocument());
- // Shows a message to the user
- // TODO<Import these texts from a single source of truth instead of having them defined in multiple places>
- const message = 'The install controls have been disabled because the Grafana server cannot access grafana.com.';
- expect(rendered.getByText(message)).toBeInTheDocument();
- });
- it('should not display the install / uninstall / update buttons if `pluginAdminEnabled` flag is set to FALSE in the Grafana config', async () => {
- let rendered: RenderResult;
- // Disable the install controls for the plugins catalog
- config.pluginAdminEnabled = false;
- // Should not show an "Install" button
- rendered = renderPluginDetails({ id, isInstalled: false });
- await waitFor(() => expect(rendered.queryByRole('button', { name: /^install/i })).not.toBeInTheDocument());
- // Should not show an "Uninstall" button
- rendered = renderPluginDetails({ id, isInstalled: true });
- await waitFor(() => expect(rendered.queryByRole('button', { name: /^uninstall/i })).not.toBeInTheDocument());
- // Should not show an "Update" button
- rendered = renderPluginDetails({ id, isInstalled: true, hasUpdate: true });
- await waitFor(() => expect(rendered.queryByRole('button', { name: /^update/i })).not.toBeInTheDocument());
- });
- it('should display a "Create" button as a post installation step for installed data source plugins', async () => {
- const name = 'Akumuli';
- const { queryByText } = renderPluginDetails({
- name,
- isInstalled: true,
- type: PluginType.datasource,
- });
- await waitFor(() => queryByText('Uninstall'));
- expect(queryByText(`Create a ${name} data source`)).toBeInTheDocument();
- });
- it('should not display a "Create" button as a post installation step for disabled data source plugins', async () => {
- const name = 'Akumuli';
- const { queryByText } = renderPluginDetails({
- name,
- isInstalled: true,
- isDisabled: true,
- type: PluginType.datasource,
- });
- await waitFor(() => queryByText('Uninstall'));
- expect(queryByText(`Create a ${name} data source`)).toBeNull();
- });
- it('should not display post installation step for panel plugins', async () => {
- const name = 'Akumuli';
- const { queryByText } = renderPluginDetails({
- name,
- isInstalled: true,
- type: PluginType.panel,
- });
- await waitFor(() => queryByText('Uninstall'));
- expect(queryByText(`Create a ${name} data source`)).toBeNull();
- });
- it('should display an enable button for app plugins that are not enabled as a post installation step', async () => {
- const name = 'Akumuli';
- // @ts-ignore
- usePluginConfig.mockReturnValue({
- value: {
- meta: {
- enabled: false,
- pinned: false,
- jsonData: {},
- },
- },
- });
- const { queryByText, queryByRole } = renderPluginDetails({
- name,
- isInstalled: true,
- type: PluginType.app,
- });
- await waitFor(() => queryByText('Uninstall'));
- expect(queryByRole('button', { name: /enable/i })).toBeInTheDocument();
- expect(queryByRole('button', { name: /disable/i })).not.toBeInTheDocument();
- });
- it('should display a disable button for app plugins that are enabled as a post installation step', async () => {
- const name = 'Akumuli';
- // @ts-ignore
- usePluginConfig.mockReturnValue({
- value: {
- meta: {
- enabled: true,
- pinned: false,
- jsonData: {},
- },
- },
- });
- const { queryByText, queryByRole } = renderPluginDetails({
- name,
- isInstalled: true,
- type: PluginType.app,
- });
- await waitFor(() => queryByText('Uninstall'));
- expect(queryByRole('button', { name: /disable/i })).toBeInTheDocument();
- expect(queryByRole('button', { name: /enable/i })).not.toBeInTheDocument();
- });
- it('should be possible to enable an app plugin', async () => {
- const id = 'akumuli-datasource';
- const name = 'Akumuli';
- // @ts-ignore
- api.updatePluginSettings = jest.fn();
- // @ts-ignore
- usePluginConfig.mockReturnValue({
- value: {
- meta: {
- enabled: false,
- pinned: false,
- jsonData: {},
- },
- },
- });
- const { queryByText, getByRole } = renderPluginDetails({
- id,
- name,
- isInstalled: true,
- type: PluginType.app,
- });
- // Wait for the header to be loaded
- await waitFor(() => queryByText('Uninstall'));
- // Click on "Enable"
- await userEvent.click(getByRole('button', { name: /enable/i }));
- // Check if the API request was initiated
- expect(api.updatePluginSettings).toHaveBeenCalledTimes(1);
- expect(api.updatePluginSettings).toHaveBeenCalledWith(id, {
- enabled: true,
- pinned: true,
- jsonData: {},
- });
- });
- it('should be possible to disable an app plugin', async () => {
- const id = 'akumuli-datasource';
- const name = 'Akumuli';
- // @ts-ignore
- api.updatePluginSettings = jest.fn();
- // @ts-ignore
- usePluginConfig.mockReturnValue({
- value: {
- meta: {
- enabled: true,
- pinned: true,
- jsonData: {},
- },
- },
- });
- const { queryByText, getByRole } = renderPluginDetails({
- id,
- name,
- isInstalled: true,
- type: PluginType.app,
- });
- // Wait for the header to be loaded
- await waitFor(() => queryByText('Uninstall'));
- // Click on "Disable"
- await userEvent.click(getByRole('button', { name: /disable/i }));
- // Check if the API request was initiated
- expect(api.updatePluginSettings).toHaveBeenCalledTimes(1);
- expect(api.updatePluginSettings).toHaveBeenCalledWith(id, {
- enabled: false,
- pinned: false,
- jsonData: {},
- });
- });
- it('should not display versions tab for plugins not published to gcom', async () => {
- const { queryByText } = renderPluginDetails({
- name: 'Akumuli',
- isInstalled: true,
- type: PluginType.app,
- isPublished: false,
- });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByText(PluginTabLabels.VERSIONS)).toBeNull();
- });
- it('should not display update for plugins not published to gcom', async () => {
- const { queryByText, queryByRole } = renderPluginDetails({
- name: 'Akumuli',
- isInstalled: true,
- hasUpdate: true,
- type: PluginType.app,
- isPublished: false,
- });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /update/i })).not.toBeInTheDocument();
- });
- it('should not display install for plugins not published to gcom', async () => {
- const { queryByText, queryByRole } = renderPluginDetails({
- name: 'Akumuli',
- isInstalled: false,
- hasUpdate: false,
- type: PluginType.app,
- isPublished: false,
- });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
- });
- it('should not display uninstall for plugins not published to gcom', async () => {
- const { queryByText, queryByRole } = renderPluginDetails({
- name: 'Akumuli',
- isInstalled: true,
- hasUpdate: false,
- type: PluginType.app,
- isPublished: false,
- });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /uninstall/i })).not.toBeInTheDocument();
- });
- });
- describe('viewed as user without grafana admin permissions', () => {
- beforeAll(() => {
- mockUserPermissions({
- isAdmin: false,
- isDataSourceEditor: false,
- isOrgAdmin: false,
- });
- });
- it("should not display an install button for a plugin that isn't installed", async () => {
- const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: false });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
- });
- it('should not display an uninstall button for an already installed plugin', async () => {
- const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: true });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /uninstall/i })).not.toBeInTheDocument();
- });
- it('should not display update or uninstall buttons for a plugin with update', async () => {
- const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: true, hasUpdate: true });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /update/i })).not.toBeInTheDocument();
- expect(queryByRole('button', { name: /uninstall/i })).not.toBeInTheDocument();
- });
- it('should not display an install button for enterprise plugins if license is valid', async () => {
- config.licenseInfo.enabledFeatures = { 'enterprise.plugins': true };
- const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: false, isEnterprise: true });
- await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
- expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
- });
- });
- describe('viewed as user without data source edit permissions', () => {
- beforeAll(() => {
- mockUserPermissions({
- isAdmin: true,
- isDataSourceEditor: false,
- isOrgAdmin: true,
- });
- });
- it('should not display the data source post intallation step', async () => {
- const name = 'Akumuli';
- const { queryByText } = renderPluginDetails({
- name,
- isInstalled: true,
- type: PluginType.app,
- });
- await waitFor(() => queryByText('Uninstall'));
- expect(queryByText(`Create a ${name} data source`)).toBeNull();
- });
- });
- });
|