import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { times } from 'lodash';
import React from 'react';
import { byLabelText, byRole, byTestId } from 'testing-library-selector';
import { PluginExtensionTypes } from '@grafana/data';
import { getPluginLinkExtensions } from '@grafana/runtime';
import { CombinedRuleNamespace } from '../../../../../types/unified-alerting';
import { GrafanaAlertState, PromAlertingRuleState } from '../../../../../types/unified-alerting-dto';
import { mockCombinedRule, mockDataSource, mockPromAlert, mockPromAlertingRule } from '../../mocks';
import { alertStateToReadable } from '../../utils/rules';
import { RuleDetailsMatchingInstances } from './RuleDetailsMatchingInstances';
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getPluginLinkExtensions: jest.fn(),
}));
const mocks = {
getPluginLinkExtensionsMock: jest.mocked(getPluginLinkExtensions),
};
const ui = {
stateFilter: byTestId('alert-instance-state-filter'),
stateButton: byRole('radio'),
grafanaStateButton: {
normal: byLabelText(/^Normal/),
alerting: byLabelText(/^Alerting/),
pending: byLabelText(/^Pending/),
noData: byLabelText(/^NoData/),
error: byLabelText(/^Error/),
},
cloudStateButton: {
firing: byLabelText(/^Firing/),
pending: byLabelText(/^Pending/),
},
instanceRow: byTestId('row'),
showAllInstances: byTestId('show-all'),
};
describe('RuleDetailsMatchingInstances', () => {
beforeEach(() => {
mocks.getPluginLinkExtensionsMock.mockReturnValue({
extensions: [
{
pluginId: 'grafana-ml-app',
id: '1',
type: PluginExtensionTypes.link,
title: 'Run investigation',
category: 'Sift',
description: 'Run a Sift investigation for this alert',
onClick: jest.fn(),
},
],
});
});
describe('Filtering', () => {
it('For Grafana Managed rules instances filter should contain five states', () => {
const rule = mockCombinedRule();
render();
const stateFilter = ui.stateFilter.get();
expect(stateFilter).toBeInTheDocument();
const stateButtons = ui.stateButton.getAll(stateFilter);
expect(stateButtons).toHaveLength(5);
expect(ui.grafanaStateButton.normal.get(stateFilter)).toBeInTheDocument();
expect(ui.grafanaStateButton.alerting.get(stateFilter)).toBeInTheDocument();
expect(ui.grafanaStateButton.pending.get(stateFilter)).toBeInTheDocument();
expect(ui.grafanaStateButton.noData.get(stateFilter)).toBeInTheDocument();
expect(ui.grafanaStateButton.error.get(stateFilter)).toBeInTheDocument();
});
it.each(Object.values(GrafanaAlertState))('Should filter grafana rules by %s state', async (state) => {
const rule = mockCombinedRule({
promRule: mockPromAlertingRule({
alerts: [
mockPromAlert({ state: GrafanaAlertState.Normal }),
mockPromAlert({ state: GrafanaAlertState.Alerting }),
mockPromAlert({ state: GrafanaAlertState.Pending }),
mockPromAlert({ state: GrafanaAlertState.NoData }),
mockPromAlert({ state: GrafanaAlertState.Error }),
],
}),
});
const buttons = {
[GrafanaAlertState.Normal]: ui.grafanaStateButton.normal,
[GrafanaAlertState.Alerting]: ui.grafanaStateButton.alerting,
[GrafanaAlertState.Pending]: ui.grafanaStateButton.pending,
[GrafanaAlertState.NoData]: ui.grafanaStateButton.noData,
[GrafanaAlertState.Error]: ui.grafanaStateButton.error,
};
render();
await userEvent.click(buttons[state].get());
expect(ui.instanceRow.getAll()).toHaveLength(1);
expect(ui.instanceRow.get()).toHaveTextContent(alertStateToReadable(state));
});
it('For Cloud rules instances filter should contain two states', () => {
const rule = mockCombinedRule({
namespace: mockPromNamespace(),
});
render();
const stateFilter = ui.stateFilter.get();
expect(stateFilter).toBeInTheDocument();
const stateButtons = ui.stateButton.getAll(stateFilter);
expect(stateButtons).toHaveLength(2);
expect(ui.cloudStateButton.firing.get(stateFilter)).toBeInTheDocument();
expect(ui.cloudStateButton.pending.get(stateFilter)).toBeInTheDocument();
});
it.each([PromAlertingRuleState.Pending, PromAlertingRuleState.Firing] as const)(
'Should filter cloud rules by %s state',
async (state) => {
const rule = mockCombinedRule({
namespace: mockPromNamespace(),
promRule: mockPromAlertingRule({
alerts: [
mockPromAlert({ state: PromAlertingRuleState.Firing }),
mockPromAlert({ state: PromAlertingRuleState.Pending }),
],
}),
});
render();
await userEvent.click(ui.cloudStateButton[state].get());
expect(ui.instanceRow.getAll()).toHaveLength(1);
expect(ui.instanceRow.get()).toHaveTextContent(alertStateToReadable(state));
}
);
it('should correctly filter instances', async () => {
const event = userEvent.setup();
const rule = mockCombinedRule({
promRule: mockPromAlertingRule({
alerts: times(100, () => mockPromAlert({ state: GrafanaAlertState.Normal })),
}),
instanceTotals: {
inactive: 100,
},
});
render();
// should show all instances by default
expect(ui.showAllInstances.query()).not.toBeInTheDocument();
// filter by "error" state, should have no instances in that state
await event.click(ui.grafanaStateButton.error.get());
// click "show all" instances
await event.click(ui.showAllInstances.get());
expect(ui.showAllInstances.query()).not.toBeInTheDocument();
});
});
});
function mockPromNamespace(): CombinedRuleNamespace {
return {
rulesSource: mockDataSource(),
groups: [{ name: 'Prom rules group', rules: [], totals: {} }],
name: 'Prometheus-test',
};
}