summaryrefslogtreecommitdiff
path: root/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js')
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js255
1 files changed, 40 insertions, 215 deletions
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
index 824f6d49c65..0e052abffeb 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
@@ -1,27 +1,20 @@
import { GlSearchBoxByType, GlDropdown } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { cloneDeep } from 'lodash';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
-import searchUsersQuery from '~/graphql_shared/queries/users_search.query.graphql';
import { IssuableType } from '~/issue_show/constants';
import SidebarAssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarInviteMembers from '~/sidebar/components/assignees/sidebar_invite_members.vue';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { ASSIGNEES_DEBOUNCE_DELAY } from '~/sidebar/constants';
-import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue';
-import getIssueParticipantsQuery from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql';
+import getIssueAssigneesQuery from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql';
import updateIssueAssigneesMutation from '~/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql';
-import {
- issuableQueryResponse,
- searchQueryResponse,
- updateIssueAssigneesMutationResponse,
-} from '../../mock_data';
+import UserSelect from '~/vue_shared/components/user_select/user_select.vue';
+import { issuableQueryResponse, updateIssueAssigneesMutationResponse } from '../../mock_data';
jest.mock('~/flash');
@@ -50,31 +43,19 @@ describe('Sidebar assignees widget', () => {
const findAssignees = () => wrapper.findComponent(IssuableAssignees);
const findRealtimeAssignees = () => wrapper.findComponent(SidebarAssigneesRealtime);
const findEditableItem = () => wrapper.findComponent(SidebarEditableItem);
- const findDropdown = () => wrapper.findComponent(MultiSelectDropdown);
const findInviteMembersLink = () => wrapper.findComponent(SidebarInviteMembers);
- const findSearchField = () => wrapper.findComponent(GlSearchBoxByType);
-
- const findParticipantsLoading = () => wrapper.find('[data-testid="loading-participants"]');
- const findSelectedParticipants = () => wrapper.findAll('[data-testid="selected-participant"]');
- const findUnselectedParticipants = () =>
- wrapper.findAll('[data-testid="unselected-participant"]');
- const findCurrentUser = () => wrapper.findAll('[data-testid="current-user"]');
- const findUnassignLink = () => wrapper.find('[data-testid="unassign"]');
- const findEmptySearchResults = () => wrapper.find('[data-testid="empty-results"]');
+ const findUserSelect = () => wrapper.findComponent(UserSelect);
const expandDropdown = () => wrapper.vm.$refs.toggle.expand();
const createComponent = ({
- search = '',
issuableQueryHandler = jest.fn().mockResolvedValue(issuableQueryResponse),
- searchQueryHandler = jest.fn().mockResolvedValue(searchQueryResponse),
updateIssueAssigneesMutationHandler = updateIssueAssigneesMutationSuccess,
props = {},
provide = {},
} = {}) => {
fakeApollo = createMockApollo([
- [getIssueParticipantsQuery, issuableQueryHandler],
- [searchUsersQuery, searchQueryHandler],
+ [getIssueAssigneesQuery, issuableQueryHandler],
[updateIssueAssigneesMutation, updateIssueAssigneesMutationHandler],
]);
wrapper = shallowMount(SidebarAssigneesWidget, {
@@ -82,15 +63,11 @@ describe('Sidebar assignees widget', () => {
apolloProvider: fakeApollo,
propsData: {
iid: '1',
+ issuableId: 0,
fullPath: '/mygroup/myProject',
+ allowMultipleAssignees: true,
...props,
},
- data() {
- return {
- search,
- selected: [],
- };
- },
provide: {
canUpdate: true,
rootPath: '/',
@@ -98,7 +75,7 @@ describe('Sidebar assignees widget', () => {
},
stubs: {
SidebarEditableItem,
- MultiSelectDropdown,
+ UserSelect,
GlSearchBoxByType,
GlDropdown,
},
@@ -148,19 +125,6 @@ describe('Sidebar assignees widget', () => {
expect(findEditableItem().props('title')).toBe('Assignee');
});
-
- describe('when expanded', () => {
- it('renders a loading spinner if participants are loading', () => {
- createComponent({
- props: {
- initialAssignees,
- },
- });
- expandDropdown();
-
- expect(findParticipantsLoading().exists()).toBe(true);
- });
- });
});
describe('without passed initial assignees', () => {
@@ -198,7 +162,7 @@ describe('Sidebar assignees widget', () => {
findAssignees().vm.$emit('assign-self');
expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
- assigneeUsernames: 'root',
+ assigneeUsernames: ['root'],
fullPath: '/mygroup/myProject',
iid: '1',
});
@@ -220,7 +184,7 @@ describe('Sidebar assignees widget', () => {
findAssignees().vm.$emit('assign-self');
expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
- assigneeUsernames: 'root',
+ assigneeUsernames: ['root'],
fullPath: '/mygroup/myProject',
iid: '1',
});
@@ -245,16 +209,20 @@ describe('Sidebar assignees widget', () => {
]);
});
- it('renders current user if they are not in participants or assignees', async () => {
- gon.current_username = 'random';
- gon.current_user_fullname = 'Mr Random';
- gon.current_user_avatar_url = '/random';
-
+ it('does not trigger mutation or fire event when editing and exiting without making changes', async () => {
createComponent();
+
await waitForPromises();
- expandDropdown();
- expect(findCurrentUser().exists()).toBe(true);
+ findEditableItem().vm.$emit('open');
+
+ await waitForPromises();
+
+ findEditableItem().vm.$emit('close');
+
+ expect(findEditableItem().props('isDirty')).toBe(false);
+ expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledTimes(0);
+ expect(wrapper.emitted('assignees-updated')).toBe(undefined);
});
describe('when expanded', () => {
@@ -264,27 +232,18 @@ describe('Sidebar assignees widget', () => {
expandDropdown();
});
- it('collapses the widget on multiselect dropdown toggle event', async () => {
- findDropdown().vm.$emit('toggle');
+ it('collapses the widget on user select toggle event', async () => {
+ findUserSelect().vm.$emit('toggle');
await nextTick();
- expect(findDropdown().isVisible()).toBe(false);
+ expect(findUserSelect().isVisible()).toBe(false);
});
- it('renders participants list with correct amount of selected and unselected', async () => {
- expect(findSelectedParticipants()).toHaveLength(1);
- expect(findUnselectedParticipants()).toHaveLength(2);
- });
-
- it('does not render current user if they are in participants', () => {
- expect(findCurrentUser().exists()).toBe(false);
- });
-
- it('unassigns all participants when clicking on `Unassign`', () => {
- findUnassignLink().vm.$emit('click');
+ it('calls an update mutation with correct variables on User Select input event', () => {
+ findUserSelect().vm.$emit('input', [{ username: 'root' }]);
findEditableItem().vm.$emit('close');
expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
- assigneeUsernames: [],
+ assigneeUsernames: ['root'],
fullPath: '/mygroup/myProject',
iid: '1',
});
@@ -293,68 +252,38 @@ describe('Sidebar assignees widget', () => {
describe('when multiselect is disabled', () => {
beforeEach(async () => {
- createComponent({ props: { multipleAssignees: false } });
+ createComponent({ props: { allowMultipleAssignees: false } });
await waitForPromises();
expandDropdown();
});
- it('adds a single assignee when clicking on unselected user', async () => {
- findUnselectedParticipants().at(0).vm.$emit('click');
+ it('closes a dropdown after User Select input event', async () => {
+ findUserSelect().vm.$emit('input', [{ username: 'root' }]);
expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
assigneeUsernames: ['root'],
fullPath: '/mygroup/myProject',
iid: '1',
});
- });
- it('removes an assignee when clicking on selected user', () => {
- findSelectedParticipants().at(0).vm.$emit('click', new Event('click'));
+ await waitForPromises();
- expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
- assigneeUsernames: [],
- fullPath: '/mygroup/myProject',
- iid: '1',
- });
+ expect(findUserSelect().isVisible()).toBe(false);
});
});
describe('when multiselect is enabled', () => {
beforeEach(async () => {
- createComponent({ props: { multipleAssignees: true } });
+ createComponent({ props: { allowMultipleAssignees: true } });
await waitForPromises();
expandDropdown();
});
- it('adds a few assignees after clicking on unselected users and closing a dropdown', () => {
- findUnselectedParticipants().at(0).vm.$emit('click');
- findUnselectedParticipants().at(1).vm.$emit('click');
- findEditableItem().vm.$emit('close');
-
- expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
- assigneeUsernames: ['francina.skiles', 'root', 'johndoe'],
- fullPath: '/mygroup/myProject',
- iid: '1',
- });
- });
-
- it('removes an assignee when clicking on selected user and then closing dropdown', () => {
- findSelectedParticipants().at(0).vm.$emit('click', new Event('click'));
-
- findEditableItem().vm.$emit('close');
-
- expect(updateIssueAssigneesMutationSuccess).toHaveBeenCalledWith({
- assigneeUsernames: [],
- fullPath: '/mygroup/myProject',
- iid: '1',
- });
- });
-
it('does not call a mutation when clicking on participants until dropdown is closed', () => {
- findUnselectedParticipants().at(0).vm.$emit('click');
- findSelectedParticipants().at(0).vm.$emit('click', new Event('click'));
+ findUserSelect().vm.$emit('input', [{ username: 'root' }]);
expect(updateIssueAssigneesMutationSuccess).not.toHaveBeenCalled();
+ expect(findUserSelect().isVisible()).toBe(true);
});
});
@@ -363,7 +292,7 @@ describe('Sidebar assignees widget', () => {
await waitForPromises();
expandDropdown();
- findUnassignLink().vm.$emit('click');
+ findUserSelect().vm.$emit('input', []);
findEditableItem().vm.$emit('close');
await waitForPromises();
@@ -372,95 +301,6 @@ describe('Sidebar assignees widget', () => {
message: 'An error occurred while updating assignees.',
});
});
-
- describe('when searching', () => {
- it('does not show loading spinner when debounce timer is still running', async () => {
- createComponent({ search: 'roo' });
- await waitForPromises();
- expandDropdown();
-
- expect(findParticipantsLoading().exists()).toBe(false);
- });
-
- it('shows loading spinner when searching for users', async () => {
- createComponent({ search: 'roo' });
- await waitForPromises();
- expandDropdown();
- jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
- await nextTick();
-
- expect(findParticipantsLoading().exists()).toBe(true);
- });
-
- it('renders a list of found users and external participants matching search term', async () => {
- const responseCopy = cloneDeep(issuableQueryResponse);
- responseCopy.data.workspace.issuable.participants.nodes.push({
- id: 'gid://gitlab/User/5',
- avatarUrl: '/someavatar',
- name: 'Roodie',
- username: 'roodie',
- webUrl: '/roodie',
- status: null,
- });
-
- const issuableQueryHandler = jest.fn().mockResolvedValue(responseCopy);
-
- createComponent({ issuableQueryHandler });
- await waitForPromises();
- expandDropdown();
-
- findSearchField().vm.$emit('input', 'roo');
- await nextTick();
-
- jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
- await nextTick();
- await waitForPromises();
-
- expect(findUnselectedParticipants()).toHaveLength(3);
- });
-
- it('renders a list of found users only if no external participants match search term', async () => {
- createComponent({ search: 'roo' });
- await waitForPromises();
- expandDropdown();
- jest.advanceTimersByTime(250);
- await nextTick();
- await waitForPromises();
-
- expect(findUnselectedParticipants()).toHaveLength(2);
- });
-
- it('shows a message about no matches if search returned an empty list', async () => {
- const responseCopy = cloneDeep(searchQueryResponse);
- responseCopy.data.workspace.users.nodes = [];
-
- createComponent({
- search: 'roo',
- searchQueryHandler: jest.fn().mockResolvedValue(responseCopy),
- });
- await waitForPromises();
- expandDropdown();
- jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
- await nextTick();
- await waitForPromises();
-
- expect(findUnselectedParticipants()).toHaveLength(0);
- expect(findEmptySearchResults().exists()).toBe(true);
- });
-
- it('shows an error if search query was rejected', async () => {
- createComponent({ search: 'roo', searchQueryHandler: mockError });
- await waitForPromises();
- expandDropdown();
- jest.advanceTimersByTime(250);
- await nextTick();
- await waitForPromises();
-
- expect(createFlash).toHaveBeenCalledWith({
- message: 'An error occurred while searching users.',
- });
- });
- });
});
describe('when user is not signed in', () => {
@@ -469,11 +309,6 @@ describe('Sidebar assignees widget', () => {
createComponent();
});
- it('does not show current user in the dropdown', () => {
- expandDropdown();
- expect(findCurrentUser().exists()).toBe(false);
- });
-
it('passes signedIn prop as false to IssuableAssignees', () => {
expect(findAssignees().props('signedIn')).toBe(false);
});
@@ -507,17 +342,17 @@ describe('Sidebar assignees widget', () => {
expect(findEditableItem().props('isDirty')).toBe(false);
});
- it('passes truthy `isDirty` prop if selected users list was changed', async () => {
+ it('passes truthy `isDirty` prop after User Select component emitted an input event', async () => {
expandDropdown();
expect(findEditableItem().props('isDirty')).toBe(false);
- findUnselectedParticipants().at(0).vm.$emit('click');
+ findUserSelect().vm.$emit('input', []);
await nextTick();
expect(findEditableItem().props('isDirty')).toBe(true);
});
it('passes falsy `isDirty` prop after dropdown is closed', async () => {
expandDropdown();
- findUnselectedParticipants().at(0).vm.$emit('click');
+ findUserSelect().vm.$emit('input', []);
findEditableItem().vm.$emit('close');
await waitForPromises();
expect(findEditableItem().props('isDirty')).toBe(false);
@@ -530,7 +365,7 @@ describe('Sidebar assignees widget', () => {
expect(findInviteMembersLink().exists()).toBe(false);
});
- it('does not render invite members link if `directlyInviteMembers` and `indirectlyInviteMembers` were not passed', async () => {
+ it('does not render invite members link if `directlyInviteMembers` was not passed', async () => {
createComponent();
await waitForPromises();
expect(findInviteMembersLink().exists()).toBe(false);
@@ -545,14 +380,4 @@ describe('Sidebar assignees widget', () => {
await waitForPromises();
expect(findInviteMembersLink().exists()).toBe(true);
});
-
- it('renders invite members link if `indirectlyInviteMembers` is true', async () => {
- createComponent({
- provide: {
- indirectlyInviteMembers: true,
- },
- });
- await waitForPromises();
- expect(findInviteMembersLink().exists()).toBe(true);
- });
});