diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 18:18:33 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 18:18:33 +0000 |
commit | f64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch) | |
tree | a2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /spec/frontend/invite_members | |
parent | bfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff) | |
download | gitlab-ce-f64a639bcfa1fc2bc89ca7db268f594306edfd7c.tar.gz |
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'spec/frontend/invite_members')
5 files changed, 272 insertions, 42 deletions
diff --git a/spec/frontend/invite_members/components/group_select_spec.js b/spec/frontend/invite_members/components/group_select_spec.js new file mode 100644 index 00000000000..2a6985de136 --- /dev/null +++ b/spec/frontend/invite_members/components/group_select_spec.js @@ -0,0 +1,90 @@ +import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import waitForPromises from 'helpers/wait_for_promises'; +import Api from '~/api'; +import GroupSelect from '~/invite_members/components/group_select.vue'; + +const createComponent = () => { + return mount(GroupSelect, {}); +}; + +const group1 = { id: 1, full_name: 'Group One' }; +const group2 = { id: 2, full_name: 'Group Two' }; +const allGroups = [group1, group2]; + +describe('GroupSelect', () => { + let wrapper; + + beforeEach(() => { + jest.spyOn(Api, 'groups').mockResolvedValue(allGroups); + + wrapper = createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType); + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findDropdownToggle = () => findDropdown().find('button[aria-haspopup="true"]'); + const findDropdownItemByText = (text) => + wrapper + .findAllComponents(GlDropdownItem) + .wrappers.find((dropdownItemWrapper) => dropdownItemWrapper.text() === text); + + it('renders GlSearchBoxByType with default attributes', () => { + expect(findSearchBoxByType().exists()).toBe(true); + expect(findSearchBoxByType().vm.$attrs).toMatchObject({ + placeholder: 'Search groups', + }); + }); + + describe('when user types in the search input', () => { + let resolveApiRequest; + + beforeEach(() => { + jest.spyOn(Api, 'groups').mockImplementation( + () => + new Promise((resolve) => { + resolveApiRequest = resolve; + }), + ); + + findSearchBoxByType().vm.$emit('input', group1.name); + }); + + it('calls the API', () => { + resolveApiRequest({ data: allGroups }); + + expect(Api.groups).toHaveBeenCalledWith(group1.name, { + active: true, + exclude_internal: true, + }); + }); + + it('displays loading icon while waiting for API call to resolve', async () => { + expect(findSearchBoxByType().props('isLoading')).toBe(true); + + resolveApiRequest({ data: allGroups }); + await waitForPromises(); + + expect(findSearchBoxByType().props('isLoading')).toBe(false); + }); + }); + + describe('when group is selected from the dropdown', () => { + beforeEach(() => { + findDropdownItemByText(group1.full_name).vm.$emit('click'); + }); + + it('emits `input` event used by `v-model`', () => { + expect(wrapper.emitted('input')[0][0].id).toEqual(group1.id); + }); + + it('sets dropdown toggle text to selected item', () => { + expect(findDropdownToggle().text()).toBe(group1.full_name); + }); + }); +}); diff --git a/spec/frontend/invite_members/components/invite_group_trigger_spec.js b/spec/frontend/invite_members/components/invite_group_trigger_spec.js new file mode 100644 index 00000000000..cb9967ebe8c --- /dev/null +++ b/spec/frontend/invite_members/components/invite_group_trigger_spec.js @@ -0,0 +1,50 @@ +import { GlButton } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import InviteGroupTrigger from '~/invite_members/components/invite_group_trigger.vue'; +import eventHub from '~/invite_members/event_hub'; + +const displayText = 'Invite a group'; + +const createComponent = (props = {}) => { + return mount(InviteGroupTrigger, { + propsData: { + displayText, + ...props, + }, + }); +}; + +describe('InviteGroupTrigger', () => { + let wrapper; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findButton = () => wrapper.findComponent(GlButton); + + describe('displayText', () => { + beforeEach(() => { + wrapper = createComponent(); + }); + + it('includes the correct displayText for the link', () => { + expect(findButton().text()).toBe(displayText); + }); + }); + + describe('when button is clicked', () => { + beforeEach(() => { + eventHub.$emit = jest.fn(); + + wrapper = createComponent(); + + findButton().trigger('click'); + }); + + it('emits event that triggers opening the modal', () => { + expect(eventHub.$emit).toHaveBeenLastCalledWith('openModal', { inviteeType: 'group' }); + }); + }); +}); diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js index e310a00133c..5ca5d855038 100644 --- a/spec/frontend/invite_members/components/invite_members_modal_spec.js +++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js @@ -6,10 +6,11 @@ import Api from '~/api'; import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue'; const id = '1'; -const name = 'testgroup'; +const name = 'test name'; const isProject = false; +const inviteeType = 'members'; const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 }; -const defaultAccessLevel = '10'; +const defaultAccessLevel = 10; const helpLink = 'https://example.com'; const user1 = { id: 1, name: 'Name One', username: 'one_1', avatar_url: '' }; @@ -20,16 +21,19 @@ const user3 = { username: 'one_2', avatar_url: '', }; +const sharedGroup = { id: '981' }; -const createComponent = (data = {}) => { +const createComponent = (data = {}, props = {}) => { return shallowMount(InviteMembersModal, { propsData: { id, name, isProject, + inviteeType, accessLevels, defaultAccessLevel, helpLink, + ...props, }, data() { return data; @@ -46,6 +50,22 @@ const createComponent = (data = {}) => { }); }; +const createInviteMembersToProjectWrapper = () => { + return createComponent({ inviteeType: 'members' }, { isProject: true }); +}; + +const createInviteMembersToGroupWrapper = () => { + return createComponent({ inviteeType: 'members' }, { isProject: false }); +}; + +const createInviteGroupToProjectWrapper = () => { + return createComponent({ inviteeType: 'group' }, { isProject: true }); +}; + +const createInviteGroupToGroupWrapper = () => { + return createComponent({ inviteeType: 'group' }, { isProject: false }); +}; + describe('InviteMembersModal', () => { let wrapper; @@ -54,12 +74,13 @@ describe('InviteMembersModal', () => { wrapper = null; }); - const findDropdown = () => wrapper.find(GlDropdown); - const findDropdownItems = () => findDropdown().findAll(GlDropdownItem); - const findDatepicker = () => wrapper.find(GlDatepicker); - const findLink = () => wrapper.find(GlLink); - const findCancelButton = () => wrapper.find({ ref: 'cancelButton' }); - const findInviteButton = () => wrapper.find({ ref: 'inviteButton' }); + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findDropdownItems = () => findDropdown().findAllComponents(GlDropdownItem); + const findDatepicker = () => wrapper.findComponent(GlDatepicker); + const findLink = () => wrapper.findComponent(GlLink); + const findIntroText = () => wrapper.find({ ref: 'introText' }).text(); + const findCancelButton = () => wrapper.findComponent({ ref: 'cancelButton' }); + const findInviteButton = () => wrapper.findComponent({ ref: 'inviteButton' }); const clickInviteButton = () => findInviteButton().vm.$emit('click'); describe('rendering the modal', () => { @@ -68,7 +89,7 @@ describe('InviteMembersModal', () => { }); it('renders the modal with the correct title', () => { - expect(wrapper.find(GlModal).props('title')).toBe('Invite team members'); + expect(wrapper.findComponent(GlModal).props('title')).toBe('Invite team members'); }); it('renders the Cancel button text correctly', () => { @@ -102,21 +123,60 @@ describe('InviteMembersModal', () => { }); }); + describe('displaying the correct introText', () => { + describe('when inviting to a project', () => { + describe('when inviting members', () => { + it('includes the correct invitee, type, and formatted name', () => { + wrapper = createInviteMembersToProjectWrapper(); + + expect(findIntroText()).toBe("You're inviting members to the test name project."); + }); + }); + + describe('when sharing with a group', () => { + it('includes the correct invitee, type, and formatted name', () => { + wrapper = createInviteGroupToProjectWrapper(); + + expect(findIntroText()).toBe("You're inviting a group to the test name project."); + }); + }); + }); + + describe('when inviting to a group', () => { + describe('when inviting members', () => { + it('includes the correct invitee, type, and formatted name', () => { + wrapper = createInviteMembersToGroupWrapper(); + + expect(findIntroText()).toBe("You're inviting members to the test name group."); + }); + }); + + describe('when sharing with a group', () => { + it('includes the correct invitee, type, and formatted name', () => { + wrapper = createInviteGroupToGroupWrapper(); + + expect(findIntroText()).toBe("You're inviting a group to the test name group."); + }); + }); + }); + }); + describe('submitting the invite form', () => { const apiErrorMessage = 'Member already exists'; describe('when inviting an existing user to group by user ID', () => { const postData = { user_id: '1', - access_level: '10', + access_level: defaultAccessLevel, expires_at: undefined, format: 'json', }; describe('when invites are sent successfully', () => { beforeEach(() => { - wrapper = createComponent({ newUsersToInvite: [user1] }); + wrapper = createInviteMembersToGroupWrapper(); + wrapper.setData({ newUsersToInvite: [user1] }); wrapper.vm.$toast = { show: jest.fn() }; jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData }); jest.spyOn(wrapper.vm, 'showToastMessageSuccess'); @@ -178,7 +238,7 @@ describe('InviteMembersModal', () => { describe('when inviting a new user by email address', () => { const postData = { - access_level: '10', + access_level: defaultAccessLevel, expires_at: undefined, email: 'email@example.com', format: 'json', @@ -227,7 +287,7 @@ describe('InviteMembersModal', () => { describe('when inviting members and non-members in same click', () => { const postData = { - access_level: '10', + access_level: defaultAccessLevel, expires_at: undefined, format: 'json', }; @@ -283,5 +343,58 @@ describe('InviteMembersModal', () => { }); }); }); + + describe('when inviting a group to share', () => { + describe('when sharing the group is successful', () => { + const groupPostData = { + group_id: sharedGroup.id, + group_access: defaultAccessLevel, + expires_at: undefined, + format: 'json', + }; + + beforeEach(() => { + wrapper = createComponent({ groupToBeSharedWith: sharedGroup }); + + wrapper.setData({ inviteeType: 'group' }); + wrapper.vm.$toast = { show: jest.fn() }; + jest.spyOn(Api, 'groupShareWithGroup').mockResolvedValue({ data: groupPostData }); + jest.spyOn(wrapper.vm, 'showToastMessageSuccess'); + + clickInviteButton(); + }); + + it('calls Api groupShareWithGroup with the correct params', () => { + expect(Api.groupShareWithGroup).toHaveBeenCalledWith(id, groupPostData); + }); + + it('displays the successful toastMessage', () => { + expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled(); + }); + }); + + describe('when sharing the group fails', () => { + beforeEach(() => { + wrapper = createComponent({ groupToBeSharedWith: sharedGroup }); + + wrapper.setData({ inviteeType: 'group' }); + wrapper.vm.$toast = { show: jest.fn() }; + + jest + .spyOn(Api, 'groupShareWithGroup') + .mockRejectedValue({ response: { data: { success: false } } }); + + jest.spyOn(wrapper.vm, 'showToastMessageError'); + + clickInviteButton(); + }); + + it('displays the generic error toastMessage', async () => { + await waitForPromises(); + + expect(wrapper.vm.showToastMessageError).toHaveBeenCalled(); + }); + }); + }); }); }); diff --git a/spec/frontend/invite_members/components/invite_members_trigger_spec.js b/spec/frontend/invite_members/components/invite_members_trigger_spec.js index 18d6662d2d4..f362aace1df 100644 --- a/spec/frontend/invite_members/components/invite_members_trigger_spec.js +++ b/spec/frontend/invite_members/components/invite_members_trigger_spec.js @@ -1,9 +1,8 @@ -import { GlIcon, GlLink } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; const displayText = 'Invite team members'; -const icon = 'plus'; const createComponent = (props = {}) => { return shallowMount(InviteMembersTrigger, { @@ -23,36 +22,14 @@ describe('InviteMembersTrigger', () => { }); describe('displayText', () => { - const findLink = () => wrapper.find(GlLink); + const findButton = () => wrapper.findComponent(GlButton); beforeEach(() => { wrapper = createComponent(); }); - it('includes the correct displayText for the link', () => { - expect(findLink().text()).toBe(displayText); - }); - }); - - describe('icon', () => { - const findIcon = () => wrapper.find(GlIcon); - - it('includes the correct icon when an icon is sent', () => { - wrapper = createComponent({ icon }); - - expect(findIcon().attributes('name')).toBe(icon); - }); - - it('does not include an icon when icon is not sent', () => { - wrapper = createComponent(); - - expect(findIcon().exists()).toBe(false); - }); - - it('does not include an icon when empty string is sent', () => { - wrapper = createComponent({ icon: '' }); - - expect(findIcon().exists()).toBe(false); + it('includes the correct displayText for the button', () => { + expect(findButton().text()).toBe(displayText); }); }); }); diff --git a/spec/frontend/invite_members/components/members_token_select_spec.js b/spec/frontend/invite_members/components/members_token_select_spec.js index a945b99bd54..f6e79d3607f 100644 --- a/spec/frontend/invite_members/components/members_token_select_spec.js +++ b/spec/frontend/invite_members/components/members_token_select_spec.js @@ -37,7 +37,7 @@ describe('MembersTokenSelect', () => { wrapper = null; }); - const findTokenSelector = () => wrapper.find(GlTokenSelector); + const findTokenSelector = () => wrapper.findComponent(GlTokenSelector); describe('rendering the token-selector component', () => { it('renders with the correct props', () => { |