summaryrefslogtreecommitdiff
path: root/spec/frontend/issues
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-01-21 00:08:59 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-01-21 00:08:59 +0000
commit367847e266036617e540e41b7fd3c7d03033800c (patch)
treea14547ad7556d48dcdeb977021f8cd89305ea026 /spec/frontend/issues
parent5d7e5a8902382caaffa616e1b496b684ba72d148 (diff)
downloadgitlab-ce-367847e266036617e540e41b7fd3c7d03033800c.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/issues')
-rw-r--r--spec/frontend/issues/show/components/app_spec.js4
-rw-r--r--spec/frontend/issues/show/components/description_spec.js80
-rw-r--r--spec/frontend/issues/show/components/task_list_item_actions_spec.js45
-rw-r--r--spec/frontend/issues/show/utils_spec.js202
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,
+ );
+ });
+ });
});