diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-21 07:08:36 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-21 07:08:36 +0000 |
commit | 48aff82709769b098321c738f3444b9bdaa694c6 (patch) | |
tree | e00c7c43e2d9b603a5a6af576b1685e400410dee /spec/frontend/vue_shared/components/members/action_buttons | |
parent | 879f5329ee916a948223f8f43d77fba4da6cd028 (diff) | |
download | gitlab-ce-48aff82709769b098321c738f3444b9bdaa694c6.tar.gz |
Add latest changes from gitlab-org/gitlab@13-5-stable-eev13.5.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/components/members/action_buttons')
8 files changed, 611 insertions, 0 deletions
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js new file mode 100644 index 00000000000..58cb8ef61d1 --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js @@ -0,0 +1,108 @@ +import { shallowMount } from '@vue/test-utils'; +import AccessRequestActionButtons from '~/vue_shared/components/members/action_buttons/access_request_action_buttons.vue'; +import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue'; +import ApproveAccessRequestButton from '~/vue_shared/components/members/action_buttons/approve_access_request_button.vue'; +import { accessRequest as member } from '../mock_data'; + +describe('AccessRequestActionButtons', () => { + let wrapper; + + const createComponent = (propsData = {}) => { + wrapper = shallowMount(AccessRequestActionButtons, { + propsData: { + member, + isCurrentUser: true, + ...propsData, + }, + }); + }; + + const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton); + const findApproveButton = () => wrapper.find(ApproveAccessRequestButton); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when user has `canRemove` permissions', () => { + beforeEach(() => { + createComponent({ + permissions: { + canRemove: true, + }, + }); + }); + + it('renders remove member button', () => { + expect(findRemoveMemberButton().exists()).toBe(true); + }); + + it('sets props correctly', () => { + expect(findRemoveMemberButton().props()).toMatchObject({ + memberId: member.id, + title: 'Deny access', + isAccessRequest: true, + icon: 'close', + }); + }); + + describe('when member is the current user', () => { + it('sets `message` prop correctly', () => { + expect(findRemoveMemberButton().props('message')).toBe( + `Are you sure you want to withdraw your access request for "${member.source.name}"`, + ); + }); + }); + + describe('when member is not the current user', () => { + it('sets `message` prop correctly', () => { + createComponent({ + isCurrentUser: false, + permissions: { + canRemove: true, + }, + }); + + expect(findRemoveMemberButton().props('message')).toBe( + `Are you sure you want to deny ${member.user.name}'s request to join "${member.source.name}"`, + ); + }); + }); + }); + + describe('when user does not have `canRemove` permissions', () => { + it('does not render remove member button', () => { + createComponent({ + permissions: { + canRemove: false, + }, + }); + + expect(findRemoveMemberButton().exists()).toBe(false); + }); + }); + + describe('when user has `canUpdate` permissions', () => { + it('renders the approve button', () => { + createComponent({ + permissions: { + canUpdate: true, + }, + }); + + expect(findApproveButton().exists()).toBe(true); + }); + }); + + describe('when user does not have `canUpdate` permissions', () => { + it('does not render the approve button', () => { + createComponent({ + permissions: { + canUpdate: false, + }, + }); + + expect(findApproveButton().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js new file mode 100644 index 00000000000..93edaaa400d --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js @@ -0,0 +1,74 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { GlButton, GlForm } from '@gitlab/ui'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import ApproveAccessRequestButton from '~/vue_shared/components/members/action_buttons/approve_access_request_button.vue'; + +jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('ApproveAccessRequestButton', () => { + let wrapper; + + const createStore = (state = {}) => { + return new Vuex.Store({ + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }); + }; + + const createComponent = (propsData = {}, state) => { + wrapper = shallowMount(ApproveAccessRequestButton, { + localVue, + store: createStore(state), + propsData: { + memberId: 1, + ...propsData, + }, + directives: { + GlTooltip: createMockDirective(), + }, + }); + }; + + const findForm = () => wrapper.find(GlForm); + const findButton = () => findForm().find(GlButton); + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('displays a tooltip', () => { + const button = findButton(); + + expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined(); + expect(button.attributes('title')).toBe('Grant access'); + }); + + it('sets `aria-label` attribute', () => { + expect(findButton().attributes('aria-label')).toBe('Grant access'); + }); + + it('submits the form when button is clicked', () => { + expect(findButton().attributes('type')).toBe('submit'); + }); + + it('displays form with correct action and inputs', () => { + const form = findForm(); + + expect(form.attributes('action')).toBe( + '/groups/foo-bar/-/group_members/1/approve_access_request', + ); + expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe( + 'mock-csrf-token', + ); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js new file mode 100644 index 00000000000..1374cdc6aef --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js @@ -0,0 +1,85 @@ +import { shallowMount } from '@vue/test-utils'; +import InviteActionButtons from '~/vue_shared/components/members/action_buttons/invite_action_buttons.vue'; +import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue'; +import ResendInviteButton from '~/vue_shared/components/members/action_buttons/resend_invite_button.vue'; +import { invite as member } from '../mock_data'; + +describe('InviteActionButtons', () => { + let wrapper; + + const createComponent = (propsData = {}) => { + wrapper = shallowMount(InviteActionButtons, { + propsData: { + member, + ...propsData, + }, + }); + }; + + const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton); + const findResendInviteButton = () => wrapper.find(ResendInviteButton); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when user has `canRemove` permissions', () => { + beforeEach(() => { + createComponent({ + permissions: { + canRemove: true, + }, + }); + }); + + it('renders remove member button', () => { + expect(findRemoveMemberButton().exists()).toBe(true); + }); + + it('sets props correctly', () => { + expect(findRemoveMemberButton().props()).toEqual({ + memberId: member.id, + message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.name}"`, + title: 'Revoke invite', + isAccessRequest: false, + icon: 'remove', + }); + }); + }); + + describe('when user does not have `canRemove` permissions', () => { + it('does not render remove member button', () => { + createComponent({ + permissions: { + canRemove: false, + }, + }); + + expect(findRemoveMemberButton().exists()).toBe(false); + }); + }); + + describe('when user has `canResend` permissions', () => { + it('renders resend invite button', () => { + createComponent({ + permissions: { + canResend: true, + }, + }); + + expect(findResendInviteButton().exists()).toBe(true); + }); + }); + + describe('when user does not have `canResend` permissions', () => { + it('does not render resend invite button', () => { + createComponent({ + permissions: { + canResend: false, + }, + }); + + expect(findResendInviteButton().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js new file mode 100644 index 00000000000..00896b23b95 --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js @@ -0,0 +1,59 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import LeaveButton from '~/vue_shared/components/members/action_buttons/leave_button.vue'; +import LeaveModal from '~/vue_shared/components/members/modals/leave_modal.vue'; +import { LEAVE_MODAL_ID } from '~/vue_shared/components/members/constants'; +import { member } from '../mock_data'; + +describe('LeaveButton', () => { + let wrapper; + + const createComponent = (propsData = {}) => { + wrapper = shallowMount(LeaveButton, { + propsData: { + member, + ...propsData, + }, + directives: { + GlTooltip: createMockDirective(), + GlModal: createMockDirective(), + }, + }); + }; + + const findButton = () => wrapper.find(GlButton); + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('displays a tooltip', () => { + const button = findButton(); + + expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined(); + expect(button.attributes('title')).toBe('Leave'); + }); + + it('sets `aria-label` attribute', () => { + expect(findButton().attributes('aria-label')).toBe('Leave'); + }); + + it('renders leave modal', () => { + const leaveModal = wrapper.find(LeaveModal); + + expect(leaveModal.exists()).toBe(true); + expect(leaveModal.props('member')).toEqual(member); + }); + + it('triggers leave modal', () => { + const binding = getBinding(findButton().element, 'gl-modal'); + + expect(binding).not.toBeUndefined(); + expect(binding.value).toBe(LEAVE_MODAL_ID); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js new file mode 100644 index 00000000000..84fe1c51773 --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js @@ -0,0 +1,64 @@ +import { mount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { GlButton } from '@gitlab/ui'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import RemoveGroupLinkButton from '~/vue_shared/components/members/action_buttons/remove_group_link_button.vue'; +import { group } from '../mock_data'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('RemoveGroupLinkButton', () => { + let wrapper; + + const actions = { + showRemoveGroupLinkModal: jest.fn(), + }; + + const createStore = () => { + return new Vuex.Store({ + actions, + }); + }; + + const createComponent = () => { + wrapper = mount(RemoveGroupLinkButton, { + localVue, + store: createStore(), + propsData: { + groupLink: group, + }, + directives: { + GlTooltip: createMockDirective(), + }, + }); + }; + + const findButton = () => wrapper.find(GlButton); + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('displays a tooltip', () => { + const button = findButton(); + + expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined(); + expect(button.attributes('title')).toBe('Remove group'); + }); + + it('sets `aria-label` attribute', () => { + expect(findButton().attributes('aria-label')).toBe('Remove group'); + }); + + it('calls Vuex action to open remove group link modal when clicked', () => { + findButton().trigger('click'); + + expect(actions.showRemoveGroupLinkModal).toHaveBeenCalledWith(expect.any(Object), group); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js new file mode 100644 index 00000000000..7aa30494234 --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js @@ -0,0 +1,66 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('RemoveMemberButton', () => { + let wrapper; + + const createStore = (state = {}) => { + return new Vuex.Store({ + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }); + }; + + const createComponent = (propsData = {}, state) => { + wrapper = shallowMount(RemoveMemberButton, { + localVue, + store: createStore(state), + propsData: { + memberId: 1, + message: 'Are you sure you want to remove John Smith?', + title: 'Remove member', + isAccessRequest: true, + ...propsData, + }, + directives: { + GlTooltip: createMockDirective(), + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('sets attributes on button', () => { + createComponent(); + + expect(wrapper.attributes()).toMatchObject({ + 'data-member-path': '/groups/foo-bar/-/group_members/1', + 'data-message': 'Are you sure you want to remove John Smith?', + 'data-is-access-request': 'true', + 'aria-label': 'Remove member', + title: 'Remove member', + icon: 'remove', + }); + }); + + it('displays `title` prop as a tooltip', () => { + createComponent(); + + expect(getBinding(wrapper.element, 'gl-tooltip')).not.toBeUndefined(); + }); + + it('has CSS class used by `remove_member_modal.vue`', () => { + createComponent(); + + expect(wrapper.classes()).toContain('js-remove-member-button'); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js new file mode 100644 index 00000000000..859fdd01043 --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js @@ -0,0 +1,66 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { GlButton } from '@gitlab/ui'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import ResendInviteButton from '~/vue_shared/components/members/action_buttons/resend_invite_button.vue'; + +jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('ResendInviteButton', () => { + let wrapper; + + const createStore = (state = {}) => { + return new Vuex.Store({ + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }); + }; + + const createComponent = (propsData = {}, state) => { + wrapper = shallowMount(ResendInviteButton, { + localVue, + store: createStore(state), + propsData: { + memberId: 1, + ...propsData, + }, + directives: { + GlTooltip: createMockDirective(), + }, + }); + }; + + const findForm = () => wrapper.find('form'); + const findButton = () => findForm().find(GlButton); + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('displays a tooltip', () => { + expect(getBinding(findButton().element, 'gl-tooltip')).not.toBeUndefined(); + expect(findButton().attributes('title')).toBe('Resend invite'); + }); + + it('submits the form when button is clicked', () => { + expect(findButton().attributes('type')).toBe('submit'); + }); + + it('displays form with correct action and inputs', () => { + expect(findForm().attributes('action')).toBe('/groups/foo-bar/-/group_members/1/resend_invite'); + expect( + findForm() + .find('input[name="authenticity_token"]') + .attributes('value'), + ).toBe('mock-csrf-token'); + }); +}); diff --git a/spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js new file mode 100644 index 00000000000..f766ad5b0d1 --- /dev/null +++ b/spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js @@ -0,0 +1,89 @@ +import { shallowMount } from '@vue/test-utils'; +import UserActionButtons from '~/vue_shared/components/members/action_buttons/user_action_buttons.vue'; +import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue'; +import LeaveButton from '~/vue_shared/components/members/action_buttons/leave_button.vue'; +import { member, orphanedMember } from '../mock_data'; + +describe('UserActionButtons', () => { + let wrapper; + + const createComponent = (propsData = {}) => { + wrapper = shallowMount(UserActionButtons, { + propsData: { + member, + isCurrentUser: false, + ...propsData, + }, + }); + }; + + const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when user has `canRemove` permissions', () => { + beforeEach(() => { + createComponent({ + permissions: { + canRemove: true, + }, + }); + }); + + it('renders remove member button', () => { + expect(findRemoveMemberButton().exists()).toBe(true); + }); + + it('sets props correctly', () => { + expect(findRemoveMemberButton().props()).toEqual({ + memberId: member.id, + message: `Are you sure you want to remove ${member.user.name} from "${member.source.name}"`, + title: 'Remove member', + isAccessRequest: false, + icon: 'remove', + }); + }); + + describe('when member is orphaned', () => { + it('sets `message` prop correctly', () => { + createComponent({ + member: orphanedMember, + permissions: { + canRemove: true, + }, + }); + + expect(findRemoveMemberButton().props('message')).toBe( + `Are you sure you want to remove this orphaned member from "${orphanedMember.source.name}"`, + ); + }); + }); + + describe('when member is the current user', () => { + it('renders leave button', () => { + createComponent({ + isCurrentUser: true, + permissions: { + canRemove: true, + }, + }); + + expect(wrapper.find(LeaveButton).exists()).toBe(true); + }); + }); + }); + + describe('when user does not have `canRemove` permissions', () => { + it('does not render remove member button', () => { + createComponent({ + permissions: { + canRemove: false, + }, + }); + + expect(findRemoveMemberButton().exists()).toBe(false); + }); + }); +}); |