mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* 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>
215 lines
7.3 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|