Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/work_items/components/work_item_links')
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js27
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js21
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js93
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js29
4 files changed, 138 insertions, 32 deletions
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
index 47489d4796b..e693ccfb156 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
@@ -5,23 +5,22 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ItemMilestone from '~/issuable/components/issue_milestone.vue';
import WorkItemLinkChildMetadata from '~/work_items/components/work_item_links/work_item_link_child_metadata.vue';
-import { mockMilestone, mockAssignees, mockLabels } from '../../mock_data';
+import { workItemObjectiveMetadataWidgets } from '../../mock_data';
describe('WorkItemLinkChildMetadata', () => {
+ const { MILESTONE, ASSIGNEES, LABELS } = workItemObjectiveMetadataWidgets;
+ const mockMilestone = MILESTONE.milestone;
+ const mockAssignees = ASSIGNEES.assignees.nodes;
+ const mockLabels = LABELS.labels.nodes;
let wrapper;
- const createComponent = ({
- allowsScopedLabels = true,
- milestone = mockMilestone,
- assignees = mockAssignees,
- labels = mockLabels,
- } = {}) => {
+ const createComponent = ({ metadataWidgets = workItemObjectiveMetadataWidgets } = {}) => {
wrapper = shallowMountExtended(WorkItemLinkChildMetadata, {
propsData: {
- allowsScopedLabels,
- milestone,
- assignees,
- labels,
+ metadataWidgets,
+ },
+ slots: {
+ default: `<div data-testid="default-slot">Foo</div>`,
},
});
};
@@ -30,7 +29,11 @@ describe('WorkItemLinkChildMetadata', () => {
createComponent();
});
- it('renders milestone link button', () => {
+ it('renders default slot contents', () => {
+ expect(wrapper.findByTestId('default-slot').text()).toBe('Foo');
+ });
+
+ it('renders item milestone', () => {
const milestoneLink = wrapper.findComponent(ItemMilestone);
expect(milestoneLink.exists()).toBe(true);
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
index 73d498ad055..0470249d7ce 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
@@ -5,11 +5,12 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import WorkItemLinkChildMetadata from 'ee_else_ce/work_items/components/work_item_links/work_item_link_child_metadata.vue';
+
import { createAlert } from '~/flash';
import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue';
import getWorkItemTreeQuery from '~/work_items/graphql/work_item_tree.query.graphql';
-import WorkItemLinkChildMetadata from '~/work_items/components/work_item_links/work_item_link_child_metadata.vue';
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
import WorkItemLinksMenu from '~/work_items/components/work_item_links/work_item_links_menu.vue';
import WorkItemTreeChildren from '~/work_items/components/work_item_links/work_item_tree_children.vue';
@@ -25,11 +26,9 @@ import {
workItemObjectiveNoMetadata,
confidentialWorkItemTask,
closedWorkItemTask,
- mockMilestone,
- mockAssignees,
- mockLabels,
workItemHierarchyTreeResponse,
workItemHierarchyTreeFailureResponse,
+ workItemObjectiveMetadataWidgets,
} from '../../mock_data';
jest.mock('~/flash');
@@ -148,10 +147,7 @@ describe('WorkItemLinkChild', () => {
const metadataEl = findMetadataComponent();
expect(metadataEl.exists()).toBe(true);
expect(metadataEl.props()).toMatchObject({
- allowsScopedLabels: true,
- milestone: mockMilestone,
- assignees: mockAssignees,
- labels: mockLabels,
+ metadataWidgets: workItemObjectiveMetadataWidgets,
});
});
@@ -265,5 +261,14 @@ describe('WorkItemLinkChild', () => {
message: 'Something went wrong while fetching children.',
});
});
+
+ it('click event on child emits `click` event', async () => {
+ findExpandButton().vm.$emit('click');
+ await waitForPromises();
+
+ findTreeChildren().vm.$emit('click', 'event');
+
+ expect(wrapper.emitted('click')).toEqual([['event']]);
+ });
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
index bbe460a55ba..5e1c46826cc 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
@@ -1,11 +1,18 @@
import Vue from 'vue';
-import { GlForm, GlFormInput, GlTokenSelector } from '@gitlab/ui';
+import { GlForm, GlFormInput, GlFormCheckbox, GlTooltip, GlTokenSelector } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
+import { sprintf, s__ } from '~/locale';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import WorkItemLinksForm from '~/work_items/components/work_item_links/work_item_links_form.vue';
-import { FORM_TYPES } from '~/work_items/constants';
+import {
+ FORM_TYPES,
+ WORK_ITEM_TYPE_ENUM_TASK,
+ WORK_ITEM_TYPE_VALUE_ISSUE,
+ I18N_WORK_ITEM_CONFIDENTIALITY_CHECKBOX_LABEL,
+ I18N_WORK_ITEM_CONFIDENTIALITY_CHECKBOX_TOOLTIP,
+} from '~/work_items/constants';
import projectWorkItemsQuery from '~/work_items/graphql/project_work_items.query.graphql';
import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql';
@@ -36,6 +43,8 @@ describe('WorkItemLinksForm', () => {
workItemsMvcEnabled = false,
parentIteration = null,
formType = FORM_TYPES.create,
+ parentWorkItemType = WORK_ITEM_TYPE_VALUE_ISSUE,
+ childrenType = WORK_ITEM_TYPE_ENUM_TASK,
} = {}) => {
wrapper = shallowMountExtended(WorkItemLinksForm, {
apolloProvider: createMockApollo([
@@ -48,6 +57,8 @@ describe('WorkItemLinksForm', () => {
issuableGid: 'gid://gitlab/WorkItem/1',
parentConfidential,
parentIteration,
+ parentWorkItemType,
+ childrenType,
formType,
},
provide: {
@@ -65,6 +76,7 @@ describe('WorkItemLinksForm', () => {
const findForm = () => wrapper.findComponent(GlForm);
const findTokenSelector = () => wrapper.findComponent(GlTokenSelector);
const findInput = () => wrapper.findComponent(GlFormInput);
+ const findConfidentialCheckbox = () => wrapper.findComponent(GlFormCheckbox);
const findAddChildButton = () => wrapper.findByTestId('add-child-button');
afterEach(() => {
@@ -90,6 +102,7 @@ describe('WorkItemLinksForm', () => {
preventDefault: jest.fn(),
});
await waitForPromises();
+ expect(wrapper.vm.childWorkItemType).toEqual('gid://gitlab/WorkItems::Type/3');
expect(createMutationResolver).toHaveBeenCalledWith({
input: {
title: 'Create task test',
@@ -112,6 +125,7 @@ describe('WorkItemLinksForm', () => {
preventDefault: jest.fn(),
});
await waitForPromises();
+ expect(wrapper.vm.childWorkItemType).toEqual('gid://gitlab/WorkItems::Type/3');
expect(createMutationResolver).toHaveBeenCalledWith({
input: {
title: 'Create confidential task',
@@ -124,9 +138,50 @@ describe('WorkItemLinksForm', () => {
},
});
});
+
+ describe('confidentiality checkbox', () => {
+ it('renders confidentiality checkbox', () => {
+ const confidentialCheckbox = findConfidentialCheckbox();
+
+ expect(confidentialCheckbox.exists()).toBe(true);
+ expect(wrapper.findComponent(GlTooltip).exists()).toBe(false);
+ expect(confidentialCheckbox.text()).toBe(
+ sprintf(I18N_WORK_ITEM_CONFIDENTIALITY_CHECKBOX_LABEL, {
+ workItemType: WORK_ITEM_TYPE_ENUM_TASK.toLocaleLowerCase(),
+ }),
+ );
+ });
+
+ it('renders confidentiality tooltip with checkbox checked and disabled when parent is confidential', () => {
+ createComponent({ parentConfidential: true });
+
+ const confidentialCheckbox = findConfidentialCheckbox();
+ const confidentialTooltip = wrapper.findComponent(GlTooltip);
+
+ expect(confidentialCheckbox.attributes('disabled')).toBe('true');
+ expect(confidentialCheckbox.attributes('checked')).toBe('true');
+ expect(confidentialTooltip.exists()).toBe(true);
+ expect(confidentialTooltip.text()).toBe(
+ sprintf(I18N_WORK_ITEM_CONFIDENTIALITY_CHECKBOX_TOOLTIP, {
+ workItemType: WORK_ITEM_TYPE_ENUM_TASK.toLocaleLowerCase(),
+ parentWorkItemType: WORK_ITEM_TYPE_VALUE_ISSUE.toLocaleLowerCase(),
+ }),
+ );
+ });
+ });
});
describe('adding an existing work item', () => {
+ const selectAvailableWorkItemTokens = async () => {
+ findTokenSelector().vm.$emit(
+ 'input',
+ availableWorkItemsResponse.data.workspace.workItems.nodes,
+ );
+ findTokenSelector().vm.$emit('blur', new FocusEvent({ relatedTarget: null }));
+
+ await waitForPromises();
+ };
+
beforeEach(async () => {
await createComponent({ formType: FORM_TYPES.add });
});
@@ -136,6 +191,7 @@ describe('WorkItemLinksForm', () => {
expect(findTokenSelector().exists()).toBe(true);
expect(findAddChildButton().text()).toBe('Add task');
expect(findInput().exists()).toBe(false);
+ expect(findConfidentialCheckbox().exists()).toBe(false);
});
it('searches for available work items as prop when typing in input', async () => {
@@ -147,13 +203,7 @@ describe('WorkItemLinksForm', () => {
});
it('selects and adds children', async () => {
- findTokenSelector().vm.$emit(
- 'input',
- availableWorkItemsResponse.data.workspace.workItems.nodes,
- );
- findTokenSelector().vm.$emit('blur', new FocusEvent({ relatedTarget: null }));
-
- await waitForPromises();
+ await selectAvailableWorkItemTokens();
expect(findAddChildButton().text()).toBe('Add tasks');
findForm().vm.$emit('submit', {
@@ -162,6 +212,31 @@ describe('WorkItemLinksForm', () => {
await waitForPromises();
expect(updateMutationResolver).toHaveBeenCalled();
});
+
+ it('shows validation error when non-confidential child items are being added to confidential parent', async () => {
+ await createComponent({ formType: FORM_TYPES.add, parentConfidential: true });
+
+ await selectAvailableWorkItemTokens();
+
+ const validationEl = wrapper.findByTestId('work-items-invalid');
+ expect(validationEl.exists()).toBe(true);
+ expect(validationEl.text().trim()).toBe(
+ sprintf(
+ s__(
+ 'WorkItem|%{invalidWorkItemsList} cannot be added: Cannot assign a non-confidential %{childWorkItemType} to a confidential parent %{parentWorkItemType}. Make the selected %{childWorkItemType} confidential and try again.',
+ ),
+ {
+ // Only non-confidential work items are shown in the error message
+ invalidWorkItemsList: availableWorkItemsResponse.data.workspace.workItems.nodes
+ .filter((wi) => !wi.confidential)
+ .map((wi) => wi.title)
+ .join(', '),
+ childWorkItemType: 'Task',
+ parentWorkItemType: 'Issue',
+ },
+ ),
+ );
+ });
});
describe('associate iteration with task', () => {
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
index 96211e12755..156f06a0d5e 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
@@ -34,6 +34,8 @@ describe('WorkItemTree', () => {
const createComponent = ({
workItemType = 'Objective',
+ parentWorkItemType = 'Objective',
+ confidential = false,
children = childrenWorkItems,
apolloProvider = null,
} = {}) => {
@@ -55,7 +57,9 @@ describe('WorkItemTree', () => {
apolloProvider || createMockApollo([[workItemQuery, getWorkItemQueryHandler]]),
propsData: {
workItemType,
+ parentWorkItemType,
workItemId: 'gid://gitlab/WorkItem/515',
+ confidential,
children,
projectPath: 'test/project',
},
@@ -90,7 +94,11 @@ describe('WorkItemTree', () => {
});
it('renders all hierarchy widget children', () => {
- expect(findWorkItemLinkChildItems()).toHaveLength(4);
+ const workItemLinkChildren = findWorkItemLinkChildItems();
+ expect(workItemLinkChildren).toHaveLength(4);
+ expect(workItemLinkChildren.at(0).props().childItem.confidential).toBe(
+ childrenWorkItems[0].confidential,
+ );
});
it('does not display form by default', () => {
@@ -110,8 +118,12 @@ describe('WorkItemTree', () => {
await nextTick();
expect(findForm().exists()).toBe(true);
- expect(findForm().props('formType')).toBe(formType);
- expect(findForm().props('childrenType')).toBe(childType);
+ expect(findForm().props()).toMatchObject({
+ formType,
+ childrenType: childType,
+ parentWorkItemType: 'Objective',
+ parentConfidential: false,
+ });
},
);
@@ -122,6 +134,17 @@ describe('WorkItemTree', () => {
expect(wrapper.emitted('removeChild')).toEqual([['gid://gitlab/WorkItem/2']]);
});
+ it('emits `show-modal` on `click` event', () => {
+ const firstChild = findWorkItemLinkChildItems().at(0);
+ const event = {
+ childItem: 'gid://gitlab/WorkItem/2',
+ };
+
+ firstChild.vm.$emit('click', event);
+
+ expect(wrapper.emitted('show-modal')).toEqual([[event, event.childItem]]);
+ });
+
it.each`
description | workItemType | prefetch
${'prefetches'} | ${'Issue'} | ${true}