diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-15 21:08:43 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-15 21:08:43 +0300 |
commit | 316fbf9f95dcdd16775f0339415572c3195eea92 (patch) | |
tree | 40d86a896fc0ff8ce22fbed7e5e3dffc2adceebf /spec/frontend/alert_management | |
parent | d9e71b0d412fb9d2d7fc8b00dddac21617eaaf19 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/alert_management')
6 files changed, 245 insertions, 343 deletions
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 3aa67614369..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, - GlDropdown, - GlDropdownItem, - 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(GlDropdown); - const findStatusFilterTabs = () => wrapper.findAll(GlTab); - const findStatusTabs = () => wrapper.find(GlTabs); - const findStatusFilterBadge = () => wrapper.findAll(GlBadge); const findDateFields = () => wrapper.findAll(TimeAgo); - const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem); - 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,8 +238,7 @@ 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, }); @@ -305,8 +252,7 @@ describe('AlertManagementTable', () => { it('navigates to the detail page in new tab when alert row is clicked with the metaKey', () => { mountComponent({ - props: { alertManagementEnabled: true, userCanEnableAlertManagement: true }, - data: { alerts: { list: mockAlerts }, alertsCount, hasError: false }, + data: { alerts: { list: mockAlerts }, alertsCount, errored: false }, loading: false, }); @@ -324,8 +270,7 @@ describe('AlertManagementTable', () => { 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, }); }); @@ -355,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: [ @@ -369,7 +313,7 @@ describe('AlertManagementTable', () => { ], }, alertsCount, - hasError: false, + errored: false, }, loading: false, }); @@ -378,7 +322,6 @@ describe('AlertManagementTable', () => { it('should not display time ago dates when values not provided', () => { mountComponent({ - props: { alertManagementEnabled: true, userCanEnableAlertManagement: true }, data: { alerts: [ { @@ -389,7 +332,7 @@ describe('AlertManagementTable', () => { }, ], alertsCount, - hasError: false, + errored: false, }, loading: false, }); @@ -403,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, }); @@ -417,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, }); @@ -435,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, }, @@ -458,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, }); }); @@ -643,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/sidebar/alert_sidebar_status_spec.js b/spec/frontend/alert_management/components/sidebar/alert_sidebar_status_spec.js index e144d473c12..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 @@ -2,7 +2,7 @@ import { mount } from '@vue/test-utils'; 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'; @@ -85,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/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 |