diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-21 00:08:59 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-21 00:08:59 +0000 |
commit | 367847e266036617e540e41b7fd3c7d03033800c (patch) | |
tree | a14547ad7556d48dcdeb977021f8cd89305ea026 /spec/frontend/issues | |
parent | 5d7e5a8902382caaffa616e1b496b684ba72d148 (diff) | |
download | gitlab-ce-367847e266036617e540e41b7fd3c7d03033800c.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/issues')
4 files changed, 269 insertions, 62 deletions
diff --git a/spec/frontend/issues/show/components/app_spec.js b/spec/frontend/issues/show/components/app_spec.js index 6cf44e60092..7c1d643dc0f 100644 --- a/spec/frontend/issues/show/components/app_spec.js +++ b/spec/frontend/issues/show/components/app_spec.js @@ -645,10 +645,10 @@ describe('Issuable output', () => { }); }); - describe('listItemReorder event', () => { + describe('saveDescription event', () => { it('makes request to update issue', async () => { const description = 'I have been updated!'; - findDescription().vm.$emit('listItemReorder', description); + findDescription().vm.$emit('saveDescription', description); await waitForPromises(); expect(mock.history.put[0].data).toContain(description); diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js index 889ff450825..9f36204139f 100644 --- a/spec/frontend/issues/show/components/description_spec.js +++ b/spec/frontend/issues/show/components/description_spec.js @@ -1,8 +1,7 @@ import $ from 'jquery'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -import { GlTooltip, GlModal } from '@gitlab/ui'; - +import { GlModal } from '@gitlab/ui'; import setWindowLocation from 'helpers/set_window_location_helper'; import { stubComponent } from 'helpers/stub_component'; import { TEST_HOST } from 'helpers/test_constants'; @@ -10,9 +9,8 @@ import { mockTracking } from 'helpers/tracking_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; - -import { createAlert } from '~/flash'; import Description from '~/issues/show/components/description.vue'; +import eventHub from '~/issues/show/event_hub'; import { updateHistory } from '~/lib/utils/url_utility'; import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import workItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql'; @@ -31,7 +29,6 @@ import { descriptionHtmlWithTask, } from '../mock_data/mock_data'; -jest.mock('~/flash'); jest.mock('~/lib/utils/url_utility', () => ({ ...jest.requireActual('~/lib/utils/url_utility'), updateHistory: jest.fn(), @@ -65,11 +62,8 @@ describe('Description component', () => { const findGfmContent = () => wrapper.find('[data-testid="gfm-content"]'); const findTextarea = () => wrapper.find('[data-testid="textarea"]'); - const findTaskActionButtons = () => wrapper.findAll('.js-add-task'); - const findConvertToTaskButton = () => wrapper.find('.js-add-task'); + const findTaskActionButtons = () => wrapper.findAll('.task-list-item-actions'); const findTaskLink = () => wrapper.find('a.gfm-issue'); - - const findTooltips = () => wrapper.findAllComponents(GlTooltip); const findModal = () => wrapper.findComponent(GlModal); const findWorkItemDetailModal = () => wrapper.findComponent(WorkItemDetailModal); @@ -125,10 +119,6 @@ describe('Description component', () => { } }); - afterEach(() => { - wrapper.destroy(); - }); - afterAll(() => { $('.issuable-meta .flash-container').remove(); }); @@ -311,13 +301,6 @@ describe('Description component', () => { expect(findTaskActionButtons()).toHaveLength(3); }); - it('renders a list of tooltips corresponding to checkboxes in description HTML', () => { - expect(findTooltips()).toHaveLength(3); - expect(findTooltips().at(0).props('target')).toBe( - findTaskActionButtons().at(0).attributes('id'), - ); - }); - it('does not show a modal by default', () => { expect(findModal().exists()).toBe(false); }); @@ -331,50 +314,29 @@ describe('Description component', () => { }); }); - describe('creating work item from checklist item', () => { - it('emits `updateDescription` after creating new work item', async () => { - createComponent({ - props: { - descriptionHtml: descriptionHtmlWithCheckboxes, - }, - provide: { - glFeatures: { - workItemsCreateFromMarkdown: true, - }, - }, - }); - - const newDescription = `<p>New description</p>`; + describe('task list item actions', () => { + describe('deleting the task list item', () => { + it('emits an event to update the description with the deleted task list item', () => { + const descriptionText = `Tasks - await findConvertToTaskButton().trigger('click'); +1. [ ] item 1 + 1. [ ] item 2 + 1. [ ] item 3 + 1. [ ] item 4;`; + const newDescriptionText = `Tasks - await waitForPromises(); +1. [ ] item 1 + 1. [ ] item 3 + 1. [ ] item 4;`; + createComponent({ + props: { descriptionText }, + provide: { glFeatures: { workItemsCreateFromMarkdown: true } }, + }); - expect(wrapper.emitted('updateDescription')).toEqual([[newDescription]]); - }); + eventHub.$emit('delete-task-list-item', '4:4-5:19'); - it('shows flash message when creating task fails', async () => { - createComponent({ - props: { - descriptionHtml: descriptionHtmlWithCheckboxes, - }, - provide: { - glFeatures: { - workItemsCreateFromMarkdown: true, - }, - }, - createWorkItemFromTaskHandler: jest.fn().mockRejectedValue({}), + expect(wrapper.emitted('saveDescription')).toEqual([[newDescriptionText]]); }); - - await findConvertToTaskButton().trigger('click'); - - await waitForPromises(); - - expect(createAlert).toHaveBeenCalledWith( - expect.objectContaining({ - message: 'Something went wrong when creating task. Please try again.', - }), - ); }); }); diff --git a/spec/frontend/issues/show/components/task_list_item_actions_spec.js b/spec/frontend/issues/show/components/task_list_item_actions_spec.js new file mode 100644 index 00000000000..d1879510d59 --- /dev/null +++ b/spec/frontend/issues/show/components/task_list_item_actions_spec.js @@ -0,0 +1,45 @@ +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import TaskListItemActions from '~/issues/show/components/task_list_item_actions.vue'; +import eventHub from '~/issues/show/event_hub'; + +describe('TaskListItemActions component', () => { + let wrapper; + + const findGlDropdown = () => wrapper.findComponent(GlDropdown); + const findGlDropdownItem = () => wrapper.findComponent(GlDropdownItem); + + const mountComponent = () => { + const li = document.createElement('li'); + li.dataset.sourcepos = '3:1-3:10'; + li.appendChild(document.createElement('div')); + document.body.appendChild(li); + + wrapper = shallowMount(TaskListItemActions, { + provide: { toggleClass: 'task-list-item-actions' }, + attachTo: document.querySelector('div'), + }); + }; + + beforeEach(() => { + mountComponent(); + }); + + it('renders dropdown', () => { + expect(findGlDropdown().props()).toMatchObject({ + category: 'tertiary', + icon: 'ellipsis_v', + right: true, + text: TaskListItemActions.i18n.taskActions, + textSrOnly: true, + }); + }); + + it('emits event when `Delete` dropdown item is clicked', () => { + jest.spyOn(eventHub, '$emit'); + + findGlDropdownItem().vm.$emit('click'); + + expect(eventHub.$emit).toHaveBeenCalledWith('delete-task-list-item', '3:1-3:10'); + }); +}); diff --git a/spec/frontend/issues/show/utils_spec.js b/spec/frontend/issues/show/utils_spec.js index 603fb5cc2a6..58966d51be2 100644 --- a/spec/frontend/issues/show/utils_spec.js +++ b/spec/frontend/issues/show/utils_spec.js @@ -1,4 +1,7 @@ -import { convertDescriptionWithNewSort } from '~/issues/show/utils'; +import { + convertDescriptionWithDeletedTaskListItem, + convertDescriptionWithNewSort, +} from '~/issues/show/utils'; describe('app/assets/javascripts/issues/show/utils.js', () => { describe('convertDescriptionWithNewSort', () => { @@ -137,4 +140,201 @@ describe('app/assets/javascripts/issues/show/utils.js', () => { expect(convertDescriptionWithNewSort(description, list.firstChild)).toBe(expected); }); }); + + describe('convertDescriptionWithDeletedTaskListItem', () => { + const description = `Tasks + +1. [ ] item 1 + 1. [ ] item 2 + 1. [ ] item 3 + 1. [ ] item 4 + 1. [ ] item 5 + 1. [ ] item 6 + + paragraph text + + 1. [ ] item 7 + + paragraph text + + 1. [ ] item 8 + + paragraph text + + 1. [ ] item 9 + 1. [ ] item 10`; + + /* The equivalent HTML for the above markdown + <ol data-sourcepos="3:1-21:17"> + <li data-sourcepos="3:1-21:17">item 1 + <ol data-sourcepos="4:4-21:17"> + <li data-sourcepos="4:4-4:16"> + <p data-sourcepos="4:7-4:16">item 2</p> + </li> + <li data-sourcepos="5:4-7:19"> + <p data-sourcepos="5:7-5:16">item 3</p> + <ol data-sourcepos="6:7-7:19"> + <li data-sourcepos="6:7-6:19">item 4</li> + <li data-sourcepos="7:7-7:19">item 5</li> + </ol> + </li> + <li data-sourcepos="8:4-11:0"> + <p data-sourcepos="8:7-8:16">item 6</p> + <p data-sourcepos="10:7-10:20">paragraph text</p> + </li> + <li data-sourcepos="12:4-20:19"> + <p data-sourcepos="12:7-12:16">item 7</p> + <p data-sourcepos="14:7-14:20">paragraph text</p> + <ol data-sourcepos="16:7-20:19"> + <li data-sourcepos="16:7-19:0"> + <p data-sourcepos="16:10-16:19">item 8</p> + <p data-sourcepos="18:10-18:23">paragraph text</p> + </li> + <li data-sourcepos="20:7-20:19"> + <p data-sourcepos="20:10-20:19">item 9</p> + </li> + </ol> + </li> + <li data-sourcepos="21:4-21:17"> + <p data-sourcepos="21:7-21:17">item 10</p> + </li> + </ol> + </li> + </ol> + */ + + it('deletes item with no children', () => { + const sourcepos = '4:4-4:14'; + const newDescription = `Tasks + +1. [ ] item 1 + 1. [ ] item 3 + 1. [ ] item 4 + 1. [ ] item 5 + 1. [ ] item 6 + + paragraph text + + 1. [ ] item 7 + + paragraph text + + 1. [ ] item 8 + + paragraph text + + 1. [ ] item 9 + 1. [ ] item 10`; + + expect(convertDescriptionWithDeletedTaskListItem(description, sourcepos)).toBe( + newDescription, + ); + }); + + it('deletes deeply nested item with no children', () => { + const sourcepos = '6:7-6:19'; + const newDescription = `Tasks + +1. [ ] item 1 + 1. [ ] item 2 + 1. [ ] item 3 + 1. [ ] item 5 + 1. [ ] item 6 + + paragraph text + + 1. [ ] item 7 + + paragraph text + + 1. [ ] item 8 + + paragraph text + + 1. [ ] item 9 + 1. [ ] item 10`; + + expect(convertDescriptionWithDeletedTaskListItem(description, sourcepos)).toBe( + newDescription, + ); + }); + + it('deletes item with children and moves sub-tasks up a level', () => { + const sourcepos = '5:4-7:19'; + const newDescription = `Tasks + +1. [ ] item 1 + 1. [ ] item 2 + 1. [ ] item 4 + 1. [ ] item 5 + 1. [ ] item 6 + + paragraph text + + 1. [ ] item 7 + + paragraph text + + 1. [ ] item 8 + + paragraph text + + 1. [ ] item 9 + 1. [ ] item 10`; + + expect(convertDescriptionWithDeletedTaskListItem(description, sourcepos)).toBe( + newDescription, + ); + }); + + it('deletes item with associated paragraph text', () => { + const sourcepos = '8:4-11:0'; + const newDescription = `Tasks + +1. [ ] item 1 + 1. [ ] item 2 + 1. [ ] item 3 + 1. [ ] item 4 + 1. [ ] item 5 + 1. [ ] item 7 + + paragraph text + + 1. [ ] item 8 + + paragraph text + + 1. [ ] item 9 + 1. [ ] item 10`; + + expect(convertDescriptionWithDeletedTaskListItem(description, sourcepos)).toBe( + newDescription, + ); + }); + + it('deletes item with associated paragraph text and moves sub-tasks up a level', () => { + const sourcepos = '12:4-20:19'; + const newDescription = `Tasks + +1. [ ] item 1 + 1. [ ] item 2 + 1. [ ] item 3 + 1. [ ] item 4 + 1. [ ] item 5 + 1. [ ] item 6 + + paragraph text + + 1. [ ] item 8 + + paragraph text + + 1. [ ] item 9 + 1. [ ] item 10`; + + expect(convertDescriptionWithDeletedTaskListItem(description, sourcepos)).toBe( + newDescription, + ); + }); + }); }); |