diff options
Diffstat (limited to 'spec/frontend/issues/show/components/incidents')
6 files changed, 161 insertions, 36 deletions
diff --git a/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js index 4c1638a9147..81c3c30bf8a 100644 --- a/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js +++ b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js @@ -40,5 +40,13 @@ describe('Edit Timeline events', () => { expect(wrapper.emitted()).toEqual(cancelEvent); }); + + it('should emit the delete event', async () => { + const deleteEvent = { delete: [[]] }; + + await findTimelineEventsForm().vm.$emit('delete'); + + expect(wrapper.emitted()).toEqual(deleteEvent); + }); }); }); diff --git a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js index 458c1c3f858..33a3a6eddfc 100644 --- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js +++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js @@ -1,10 +1,11 @@ -import { GlTab } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import merge from 'lodash/merge'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; import DescriptionComponent from '~/issues/show/components/description.vue'; import HighlightBar from '~/issues/show/components/incidents/highlight_bar.vue'; -import IncidentTabs from '~/issues/show/components/incidents/incident_tabs.vue'; +import IncidentTabs, { + incidentTabsI18n, +} from '~/issues/show/components/incidents/incident_tabs.vue'; import INVALID_URL from '~/lib/utils/invalid_url'; import Tracking from '~/tracking'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; @@ -16,11 +17,24 @@ const mockAlert = { iid: '1', }; +const defaultMocks = { + $apollo: { + queries: { + alert: { + loading: true, + }, + timelineEvents: { + loading: false, + }, + }, + }, +}; + describe('Incident Tabs component', () => { let wrapper; - const mountComponent = (data = {}, options = {}) => { - wrapper = shallowMount( + const mountComponent = ({ data = {}, options = {}, mount = shallowMountExtended } = {}) => { + wrapper = mount( IncidentTabs, merge( { @@ -29,7 +43,7 @@ describe('Incident Tabs component', () => { }, stubs: { DescriptionComponent: true, - MetricsTab: true, + IncidentMetricTab: true, }, provide: { fullPath: '', @@ -37,41 +51,37 @@ describe('Incident Tabs component', () => { projectId: '', issuableId: '', uploadMetricsFeatureAvailable: true, + slaFeatureAvailable: true, + canUpdate: true, + canUpdateTimelineEvent: true, }, data() { return { alert: mockAlert, ...data }; }, - mocks: { - $apollo: { - queries: { - alert: { - loading: true, - }, - timelineEvents: { - loading: false, - }, - }, - }, - }, + mocks: defaultMocks, }, options, ), ); }; - const findTabs = () => wrapper.findAllComponents(GlTab); - const findSummaryTab = () => findTabs().at(0); - const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]'); + const findSummaryTab = () => wrapper.findByTestId('summary-tab'); + const findTimelineTab = () => wrapper.findByTestId('timeline-tab'); + const findAlertDetailsTab = () => wrapper.findByTestId('alert-details-tab'); const findAlertDetailsComponent = () => wrapper.findComponent(AlertDetailsTable); const findDescriptionComponent = () => wrapper.findComponent(DescriptionComponent); const findHighlightBarComponent = () => wrapper.findComponent(HighlightBar); + const findTabButtonByFilter = (filter) => wrapper.findAllByRole('tab').filter(filter); + const findTimelineTabButton = () => + findTabButtonByFilter((inner) => inner.text() === incidentTabsI18n.timelineTitle).at(0); + const findActiveTabs = () => findTabButtonByFilter((inner) => inner.classes('active')); - describe('empty state', () => { + describe('with no alerts', () => { beforeEach(() => { - mountComponent({ alert: null }); + mountComponent({ data: { alert: null } }); }); - it('does not show the alert details tab', () => { + it('does not show the alert details tab option', () => { expect(findAlertDetailsComponent().exists()).toBe(false); }); }); @@ -83,7 +93,12 @@ describe('Incident Tabs component', () => { it('renders the summary tab', () => { expect(findSummaryTab().exists()).toBe(true); - expect(findSummaryTab().attributes('title')).toBe('Summary'); + expect(findSummaryTab().attributes('title')).toBe(incidentTabsI18n.summaryTitle); + }); + + it('renders the timeline tab', () => { + expect(findTimelineTab().exists()).toBe(true); + expect(findTimelineTab().attributes('title')).toBe(incidentTabsI18n.timelineTitle); }); it('renders the alert details tab', () => { @@ -125,4 +140,22 @@ describe('Incident Tabs component', () => { expect(Tracking.event).toHaveBeenCalledWith(category, action); }); }); + + describe('tab changing', () => { + beforeEach(() => { + mountComponent({ mount: mountExtended }); + }); + + it('shows only the summary tab by default', async () => { + expect(findActiveTabs()).toHaveLength(1); + expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.summaryTitle); + }); + + it("shows the timeline tab after it's clicked", async () => { + await findTimelineTabButton().trigger('click'); + + expect(findActiveTabs()).toHaveLength(1); + expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle); + }); + }); }); diff --git a/spec/frontend/issues/show/components/incidents/mock_data.js b/spec/frontend/issues/show/components/incidents/mock_data.js index adea2b6df59..9accfcea791 100644 --- a/spec/frontend/issues/show/components/incidents/mock_data.js +++ b/spec/frontend/issues/show/components/incidents/mock_data.js @@ -13,6 +13,9 @@ export const mockEvents = [ noteHtml: '<p>Dummy event 1</p>', occurredAt: '2022-03-22T15:59:00Z', updatedAt: '2022-03-22T15:59:08Z', + timelineEventTags: { + nodes: [], + }, __typename: 'TimelineEventType', }, { @@ -29,6 +32,18 @@ export const mockEvents = [ noteHtml: '<p>Dummy event 2</p>', occurredAt: '2022-03-23T14:57:00Z', updatedAt: '2022-03-23T14:57:08Z', + timelineEventTags: { + nodes: [ + { + id: 'gid://gitlab/IncidentManagement::TimelineEvent/132', + name: 'Start time', + }, + { + id: 'gid://gitlab/IncidentManagement::TimelineEvent/132', + name: 'End time', + }, + ], + }, __typename: 'TimelineEventType', }, { @@ -45,6 +60,9 @@ export const mockEvents = [ noteHtml: '<p>Dummy event 3</p>', occurredAt: '2022-03-23T15:59:00Z', updatedAt: '2022-03-23T15:59:08Z', + timelineEventTags: { + nodes: [], + }, __typename: 'TimelineEventType', }, ]; @@ -152,6 +170,9 @@ export const mockGetTimelineData = { action: 'comment', occurredAt: '2022-07-01T12:47:00Z', createdAt: '2022-07-20T12:47:40Z', + timelineEventTags: { + nodes: [], + }, }, ], }, diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js index 0ce3f75f576..d5b199cc790 100644 --- a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js +++ b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js @@ -22,11 +22,12 @@ describe('Timeline events form', () => { useFakeDate(fakeDate); let wrapper; - const mountComponent = ({ mountMethod = shallowMountExtended } = {}) => { + const mountComponent = ({ mountMethod = shallowMountExtended } = {}, props = {}) => { wrapper = mountMethod(TimelineEventsForm, { propsData: { showSaveAndAdd: true, isEventProcessed: false, + ...props, }, stubs: { GlButton: true, @@ -43,6 +44,7 @@ describe('Timeline events form', () => { const findSubmitButton = () => wrapper.findByText(timelineFormI18n.save); const findSubmitAndAddButton = () => wrapper.findByText(timelineFormI18n.saveAndAdd); const findCancelButton = () => wrapper.findByText(timelineFormI18n.cancel); + const findDeleteButton = () => wrapper.findByText(timelineFormI18n.delete); const findDatePicker = () => wrapper.findComponent(GlDatepicker); const findHourInput = () => wrapper.findByTestId('input-hours'); const findMinuteInput = () => wrapper.findByTestId('input-minutes'); @@ -68,6 +70,9 @@ describe('Timeline events form', () => { findCancelButton().vm.$emit('click'); await waitForPromises(); }; + const deleteForm = () => { + findDeleteButton().vm.$emit('click'); + }; it('renders markdown-field component with correct list of toolbar items', () => { mountComponent({ mountMethod: mountExtended }); @@ -165,4 +170,38 @@ describe('Timeline events form', () => { expect(findSubmitAndAddButton().props('disabled')).toBe(true); }); }); + + describe('Delete button', () => { + it('does not show the delete button if showDelete prop is false', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: false }); + + expect(findDeleteButton().exists()).toBe(false); + }); + + it('shows the delete button if showDelete prop is true', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true }); + + expect(findDeleteButton().exists()).toBe(true); + }); + + it('disables the delete button if isEventProcessed prop is true', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true }); + + expect(findDeleteButton().props('disabled')).toBe(true); + }); + + it('does not disable the delete button if isEventProcessed prop is false', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: false }); + + expect(findDeleteButton().props('disabled')).toBe(false); + }); + + it('emits delete event on click', () => { + mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true }); + + deleteForm(); + + expect(wrapper.emitted('delete')).toEqual([[]]); + }); + }); }); diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js index 1bf8d68efd4..ba0527e5395 100644 --- a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js +++ b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js @@ -1,5 +1,5 @@ import timezoneMock from 'timezone-mock'; -import { GlIcon, GlDropdown } from '@gitlab/ui'; +import { GlIcon, GlDropdown, GlBadge } from '@gitlab/ui'; import { nextTick } from 'vue'; import { timelineItemI18n } from '~/issues/show/components/incidents/constants'; import { mountExtended } from 'helpers/vue_test_utils_helper'; @@ -27,25 +27,24 @@ describe('IncidentTimelineEventList', () => { const findCommentIcon = () => wrapper.findComponent(GlIcon); const findEventTime = () => wrapper.findByTestId('event-time'); + const findEventTag = () => wrapper.findComponent(GlBadge); const findDropdown = () => wrapper.findComponent(GlDropdown); const findDeleteButton = () => wrapper.findByText(timelineItemI18n.delete); describe('template', () => { - it('shows comment icon', () => { + beforeEach(() => { mountComponent(); + }); + it('shows comment icon', () => { expect(findCommentIcon().exists()).toBe(true); }); it('sets correct props for icon', () => { - mountComponent(); - expect(findCommentIcon().props('name')).toBe(mockEvents[0].action); }); it('displays the correct time', () => { - mountComponent(); - expect(findEventTime().text()).toBe('15:59 UTC'); }); @@ -58,8 +57,6 @@ describe('IncidentTimelineEventList', () => { describe(timezone, () => { beforeEach(() => { timezoneMock.register(timezone); - - mountComponent(); }); afterEach(() => { @@ -72,10 +69,20 @@ describe('IncidentTimelineEventList', () => { }); }); + describe('timeline event tag', () => { + it('does not show when tag is not provided', () => { + expect(findEventTag().exists()).toBe(false); + }); + + it('shows when tag is provided', () => { + mountComponent({ propsData: { eventTag: 'Start time' } }); + + expect(findEventTag().exists()).toBe(true); + }); + }); + describe('action dropdown', () => { it('does not show the action dropdown by default', () => { - mountComponent(); - expect(findDropdown().exists()).toBe(false); expect(findDeleteButton().exists()).toBe(false); }); diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js index dff1c429d07..a7250e8ad0d 100644 --- a/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js +++ b/spec/frontend/issues/show/components/incidents/timeline_events_list_spec.js @@ -92,6 +92,9 @@ describe('IncidentTimelineEventList', () => { expect(findItems().at(1).props('occurredAt')).toBe(mockEvents[1].occurredAt); expect(findItems().at(1).props('action')).toBe(mockEvents[1].action); expect(findItems().at(1).props('noteHtml')).toBe(mockEvents[1].noteHtml); + expect(findItems().at(1).props('eventTag')).toBe( + mockEvents[1].timelineEventTags.nodes[0].name, + ); }); it('formats dates correctly', () => { @@ -120,6 +123,20 @@ describe('IncidentTimelineEventList', () => { }); }); + describe('getFirstTag', () => { + it('returns undefined, when timelineEventTags contains an empty array', () => { + const returnedTag = wrapper.vm.getFirstTag(mockEvents[0].timelineEventTags); + + expect(returnedTag).toEqual(undefined); + }); + + it('returns the first string, when timelineEventTags contains array with at least one tag', () => { + const returnedTag = wrapper.vm.getFirstTag(mockEvents[1].timelineEventTags); + + expect(returnedTag).toBe(mockEvents[1].timelineEventTags.nodes[0].name); + }); + }); + describe('delete functionality', () => { beforeEach(() => { mockConfirmAction({ confirmed: true }); |