grafana/public/app/features/alerting/unified/AlertGroups.test.tsx
Sonia Aguilar 68564b1940
Alerting: Gops labels integration (#85467)
* Show list of labels on the alert rule form in a more visual way, and use a modal for managing them

* fix test

* Fix more tests

* Show orange badge when no labels are selected

* Remove unused datasource property in LabelsField

* Remove unused div and add comment

* Use button instead of icon for editing labels

* Use subform for labels

* Move logic fetching labels from different places in a separate hook

* Fix tests

* remove unused getLabelInput const in test

* Add ellispis and tooltip for long labels and move labels list in modal to the bottom

* Use text instead of badge when no using labels

* Fix tests after adding ellipsis and tooltip to the labels

* simplify styles

* Fix fetching values from gops when new key is used

* Address pr review comments

* Address pr review comments part2

* Fix tag on rtkq

* Remove color for no labels selected text

* Disable already used keys in the labels sub form

* Fix typo

* use the UseFieldArrayRemove type from react-hook-form

* Update some styles and text in the labels modal

* Address some review comments (nits)

* Address review comments part1

* Move logic getting labels in useCombinedLabels hook

---------

Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2024-04-23 14:38:31 +02:00

215 lines
7.3 KiB
TypeScript

import { render, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { selectors } from '@grafana/e2e-selectors';
import { setDataSourceSrv } from '@grafana/runtime';
import { AccessControlAction } from 'app/types';
import AlertGroups from './AlertGroups';
import { fetchAlertGroups } from './api/alertmanager';
import {
grantUserPermissions,
mockAlertGroup,
mockAlertmanagerAlert,
mockDataSource,
MockDataSourceSrv,
} from './mocks';
import { AlertmanagerProvider } from './state/AlertmanagerContext';
import { DataSourceType } from './utils/datasource';
jest.mock('./api/alertmanager');
const mocks = {
api: {
fetchAlertGroups: jest.mocked(fetchAlertGroups),
},
};
const renderAmNotifications = () => {
return render(
<TestProvider>
<AlertmanagerProvider accessType={'instance'}>
<AlertGroups />
</AlertmanagerProvider>
</TestProvider>
);
};
const dataSources = {
am: mockDataSource({
name: 'Alertmanager',
type: DataSourceType.Alertmanager,
}),
};
const ui = {
group: byTestId('alert-group'),
groupCollapseToggle: byTestId('alert-group-collapse-toggle'),
groupTable: byTestId('alert-group-table'),
row: byTestId('row'),
collapseToggle: byTestId(selectors.components.AlertRules.toggle),
silenceButton: byText('Silence'),
sourceButton: byText('See source'),
matcherInput: byTestId('search-query-input'),
groupByContainer: byTestId('group-by-container'),
groupByInput: byRole('combobox', { name: /group by label keys/i }),
clearButton: byRole('button', { name: 'Clear filters' }),
loadingIndicator: byText('Loading notifications'),
};
describe('AlertGroups', () => {
beforeAll(() => {
grantUserPermissions([
AccessControlAction.AlertingInstanceRead,
AccessControlAction.AlertingInstanceCreate,
AccessControlAction.AlertingInstancesExternalRead,
AccessControlAction.AlertingRuleRead,
]);
});
beforeEach(() => {
setDataSourceSrv(new MockDataSourceSrv(dataSources));
});
afterEach(() => {
mocks.api.fetchAlertGroups.mockClear();
});
it('loads and shows groups', async () => {
mocks.api.fetchAlertGroups.mockImplementation(() => {
return Promise.resolve([
mockAlertGroup({ labels: {}, alerts: [mockAlertmanagerAlert({ labels: { foo: 'bar' } })] }),
mockAlertGroup(),
]);
});
renderAmNotifications();
await waitFor(() => expect(mocks.api.fetchAlertGroups).toHaveBeenCalled());
const groups = await ui.group.findAll();
expect(groups).toHaveLength(2);
expect(groups[0]).toHaveTextContent('No grouping');
const labels = byTestId('label-value').getAll();
expect(labels[0]).toHaveTextContent('severitywarning');
expect(labels[1]).toHaveTextContent('regionUS-Central');
await userEvent.click(ui.groupCollapseToggle.get(groups[0]));
expect(ui.groupTable.get()).toBeDefined();
await userEvent.click(ui.collapseToggle.get(ui.groupTable.get()));
expect(ui.silenceButton.get(ui.groupTable.get())).toBeDefined();
expect(ui.sourceButton.get(ui.groupTable.get())).toBeDefined();
});
it('should group by custom grouping', async () => {
const regions = ['NASA', 'EMEA', 'APAC'];
mocks.api.fetchAlertGroups.mockImplementation(() => {
const groups = regions.map((region) =>
mockAlertGroup({
labels: { region },
alerts: [
mockAlertmanagerAlert({ fingerprint: '1', labels: { region, appName: 'billing', env: 'production' } }),
mockAlertmanagerAlert({
fingerprint: '2',
labels: { region, appName: 'auth', env: 'staging', uniqueLabel: 'true' },
}),
mockAlertmanagerAlert({ fingerprint: '3', labels: { region, appName: 'frontend', env: 'production' } }),
],
})
);
return Promise.resolve(groups);
});
renderAmNotifications();
await waitFor(() => expect(mocks.api.fetchAlertGroups).toHaveBeenCalled());
let groups = await ui.group.findAll();
const groupByInput = ui.groupByInput.get();
const groupByWrapper = ui.groupByContainer.get();
expect(groups).toHaveLength(3);
expect(groups[0]).toHaveTextContent('regionNASA');
expect(groups[1]).toHaveTextContent('regionEMEA');
expect(groups[2]).toHaveTextContent('regionAPAC');
await userEvent.type(groupByInput, 'appName{enter}');
await waitFor(() => expect(groupByWrapper).toHaveTextContent('appName'));
groups = await ui.group.findAll();
await waitFor(() => expect(ui.clearButton.get()).toBeInTheDocument());
expect(groups).toHaveLength(3);
expect(groups[0]).toHaveTextContent('appNamebilling');
expect(groups[1]).toHaveTextContent('appNameauth');
expect(groups[2]).toHaveTextContent('appNamefrontend');
await userEvent.click(ui.clearButton.get());
await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('appName'));
await userEvent.type(groupByInput, 'env{enter}');
await waitFor(() => expect(groupByWrapper).toHaveTextContent('env'));
groups = await ui.group.findAll();
expect(groups).toHaveLength(2);
expect(groups[0]).toHaveTextContent('envproduction');
expect(groups[1]).toHaveTextContent('envstaging');
await userEvent.click(ui.clearButton.get());
await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('env'));
await userEvent.type(groupByInput, 'uniqueLabel{enter}');
await waitFor(() => expect(groupByWrapper).toHaveTextContent('uniqueLabel'));
groups = await ui.group.findAll();
expect(groups).toHaveLength(2);
expect(groups[0]).toHaveTextContent('No grouping');
expect(groups[1]).toHaveTextContent('uniqueLabeltrue');
});
it('should split custom grouping groups with the same label by receiver', async () => {
// The same alert is repeated in two groups with different receivers
const alert = mockAlertmanagerAlert({
fingerprint: '1',
labels: { region: 'NASA', appName: 'billing' },
receivers: [{ name: 'slack' }, { name: 'email' }],
});
const amGroups = [
mockAlertGroup({ receiver: { name: 'slack' }, labels: { region: 'NASA' }, alerts: [alert] }),
mockAlertGroup({ receiver: { name: 'email' }, labels: { region: 'NASA' }, alerts: [alert] }),
];
mocks.api.fetchAlertGroups.mockResolvedValue(amGroups);
const user = userEvent.setup();
renderAmNotifications();
await waitForElementToBeRemoved(ui.loadingIndicator.query());
await user.type(ui.groupByInput.get(), 'appName{enter}');
const groups = await ui.group.findAll();
expect(groups).toHaveLength(2);
expect(groups[0]).toHaveTextContent('appNamebillingDelivered to slack');
expect(groups[1]).toHaveTextContent('appNamebillingDelivered to email');
});
it('should combine multiple ungrouped groups', async () => {
mocks.api.fetchAlertGroups.mockImplementation(() => {
const groups = [
mockAlertGroup({ labels: {} }),
mockAlertGroup({ labels: {}, alerts: [mockAlertmanagerAlert({ labels: { foo: 'bar' } })] }),
];
return Promise.resolve(groups);
});
renderAmNotifications();
await waitFor(() => {
expect(ui.group.getAll()).toHaveLength(1);
});
});
});