Commit 0f896bd4 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'assign-alerts-sidebar-base' into 'master'

Assign alerts sidebar base

See merge request gitlab-org/gitlab!32642
parents 7b152201 14f5965a
...@@ -4,36 +4,27 @@ import { ...@@ -4,36 +4,27 @@ import {
GlAlert, GlAlert,
GlIcon, GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlDropdown,
GlDropdownItem,
GlSprintf, GlSprintf,
GlTabs, GlTabs,
GlTab, GlTab,
GlButton, GlButton,
GlTable, GlTable,
} from '@gitlab/ui'; } from '@gitlab/ui';
import createFlash from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import query from '../graphql/queries/details.query.graphql'; import query from '../graphql/queries/details.query.graphql';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { import { ALERTS_SEVERITY_LABELS, trackAlertsDetailsViewsOptions } from '../constants';
ALERTS_SEVERITY_LABELS,
trackAlertsDetailsViewsOptions,
trackAlertStatusUpdateOptions,
} from '../constants';
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
import createIssueQuery from '../graphql/mutations/create_issue_from_alert.graphql'; import createIssueQuery from '../graphql/mutations/create_issue_from_alert.graphql';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility'; import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
import AlertSidebar from './alert_sidebar.vue';
const containerEl = document.querySelector('.page-with-contextual-sidebar');
export default { export default {
statuses: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
i18n: { i18n: {
errorMsg: s__( errorMsg: s__(
'AlertManagement|There was an error displaying the alert. Please refresh the page to try again.', 'AlertManagement|There was an error displaying the alert. Please refresh the page to try again.',
...@@ -49,13 +40,12 @@ export default { ...@@ -49,13 +40,12 @@ export default {
GlIcon, GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlDropdown,
GlDropdownItem,
GlTab, GlTab,
GlTabs, GlTabs,
GlButton, GlButton,
GlTable, GlTable,
TimeAgoTooltip, TimeAgoTooltip,
AlertSidebar,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
props: { props: {
...@@ -98,6 +88,8 @@ export default { ...@@ -98,6 +88,8 @@ export default {
isErrorDismissed: false, isErrorDismissed: false,
createIssueError: '', createIssueError: '',
issueCreationInProgress: false, issueCreationInProgress: false,
sidebarCollapsed: false,
sidebarErrorMessage: '',
}; };
}, },
computed: { computed: {
...@@ -115,32 +107,27 @@ export default { ...@@ -115,32 +107,27 @@ export default {
}, },
mounted() { mounted() {
this.trackPageViews(); this.trackPageViews();
toggleContainerClasses(containerEl, {
'issuable-bulk-update-sidebar': true,
'right-sidebar-expanded': true,
});
}, },
methods: { methods: {
dismissError() { dismissError() {
this.isErrorDismissed = true; this.isErrorDismissed = true;
this.sidebarErrorMessage = '';
}, },
updateAlertStatus(status) { toggleSidebar() {
this.$apollo this.sidebarCollapsed = !this.sidebarCollapsed;
.mutate({ toggleContainerClasses(containerEl, {
mutation: updateAlertStatus, 'right-sidebar-collapsed': this.sidebarCollapsed,
variables: { 'right-sidebar-expanded': !this.sidebarCollapsed,
iid: this.alertId,
status: status.toUpperCase(),
projectPath: this.projectPath,
},
})
.then(() => {
this.trackStatusUpdate(status);
})
.catch(() => {
createFlash(
s__(
'AlertManagement|There was an error while updating the status of the alert. Please try again.',
),
);
}); });
}, },
handleAlertSidebarError(errorMessage) {
this.errored = true;
this.sidebarErrorMessage = errorMessage;
},
createIssue() { createIssue() {
this.issueCreationInProgress = true; this.issueCreationInProgress = true;
...@@ -172,17 +159,14 @@ export default { ...@@ -172,17 +159,14 @@ export default {
const { category, action } = trackAlertsDetailsViewsOptions; const { category, action } = trackAlertsDetailsViewsOptions;
Tracking.event(category, action); Tracking.event(category, action);
}, },
trackStatusUpdate(status) {
const { category, action, label } = trackAlertStatusUpdateOptions;
Tracking.event(category, action, { label, property: status });
},
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
<gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError"> <gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError">
{{ $options.i18n.errorMsg }} {{ sidebarErrorMessage || $options.i18n.errorMsg }}
</gl-alert> </gl-alert>
<gl-alert <gl-alert
v-if="createIssueError" v-if="createIssueError"
...@@ -243,6 +227,16 @@ export default { ...@@ -243,6 +227,16 @@ export default {
{{ s__('AlertManagement|Create issue') }} {{ s__('AlertManagement|Create issue') }}
</gl-button> </gl-button>
</div> </div>
<gl-button
:aria-label="__('Toggle sidebar')"
category="primary"
variant="default"
class="d-sm-none position-absolute toggle-sidebar-mobile-button"
type="button"
@click="toggleSidebar"
>
<i class="fa fa-angle-double-left"></i>
</gl-button>
</div> </div>
<div <div
v-if="alert" v-if="alert"
...@@ -250,24 +244,6 @@ export default { ...@@ -250,24 +244,6 @@ export default {
> >
<h2 data-testid="title">{{ alert.title }}</h2> <h2 data-testid="title">{{ alert.title }}</h2>
</div> </div>
<gl-dropdown :text="$options.statuses[alert.status]" class="gl-absolute gl-right-0" right>
<gl-dropdown-item
v-for="(label, field) in $options.statuses"
:key="field"
data-testid="statusDropdownItem"
class="gl-vertical-align-middle"
@click="updateAlertStatus(label)"
>
<span class="d-flex">
<gl-icon
class="flex-shrink-0 append-right-4"
:class="{ invisible: label.toUpperCase() !== alert.status }"
name="mobile-issue-close"
/>
{{ label }}
</span>
</gl-dropdown-item>
</gl-dropdown>
<gl-tabs v-if="alert" data-testid="alertDetailsTabs"> <gl-tabs v-if="alert" data-testid="alertDetailsTabs">
<gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle"> <gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle">
<ul class="pl-4 mb-n1"> <ul class="pl-4 mb-n1">
...@@ -306,6 +282,13 @@ export default { ...@@ -306,6 +282,13 @@ export default {
</gl-table> </gl-table>
</gl-tab> </gl-tab>
</gl-tabs> </gl-tabs>
<alert-sidebar
:project-path="projectPath"
:alert="alert"
:sidebar-collapsed="sidebarCollapsed"
@toggle-sidebar="toggleSidebar"
@alert-sidebar-error="handleAlertSidebarError"
/>
</div> </div>
</div> </div>
</template> </template>
<script>
import SidebarHeader from './sidebar/sidebar_header.vue';
import SidebarTodo from './sidebar/sidebar_todo.vue';
import SidebarStatus from './sidebar/sidebar_status.vue';
export default {
components: {
SidebarHeader,
SidebarTodo,
SidebarStatus,
},
props: {
sidebarCollapsed: {
type: Boolean,
required: true,
},
projectPath: {
type: String,
required: true,
},
alert: {
type: Object,
required: true,
},
},
computed: {
sidebarCollapsedClass() {
return this.sidebarCollapsed ? 'right-sidebar-collapsed' : 'right-sidebar-expanded';
},
},
methods: {
handleAlertSidebarError(errorMessage) {
this.$emit('alert-sidebar-error', errorMessage);
},
},
};
</script>
<template>
<aside :class="sidebarCollapsedClass" class="right-sidebar alert-sidebar">
<div class="issuable-sidebar js-issuable-update">
<sidebar-header
:sidebar-collapsed="sidebarCollapsed"
@toggle-sidebar="$emit('toggle-sidebar')"
/>
<sidebar-todo v-if="sidebarCollapsed" :sidebar-collapsed="sidebarCollapsed" />
<sidebar-status
:project-path="projectPath"
:alert="alert"
@toggle-sidebar="$emit('toggle-sidebar')"
@alert-sidebar-error="handleAlertSidebarError"
/>
<!-- TODO: Remove after adding extra attribute blocks to sidebar -->
<div class="block"></div>
</div>
</aside>
</template>
<script>
import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
import SidebarTodo from './sidebar_todo.vue';
export default {
components: {
ToggleSidebar,
SidebarTodo,
},
props: {
sidebarCollapsed: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<div class="block">
<span class="issuable-header-text hide-collapsed float-left">
{{ __('Quick actions') }}
</span>
<toggle-sidebar
:collapsed="sidebarCollapsed"
css-classes="float-right"
@toggle="$emit('toggle-sidebar')"
/>
<!-- TODO: Implement after or as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/215946 -->
<template v-if="false">
<sidebar-todo v-if="!sidebarCollapsed" :sidebar-collapsed="sidebarCollapsed" />
</template>
</div>
</template>
<script>
import {
GlIcon,
GlDropdown,
GlDropdownItem,
GlLoadingIcon,
GlTooltip,
GlButton,
GlSprintf,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import { trackAlertStatusUpdateOptions } from '../../constants';
import updateAlertStatus from '../../graphql/mutations/update_alert_status.graphql';
export default {
statuses: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
components: {
GlIcon,
GlDropdown,
GlDropdownItem,
GlLoadingIcon,
GlTooltip,
GlButton,
GlSprintf,
},
props: {
projectPath: {
type: String,
required: true,
},
alert: {
type: Object,
required: true,
},
isEditable: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
isDropdownShowing: false,
isUpdating: false,
};
},
computed: {
dropdownClass() {
return this.isDropdownShowing ? 'show' : 'd-none';
},
},
methods: {
hideDropdown() {
this.isDropdownShowing = false;
},
toggleFormDropdown() {
this.isDropdownShowing = !this.isDropdownShowing;
const { dropdown } = this.$refs.dropdown.$refs;
if (dropdown && this.isDropdownShowing) {
dropdown.show();
}
},
isSelected(status) {
return this.alert.status === status;
},
updateAlertStatus(status) {
this.isUpdating = true;
this.$apollo
.mutate({
mutation: updateAlertStatus,
variables: {
iid: this.alert.iid,
status: status.toUpperCase(),
projectPath: this.projectPath,
},
})
.then(() => {
this.trackStatusUpdate(status);
this.hideDropdown();
})
.catch(() => {
this.$emit(
'alert-sidebar-error',
s__(
'AlertManagement|There was an error while updating the status of the alert. Please try again.',
),
);
})
.finally(() => {
this.isUpdating = false;
});
},
trackStatusUpdate(status) {
const { category, action, label } = trackAlertStatusUpdateOptions;
Tracking.event(category, action, { label, property: status });
},
},
};
</script>
<template>
<div class="block alert-status">
<div ref="status" class="sidebar-collapsed-icon" @click="$emit('toggle-sidebar')">
<gl-icon name="status" :size="14" />
<gl-loading-icon v-if="isUpdating" />
</div>
<gl-tooltip :target="() => $refs.status" boundary="viewport" placement="left">
<gl-sprintf :message="s__('AlertManagement|Alert status: %{status}')">
<template #status>
{{ alert.status.toLowerCase() }}
</template>
</gl-sprintf>
</gl-tooltip>
<div class="hide-collapsed">
<p class="title gl-display-flex justify-content-between">
{{ s__('AlertManagement|Status') }}
<a
v-if="isEditable"
ref="editButton"
class="btn-link"
href="#"
@click="toggleFormDropdown"
@keydown.esc="hideDropdown"
>
{{ s__('AlertManagement|Edit') }}
</a>
</p>
<div class="dropdown dropdown-menu-selectable" :class="dropdownClass">
<gl-dropdown
ref="dropdown"
:text="$options.statuses[alert.status]"
class="w-100"
toggle-class="dropdown-menu-toggle"
variant="outline-default"
@keydown.esc.native="hideDropdown"
@hide="hideDropdown"
>
<div class="dropdown-title">
<span class="alert-title">{{ s__('AlertManagement|Assign status') }}</span>
<gl-button
:aria-label="__('Close')"
variant="link"
class="dropdown-title-button dropdown-menu-close"
icon="close"
@click="hideDropdown"
/>
</div>
<div class="dropdown-content dropdown-body">
<gl-dropdown-item
v-for="(label, field) in $options.statuses"
:key="field"
data-testid="statusDropdownItem"
class="gl-vertical-align-middle"
:active="label.toUpperCase() === alert.status"
:active-class="'is-active'"
@click="updateAlertStatus(label)"
>
{{ label }}
</gl-dropdown-item>
</div>
</gl-dropdown>
</div>
<gl-loading-icon v-if="isUpdating" :inline="true" />
<p
v-else-if="!isDropdownShowing"
class="value m-0"
:class="{ 'no-value': !$options.statuses[alert.status] }"
>
<span v-if="$options.statuses[alert.status]" class="gl-text-gray-700">{{
$options.statuses[alert.status]
}}</span>
<span v-else>
{{ s__('AlertManagement|None') }}
</span>
</p>
</div>
</div>
</template>
<script>
import Todo from '~/sidebar/components/todo_toggle/todo.vue';
export default {
components: {
Todo,
},
props: {
sidebarCollapsed: {
type: Boolean,
required: true,
},
},
};
</script>
<!-- TODO: Implement after or as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/215946 -->
<template>
<div v-if="false" :class="{ 'block todo': sidebarCollapsed }">
<todo
:collapsed="sidebarCollapsed"
:issuable-id="1"
:is-todo="false"
:is-action-active="false"
issuable-type="alert"
@toggleTodo="() => {}"
/>
</div>
</template>
...@@ -12,3 +12,16 @@ export const canScrollUp = ({ scrollTop }, margin = 0) => scrollTop > margin; ...@@ -12,3 +12,16 @@ export const canScrollUp = ({ scrollTop }, margin = 0) => scrollTop > margin;
export const canScrollDown = ({ scrollTop, offsetHeight, scrollHeight }, margin = 0) => export const canScrollDown = ({ scrollTop, offsetHeight, scrollHeight }, margin = 0) =>
scrollTop + offsetHeight < scrollHeight - margin; scrollTop + offsetHeight < scrollHeight - margin;
export const toggleContainerClasses = (containerEl, classList) => {
if (containerEl) {
// eslint-disable-next-line array-callback-return
Object.entries(classList).map(([key, value]) => {
if (value) {
containerEl.classList.add(key);
} else {
containerEl.classList.remove(key);
}
});
}
};
...@@ -39,4 +39,14 @@ ...@@ -39,4 +39,14 @@
width: 100%; width: 100%;
} }
} }
.toggle-sidebar-mobile-button {
right: 0;
}
.dropdown-menu-toggle {
&:hover {
background-color: $white;
}
}
} }
---
title: Assign alerts sidebar base
merge_request 32642:
author:
type: changed
---
title: Assign alerts sidebar container fix
merge_request: 32743
author:
type: other
...@@ -1775,12 +1775,18 @@ msgstr "" ...@@ -1775,12 +1775,18 @@ msgstr ""
msgid "AlertManagement|Alert details" msgid "AlertManagement|Alert details"
msgstr "" msgstr ""
msgid "AlertManagement|Alert status: %{status}"
msgstr ""
msgid "AlertManagement|Alerts" msgid "AlertManagement|Alerts"
msgstr "" msgstr ""
msgid "AlertManagement|All alerts" msgid "AlertManagement|All alerts"
msgstr "" msgstr ""
msgid "AlertManagement|Assign status"
msgstr ""
msgid "AlertManagement|Authorize external service" msgid "AlertManagement|Authorize external service"
msgstr "" msgstr ""
...@@ -1793,6 +1799,9 @@ msgstr "" ...@@ -1793,6 +1799,9 @@ msgstr ""
msgid "AlertManagement|Display alerts from all your monitoring tools directly within GitLab. Streamline the investigation of your alerts and the escalation of alerts to incidents." msgid "AlertManagement|Display alerts from all your monitoring tools directly within GitLab. Streamline the investigation of your alerts and the escalation of alerts to incidents."
msgstr "" msgstr ""
msgid "AlertManagement|Edit"
msgstr ""
msgid "AlertManagement|End time" msgid "AlertManagement|End time"
msgstr "" msgstr ""
...@@ -1823,6 +1832,9 @@ msgstr "" ...@@ -1823,6 +1832,9 @@ msgstr ""
msgid "AlertManagement|No alerts to display." msgid "AlertManagement|No alerts to display."
msgstr "" msgstr ""
msgid "AlertManagement|None"
msgstr ""
msgid "AlertManagement|Open" msgid "AlertManagement|Open"
msgstr "" msgstr ""
...@@ -17638,6 +17650,9 @@ msgstr "" ...@@ -17638,6 +17650,9 @@ msgstr ""
msgid "Queued" msgid "Queued"
msgstr "" msgstr ""
msgid "Quick actions"
msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes." msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "" msgstr ""
......
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import { GlAlert, GlLoadingIcon, GlDropdownItem, GlTable } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon, GlTable } from '@gitlab/ui';
import AlertDetails from '~/alert_management/components/alert_details.vue'; import AlertDetails from '~/alert_management/components/alert_details.vue';
import updateAlertStatus from '~/alert_management/graphql/mutations/update_alert_status.graphql';
import createIssueQuery from '~/alert_management/graphql/mutations/create_issue_from_alert.graphql'; import createIssueQuery from '~/alert_management/graphql/mutations/create_issue_from_alert.graphql';
import createFlash from '~/flash';
import { joinPaths } from '~/lib/utils/url_utility'; import { joinPaths } from '~/lib/utils/url_utility';
import { import { trackAlertsDetailsViewsOptions } from '~/alert_management/constants';
trackAlertsDetailsViewsOptions,
trackAlertStatusUpdateOptions,
} from '~/alert_management/constants';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import mockAlerts from '../mocks/alerts.json'; import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0]; const mockAlert = mockAlerts[0];
jest.mock('~/flash');
describe('AlertDetails', () => { describe('AlertDetails', () => {
let wrapper; let wrapper;
const projectPath = 'root/alerts'; const projectPath = 'root/alerts';
const projectIssuesPath = 'root/alerts/-/issues'; const projectIssuesPath = 'root/alerts/-/issues';
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
const findDetailsTable = () => wrapper.find(GlTable); const findDetailsTable = () => wrapper.find(GlTable);
function mountComponent({ function mountComponent({
...@@ -258,52 +251,6 @@ describe('AlertDetails', () => { ...@@ -258,52 +251,6 @@ describe('AlertDetails', () => {
}); });
}); });
describe('Updating the alert status', () => {
const mockUpdatedMutationResult = {
data: {
updateAlertStatus: {
errors: [],
alert: {
status: 'acknowledged',
},
},
},
};
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alert: mockAlert },
loading: false,
});
});
it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findStatusDropdownItem().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateAlertStatus,
variables: {
iid: 'alertId',
status: 'TRIGGERED',
projectPath,
},
});
});
it('calls `createFlash` when request fails', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
findStatusDropdownItem().vm.$emit('click');
setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error while updating the status of the alert. Please try again.',
);
});
});
});
describe('Snowplow tracking', () => { describe('Snowplow tracking', () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(Tracking, 'event'); jest.spyOn(Tracking, 'event');
...@@ -318,16 +265,5 @@ describe('AlertDetails', () => { ...@@ -318,16 +265,5 @@ describe('AlertDetails', () => {
const { category, action } = trackAlertsDetailsViewsOptions; const { category, action } = trackAlertsDetailsViewsOptions;
expect(Tracking.event).toHaveBeenCalledWith(category, action); expect(Tracking.event).toHaveBeenCalledWith(category, action);
}); });
it('should track alert status updates', () => {
Tracking.event.mockClear();
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
findStatusDropdownItem().vm.$emit('click');
const status = findStatusDropdownItem().text();
setImmediate(() => {
const { category, action, label } = trackAlertStatusUpdateOptions;
expect(Tracking.event).toHaveBeenCalledWith(category, action, { label, property: status });
});
});
}); });
}); });
import { shallowMount } from '@vue/test-utils';
import AlertSidebar from '~/alert_management/components/alert_sidebar.vue';
describe('Alert Details Sidebar', () => {
let wrapper;
function mountComponent({
sidebarCollapsed = true,
mountMethod = shallowMount,
stubs = {},
} = {}) {
wrapper = mountMethod(AlertSidebar, {
propsData: {
alert: {},
sidebarCollapsed,
projectPath: 'projectPath',
},
stubs,
});
}
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
describe('the sidebar renders', () => {
beforeEach(() => {
mountComponent();
});
it('open as default', () => {
expect(wrapper.props('sidebarCollapsed')).toBe(true);
});
});
});
import { shallowMount } from '@vue/test-utils';
import { 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.graphql';
import Tracking from '~/tracking';
import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0];
describe('Alert Details Sidebar Status', () => {
let wrapper;
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
const findStatusLoadingIcon = () => wrapper.find(GlLoadingIcon);
function mountComponent({
data,
sidebarCollapsed = true,
loading = false,
mountMethod = shallowMount,
stubs = {},
} = {}) {
wrapper = mountMethod(AlertSidebarStatus, {
propsData: {
alert: { ...mockAlert },
...data,
sidebarCollapsed,
projectPath: 'projectPath',
},
mocks: {
$apollo: {
mutate: jest.fn(),
queries: {
alert: {
loading,
},
},
},
},
stubs,
});
}
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
describe('updating the alert status', () => {
const mockUpdatedMutationResult = {
data: {
updateAlertStatus: {
errors: [],
alert: {
status: 'acknowledged',
},
},
},
};
beforeEach(() => {
mountComponent({
data: { alert: mockAlert },
sidebarCollapsed: false,
loading: false,
});
});
it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findStatusDropdownItem().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateAlertStatus,
variables: {
iid: '1527542',
status: 'TRIGGERED',
projectPath: 'projectPath',
},
});
});
it('stops updating when the request fails', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
findStatusDropdownItem().vm.$emit('click');
expect(findStatusLoadingIcon().exists()).toBe(false);
expect(wrapper.find('.gl-text-gray-700').text()).toBe('Triggered');
});
});
describe('Snowplow tracking', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alert: mockAlert },
loading: false,
});
});
it('should track alert status updates', () => {
Tracking.event.mockClear();
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
findStatusDropdownItem().vm.$emit('click');
const status = findStatusDropdownItem().text();
setImmediate(() => {
const { category, action, label } = trackAlertStatusUpdateOptions;
expect(Tracking.event).toHaveBeenCalledWith(category, action, { label, property: status });
});
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment