diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-21 07:08:36 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-21 07:08:36 +0000 |
commit | 48aff82709769b098321c738f3444b9bdaa694c6 (patch) | |
tree | e00c7c43e2d9b603a5a6af576b1685e400410dee /spec/frontend/alert_management | |
parent | 879f5329ee916a948223f8f43d77fba4da6cd028 (diff) | |
download | gitlab-ce-48aff82709769b098321c738f3444b9bdaa694c6.tar.gz |
Add latest changes from gitlab-org/gitlab@13-5-stable-eev13.5.0-rc42
Diffstat (limited to 'spec/frontend/alert_management')
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 |