grafana/public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx

237 lines
7.0 KiB
TypeScript

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { AutoSizerProps } from 'react-virtualized-auto-sizer';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { contextSrv } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
import { AccessControlAction } from 'app/types';
import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
import * as analytics from '../../Analytics';
import { useHasRuler } from '../../hooks/useHasRuler';
import { mockExportApi, mockFolderApi, setupMswServer } from '../../mockApi';
import { grantUserPermissions, mockCombinedRule, mockDataSource, mockFolder, mockGrafanaRulerRule } from '../../mocks';
import { RulesGroup } from './RulesGroup';
jest.mock('../../hooks/useHasRuler');
jest.spyOn(analytics, 'logInfo');
jest.mock('react-virtualized-auto-sizer', () => {
return ({ children }: AutoSizerProps) => children({ height: 600, width: 1 });
});
jest.mock('@grafana/ui', () => ({
...jest.requireActual('@grafana/ui'),
CodeEditor: ({ value }: { value: string }) => <textarea data-testid="code-editor" value={value} readOnly />,
}));
const mocks = {
useHasRuler: jest.mocked(useHasRuler),
};
function mockUseHasRuler(hasRuler: boolean, rulerRulesLoaded: boolean) {
mocks.useHasRuler.mockReturnValue({
hasRuler: () => hasRuler,
rulerRulesLoaded: () => rulerRulesLoaded,
});
}
beforeEach(() => {
mocks.useHasRuler.mockReset();
// FIXME: scope down
grantUserPermissions(Object.values(AccessControlAction));
});
const ui = {
editGroupButton: byTestId('edit-group'),
deleteGroupButton: byTestId('delete-group'),
exportGroupButton: byRole('button', { name: 'Export rule group' }),
confirmDeleteModal: {
header: byText('Delete group'),
confirmButton: byText('Delete'),
},
moreActionsButton: byRole('button', { name: 'More' }),
export: {
dialog: byRole('dialog', { name: /Drawer title Export .* rules/ }),
jsonTab: byRole('tab', { name: /JSON/ }),
yamlTab: byRole('tab', { name: /YAML/ }),
editor: byTestId('code-editor'),
copyCodeButton: byRole('button', { name: 'Copy code' }),
downloadButton: byRole('button', { name: 'Download' }),
},
loadingSpinner: byTestId('spinner'),
};
const server = setupMswServer();
afterEach(() => {
server.resetHandlers();
});
describe('Rules group tests', () => {
const store = configureStore();
function renderRulesGroup(namespace: CombinedRuleNamespace, group: CombinedRuleGroup) {
return render(
<Provider store={store}>
<RulesGroup group={group} namespace={namespace} expandAll={false} viewMode={'grouped'} />
</Provider>
);
}
describe('Grafana rules', () => {
const group: CombinedRuleGroup = {
name: 'TestGroup',
rules: [
mockCombinedRule({
rulerRule: mockGrafanaRulerRule({
namespace_uid: 'cpu-usage',
}),
}),
],
totals: {},
};
const namespace: CombinedRuleNamespace = {
name: 'TestNamespace',
rulesSource: 'grafana',
groups: [group],
};
it('Should hide delete and edit group buttons', () => {
// Act
mockUseHasRuler(true, true);
renderRulesGroup(namespace, group);
// Assert
expect(ui.deleteGroupButton.query()).not.toBeInTheDocument();
expect(ui.editGroupButton.query()).not.toBeInTheDocument();
});
it('Should allow exporting rules group', async () => {
// Arrange
mockUseHasRuler(true, true);
mockFolderApi(server).folder('cpu-usage', mockFolder({ uid: 'cpu-usage' }));
mockExportApi(server).exportRulesGroup('cpu-usage', 'TestGroup', {
yaml: 'Yaml Export Content',
json: 'Json Export Content',
});
const user = userEvent.setup();
// Act
renderRulesGroup(namespace, group);
await user.click(await ui.exportGroupButton.find());
// Assert
const drawer = await ui.export.dialog.find();
expect(ui.export.yamlTab.get(drawer)).toHaveAttribute('aria-selected', 'true');
await waitFor(() => {
expect(ui.export.editor.get(drawer)).toHaveTextContent('Yaml Export Content');
});
await user.click(ui.export.jsonTab.get(drawer));
await waitFor(() => {
expect(ui.export.editor.get(drawer)).toHaveTextContent('Json Export Content');
});
expect(ui.export.copyCodeButton.get(drawer)).toBeInTheDocument();
expect(ui.export.downloadButton.get(drawer)).toBeInTheDocument();
});
});
describe('Cloud rules', () => {
beforeEach(() => {
contextSrv.isEditor = true;
});
const group: CombinedRuleGroup = {
name: 'TestGroup',
rules: [mockCombinedRule()],
totals: {},
};
const namespace: CombinedRuleNamespace = {
name: 'TestNamespace',
rulesSource: mockDataSource(),
groups: [group],
};
it('When ruler enabled should display delete and edit group buttons', () => {
// Arrange
mockUseHasRuler(true, true);
// Act
renderRulesGroup(namespace, group);
// Assert
expect(mocks.useHasRuler).toHaveBeenCalled();
expect(ui.deleteGroupButton.get()).toBeInTheDocument();
expect(ui.editGroupButton.get()).toBeInTheDocument();
});
it('When ruler disabled should hide delete and edit group buttons', () => {
// Arrange
mockUseHasRuler(false, false);
// Act
renderRulesGroup(namespace, group);
// Assert
expect(mocks.useHasRuler).toHaveBeenCalled();
expect(ui.deleteGroupButton.query()).not.toBeInTheDocument();
expect(ui.editGroupButton.query()).not.toBeInTheDocument();
});
it('Delete button click should display confirmation modal', async () => {
// Arrange
mockUseHasRuler(true, true);
// Act
renderRulesGroup(namespace, group);
await userEvent.click(ui.deleteGroupButton.get());
// Assert
expect(ui.confirmDeleteModal.header.get()).toBeInTheDocument();
expect(ui.confirmDeleteModal.confirmButton.get()).toBeInTheDocument();
});
});
describe('Analytics', () => {
beforeEach(() => {
contextSrv.isEditor = true;
});
const group: CombinedRuleGroup = {
name: 'TestGroup',
rules: [mockCombinedRule()],
totals: {},
};
const namespace: CombinedRuleNamespace = {
name: 'TestNamespace',
rulesSource: mockDataSource(),
groups: [group],
};
it('Should log info when closing the edit group rule modal without saving', async () => {
mockUseHasRuler(true, true);
renderRulesGroup(namespace, group);
await userEvent.click(ui.editGroupButton.get());
expect(screen.getByText('Cancel')).toBeInTheDocument();
await userEvent.click(screen.getByText('Cancel'));
expect(screen.queryByText('Cancel')).not.toBeInTheDocument();
expect(analytics.logInfo).toHaveBeenCalledWith(analytics.LogMessages.leavingRuleGroupEdit);
});
});
});