mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Hide dashboard hint when dashboard already selected * Disable panels of types other than graph and timeseries * Add a test checking disabled panels * Make all panels selectable * Fix tests
305 lines
10 KiB
TypeScript
305 lines
10 KiB
TypeScript
import { findByRole, findByText, findByTitle, getByTestId, render } from '@testing-library/react';
|
||
import userEvent from '@testing-library/user-event';
|
||
import { rest } from 'msw';
|
||
import { setupServer } from 'msw/node';
|
||
import React from 'react';
|
||
import { FormProvider, useForm } from 'react-hook-form';
|
||
import { TestProvider } from 'test/helpers/TestProvider';
|
||
import { byRole, byTestId } from 'testing-library-selector';
|
||
|
||
import { setBackendSrv } from '@grafana/runtime';
|
||
import { defaultDashboard } from '@grafana/schema';
|
||
import { backendSrv } from 'app/core/services/backend_srv';
|
||
|
||
import { DashboardDTO } from '../../../../../types';
|
||
import { DashboardSearchItem, DashboardSearchItemType } from '../../../../search/types';
|
||
import { mockStore } from '../../mocks';
|
||
import { mockSearchApiResponse } from '../../mocks/grafanaApi';
|
||
import { RuleFormValues } from '../../types/rule-form';
|
||
import { Annotation } from '../../utils/constants';
|
||
import { getDefaultFormValues } from '../../utils/rule-form';
|
||
|
||
import 'whatwg-fetch';
|
||
|
||
import AnnotationsField from './AnnotationsField';
|
||
|
||
// To get anything displayed inside the Autosize component we need to mock it
|
||
// Ref https://github.com/bvaughn/react-window/issues/454#issuecomment-646031139
|
||
jest.mock(
|
||
'react-virtualized-auto-sizer',
|
||
() =>
|
||
({ children }: { children: ({ height, width }: { height: number; width: number }) => JSX.Element }) =>
|
||
children({ height: 500, width: 330 })
|
||
);
|
||
|
||
const ui = {
|
||
setDashboardButton: byRole('button', { name: 'Set dashboard and panel' }),
|
||
annotationKeys: byTestId('annotation-key-', { exact: false }),
|
||
annotationValues: byTestId('annotation-value-', { exact: false }),
|
||
dashboardPicker: {
|
||
dialog: byRole('dialog'),
|
||
heading: byRole('heading', { name: 'Select dashboard and panel' }),
|
||
confirmButton: byRole('button', { name: 'Confirm' }),
|
||
},
|
||
} as const;
|
||
|
||
const server = setupServer();
|
||
|
||
beforeAll(() => {
|
||
setBackendSrv(backendSrv);
|
||
server.listen({ onUnhandledRequest: 'error' });
|
||
});
|
||
|
||
beforeEach(() => {
|
||
server.resetHandlers();
|
||
});
|
||
|
||
afterAll(() => {
|
||
server.close();
|
||
});
|
||
|
||
function FormWrapper({ formValues }: { formValues?: Partial<RuleFormValues> }) {
|
||
const store = mockStore(() => null);
|
||
const formApi = useForm<RuleFormValues>({ defaultValues: { ...getDefaultFormValues(), ...formValues } });
|
||
|
||
return (
|
||
<TestProvider store={store}>
|
||
<FormProvider {...formApi}>
|
||
<AnnotationsField />
|
||
</FormProvider>
|
||
</TestProvider>
|
||
);
|
||
}
|
||
|
||
describe('AnnotationsField', function () {
|
||
it('should display default list of annotations', function () {
|
||
render(<FormWrapper />);
|
||
|
||
const annotationElements = ui.annotationKeys.getAll();
|
||
|
||
expect(annotationElements).toHaveLength(3);
|
||
expect(annotationElements[0]).toHaveTextContent('Summary');
|
||
expect(annotationElements[1]).toHaveTextContent('Description');
|
||
expect(annotationElements[2]).toHaveTextContent('Runbook URL');
|
||
});
|
||
|
||
describe('Dashboard and panel picker', function () {
|
||
it('should display dashboard and panel selector when select button clicked', async function () {
|
||
mockSearchApiResponse(server, []);
|
||
|
||
const user = userEvent.setup();
|
||
|
||
render(<FormWrapper />);
|
||
|
||
await user.click(ui.setDashboardButton.get());
|
||
|
||
expect(ui.dashboardPicker.dialog.get()).toBeInTheDocument();
|
||
expect(ui.dashboardPicker.heading.get()).toBeInTheDocument();
|
||
});
|
||
|
||
it('should enable Confirm button only when dashboard and panel selected', async function () {
|
||
mockSearchApiResponse(server, [
|
||
mockDashboardSearchItem({ title: 'My dashboard', uid: 'dash-test-uid', type: DashboardSearchItemType.DashDB }),
|
||
]);
|
||
|
||
mockGetDashboardResponse(
|
||
mockDashboardDto({
|
||
title: 'My dashboard',
|
||
uid: 'dash-test-uid',
|
||
panels: [
|
||
{ id: 1, title: 'First panel', type: 'timeseries' },
|
||
{ id: 2, title: 'Second panel', type: 'timeseries' },
|
||
],
|
||
})
|
||
);
|
||
|
||
const user = userEvent.setup();
|
||
|
||
render(<FormWrapper />);
|
||
|
||
await user.click(ui.setDashboardButton.get());
|
||
expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled();
|
||
|
||
await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard'));
|
||
expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled();
|
||
|
||
await user.click(await findByText(ui.dashboardPicker.dialog.get(), 'First panel'));
|
||
expect(ui.dashboardPicker.confirmButton.get()).toBeEnabled();
|
||
});
|
||
|
||
it('should add selected dashboard and panel as annotations', async function () {
|
||
mockSearchApiResponse(server, [
|
||
mockDashboardSearchItem({ title: 'My dashboard', uid: 'dash-test-uid', type: DashboardSearchItemType.DashDB }),
|
||
]);
|
||
|
||
mockGetDashboardResponse(
|
||
mockDashboardDto({
|
||
title: 'My dashboard',
|
||
uid: 'dash-test-uid',
|
||
panels: [
|
||
{ id: 1, title: 'First panel', type: 'graph' },
|
||
{ id: 2, title: 'Second panel', type: 'graph' },
|
||
],
|
||
})
|
||
);
|
||
|
||
const user = userEvent.setup();
|
||
|
||
render(<FormWrapper formValues={{ annotations: [] }} />);
|
||
|
||
await user.click(ui.setDashboardButton.get());
|
||
await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard'));
|
||
|
||
await user.click(await findByText(ui.dashboardPicker.dialog.get(), 'Second panel'));
|
||
|
||
await user.click(ui.dashboardPicker.confirmButton.get());
|
||
|
||
const annotationKeyElements = ui.annotationKeys.getAll();
|
||
const annotationValueElements = ui.annotationValues.getAll();
|
||
|
||
expect(ui.dashboardPicker.dialog.query()).not.toBeInTheDocument();
|
||
|
||
expect(annotationKeyElements).toHaveLength(2);
|
||
expect(annotationValueElements).toHaveLength(2);
|
||
|
||
expect(annotationKeyElements[0]).toHaveTextContent('Dashboard UID');
|
||
expect(annotationValueElements[0]).toHaveTextContent('dash-test-uid');
|
||
|
||
expect(annotationKeyElements[1]).toHaveTextContent('Panel ID');
|
||
expect(annotationValueElements[1]).toHaveTextContent('2');
|
||
});
|
||
|
||
// this test _should_ work in theory but something is stopping the 'onClick' function on the dashboard item
|
||
// to trigger "handleDashboardChange" – skipping it for now but has been manually tested.
|
||
it('should update existing dashboard and panel identifies', async function () {
|
||
mockSearchApiResponse(server, [
|
||
mockDashboardSearchItem({ title: 'My dashboard', uid: 'dash-test-uid', type: DashboardSearchItemType.DashDB }),
|
||
mockDashboardSearchItem({
|
||
title: 'My other dashboard',
|
||
uid: 'dash-other-uid',
|
||
type: DashboardSearchItemType.DashDB,
|
||
}),
|
||
]);
|
||
|
||
mockGetDashboardResponse(
|
||
mockDashboardDto({
|
||
title: 'My dashboard',
|
||
uid: 'dash-test-uid',
|
||
panels: [
|
||
{ id: 1, title: 'First panel', type: 'timeseries' },
|
||
{ id: 2, title: 'Second panel', type: 'timeseries' },
|
||
],
|
||
})
|
||
);
|
||
mockGetDashboardResponse(
|
||
mockDashboardDto({
|
||
title: 'My other dashboard',
|
||
uid: 'dash-other-uid',
|
||
panels: [{ id: 3, title: 'Third panel', type: 'timeseries' }],
|
||
})
|
||
);
|
||
|
||
const user = userEvent.setup();
|
||
|
||
render(
|
||
<FormWrapper
|
||
formValues={{
|
||
annotations: [
|
||
{ key: Annotation.dashboardUID, value: 'dash-test-uid' },
|
||
{ key: Annotation.panelID, value: '1' },
|
||
],
|
||
}}
|
||
/>
|
||
);
|
||
|
||
let annotationValueElements = ui.annotationValues.getAll();
|
||
expect(annotationValueElements[0]).toHaveTextContent('dash-test-uid');
|
||
expect(annotationValueElements[1]).toHaveTextContent('1');
|
||
|
||
const { confirmButton, dialog } = ui.dashboardPicker;
|
||
|
||
await user.click(ui.setDashboardButton.get());
|
||
await user.click(await findByRole(dialog.get(), 'button', { name: /My other dashboard/ }));
|
||
await user.click(await findByRole(dialog.get(), 'button', { name: /Third panel/ }));
|
||
await user.click(confirmButton.get());
|
||
|
||
expect(ui.dashboardPicker.dialog.query()).not.toBeInTheDocument();
|
||
|
||
const annotationKeyElements = ui.annotationKeys.getAll();
|
||
annotationValueElements = ui.annotationValues.getAll();
|
||
|
||
expect(annotationKeyElements).toHaveLength(2);
|
||
expect(annotationValueElements).toHaveLength(2);
|
||
|
||
expect(annotationKeyElements[0]).toHaveTextContent('Dashboard UID');
|
||
expect(annotationValueElements[0]).toHaveTextContent('dash-other-uid');
|
||
|
||
expect(annotationKeyElements[1]).toHaveTextContent('Panel ID');
|
||
expect(annotationValueElements[1]).toHaveTextContent('3');
|
||
});
|
||
|
||
it('should render warning icon for panels of type other than graph and timeseries', async function () {
|
||
mockSearchApiResponse(server, [
|
||
mockDashboardSearchItem({ title: 'My dashboard', uid: 'dash-test-uid', type: DashboardSearchItemType.DashDB }),
|
||
]);
|
||
|
||
mockGetDashboardResponse(
|
||
mockDashboardDto({
|
||
title: 'My dashboard',
|
||
uid: 'dash-test-uid',
|
||
panels: [
|
||
{ id: 1, title: 'First panel', type: 'bar' },
|
||
{ id: 2, title: 'Second panel', type: 'graph' },
|
||
],
|
||
})
|
||
);
|
||
|
||
const user = userEvent.setup();
|
||
|
||
render(<FormWrapper formValues={{ annotations: [] }} />);
|
||
|
||
const { dialog } = ui.dashboardPicker;
|
||
|
||
await user.click(ui.setDashboardButton.get());
|
||
await user.click(await findByTitle(dialog.get(), 'My dashboard'));
|
||
|
||
const warnedPanel = await findByRole(dialog.get(), 'button', { name: /First panel/ });
|
||
|
||
expect(getByTestId(warnedPanel, 'warning-icon')).toBeInTheDocument();
|
||
});
|
||
});
|
||
});
|
||
|
||
function mockGetDashboardResponse(dashboard: DashboardDTO) {
|
||
server.use(
|
||
rest.get(`/api/dashboards/uid/${dashboard.dashboard.uid}`, (req, res, ctx) =>
|
||
res(ctx.json<DashboardDTO>(dashboard))
|
||
)
|
||
);
|
||
}
|
||
|
||
function mockDashboardSearchItem(searchItem: Partial<DashboardSearchItem>) {
|
||
return {
|
||
title: '',
|
||
uid: '',
|
||
type: DashboardSearchItemType.DashDB,
|
||
url: '',
|
||
uri: '',
|
||
items: [],
|
||
tags: [],
|
||
slug: '',
|
||
isStarred: false,
|
||
...searchItem,
|
||
};
|
||
}
|
||
|
||
function mockDashboardDto(dashboard: Partial<DashboardDTO['dashboard']>): DashboardDTO {
|
||
return {
|
||
dashboard: {
|
||
...defaultDashboard,
|
||
...dashboard,
|
||
} as DashboardDTO['dashboard'],
|
||
meta: {},
|
||
};
|
||
}
|