diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-18 19:00:14 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-18 19:00:14 +0000 |
commit | 05f0ebba3a2c8ddf39e436f412dc2ab5bf1353b2 (patch) | |
tree | 11d0f2a6ec31c7793c184106cedc2ded3d9a2cc5 /spec/frontend/issues/show | |
parent | ec73467c23693d0db63a797d10194da9e72a74af (diff) | |
download | gitlab-ce-05f0ebba3a2c8ddf39e436f412dc2ab5bf1353b2.tar.gz |
Add latest changes from gitlab-org/gitlab@15-8-stable-eev15.8.0-rc42
Diffstat (limited to 'spec/frontend/issues/show')
4 files changed, 158 insertions, 27 deletions
diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js index 7d6ca44e679..aaf228ae181 100644 --- a/spec/frontend/issues/show/components/header_actions_spec.js +++ b/spec/frontend/issues/show/components/header_actions_spec.js @@ -6,6 +6,7 @@ import { mockTracking } from 'helpers/tracking_helper'; import { createAlert, VARIANT_SUCCESS } from '~/flash'; import { IssuableStatus, IssueType } from '~/issues/constants'; import DeleteIssueModal from '~/issues/show/components/delete_issue_modal.vue'; +import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; import HeaderActions from '~/issues/show/components/header_actions.vue'; import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants'; import promoteToEpicMutation from '~/issues/show/queries/promote_to_epic.mutation.graphql'; @@ -38,8 +39,9 @@ describe('HeaderActions component', () => { issueType: IssueType.Issue, newIssuePath: 'gitlab-org/gitlab-test/-/issues/new', projectPath: 'gitlab-org/gitlab-test', - reportAbusePath: - '-/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%2Fgitlab-org%2Fgitlab-test%2F-%2Fissues%2F32&user_id=1', + reportAbusePath: '-/abuse_reports/add_category', + reportedUserId: '1', + reportedFromUrl: 'http://localhost:/gitlab-org/-/issues/32', submitAsSpamPath: 'gitlab-org/gitlab-test/-/issues/32/submit_as_spam', }; @@ -401,4 +403,31 @@ describe('HeaderActions component', () => { }); }); }); + + describe('abuse category selector', () => { + const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector); + + beforeEach(() => { + wrapper = mountComponent({ props: { isIssueAuthor: false } }); + }); + + it('renders', () => { + expect(findAbuseCategorySelector().exists()).toBe(true); + expect(findAbuseCategorySelector().props('showDrawer')).toEqual(false); + }); + + it('opens the drawer', async () => { + findDesktopDropdownItems().at(2).vm.$emit('click'); + + await nextTick(); + + expect(findAbuseCategorySelector().props('showDrawer')).toEqual(true); + }); + + it('closes the drawer', async () => { + await findAbuseCategorySelector().vm.$emit('close-drawer'); + + expect(findAbuseCategorySelector().props('showDrawer')).toEqual(false); + }); + }); }); diff --git a/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js index 1286617d64a..6c923cae0cc 100644 --- a/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js +++ b/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js @@ -1,6 +1,6 @@ import VueApollo from 'vue-apollo'; import Vue from 'vue'; -import { GlDatepicker } from '@gitlab/ui'; +import { GlDatepicker, GlListboxItem } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import CreateTimelineEvent from '~/issues/show/components/incidents/create_timeline_event.vue'; @@ -27,6 +27,7 @@ const mockInputData = { incidentId: 'gid://gitlab/Issue/1', note: 'test', occurredAt: '2020-07-08T00:00:00.000Z', + timelineEventTagNames: ['Start time'], }; describe('Create Timeline events', () => { @@ -51,9 +52,14 @@ describe('Create Timeline events', () => { findHourInput().setValue(inputDate.getHours()); findMinuteInput().setValue(inputDate.getMinutes()); }; + const findListboxItems = () => wrapper.findAllComponents(GlListboxItem); + const setEventTags = () => { + findListboxItems().at(0).vm.$emit('select', true); + }; const fillForm = () => { setDatetime(); setNoteInput(); + setEventTags(); }; function createMockApolloProvider() { @@ -80,6 +86,7 @@ describe('Create Timeline events', () => { provide: { fullPath: 'group/project', issuableId: '1', + glFeatures: { incidentEventTags: true }, }, apolloProvider, }); diff --git a/spec/frontend/issues/show/components/incidents/mock_data.js b/spec/frontend/issues/show/components/incidents/mock_data.js index 9accfcea791..6606bed1567 100644 --- a/spec/frontend/issues/show/components/incidents/mock_data.js +++ b/spec/frontend/issues/show/components/incidents/mock_data.js @@ -74,6 +74,7 @@ const mockUpdatedEvent = { action: 'comment', occurredAt: '2022-07-01T12:47:00Z', createdAt: '2022-07-20T12:47:40Z', + timelineEventTags: [], }; export const timelineEventsQueryListResponse = { 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 d5b199cc790..f06d968a4c5 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 @@ -1,11 +1,15 @@ import VueApollo from 'vue-apollo'; import Vue, { nextTick } from 'vue'; -import { GlDatepicker } from '@gitlab/ui'; -import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; +import { GlDatepicker, GlListbox } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import TimelineEventsForm from '~/issues/show/components/incidents/timeline_events_form.vue'; import MarkdownField from '~/vue_shared/components/markdown/field.vue'; -import { timelineFormI18n } from '~/issues/show/components/incidents/constants'; +import { + timelineFormI18n, + TIMELINE_EVENT_TAGS, + timelineEventTagsI18n, +} from '~/issues/show/components/incidents/constants'; import { createAlert } from '~/flash'; import { useFakeDate } from 'helpers/fake_date'; @@ -17,17 +21,23 @@ const fakeDate = '2020-07-08T00:00:00.000Z'; const mockInputDate = new Date('2021-08-12'); +const mockTags = TIMELINE_EVENT_TAGS; + describe('Timeline events form', () => { // July 8 2020 useFakeDate(fakeDate); let wrapper; - const mountComponent = ({ mountMethod = shallowMountExtended } = {}, props = {}) => { + const mountComponent = ({ mountMethod = mountExtended } = {}, props = {}, glFeatures = {}) => { wrapper = mountMethod(TimelineEventsForm, { + provide: { + glFeatures, + }, propsData: { showSaveAndAdd: true, isEventProcessed: false, ...props, + tags: mockTags, }, stubs: { GlButton: true, @@ -35,6 +45,10 @@ describe('Timeline events form', () => { }); }; + beforeEach(() => { + mountComponent(); + }); + afterEach(() => { createAlert.mockReset(); wrapper.destroy(); @@ -48,16 +62,26 @@ describe('Timeline events form', () => { const findDatePicker = () => wrapper.findComponent(GlDatepicker); const findHourInput = () => wrapper.findByTestId('input-hours'); const findMinuteInput = () => wrapper.findByTestId('input-minutes'); - const setDatetime = () => { - findDatePicker().vm.$emit('input', mockInputDate); - findHourInput().setValue(5); - findMinuteInput().setValue(45); - }; + const findTagDropdown = () => wrapper.findComponent(GlListbox); const findTextarea = () => wrapper.findByTestId('input-note'); + const findTextareaValue = () => findTextarea().element.value; const findCountNumeric = (count) => wrapper.findByText(count); const findCountVerbose = (count) => wrapper.findByText(`${count} characters remaining`); const findCountHint = () => wrapper.findByText(timelineFormI18n.hint); + const setDatetime = () => { + findDatePicker().vm.$emit('input', mockInputDate); + findHourInput().setValue(5); + findMinuteInput().setValue(45); + }; + const selectTags = async (tags) => { + findTagDropdown().vm.$emit( + 'select', + tags.map((x) => x.value), + ); + await nextTick(); + }; + const selectOneTag = () => selectTags([mockTags[0]]); const submitForm = async () => { findSubmitButton().vm.$emit('click'); await waitForPromises(); @@ -90,23 +114,97 @@ describe('Timeline events form', () => { ]); }); - describe('form button behaviour', () => { + describe('with incident_event_tag feature flag enabled', () => { beforeEach(() => { - mountComponent({ mountMethod: mountExtended }); + mountComponent( + {}, + {}, + { + incidentEventTags: true, + }, + ); + }); + + describe('event tag dropdown', () => { + it('should render option list from provided array', () => { + expect(findTagDropdown().props('items')).toEqual(mockTags); + }); + + it('should allow to choose multiple tags', async () => { + await selectTags(mockTags); + + expect(findTagDropdown().props('selected')).toEqual(mockTags.map((x) => x.value)); + }); + + it('should show default option, when none is chosen', () => { + expect(findTagDropdown().props('toggleText')).toBe(timelineFormI18n.selectTags); + }); + + it('should show the tag, when one is selected', async () => { + await selectOneTag(); + + expect(findTagDropdown().props('toggleText')).toBe(timelineEventTagsI18n.startTime); + }); + + it('should show the number of selected tags, when more than one is selected', async () => { + await selectTags(mockTags); + + expect(findTagDropdown().props('toggleText')).toBe('2 tags'); + }); + + it('should be cleared when clear is triggered', async () => { + await selectTags(mockTags); + + // This component expects the parent to call `clear`, so this is the only way to trigger this + wrapper.vm.clear(); + await nextTick(); + + expect(findTagDropdown().props('toggleText')).toBe(timelineFormI18n.selectTags); + expect(findTagDropdown().props('selected')).toEqual([]); + }); + + it('should populate incident note with tags if a note was empty', async () => { + await selectTags(mockTags); + + expect(findTextareaValue()).toBe( + `${timelineFormI18n.areaDefaultMessage} ${mockTags + .map((x) => x.value.toLowerCase()) + .join(', ')}`, + ); + }); + + it('should populate incident note with tag but allow to customise it', async () => { + await selectOneTag(); + + await findTextarea().setValue('my customised event note'); + + await nextTick(); + + expect(findTextareaValue()).toBe('my customised event note'); + }); + + it('should not populate incident note with tag if it had a note', async () => { + await findTextarea().setValue('hello'); + await selectOneTag(); + + expect(findTextareaValue()).toBe('hello'); + }); }); + }); + describe('form button behaviour', () => { it('should save event on submit', async () => { await submitForm(); expect(wrapper.emitted()).toEqual({ - 'save-event': [[{ note: '', occurredAt: fakeDate }, false]], + 'save-event': [[{ note: '', occurredAt: fakeDate, timelineEventTags: [] }, false]], }); }); it('should save event on "submit and add another"', async () => { await submitFormAndAddAnother(); expect(wrapper.emitted()).toEqual({ - 'save-event': [[{ note: '', occurredAt: fakeDate }, true]], + 'save-event': [[{ note: '', occurredAt: fakeDate, timelineEventTags: [] }, true]], }); }); @@ -145,10 +243,6 @@ describe('Timeline events form', () => { }); describe('form character limit', () => { - beforeEach(() => { - mountComponent({ mountMethod: mountExtended }); - }); - it('sets a character limit hint', () => { expect(findCountHint().exists()).toBe(true); }); @@ -172,32 +266,32 @@ describe('Timeline events form', () => { }); describe('Delete button', () => { - it('does not show the delete button if showDelete prop is false', () => { - mountComponent({ mountMethod: mountExtended }, { showDelete: false }); + it('does not show the delete button if isEditing prop is false', () => { + mountComponent({ mountMethod: mountExtended }, { isEditing: false }); expect(findDeleteButton().exists()).toBe(false); }); - it('shows the delete button if showDelete prop is true', () => { - mountComponent({ mountMethod: mountExtended }, { showDelete: true }); + it('shows the delete button if isEditing prop is true', () => { + mountComponent({ mountMethod: mountExtended }, { isEditing: true }); expect(findDeleteButton().exists()).toBe(true); }); it('disables the delete button if isEventProcessed prop is true', () => { - mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true }); + mountComponent({ mountMethod: mountExtended }, { isEditing: 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 }); + mountComponent({ mountMethod: mountExtended }, { isEditing: true, isEventProcessed: false }); expect(findDeleteButton().props('disabled')).toBe(false); }); it('emits delete event on click', () => { - mountComponent({ mountMethod: mountExtended }, { showDelete: true, isEventProcessed: true }); + mountComponent({ mountMethod: mountExtended }, { isEditing: true, isEventProcessed: true }); deleteForm(); |