mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactor Silences test
This commit is contained in:
parent
10fcd541e3
commit
f397830098
@ -1,49 +1,35 @@
|
|||||||
import { render, waitFor } from '@testing-library/react';
|
|
||||||
import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TestProvider } from 'test/helpers/TestProvider';
|
import { render, waitFor, userEvent } from 'test/test-utils';
|
||||||
import { byLabelText, byPlaceholderText, byRole, byTestId, byText } from 'testing-library-selector';
|
import { byLabelText, byPlaceholderText, byRole, byTestId, byText } from 'testing-library-selector';
|
||||||
|
|
||||||
import { dateTime } from '@grafana/data';
|
import { dateTime } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { config, locationService, setDataSourceSrv } from '@grafana/runtime';
|
import { config, locationService, setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||||
import { AlertState, MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
|
import { MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { AccessControlAction } from 'app/types';
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
import { SilenceState } from '../../../plugins/datasource/alertmanager/types';
|
|
||||||
|
|
||||||
import Silences from './Silences';
|
import Silences from './Silences';
|
||||||
import { createOrUpdateSilence, fetchAlerts, fetchSilences } from './api/alertmanager';
|
import { grantUserPermissions, mockDataSource, MockDataSourceSrv } from './mocks';
|
||||||
import { grantUserPermissions, mockAlertmanagerAlert, mockDataSource, MockDataSourceSrv, mockSilence } from './mocks';
|
|
||||||
import { AlertmanagerProvider } from './state/AlertmanagerContext';
|
import { AlertmanagerProvider } from './state/AlertmanagerContext';
|
||||||
import { setupDataSources } from './testSetup/datasources';
|
import { setupDataSources } from './testSetup/datasources';
|
||||||
import { parseMatchers } from './utils/alertmanager';
|
|
||||||
import { DataSourceType } from './utils/datasource';
|
import { DataSourceType } from './utils/datasource';
|
||||||
|
|
||||||
jest.mock('./api/alertmanager');
|
|
||||||
jest.mock('app/core/services/context_srv');
|
jest.mock('app/core/services/context_srv');
|
||||||
|
|
||||||
const TEST_TIMEOUT = 60000;
|
const TEST_TIMEOUT = 60000;
|
||||||
|
|
||||||
const mocks = {
|
|
||||||
api: {
|
|
||||||
fetchSilences: jest.mocked(fetchSilences),
|
|
||||||
fetchAlerts: jest.mocked(fetchAlerts),
|
|
||||||
createOrUpdateSilence: jest.mocked(createOrUpdateSilence),
|
|
||||||
},
|
|
||||||
contextSrv: jest.mocked(contextSrv),
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderSilences = (location = '/alerting/silences/') => {
|
const renderSilences = (location = '/alerting/silences/') => {
|
||||||
locationService.push(location);
|
locationService.push(location);
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
<TestProvider>
|
|
||||||
<AlertmanagerProvider accessType="instance">
|
<AlertmanagerProvider accessType="instance">
|
||||||
<Silences />
|
<Silences />
|
||||||
</AlertmanagerProvider>
|
</AlertmanagerProvider>,
|
||||||
</TestProvider>
|
{
|
||||||
|
routerOptions: {
|
||||||
|
initialEntries: [location],
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,7 +43,8 @@ const dataSources = {
|
|||||||
const ui = {
|
const ui = {
|
||||||
notExpiredTable: byTestId('not-expired-table'),
|
notExpiredTable: byTestId('not-expired-table'),
|
||||||
expiredTable: byTestId('expired-table'),
|
expiredTable: byTestId('expired-table'),
|
||||||
expiredCaret: byText(/expired/i),
|
expiredCaret: byText(/expired silences \(/i),
|
||||||
|
silencesTags: byLabelText(/tags/i),
|
||||||
silenceRow: byTestId('row'),
|
silenceRow: byTestId('row'),
|
||||||
silencedAlertCell: byTestId('alerts'),
|
silencedAlertCell: byTestId('alerts'),
|
||||||
addSilenceButton: byRole('link', { name: /add silence/i }),
|
addSilenceButton: byRole('link', { name: /add silence/i }),
|
||||||
@ -80,28 +67,6 @@ const ui = {
|
|||||||
|
|
||||||
const resetMocks = () => {
|
const resetMocks = () => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
mocks.api.fetchSilences.mockImplementation(() => {
|
|
||||||
return Promise.resolve([
|
|
||||||
mockSilence({ id: '12345' }),
|
|
||||||
mockSilence({ id: '67890', matchers: parseMatchers('foo!=bar'), comment: 'Catch all' }),
|
|
||||||
mockSilence({ id: '1111', status: { state: SilenceState.Expired } }),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
mocks.api.fetchAlerts.mockImplementation(() => {
|
|
||||||
return Promise.resolve([
|
|
||||||
mockAlertmanagerAlert({
|
|
||||||
labels: { foo: 'bar' },
|
|
||||||
status: { state: AlertState.Suppressed, silencedBy: ['12345'], inhibitedBy: [] },
|
|
||||||
}),
|
|
||||||
mockAlertmanagerAlert({
|
|
||||||
labels: { foo: 'buzz' },
|
|
||||||
status: { state: AlertState.Suppressed, silencedBy: ['67890'], inhibitedBy: [] },
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
mocks.api.createOrUpdateSilence.mockResolvedValue(mockSilence());
|
|
||||||
|
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
AccessControlAction.AlertingInstanceRead,
|
AccessControlAction.AlertingInstanceRead,
|
||||||
@ -117,6 +82,21 @@ const setUserLogged = (isLogged: boolean) => {
|
|||||||
config.bootData.user.name = isLogged ? 'admin' : '';
|
config.bootData.user.name = isLogged ? 'admin' : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enterSilenceLabel = async (index: number, name: string, matcher: MatcherOperator, value: string) => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
await user.type(ui.editor.matcherName.getAll()[index], name);
|
||||||
|
await user.type(ui.editor.matcherOperatorSelect.getAll()[index], matcher);
|
||||||
|
await user.tab();
|
||||||
|
await user.type(ui.editor.matcherValue.getAll()[index], value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAdditionalMatcher = async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
await user.click(ui.editor.addMatcherButton.get());
|
||||||
|
};
|
||||||
|
|
||||||
|
setupMswServer();
|
||||||
|
|
||||||
describe('Silences', () => {
|
describe('Silences', () => {
|
||||||
beforeAll(resetMocks);
|
beforeAll(resetMocks);
|
||||||
afterEach(resetMocks);
|
afterEach(resetMocks);
|
||||||
@ -128,26 +108,29 @@ describe('Silences', () => {
|
|||||||
it(
|
it(
|
||||||
'loads and shows silences',
|
'loads and shows silences',
|
||||||
async () => {
|
async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
renderSilences();
|
renderSilences();
|
||||||
await waitFor(() => expect(mocks.api.fetchSilences).toHaveBeenCalled());
|
|
||||||
await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled());
|
|
||||||
|
|
||||||
await userEvent.click(ui.expiredCaret.get());
|
expect(await ui.notExpiredTable.find()).toBeInTheDocument();
|
||||||
expect(ui.notExpiredTable.get()).not.toBeNull();
|
|
||||||
expect(ui.expiredTable.get()).not.toBeNull();
|
|
||||||
let silences = ui.silenceRow.queryAll();
|
|
||||||
expect(silences).toHaveLength(3);
|
|
||||||
expect(silences[0]).toHaveTextContent('foo=bar');
|
|
||||||
expect(silences[1]).toHaveTextContent('foo!=bar');
|
|
||||||
expect(silences[2]).toHaveTextContent('foo=bar');
|
|
||||||
|
|
||||||
await userEvent.click(ui.expiredCaret.getAll()[0]);
|
await user.click(ui.expiredCaret.get());
|
||||||
expect(ui.notExpiredTable.get()).not.toBeNull();
|
expect(ui.expiredTable.get()).toBeInTheDocument();
|
||||||
expect(ui.expiredTable.query()).toBeNull();
|
|
||||||
silences = ui.silenceRow.queryAll();
|
const allSilences = ui.silenceRow.queryAll();
|
||||||
expect(silences).toHaveLength(2);
|
expect(allSilences).toHaveLength(3);
|
||||||
expect(silences[0]).toHaveTextContent('foo=bar');
|
expect(allSilences[0]).toHaveTextContent('foo=bar');
|
||||||
expect(silences[1]).toHaveTextContent('foo!=bar');
|
expect(allSilences[1]).toHaveTextContent('foo!=bar');
|
||||||
|
expect(allSilences[2]).toHaveTextContent('foo=bar');
|
||||||
|
|
||||||
|
await user.click(ui.expiredCaret.get());
|
||||||
|
|
||||||
|
expect(ui.notExpiredTable.get()).toBeInTheDocument();
|
||||||
|
expect(ui.expiredTable.query()).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const activeSilences = ui.silenceRow.queryAll();
|
||||||
|
expect(activeSilences).toHaveLength(2);
|
||||||
|
expect(activeSilences[0]).toHaveTextContent('foo=bar');
|
||||||
|
expect(activeSilences[1]).toHaveTextContent('foo!=bar');
|
||||||
},
|
},
|
||||||
TEST_TIMEOUT
|
TEST_TIMEOUT
|
||||||
);
|
);
|
||||||
@ -155,25 +138,13 @@ describe('Silences', () => {
|
|||||||
it(
|
it(
|
||||||
'shows the correct number of silenced alerts',
|
'shows the correct number of silenced alerts',
|
||||||
async () => {
|
async () => {
|
||||||
mocks.api.fetchAlerts.mockImplementation(() => {
|
|
||||||
return Promise.resolve([
|
|
||||||
mockAlertmanagerAlert({
|
|
||||||
labels: { foo: 'bar', buzz: 'bazz' },
|
|
||||||
status: { state: AlertState.Suppressed, silencedBy: ['12345'], inhibitedBy: [] },
|
|
||||||
}),
|
|
||||||
mockAlertmanagerAlert({
|
|
||||||
labels: { foo: 'bar', buzz: 'bazz' },
|
|
||||||
status: { state: AlertState.Suppressed, silencedBy: ['12345'], inhibitedBy: [] },
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
renderSilences();
|
renderSilences();
|
||||||
await waitFor(() => expect(mocks.api.fetchSilences).toHaveBeenCalled());
|
|
||||||
await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled());
|
|
||||||
|
|
||||||
const silencedAlertRows = ui.silencedAlertCell.getAll(ui.notExpiredTable.get());
|
const notExpiredTable = await ui.notExpiredTable.find();
|
||||||
expect(silencedAlertRows).toHaveLength(2);
|
|
||||||
|
expect(notExpiredTable).toBeInTheDocument();
|
||||||
|
|
||||||
|
const silencedAlertRows = await ui.silencedAlertCell.findAll(notExpiredTable);
|
||||||
expect(silencedAlertRows[0]).toHaveTextContent('2');
|
expect(silencedAlertRows[0]).toHaveTextContent('2');
|
||||||
expect(silencedAlertRows[1]).toHaveTextContent('0');
|
expect(silencedAlertRows[1]).toHaveTextContent('0');
|
||||||
},
|
},
|
||||||
@ -184,12 +155,9 @@ describe('Silences', () => {
|
|||||||
'filters silences by matchers',
|
'filters silences by matchers',
|
||||||
async () => {
|
async () => {
|
||||||
renderSilences();
|
renderSilences();
|
||||||
await waitFor(() => expect(mocks.api.fetchSilences).toHaveBeenCalled());
|
|
||||||
await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled());
|
|
||||||
|
|
||||||
const queryBar = ui.queryBar.get();
|
const queryBar = await ui.queryBar.find();
|
||||||
await userEvent.click(queryBar);
|
await userEvent.type(queryBar, 'foo=bar');
|
||||||
await userEvent.paste('foo=bar');
|
|
||||||
|
|
||||||
await waitFor(() => expect(ui.silenceRow.getAll()).toHaveLength(2));
|
await waitFor(() => expect(ui.silenceRow.getAll()).toHaveLength(2));
|
||||||
},
|
},
|
||||||
@ -199,24 +167,23 @@ describe('Silences', () => {
|
|||||||
it('shows creating a silence button for users with access', async () => {
|
it('shows creating a silence button for users with access', async () => {
|
||||||
renderSilences();
|
renderSilences();
|
||||||
|
|
||||||
await waitFor(() => expect(mocks.api.fetchSilences).toHaveBeenCalled());
|
expect(await ui.addSilenceButton.find()).toBeInTheDocument();
|
||||||
await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled());
|
|
||||||
|
|
||||||
expect(ui.addSilenceButton.get()).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hides actions for creating a silence for users without access', async () => {
|
it('hides actions for creating a silence for users without access', async () => {
|
||||||
grantUserPermissions([AccessControlAction.AlertingInstanceRead, AccessControlAction.AlertingInstancesExternalRead]);
|
grantUserPermissions([AccessControlAction.AlertingInstanceRead, AccessControlAction.AlertingInstancesExternalRead]);
|
||||||
|
|
||||||
renderSilences();
|
renderSilences();
|
||||||
await waitFor(() => expect(mocks.api.fetchSilences).toHaveBeenCalled());
|
|
||||||
await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled());
|
const notExpiredTable = await ui.notExpiredTable.find();
|
||||||
|
|
||||||
|
expect(notExpiredTable).toBeInTheDocument();
|
||||||
|
|
||||||
expect(ui.addSilenceButton.query()).not.toBeInTheDocument();
|
expect(ui.addSilenceButton.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Silence edit', () => {
|
describe('Silence create/edit', () => {
|
||||||
const baseUrlPath = '/alerting/silence/new';
|
const baseUrlPath = '/alerting/silence/new';
|
||||||
beforeAll(resetMocks);
|
beforeAll(resetMocks);
|
||||||
afterEach(resetMocks);
|
afterEach(resetMocks);
|
||||||
@ -242,7 +209,7 @@ describe('Silence edit', () => {
|
|||||||
const matchersQueryString = matchersParams.map((matcher) => `matcher=${encodeURIComponent(matcher)}`).join('&');
|
const matchersQueryString = matchersParams.map((matcher) => `matcher=${encodeURIComponent(matcher)}`).join('&');
|
||||||
|
|
||||||
renderSilences(`${baseUrlPath}?${matchersQueryString}`);
|
renderSilences(`${baseUrlPath}?${matchersQueryString}`);
|
||||||
await waitFor(() => expect(ui.editor.durationField.query()).not.toBeNull());
|
expect(await ui.editor.durationField.find()).toBeInTheDocument();
|
||||||
|
|
||||||
const matchers = ui.editor.matchersField.queryAll();
|
const matchers = ui.editor.matchersField.queryAll();
|
||||||
expect(matchers).toHaveLength(4);
|
expect(matchers).toHaveLength(4);
|
||||||
@ -270,7 +237,7 @@ describe('Silence edit', () => {
|
|||||||
'creates a new silence',
|
'creates a new silence',
|
||||||
async () => {
|
async () => {
|
||||||
renderSilences(baseUrlPath);
|
renderSilences(baseUrlPath);
|
||||||
await waitFor(() => expect(ui.editor.durationField.query()).not.toBeNull());
|
expect(await ui.editor.durationField.find()).toBeInTheDocument();
|
||||||
|
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
const end = new Date(start.getTime() + 24 * 60 * 60 * 1000);
|
const end = new Date(start.getTime() + 24 * 60 * 60 * 1000);
|
||||||
@ -285,48 +252,20 @@ describe('Silence edit', () => {
|
|||||||
await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(startDateString));
|
await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(startDateString));
|
||||||
await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(endDateString));
|
await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(endDateString));
|
||||||
|
|
||||||
await userEvent.type(ui.editor.matcherName.get(), 'foo');
|
await enterSilenceLabel(0, 'foo', MatcherOperator.equal, 'bar');
|
||||||
await userEvent.type(ui.editor.matcherOperatorSelect.get(), '=');
|
|
||||||
await userEvent.tab();
|
|
||||||
await userEvent.type(ui.editor.matcherValue.get(), 'bar');
|
|
||||||
|
|
||||||
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
|
await addAdditionalMatcher();
|
||||||
await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never });
|
await enterSilenceLabel(1, 'bar', MatcherOperator.notEqual, 'buzz');
|
||||||
await userEvent.type(ui.editor.matcherName.getAll()[1], 'bar');
|
|
||||||
await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[1], '!=');
|
|
||||||
await userEvent.tab();
|
|
||||||
await userEvent.type(ui.editor.matcherValue.getAll()[1], 'buzz');
|
|
||||||
|
|
||||||
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
|
await addAdditionalMatcher();
|
||||||
await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never });
|
await enterSilenceLabel(2, 'region', MatcherOperator.regex, 'us-west-.*');
|
||||||
await userEvent.type(ui.editor.matcherName.getAll()[2], 'region');
|
|
||||||
await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[2], '=~');
|
|
||||||
await userEvent.tab();
|
|
||||||
await userEvent.type(ui.editor.matcherValue.getAll()[2], 'us-west-.*');
|
|
||||||
|
|
||||||
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
|
await addAdditionalMatcher();
|
||||||
await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never });
|
await enterSilenceLabel(3, 'env', MatcherOperator.notRegex, 'dev|staging');
|
||||||
await userEvent.type(ui.editor.matcherName.getAll()[3], 'env');
|
|
||||||
await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[3], '!~');
|
|
||||||
await userEvent.tab();
|
|
||||||
await userEvent.type(ui.editor.matcherValue.getAll()[3], 'dev|staging');
|
|
||||||
|
|
||||||
await userEvent.click(ui.editor.submit.get());
|
await userEvent.click(ui.editor.submit.get());
|
||||||
|
|
||||||
await waitFor(() =>
|
expect(await ui.notExpiredTable.find()).toBeInTheDocument();
|
||||||
expect(mocks.api.createOrUpdateSilence).toHaveBeenCalledWith(
|
|
||||||
'grafana',
|
|
||||||
expect.objectContaining({
|
|
||||||
comment: expect.stringMatching(/created (\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/),
|
|
||||||
matchers: [
|
|
||||||
{ isEqual: true, isRegex: false, name: 'foo', value: 'bar' },
|
|
||||||
{ isEqual: false, isRegex: false, name: 'bar', value: 'buzz' },
|
|
||||||
{ isEqual: true, isRegex: true, name: 'region', value: 'us-west-.*' },
|
|
||||||
{ isEqual: false, isRegex: true, name: 'env', value: 'dev|staging' },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
TEST_TIMEOUT
|
TEST_TIMEOUT
|
||||||
);
|
);
|
||||||
@ -339,20 +278,11 @@ describe('Silence edit', () => {
|
|||||||
renderSilences(`${baseUrlPath}?alertmanager=Alertmanager`);
|
renderSilences(`${baseUrlPath}?alertmanager=Alertmanager`);
|
||||||
await waitFor(() => expect(ui.editor.durationField.query()).not.toBeNull());
|
await waitFor(() => expect(ui.editor.durationField.query()).not.toBeNull());
|
||||||
|
|
||||||
await user.type(ui.editor.matcherName.getAll()[0], 'foo');
|
await enterSilenceLabel(0, 'foo', MatcherOperator.equal, 'bar');
|
||||||
await user.type(ui.editor.matcherOperatorSelect.getAll()[0], '=');
|
|
||||||
await user.type(ui.editor.matcherValue.getAll()[0], 'bar');
|
|
||||||
|
|
||||||
await user.click(ui.editor.submit.get());
|
await user.click(ui.editor.submit.get());
|
||||||
|
|
||||||
await waitFor(() =>
|
expect(await ui.notExpiredTable.find()).toBeInTheDocument();
|
||||||
expect(mocks.api.createOrUpdateSilence).toHaveBeenCalledWith(
|
|
||||||
'Alertmanager',
|
|
||||||
expect.objectContaining({
|
|
||||||
matchers: [{ isEqual: true, isRegex: false, name: 'foo', value: 'bar' }],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(locationService.getSearch().get('alertmanager')).toBe('Alertmanager');
|
expect(locationService.getSearch().get('alertmanager')).toBe('Alertmanager');
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user