DashboardLibrary: Add template dashboard tests (#114179)

* dashboard template tests

* command plaette restored

* command plaette restored
This commit is contained in:
Juan Cabanas
2025-11-19 16:26:34 -03:00
committed by GitHub
parent 5a0e9e4183
commit 62b8caec9b
4 changed files with 278 additions and 20 deletions
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
import { TestProvider } from 'test/helpers/TestProvider';
import { NavModelItem } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { config, reportInteraction } from '@grafana/runtime';
import { QuickAdd } from './QuickAdd';
@@ -11,6 +11,13 @@ jest.mock('@grafana/runtime', () => {
return {
...jest.requireActual('@grafana/runtime'),
reportInteraction: jest.fn(),
getDataSourceSrv: () => ({
getList: jest
.fn()
.mockReturnValue([
{ name: 'Test Data Source', uid: 'test-data-source-uid', type: 'grafana-testdata-datasource' },
]),
}),
};
});
@@ -74,4 +81,31 @@ describe('QuickAdd', () => {
from: 'quickadd',
});
});
describe('Dashboard from template button', () => {
beforeEach(() => {
config.featureToggles.dashboardTemplates = true;
});
it('shows a `Dashboard from template` button when the feature flag is enabled', async () => {
setup();
await userEvent.click(screen.getByRole('button', { name: 'New' }));
expect(screen.getByRole('link', { name: 'Dashboard from template' })).toBeInTheDocument();
});
it('does not show a `Dashboard from template` button when the feature flag is disabled', async () => {
config.featureToggles.dashboardTemplates = false;
setup();
await userEvent.click(screen.getByRole('button', { name: 'New' }));
expect(screen.queryByRole('link', { name: 'Dashboard from template' })).not.toBeInTheDocument();
});
it('redirects the user to the dashboard from template page when the button is clicked', async () => {
setup();
await userEvent.click(screen.getByRole('button', { name: 'New' }));
const link = screen.getByRole('link', { name: 'Dashboard from template' });
expect(link).toHaveAttribute('href', '/dashboards?templateDashboards=true&source=quickAdd');
});
});
});
@@ -1,3 +1,4 @@
import { http, HttpResponse } from 'msw';
import { ComponentProps } from 'react';
import { useParams } from 'react-router-dom-v5-compat';
import AutoSizer from 'react-virtualized-auto-sizer';
@@ -5,7 +6,7 @@ import { render as testRender, screen, waitFor } from 'test/test-utils';
import { selectors } from '@grafana/e2e-selectors';
import { config, setBackendSrv } from '@grafana/runtime';
import { setupMockServer } from '@grafana/test-utils/server';
import server, { setupMockServer } from '@grafana/test-utils/server';
import { getFolderFixtures } from '@grafana/test-utils/unstable';
import { backendSrv } from 'app/core/services/backend_srv';
import { contextSrv } from 'app/core/services/context_srv';
@@ -41,9 +42,25 @@ jest.mock('react-router-dom-v5-compat', () => ({
useParams: jest.fn().mockReturnValue({}),
}));
function render(ui: Parameters<typeof testRender>[0]) {
jest.mock('@grafana/runtime', () => {
return {
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => ({
getList: jest
.fn()
.mockReturnValue([
{ name: 'Test Data Source', uid: 'test-data-source-uid', type: 'grafana-testdata-datasource' },
]),
}),
};
});
function render(ui: Parameters<typeof testRender>[0], options: Parameters<typeof testRender>[1] = {}) {
return testRender(ui, {
preloadedState: { navIndex: { 'dashboards/browse': { text: 'Dashboards', id: 'dashboards/browse' } } },
preloadedState: {
navIndex: { 'dashboards/browse': { text: 'Dashboards', id: 'dashboards/browse' } },
},
...options,
});
}
@@ -324,4 +341,42 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
expect(checkbox).toBeInTheDocument();
});
});
describe('Template dashboard modal', () => {
beforeEach(() => {
config.featureToggles.dashboardTemplates = true;
server.use(
http.get('/api/gnet/dashboards', () => {
return HttpResponse.json({
page: 1,
pages: 1,
items: [
{
id: 1,
name: 'Test Template Dashboard',
description: 'A test template dashboard',
downloads: 100,
datasource: 'grafana-testdata-datasource',
},
],
});
})
);
});
it('should show TemplateDashboard modal when the feature flag is enabled', async () => {
render(<BrowseDashboardsPage queryParams={{}} />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
expect(await screen.findByRole('dialog', { name: 'Start a dashboard from a template' })).toBeInTheDocument();
});
it('should not show TemplateDashboard modal when the feature flag is disabled', async () => {
config.featureToggles.dashboardTemplates = false;
render(<BrowseDashboardsPage queryParams={{}} />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
expect(screen.queryByRole('dialog', { name: 'Start a dashboard from a template' })).not.toBeInTheDocument();
});
});
});
@@ -1,7 +1,7 @@
import { render as rtlRender, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TestProvider } from 'test/helpers/TestProvider';
import { screen, within } from '@testing-library/react';
import { render } from 'test/test-utils';
import { config } from '@grafana/runtime';
import { ManagerKind } from 'app/features/apiserver/types';
import { useIsProvisionedInstance } from 'app/features/provisioning/hooks/useIsProvisionedInstance';
import { FolderDTO } from 'app/types/folders';
@@ -14,18 +14,29 @@ jest.mock('app/features/provisioning/hooks/useIsProvisionedInstance', () => ({
useIsProvisionedInstance: jest.fn(),
}));
jest.mock('@grafana/runtime', () => {
return {
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => ({
getList: jest
.fn()
.mockReturnValue([
{ name: 'Test Data Source', uid: 'test-data-source-uid', type: 'grafana-testdata-datasource' },
]),
}),
};
});
const mockUseIsProvisionedInstance = useIsProvisionedInstance as jest.MockedFunction<typeof useIsProvisionedInstance>;
const mockParentFolder = mockFolderDTO();
function render(...[ui, options]: Parameters<typeof rtlRender>) {
rtlRender(<TestProvider>{ui}</TestProvider>, options);
}
async function renderAndOpen(folder?: FolderDTO) {
render(<CreateNewButton canCreateDashboard canCreateFolder parentFolder={folder} isReadOnlyRepo={false} />);
const { user } = render(
<CreateNewButton canCreateDashboard canCreateFolder parentFolder={folder} isReadOnlyRepo={false} />
);
const newButton = screen.getByText('New');
await userEvent.click(newButton);
await user.click(newButton);
}
describe('NewActionsButton', () => {
@@ -53,13 +64,13 @@ describe('NewActionsButton', () => {
});
it('clicking the "New folder" button opens the drawer', async () => {
render(
const { user } = render(
<CreateNewButton canCreateDashboard canCreateFolder parentFolder={mockParentFolder} isReadOnlyRepo={false} />
);
const newButton = screen.getByText('New');
await userEvent.click(newButton);
await userEvent.click(screen.getByText('New folder'));
await user.click(newButton);
await user.click(screen.getByText('New folder'));
const drawer = screen.getByRole('dialog', { name: 'Drawer title New folder' });
expect(drawer).toBeInTheDocument();
@@ -68,9 +79,9 @@ describe('NewActionsButton', () => {
});
it('should only render dashboard items when folder creation is disabled', async () => {
render(<CreateNewButton canCreateDashboard canCreateFolder={false} isReadOnlyRepo={false} />);
const { user } = render(<CreateNewButton canCreateDashboard canCreateFolder={false} isReadOnlyRepo={false} />);
const newButton = screen.getByText('New');
await userEvent.click(newButton);
await user.click(newButton);
expect(screen.getByRole('link', { name: 'New dashboard' })).toBeInTheDocument();
expect(screen.getByText('Import')).toBeInTheDocument();
@@ -78,9 +89,9 @@ describe('NewActionsButton', () => {
});
it('should only render folder item when dashboard creation is disabled', async () => {
render(<CreateNewButton canCreateDashboard={false} canCreateFolder isReadOnlyRepo={false} />);
const { user } = render(<CreateNewButton canCreateDashboard={false} canCreateFolder isReadOnlyRepo={false} />);
const newButton = screen.getByText('New');
await userEvent.click(newButton);
await user.click(newButton);
expect(screen.queryByText('New dashboard')).not.toBeInTheDocument();
expect(screen.queryByText('Import')).not.toBeInTheDocument();
@@ -124,4 +135,27 @@ describe('NewActionsButton', () => {
expect(screen.getByText('New folder')).toBeInTheDocument();
expect(screen.queryByText('Import')).not.toBeInTheDocument();
});
describe('Dashboard from template button', () => {
beforeEach(() => {
config.featureToggles.dashboardTemplates = true;
});
it('should show a `Dashboard from template` button when the feature flag is enabled', async () => {
await renderAndOpen();
expect(screen.getByRole('link', { name: 'Dashboard from template' })).toBeInTheDocument();
});
it('should not show a `Dashboard from template` button when the feature flag is disabled', async () => {
config.featureToggles.dashboardTemplates = false;
await renderAndOpen();
expect(screen.queryByRole('link', { name: 'Dashboard from template' })).not.toBeInTheDocument();
});
it('should redirect the user to the dashboard from template page when the button is clicked', async () => {
await renderAndOpen();
const link = screen.getByRole('link', { name: 'Dashboard from template' });
expect(link).toHaveAttribute('href', '/dashboards?templateDashboards=true&source=createNewButton');
});
});
});
@@ -0,0 +1,135 @@
import { http, HttpResponse } from 'msw';
import { render, screen, waitFor } from 'test/test-utils';
import { setBackendSrv } from '@grafana/runtime';
import server, { setupMockServer } from '@grafana/test-utils/server';
import { backendSrv } from 'app/core/services/backend_srv';
import { TemplateDashboardModal } from './TemplateDashboardModal';
setBackendSrv(backendSrv);
setupMockServer();
const mockGetList = jest
.fn()
.mockReturnValue([{ name: 'Test Data Source', uid: 'test-data-source-uid', type: 'grafana-testdata-datasource' }]);
jest.mock('@grafana/runtime', () => {
return {
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => ({
getList: mockGetList,
}),
};
});
describe('TemplateDashboardModal', () => {
beforeEach(() => {
mockGetList.mockReturnValue([
{ name: 'Test Data Source', uid: 'test-data-source-uid', type: 'grafana-testdata-datasource' },
]);
server.use(
http.get('/api/gnet/dashboards', () => {
return HttpResponse.json({
page: 1,
pages: 1,
items: [
{
id: 1,
name: 'Test Template Dashboard',
description: 'A test template dashboard',
downloads: 100,
datasource: 'grafana-testdata-datasource',
},
{
id: 2,
name: 'Test Template Dashboard 2',
description: 'A test template dashboard 2',
downloads: 100,
datasource: 'grafana-testdata-datasource',
},
],
});
})
);
});
describe('Render conditions', () => {
it('should show TemplateDashboard modal when query param is present, test data source is available and there are template dashboards', async () => {
render(<TemplateDashboardModal />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
expect(await screen.findByRole('dialog', { name: 'Start a dashboard from a template' })).toBeInTheDocument();
});
it('should not show TemplateDashboard modal when query param is present but test data source is not available', async () => {
mockGetList.mockReturnValueOnce([]);
render(<TemplateDashboardModal />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
expect(screen.queryByRole('dialog', { name: 'Start a dashboard from a template' })).not.toBeInTheDocument();
});
it('should not show TemplateDashboard modal when query param is present but there are no template dashboards', async () => {
server.use(
http.get('/api/gnet/dashboards', () => {
return HttpResponse.json({
page: 1,
pages: 1,
items: [],
});
})
);
render(<TemplateDashboardModal />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
await waitFor(() => {
expect(screen.queryByRole('dialog', { name: 'Start a dashboard from a template' })).not.toBeInTheDocument();
});
});
it('should not show TemplateDashboard modal when query param is not present', async () => {
render(<TemplateDashboardModal />);
await waitFor(() => {
expect(screen.queryByRole('dialog', { name: 'Start a dashboard from a template' })).not.toBeInTheDocument();
});
});
});
describe('Render content', () => {
it('should render title and description', async () => {
render(<TemplateDashboardModal />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
await waitFor(() => {
expect(screen.getByText('Start a dashboard from a template')).toBeInTheDocument();
});
expect(
screen.getByText(
'Get started with Grafana templates using sample data. Connect your data to power them with real metrics.'
)
).toBeInTheDocument();
});
it('should show template dashboard cards', async () => {
render(<TemplateDashboardModal />, {
historyOptions: { initialEntries: [`/dashboards?templateDashboards=true`] },
});
await waitFor(() => {
// Assert DashboardCard components are rendered by checking for their headings
expect(screen.getByRole('heading', { name: 'Test Template Dashboard' })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Test Template Dashboard 2' })).toBeInTheDocument();
// Assert DashboardCard components are rendered by checking for "Use template" buttons
const useTemplateButtons = screen.getAllByRole('button', { name: 'Use template' });
expect(useTemplateButtons).toHaveLength(2);
// Assert text content (descriptions)
expect(screen.getByText('A test template dashboard')).toBeInTheDocument();
expect(screen.getByText('A test template dashboard 2')).toBeInTheDocument();
});
});
});
});