summaryrefslogtreecommitdiff
path: root/spec/frontend/alert_management
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/alert_management')
-rw-r--r--spec/frontend/alert_management/components/alert_details_spec.js140
-rw-r--r--spec/frontend/alert_management/components/alert_management_empty_state_spec.js20
-rw-r--r--spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js50
-rw-r--r--spec/frontend/alert_management/components/alert_management_table_spec.js370
-rw-r--r--spec/frontend/alert_management/components/alert_status_spec.js151
-rw-r--r--spec/frontend/alert_management/components/alert_summary_row_spec.js40
-rw-r--r--spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js4
-rw-r--r--spec/frontend/alert_management/components/sidebar/alert_sidebar_status_spec.js17
-rw-r--r--spec/frontend/alert_management/components/system_notes/alert_management_system_note_spec.js6
-rw-r--r--spec/frontend/alert_management/mocks/alerts_provide_config.json13
10 files changed, 413 insertions, 398 deletions
diff --git a/spec/frontend/alert_management/components/alert_details_spec.js b/spec/frontend/alert_management/components/alert_details_spec.js
index 8aa26dbca3b..f3ebdfc5cc2 100644
--- a/spec/frontend/alert_management/components/alert_details_spec.js
+++ b/spec/frontend/alert_management/components/alert_details_spec.js
@@ -1,54 +1,76 @@
-import { mount, shallowMount } from '@vue/test-utils';
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
-import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import AlertDetails from '~/alert_management/components/alert_details.vue';
-import createIssueMutation from '~/alert_management/graphql/mutations/create_issue_from_alert.mutation.graphql';
-import { joinPaths } from '~/lib/utils/url_utility';
+import AlertSummaryRow from '~/alert_management/components/alert_summary_row.vue';
import {
- trackAlertsDetailsViewsOptions,
ALERTS_SEVERITY_LABELS,
+ trackAlertsDetailsViewsOptions,
} from '~/alert_management/constants';
+import createIssueMutation from '~/alert_management/graphql/mutations/create_issue_from_alert.mutation.graphql';
+import { joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
+import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0];
+const environmentName = 'Production';
+const environmentPath = '/fake/path';
describe('AlertDetails', () => {
- let wrapper;
+ let environmentData = {
+ name: environmentName,
+ path: environmentPath,
+ };
+ let glFeatures = { exposeEnvironmentPathInAlertDetails: false };
let mock;
+ let wrapper;
const projectPath = 'root/alerts';
const projectIssuesPath = 'root/alerts/-/issues';
const projectId = '1';
const $router = { replace: jest.fn() };
function mountComponent({ data, loading = false, mountMethod = shallowMount, stubs = {} } = {}) {
- wrapper = mountMethod(AlertDetails, {
- provide: {
- alertId: 'alertId',
- projectPath,
- projectIssuesPath,
- projectId,
- },
- data() {
- return { alert: { ...mockAlert }, sidebarStatus: false, ...data };
- },
- mocks: {
- $apollo: {
- mutate: jest.fn(),
- queries: {
+ wrapper = extendedWrapper(
+ mountMethod(AlertDetails, {
+ provide: {
+ alertId: 'alertId',
+ projectPath,
+ projectIssuesPath,
+ projectId,
+ glFeatures,
+ },
+ data() {
+ return {
alert: {
- loading,
+ ...mockAlert,
+ environment: environmentData,
+ },
+ sidebarStatus: false,
+ ...data,
+ };
+ },
+ mocks: {
+ $apollo: {
+ mutate: jest.fn(),
+ queries: {
+ alert: {
+ loading,
+ },
+ sidebarStatus: {},
},
- sidebarStatus: {},
},
+ $router,
+ $route: { params: {} },
},
- $router,
- $route: { params: {} },
- },
- stubs,
- });
+ stubs: {
+ ...stubs,
+ AlertSummaryRow,
+ },
+ }),
+ );
}
beforeEach(() => {
@@ -62,9 +84,11 @@ describe('AlertDetails', () => {
mock.restore();
});
- const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]');
- const findViewIncidentBtn = () => wrapper.find('[data-testid="viewIncidentBtn"]');
- const findIncidentCreationAlert = () => wrapper.find('[data-testid="incidentCreationError"]');
+ 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');
const findDetailsTable = () => wrapper.find(AlertDetailsTable);
describe('Alert details', () => {
@@ -74,7 +98,7 @@ describe('AlertDetails', () => {
});
it('shows an empty state', () => {
- expect(wrapper.find('[data-testid="alertDetailsTabs"]').exists()).toBe(false);
+ expect(wrapper.findByTestId('alertDetailsTabs').exists()).toBe(false);
});
});
@@ -84,28 +108,26 @@ describe('AlertDetails', () => {
});
it('renders a tab with overview information', () => {
- expect(wrapper.find('[data-testid="overview"]').exists()).toBe(true);
+ expect(wrapper.findByTestId('overview').exists()).toBe(true);
});
it('renders a tab with an activity feed', () => {
- expect(wrapper.find('[data-testid="activity"]').exists()).toBe(true);
+ expect(wrapper.findByTestId('activity').exists()).toBe(true);
});
it('renders severity', () => {
- expect(wrapper.find('[data-testid="severity"]').text()).toBe(
+ expect(wrapper.findByTestId('severity').text()).toBe(
ALERTS_SEVERITY_LABELS[mockAlert.severity],
);
});
it('renders a title', () => {
- expect(wrapper.find('[data-testid="title"]').text()).toBe(mockAlert.title);
+ expect(wrapper.findByTestId('title').text()).toBe(mockAlert.title);
});
it('renders a start time', () => {
- expect(wrapper.find('[data-testid="startTimeItem"]').exists()).toBe(true);
- expect(wrapper.find('[data-testid="startTimeItem"]').props().time).toBe(
- mockAlert.startedAt,
- );
+ expect(wrapper.findByTestId('startTimeItem').exists()).toBe(true);
+ expect(wrapper.findByTestId('startTimeItem').props('time')).toBe(mockAlert.startedAt);
});
});
@@ -126,15 +148,47 @@ describe('AlertDetails', () => {
});
it(`${field} is ${isShown ? 'displayed' : 'hidden'} correctly`, () => {
+ const element = wrapper.findByTestId(field);
if (isShown) {
- expect(wrapper.find(`[data-testid="${field}"]`).text()).toBe(data.toString());
+ expect(element.text()).toContain(data.toString());
} else {
- expect(wrapper.find(`[data-testid="${field}"]`).exists()).toBe(false);
+ expect(wrapper.findByTestId(field).exists()).toBe(false);
}
});
});
});
+ describe('environment fields', () => {
+ describe('when exposeEnvironmentPathInAlertDetails is disabled', () => {
+ beforeEach(mountComponent);
+
+ it('should not show the environment', () => {
+ expect(findEnvironmentName().exists()).toBe(false);
+ expect(findEnvironmentPath().exists()).toBe(false);
+ });
+ });
+
+ describe('when exposeEnvironmentPathInAlertDetails is enabled', () => {
+ beforeEach(() => {
+ glFeatures = { exposeEnvironmentPathInAlertDetails: true };
+ mountComponent();
+ });
+
+ it('should show the environment name with link to path', () => {
+ expect(findEnvironmentName().exists()).toBe(false);
+ expect(findEnvironmentPath().text()).toBe(environmentName);
+ expect(findEnvironmentPath().attributes('href')).toBe(environmentPath);
+ });
+
+ it('should only show the environment name if the path is not provided', () => {
+ environmentData = { name: environmentName, path: null };
+ mountComponent();
+ expect(findEnvironmentPath().exists()).toBe(false);
+ expect(findEnvironmentName().text()).toBe(environmentName);
+ });
+ });
+ });
+
describe('Create incident from alert', () => {
it('should display "View incident" button that links the incident page when incident exists', () => {
const issueIid = '3';
@@ -222,7 +276,7 @@ describe('AlertDetails', () => {
mountComponent({
data: { errored: true, sidebarErrorMessage: '<span data-testid="htmlError" />' },
});
- expect(wrapper.find('[data-testid="htmlError"]').exists()).toBe(true);
+ expect(wrapper.findByTestId('htmlError').exists()).toBe(true);
});
it('does not display an error when dismissed', () => {
@@ -232,7 +286,7 @@ describe('AlertDetails', () => {
});
describe('header', () => {
- const findHeader = () => wrapper.find('[data-testid="alert-header"]');
+ const findHeader = () => wrapper.findByTestId('alert-header');
const stubs = { TimeAgoTooltip: { template: '<span>now</span>' } };
describe('individual header fields', () => {
diff --git a/spec/frontend/alert_management/components/alert_management_empty_state_spec.js b/spec/frontend/alert_management/components/alert_management_empty_state_spec.js
index 6712282503d..ddb102339cc 100644
--- a/spec/frontend/alert_management/components/alert_management_empty_state_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_empty_state_spec.js
@@ -1,25 +1,17 @@
import { shallowMount } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui';
import AlertManagementEmptyState from '~/alert_management/components/alert_management_empty_state.vue';
+import defaultProvideValues from '../mocks/alerts_provide_config.json';
describe('AlertManagementEmptyState', () => {
let wrapper;
- function mountComponent({
- props = {
- alertManagementEnabled: false,
- userCanEnableAlertManagement: false,
- },
- stubs = {},
- } = {}) {
+ function mountComponent({ provide = {} } = {}) {
wrapper = shallowMount(AlertManagementEmptyState, {
- propsData: {
- enableAlertManagementPath: '/link',
- alertsHelpUrl: '/link',
- emptyAlertSvgPath: 'illustration/path',
- ...props,
+ provide: {
+ ...defaultProvideValues,
+ ...provide,
},
- stubs,
});
}
@@ -42,7 +34,7 @@ describe('AlertManagementEmptyState', () => {
it('show OpsGenie integration state when OpsGenie mcv is true', () => {
mountComponent({
- props: {
+ provide: {
alertManagementEnabled: false,
userCanEnableAlertManagement: false,
opsgenieMvcEnabled: true,
diff --git a/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js b/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js
index c36107c28ce..1d79b10a796 100644
--- a/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js
@@ -1,33 +1,18 @@
import { shallowMount } from '@vue/test-utils';
import AlertManagementList from '~/alert_management/components/alert_management_list_wrapper.vue';
-import { trackAlertListViewsOptions } from '~/alert_management/constants';
-import mockAlerts from '../mocks/alerts.json';
-import Tracking from '~/tracking';
+import AlertManagementEmptyState from '~/alert_management/components/alert_management_empty_state.vue';
+import AlertManagementTable from '~/alert_management/components/alert_management_table.vue';
+import defaultProvideValues from '../mocks/alerts_provide_config.json';
describe('AlertManagementList', () => {
let wrapper;
- function mountComponent({
- props = {
- alertManagementEnabled: false,
- userCanEnableAlertManagement: false,
- },
- data = {},
- stubs = {},
- } = {}) {
+ function mountComponent({ provide = {} } = {}) {
wrapper = shallowMount(AlertManagementList, {
- propsData: {
- projectPath: 'gitlab-org/gitlab',
- enableAlertManagementPath: '/link',
- alertsHelpUrl: '/link',
- populatingAlertsHelpUrl: '/help/help-page.md#populating-alert-data',
- emptyAlertSvgPath: 'illustration/path',
- ...props,
+ provide: {
+ ...defaultProvideValues,
+ ...provide,
},
- data() {
- return data;
- },
- stubs,
});
}
@@ -41,18 +26,21 @@ describe('AlertManagementList', () => {
}
});
- describe('Snowplow tracking', () => {
- beforeEach(() => {
- jest.spyOn(Tracking, 'event');
+ describe('Alert List Wrapper', () => {
+ it('should show the empty state when alerts are not enabled', () => {
+ expect(wrapper.find(AlertManagementEmptyState).exists()).toBe(true);
+ expect(wrapper.find(AlertManagementTable).exists()).toBe(false);
+ });
+
+ it('should show the alerts table when alerts are enabled', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts } },
+ provide: {
+ alertManagementEnabled: true,
+ },
});
- });
- it('should track alert list page views', () => {
- const { category, action } = trackAlertListViewsOptions;
- expect(Tracking.event).toHaveBeenCalledWith(category, action);
+ expect(wrapper.find(AlertManagementEmptyState).exists()).toBe(false);
+ expect(wrapper.find(AlertManagementTable).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/alert_management/components/alert_management_table_spec.js b/spec/frontend/alert_management/components/alert_management_table_spec.js
index bcad415eb19..f7a629142f9 100644
--- a/spec/frontend/alert_management/components/alert_management_table_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_table_spec.js
@@ -1,26 +1,13 @@
import { mount } from '@vue/test-utils';
-import {
- GlTable,
- GlAlert,
- GlLoadingIcon,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownItem,
- GlIcon,
- GlTabs,
- GlTab,
- GlBadge,
- GlPagination,
- GlSearchBoxByType,
- GlAvatar,
-} from '@gitlab/ui';
-import waitForPromises from 'helpers/wait_for_promises';
+import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar } from '@gitlab/ui';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
import { visitUrl } from '~/lib/utils/url_utility';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import AlertManagementTable from '~/alert_management/components/alert_management_table.vue';
-import { ALERTS_STATUS_TABS, trackAlertStatusUpdateOptions } from '~/alert_management/constants';
-import updateAlertStatus from '~/alert_management/graphql/mutations/update_alert_status.mutation.graphql';
+import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import mockAlerts from '../mocks/alerts.json';
-import Tracking from '~/tracking';
+import defaultProvideValues from '../mocks/alerts_provide_config.json';
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn().mockName('visitUrlMock'),
@@ -29,26 +16,21 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('AlertManagementTable', () => {
let wrapper;
+ let mock;
const findAlertsTable = () => wrapper.find(GlTable);
const findAlerts = () => wrapper.findAll('table tbody tr');
const findAlert = () => wrapper.find(GlAlert);
const findLoader = () => wrapper.find(GlLoadingIcon);
- const findStatusDropdown = () => wrapper.find(GlDeprecatedDropdown);
- const findStatusFilterTabs = () => wrapper.findAll(GlTab);
- const findStatusTabs = () => wrapper.find(GlTabs);
- const findStatusFilterBadge = () => wrapper.findAll(GlBadge);
+ const findStatusDropdown = () => wrapper.find(GlDropdown);
const findDateFields = () => wrapper.findAll(TimeAgo);
- const findFirstStatusOption = () => findStatusDropdown().find(GlDeprecatedDropdownItem);
- const findPagination = () => wrapper.find(GlPagination);
- const findSearch = () => wrapper.find(GlSearchBoxByType);
+ const findSearch = () => wrapper.find(FilteredSearchBar);
const findSeverityColumnHeader = () =>
wrapper.find('[data-testid="alert-management-severity-sort"]');
const findFirstIDField = () => wrapper.findAll('[data-testid="idField"]').at(0);
const findAssignees = () => wrapper.findAll('[data-testid="assigneesField"]');
const findSeverityFields = () => wrapper.findAll('[data-testid="severityField"]');
const findIssueFields = () => wrapper.findAll('[data-testid="issueField"]');
- const findAlertError = () => wrapper.find('[data-testid="alert-error"]');
const alertsCount = {
open: 24,
triggered: 20,
@@ -56,26 +38,14 @@ describe('AlertManagementTable', () => {
resolved: 11,
all: 26,
};
- const selectFirstStatusOption = () => {
- findFirstStatusOption().vm.$emit('click');
- return waitForPromises();
- };
-
- function mountComponent({
- props = {
- alertManagementEnabled: false,
- userCanEnableAlertManagement: false,
- },
- data = {},
- loading = false,
- stubs = {},
- } = {}) {
+ function mountComponent({ provide = {}, data = {}, loading = false, stubs = {} } = {}) {
wrapper = mount(AlertManagementTable, {
- propsData: {
- projectPath: 'gitlab-org/gitlab',
- populatingAlertsHelpUrl: '/help/help-page.md#populating-alert-data',
- ...props,
+ provide: {
+ ...defaultProvideValues,
+ alertManagementEnabled: true,
+ userCanEnableAlertManagement: true,
+ ...provide,
},
data() {
return data;
@@ -95,41 +65,21 @@ describe('AlertManagementTable', () => {
});
}
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
- });
-
- describe('Status Filter Tabs', () => {
- beforeEach(() => {
- mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: mockAlerts, alertsCount },
- loading: false,
- stubs: {
- GlTab: true,
- },
- });
- });
-
- it('should display filter tabs with alerts count badge for each status', () => {
- const tabs = findStatusFilterTabs().wrappers;
- const badges = findStatusFilterBadge();
-
- tabs.forEach((tab, i) => {
- const status = ALERTS_STATUS_TABS[i].status.toLowerCase();
- expect(tab.text()).toContain(ALERTS_STATUS_TABS[i].title);
- expect(badges.at(i).text()).toContain(alertsCount[status]);
- });
- });
+ mock.restore();
});
describe('Alerts table', () => {
it('loading state', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: {}, alertsCount: null },
loading: true,
});
@@ -144,8 +94,7 @@ describe('AlertManagementTable', () => {
it('error state', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { errors: ['error'] }, alertsCount: null, hasError: true },
+ data: { alerts: { errors: ['error'] }, alertsCount: null, errored: true },
loading: false,
});
expect(findAlertsTable().exists()).toBe(true);
@@ -161,10 +110,17 @@ describe('AlertManagementTable', () => {
it('empty state', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: [], pageInfo: {} }, alertsCount: { all: 0 }, hasError: false },
+ data: {
+ alerts: { list: [], pageInfo: {} },
+ alertsCount: { all: 0 },
+ errored: false,
+ isErrorAlertDismissed: false,
+ searchTerm: '',
+ assigneeUsername: '',
+ },
loading: false,
});
+
expect(findAlertsTable().exists()).toBe(true);
expect(findAlertsTable().text()).toContain('No alerts to display');
expect(findLoader().exists()).toBe(false);
@@ -178,8 +134,7 @@ describe('AlertManagementTable', () => {
it('has data state', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
expect(findLoader().exists()).toBe(false);
@@ -194,8 +149,7 @@ describe('AlertManagementTable', () => {
it('displays the alert ID and title formatted correctly', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@@ -205,8 +159,7 @@ describe('AlertManagementTable', () => {
it('displays status dropdown', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
expect(findStatusDropdown().exists()).toBe(true);
@@ -214,8 +167,7 @@ describe('AlertManagementTable', () => {
it('does not display a dropdown status header', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
expect(
@@ -225,27 +177,25 @@ describe('AlertManagementTable', () => {
).toBe(false);
});
- it('shows correct severity icons', () => {
+ it('shows correct severity icons', async () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(GlTable).exists()).toBe(true);
- expect(
- findAlertsTable()
- .find(GlIcon)
- .classes('icon-critical'),
- ).toBe(true);
- });
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.find(GlTable).exists()).toBe(true);
+ expect(
+ findAlertsTable()
+ .find(GlIcon)
+ .classes('icon-critical'),
+ ).toBe(true);
});
it('renders severity text', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@@ -258,8 +208,7 @@ describe('AlertManagementTable', () => {
it('renders Unassigned when no assignee(s) present', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@@ -272,8 +221,7 @@ describe('AlertManagementTable', () => {
it('renders user avatar when assignee present', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@@ -290,22 +238,39 @@ describe('AlertManagementTable', () => {
it('navigates to the detail page when alert row is clicked', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
+ expect(visitUrl).not.toHaveBeenCalled();
+
findAlerts()
.at(0)
.trigger('click');
- expect(visitUrl).toHaveBeenCalledWith('/1527542/details');
+ expect(visitUrl).toHaveBeenCalledWith('/1527542/details', false);
+ });
+
+ it('navigates to the detail page in new tab when alert row is clicked with the metaKey', () => {
+ mountComponent({
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
+ loading: false,
+ });
+
+ expect(visitUrl).not.toHaveBeenCalled();
+
+ findAlerts()
+ .at(0)
+ .trigger('click', {
+ metaKey: true,
+ });
+
+ expect(visitUrl).toHaveBeenCalledWith('/1527542/details', true);
});
describe('alert issue links', () => {
beforeEach(() => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
});
@@ -335,7 +300,6 @@ describe('AlertManagementTable', () => {
describe('handle date fields', () => {
it('should display time ago dates when values provided', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: {
alerts: {
list: [
@@ -349,7 +313,7 @@ describe('AlertManagementTable', () => {
],
},
alertsCount,
- hasError: false,
+ errored: false,
},
loading: false,
});
@@ -358,7 +322,6 @@ describe('AlertManagementTable', () => {
it('should not display time ago dates when values not provided', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: {
alerts: [
{
@@ -369,7 +332,7 @@ describe('AlertManagementTable', () => {
},
],
alertsCount,
- hasError: false,
+ errored: false,
},
loading: false,
});
@@ -383,8 +346,7 @@ describe('AlertManagementTable', () => {
it('should highlight the row when alert is new', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: [newAlert] }, alertsCount, hasError: false },
+ data: { alerts: { list: [newAlert] }, alertsCount, errored: false },
loading: false,
});
@@ -397,8 +359,7 @@ describe('AlertManagementTable', () => {
it('should not highlight the row when alert is not new', () => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: [oldAlert] }, alertsCount, hasError: false },
+ data: { alerts: { list: [oldAlert] }, alertsCount, errored: false },
loading: false,
});
@@ -415,10 +376,9 @@ describe('AlertManagementTable', () => {
describe('sorting the alert list by column', () => {
beforeEach(() => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: {
alerts: { list: mockAlerts },
- hasError: false,
+ errored: false,
sort: 'STARTED_AT_DESC',
alertsCount,
},
@@ -438,184 +398,10 @@ describe('AlertManagementTable', () => {
});
});
- describe('updating the alert status', () => {
- const iid = '1527542';
- const mockUpdatedMutationResult = {
- data: {
- updateAlertStatus: {
- errors: [],
- alert: {
- iid,
- status: 'acknowledged',
- },
- },
- },
- };
-
- beforeEach(() => {
- mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
- loading: false,
- });
- });
-
- it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
- findFirstStatusOption().vm.$emit('click');
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: updateAlertStatus,
- variables: {
- iid,
- status: 'TRIGGERED',
- projectPath: 'gitlab-org/gitlab',
- },
- });
- });
-
- describe('when a request fails', () => {
- beforeEach(() => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
- });
-
- it('shows an error', async () => {
- await selectFirstStatusOption();
-
- expect(findAlertError().text()).toContain(
- 'There was an error while updating the status of the alert.',
- );
- });
-
- it('shows an error when triggered a second time', async () => {
- await selectFirstStatusOption();
-
- wrapper.find(GlAlert).vm.$emit('dismiss');
-
- await wrapper.vm.$nextTick();
-
- // Assert that the error has been dismissed in the setup
- expect(findAlertError().exists()).toBe(false);
-
- await selectFirstStatusOption();
-
- expect(findAlertError().exists()).toBe(true);
- });
- });
-
- it('shows an error when response includes HTML errors', async () => {
- const mockUpdatedMutationErrorResult = {
- data: {
- updateAlertStatus: {
- errors: ['<span data-testid="htmlError" />'],
- alert: {
- iid,
- status: 'acknowledged',
- },
- },
- },
- };
-
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationErrorResult);
-
- await selectFirstStatusOption();
-
- expect(findAlertError().exists()).toBe(true);
- expect(
- findAlertError()
- .find('[data-testid="htmlError"]')
- .exists(),
- ).toBe(true);
- });
- });
-
- describe('Snowplow tracking', () => {
- beforeEach(() => {
- jest.spyOn(Tracking, 'event');
- mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount },
- loading: false,
- });
- });
-
- it('should track alert status updates', () => {
- Tracking.event.mockClear();
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
- findFirstStatusOption().vm.$emit('click');
- const status = findFirstStatusOption().text();
- setImmediate(() => {
- const { category, action, label } = trackAlertStatusUpdateOptions;
- expect(Tracking.event).toHaveBeenCalledWith(category, action, { label, property: status });
- });
- });
- });
-
- describe('Pagination', () => {
- beforeEach(() => {
- mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts, pageInfo: {} }, alertsCount, hasError: false },
- loading: false,
- });
- });
-
- it('does NOT show pagination control when list is smaller than default page size', () => {
- findStatusTabs().vm.$emit('input', 3);
- return wrapper.vm.$nextTick(() => {
- expect(findPagination().exists()).toBe(false);
- });
- });
-
- it('shows pagination control when list is larger than default page size', () => {
- findStatusTabs().vm.$emit('input', 0);
- return wrapper.vm.$nextTick(() => {
- expect(findPagination().exists()).toBe(true);
- });
- });
-
- describe('prevPage', () => {
- it('returns prevPage number', () => {
- findPagination().vm.$emit('input', 3);
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.prevPage).toBe(2);
- });
- });
-
- it('returns 0 when it is the first page', () => {
- findPagination().vm.$emit('input', 1);
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.prevPage).toBe(0);
- });
- });
- });
-
- describe('nextPage', () => {
- it('returns nextPage number', () => {
- findPagination().vm.$emit('input', 1);
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.nextPage).toBe(2);
- });
- });
-
- it('returns `null` when currentPage is already last page', () => {
- findStatusTabs().vm.$emit('input', 3);
- findPagination().vm.$emit('input', 1);
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.nextPage).toBeNull();
- });
- });
- });
- });
-
describe('Search', () => {
beforeEach(() => {
mountComponent({
- props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
- data: { alerts: { list: mockAlerts }, alertsCount, hasError: false },
+ data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
});
@@ -623,13 +409,5 @@ describe('AlertManagementTable', () => {
it('renders the search component', () => {
expect(findSearch().exists()).toBe(true);
});
-
- it('sets the `searchTerm` graphql variable', () => {
- const SEARCH_TERM = 'Simple Alert';
-
- findSearch().vm.$emit('input', SEARCH_TERM);
-
- expect(wrapper.vm.$data.searchTerm).toBe(SEARCH_TERM);
- });
});
});
diff --git a/spec/frontend/alert_management/components/alert_status_spec.js b/spec/frontend/alert_management/components/alert_status_spec.js
new file mode 100644
index 00000000000..f5916b8b265
--- /dev/null
+++ b/spec/frontend/alert_management/components/alert_status_spec.js
@@ -0,0 +1,151 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+import { trackAlertStatusUpdateOptions } from '~/alert_management/constants';
+import AlertManagementStatus from '~/alert_management/components/alert_status.vue';
+import updateAlertStatusMutation from '~/alert_management/graphql/mutations/update_alert_status.mutation.graphql';
+import Tracking from '~/tracking';
+import mockAlerts from '../mocks/alerts.json';
+
+const mockAlert = mockAlerts[0];
+
+describe('AlertManagementStatus', () => {
+ let wrapper;
+ const findStatusDropdown = () => wrapper.find(GlDropdown);
+ const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
+
+ const selectFirstStatusOption = () => {
+ findFirstStatusOption().vm.$emit('click');
+
+ return waitForPromises();
+ };
+
+ function mountComponent({ props = {}, loading = false, stubs = {} } = {}) {
+ wrapper = shallowMount(AlertManagementStatus, {
+ propsData: {
+ alert: { ...mockAlert },
+ projectPath: 'gitlab-org/gitlab',
+ isSidebar: false,
+ ...props,
+ },
+ mocks: {
+ $apollo: {
+ mutate: jest.fn(),
+ queries: {
+ alert: {
+ loading,
+ },
+ },
+ },
+ },
+ stubs,
+ });
+ }
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ describe('updating the alert status', () => {
+ const iid = '1527542';
+ const mockUpdatedMutationResult = {
+ data: {
+ updateAlertStatus: {
+ errors: [],
+ alert: {
+ iid,
+ status: 'acknowledged',
+ },
+ },
+ },
+ };
+
+ beforeEach(() => {
+ mountComponent({});
+ });
+
+ it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
+ findFirstStatusOption().vm.$emit('click');
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: updateAlertStatusMutation,
+ variables: {
+ iid,
+ status: 'TRIGGERED',
+ projectPath: 'gitlab-org/gitlab',
+ },
+ });
+ });
+
+ describe('when a request fails', () => {
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
+ });
+
+ it('emits an error', async () => {
+ await selectFirstStatusOption();
+
+ expect(wrapper.emitted('alert-error')[0]).toEqual([
+ 'There was an error while updating the status of the alert. Please try again.',
+ ]);
+ });
+
+ it('emits an error when triggered a second time', async () => {
+ await selectFirstStatusOption();
+ await wrapper.vm.$nextTick();
+ await selectFirstStatusOption();
+ // Should emit two errors [0,1]
+ expect(wrapper.emitted('alert-error').length > 1).toBe(true);
+ });
+ });
+
+ it('shows an error when response includes HTML errors', async () => {
+ const mockUpdatedMutationErrorResult = {
+ data: {
+ updateAlertStatus: {
+ errors: ['<span data-testid="htmlError" />'],
+ alert: {
+ iid,
+ status: 'acknowledged',
+ },
+ },
+ },
+ };
+
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationErrorResult);
+
+ await selectFirstStatusOption();
+
+ expect(wrapper.emitted('alert-error').length > 0).toBe(true);
+ expect(wrapper.emitted('alert-error')[0]).toEqual([
+ 'There was an error while updating the status of the alert. <span data-testid="htmlError" />',
+ ]);
+ });
+ });
+
+ describe('Snowplow tracking', () => {
+ beforeEach(() => {
+ jest.spyOn(Tracking, 'event');
+ mountComponent({});
+ });
+
+ it('should track alert status updates', () => {
+ Tracking.event.mockClear();
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
+ findFirstStatusOption().vm.$emit('click');
+ const status = findFirstStatusOption().text();
+ setImmediate(() => {
+ const { category, action, label } = trackAlertStatusUpdateOptions;
+ expect(Tracking.event).toHaveBeenCalledWith(category, action, { label, property: status });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/alert_management/components/alert_summary_row_spec.js b/spec/frontend/alert_management/components/alert_summary_row_spec.js
new file mode 100644
index 00000000000..47c715c089a
--- /dev/null
+++ b/spec/frontend/alert_management/components/alert_summary_row_spec.js
@@ -0,0 +1,40 @@
+import { shallowMount } from '@vue/test-utils';
+import AlertSummaryRow from '~/alert_management/components/alert_summary_row.vue';
+
+const label = 'a label';
+const value = 'a value';
+
+describe('AlertSummaryRow', () => {
+ let wrapper;
+
+ function mountComponent({ mountMethod = shallowMount, props, defaultSlot } = {}) {
+ wrapper = mountMethod(AlertSummaryRow, {
+ propsData: props,
+ scopedSlots: {
+ default: defaultSlot,
+ },
+ });
+ }
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ describe('Alert Summary Row', () => {
+ beforeEach(() => {
+ mountComponent({
+ props: {
+ label,
+ },
+ defaultSlot: `<span class="value">${value}</span>`,
+ });
+ });
+
+ it('should display a label and a value', () => {
+ expect(wrapper.text()).toBe(`${label} ${value}`);
+ });
+ });
+});
diff --git a/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js b/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js
index 4c9db02eff4..1d87301aac9 100644
--- a/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js
+++ b/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js
@@ -1,7 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
-import { GlDeprecatedDropdownItem } from '@gitlab/ui';
+import { GlDropdownItem } from '@gitlab/ui';
import SidebarAssignee from '~/alert_management/components/sidebar/sidebar_assignee.vue';
import SidebarAssignees from '~/alert_management/components/sidebar/sidebar_assignees.vue';
import AlertSetAssignees from '~/alert_management/graphql/mutations/alert_set_assignees.mutation.graphql';
@@ -106,7 +106,7 @@ describe('Alert Details Sidebar Assignees', () => {
it('renders a unassigned option', async () => {
wrapper.setData({ isDropdownSearching: false });
await wrapper.vm.$nextTick();
- expect(wrapper.find(GlDeprecatedDropdownItem).text()).toBe('Unassigned');
+ expect(wrapper.find(GlDropdownItem).text()).toBe('Unassigned');
});
it('calls `$apollo.mutate` with `AlertSetAssignees` mutation and variables containing `iid`, `assigneeUsernames`, & `projectPath`', async () => {
diff --git a/spec/frontend/alert_management/components/sidebar/alert_sidebar_status_spec.js b/spec/frontend/alert_management/components/sidebar/alert_sidebar_status_spec.js
index a8fe40687e1..bef4a341985 100644
--- a/spec/frontend/alert_management/components/sidebar/alert_sidebar_status_spec.js
+++ b/spec/frontend/alert_management/components/sidebar/alert_sidebar_status_spec.js
@@ -1,8 +1,8 @@
import { mount } from '@vue/test-utils';
-import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlLoadingIcon } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
import { trackAlertStatusUpdateOptions } from '~/alert_management/constants';
import AlertSidebarStatus from '~/alert_management/components/sidebar/sidebar_status.vue';
-import updateAlertStatus from '~/alert_management/graphql/mutations/update_alert_status.mutation.graphql';
+import updateAlertStatusMutation from '~/alert_management/graphql/mutations/update_alert_status.mutation.graphql';
import Tracking from '~/tracking';
import mockAlerts from '../../mocks/alerts.json';
@@ -10,9 +10,10 @@ const mockAlert = mockAlerts[0];
describe('Alert Details Sidebar Status', () => {
let wrapper;
- const findStatusDropdown = () => wrapper.find(GlDeprecatedDropdown);
- const findStatusDropdownItem = () => wrapper.find(GlDeprecatedDropdownItem);
+ const findStatusDropdown = () => wrapper.find(GlDropdown);
+ const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
const findStatusLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findStatusDropdownHeader = () => wrapper.find('[data-testid="dropdown-header"]');
function mountComponent({ data, sidebarCollapsed = true, loading = false, stubs = {} } = {}) {
wrapper = mount(AlertSidebarStatus, {
@@ -56,11 +57,7 @@ describe('Alert Details Sidebar Status', () => {
});
it('displays the dropdown status header', () => {
- expect(
- findStatusDropdown()
- .find('.dropdown-title')
- .exists(),
- ).toBe(true);
+ expect(findStatusDropdownHeader().exists()).toBe(true);
});
describe('updating the alert status', () => {
@@ -88,7 +85,7 @@ describe('Alert Details Sidebar Status', () => {
findStatusDropdownItem().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: updateAlertStatus,
+ mutation: updateAlertStatusMutation,
variables: {
iid: '1527542',
status: 'TRIGGERED',
diff --git a/spec/frontend/alert_management/components/system_notes/alert_management_system_note_spec.js b/spec/frontend/alert_management/components/system_notes/alert_management_system_note_spec.js
index 8dd663e55d9..65cfc600d76 100644
--- a/spec/frontend/alert_management/components/system_notes/alert_management_system_note_spec.js
+++ b/spec/frontend/alert_management/components/system_notes/alert_management_system_note_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
import SystemNote from '~/alert_management/components/system_notes/system_note.vue';
import mockAlerts from '../../mocks/alerts.json';
@@ -19,6 +20,7 @@ describe('Alert Details System Note', () => {
afterEach(() => {
if (wrapper) {
wrapper.destroy();
+ wrapper = null;
}
});
@@ -29,10 +31,10 @@ describe('Alert Details System Note', () => {
it('renders the correct system note', () => {
const noteId = wrapper.find('.note-wrapper').attributes('id');
- const iconRoute = wrapper.find('use').attributes('href');
+ const iconName = wrapper.find(GlIcon).attributes('name');
expect(noteId).toBe('note_1628');
- expect(iconRoute.includes('user')).toBe(true);
+ expect(iconName).toBe(mockAlert.notes.nodes[0].systemNoteIconName);
});
});
});
diff --git a/spec/frontend/alert_management/mocks/alerts_provide_config.json b/spec/frontend/alert_management/mocks/alerts_provide_config.json
new file mode 100644
index 00000000000..af543e641bc
--- /dev/null
+++ b/spec/frontend/alert_management/mocks/alerts_provide_config.json
@@ -0,0 +1,13 @@
+{
+ "textQuery": "foo",
+ "authorUsernameQuery": "root",
+ "assigneeUsernameQuery": "root",
+ "projectPath": "gitlab-org/gitlab",
+ "enableAlertManagementPath": "/link",
+ "populatingAlertsHelpUrl": "/link",
+ "emptyAlertSvgPath": "/link",
+ "alertManagementEnabled": false,
+ "userCanEnableAlertManagement": false,
+ "opsgenieMvcTargetUrl": "/link",
+ "opsgenieMvcEnabled": false
+} \ No newline at end of file