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')
-rw-r--r--spec/frontend/work_items/components/notes/system_note_spec.js10
-rw-r--r--spec/frontend/work_items/components/notes/work_item_comment_form_spec.js4
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_actions_spec.js4
-rw-r--r--spec/frontend/work_items/components/shared/work_item_link_child_contents_spec.js60
-rw-r--r--spec/frontend/work_items/components/shared/work_item_links_menu_spec.js30
-rw-r--r--spec/frontend/work_items/components/shared/work_item_token_input_spec.js119
-rw-r--r--spec/frontend/work_items/components/work_item_actions_spec.js29
-rw-r--r--spec/frontend/work_items/components/work_item_award_emoji_spec.js27
-rw-r--r--spec/frontend/work_items/components/work_item_description_spec.js2
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js40
-rw-r--r--spec/frontend/work_items/components/work_item_labels_spec.js33
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js1
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_spec.js19
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js19
-rw-r--r--spec/frontend/work_items/components/work_item_milestone_spec.js53
-rw-r--r--spec/frontend/work_items/components/work_item_parent_spec.js84
-rw-r--r--spec/frontend/work_items/components/work_item_relationships/__snapshots__/work_item_relationship_list_spec.js.snap1
-rw-r--r--spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js3
-rw-r--r--spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js33
-rw-r--r--spec/frontend/work_items/components/work_item_state_toggle_button_spec.js4
-rw-r--r--spec/frontend/work_items/components/work_item_title_spec.js20
-rw-r--r--spec/frontend/work_items/components/work_item_todos_spec.js2
22 files changed, 427 insertions, 170 deletions
diff --git a/spec/frontend/work_items/components/notes/system_note_spec.js b/spec/frontend/work_items/components/notes/system_note_spec.js
index 03f1aa356ad..69bc0961240 100644
--- a/spec/frontend/work_items/components/notes/system_note_spec.js
+++ b/spec/frontend/work_items/components/notes/system_note_spec.js
@@ -40,8 +40,14 @@ describe('Work Items system note component', () => {
);
});
- it('should render svg icon', () => {
- expect(findTimelineIcon().exists()).toBe(true);
+ it('should render svg icon only for allowed icons', () => {
+ expect(findTimelineIcon().exists()).toBe(false);
+
+ const ALLOWED_ICONS = ['issue-close'];
+ ALLOWED_ICONS.forEach((icon) => {
+ createComponent({ note: { ...workItemSystemNoteWithMetadata, systemNoteIconName: icon } });
+ expect(findTimelineIcon().exists()).toBe(true);
+ });
});
it('should not show compare previous version for FOSS', () => {
diff --git a/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js b/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js
index ee2b434bd75..fe89c525fea 100644
--- a/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js
@@ -10,7 +10,7 @@ import { STATE_OPEN } from '~/work_items/constants';
import * as confirmViaGlModal from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import WorkItemCommentForm from '~/work_items/components/notes/work_item_comment_form.vue';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
-import WorkItemStateToggleButton from '~/work_items/components/work_item_state_toggle_button.vue';
+import WorkItemStateToggle from '~/work_items/components/work_item_state_toggle.vue';
Vue.use(VueApollo);
@@ -37,7 +37,7 @@ describe('Work item comment form component', () => {
const findConfirmButton = () => wrapper.find('[data-testid="confirm-button"]');
const findInternalNoteCheckbox = () => wrapper.findComponent(GlFormCheckbox);
const findInternalNoteTooltipIcon = () => wrapper.findComponent(GlIcon);
- const findWorkItemToggleStateButton = () => wrapper.findComponent(WorkItemStateToggleButton);
+ const findWorkItemToggleStateButton = () => wrapper.findComponent(WorkItemStateToggle);
const createComponent = ({
isSubmitting = false,
diff --git a/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js b/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
index 6a24987b737..596283a9590 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
@@ -1,4 +1,4 @@
-import { GlDisclosureDropdown } from '@gitlab/ui';
+import { GlButton, GlDisclosureDropdown } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -17,7 +17,7 @@ describe('Work Item Note Actions', () => {
const showSpy = jest.fn();
const findReplyButton = () => wrapper.findComponent(ReplyButton);
- const findEditButton = () => wrapper.findByTestId('edit-work-item-note');
+ const findEditButton = () => wrapper.findComponent(GlButton);
const findEmojiButton = () => wrapper.findByTestId('note-emoji-button');
const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findDeleteNoteButton = () => wrapper.findByTestId('delete-note-action');
diff --git a/spec/frontend/work_items/components/shared/work_item_link_child_contents_spec.js b/spec/frontend/work_items/components/shared/work_item_link_child_contents_spec.js
index 2e1a7983dec..a40e860d9fe 100644
--- a/spec/frontend/work_items/components/shared/work_item_link_child_contents_spec.js
+++ b/spec/frontend/work_items/components/shared/work_item_link_child_contents_spec.js
@@ -1,4 +1,4 @@
-import { GlLabel, GlIcon, GlLink } from '@gitlab/ui';
+import { GlLabel, GlIcon, GlLink, GlButton } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -9,7 +9,6 @@ import { createAlert } from '~/alert';
import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue';
import WorkItemLinkChildContents from '~/work_items/components/shared/work_item_link_child_contents.vue';
-import WorkItemLinksMenu from '~/work_items/components/shared/work_item_links_menu.vue';
import { WORK_ITEM_TYPE_VALUE_OBJECTIVE } from '~/work_items/constants';
import {
@@ -39,13 +38,18 @@ describe('WorkItemLinkChildContents', () => {
const findAllLabels = () => wrapper.findAllComponents(GlLabel);
const findRegularLabel = () => findAllLabels().at(0);
const findScopedLabel = () => findAllLabels().at(1);
- const findLinksMenuComponent = () => wrapper.findComponent(WorkItemLinksMenu);
+ const findRemoveButton = () => wrapper.findComponent(GlButton);
- const createComponent = ({ canUpdate = true, childItem = workItemTask } = {}) => {
+ const createComponent = ({
+ canUpdate = true,
+ childItem = workItemTask,
+ showLabels = true,
+ } = {}) => {
wrapper = shallowMountExtended(WorkItemLinkChildContents, {
propsData: {
canUpdate,
childItem,
+ showLabels,
},
});
};
@@ -129,19 +133,6 @@ describe('WorkItemLinkChildContents', () => {
expect(findMetadataComponent().exists()).toBe(false);
});
-
- it('renders labels', () => {
- const mockLabel = mockLabels[0];
-
- expect(findAllLabels()).toHaveLength(mockLabels.length);
- expect(findRegularLabel().props()).toMatchObject({
- title: mockLabel.title,
- backgroundColor: mockLabel.color,
- description: mockLabel.description,
- scoped: false,
- });
- expect(findScopedLabel().props('scoped')).toBe(true); // Second label is scoped
- });
});
describe('item menu', () => {
@@ -149,20 +140,47 @@ describe('WorkItemLinkChildContents', () => {
createComponent();
});
- it('renders work-item-links-menu', () => {
- expect(findLinksMenuComponent().exists()).toBe(true);
+ it('renders remove button', () => {
+ expect(findRemoveButton().exists()).toBe(true);
});
it('does not render work-item-links-menu when canUpdate is false', () => {
createComponent({ canUpdate: false });
- expect(findLinksMenuComponent().exists()).toBe(false);
+ expect(findRemoveButton().exists()).toBe(false);
});
it('removeChild event on menu triggers `click-remove-child` event', () => {
- findLinksMenuComponent().vm.$emit('removeChild');
+ findRemoveButton().vm.$emit('click');
expect(wrapper.emitted('removeChild')).toEqual([[workItemTask]]);
});
});
+
+ describe('item labels', () => {
+ it('renders normal and scoped label', () => {
+ createComponent({ childItem: workItemObjectiveWithChild });
+
+ const mockLabel = mockLabels[0];
+
+ expect(findAllLabels()).toHaveLength(mockLabels.length);
+ expect(findRegularLabel().props()).toMatchObject({
+ title: mockLabel.title,
+ backgroundColor: mockLabel.color,
+ description: mockLabel.description,
+ scoped: false,
+ });
+ expect(findScopedLabel().props('scoped')).toBe(true); // Second label is scoped
+ });
+
+ it.each`
+ expectedAssertion | showLabels
+ ${'does not render labels'} | ${true}
+ ${'renders label'} | ${false}
+ `('$expectedAssertion when showLabels is $showLabels', ({ showLabels }) => {
+ createComponent({ showLabels, childItem: workItemObjectiveWithChild });
+
+ expect(findAllLabels().exists()).toBe(showLabels);
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/shared/work_item_links_menu_spec.js b/spec/frontend/work_items/components/shared/work_item_links_menu_spec.js
deleted file mode 100644
index 338a70feae4..00000000000
--- a/spec/frontend/work_items/components/shared/work_item_links_menu_spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui';
-
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import WorkItemLinksMenu from '~/work_items/components/shared/work_item_links_menu.vue';
-
-describe('WorkItemLinksMenu', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMountExtended(WorkItemLinksMenu);
- };
-
- const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
- const findRemoveDropdownItem = () => wrapper.findComponent(GlDisclosureDropdownItem);
-
- beforeEach(() => {
- createComponent();
- });
-
- it('renders dropdown and dropdown items', () => {
- expect(findDropdown().exists()).toBe(true);
- expect(findRemoveDropdownItem().exists()).toBe(true);
- });
-
- it('emits removeChild event on click Remove', () => {
- findRemoveDropdownItem().vm.$emit('action');
-
- expect(wrapper.emitted('removeChild')).toHaveLength(1);
- });
-});
diff --git a/spec/frontend/work_items/components/shared/work_item_token_input_spec.js b/spec/frontend/work_items/components/shared/work_item_token_input_spec.js
index 075b69415cf..5726aaaa2d0 100644
--- a/spec/frontend/work_items/components/shared/work_item_token_input_spec.js
+++ b/spec/frontend/work_items/components/shared/work_item_token_input_spec.js
@@ -1,13 +1,19 @@
-import Vue from 'vue';
-import { GlTokenSelector } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
+import { GlTokenSelector, GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import WorkItemTokenInput from '~/work_items/components/shared/work_item_token_input.vue';
import { WORK_ITEM_TYPE_ENUM_TASK } from '~/work_items/constants';
+import groupWorkItemsQuery from '~/work_items/graphql/group_work_items.query.graphql';
import projectWorkItemsQuery from '~/work_items/graphql/project_work_items.query.graphql';
-import { availableWorkItemsResponse, searchedWorkItemsResponse } from '../../mock_data';
+import {
+ availableWorkItemsResponse,
+ searchWorkItemsTextResponse,
+ searchWorkItemsIidResponse,
+ searchWorkItemsTextIidResponse,
+} from '../../mock_data';
Vue.use(VueApollo);
@@ -15,17 +21,27 @@ describe('WorkItemTokenInput', () => {
let wrapper;
const availableWorkItemsResolver = jest.fn().mockResolvedValue(availableWorkItemsResponse);
- const searchedWorkItemResolver = jest.fn().mockResolvedValue(searchedWorkItemsResponse);
+ const groupSearchedWorkItemResolver = jest.fn().mockResolvedValue(searchWorkItemsTextResponse);
+ const searchWorkItemTextResolver = jest.fn().mockResolvedValue(searchWorkItemsTextResponse);
+ const searchWorkItemIidResolver = jest.fn().mockResolvedValue(searchWorkItemsIidResponse);
+ const searchWorkItemTextIidResolver = jest.fn().mockResolvedValue(searchWorkItemsTextIidResponse);
const createComponent = async ({
workItemsToAdd = [],
parentConfidential = false,
childrenType = WORK_ITEM_TYPE_ENUM_TASK,
areWorkItemsToAddValid = true,
- workItemsResolver = searchedWorkItemResolver,
+ workItemsResolver = searchWorkItemTextResolver,
+ isGroup = false,
} = {}) => {
wrapper = shallowMountExtended(WorkItemTokenInput, {
- apolloProvider: createMockApollo([[projectWorkItemsQuery, workItemsResolver]]),
+ apolloProvider: createMockApollo([
+ [projectWorkItemsQuery, workItemsResolver],
+ [groupWorkItemsQuery, groupSearchedWorkItemResolver],
+ ]),
+ provide: {
+ isGroup,
+ },
propsData: {
value: workItemsToAdd,
childrenType,
@@ -41,6 +57,7 @@ describe('WorkItemTokenInput', () => {
};
const findTokenSelector = () => wrapper.findComponent(GlTokenSelector);
+ const findGlAlert = () => wrapper.findComponent(GlAlert);
it('searches for available work items on focus', async () => {
createComponent({ workItemsResolver: availableWorkItemsResolver });
@@ -52,24 +69,34 @@ describe('WorkItemTokenInput', () => {
searchTerm: '',
types: [WORK_ITEM_TYPE_ENUM_TASK],
in: undefined,
+ iid: null,
+ isNumber: false,
});
expect(findTokenSelector().props('dropdownItems')).toHaveLength(3);
});
- it('searches for available work items when typing in input', async () => {
- createComponent({ workItemsResolver: searchedWorkItemResolver });
- findTokenSelector().vm.$emit('focus');
- findTokenSelector().vm.$emit('text-input', 'Task 2');
- await waitForPromises();
+ it.each`
+ inputType | input | resolver | searchTerm | iid | isNumber | length
+ ${'iid'} | ${'101'} | ${searchWorkItemIidResolver} | ${'101'} | ${'101'} | ${true} | ${1}
+ ${'text'} | ${'Task 2'} | ${searchWorkItemTextResolver} | ${'Task 2'} | ${null} | ${false} | ${1}
+ ${'iid and text'} | ${'123'} | ${searchWorkItemTextIidResolver} | ${'123'} | ${'123'} | ${true} | ${2}
+ `(
+ 'searches by $inputType for available work items when typing in input',
+ async ({ input, resolver, searchTerm, iid, isNumber, length }) => {
+ createComponent({ workItemsResolver: resolver });
+ findTokenSelector().vm.$emit('focus');
+ findTokenSelector().vm.$emit('text-input', input);
+ await waitForPromises();
- expect(searchedWorkItemResolver).toHaveBeenCalledWith({
- fullPath: 'test-project-path',
- searchTerm: 'Task 2',
- types: [WORK_ITEM_TYPE_ENUM_TASK],
- in: 'TITLE',
- });
- expect(findTokenSelector().props('dropdownItems')).toHaveLength(1);
- });
+ expect(resolver).toHaveBeenCalledWith({
+ searchTerm,
+ in: 'TITLE',
+ iid,
+ isNumber,
+ });
+ expect(findTokenSelector().props('dropdownItems')).toHaveLength(length);
+ },
+ );
it('renders red border around token selector input when work item is not valid', () => {
createComponent({
@@ -78,4 +105,58 @@ describe('WorkItemTokenInput', () => {
expect(findTokenSelector().props('containerClass')).toBe('gl-inset-border-1-red-500!');
});
+
+ describe('when project context', () => {
+ beforeEach(() => {
+ createComponent();
+ findTokenSelector().vm.$emit('focus');
+ });
+
+ it('calls the project work items query', () => {
+ expect(searchWorkItemTextResolver).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work items query', () => {
+ expect(groupSearchedWorkItemResolver).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ beforeEach(() => {
+ createComponent({ isGroup: true });
+ findTokenSelector().vm.$emit('focus');
+ });
+
+ it('skips calling the project work items query', () => {
+ expect(searchWorkItemTextResolver).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work items query', () => {
+ expect(groupSearchedWorkItemResolver).toHaveBeenCalled();
+ });
+ });
+
+ describe('when project work items query fails', () => {
+ beforeEach(() => {
+ createComponent({
+ workItemsResolver: jest
+ .fn()
+ .mockRejectedValue('Something went wrong while fetching the results'),
+ });
+ findTokenSelector().vm.$emit('focus');
+ });
+
+ it('shows error and allows error alert to be closed', async () => {
+ await waitForPromises();
+ expect(findGlAlert().exists()).toBe(true);
+ expect(findGlAlert().text()).toBe(
+ 'Something went wrong while fetching the task. Please try again.',
+ );
+
+ findGlAlert().vm.$emit('dismiss');
+ await nextTick();
+
+ expect(findGlAlert().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_actions_spec.js b/spec/frontend/work_items/components/work_item_actions_spec.js
index 15c33bf5b1e..6cbb3c4384e 100644
--- a/spec/frontend/work_items/components/work_item_actions_spec.js
+++ b/spec/frontend/work_items/components/work_item_actions_spec.js
@@ -1,4 +1,10 @@
-import { GlDisclosureDropdown, GlDropdownDivider, GlModal, GlToggle } from '@gitlab/ui';
+import {
+ GlDisclosureDropdown,
+ GlDropdownDivider,
+ GlModal,
+ GlToggle,
+ GlDisclosureDropdownItem,
+} from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
@@ -10,14 +16,16 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { isLoggedIn } from '~/lib/utils/common_utils';
import toast from '~/vue_shared/plugins/global_toast';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
+import WorkItemStateToggle from '~/work_items/components/work_item_state_toggle.vue';
import {
+ STATE_OPEN,
TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION,
- TEST_ID_NOTIFICATIONS_TOGGLE_ACTION,
TEST_ID_NOTIFICATIONS_TOGGLE_FORM,
TEST_ID_DELETE_ACTION,
TEST_ID_PROMOTE_ACTION,
TEST_ID_COPY_REFERENCE_ACTION,
TEST_ID_COPY_CREATE_NOTE_EMAIL_ACTION,
+ TEST_ID_TOGGLE_ACTION,
} from '~/work_items/constants';
import updateWorkItemNotificationsMutation from '~/work_items/graphql/update_work_item_notifications.mutation.graphql';
import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
@@ -44,11 +52,10 @@ describe('WorkItemActions component', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findConfidentialityToggleButton = () =>
wrapper.findByTestId(TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION);
- const findNotificationsToggleButton = () =>
- wrapper.findByTestId(TEST_ID_NOTIFICATIONS_TOGGLE_ACTION);
const findDeleteButton = () => wrapper.findByTestId(TEST_ID_DELETE_ACTION);
const findPromoteButton = () => wrapper.findByTestId(TEST_ID_PROMOTE_ACTION);
const findCopyReferenceButton = () => wrapper.findByTestId(TEST_ID_COPY_REFERENCE_ACTION);
+ const findWorkItemToggleOption = () => wrapper.findComponent(WorkItemStateToggle);
const findCopyCreateNoteEmailButton = () =>
wrapper.findByTestId(TEST_ID_COPY_CREATE_NOTE_EMAIL_ACTION);
const findDropdownItems = () => wrapper.findAll('[data-testid="work-item-actions-dropdown"] > *');
@@ -108,6 +115,7 @@ describe('WorkItemActions component', () => {
[updateWorkItemNotificationsMutation, notificationsMutationHandler],
]),
propsData: {
+ workItemState: STATE_OPEN,
fullPath: 'gitlab-org/gitlab-test',
workItemId: 'gid://gitlab/WorkItem/1',
canUpdate,
@@ -132,6 +140,7 @@ describe('WorkItemActions component', () => {
show: jest.fn(),
},
}),
+ GlDisclosureDropdownItem,
GlDisclosureDropdown: stubComponent(GlDisclosureDropdown, {
methods: {
close: modalShowSpy,
@@ -167,6 +176,10 @@ describe('WorkItemActions component', () => {
text: 'Turn on confidentiality',
},
{
+ testId: TEST_ID_TOGGLE_ACTION,
+ text: '',
+ },
+ {
testId: TEST_ID_COPY_REFERENCE_ACTION,
text: 'Copy reference',
},
@@ -248,7 +261,7 @@ describe('WorkItemActions component', () => {
});
it('renders toggle button', () => {
- expect(findNotificationsToggleButton().exists()).toBe(true);
+ expect(findNotificationsToggle().exists()).toBe(true);
});
it.each`
@@ -366,4 +379,10 @@ describe('WorkItemActions component', () => {
expect(toast).toHaveBeenCalledWith('Email address copied');
});
});
+
+ it('renders the toggle status button', () => {
+ createComponent();
+
+ expect(findWorkItemToggleOption().exists()).toBe(true);
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_award_emoji_spec.js b/spec/frontend/work_items/components/work_item_award_emoji_spec.js
index f8c5f8edc4c..a756bfa6889 100644
--- a/spec/frontend/work_items/components/work_item_award_emoji_spec.js
+++ b/spec/frontend/work_items/components/work_item_award_emoji_spec.js
@@ -9,7 +9,8 @@ import { isLoggedIn } from '~/lib/utils/common_utils';
import AwardList from '~/vue_shared/components/awards_list.vue';
import WorkItemAwardEmoji from '~/work_items/components/work_item_award_emoji.vue';
import updateAwardEmojiMutation from '~/work_items/graphql/update_award_emoji.mutation.graphql';
-import workItemAwardEmojiQuery from '~/work_items/graphql/award_emoji.query.graphql';
+import groupWorkItemAwardEmojiQuery from '~/work_items/graphql/group_award_emoji.query.graphql';
+import projectWorkItemAwardEmojiQuery from '~/work_items/graphql/award_emoji.query.graphql';
import {
EMOJI_THUMBSUP,
EMOJI_THUMBSDOWN,
@@ -42,6 +43,7 @@ describe('WorkItemAwardEmoji component', () => {
const workItemQueryResponse = workItemByIidResponseFactory();
const mockWorkItem = workItemQueryResponse.data.workspace.workItems.nodes[0];
+ const groupAwardEmojiQuerySuccessHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
const awardEmojiQuerySuccessHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
const awardEmojiQueryEmptyHandler = jest.fn().mockResolvedValue(
workItemByIidResponseFactory({
@@ -83,10 +85,12 @@ describe('WorkItemAwardEmoji component', () => {
awardEmojiQueryHandler = awardEmojiQuerySuccessHandler,
awardEmojiMutationHandler = awardEmojiAddSuccessHandler,
workItemIid = '1',
+ isGroup = false,
} = {}) => {
mockApolloProvider = createMockApollo(
[
- [workItemAwardEmojiQuery, awardEmojiQueryHandler],
+ [projectWorkItemAwardEmojiQuery, awardEmojiQueryHandler],
+ [groupWorkItemAwardEmojiQuery, groupAwardEmojiQuerySuccessHandler],
[updateAwardEmojiMutation, awardEmojiMutationHandler],
],
{},
@@ -108,6 +112,9 @@ describe('WorkItemAwardEmoji component', () => {
wrapper = shallowMount(WorkItemAwardEmoji, {
isLoggedIn: isLoggedIn(),
apolloProvider: mockApolloProvider,
+ provide: {
+ isGroup,
+ },
propsData: {
workItemId: 'gid://gitlab/WorkItem/1',
workItemFullpath: 'test-project-path',
@@ -270,7 +277,7 @@ describe('WorkItemAwardEmoji component', () => {
};
});
- it('calls mutation succesfully and adds the award emoji with proper user details', async () => {
+ it('calls mutation successfully and adds the award emoji with proper user details', async () => {
createComponent({
awardEmojiMutationHandler: awardEmojiAddSuccessHandler,
});
@@ -345,4 +352,18 @@ describe('WorkItemAwardEmoji component', () => {
});
});
});
+
+ describe('group award emoji query', () => {
+ it('is not called in a project context', () => {
+ createComponent();
+
+ expect(groupAwardEmojiQuerySuccessHandler).not.toHaveBeenCalled();
+ });
+
+ it('is called in a group context', () => {
+ createComponent({ isGroup: true });
+
+ expect(groupAwardEmojiQuerySuccessHandler).toHaveBeenCalled();
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_description_spec.js b/spec/frontend/work_items/components/work_item_description_spec.js
index de2895591dd..1d25bb74986 100644
--- a/spec/frontend/work_items/components/work_item_description_spec.js
+++ b/spec/frontend/work_items/components/work_item_description_spec.js
@@ -92,7 +92,7 @@ describe('WorkItemDescription', () => {
it('passes correct autocompletion data and preview markdown sources and enables quick actions', async () => {
const {
iid,
- project: { fullPath },
+ namespace: { fullPath },
} = workItemQueryResponse.data.workItem;
await createComponent({ isEditing: true });
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index 28826748cb0..acfe4571cd2 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -23,8 +23,6 @@ import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree
import WorkItemRelationships from '~/work_items/components/work_item_relationships/work_item_relationships.vue';
import WorkItemNotes from '~/work_items/components/work_item_notes.vue';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
-import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
-import WorkItemStateToggleButton from '~/work_items/components/work_item_state_toggle_button.vue';
import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
import WorkItemTodos from '~/work_items/components/work_item_todos.vue';
import { i18n } from '~/work_items/constants';
@@ -55,10 +53,6 @@ describe('WorkItemDetail component', () => {
canUpdate: true,
canDelete: true,
});
- const workItemQueryResponseWithCannotUpdate = workItemByIidResponseFactory({
- canUpdate: false,
- canDelete: false,
- });
const workItemQueryResponseWithoutParent = workItemByIidResponseFactory({
parent: null,
canUpdate: true,
@@ -95,8 +89,6 @@ describe('WorkItemDetail component', () => {
const findWorkItemTwoColumnViewContainer = () => wrapper.findByTestId('work-item-overview');
const findRightSidebar = () => wrapper.findByTestId('work-item-overview-right-sidebar');
const triggerPageScroll = () => findIntersectionObserver().vm.$emit('disappear');
- const findWorkItemStateToggleButton = () => wrapper.findComponent(WorkItemStateToggleButton);
- const findWorkItemTypeIcon = () => wrapper.findComponent(WorkItemTypeIcon);
const createComponent = ({
isGroup = false,
@@ -212,25 +204,6 @@ describe('WorkItemDetail component', () => {
});
});
- describe('work item state toggle button', () => {
- describe.each`
- description | canUpdate
- ${'when user cannot update'} | ${false}
- ${'when user can update'} | ${true}
- `('$description', ({ canUpdate }) => {
- it(`${canUpdate ? 'is rendered' : 'is not rendered'}`, async () => {
- createComponent({
- handler: canUpdate
- ? jest.fn().mockResolvedValue(workItemQueryResponse)
- : jest.fn().mockResolvedValue(workItemQueryResponseWithCannotUpdate),
- });
- await waitForPromises();
-
- expect(findWorkItemStateToggleButton().exists()).toBe(canUpdate);
- });
- });
- });
-
describe('close button', () => {
describe('when isModal prop is false', () => {
it('does not render', async () => {
@@ -408,12 +381,11 @@ describe('WorkItemDetail component', () => {
expect(findParent().exists()).toBe(false);
});
- it('shows work item type with reference when there is no a parent', async () => {
+ it('shows title in the header when there is no parent', async () => {
createComponent({ handler: jest.fn().mockResolvedValue(workItemQueryResponseWithoutParent) });
await waitForPromises();
- expect(findWorkItemTypeIcon().props('showText')).toBe(true);
- expect(findWorkItemType().text()).toBe('#1');
+ expect(findWorkItemType().classes()).toEqual(['gl-w-full']);
});
describe('with parent', () => {
@@ -428,10 +400,6 @@ describe('WorkItemDetail component', () => {
expect(findParent().exists()).toBe(true);
});
- it('does not show work item type', () => {
- expect(findWorkItemType().exists()).toBe(false);
- });
-
it('shows parent breadcrumb icon', () => {
expect(findParentButton().props('icon')).toBe(mockParent.parent.workItemType.iconName);
});
@@ -468,6 +436,10 @@ describe('WorkItemDetail component', () => {
const { iid } = workItemQueryResponse.data.workspace.workItems.nodes[0];
expect(findParent().text()).toContain(`#${iid}`);
});
+
+ it('does not show title in the header when parent exists', () => {
+ expect(findWorkItemType().classes()).toEqual(['gl-sm-display-none!']);
+ });
});
});
diff --git a/spec/frontend/work_items/components/work_item_labels_spec.js b/spec/frontend/work_items/components/work_item_labels_spec.js
index 28aa7ffa1be..d7bebac6dbd 100644
--- a/spec/frontend/work_items/components/work_item_labels_spec.js
+++ b/spec/frontend/work_items/components/work_item_labels_spec.js
@@ -5,7 +5,8 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import labelSearchQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
+import groupLabelsQuery from '~/sidebar/components/labels/labels_select_widget/graphql/group_labels.query.graphql';
+import projectLabelsQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
@@ -37,7 +38,8 @@ describe('WorkItemLabels component', () => {
const groupWorkItemQuerySuccess = jest
.fn()
.mockResolvedValue(groupWorkItemByIidResponseFactory({ labels: null }));
- const successSearchQueryHandler = jest.fn().mockResolvedValue(projectLabelsResponse);
+ const projectLabelsQueryHandler = jest.fn().mockResolvedValue(projectLabelsResponse);
+ const groupLabelsQueryHandler = jest.fn().mockResolvedValue(projectLabelsResponse);
const successUpdateWorkItemMutationHandler = jest
.fn()
.mockResolvedValue(updateWorkItemMutationResponse);
@@ -47,7 +49,7 @@ describe('WorkItemLabels component', () => {
canUpdate = true,
isGroup = false,
workItemQueryHandler = workItemQuerySuccess,
- searchQueryHandler = successSearchQueryHandler,
+ searchQueryHandler = projectLabelsQueryHandler,
updateWorkItemMutationHandler = successUpdateWorkItemMutationHandler,
workItemIid = '1',
} = {}) => {
@@ -55,7 +57,8 @@ describe('WorkItemLabels component', () => {
apolloProvider: createMockApollo([
[workItemByIidQuery, workItemQueryHandler],
[groupWorkItemByIidQuery, groupWorkItemQuerySuccess],
- [labelSearchQuery, searchQueryHandler],
+ [projectLabelsQuery, searchQueryHandler],
+ [groupLabelsQuery, groupLabelsQueryHandler],
[updateWorkItemMutation, updateWorkItemMutationHandler],
]),
provide: {
@@ -179,7 +182,7 @@ describe('WorkItemLabels component', () => {
findTokenSelector().vm.$emit('text-input', searchKey);
await waitForPromises();
- expect(successSearchQueryHandler).toHaveBeenCalledWith(
+ expect(projectLabelsQueryHandler).toHaveBeenCalledWith(
expect.objectContaining({ searchTerm: searchKey }),
);
});
@@ -273,6 +276,16 @@ describe('WorkItemLabels component', () => {
expect(workItemQuerySuccess).not.toHaveBeenCalled();
});
+
+ it('calls the project labels query on search', async () => {
+ createComponent();
+
+ findTokenSelector().vm.$emit('focus');
+ findTokenSelector().vm.$emit('text-input', 'hello');
+ await waitForPromises();
+
+ expect(projectLabelsQueryHandler).toHaveBeenCalled();
+ });
});
describe('when group context', () => {
@@ -296,5 +309,15 @@ describe('WorkItemLabels component', () => {
expect(groupWorkItemQuerySuccess).not.toHaveBeenCalled();
});
+
+ it('calls the group labels query on search', async () => {
+ createComponent({ isGroup: true });
+
+ findTokenSelector().vm.$emit('focus');
+ findTokenSelector().vm.$emit('text-input', 'hello');
+ await waitForPromises();
+
+ expect(groupLabelsQueryHandler).toHaveBeenCalled();
+ });
});
});
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 9addf6c3450..36af0c5b3c8 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
@@ -91,6 +91,7 @@ describe('WorkItemLinkChild', () => {
childItem: workItemObjectiveWithChild,
canUpdate: true,
showTaskIcon: false,
+ showLabels: true,
});
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
index 0b88b3ff5b4..f8b2736c0f8 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
@@ -1,5 +1,6 @@
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import { GlToggle } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -93,6 +94,7 @@ describe('WorkItemLinks', () => {
const findWorkItemDetailModal = () => wrapper.findComponent(WorkItemDetailModal);
const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector);
const findWorkItemLinkChildrenWrapper = () => wrapper.findComponent(WorkItemChildrenWrapper);
+ const findShowLabelsToggle = () => wrapper.findComponent(GlToggle);
afterEach(() => {
mockApollo = null;
@@ -278,4 +280,21 @@ describe('WorkItemLinks', () => {
expect(groupResponseWithAddChildPermission).toHaveBeenCalled();
});
});
+
+ it.each`
+ toggleValue
+ ${true}
+ ${false}
+ `(
+ 'passes showLabels as $toggleValue to child items when toggle is $toggleValue',
+ async ({ toggleValue }) => {
+ await createComponent();
+
+ findShowLabelsToggle().vm.$emit('change', toggleValue);
+
+ await nextTick();
+
+ expect(findWorkItemLinkChildrenWrapper().props('showLabels')).toBe(toggleValue);
+ },
+ );
});
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 f30fded0b45..6c1d1035c3d 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
@@ -1,4 +1,5 @@
import { nextTick } from 'vue';
+import { GlToggle } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import WidgetWrapper from '~/work_items/components/widget_wrapper.vue';
import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue';
@@ -20,6 +21,7 @@ describe('WorkItemTree', () => {
const findForm = () => wrapper.findComponent(WorkItemLinksForm);
const findWidgetWrapper = () => wrapper.findComponent(WidgetWrapper);
const findWorkItemLinkChildrenWrapper = () => wrapper.findComponent(WorkItemChildrenWrapper);
+ const findShowLabelsToggle = () => wrapper.findComponent(GlToggle);
const createComponent = ({
workItemType = 'Objective',
@@ -126,4 +128,21 @@ describe('WorkItemTree', () => {
expect(wrapper.emitted('addChild')).toEqual([[]]);
});
+
+ it.each`
+ toggleValue
+ ${true}
+ ${false}
+ `(
+ 'passes showLabels as $toggleValue to child items when toggle is $toggleValue',
+ async ({ toggleValue }) => {
+ createComponent();
+
+ findShowLabelsToggle().vm.$emit('change', toggleValue);
+
+ await nextTick();
+
+ expect(findWorkItemLinkChildrenWrapper().props('showLabels')).toBe(toggleValue);
+ },
+ );
});
diff --git a/spec/frontend/work_items/components/work_item_milestone_spec.js b/spec/frontend/work_items/components/work_item_milestone_spec.js
index e303ad4b481..fc2c5eb2af2 100644
--- a/spec/frontend/work_items/components/work_item_milestone_spec.js
+++ b/spec/frontend/work_items/components/work_item_milestone_spec.js
@@ -1,14 +1,8 @@
-import {
- GlDropdown,
- GlDropdownItem,
- GlSearchBoxByType,
- GlSkeletonLoader,
- GlFormGroup,
- GlDropdownText,
-} from '@gitlab/ui';
+import { GlCollapsibleListbox, GlListboxItem, GlSkeletonLoader, GlFormGroup } from '@gitlab/ui';
+
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import WorkItemMilestone from '~/work_items/components/work_item_milestone.vue';
+import WorkItemMilestone, { noMilestoneId } from '~/work_items/components/work_item_milestone.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -32,17 +26,13 @@ describe('WorkItemMilestone component', () => {
const workItemId = 'gid://gitlab/WorkItem/1';
const workItemType = 'Task';
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+ const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
- const findNoMilestoneDropdownItem = () => wrapper.findByTestId('no-milestone');
- const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findFirstDropdownItem = () => findDropdownItems().at(0);
- const findDropdownTexts = () => wrapper.findAllComponents(GlDropdownText);
- const findDropdownItemAtIndex = (index) => findDropdownItems().at(index);
+ const findNoMilestoneDropdownItem = () => wrapper.findByTestId('listbox-item-no-milestone-id');
+ const findDropdownItems = () => wrapper.findAllComponents(GlListboxItem);
const findDisabledTextSpan = () => wrapper.findByTestId('disabled-text');
- const findDropdownTextAtIndex = (index) => findDropdownTexts().at(index);
const findInputGroup = () => wrapper.findComponent(GlFormGroup);
+ const findNoResultsText = () => wrapper.findByTestId('no-results-text');
const successSearchQueryHandler = jest.fn().mockResolvedValue(projectMilestonesResponse);
const successSearchWithNoMatchingMilestones = jest
@@ -74,8 +64,7 @@ describe('WorkItemMilestone component', () => {
workItemType,
},
stubs: {
- GlDropdown,
- GlSearchBoxByType,
+ GlCollapsibleListbox,
},
});
};
@@ -106,7 +95,7 @@ describe('WorkItemMilestone component', () => {
it(`has a value of "Add to milestone"`, () => {
createComponent({ canUpdate: true, milestone: null });
- expect(findDropdown().props('text')).toBe(WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER);
+ expect(findDropdown().props('toggleText')).toBe(WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER);
});
});
@@ -114,7 +103,7 @@ describe('WorkItemMilestone component', () => {
it('has the search box', () => {
createComponent();
- expect(findSearchBox().exists()).toBe(true);
+ expect(findDropdown().props('searchable')).toBe(true);
});
it('shows no matching results when no items', () => {
@@ -122,9 +111,8 @@ describe('WorkItemMilestone component', () => {
searchQueryHandler: successSearchWithNoMatchingMilestones,
});
- expect(findDropdownTextAtIndex(0).text()).toBe(WorkItemMilestone.i18n.NO_MATCHING_RESULTS);
+ expect(findNoResultsText().text()).toBe(WorkItemMilestone.i18n.NO_MATCHING_RESULTS);
expect(findDropdownItems()).toHaveLength(1);
- expect(findDropdownTexts()).toHaveLength(1);
});
});
@@ -165,16 +153,18 @@ describe('WorkItemMilestone component', () => {
it('changes the milestone to null when clicked on no milestone', async () => {
showDropdown();
- findFirstDropdownItem().vm.$emit('click');
+ findDropdown().vm.$emit('select', noMilestoneId);
hideDropdown();
await nextTick();
expect(findDropdown().props('loading')).toBe(true);
await waitForPromises();
-
- expect(findDropdown().props('loading')).toBe(false);
- expect(findDropdown().props('text')).toBe(WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER);
+ expect(findDropdown().props()).toMatchObject({
+ loading: false,
+ toggleText: WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER,
+ toggleClass: expect.arrayContaining(['gl-text-gray-500!']),
+ });
});
it('changes the milestone to the selected milestone', async () => {
@@ -182,15 +172,16 @@ describe('WorkItemMilestone component', () => {
/** the index is -1 since no matching results is also a dropdown item */
const milestoneAtIndex =
projectMilestonesResponse.data.workspace.attributes.nodes[milestoneIndex - 1];
+
showDropdown();
await waitForPromises();
- findDropdownItemAtIndex(milestoneIndex).vm.$emit('click');
+ findDropdown().vm.$emit('select', milestoneAtIndex.id);
hideDropdown();
await waitForPromises();
- expect(findDropdown().props('text')).toBe(milestoneAtIndex.title);
+ expect(findDropdown().props('toggleText')).toBe(milestoneAtIndex.title);
});
});
@@ -208,7 +199,7 @@ describe('WorkItemMilestone component', () => {
});
showDropdown();
- findFirstDropdownItem().vm.$emit('click');
+ findDropdown().vm.$emit('select', noMilestoneId);
hideDropdown();
await waitForPromises();
@@ -224,7 +215,7 @@ describe('WorkItemMilestone component', () => {
createComponent({ canUpdate: true });
showDropdown();
- findFirstDropdownItem().vm.$emit('click');
+ findDropdown().vm.$emit('select', noMilestoneId);
hideDropdown();
await waitForPromises();
diff --git a/spec/frontend/work_items/components/work_item_parent_spec.js b/spec/frontend/work_items/components/work_item_parent_spec.js
index a72eeabc43c..11fe6dffbfa 100644
--- a/spec/frontend/work_items/components/work_item_parent_spec.js
+++ b/spec/frontend/work_items/components/work_item_parent_spec.js
@@ -1,14 +1,15 @@
-import * as Sentry from '@sentry/browser';
import { GlCollapsibleListbox, GlFormGroup } from '@gitlab/ui';
-
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import waitForPromises from 'helpers/wait_for_promises';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import * as Sentry from '~/sentry/sentry_browser_wrapper';
import WorkItemParent from '~/work_items/components/work_item_parent.vue';
+import { removeHierarchyChild } from '~/work_items/graphql/cache_utils';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+import groupWorkItemsQuery from '~/work_items/graphql/group_work_items.query.graphql';
import projectWorkItemsQuery from '~/work_items/graphql/project_work_items.query.graphql';
import { WORK_ITEM_TYPE_ENUM_OBJECTIVE } from '~/work_items/constants';
@@ -20,7 +21,10 @@ import {
updateWorkItemMutationErrorResponse,
} from '../mock_data';
-jest.mock('@sentry/browser');
+jest.mock('~/sentry/sentry_browser_wrapper');
+jest.mock('~/work_items/graphql/cache_utils', () => ({
+ removeHierarchyChild: jest.fn(),
+}));
describe('WorkItemParent component', () => {
Vue.use(VueApollo);
@@ -29,7 +33,9 @@ describe('WorkItemParent component', () => {
const workItemId = 'gid://gitlab/WorkItem/1';
const workItemType = 'Objective';
+ const mockFullPath = 'full-path';
+ const groupWorkItemsSuccessHandler = jest.fn().mockResolvedValue(availableObjectivesResponse);
const availableWorkItemsSuccessHandler = jest.fn().mockResolvedValue(availableObjectivesResponse);
const availableWorkItemsFailureHandler = jest.fn().mockRejectedValue(new Error());
@@ -42,14 +48,17 @@ describe('WorkItemParent component', () => {
parent = null,
searchQueryHandler = availableWorkItemsSuccessHandler,
mutationHandler = successUpdateWorkItemMutationHandler,
+ isGroup = false,
} = {}) => {
wrapper = shallowMountExtended(WorkItemParent, {
apolloProvider: createMockApollo([
[projectWorkItemsQuery, searchQueryHandler],
+ [groupWorkItemsQuery, groupWorkItemsSuccessHandler],
[updateWorkItemMutation, mutationHandler],
]),
provide: {
- fullPath: 'full-path',
+ fullPath: mockFullPath,
+ isGroup,
},
propsData: {
canUpdate,
@@ -81,7 +90,6 @@ describe('WorkItemParent component', () => {
headerText: 'Assign parent',
category: 'tertiary',
loading: false,
- noCaret: true,
isCheckCentered: true,
searchable: true,
searching: false,
@@ -90,7 +98,6 @@ describe('WorkItemParent component', () => {
toggleText: 'None',
searchPlaceholder: 'Search',
resetButtonLabel: 'Unassign',
- block: true,
});
});
@@ -98,14 +105,12 @@ describe('WorkItemParent component', () => {
createComponent({ canUpdate: false, parent: mockParentWidgetResponse });
expect(findCollapsibleListbox().exists()).toBe(false);
- expect(findParentText().exists()).toBe(true);
expect(findParentText().text()).toBe('Objective 101');
});
it('shows loading while searching', async () => {
await findCollapsibleListbox().vm.$emit('shown');
expect(findCollapsibleListbox().props('searching')).toBe(true);
- expect(findCollapsibleListbox().props('no-caret')).toBeUndefined();
});
});
@@ -143,15 +148,27 @@ describe('WorkItemParent component', () => {
});
await findCollapsibleListbox().vm.$emit('shown');
- await findCollapsibleListbox().vm.$emit('search', 'Objective 101');
await waitForPromises();
expect(searchedItemQueryHandler).toHaveBeenCalledWith({
fullPath: 'full-path',
+ searchTerm: '',
+ types: [WORK_ITEM_TYPE_ENUM_OBJECTIVE],
+ in: undefined,
+ iid: null,
+ isNumber: false,
+ });
+
+ await findCollapsibleListbox().vm.$emit('search', 'Objective 101');
+
+ expect(searchedItemQueryHandler).toHaveBeenCalledWith({
+ fullPath: 'full-path',
searchTerm: 'Objective 101',
types: [WORK_ITEM_TYPE_ENUM_OBJECTIVE],
in: 'TITLE',
+ iid: null,
+ isNumber: false,
});
await nextTick();
@@ -164,7 +181,6 @@ describe('WorkItemParent component', () => {
describe('listbox', () => {
const selectWorkItem = async (workItem) => {
- await findCollapsibleListbox().vm.$emit('shown');
await findCollapsibleListbox().vm.$emit('select', workItem);
};
@@ -181,6 +197,14 @@ describe('WorkItemParent component', () => {
},
},
});
+
+ expect(removeHierarchyChild).toHaveBeenCalledWith({
+ cache: expect.anything(Object),
+ fullPath: mockFullPath,
+ iid: undefined,
+ isGroup: false,
+ workItem: { id: 'gid://gitlab/WorkItem/1' },
+ });
});
it('calls mutation when item is unassigned', async () => {
@@ -188,6 +212,9 @@ describe('WorkItemParent component', () => {
.fn()
.mockResolvedValue(updateWorkItemMutationResponseFactory({ parent: null }));
createComponent({
+ parent: {
+ iid: '1',
+ },
mutationHandler: unAssignParentWorkItemMutationHandler,
});
@@ -203,6 +230,13 @@ describe('WorkItemParent component', () => {
},
},
});
+ expect(removeHierarchyChild).toHaveBeenCalledWith({
+ cache: expect.anything(Object),
+ fullPath: mockFullPath,
+ iid: '1',
+ isGroup: false,
+ workItem: { id: 'gid://gitlab/WorkItem/1' },
+ });
});
it('emits error when mutation fails', async () => {
@@ -233,4 +267,34 @@ describe('WorkItemParent component', () => {
expect(Sentry.captureException).toHaveBeenCalledWith(error);
});
});
+
+ describe('when project context', () => {
+ beforeEach(() => {
+ createComponent();
+ findCollapsibleListbox().vm.$emit('shown');
+ });
+
+ it('calls the project work items query', () => {
+ expect(availableWorkItemsSuccessHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work items query', () => {
+ expect(groupWorkItemsSuccessHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ beforeEach(() => {
+ createComponent({ isGroup: true });
+ findCollapsibleListbox().vm.$emit('shown');
+ });
+
+ it('skips calling the project work items query', () => {
+ expect(availableWorkItemsSuccessHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work items query', () => {
+ expect(groupWorkItemsSuccessHandler).toHaveBeenCalled();
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_relationships/__snapshots__/work_item_relationship_list_spec.js.snap b/spec/frontend/work_items/components/work_item_relationships/__snapshots__/work_item_relationship_list_spec.js.snap
index bbc19a011a5..20af8584e37 100644
--- a/spec/frontend/work_items/components/work_item_relationships/__snapshots__/work_item_relationship_list_spec.js.snap
+++ b/spec/frontend/work_items/components/work_item_relationships/__snapshots__/work_item_relationship_list_spec.js.snap
@@ -22,6 +22,7 @@ exports[`WorkItemRelationshipList renders linked item list 1`] = `
<work-item-link-child-contents-stub
canupdate="true"
childitem="[object Object]"
+ showlabels="true"
showtaskicon="true"
/>
</li>
diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js
index d7b3ced2ff9..520cf5f7ea4 100644
--- a/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js
+++ b/spec/frontend/work_items/components/work_item_relationships/work_item_add_relationship_form_spec.js
@@ -34,6 +34,9 @@ describe('WorkItemAddRelationshipForm', () => {
wrapper = shallowMountExtended(WorkItemAddRelationshipForm, {
apolloProvider: mockApolloProvider,
+ provide: {
+ isGroup: false,
+ },
propsData: {
workItemId,
workItemIid,
diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
index 7178fa1aae7..0faea0e4862 100644
--- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
+++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
@@ -1,6 +1,6 @@
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import { GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlToggle } from '@gitlab/ui';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -82,6 +82,7 @@ describe('WorkItemRelationships', () => {
wrapper.findAllComponents(WorkItemRelationshipList);
const findAddButton = () => wrapper.findByTestId('link-item-add-button');
const findWorkItemRelationshipForm = () => wrapper.findComponent(WorkItemAddRelationshipForm);
+ const findShowLabelsToggle = () => wrapper.findComponent(GlToggle);
it('shows loading icon when query is not processed', () => {
createComponent();
@@ -99,6 +100,11 @@ describe('WorkItemRelationships', () => {
expect(findLinkedItemsHelpLink().attributes('href')).toBe(
'/help/user/okrs.md#linked-items-in-okrs',
);
+ expect(findShowLabelsToggle().props()).toMatchObject({
+ value: true,
+ labelPosition: 'left',
+ label: 'Show labels',
+ });
});
it('renders blocking linked item lists', async () => {
@@ -153,6 +159,29 @@ describe('WorkItemRelationships', () => {
expect(findWorkItemRelationshipForm().exists()).toBe(false);
});
+ it.each`
+ toggleValue
+ ${true}
+ ${false}
+ `(
+ 'passes showLabels as $toggleValue to child items when toggle is $toggleValue',
+ async ({ toggleValue }) => {
+ await createComponent({
+ workItemQueryHandler: jest
+ .fn()
+ .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })),
+ });
+
+ findShowLabelsToggle().vm.$emit('change', toggleValue);
+
+ await nextTick();
+
+ expect(findAllWorkItemRelationshipListComponents().at(0).props('showLabels')).toBe(
+ toggleValue,
+ );
+ },
+ );
+
describe('when project context', () => {
it('calls the project work item query', () => {
createComponent();
diff --git a/spec/frontend/work_items/components/work_item_state_toggle_button_spec.js b/spec/frontend/work_items/components/work_item_state_toggle_button_spec.js
index c0b206e5da4..a210bd50422 100644
--- a/spec/frontend/work_items/components/work_item_state_toggle_button_spec.js
+++ b/spec/frontend/work_items/components/work_item_state_toggle_button_spec.js
@@ -5,7 +5,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mockTracking } from 'helpers/tracking_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import WorkItemStateToggleButton from '~/work_items/components/work_item_state_toggle_button.vue';
+import WorkItemStateToggle from '~/work_items/components/work_item_state_toggle.vue';
import {
STATE_OPEN,
STATE_CLOSED,
@@ -33,7 +33,7 @@ describe('Work Item State toggle button component', () => {
workItemState = STATE_OPEN,
workItemType = 'Task',
} = {}) => {
- wrapper = shallowMount(WorkItemStateToggleButton, {
+ wrapper = shallowMount(WorkItemStateToggle, {
apolloProvider: createMockApollo([[updateWorkItemMutation, mutationHandler]]),
propsData: {
workItemId: id,
diff --git a/spec/frontend/work_items/components/work_item_title_spec.js b/spec/frontend/work_items/components/work_item_title_spec.js
index 34391b74cf7..0f466bcf691 100644
--- a/spec/frontend/work_items/components/work_item_title_spec.js
+++ b/spec/frontend/work_items/components/work_item_title_spec.js
@@ -131,5 +131,25 @@ describe('WorkItemTitle component', () => {
property: 'type_Task',
});
});
+
+ describe('when title has more than 255 characters', () => {
+ const title = new Array(257).join('a');
+
+ it('does not call a mutation', () => {
+ createComponent();
+
+ findItemTitle().vm.$emit('title-changed', title);
+
+ expect(mutationSuccessHandler).not.toHaveBeenCalled();
+ });
+
+ it('emits an error message', () => {
+ createComponent();
+
+ findItemTitle().vm.$emit('title-changed', title);
+
+ expect(wrapper.emitted('error')).toEqual([['Title cannot have more than 255 characters.']]);
+ });
+ });
});
});
diff --git a/spec/frontend/work_items/components/work_item_todos_spec.js b/spec/frontend/work_items/components/work_item_todos_spec.js
index c76cdbcee53..d67d84e75b5 100644
--- a/spec/frontend/work_items/components/work_item_todos_spec.js
+++ b/spec/frontend/work_items/components/work_item_todos_spec.js
@@ -34,7 +34,7 @@ describe('WorkItemTodo component', () => {
const workItemQueryResponse = workItemResponseFactory({ canUpdate: true });
const mockWorkItemId = workItemQueryResponse.data.workItem.id;
const mockWorkItemIid = workItemQueryResponse.data.workItem.iid;
- const mockWorkItemFullpath = workItemQueryResponse.data.workItem.project.fullPath;
+ const mockWorkItemFullpath = workItemQueryResponse.data.workItem.namespace.fullPath;
const createTodoSuccessHandler = jest
.fn()