summaryrefslogtreecommitdiff
path: root/spec/frontend/members
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/members')
-rw-r--r--spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js2
-rw-r--r--spec/frontend/members/components/action_buttons/remove_member_button_spec.js27
-rw-r--r--spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js42
-rw-r--r--spec/frontend/members/components/filter_sort/sort_dropdown_spec.js30
-rw-r--r--spec/frontend/members/components/members_tabs_spec.js33
-rw-r--r--spec/frontend/members/components/modals/remove_member_modal_spec.js133
-rw-r--r--spec/frontend/members/components/table/members_table_spec.js12
-rw-r--r--spec/frontend/members/mock_data.js9
-rw-r--r--spec/frontend/members/store/actions_spec.js32
-rw-r--r--spec/frontend/members/store/mutations_spec.js30
-rw-r--r--spec/frontend/members/utils_spec.js17
11 files changed, 294 insertions, 73 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/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
index a3b91cb20bb..3f47fa024bc 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
@@ -1,11 +1,23 @@
import { GlFilteredSearchToken } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { redirectTo } from '~/lib/utils/url_utility';
import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue';
import { MEMBER_TYPES } from '~/members/constants';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+jest.mock('~/lib/utils/url_utility', () => {
+ const urlUtility = jest.requireActual('~/lib/utils/url_utility');
+
+ return {
+ __esModule: true,
+ ...urlUtility,
+ redirectTo: jest.fn(),
+ };
+});
+
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -113,12 +125,11 @@ describe('MembersFilteredSearchBar', () => {
describe('when filters are set via query params', () => {
beforeEach(() => {
- delete window.location;
- window.location = new URL('https://localhost');
+ setWindowLocation('https://localhost');
});
it('parses and passes tokens to `FilteredSearchBar` component as `initialFilterValue` prop', () => {
- window.location.search = '?two_factor=enabled&token_not_available=foobar';
+ setWindowLocation('?two_factor=enabled&token_not_available=foobar');
createComponent();
@@ -134,7 +145,7 @@ describe('MembersFilteredSearchBar', () => {
});
it('parses and passes search param to `FilteredSearchBar` component as `initialFilterValue` prop', () => {
- window.location.search = '?search=foobar';
+ setWindowLocation('?search=foobar');
createComponent();
@@ -149,7 +160,7 @@ describe('MembersFilteredSearchBar', () => {
});
it('parses and passes search param with multiple words to `FilteredSearchBar` component as `initialFilterValue` prop', () => {
- window.location.search = '?search=foo+bar+baz';
+ setWindowLocation('?search=foo+bar+baz');
createComponent();
@@ -166,8 +177,7 @@ describe('MembersFilteredSearchBar', () => {
describe('when filter bar is submitted', () => {
beforeEach(() => {
- delete window.location;
- window.location = new URL('https://localhost');
+ setWindowLocation('https://localhost');
});
it('adds correct filter query params', () => {
@@ -177,7 +187,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: 'two_factor', value: { data: 'enabled', operator: '=' } },
]);
- expect(window.location.href).toBe('https://localhost/?two_factor=enabled');
+ expect(redirectTo).toHaveBeenCalledWith('https://localhost/?two_factor=enabled');
});
it('adds search query param', () => {
@@ -188,7 +198,9 @@ describe('MembersFilteredSearchBar', () => {
{ type: 'filtered-search-term', value: { data: 'foobar' } },
]);
- expect(window.location.href).toBe('https://localhost/?two_factor=enabled&search=foobar');
+ expect(redirectTo).toHaveBeenCalledWith(
+ 'https://localhost/?two_factor=enabled&search=foobar',
+ );
});
it('adds search query param with multiple words', () => {
@@ -199,11 +211,13 @@ describe('MembersFilteredSearchBar', () => {
{ type: 'filtered-search-term', value: { data: 'foo bar baz' } },
]);
- expect(window.location.href).toBe('https://localhost/?two_factor=enabled&search=foo+bar+baz');
+ expect(redirectTo).toHaveBeenCalledWith(
+ 'https://localhost/?two_factor=enabled&search=foo+bar+baz',
+ );
});
it('adds sort query param', () => {
- window.location.search = '?sort=name_asc';
+ setWindowLocation('?sort=name_asc');
createComponent();
@@ -212,13 +226,13 @@ describe('MembersFilteredSearchBar', () => {
{ type: 'filtered-search-term', value: { data: 'foobar' } },
]);
- expect(window.location.href).toBe(
+ expect(redirectTo).toHaveBeenCalledWith(
'https://localhost/?two_factor=enabled&search=foobar&sort=name_asc',
);
});
it('adds active tab query param', () => {
- window.location.search = '?tab=invited';
+ setWindowLocation('?tab=invited');
createComponent();
@@ -226,7 +240,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: 'filtered-search-term', value: { data: 'foobar' } },
]);
- expect(window.location.href).toBe('https://localhost/?search=foobar&tab=invited');
+ expect(redirectTo).toHaveBeenCalledWith('https://localhost/?search=foobar&tab=invited');
});
});
});
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 4b335755980..d0684acd487 100644
--- a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
+++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
@@ -1,6 +1,7 @@
import { GlSorting, GlSortingItem } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
+import setWindowLocation from 'helpers/set_window_location_helper';
import * as urlUtilities from '~/lib/utils/url_utility';
import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue';
import { MEMBER_TYPES } from '~/members/constants';
@@ -52,17 +53,16 @@ describe('SortDropdown', () => {
.findAll(GlSortingItem)
.wrappers.find((dropdownItemWrapper) => dropdownItemWrapper.text() === text);
- describe('dropdown options', () => {
- beforeEach(() => {
- delete window.location;
- window.location = new URL(URL_HOST);
- });
+ beforeEach(() => {
+ setWindowLocation(URL_HOST);
+ });
+ describe('dropdown options', () => {
it('adds dropdown items for all the sortable fields', () => {
const URL_FILTER_PARAMS = '?two_factor=enabled&search=foobar';
const EXPECTED_BASE_URL = `${URL_HOST}${URL_FILTER_PARAMS}&sort=`;
- window.location.search = URL_FILTER_PARAMS;
+ setWindowLocation(URL_FILTER_PARAMS);
const expectedDropdownItems = [
{
@@ -94,7 +94,7 @@ describe('SortDropdown', () => {
});
it('checks selected sort option', () => {
- window.location.search = '?sort=access_level_asc';
+ setWindowLocation('?sort=access_level_asc');
createComponent();
@@ -103,11 +103,6 @@ describe('SortDropdown', () => {
});
describe('dropdown toggle', () => {
- beforeEach(() => {
- delete window.location;
- window.location = new URL(URL_HOST);
- });
-
it('defaults to sorting by "Account" in ascending order', () => {
createComponent();
@@ -116,7 +111,7 @@ describe('SortDropdown', () => {
});
it('sets text as selected sort option', () => {
- window.location.search = '?sort=access_level_asc';
+ setWindowLocation('?sort=access_level_asc');
createComponent();
@@ -126,15 +121,12 @@ describe('SortDropdown', () => {
describe('sort direction toggle', () => {
beforeEach(() => {
- delete window.location;
- window.location = new URL(URL_HOST);
-
- jest.spyOn(urlUtilities, 'visitUrl');
+ jest.spyOn(urlUtilities, 'visitUrl').mockImplementation();
});
describe('when current sort direction is ascending', () => {
beforeEach(() => {
- window.location.search = '?sort=access_level_asc';
+ setWindowLocation('?sort=access_level_asc');
createComponent();
});
@@ -152,7 +144,7 @@ describe('SortDropdown', () => {
describe('when current sort direction is descending', () => {
beforeEach(() => {
- window.location.search = '?sort=access_level_desc';
+ setWindowLocation('?sort=access_level_desc');
createComponent();
});
diff --git a/spec/frontend/members/components/members_tabs_spec.js b/spec/frontend/members/components/members_tabs_spec.js
index 33d8eebf7eb..1d882e5ef09 100644
--- a/spec/frontend/members/components/members_tabs_spec.js
+++ b/spec/frontend/members/components/members_tabs_spec.js
@@ -1,6 +1,7 @@
-import { GlTabs } from '@gitlab/ui';
+import { GlTabs, GlButton } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
+import setWindowLocation from 'helpers/set_window_location_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import MembersApp from '~/members/components/app.vue';
import MembersTabs from '~/members/components/members_tabs.vue';
@@ -16,7 +17,7 @@ describe('MembersTabs', () => {
let wrapper;
- const createComponent = ({ totalItems = 10, options = {} } = {}) => {
+ const createComponent = ({ totalItems = 10, provide = {} } = {}) => {
const store = new Vuex.Store({
modules: {
[MEMBER_TYPES.user]: {
@@ -78,8 +79,10 @@ describe('MembersTabs', () => {
stubs: ['members-app'],
provide: {
canManageMembers: true,
+ canExportMembers: true,
+ exportCsvPath: '',
+ ...provide,
},
- ...options,
});
return nextTick();
@@ -88,10 +91,10 @@ describe('MembersTabs', () => {
const findTabs = () => wrapper.findAllByRole('tab').wrappers;
const findTabByText = (text) => findTabs().find((tab) => tab.text().includes(text));
const findActiveTab = () => wrapper.findByRole('tab', { selected: true });
+ const findExportButton = () => wrapper.findComponent(GlButton);
beforeEach(() => {
- delete window.location;
- window.location = new URL('https://localhost');
+ setWindowLocation('https://localhost');
});
afterEach(() => {
@@ -151,7 +154,7 @@ describe('MembersTabs', () => {
describe('when url param matches `filteredSearchBar.searchParam`', () => {
beforeEach(() => {
- window.location.search = '?search_groups=foo+bar';
+ setWindowLocation('?search_groups=foo+bar');
});
it('shows tab that corresponds to search param', async () => {
@@ -164,7 +167,7 @@ describe('MembersTabs', () => {
describe('when `canManageMembers` is `false`', () => {
it('shows all tabs except `Invited` and `Access requests`', async () => {
- await createComponent({ options: { provide: { canManageMembers: false } } });
+ await createComponent({ provide: { canManageMembers: false } });
expect(findTabByText('Members')).not.toBeUndefined();
expect(findTabByText('Groups')).not.toBeUndefined();
@@ -172,4 +175,20 @@ describe('MembersTabs', () => {
expect(findTabByText('Access requests')).toBeUndefined();
});
});
+
+ describe('when `canExportMembers` is true', () => {
+ it('shows the CSV export button with export path', async () => {
+ await createComponent({ provide: { canExportMembers: true, exportCsvPath: 'foo' } });
+
+ expect(findExportButton().attributes('href')).toBe('foo');
+ });
+ });
+
+ describe('when `canExportMembers` is false', () => {
+ it('does not show the CSV export button', async () => {
+ await createComponent({ provide: { canExportMembers: false } });
+
+ expect(findExportButton().exists()).toBe(false);
+ });
+ });
});
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..6885da53b26 100644
--- a/spec/frontend/members/components/table/members_table_spec.js
+++ b/spec/frontend/members/components/table/members_table_spec.js
@@ -6,6 +6,7 @@ import {
} from '@testing-library/dom';
import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
import Vuex from 'vuex';
+import setWindowLocation from 'helpers/set_window_location_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import CreatedAt from '~/members/components/table/created_at.vue';
import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue';
@@ -72,6 +73,7 @@ describe('MembersTable', () => {
'member-action-buttons',
'role-dropdown',
'remove-group-link-modal',
+ 'remove-member-modal',
'expiration-datepicker',
],
});
@@ -242,12 +244,8 @@ describe('MembersTable', () => {
});
describe('when required pagination data is provided', () => {
- beforeEach(() => {
- delete window.location;
- });
-
it('renders `gl-pagination` component with correct props', () => {
- window.location = new URL(url);
+ setWindowLocation(url);
createComponent();
@@ -267,7 +265,7 @@ describe('MembersTable', () => {
});
it('uses `pagination.paramName` to generate the pagination links', () => {
- window.location = new URL(url);
+ setWindowLocation(url);
createComponent({
pagination: {
@@ -282,7 +280,7 @@ describe('MembersTable', () => {
});
it('removes any url params defined as `null` in the `params` attribute', () => {
- window.location = new URL(`${url}&search_groups=foo`);
+ setWindowLocation(`${url}&search_groups=foo`);
createComponent({
pagination: {
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 78bbad394a0..8160cc373d8 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';
@@ -152,4 +152,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);
+ });
+ });
});
diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js
index 9740e1c2edb..a157cfa1c1d 100644
--- a/spec/frontend/members/utils_spec.js
+++ b/spec/frontend/members/utils_spec.js
@@ -1,3 +1,4 @@
+import setWindowLocation from 'helpers/set_window_location_helper';
import { DEFAULT_SORT, MEMBER_TYPES } from '~/members/constants';
import {
generateBadges,
@@ -150,21 +151,18 @@ describe('Members Utils', () => {
describe('parseSortParam', () => {
beforeEach(() => {
- delete window.location;
- window.location = new URL(URL_HOST);
+ setWindowLocation(URL_HOST);
});
describe('when `sort` param is not present', () => {
it('returns default sort options', () => {
- window.location.search = '';
-
expect(parseSortParam(['account'])).toEqual(DEFAULT_SORT);
});
});
describe('when field passed in `sortableFields` argument does not have `sort` key defined', () => {
it('returns default sort options', () => {
- window.location.search = '?sort=source_asc';
+ setWindowLocation('?sort=source_asc');
expect(parseSortParam(['source'])).toEqual(DEFAULT_SORT);
});
@@ -182,7 +180,7 @@ describe('Members Utils', () => {
${'oldest_sign_in'} | ${{ sortByKey: 'lastSignIn', sortDesc: true }}
`('when `sort` query string param is `$sortParam`', ({ sortParam, expected }) => {
it(`returns ${JSON.stringify(expected)}`, async () => {
- window.location.search = `?sort=${sortParam}`;
+ setWindowLocation(`?sort=${sortParam}`);
expect(parseSortParam(['account', 'granted', 'expires', 'maxRole', 'lastSignIn'])).toEqual(
expected,
@@ -193,8 +191,7 @@ describe('Members Utils', () => {
describe('buildSortHref', () => {
beforeEach(() => {
- delete window.location;
- window.location = new URL(URL_HOST);
+ setWindowLocation(URL_HOST);
});
describe('when field passed in `sortBy` argument does not have `sort` key defined', () => {
@@ -225,7 +222,7 @@ describe('Members Utils', () => {
describe('when filter params are set', () => {
it('merges the `sort` param with the filter params', () => {
- window.location.search = '?two_factor=enabled&with_inherited_permissions=exclude';
+ setWindowLocation('?two_factor=enabled&with_inherited_permissions=exclude');
expect(
buildSortHref({
@@ -240,7 +237,7 @@ describe('Members Utils', () => {
describe('when search param is set', () => {
it('merges the `sort` param with the search param', () => {
- window.location.search = '?search=foobar';
+ setWindowLocation('?search=foobar');
expect(
buildSortHref({