diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-22 15:10:04 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-22 15:10:04 +0300 |
commit | cb8bd7d5946ca50a2de99645687a633a0c8105be (patch) | |
tree | a327e4da74f2a2d050a6971e0e03d41e5b0c5780 /spec/frontend/members | |
parent | 8e0dafbb6665d431f7f1b635c0f7c4a5c61bc325 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/members')
7 files changed, 218 insertions, 16 deletions
diff --git a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js index e7a99a96da6..79252456f67 100644 --- a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js +++ b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js @@ -37,7 +37,7 @@ describe('InviteActionButtons', () => { }); it('sets props correctly', () => { - expect(findRemoveMemberButton().props()).toEqual({ + expect(findRemoveMemberButton().props()).toMatchObject({ memberId: member.id, memberType: null, message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.fullName}"`, diff --git a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js index 4ff12f7fa97..d8453d453e7 100644 --- a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js +++ b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js @@ -1,6 +1,8 @@ +import { GlButton } from '@gitlab/ui'; import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import { modalData } from 'jest/members/mock_data'; import RemoveMemberButton from '~/members/components/action_buttons/remove_member_button.vue'; import { MEMBER_TYPES } from '~/members/constants'; @@ -10,6 +12,10 @@ localVue.use(Vuex); describe('RemoveMemberButton', () => { let wrapper; + const actions = { + showRemoveMemberModal: jest.fn(), + }; + const createStore = (state = {}) => { return new Vuex.Store({ modules: { @@ -19,6 +25,7 @@ describe('RemoveMemberButton', () => { memberPath: '/groups/foo-bar/-/group_members/:id', ...state, }, + actions, }, }, }); @@ -47,20 +54,16 @@ describe('RemoveMemberButton', () => { }); }; + beforeEach(() => { + createComponent(); + }); + afterEach(() => { wrapper.destroy(); }); it('sets attributes on button', () => { - createComponent(); - expect(wrapper.attributes()).toMatchObject({ - 'data-member-path': '/groups/foo-bar/-/group_members/1', - 'data-member-type': 'GroupMember', - 'data-message': 'Are you sure you want to remove John Smith?', - 'data-is-access-request': 'true', - 'data-is-invite': 'true', - 'data-oncall-schedules': '{"name":"user","schedules":[]}', 'aria-label': 'Remove member', title: 'Remove member', icon: 'remove', @@ -68,14 +71,12 @@ describe('RemoveMemberButton', () => { }); 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(); + it('calls Vuex action to show `remove member` modal when clicked', () => { + wrapper.findComponent(GlButton).vm.$emit('click'); - expect(wrapper.classes()).toContain('js-remove-member-button'); + expect(actions.showRemoveMemberModal).toHaveBeenCalledWith(expect.any(Object), modalData); }); }); diff --git a/spec/frontend/members/components/modals/remove_member_modal_spec.js b/spec/frontend/members/components/modals/remove_member_modal_spec.js new file mode 100644 index 00000000000..1dc41582c12 --- /dev/null +++ b/spec/frontend/members/components/modals/remove_member_modal_spec.js @@ -0,0 +1,133 @@ +import { GlModal } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import RemoveMemberModal from '~/members/components/modals/remove_member_modal.vue'; +import { MEMBER_TYPES } from '~/members/constants'; +import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue'; + +Vue.use(Vuex); + +describe('RemoveMemberModal', () => { + const memberPath = '/gitlab-org/gitlab-test/-/project_members/90'; + const mockSchedules = { + name: 'User1', + schedules: [{ id: 1, name: 'Schedule 1' }], + }; + let wrapper; + + const actions = { + hideRemoveMemberModal: jest.fn(), + }; + + const createStore = (removeMemberModalData) => + new Vuex.Store({ + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + removeMemberModalData, + }, + actions, + }, + }, + }); + + const createComponent = (state) => { + wrapper = shallowMount(RemoveMemberModal, { + store: createStore(state), + provide: { + namespace: MEMBER_TYPES.user, + }, + }); + }; + + const findForm = () => wrapper.find({ ref: 'form' }); + const findGlModal = () => wrapper.findComponent(GlModal); + const findOnCallSchedulesList = () => wrapper.findComponent(OncallSchedulesList); + + afterEach(() => { + wrapper.destroy(); + }); + + describe.each` + state | memberType | isAccessRequest | isInvite | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message | onCallSchedules + ${'removing a group member'} | ${'GroupMember'} | ${false} | ${false} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'} | ${{}} + ${'removing a project member'} | ${'ProjectMember'} | ${false} | ${false} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'} | ${mockSchedules} + ${'denying an access request'} | ${'ProjectMember'} | ${true} | ${false} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"} | ${{}} + ${'revoking invite'} | ${'ProjectMember'} | ${false} | ${true} | ${'Revoke invite'} | ${false} | ${false} | ${'Are you sure you want to revoke the invitation for foo@bar.com to join the Gitlab Org / Gitlab Test project?'} | ${mockSchedules} + `( + 'when $state', + ({ + actionText, + memberType, + isAccessRequest, + isInvite, + message, + removeSubMembershipsCheckboxExpected, + unassignIssuablesCheckboxExpected, + onCallSchedules, + }) => { + beforeEach(() => { + createComponent({ + isAccessRequest, + isInvite, + message, + memberPath, + memberType, + onCallSchedules, + }); + }); + + const isPartOfOncallSchedules = Boolean(isAccessRequest && onCallSchedules.schedules?.length); + + it(`has the title ${actionText}`, () => { + expect(findGlModal().attributes('title')).toBe(actionText); + }); + + it('contains a form action', () => { + expect(findForm().attributes('action')).toBe(memberPath); + }); + + it('displays a message to the user', () => { + expect(wrapper.find('p').text()).toBe(message); + }); + + it(`shows ${ + removeSubMembershipsCheckboxExpected ? 'a' : 'no' + } checkbox to remove direct memberships of subgroups/projects`, () => { + expect(wrapper.find('[name=remove_sub_memberships]').exists()).toBe( + removeSubMembershipsCheckboxExpected, + ); + }); + + it(`shows ${ + unassignIssuablesCheckboxExpected ? 'a' : 'no' + } checkbox to allow removal from related issues and MRs`, () => { + expect(wrapper.find('[name=unassign_issuables]').exists()).toBe( + unassignIssuablesCheckboxExpected, + ); + }); + + it(`shows ${isPartOfOncallSchedules ? 'all' : 'no'} related on-call schedules`, () => { + expect(findOnCallSchedulesList().exists()).toBe(isPartOfOncallSchedules); + }); + + it('submits the form when the modal is submitted', () => { + const spy = jest.spyOn(findForm().element, 'submit'); + + findGlModal().vm.$emit('primary'); + + expect(spy).toHaveBeenCalled(); + + spy.mockRestore(); + }); + + it('calls Vuex action to hide the modal when `GlModal` emits `hide` event', () => { + findGlModal().vm.$emit('hide'); + + expect(actions.hideRemoveMemberModal).toHaveBeenCalled(); + }); + }, + ); +}); diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js index 3a17d78bd17..6e7d9bc6b6e 100644 --- a/spec/frontend/members/components/table/members_table_spec.js +++ b/spec/frontend/members/components/table/members_table_spec.js @@ -72,6 +72,7 @@ describe('MembersTable', () => { 'member-action-buttons', 'role-dropdown', 'remove-group-link-modal', + 'remove-member-modal', 'expiration-datepicker', ], }); diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js index 4275db5fa9f..eb9f905fea2 100644 --- a/spec/frontend/members/mock_data.js +++ b/spec/frontend/members/mock_data.js @@ -57,6 +57,15 @@ export const group = { validRoles: { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 }, }; +export const modalData = { + isAccessRequest: true, + isInvite: true, + memberPath: '/groups/foo-bar/-/group_members/1', + memberType: 'GroupMember', + message: 'Are you sure you want to remove John Smith?', + oncallSchedules: { name: 'user', schedules: [] }, +}; + const { user, ...memberNoUser } = member; export const invite = { ...memberNoUser, diff --git a/spec/frontend/members/store/actions_spec.js b/spec/frontend/members/store/actions_spec.js index d913c5c56df..d37e6871387 100644 --- a/spec/frontend/members/store/actions_spec.js +++ b/spec/frontend/members/store/actions_spec.js @@ -3,12 +3,14 @@ import MockAdapter from 'axios-mock-adapter'; import { noop } from 'lodash'; import { useFakeDate } from 'helpers/fake_date'; import testAction from 'helpers/vuex_action_helper'; -import { members, group } from 'jest/members/mock_data'; +import { members, group, modalData } from 'jest/members/mock_data'; import httpStatusCodes from '~/lib/utils/http_status'; import { updateMemberRole, showRemoveGroupLinkModal, hideRemoveGroupLinkModal, + showRemoveMemberModal, + hideRemoveMemberModal, updateMemberExpiration, } from '~/members/store/actions'; import * as types from '~/members/store/mutation_types'; @@ -153,4 +155,32 @@ describe('Vuex members actions', () => { }); }); }); + + describe('Remove member modal', () => { + const state = { + removeMemberModalVisible: false, + removeMemberModalData: {}, + }; + + describe('showRemoveMemberModal', () => { + it(`commits ${types.SHOW_REMOVE_MEMBER_MODAL} mutation`, () => { + testAction(showRemoveMemberModal, modalData, state, [ + { + type: types.SHOW_REMOVE_MEMBER_MODAL, + payload: modalData, + }, + ]); + }); + }); + + describe('hideRemoveMemberModal', () => { + it(`commits ${types.HIDE_REMOVE_MEMBER_MODAL} mutation`, () => { + testAction(hideRemoveMemberModal, undefined, state, [ + { + type: types.HIDE_REMOVE_MEMBER_MODAL, + }, + ]); + }); + }); + }); }); diff --git a/spec/frontend/members/store/mutations_spec.js b/spec/frontend/members/store/mutations_spec.js index 7ad7034eb6d..72423c6b4d4 100644 --- a/spec/frontend/members/store/mutations_spec.js +++ b/spec/frontend/members/store/mutations_spec.js @@ -1,4 +1,4 @@ -import { members, group } from 'jest/members/mock_data'; +import { members, group, modalData } from 'jest/members/mock_data'; import * as types from '~/members/store/mutation_types'; import mutations from '~/members/store/mutations'; @@ -154,4 +154,32 @@ describe('Vuex members mutations', () => { expect(state.removeGroupLinkModalVisible).toBe(false); }); }); + + describe(types.SHOW_REMOVE_MEMBER_MODAL, () => { + it('sets `removeMemberModalVisible` and `removeMemberModalData`', () => { + const state = { + removeMemberModalVisible: false, + removeMemberModalData: {}, + }; + + mutations[types.SHOW_REMOVE_MEMBER_MODAL](state, modalData); + + expect(state).toEqual({ + removeMemberModalVisible: true, + removeMemberModalData: modalData, + }); + }); + }); + + describe(types.HIDE_REMOVE_MEMBER_MODAL, () => { + it('sets `removeMemberModalVisible` to `false`', () => { + const state = { + removeMemberModalVisible: true, + }; + + mutations[types.HIDE_REMOVE_MEMBER_MODAL](state); + + expect(state.removeMemberModalVisible).toBe(false); + }); + }); }); |