2020-11-24 15:15:51 +05:30
|
|
|
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
|
2021-01-03 14:25:43 +05:30
|
|
|
import { mount, shallowMount } from '@vue/test-utils';
|
2020-06-23 00:09:42 +05:30
|
|
|
import axios from 'axios';
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
2021-01-03 14:25:43 +05:30
|
|
|
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
2021-03-08 18:12:59 +05:30
|
|
|
import waitForPromises from 'helpers/wait_for_promises';
|
2021-01-03 14:25:43 +05:30
|
|
|
import { joinPaths } from '~/lib/utils/url_utility';
|
2020-06-23 00:09:42 +05:30
|
|
|
import Tracking from '~/tracking';
|
2021-03-11 19:13:27 +05:30
|
|
|
import AlertDetails from '~/vue_shared/alert_details/components/alert_details.vue';
|
|
|
|
import AlertSummaryRow from '~/vue_shared/alert_details/components/alert_summary_row.vue';
|
2021-04-29 21:17:54 +05:30
|
|
|
import { PAGE_CONFIG, SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants';
|
2021-03-11 19:13:27 +05:30
|
|
|
import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql';
|
2021-01-03 14:25:43 +05:30
|
|
|
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
|
2021-03-11 19:13:27 +05:30
|
|
|
import mockAlerts from './mocks/alerts.json';
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
const mockAlert = mockAlerts[0];
|
2021-01-03 14:25:43 +05:30
|
|
|
const environmentName = 'Production';
|
|
|
|
const environmentPath = '/fake/path';
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
describe('AlertDetails', () => {
|
2021-01-29 00:20:46 +05:30
|
|
|
let environmentData = { name: environmentName, path: environmentPath };
|
2020-06-23 00:09:42 +05:30
|
|
|
let mock;
|
2021-01-03 14:25:43 +05:30
|
|
|
let wrapper;
|
2020-06-23 00:09:42 +05:30
|
|
|
const projectPath = 'root/alerts';
|
|
|
|
const projectIssuesPath = 'root/alerts/-/issues';
|
2020-07-28 23:09:34 +05:30
|
|
|
const projectId = '1';
|
2020-10-24 23:57:45 +05:30
|
|
|
const $router = { replace: jest.fn() };
|
2020-06-23 00:09:42 +05:30
|
|
|
|
2021-03-11 19:13:27 +05:30
|
|
|
function mountComponent({
|
|
|
|
data,
|
|
|
|
loading = false,
|
|
|
|
mountMethod = shallowMount,
|
|
|
|
provide = {},
|
|
|
|
stubs = {},
|
|
|
|
} = {}) {
|
2021-01-03 14:25:43 +05:30
|
|
|
wrapper = extendedWrapper(
|
|
|
|
mountMethod(AlertDetails, {
|
|
|
|
provide: {
|
|
|
|
alertId: 'alertId',
|
|
|
|
projectPath,
|
|
|
|
projectIssuesPath,
|
|
|
|
projectId,
|
2021-03-11 19:13:27 +05:30
|
|
|
...provide,
|
2021-01-03 14:25:43 +05:30
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
2020-05-24 23:13:21 +05:30
|
|
|
alert: {
|
2021-01-03 14:25:43 +05:30
|
|
|
...mockAlert,
|
|
|
|
environment: environmentData,
|
|
|
|
},
|
|
|
|
sidebarStatus: false,
|
|
|
|
...data,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
mocks: {
|
|
|
|
$apollo: {
|
|
|
|
mutate: jest.fn(),
|
|
|
|
queries: {
|
|
|
|
alert: {
|
|
|
|
loading,
|
|
|
|
},
|
|
|
|
sidebarStatus: {},
|
2020-05-24 23:13:21 +05:30
|
|
|
},
|
|
|
|
},
|
2021-01-03 14:25:43 +05:30
|
|
|
$router,
|
|
|
|
$route: { params: {} },
|
2020-05-24 23:13:21 +05:30
|
|
|
},
|
2021-01-03 14:25:43 +05:30
|
|
|
stubs: {
|
|
|
|
...stubs,
|
|
|
|
AlertSummaryRow,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
);
|
2020-05-24 23:13:21 +05:30
|
|
|
}
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
beforeEach(() => {
|
|
|
|
mock = new MockAdapter(axios);
|
|
|
|
});
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
afterEach(() => {
|
|
|
|
if (wrapper) {
|
2020-07-28 23:09:34 +05:30
|
|
|
wrapper.destroy();
|
2020-05-24 23:13:21 +05:30
|
|
|
}
|
2020-06-23 00:09:42 +05:30
|
|
|
mock.restore();
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
const findCreateIncidentBtn = () => wrapper.findByTestId('createIncidentBtn');
|
|
|
|
const findViewIncidentBtn = () => wrapper.findByTestId('viewIncidentBtn');
|
|
|
|
const findIncidentCreationAlert = () => wrapper.findByTestId('incidentCreationError');
|
|
|
|
const findEnvironmentName = () => wrapper.findByTestId('environmentName');
|
|
|
|
const findEnvironmentPath = () => wrapper.findByTestId('environmentPath');
|
2021-04-17 20:07:23 +05:30
|
|
|
const findDetailsTable = () => wrapper.findComponent(AlertDetailsTable);
|
2021-03-11 19:13:27 +05:30
|
|
|
const findMetricsTab = () => wrapper.findByTestId('metrics');
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
describe('Alert details', () => {
|
|
|
|
describe('when alert is null', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({ data: { alert: null } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('shows an empty state', () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId('alertDetailsTabs').exists()).toBe(false);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when alert is present', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({ data: { alert: mockAlert } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders a tab with overview information', () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId('overview').exists()).toBe(true);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
2020-11-24 15:15:51 +05:30
|
|
|
it('renders a tab with an activity feed', () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId('activity').exists()).toBe(true);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
it('renders severity', () => {
|
2021-03-11 19:13:27 +05:30
|
|
|
expect(wrapper.findByTestId('severity').text()).toBe(SEVERITY_LEVELS[mockAlert.severity]);
|
2020-06-23 00:09:42 +05:30
|
|
|
});
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it('renders a title', () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId('title').text()).toBe(mockAlert.title);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it('renders a start time', () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId('startTimeItem').exists()).toBe(true);
|
|
|
|
expect(wrapper.findByTestId('startTimeItem').props('time')).toBe(mockAlert.startedAt);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
2021-03-11 19:13:27 +05:30
|
|
|
|
|
|
|
it('renders the metrics tab', () => {
|
|
|
|
expect(findMetricsTab().exists()).toBe(true);
|
|
|
|
});
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
describe('individual alert fields', () => {
|
|
|
|
describe.each`
|
|
|
|
field | data | isShown
|
|
|
|
${'eventCount'} | ${1} | ${true}
|
|
|
|
${'eventCount'} | ${undefined} | ${false}
|
|
|
|
${'monitoringTool'} | ${'New Relic'} | ${true}
|
|
|
|
${'monitoringTool'} | ${undefined} | ${false}
|
|
|
|
${'service'} | ${'Prometheus'} | ${true}
|
|
|
|
${'service'} | ${undefined} | ${false}
|
2020-10-24 23:57:45 +05:30
|
|
|
${'runbook'} | ${undefined} | ${false}
|
|
|
|
${'runbook'} | ${'run.com'} | ${true}
|
2020-05-24 23:13:21 +05:30
|
|
|
`(`$desc`, ({ field, data, isShown }) => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({ data: { alert: { ...mockAlert, [field]: data } } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it(`${field} is ${isShown ? 'displayed' : 'hidden'} correctly`, () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
const element = wrapper.findByTestId(field);
|
2020-05-24 23:13:21 +05:30
|
|
|
if (isShown) {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(element.text()).toContain(data.toString());
|
2020-05-24 23:13:21 +05:30
|
|
|
} else {
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId(field).exists()).toBe(false);
|
2020-05-24 23:13:21 +05:30
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
describe('environment fields', () => {
|
2021-01-29 00:20:46 +05:30
|
|
|
it('should show the environment name with a link to the path', () => {
|
|
|
|
mountComponent();
|
|
|
|
const path = findEnvironmentPath();
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-01-29 00:20:46 +05:30
|
|
|
expect(findEnvironmentName().exists()).toBe(false);
|
|
|
|
expect(path.text()).toBe(environmentName);
|
|
|
|
expect(path.attributes('href')).toBe(environmentPath);
|
2021-01-03 14:25:43 +05:30
|
|
|
});
|
|
|
|
|
2021-01-29 00:20:46 +05:30
|
|
|
it('should only show the environment name if the path is not provided', () => {
|
|
|
|
environmentData = { name: environmentName, path: null };
|
|
|
|
mountComponent();
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-01-29 00:20:46 +05:30
|
|
|
expect(findEnvironmentPath().exists()).toBe(false);
|
|
|
|
expect(findEnvironmentName().text()).toBe(environmentName);
|
2021-01-03 14:25:43 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-03-11 19:13:27 +05:30
|
|
|
describe('Threat Monitoring details', () => {
|
|
|
|
it('should not render the metrics tab', () => {
|
|
|
|
mountComponent({
|
|
|
|
data: { alert: mockAlert },
|
|
|
|
provide: { isThreatMonitoringPage: true },
|
|
|
|
});
|
|
|
|
expect(findMetricsTab().exists()).toBe(false);
|
|
|
|
});
|
2021-04-17 20:07:23 +05:30
|
|
|
|
|
|
|
it('should display "View incident" button that links the issues page when incident exists', () => {
|
|
|
|
const iid = '3';
|
|
|
|
mountComponent({
|
|
|
|
data: { alert: { ...mockAlert, issue: { iid } }, sidebarStatus: false },
|
|
|
|
provide: { isThreatMonitoringPage: true },
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(findViewIncidentBtn().exists()).toBe(true);
|
|
|
|
expect(findViewIncidentBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, iid));
|
|
|
|
expect(findCreateIncidentBtn().exists()).toBe(false);
|
|
|
|
});
|
2021-03-11 19:13:27 +05:30
|
|
|
});
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
describe('Create incident from alert', () => {
|
|
|
|
it('should display "View incident" button that links the incident page when incident exists', () => {
|
2021-04-17 20:07:23 +05:30
|
|
|
const iid = '3';
|
2020-06-23 00:09:42 +05:30
|
|
|
mountComponent({
|
2021-04-17 20:07:23 +05:30
|
|
|
data: { alert: { ...mockAlert, issue: { iid } }, sidebarStatus: false },
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
expect(findViewIncidentBtn().exists()).toBe(true);
|
|
|
|
expect(findViewIncidentBtn().attributes('href')).toBe(
|
2021-04-17 20:07:23 +05:30
|
|
|
joinPaths(projectIssuesPath, 'incident', iid),
|
2020-10-24 23:57:45 +05:30
|
|
|
);
|
|
|
|
expect(findCreateIncidentBtn().exists()).toBe(false);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
it('should display "Create incident" button when incident doesn\'t exist yet', () => {
|
2021-04-17 20:07:23 +05:30
|
|
|
const issue = null;
|
2020-06-23 00:09:42 +05:30
|
|
|
mountComponent({
|
|
|
|
mountMethod: mount,
|
2021-04-17 20:07:23 +05:30
|
|
|
data: { alert: { ...mockAlert, issue } },
|
2020-06-23 00:09:42 +05:30
|
|
|
});
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
2020-10-24 23:57:45 +05:30
|
|
|
expect(findViewIncidentBtn().exists()).toBe(false);
|
|
|
|
expect(findCreateIncidentBtn().exists()).toBe(true);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
2020-06-23 00:09:42 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
it('calls `$apollo.mutate` with `createIssueQuery`', () => {
|
|
|
|
const issueIid = '10';
|
2021-03-08 18:12:59 +05:30
|
|
|
mountComponent({
|
|
|
|
mountMethod: mount,
|
|
|
|
data: { alert: { ...mockAlert } },
|
|
|
|
});
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
jest
|
|
|
|
.spyOn(wrapper.vm.$apollo, 'mutate')
|
|
|
|
.mockResolvedValue({ data: { createAlertIssue: { issue: { iid: issueIid } } } });
|
2020-10-24 23:57:45 +05:30
|
|
|
findCreateIncidentBtn().trigger('click');
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
|
2020-07-28 23:09:34 +05:30
|
|
|
mutation: createIssueMutation,
|
2020-06-23 00:09:42 +05:30
|
|
|
variables: {
|
|
|
|
iid: mockAlert.iid,
|
|
|
|
projectPath,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-03-08 18:12:59 +05:30
|
|
|
it('shows error alert when incident creation fails ', async () => {
|
2020-06-23 00:09:42 +05:30
|
|
|
const errorMsg = 'Something went wrong';
|
|
|
|
mountComponent({
|
|
|
|
mountMethod: mount,
|
|
|
|
data: { alert: { ...mockAlert, alertIid: 1 } },
|
|
|
|
});
|
|
|
|
|
|
|
|
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
|
2020-10-24 23:57:45 +05:30
|
|
|
findCreateIncidentBtn().trigger('click');
|
2020-06-23 00:09:42 +05:30
|
|
|
|
2021-03-08 18:12:59 +05:30
|
|
|
await waitForPromises();
|
|
|
|
expect(findIncidentCreationAlert().text()).toBe(errorMsg);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('View full alert details', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({ data: { alert: mockAlert } });
|
|
|
|
});
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it('should display a table of raw alert details data', () => {
|
2021-04-29 21:17:54 +05:30
|
|
|
const details = findDetailsTable();
|
|
|
|
expect(details.exists()).toBe(true);
|
|
|
|
expect(details.props()).toStrictEqual({
|
|
|
|
alert: mockAlert,
|
|
|
|
statuses: PAGE_CONFIG.OPERATIONS.STATUSES,
|
|
|
|
loading: false,
|
|
|
|
});
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('loading state', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({ loading: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('displays a loading state when loading', () => {
|
|
|
|
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('error state', () => {
|
|
|
|
it('displays a error state correctly', () => {
|
|
|
|
mountComponent({ data: { errored: true } });
|
|
|
|
expect(wrapper.find(GlAlert).exists()).toBe(true);
|
|
|
|
});
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
it('renders html-errors correctly', () => {
|
|
|
|
mountComponent({
|
|
|
|
data: { errored: true, sidebarErrorMessage: '<span data-testid="htmlError" />' },
|
|
|
|
});
|
2021-01-03 14:25:43 +05:30
|
|
|
expect(wrapper.findByTestId('htmlError').exists()).toBe(true);
|
2020-07-28 23:09:34 +05:30
|
|
|
});
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it('does not display an error when dismissed', () => {
|
|
|
|
mountComponent({ data: { errored: true, isErrorDismissed: true } });
|
|
|
|
expect(wrapper.find(GlAlert).exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('header', () => {
|
2021-01-03 14:25:43 +05:30
|
|
|
const findHeader = () => wrapper.findByTestId('alert-header');
|
2020-11-24 15:15:51 +05:30
|
|
|
const stubs = { TimeAgoTooltip: { template: '<span>now</span>' } };
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
describe('individual header fields', () => {
|
|
|
|
describe.each`
|
2020-06-23 00:09:42 +05:30
|
|
|
createdAt | monitoringTool | result
|
|
|
|
${'2020-04-17T23:18:14.996Z'} | ${null} | ${'Alert Reported now'}
|
|
|
|
${'2020-04-17T23:18:14.996Z'} | ${'Datadog'} | ${'Alert Reported now by Datadog'}
|
2020-05-24 23:13:21 +05:30
|
|
|
`(
|
2020-06-23 00:09:42 +05:30
|
|
|
`When createdAt=$createdAt, monitoringTool=$monitoringTool`,
|
|
|
|
({ createdAt, monitoringTool, result }) => {
|
2020-05-24 23:13:21 +05:30
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({
|
2020-06-23 00:09:42 +05:30
|
|
|
data: { alert: { ...mockAlert, createdAt, monitoringTool } },
|
2020-05-24 23:13:21 +05:30
|
|
|
mountMethod: mount,
|
|
|
|
stubs,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('header text is shown correctly', () => {
|
|
|
|
expect(findHeader().text()).toBe(result);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2020-10-24 23:57:45 +05:30
|
|
|
|
|
|
|
describe('tab navigation', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mountComponent({ data: { alert: mockAlert } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
index | tabId
|
|
|
|
${0} | ${'overview'}
|
2020-11-24 15:15:51 +05:30
|
|
|
${1} | ${'metrics'}
|
|
|
|
${2} | ${'activity'}
|
2020-10-24 23:57:45 +05:30
|
|
|
`('will navigate to the correct tab via $tabId', ({ index, tabId }) => {
|
|
|
|
wrapper.setData({ currentTabIndex: index });
|
|
|
|
expect($router.replace).toHaveBeenCalledWith({ name: 'tab', params: { tabId } });
|
|
|
|
});
|
|
|
|
});
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
describe('Snowplow tracking', () => {
|
2021-03-11 19:13:27 +05:30
|
|
|
const mountOptions = {
|
|
|
|
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
|
|
|
|
data: { alert: mockAlert },
|
|
|
|
loading: false,
|
|
|
|
};
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
beforeEach(() => {
|
2020-06-23 00:09:42 +05:30
|
|
|
jest.spyOn(Tracking, 'event');
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
|
2021-03-11 19:13:27 +05:30
|
|
|
it('should not track alert details page views when the tracking options do not exist', () => {
|
|
|
|
mountComponent(mountOptions);
|
|
|
|
expect(Tracking.event).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should track alert details page views when the tracking options exist', () => {
|
|
|
|
const trackAlertsDetailsViewsOptions = {
|
|
|
|
category: 'Alert Management',
|
|
|
|
action: 'view_alert_details',
|
|
|
|
};
|
|
|
|
mountComponent({ ...mountOptions, provide: { trackAlertsDetailsViewsOptions } });
|
2020-06-23 00:09:42 +05:30
|
|
|
const { category, action } = trackAlertsDetailsViewsOptions;
|
|
|
|
expect(Tracking.event).toHaveBeenCalledWith(category, action);
|
2020-05-24 23:13:21 +05:30
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|