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_assignees_with_edit_spec.js')
-rw-r--r--spec/frontend/work_items/components/work_item_assignees_with_edit_spec.js300
1 files changed, 300 insertions, 0 deletions
diff --git a/spec/frontend/work_items/components/work_item_assignees_with_edit_spec.js b/spec/frontend/work_items/components/work_item_assignees_with_edit_spec.js
new file mode 100644
index 00000000000..f9c875fa124
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_assignees_with_edit_spec.js
@@ -0,0 +1,300 @@
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import WorkItemAssignees from '~/work_items/components/work_item_assignees_with_edit.vue';
+import WorkItemSidebarDropdownWidgetWithEdit from '~/work_items/components/shared/work_item_sidebar_dropdown_widget_with_edit.vue';
+import groupUsersSearchQuery from '~/graphql_shared/queries/group_users_search.query.graphql';
+import usersSearchQuery from '~/graphql_shared/queries/users_search.query.graphql';
+import currentUserQuery from '~/graphql_shared/queries/current_user.query.graphql';
+import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
+import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mockTracking } from 'helpers/tracking_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import {
+ projectMembersResponseWithCurrentUser,
+ mockAssignees,
+ currentUserResponse,
+ currentUserNullResponse,
+ updateWorkItemMutationResponse,
+ projectMembersResponseWithCurrentUserWithNextPage,
+ projectMembersResponseWithNoMatchingUsers,
+} from 'jest/work_items/mock_data';
+import { DEFAULT_PAGE_SIZE_ASSIGNEES, i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
+
+const workItemId = 'gid://gitlab/WorkItem/1';
+
+describe('WorkItemAssigneesWithEdit component', () => {
+ Vue.use(VueApollo);
+
+ let wrapper;
+
+ const findInviteMembersTrigger = () => wrapper.findComponent(InviteMembersTrigger);
+ const findAssignSelfButton = () => wrapper.findByTestId('assign-self');
+ const findSidebarDropdownWidget = () =>
+ wrapper.findComponent(WorkItemSidebarDropdownWidgetWithEdit);
+
+ const successSearchQueryHandler = jest
+ .fn()
+ .mockResolvedValue(projectMembersResponseWithCurrentUser);
+ const successGroupSearchQueryHandler = jest
+ .fn()
+ .mockResolvedValue(projectMembersResponseWithCurrentUser);
+ const successSearchQueryHandlerWithMoreAssignees = jest
+ .fn()
+ .mockResolvedValue(projectMembersResponseWithCurrentUserWithNextPage);
+ const successCurrentUserQueryHandler = jest.fn().mockResolvedValue(currentUserResponse);
+ const noCurrentUserQueryHandler = jest.fn().mockResolvedValue(currentUserNullResponse);
+ const successUpdateWorkItemMutationHandler = jest
+ .fn()
+ .mockResolvedValue(updateWorkItemMutationResponse);
+ const successSearchWithNoMatchingUsers = jest
+ .fn()
+ .mockResolvedValue(projectMembersResponseWithNoMatchingUsers);
+
+ const errorHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
+
+ const showDropdown = () => {
+ findSidebarDropdownWidget().vm.$emit('dropdownShown');
+ };
+
+ const createComponent = ({
+ assignees = mockAssignees,
+ searchQueryHandler = successSearchQueryHandler,
+ currentUserQueryHandler = successCurrentUserQueryHandler,
+ allowsMultipleAssignees = false,
+ canInviteMembers = false,
+ canUpdate = true,
+ } = {}) => {
+ const apolloProvider = createMockApollo([
+ [usersSearchQuery, searchQueryHandler],
+ [groupUsersSearchQuery, successGroupSearchQueryHandler],
+ [currentUserQuery, currentUserQueryHandler],
+ [updateWorkItemMutation, successUpdateWorkItemMutationHandler],
+ ]);
+
+ wrapper = shallowMountExtended(WorkItemAssignees, {
+ provide: {
+ isGroup: false,
+ },
+ propsData: {
+ assignees,
+ fullPath: 'test-project-path',
+ workItemId,
+ allowsMultipleAssignees,
+ workItemType: 'Task',
+ canUpdate,
+ canInviteMembers,
+ },
+ apolloProvider,
+ });
+ };
+
+ it('has "Assignee" label for single select', () => {
+ createComponent();
+
+ expect(findSidebarDropdownWidget().props('dropdownLabel')).toBe('Assignee');
+ });
+
+ describe('Dropdown search', () => {
+ it('shows no items in the dropdown when no results matching', async () => {
+ createComponent({ searchQueryHandler: successSearchWithNoMatchingUsers });
+ showDropdown();
+ await waitForPromises();
+
+ expect(findSidebarDropdownWidget().props('listItems')).toHaveLength(0);
+ });
+
+ it('emits error event if search users query fails', async () => {
+ createComponent({ searchQueryHandler: errorHandler });
+ showDropdown();
+ await waitForPromises();
+
+ expect(wrapper.emitted('error')).toEqual([[i18n.fetchError]]);
+ });
+ });
+
+ describe('when assigning to current user', () => {
+ it('does not show `Assign yourself` button if current user is loading', () => {
+ createComponent();
+
+ expect(findAssignSelfButton().exists()).toBe(false);
+ });
+
+ it('does now show `Assign yourself` button if user is not logged in', async () => {
+ createComponent({ currentUserQueryHandler: noCurrentUserQueryHandler, assignees: [] });
+ await waitForPromises();
+
+ expect(findAssignSelfButton().exists()).toBe(false);
+ });
+ });
+
+ describe('Dropdown options', () => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true });
+ });
+
+ it('calls successSearchQueryHandler with variables when dropdown is opened', async () => {
+ showDropdown();
+
+ await waitForPromises();
+
+ expect(successSearchQueryHandler).toHaveBeenCalledWith({
+ first: DEFAULT_PAGE_SIZE_ASSIGNEES,
+ fullPath: 'test-project-path',
+ search: '',
+ });
+ });
+
+ it('shows the skeleton loader when the items are being fetched on click', async () => {
+ showDropdown();
+
+ await nextTick();
+
+ expect(findSidebarDropdownWidget().props('loading')).toBe(true);
+ });
+
+ it('shows the iterations in dropdown when the items have finished fetching', async () => {
+ showDropdown();
+
+ await waitForPromises();
+
+ expect(findSidebarDropdownWidget().props('loading')).toBe(false);
+ expect(findSidebarDropdownWidget().props('listItems')).toHaveLength(
+ projectMembersResponseWithCurrentUser.data.workspace.users.nodes.length,
+ );
+ });
+ });
+
+ describe('when user is logged in and there are no assignees', () => {
+ beforeEach(() => {
+ createComponent({ assignees: [] });
+ return waitForPromises();
+ });
+
+ it('renders `Assign yourself` button', () => {
+ expect(findAssignSelfButton().exists()).toBe(true);
+ });
+
+ it('calls update work item assignees mutation with current user as a variable on button click', async () => {
+ const { currentUser } = currentUserResponse.data;
+ findAssignSelfButton().vm.$emit('click', new MouseEvent('click'));
+ await nextTick();
+
+ expect(successUpdateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: workItemId,
+ assigneesWidget: {
+ assigneeIds: [currentUser.id],
+ },
+ },
+ });
+ });
+ });
+
+ describe('when multiple assignees are allowed', () => {
+ beforeEach(() => {
+ createComponent({ allowsMultipleAssignees: true, assignees: [] });
+ return waitForPromises();
+ });
+
+ it('renders `Assignees` as label and `Select assignees` as dropdown button header', () => {
+ expect(findSidebarDropdownWidget().props()).toMatchObject({
+ dropdownLabel: 'Assignees',
+ headerText: 'Select assignees',
+ });
+ });
+
+ it('adds multiple assignees when collapsible listbox provides multiple values', async () => {
+ showDropdown();
+ await waitForPromises();
+
+ findSidebarDropdownWidget().vm.$emit('updateValue', [
+ 'gid://gitlab/User/5',
+ 'gid://gitlab/User/6',
+ ]);
+ await nextTick();
+
+ expect(findSidebarDropdownWidget().props('itemValue')).toHaveLength(2);
+ });
+ });
+
+ describe('tracking', () => {
+ let trackingSpy;
+
+ beforeEach(() => {
+ createComponent();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ trackingSpy = null;
+ });
+
+ it('tracks editing the assignees on dropdown widget updateValue', async () => {
+ showDropdown();
+ await waitForPromises();
+
+ findSidebarDropdownWidget().vm.$emit('updateValue', mockAssignees[0].id);
+ await waitForPromises();
+
+ expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_assignees', {
+ category: TRACKING_CATEGORY_SHOW,
+ label: 'item_assignees',
+ property: 'type_Task',
+ });
+ });
+ });
+
+ describe('invite members', () => {
+ it('does not render `Invite members` link if user has no permission to invite members', () => {
+ createComponent();
+
+ expect(findInviteMembersTrigger().exists()).toBe(false);
+ });
+
+ it('renders `Invite members` link if user has a permission to invite members', () => {
+ createComponent({ canInviteMembers: true });
+
+ expect(findInviteMembersTrigger().exists()).toBe(true);
+ });
+ });
+
+ describe('load more assignees', () => {
+ it('does not have infinite scroll when no matching users', async () => {
+ createComponent({ searchQueryHandler: successSearchWithNoMatchingUsers });
+
+ showDropdown();
+ await waitForPromises();
+
+ expect(findSidebarDropdownWidget().props('infiniteScroll')).toBe(false);
+ });
+
+ it('does not trigger load more when does not have next page', async () => {
+ createComponent();
+
+ showDropdown();
+ await waitForPromises();
+
+ expect(findSidebarDropdownWidget().props('infiniteScroll')).toBe(false);
+ });
+
+ it('triggers load more when there are more users', async () => {
+ createComponent({ searchQueryHandler: successSearchQueryHandlerWithMoreAssignees });
+
+ showDropdown();
+ await waitForPromises();
+
+ findSidebarDropdownWidget().vm.$emit('bottomReached');
+ await waitForPromises();
+
+ expect(successSearchQueryHandlerWithMoreAssignees).toHaveBeenCalledWith({
+ first: DEFAULT_PAGE_SIZE_ASSIGNEES,
+ after:
+ projectMembersResponseWithCurrentUserWithNextPage.data.workspace.users.pageInfo.endCursor,
+ search: '',
+ fullPath: 'test-project-path',
+ });
+ });
+ });
+});