debian-mirror-gitlab/spec/frontend/issues/show/components/description_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

403 lines
11 KiB
JavaScript
Raw Normal View History

2018-05-09 12:01:36 +05:30
import $ from 'jquery';
2022-07-16 23:28:13 +05:30
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
2023-04-23 21:23:45 +05:30
import getIssueDetailsQuery from 'ee_else_ce/work_items/graphql/get_issue_details.query.graphql';
2022-06-21 17:19:12 +05:30
import setWindowLocation from 'helpers/set_window_location_helper';
2020-05-24 23:13:21 +05:30
import { TEST_HOST } from 'helpers/test_constants';
2022-07-16 23:28:13 +05:30
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
2022-05-07 20:08:51 +05:30
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
2023-05-27 22:25:52 +05:30
import { createAlert } from '~/alert';
2022-01-26 12:08:38 +05:30
import Description from '~/issues/show/components/description.vue';
2023-04-23 21:23:45 +05:30
import eventHub from '~/issues/show/event_hub';
2022-07-16 23:28:13 +05:30
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
2023-04-23 21:23:45 +05:30
import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql';
2022-08-13 15:12:31 +05:30
import workItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
2020-05-24 23:13:21 +05:30
import TaskList from '~/task_list';
2023-03-04 22:38:38 +05:30
import { renderGFM } from '~/behaviors/markdown/render_gfm';
2022-08-13 15:12:31 +05:30
import {
2023-04-23 21:23:45 +05:30
createWorkItemMutationErrorResponse,
createWorkItemMutationResponse,
getIssueDetailsResponse,
2022-08-13 15:12:31 +05:30
projectWorkItemTypesQueryResponse,
} from 'jest/work_items/mock_data';
2023-05-27 22:25:52 +05:30
import { descriptionProps as initialProps, descriptionHtmlWithList } from '../mock_data/mock_data';
2020-05-24 23:13:21 +05:30
2023-05-27 22:25:52 +05:30
jest.mock('~/alert');
2022-06-21 17:19:12 +05:30
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
updateHistory: jest.fn(),
}));
2020-05-24 23:13:21 +05:30
jest.mock('~/task_list');
2023-03-04 22:38:38 +05:30
jest.mock('~/behaviors/markdown/render_gfm');
2017-09-10 17:25:29 +05:30
2023-04-23 21:23:45 +05:30
const mockSpriteIcons = '/icons.svg';
2022-06-21 17:19:12 +05:30
const $toast = {
show: jest.fn(),
};
2022-04-04 11:22:00 +05:30
2023-04-23 21:23:45 +05:30
const issueDetailsResponse = getIssueDetailsResponse();
2022-07-16 23:28:13 +05:30
const workItemQueryResponse = {
data: {
workItem: null,
},
};
const queryHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
2022-08-13 15:12:31 +05:30
const workItemTypesQueryHandler = jest.fn().mockResolvedValue(projectWorkItemTypesQueryResponse);
2022-07-16 23:28:13 +05:30
2017-09-10 17:25:29 +05:30
describe('Description component', () => {
2022-04-04 11:22:00 +05:30
let wrapper;
2022-07-16 23:28:13 +05:30
Vue.use(VueApollo);
2022-04-04 11:22:00 +05:30
const findGfmContent = () => wrapper.find('[data-testid="gfm-content"]');
const findTextarea = () => wrapper.find('[data-testid="textarea"]');
2023-04-23 21:23:45 +05:30
const findListItems = () => findGfmContent().findAll('ul > li');
const findTaskActionButtons = () => wrapper.findAll('.task-list-item-actions');
2022-04-04 11:22:00 +05:30
2022-10-11 01:57:18 +05:30
function createComponent({
props = {},
provide,
2023-04-23 21:23:45 +05:30
issueDetailsQueryHandler = jest.fn().mockResolvedValue(issueDetailsResponse),
createWorkItemMutationHandler,
2022-10-11 01:57:18 +05:30
} = {}) {
2022-05-07 20:08:51 +05:30
wrapper = shallowMountExtended(Description, {
2022-04-04 11:22:00 +05:30
propsData: {
2022-06-21 17:19:12 +05:30
issueId: 1,
2022-04-04 11:22:00 +05:30
...initialProps,
...props,
},
2022-08-13 15:12:31 +05:30
provide: {
fullPath: 'gitlab-org/gitlab-test',
2023-04-23 21:23:45 +05:30
hasIterationsFeature: true,
2022-08-13 15:12:31 +05:30
...provide,
},
apolloProvider: createMockApollo([
[workItemQuery, queryHandler],
[workItemTypesQuery, workItemTypesQueryHandler],
2023-04-23 21:23:45 +05:30
[getIssueDetailsQuery, issueDetailsQueryHandler],
[createWorkItemMutation, createWorkItemMutationHandler],
2022-08-13 15:12:31 +05:30
]),
2022-06-21 17:19:12 +05:30
mocks: {
$toast,
},
2022-04-04 11:22:00 +05:30
});
}
2017-09-10 17:25:29 +05:30
beforeEach(() => {
2023-04-23 21:23:45 +05:30
window.gon = { sprite_icons: mockSpriteIcons };
2022-06-21 17:19:12 +05:30
setWindowLocation(TEST_HOST);
2017-09-10 17:25:29 +05:30
if (!document.querySelector('.issuable-meta')) {
const metaData = document.createElement('div');
metaData.classList.add('issuable-meta');
2019-03-02 22:35:43 +05:30
metaData.innerHTML =
'<div class="flash-container"></div><span id="task_status"></span><span id="task_status_short"></span>';
2017-09-10 17:25:29 +05:30
document.body.appendChild(metaData);
}
2018-03-17 18:26:18 +05:30
});
2019-03-02 22:35:43 +05:30
afterAll(() => {
$('.issuable-meta .flash-container').remove();
});
2022-04-04 11:22:00 +05:30
it('doesnt animate first description changes', async () => {
createComponent();
await wrapper.setProps({
descriptionHtml: 'changed',
2020-11-24 15:15:51 +05:30
});
2022-04-04 11:22:00 +05:30
expect(findGfmContent().classes()).not.toContain('issue-realtime-pre-pulse');
2020-11-24 15:15:51 +05:30
});
2022-04-04 11:22:00 +05:30
it('animates description changes on live update', async () => {
createComponent();
await wrapper.setProps({
descriptionHtml: 'changed',
});
expect(findGfmContent().classes()).not.toContain('issue-realtime-pre-pulse');
await wrapper.setProps({
descriptionHtml: 'changed second time',
});
expect(findGfmContent().classes()).toContain('issue-realtime-pre-pulse');
await jest.runOnlyPendingTimers();
expect(findGfmContent().classes()).toContain('issue-realtime-trigger-pulse');
2017-09-10 17:25:29 +05:30
});
2022-04-04 11:22:00 +05:30
it('applies syntax highlighting and math when description changed', async () => {
createComponent();
2018-10-15 14:42:47 +05:30
2022-04-04 11:22:00 +05:30
await wrapper.setProps({
descriptionHtml: 'changed',
2020-05-24 23:13:21 +05:30
});
2022-04-04 11:22:00 +05:30
expect(findGfmContent().exists()).toBe(true);
2023-03-04 22:38:38 +05:30
expect(renderGFM).toHaveBeenCalled();
2020-05-24 23:13:21 +05:30
});
it('sets data-update-url', () => {
2022-04-04 11:22:00 +05:30
createComponent();
expect(findTextarea().attributes('data-update-url')).toBe(TEST_HOST);
2020-05-24 23:13:21 +05:30
});
describe('TaskList', () => {
2018-03-17 18:26:18 +05:30
beforeEach(() => {
2020-05-24 23:13:21 +05:30
TaskList.mockClear();
2018-03-17 18:26:18 +05:30
});
2020-05-24 23:13:21 +05:30
it('re-inits the TaskList when description changed', () => {
2022-04-04 11:22:00 +05:30
createComponent({
props: {
issuableType: 'issuableType',
},
});
wrapper.setProps({
descriptionHtml: 'changed',
});
2018-03-17 18:26:18 +05:30
2020-05-24 23:13:21 +05:30
expect(TaskList).toHaveBeenCalled();
2018-03-17 18:26:18 +05:30
});
2022-04-04 11:22:00 +05:30
it('does not re-init the TaskList when canUpdate is false', async () => {
createComponent({
props: {
issuableType: 'issuableType',
canUpdate: false,
},
});
wrapper.setProps({
descriptionHtml: 'changed',
});
2018-03-17 18:26:18 +05:30
2022-04-04 11:22:00 +05:30
expect(TaskList).not.toHaveBeenCalled();
2018-03-17 18:26:18 +05:30
});
2020-05-24 23:13:21 +05:30
it('calls with issuableType dataType', () => {
2022-04-04 11:22:00 +05:30
createComponent({
props: {
issuableType: 'issuableType',
},
});
wrapper.setProps({
descriptionHtml: 'changed',
});
2018-03-17 18:26:18 +05:30
2020-05-24 23:13:21 +05:30
expect(TaskList).toHaveBeenCalledWith({
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
2021-12-11 22:18:48 +05:30
onUpdate: expect.any(Function),
onSuccess: expect.any(Function),
2020-05-24 23:13:21 +05:30
onError: expect.any(Function),
lockVersion: 0,
2018-03-17 18:26:18 +05:30
});
});
});
2017-09-10 17:25:29 +05:30
describe('taskStatus', () => {
2022-04-04 11:22:00 +05:30
it('adds full taskStatus', async () => {
createComponent({
props: {
taskStatus: '1 of 1',
},
2017-09-10 17:25:29 +05:30
});
2022-04-04 11:22:00 +05:30
await nextTick();
2017-09-10 17:25:29 +05:30
2022-04-04 11:22:00 +05:30
expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(
'1 of 1',
);
});
2017-09-10 17:25:29 +05:30
2022-04-04 11:22:00 +05:30
it('adds short taskStatus', async () => {
createComponent({
props: {
taskStatus: '1 of 1',
},
2017-09-10 17:25:29 +05:30
});
2022-04-04 11:22:00 +05:30
await nextTick();
2017-09-10 17:25:29 +05:30
2022-04-04 11:22:00 +05:30
expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe(
2022-08-27 11:52:29 +05:30
'1/1 checklist item',
2022-04-04 11:22:00 +05:30
);
});
2017-09-10 17:25:29 +05:30
2022-04-04 11:22:00 +05:30
it('clears task status text when no tasks are present', async () => {
createComponent({
props: {
taskStatus: '0 of 0',
},
2017-09-10 17:25:29 +05:30
});
2022-04-04 11:22:00 +05:30
await nextTick();
expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe('');
2017-09-10 17:25:29 +05:30
});
});
2023-04-23 21:23:45 +05:30
describe('with list', () => {
beforeEach(async () => {
createComponent({
props: {
descriptionHtml: descriptionHtmlWithList,
},
});
await nextTick();
});
it('shows list items', () => {
expect(findListItems()).toHaveLength(3);
});
it('shows list items drag icons', () => {
const dragIcon = findListItems().at(0).find('.drag-icon');
expect(dragIcon.classes()).toEqual(
expect.arrayContaining(['s14', 'gl-icon', 'gl-cursor-grab', 'gl-opacity-0']),
);
expect(dragIcon.attributes()).toMatchObject({
'aria-hidden': 'true',
role: 'img',
});
expect(dragIcon.find('use').attributes()).toEqual({
href: `${mockSpriteIcons}#grip`,
});
});
});
2023-05-08 21:46:49 +05:30
describe('empty description', () => {
beforeEach(() => {
createComponent({
props: {
descriptionHtml: '',
},
2022-04-04 11:22:00 +05:30
});
2023-05-08 21:46:49 +05:30
return nextTick();
});
2021-12-11 22:18:48 +05:30
2023-05-08 21:46:49 +05:30
it('renders without error', () => {
expect(findTaskActionButtons()).toHaveLength(0);
2021-12-11 22:18:48 +05:30
});
2023-05-08 21:46:49 +05:30
});
2021-12-11 22:18:48 +05:30
2023-05-08 21:46:49 +05:30
describe('task list item actions', () => {
describe('converting the task list item to a task', () => {
describe('when successful', () => {
let createWorkItemMutationHandler;
2022-10-11 01:57:18 +05:30
2023-05-08 21:46:49 +05:30
beforeEach(async () => {
createWorkItemMutationHandler = jest
.fn()
.mockResolvedValue(createWorkItemMutationResponse);
const descriptionText = `Tasks
2022-04-04 11:22:00 +05:30
2023-04-23 21:23:45 +05:30
1. [ ] item 1
1. [ ] item 2
2019-03-02 22:35:43 +05:30
2023-04-23 21:23:45 +05:30
paragraph text
2022-08-13 15:12:31 +05:30
2023-04-23 21:23:45 +05:30
1. [ ] item 3
1. [ ] item 4;`;
2023-05-08 21:46:49 +05:30
createComponent({
props: { descriptionText },
createWorkItemMutationHandler,
2023-04-23 21:23:45 +05:30
});
2023-05-08 21:46:49 +05:30
await waitForPromises();
2023-04-23 21:23:45 +05:30
2023-05-08 21:46:49 +05:30
eventHub.$emit('convert-task-list-item', '4:4-8:19');
await waitForPromises();
});
it('emits an event to update the description with the deleted task list item omitted', () => {
const newDescriptionText = `Tasks
2023-04-23 21:23:45 +05:30
1. [ ] item 1
1. [ ] item 3
1. [ ] item 4;`;
2023-05-08 21:46:49 +05:30
expect(wrapper.emitted('saveDescription')).toEqual([[newDescriptionText]]);
});
2023-04-23 21:23:45 +05:30
2023-05-08 21:46:49 +05:30
it('calls a mutation to create a task', () => {
2023-05-27 22:25:52 +05:30
const { confidential, iteration, milestone } = issueDetailsResponse.data.issue;
2023-05-08 21:46:49 +05:30
expect(createWorkItemMutationHandler).toHaveBeenCalledWith({
input: {
2023-04-23 21:23:45 +05:30
confidential,
2023-05-08 21:46:49 +05:30
description: '\nparagraph text\n',
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
},
iterationWidget: {
iterationId: IS_EE ? iteration.id : null,
},
milestoneWidget: {
milestoneId: milestone.id,
2023-04-23 21:23:45 +05:30
},
2023-05-08 21:46:49 +05:30
projectPath: 'gitlab-org/gitlab-test',
title: 'item 2',
workItemTypeId: 'gid://gitlab/WorkItems::Type/3',
},
2023-04-23 21:23:45 +05:30
});
2023-05-08 21:46:49 +05:30
});
2023-04-23 21:23:45 +05:30
2023-05-08 21:46:49 +05:30
it('shows a toast to confirm the creation of the task', () => {
expect($toast.show).toHaveBeenCalledWith('Converted to task', expect.any(Object));
2022-10-11 01:57:18 +05:30
});
2023-05-08 21:46:49 +05:30
});
2022-04-04 11:22:00 +05:30
2023-05-08 21:46:49 +05:30
describe('when unsuccessful', () => {
beforeEach(async () => {
createComponent({
props: { descriptionText: 'description' },
createWorkItemMutationHandler: jest
.fn()
.mockResolvedValue(createWorkItemMutationErrorResponse),
2023-04-23 21:23:45 +05:30
});
2023-05-08 21:46:49 +05:30
await waitForPromises();
2022-10-11 01:57:18 +05:30
2023-05-08 21:46:49 +05:30
eventHub.$emit('convert-task-list-item', '1:1-1:11');
await waitForPromises();
});
it('shows an alert with an error message', () => {
expect(createAlert).toHaveBeenCalledWith({
message: 'Something went wrong when creating task. Please try again.',
error: new Error('an error'),
captureError: true,
2023-04-23 21:23:45 +05:30
});
});
});
2023-05-08 21:46:49 +05:30
});
2023-04-23 21:23:45 +05:30
2023-05-08 21:46:49 +05:30
describe('deleting the task list item', () => {
it('emits an event to update the description with the deleted task list item', () => {
const descriptionText = `Tasks
2023-04-23 21:23:45 +05:30
1. [ ] item 1
1. [ ] item 2
1. [ ] item 3
1. [ ] item 4;`;
2023-05-08 21:46:49 +05:30
const newDescriptionText = `Tasks
2023-04-23 21:23:45 +05:30
1. [ ] item 1
1. [ ] item 3
1. [ ] item 4;`;
2023-05-08 21:46:49 +05:30
createComponent({
props: { descriptionText },
});
2023-04-23 21:23:45 +05:30
2023-05-08 21:46:49 +05:30
eventHub.$emit('delete-task-list-item', '4:4-5:19');
2023-04-23 21:23:45 +05:30
2023-05-08 21:46:49 +05:30
expect(wrapper.emitted('saveDescription')).toEqual([[newDescriptionText]]);
2022-04-04 11:22:00 +05:30
});
2019-03-02 22:35:43 +05:30
});
2023-05-08 21:46:49 +05:30
});
2017-09-10 17:25:29 +05:30
});