diff options
Diffstat (limited to 'spec/frontend/members')
20 files changed, 301 insertions, 134 deletions
diff --git a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js index f86237dc160..f1471f625f8 100644 --- a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js +++ b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js @@ -42,6 +42,7 @@ describe('AccessRequestActionButtons', () => { memberId: member.id, title: 'Deny access', isAccessRequest: true, + isInvite: false, icon: 'close', }); }); diff --git a/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js b/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js index f77d41a642e..936715e7723 100644 --- a/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js +++ b/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js @@ -3,6 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import ApproveAccessRequestButton from '~/members/components/action_buttons/approve_access_request_button.vue'; +import { MEMBER_TYPES } from '~/members/constants'; jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); @@ -14,9 +15,14 @@ describe('ApproveAccessRequestButton', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - memberPath: '/groups/foo-bar/-/group_members/:id', - ...state, + modules: { + [MEMBER_TYPES.accessRequest]: { + namespaced: true, + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }, }, }); }; @@ -25,6 +31,9 @@ describe('ApproveAccessRequestButton', () => { wrapper = shallowMount(ApproveAccessRequestButton, { localVue, store: createStore(state), + provide: { + namespace: MEMBER_TYPES.accessRequest, + }, propsData: { memberId: 1, ...propsData, 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 fe63f9bfaa7..e7a99a96da6 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 @@ -39,9 +39,11 @@ describe('InviteActionButtons', () => { it('sets props correctly', () => { expect(findRemoveMemberButton().props()).toEqual({ memberId: member.id, + memberType: null, message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.fullName}"`, title: 'Revoke invite', isAccessRequest: false, + isInvite: true, icon: 'remove', }); }); diff --git a/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js b/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js index f6e342898cb..f91aef131a1 100644 --- a/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js +++ b/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js @@ -3,6 +3,7 @@ import { mount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import RemoveGroupLinkButton from '~/members/components/action_buttons/remove_group_link_button.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import { group } from '../../mock_data'; const localVue = createLocalVue(); @@ -17,7 +18,12 @@ describe('RemoveGroupLinkButton', () => { const createStore = () => { return new Vuex.Store({ - actions, + modules: { + [MEMBER_TYPES.group]: { + namespaced: true, + actions, + }, + }, }); }; @@ -25,6 +31,9 @@ describe('RemoveGroupLinkButton', () => { wrapper = mount(RemoveGroupLinkButton, { localVue, store: createStore(), + provide: { + namespace: MEMBER_TYPES.group, + }, propsData: { groupLink: group, }, 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 437b3e705a4..4ff12f7fa97 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 @@ -2,6 +2,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import RemoveMemberButton from '~/members/components/action_buttons/remove_member_button.vue'; +import { MEMBER_TYPES } from '~/members/constants'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -11,9 +12,14 @@ describe('RemoveMemberButton', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - memberPath: '/groups/foo-bar/-/group_members/:id', - ...state, + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }, }, }); }; @@ -22,11 +28,17 @@ describe('RemoveMemberButton', () => { wrapper = shallowMount(RemoveMemberButton, { localVue, store: createStore(state), + provide: { + namespace: MEMBER_TYPES.user, + }, propsData: { memberId: 1, + memberType: 'GroupMember', message: 'Are you sure you want to remove John Smith?', title: 'Remove member', isAccessRequest: true, + isInvite: true, + oncallSchedules: { name: 'user', schedules: [] }, ...propsData, }, directives: { @@ -44,8 +56,11 @@ describe('RemoveMemberButton', () => { 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', diff --git a/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js b/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js index 49b6979f954..547e067450c 100644 --- a/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js +++ b/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js @@ -3,6 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import ResendInviteButton from '~/members/components/action_buttons/resend_invite_button.vue'; +import { MEMBER_TYPES } from '~/members/constants'; jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); @@ -14,9 +15,14 @@ describe('ResendInviteButton', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - memberPath: '/groups/foo-bar/-/group_members/:id', - ...state, + modules: { + [MEMBER_TYPES.invite]: { + namespaced: true, + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }, }, }); }; @@ -25,6 +31,9 @@ describe('ResendInviteButton', () => { wrapper = shallowMount(ResendInviteButton, { localVue, store: createStore(state), + provide: { + namespace: MEMBER_TYPES.invite, + }, propsData: { memberId: 1, ...propsData, diff --git a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js index 1d7ea5b3109..0aa3780f030 100644 --- a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js +++ b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js @@ -39,10 +39,16 @@ describe('UserActionButtons', () => { 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.fullName}"`, + memberType: 'GroupMember', + message: `Are you sure you want to remove ${member.user.name} from "${member.source.fullName}"?`, title: 'Remove member', isAccessRequest: false, + isInvite: false, icon: 'remove', + oncallSchedules: { + name: member.user.name, + schedules: member.user.oncallSchedules, + }, }); }); @@ -56,7 +62,7 @@ describe('UserActionButtons', () => { }); expect(findRemoveMemberButton().props('message')).toBe( - `Are you sure you want to remove this orphaned member from "${orphanedMember.source.fullName}"`, + `Are you sure you want to remove this orphaned member from "${orphanedMember.source.fullName}"?`, ); }); }); @@ -86,4 +92,40 @@ describe('UserActionButtons', () => { expect(findRemoveMemberButton().exists()).toBe(false); }); }); + + describe('when group member', () => { + beforeEach(() => { + createComponent({ + member: { + ...member, + type: 'GroupMember', + }, + permissions: { + canRemove: true, + }, + }); + }); + + it('sets member type correctly', () => { + expect(findRemoveMemberButton().props().memberType).toBe('GroupMember'); + }); + }); + + describe('when project member', () => { + beforeEach(() => { + createComponent({ + member: { + ...member, + type: 'ProjectMember', + }, + permissions: { + canRemove: true, + }, + }); + }); + + it('sets member type correctly', () => { + expect(findRemoveMemberButton().props().memberType).toBe('ProjectMember'); + }); + }); }); diff --git a/spec/frontend/members/components/app_spec.js b/spec/frontend/members/components/app_spec.js index a1329c3ee9f..05933e36b52 100644 --- a/spec/frontend/members/components/app_spec.js +++ b/spec/frontend/members/components/app_spec.js @@ -5,6 +5,7 @@ import Vuex from 'vuex'; import * as commonUtils from '~/lib/utils/common_utils'; import MembersApp from '~/members/components/app.vue'; import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import { RECEIVE_MEMBER_ROLE_ERROR, HIDE_ERROR } from '~/members/store/mutation_types'; import mutations from '~/members/store/mutations'; @@ -17,16 +18,24 @@ describe('MembersApp', () => { const createComponent = (state = {}, options = {}) => { store = new Vuex.Store({ - state: { - showError: true, - errorMessage: 'Something went wrong, please try again.', - ...state, + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + showError: true, + errorMessage: 'Something went wrong, please try again.', + ...state, + }, + mutations, + }, }, - mutations, }); wrapper = shallowMount(MembersApp, { localVue, + provide: { + namespace: MEMBER_TYPES.user, + }, store, ...options, }); @@ -48,7 +57,9 @@ describe('MembersApp', () => { it('renders and scrolls to error alert', async () => { createComponent({ showError: false, errorMessage: '' }); - store.commit(RECEIVE_MEMBER_ROLE_ERROR, { error: new Error('Network Error') }); + store.commit(`${MEMBER_TYPES.user}/${RECEIVE_MEMBER_ROLE_ERROR}`, { + error: new Error('Network Error'), + }); await nextTick(); @@ -66,7 +77,7 @@ describe('MembersApp', () => { it('does not render and scroll to error alert', async () => { createComponent(); - store.commit(HIDE_ERROR); + store.commit(`${MEMBER_TYPES.user}/${HIDE_ERROR}`); await nextTick(); diff --git a/spec/frontend/members/components/avatars/user_avatar_spec.js b/spec/frontend/members/components/avatars/user_avatar_spec.js index 3f4d9155c5d..5cf3a4cdc13 100644 --- a/spec/frontend/members/components/avatars/user_avatar_spec.js +++ b/spec/frontend/members/components/avatars/user_avatar_spec.js @@ -1,31 +1,25 @@ import { GlAvatarLink, GlBadge } from '@gitlab/ui'; import { within } from '@testing-library/dom'; import { mount, createWrapper } from '@vue/test-utils'; -import Vue from 'vue'; -import Vuex from 'vuex'; import UserAvatar from '~/members/components/avatars/user_avatar.vue'; import { member as memberMock, member2faEnabled, orphanedMember } from '../../mock_data'; -Vue.use(Vuex); - describe('UserAvatar', () => { let wrapper; const { user } = memberMock; - const createComponent = (propsData = {}, state = {}) => { + const createComponent = (propsData = {}, provide = {}) => { wrapper = mount(UserAvatar, { propsData: { member: memberMock, isCurrentUser: false, ...propsData, }, - store: new Vuex.Store({ - state: { - canManageMembers: true, - ...state, - }, - }), + provide: { + canManageMembers: true, + ...provide, + }, }); }; diff --git a/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js b/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js index 0d9f9acbbeb..16ac52737bc 100644 --- a/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js +++ b/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js @@ -3,6 +3,7 @@ import Vuex from 'vuex'; import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue'; import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue'; import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue'; +import { MEMBER_TYPES } from '~/members/constants'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -12,22 +13,30 @@ describe('FilterSortContainer', () => { const createComponent = (state) => { const store = new Vuex.Store({ - state: { - filteredSearchBar: { - show: true, - tokens: ['two_factor'], - searchParam: 'search', - placeholder: 'Filter members', - recentSearchesStorageKey: 'group_members', + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + filteredSearchBar: { + show: true, + tokens: ['two_factor'], + searchParam: 'search', + placeholder: 'Filter members', + recentSearchesStorageKey: 'group_members', + }, + tableSortableFields: ['account'], + ...state, + }, }, - tableSortableFields: ['account'], - ...state, }, }); wrapper = shallowMount(FilterSortContainer, { localVue, store, + provide: { + namespace: MEMBER_TYPES.user, + }, }); }; diff --git a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js index 14b437a8c4e..af5434f7068 100644 --- a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js +++ b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js @@ -2,6 +2,7 @@ import { GlFilteredSearchToken } from '@gitlab/ui'; import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; const localVue = createLocalVue(); @@ -10,24 +11,33 @@ localVue.use(Vuex); describe('MembersFilteredSearchBar', () => { let wrapper; - const createComponent = (state) => { + const createComponent = ({ state = {}, provide = {} } = {}) => { const store = new Vuex.Store({ - state: { - sourceId: 1, - filteredSearchBar: { - show: true, - tokens: ['two_factor'], - searchParam: 'search', - placeholder: 'Filter members', - recentSearchesStorageKey: 'group_members', + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + filteredSearchBar: { + show: true, + tokens: ['two_factor'], + searchParam: 'search', + placeholder: 'Filter members', + recentSearchesStorageKey: 'group_members', + }, + ...state, + }, }, - canManageMembers: true, - ...state, }, }); wrapper = shallowMount(MembersFilteredSearchBar, { localVue, + provide: { + sourceId: 1, + canManageMembers: true, + namespace: MEMBER_TYPES.user, + ...provide, + }, store, }); }; @@ -68,14 +78,18 @@ describe('MembersFilteredSearchBar', () => { describe('when `canManageMembers` is false', () => { it('excludes 2FA token', () => { createComponent({ - filteredSearchBar: { - show: true, - tokens: ['two_factor', 'with_inherited_permissions'], - searchParam: 'search', - placeholder: 'Filter members', - recentSearchesStorageKey: 'group_members', + state: { + filteredSearchBar: { + show: true, + tokens: ['two_factor', 'with_inherited_permissions'], + searchParam: 'search', + placeholder: 'Filter members', + recentSearchesStorageKey: 'group_members', + }, + }, + provide: { + canManageMembers: false, }, - canManageMembers: false, }); expect(findFilteredSearchBar().props('tokens')).toEqual([ diff --git a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js index 357fad741e9..4b335755980 100644 --- a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js +++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js @@ -3,6 +3,7 @@ import { mount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import * as urlUtilities from '~/lib/utils/url_utility'; import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue'; +import { MEMBER_TYPES } from '~/members/constants'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -14,22 +15,30 @@ describe('SortDropdown', () => { const createComponent = (state) => { const store = new Vuex.Store({ - state: { - sourceId: 1, - tableSortableFields: ['account', 'granted', 'expires', 'maxRole', 'lastSignIn'], - filteredSearchBar: { - show: true, - tokens: ['two_factor'], - searchParam: 'search', - placeholder: 'Filter members', - recentSearchesStorageKey: 'group_members', + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + tableSortableFields: ['account', 'granted', 'expires', 'maxRole', 'lastSignIn'], + filteredSearchBar: { + show: true, + tokens: ['two_factor'], + searchParam: 'search', + placeholder: 'Filter members', + recentSearchesStorageKey: 'group_members', + }, + ...state, + }, }, - ...state, }, }); wrapper = mount(SortDropdown, { localVue, + provide: { + sourceId: 1, + namespace: MEMBER_TYPES.user, + }, store, }); }; diff --git a/spec/frontend/members/components/modals/leave_modal_spec.js b/spec/frontend/members/components/modals/leave_modal_spec.js index 2d52911572f..ea9eb7bf923 100644 --- a/spec/frontend/members/components/modals/leave_modal_spec.js +++ b/spec/frontend/members/components/modals/leave_modal_spec.js @@ -1,10 +1,12 @@ import { GlModal, GlForm } from '@gitlab/ui'; import { within } from '@testing-library/dom'; import { mount, createLocalVue, createWrapper } from '@vue/test-utils'; +import { cloneDeep } from 'lodash'; import { nextTick } from 'vue'; import Vuex from 'vuex'; import LeaveModal from '~/members/components/modals/leave_modal.vue'; -import { LEAVE_MODAL_ID } from '~/members/constants'; +import { LEAVE_MODAL_ID, MEMBER_TYPES } from '~/members/constants'; +import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue'; import { member } from '../../mock_data'; jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); @@ -17,9 +19,14 @@ describe('LeaveModal', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - memberPath: '/groups/foo-bar/-/group_members/:id', - ...state, + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + memberPath: '/groups/foo-bar/-/group_members/:id', + ...state, + }, + }, }, }); }; @@ -28,6 +35,9 @@ describe('LeaveModal', () => { wrapper = mount(LeaveModal, { localVue, store: createStore(state), + provide: { + namespace: MEMBER_TYPES.user, + }, propsData: { member, ...propsData, @@ -39,9 +49,9 @@ describe('LeaveModal', () => { }); }; - const findModal = () => wrapper.find(GlModal); - - const findForm = () => findModal().find(GlForm); + const findModal = () => wrapper.findComponent(GlModal); + const findForm = () => findModal().findComponent(GlForm); + const findOncallSchedulesList = () => findModal().findComponent(OncallSchedulesList); const getByText = (text, options) => createWrapper(within(findModal().element).getByText(text, options)); @@ -79,6 +89,24 @@ describe('LeaveModal', () => { ); }); + describe('On-call schedules list', () => { + it("displays oncall schedules list when member's user is part of on-call schedules ", () => { + const schedulesList = findOncallSchedulesList(); + expect(schedulesList.exists()).toBe(true); + expect(schedulesList.props()).toMatchObject({ + isCurrentUser: true, + schedules: member.user.oncallSchedules, + }); + }); + + it("does NOT display oncall schedules list when member's user is NOT a part of on-call schedules ", () => { + const memberWithoutOncallSchedules = cloneDeep(member); + delete (memberWithoutOncallSchedules, 'user.oncallSchedules'); + createComponent({ member: memberWithoutOncallSchedules }); + expect(findOncallSchedulesList().exists()).toBe(false); + }); + }); + it('submits the form when "Leave" button is clicked', () => { const submitSpy = jest.spyOn(findForm().element, 'submit'); diff --git a/spec/frontend/members/components/modals/remove_group_link_modal_spec.js b/spec/frontend/members/components/modals/remove_group_link_modal_spec.js index 62df912c1a2..01279581c55 100644 --- a/spec/frontend/members/components/modals/remove_group_link_modal_spec.js +++ b/spec/frontend/members/components/modals/remove_group_link_modal_spec.js @@ -4,7 +4,7 @@ import { mount, createLocalVue, createWrapper } from '@vue/test-utils'; import { nextTick } from 'vue'; import Vuex from 'vuex'; import RemoveGroupLinkModal from '~/members/components/modals/remove_group_link_modal.vue'; -import { REMOVE_GROUP_LINK_MODAL_ID } from '~/members/constants'; +import { REMOVE_GROUP_LINK_MODAL_ID, MEMBER_TYPES } from '~/members/constants'; import { group } from '../../mock_data'; jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); @@ -21,13 +21,18 @@ describe('RemoveGroupLinkModal', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - memberPath: '/groups/foo-bar/-/group_links/:id', - groupLinkToRemove: group, - removeGroupLinkModalVisible: true, - ...state, + modules: { + [MEMBER_TYPES.group]: { + namespaced: true, + state: { + memberPath: '/groups/foo-bar/-/group_links/:id', + groupLinkToRemove: group, + removeGroupLinkModalVisible: true, + ...state, + }, + actions, + }, }, - actions, }); }; @@ -35,6 +40,9 @@ describe('RemoveGroupLinkModal', () => { wrapper = mount(RemoveGroupLinkModal, { localVue, store: createStore(state), + provide: { + namespace: MEMBER_TYPES.group, + }, attrs: { static: true, }, diff --git a/spec/frontend/members/components/table/expiration_datepicker_spec.js b/spec/frontend/members/components/table/expiration_datepicker_spec.js index d26172b4ed1..3c4a9ba37ff 100644 --- a/spec/frontend/members/components/table/expiration_datepicker_spec.js +++ b/spec/frontend/members/components/table/expiration_datepicker_spec.js @@ -5,6 +5,7 @@ import Vuex from 'vuex'; import { useFakeDate } from 'helpers/fake_date'; import waitForPromises from 'helpers/wait_for_promises'; import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import { member } from '../../mock_data'; const localVue = createLocalVue(); @@ -31,7 +32,11 @@ describe('ExpirationDatepicker', () => { ), }; - return new Vuex.Store({ actions }); + return new Vuex.Store({ + modules: { + [MEMBER_TYPES.user]: { namespaced: true, actions }, + }, + }); }; const createComponent = (propsData = {}) => { @@ -41,6 +46,9 @@ describe('ExpirationDatepicker', () => { permissions: { canUpdate: true }, ...propsData, }, + provide: { + namespace: MEMBER_TYPES.user, + }, localVue, store: createStore(), mocks: { diff --git a/spec/frontend/members/components/table/members_table_cell_spec.js b/spec/frontend/members/components/table/members_table_cell_spec.js index b7dcd2a9fae..5375ee11736 100644 --- a/spec/frontend/members/components/table/members_table_cell_spec.js +++ b/spec/frontend/members/components/table/members_table_cell_spec.js @@ -42,21 +42,21 @@ describe('MembersTableCell', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - sourceId: 1, - currentUserId: 1, - ...state, - }, + state, }); }; let wrapper; - const createComponent = (propsData, state = {}) => { + const createComponent = (propsData, state) => { wrapper = mount(MembersTableCell, { localVue, propsData, store: createStore(state), + provide: { + sourceId: 1, + currentUserId: 1, + }, scopedSlots: { default: ` <wrapped-component diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js index cf5811e72e7..5cf1f40a8f4 100644 --- a/spec/frontend/members/components/table/members_table_spec.js +++ b/spec/frontend/members/components/table/members_table_spec.js @@ -14,6 +14,7 @@ import MemberAvatar from '~/members/components/table/member_avatar.vue'; import MemberSource from '~/members/components/table/member_source.vue'; import MembersTable from '~/members/components/table/members_table.vue'; import RoleDropdown from '~/members/components/table/role_dropdown.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import * as initUserPopovers from '~/user_popovers'; import { member as memberMock, directMember, invite, accessRequest } from '../../mock_data'; @@ -25,24 +26,33 @@ describe('MembersTable', () => { const createStore = (state = {}) => { return new Vuex.Store({ - state: { - members: [], - tableFields: [], - tableAttrs: { - table: { 'data-qa-selector': 'members_list' }, - tr: { 'data-qa-selector': 'member_row' }, + modules: { + [MEMBER_TYPES.user]: { + namespaced: true, + state: { + members: [], + tableFields: [], + tableAttrs: { + table: { 'data-qa-selector': 'members_list' }, + tr: { 'data-qa-selector': 'member_row' }, + }, + ...state, + }, }, - sourceId: 1, - currentUserId: 1, - ...state, }, }); }; - const createComponent = (state) => { + const createComponent = (state, provide = {}) => { wrapper = mount(MembersTable, { localVue, store: createStore(state), + provide: { + sourceId: 1, + currentUserId: 1, + namespace: MEMBER_TYPES.user, + ...provide, + }, stubs: [ 'member-avatar', 'member-source', @@ -119,7 +129,7 @@ describe('MembersTable', () => { describe('when user is not logged in', () => { it('does not render the "Actions" field', () => { - createComponent({ currentUserId: null, tableFields: ['actions'] }); + createComponent({ tableFields: ['actions'] }, { currentUserId: null }); expect(within(wrapper.element).queryByTestId('col-actions')).toBe(null); }); diff --git a/spec/frontend/members/components/table/role_dropdown_spec.js b/spec/frontend/members/components/table/role_dropdown_spec.js index aa280599061..c8b6bead450 100644 --- a/spec/frontend/members/components/table/role_dropdown_spec.js +++ b/spec/frontend/members/components/table/role_dropdown_spec.js @@ -7,6 +7,7 @@ import Vuex from 'vuex'; import waitForPromises from 'helpers/wait_for_promises'; import { BV_DROPDOWN_SHOW } from '~/lib/utils/constants'; import RoleDropdown from '~/members/components/table/role_dropdown.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import { member } from '../../mock_data'; const localVue = createLocalVue(); @@ -24,11 +25,18 @@ describe('RoleDropdown', () => { updateMemberRole: jest.fn(() => Promise.resolve()), }; - return new Vuex.Store({ actions }); + return new Vuex.Store({ + modules: { + [MEMBER_TYPES.user]: { namespaced: true, actions }, + }, + }); }; const createComponent = (propsData = {}) => { wrapper = mount(RoleDropdown, { + provide: { + namespace: MEMBER_TYPES.user, + }, propsData: { member, permissions: {}, diff --git a/spec/frontend/members/index_spec.js b/spec/frontend/members/index_spec.js index dd3b9ddd912..8b645d9b059 100644 --- a/spec/frontend/members/index_spec.js +++ b/spec/frontend/members/index_spec.js @@ -1,5 +1,6 @@ import { createWrapper } from '@vue/test-utils'; import MembersApp from '~/members/components/app.vue'; +import { MEMBER_TYPES } from '~/members/constants'; import { initMembersApp } from '~/members/index'; import { membersJsonString, members } from './mock_data'; @@ -10,6 +11,7 @@ describe('initMembersApp', () => { const setup = () => { vm = initMembersApp(el, { + namespace: MEMBER_TYPES.user, tableFields: ['account'], tableAttrs: { table: { 'data-qa-selector': 'members_list' } }, tableSortableFields: ['account'], @@ -42,72 +44,49 @@ describe('initMembersApp', () => { expect(wrapper.find(MembersApp).exists()).toBe(true); }); - it('sets `currentUserId` in Vuex store', () => { - setup(); - - expect(vm.$store.state.currentUserId).toBe(123); - }); - - describe('when `gon.current_user_id` is not set (user is not logged in)', () => { - it('sets `currentUserId` as `null` in Vuex store', () => { - window.gon = {}; - setup(); - - expect(vm.$store.state.currentUserId).toBeNull(); - }); - }); - - it('parses and sets `data-source-id` as `sourceId` in Vuex store', () => { - setup(); - - expect(vm.$store.state.sourceId).toBe(234); - }); - - it('parses and sets `data-can-manage-members` as `canManageMembers` in Vuex store', () => { - setup(); - - expect(vm.$store.state.canManageMembers).toBe(true); - }); - it('parses and sets `members` in Vuex store', () => { setup(); - expect(vm.$store.state.members).toEqual(members); + expect(vm.$store.state[MEMBER_TYPES.user].members).toEqual(members); }); it('sets `tableFields` in Vuex store', () => { setup(); - expect(vm.$store.state.tableFields).toEqual(['account']); + expect(vm.$store.state[MEMBER_TYPES.user].tableFields).toEqual(['account']); }); it('sets `tableAttrs` in Vuex store', () => { setup(); - expect(vm.$store.state.tableAttrs).toEqual({ table: { 'data-qa-selector': 'members_list' } }); + expect(vm.$store.state[MEMBER_TYPES.user].tableAttrs).toEqual({ + table: { 'data-qa-selector': 'members_list' }, + }); }); it('sets `tableSortableFields` in Vuex store', () => { setup(); - expect(vm.$store.state.tableSortableFields).toEqual(['account']); + expect(vm.$store.state[MEMBER_TYPES.user].tableSortableFields).toEqual(['account']); }); it('sets `requestFormatter` in Vuex store', () => { setup(); - expect(vm.$store.state.requestFormatter()).toEqual({}); + expect(vm.$store.state[MEMBER_TYPES.user].requestFormatter()).toEqual({}); }); it('sets `filteredSearchBar` in Vuex store', () => { setup(); - expect(vm.$store.state.filteredSearchBar).toEqual({ show: false }); + expect(vm.$store.state[MEMBER_TYPES.user].filteredSearchBar).toEqual({ show: false }); }); it('sets `memberPath` in Vuex store', () => { setup(); - expect(vm.$store.state.memberPath).toBe('/groups/foo-bar/-/group_members/:id'); + expect(vm.$store.state[MEMBER_TYPES.user].memberPath).toBe( + '/groups/foo-bar/-/group_members/:id', + ); }); }); diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js index 6a73b2fcf8c..a47b7ab2118 100644 --- a/spec/frontend/members/mock_data.js +++ b/spec/frontend/members/mock_data.js @@ -11,6 +11,7 @@ export const member = { fullName: 'Foo Bar', webUrl: 'https://gitlab.com/groups/foo-bar', }, + type: 'GroupMember', user: { id: 123, name: 'Administrator', @@ -19,6 +20,7 @@ export const member = { avatarUrl: 'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon', blocked: false, twoFactorEnabled: false, + oncallSchedules: [{ name: 'schedule 1' }], }, id: 238, createdAt: '2020-07-17T16:22:46.923Z', |