Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/invite_members/components')
-rw-r--r--spec/frontend/invite_members/components/group_select_spec.js5
-rw-r--r--spec/frontend/invite_members/components/invite_groups_modal_spec.js13
-rw-r--r--spec/frontend/invite_members/components/invite_members_modal_spec.js131
-rw-r--r--spec/frontend/invite_members/components/invite_modal_base_spec.js24
-rw-r--r--spec/frontend/invite_members/components/members_token_select_spec.js4
-rw-r--r--spec/frontend/invite_members/components/user_limit_notification_spec.js71
6 files changed, 137 insertions, 111 deletions
diff --git a/spec/frontend/invite_members/components/group_select_spec.js b/spec/frontend/invite_members/components/group_select_spec.js
index 192f3fdd381..e1563a7bb3a 100644
--- a/spec/frontend/invite_members/components/group_select_spec.js
+++ b/spec/frontend/invite_members/components/group_select_spec.js
@@ -4,7 +4,6 @@ import waitForPromises from 'helpers/wait_for_promises';
import * as groupsApi from '~/api/groups_api';
import GroupSelect from '~/invite_members/components/group_select.vue';
-const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 };
const group1 = { id: 1, full_name: 'Group One', avatar_url: 'test' };
const group2 = { id: 2, full_name: 'Group Two', avatar_url: 'test' };
const allGroups = [group1, group2];
@@ -13,7 +12,6 @@ const createComponent = (props = {}) => {
return mount(GroupSelect, {
propsData: {
invalidGroups: [],
- accessLevels,
...props,
},
});
@@ -66,9 +64,8 @@ describe('GroupSelect', () => {
resolveApiRequest({ data: allGroups });
expect(groupsApi.getGroups).toHaveBeenCalledWith(group1.name, {
- active: true,
exclude_internal: true,
- min_access_level: accessLevels.Guest,
+ active: true,
});
});
diff --git a/spec/frontend/invite_members/components/invite_groups_modal_spec.js b/spec/frontend/invite_members/components/invite_groups_modal_spec.js
index 8085f48f6e2..f9cb4a149f2 100644
--- a/spec/frontend/invite_members/components/invite_groups_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_groups_modal_spec.js
@@ -42,18 +42,19 @@ describe('InviteGroupsModal', () => {
wrapper = null;
});
+ const findModal = () => wrapper.findComponent(GlModal);
const findGroupSelect = () => wrapper.findComponent(GroupSelect);
const findIntroText = () => wrapper.findByTestId('modal-base-intro-text').text();
- const findCancelButton = () => wrapper.findByTestId('cancel-button');
- const findInviteButton = () => wrapper.findByTestId('invite-button');
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
const membersFormGroupInvalidFeedback = () =>
findMembersFormGroup().attributes('invalid-feedback');
- const clickInviteButton = () => findInviteButton().vm.$emit('click');
- const clickCancelButton = () => findCancelButton().vm.$emit('click');
- const triggerGroupSelect = (val) => findGroupSelect().vm.$emit('input', val);
const findBase = () => wrapper.findComponent(InviteModalBase);
- const hideModal = () => wrapper.findComponent(GlModal).vm.$emit('hide');
+ const triggerGroupSelect = (val) => findGroupSelect().vm.$emit('input', val);
+ const emitEventFromModal = (eventName) => () =>
+ findModal().vm.$emit(eventName, { preventDefault: jest.fn() });
+ const hideModal = emitEventFromModal('hidden');
+ const clickInviteButton = emitEventFromModal('primary');
+ const clickCancelButton = emitEventFromModal('cancel');
describe('displaying the correct introText and form group description', () => {
describe('when inviting to a project', () => {
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 dd16bb48cb8..84317da39e6 100644
--- a/spec/frontend/invite_members/components/invite_members_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js
@@ -23,7 +23,7 @@ import ContentTransition from '~/vue_shared/components/content_transition.vue';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import { getParameterValues } from '~/lib/utils/url_utility';
-import { apiPaths, membersApiResponse, invitationsApiResponse } from '../mock_data/api_responses';
+import { GROUPS_INVITATIONS_PATH, invitationsApiResponse } from '../mock_data/api_responses';
import {
propsData,
inviteSource,
@@ -85,12 +85,13 @@ describe('InviteMembersModal', () => {
mock.restore();
});
+ const findModal = () => wrapper.findComponent(GlModal);
const findBase = () => wrapper.findComponent(InviteModalBase);
const findIntroText = () => wrapper.findByTestId('modal-base-intro-text').text();
- const findCancelButton = () => wrapper.findByTestId('cancel-button');
- const findInviteButton = () => wrapper.findByTestId('invite-button');
- const clickInviteButton = () => findInviteButton().vm.$emit('click');
- const clickCancelButton = () => findCancelButton().vm.$emit('click');
+ const emitEventFromModal = (eventName) => () =>
+ findModal().vm.$emit(eventName, { preventDefault: jest.fn() });
+ const clickInviteButton = emitEventFromModal('primary');
+ const clickCancelButton = emitEventFromModal('cancel');
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
const membersFormGroupInvalidFeedback = () =>
findMembersFormGroup().attributes('invalid-feedback');
@@ -276,7 +277,7 @@ describe('InviteMembersModal', () => {
});
it('renders the modal with the correct title', () => {
- expect(wrapper.findComponent(GlModal).props('title')).toBe(MEMBERS_MODAL_CELEBRATE_TITLE);
+ expect(findModal().props('title')).toBe(MEMBERS_MODAL_CELEBRATE_TITLE);
});
it('includes the correct celebration text and emoji', () => {
@@ -300,11 +301,8 @@ describe('InviteMembersModal', () => {
});
describe('submitting the invite form', () => {
- const mockMembersApi = (code, data) => {
- mock.onPost(apiPaths.GROUPS_MEMBERS).reply(code, data);
- };
const mockInvitationsApi = (code, data) => {
- mock.onPost(apiPaths.GROUPS_INVITATIONS).reply(code, data);
+ mock.onPost(GROUPS_INVITATIONS_PATH).reply(code, data);
};
const expectedEmailRestrictedError =
@@ -328,7 +326,7 @@ describe('InviteMembersModal', () => {
await triggerMembersTokenSelect([user1, user2]);
wrapper.vm.$toast = { show: jest.fn() };
- jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
+ jest.spyOn(Api, 'inviteGroupMembers').mockResolvedValue({ data: postData });
});
describe('when triggered from regular mounting', () => {
@@ -336,12 +334,8 @@ describe('InviteMembersModal', () => {
clickInviteButton();
});
- it('sets isLoading on the Invite button when it is clicked', () => {
- expect(findInviteButton().props('loading')).toBe(true);
- });
-
- it('calls Api addGroupMembersByUserId with the correct params', () => {
- expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(propsData.id, postData);
+ it('calls Api inviteGroupMembers with the correct params', () => {
+ expect(Api.inviteGroupMembers).toHaveBeenCalledWith(propsData.id, postData);
});
it('displays the successful toastMessage', () => {
@@ -371,21 +365,9 @@ describe('InviteMembersModal', () => {
await triggerMembersTokenSelect([user1]);
});
- it('displays "Member already exists" api message for http status conflict', async () => {
- mockMembersApi(httpStatus.CONFLICT, membersApiResponse.MEMBER_ALREADY_EXISTS);
-
- clickInviteButton();
-
- await waitForPromises();
-
- expect(membersFormGroupInvalidFeedback()).toBe('Member already exists');
- expect(findMembersSelect().props('validationState')).toBe(false);
- expect(findInviteButton().props('loading')).toBe(false);
- });
-
describe('clearing the invalid state and message', () => {
beforeEach(async () => {
- mockMembersApi(httpStatus.CONFLICT, membersApiResponse.MEMBER_ALREADY_EXISTS);
+ mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_TAKEN);
clickInviteButton();
@@ -393,7 +375,9 @@ describe('InviteMembersModal', () => {
});
it('clears the error when the list of members to invite is cleared', async () => {
- expect(membersFormGroupInvalidFeedback()).toBe('Member already exists');
+ expect(membersFormGroupInvalidFeedback()).toBe(
+ Object.values(invitationsApiResponse.EMAIL_TAKEN.message)[0],
+ );
expect(findMembersSelect().props('validationState')).toBe(false);
findMembersSelect().vm.$emit('clear');
@@ -414,7 +398,7 @@ describe('InviteMembersModal', () => {
});
it('clears the error when the modal is hidden', async () => {
- wrapper.findComponent(GlModal).vm.$emit('hide');
+ findModal().vm.$emit('hidden');
await nextTick();
@@ -424,15 +408,17 @@ describe('InviteMembersModal', () => {
});
it('clears the invalid state and message once the list of members to invite is cleared', async () => {
- mockMembersApi(httpStatus.CONFLICT, membersApiResponse.MEMBER_ALREADY_EXISTS);
+ mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_TAKEN);
clickInviteButton();
await waitForPromises();
- expect(membersFormGroupInvalidFeedback()).toBe('Member already exists');
+ expect(membersFormGroupInvalidFeedback()).toBe(
+ Object.values(invitationsApiResponse.EMAIL_TAKEN.message)[0],
+ );
expect(findMembersSelect().props('validationState')).toBe(false);
- expect(findInviteButton().props('loading')).toBe(false);
+ expect(findModal().props('actionPrimary').attributes.loading).toBe(false);
findMembersSelect().vm.$emit('clear');
@@ -440,11 +426,14 @@ describe('InviteMembersModal', () => {
expect(membersFormGroupInvalidFeedback()).toBe('');
expect(findMembersSelect().props('validationState')).toBe(null);
- expect(findInviteButton().props('loading')).toBe(false);
+ expect(findModal().props('actionPrimary').attributes.loading).toBe(false);
});
it('displays the generic error for http server error', async () => {
- mockMembersApi(httpStatus.INTERNAL_SERVER_ERROR, 'Request failed with status code 500');
+ mockInvitationsApi(
+ httpStatus.INTERNAL_SERVER_ERROR,
+ 'Request failed with status code 500',
+ );
clickInviteButton();
@@ -454,7 +443,7 @@ describe('InviteMembersModal', () => {
});
it('displays the restricted user api message for response with bad request', async () => {
- mockMembersApi(httpStatus.BAD_REQUEST, membersApiResponse.SINGLE_USER_RESTRICTED);
+ mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
clickInviteButton();
@@ -464,7 +453,7 @@ describe('InviteMembersModal', () => {
});
it('displays the first part of the error when multiple existing users are restricted by email', async () => {
- mockMembersApi(httpStatus.CREATED, membersApiResponse.MULTIPLE_USERS_RESTRICTED);
+ mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
clickInviteButton();
@@ -475,19 +464,6 @@ describe('InviteMembersModal', () => {
);
expect(findMembersSelect().props('validationState')).toBe(false);
});
-
- it('displays an access_level error message received for the existing user', async () => {
- mockMembersApi(httpStatus.BAD_REQUEST, membersApiResponse.SINGLE_USER_ACCESS_LEVEL);
-
- clickInviteButton();
-
- await waitForPromises();
-
- expect(membersFormGroupInvalidFeedback()).toBe(
- 'should be greater than or equal to Owner inherited membership from group Gitlab Org',
- );
- expect(findMembersSelect().props('validationState')).toBe(false);
- });
});
});
@@ -508,7 +484,7 @@ describe('InviteMembersModal', () => {
await triggerMembersTokenSelect([user3]);
wrapper.vm.$toast = { show: jest.fn() };
- jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
+ jest.spyOn(Api, 'inviteGroupMembers').mockResolvedValue({ data: postData });
});
describe('when triggered from regular mounting', () => {
@@ -516,8 +492,8 @@ describe('InviteMembersModal', () => {
clickInviteButton();
});
- it('calls Api inviteGroupMembersByEmail with the correct params', () => {
- expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(propsData.id, postData);
+ it('calls Api inviteGroupMembers with the correct params', () => {
+ expect(Api.inviteGroupMembers).toHaveBeenCalledWith(propsData.id, postData);
});
it('displays the successful toastMessage', () => {
@@ -542,7 +518,7 @@ describe('InviteMembersModal', () => {
expect(membersFormGroupInvalidFeedback()).toBe(expectedSyntaxError);
expect(findMembersSelect().props('validationState')).toBe(false);
- expect(findInviteButton().props('loading')).toBe(false);
+ expect(findModal().props('actionPrimary').attributes.loading).toBe(false);
});
it('displays the restricted email error when restricted email is invited', async () => {
@@ -554,23 +530,11 @@ describe('InviteMembersModal', () => {
expect(membersFormGroupInvalidFeedback()).toContain(expectedEmailRestrictedError);
expect(findMembersSelect().props('validationState')).toBe(false);
- expect(findInviteButton().props('loading')).toBe(false);
- });
-
- it('displays the successful toast message when email has already been invited', async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_TAKEN);
- wrapper.vm.$toast = { show: jest.fn() };
-
- clickInviteButton();
-
- await waitForPromises();
-
- expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added');
- expect(findMembersSelect().props('validationState')).toBe(null);
+ expect(findModal().props('actionPrimary').attributes.loading).toBe(false);
});
it('displays the first error message when multiple emails return a restricted error message', async () => {
- mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_EMAIL_RESTRICTED);
+ mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
clickInviteButton();
@@ -617,19 +581,17 @@ describe('InviteMembersModal', () => {
format: 'json',
tasks_to_be_done: [],
tasks_project_id: '',
+ user_id: '1',
+ email: 'email@example.com',
};
- const emailPostData = { ...postData, email: 'email@example.com' };
- const idPostData = { ...postData, user_id: '1' };
-
describe('when invites are sent successfully', () => {
beforeEach(async () => {
createComponent();
await triggerMembersTokenSelect([user1, user3]);
wrapper.vm.$toast = { show: jest.fn() };
- jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
- jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
+ jest.spyOn(Api, 'inviteGroupMembers').mockResolvedValue({ data: postData });
});
describe('when triggered from regular mounting', () => {
@@ -637,12 +599,8 @@ describe('InviteMembersModal', () => {
clickInviteButton();
});
- it('calls Api inviteGroupMembersByEmail with the correct params', () => {
- expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(propsData.id, emailPostData);
- });
-
- it('calls Api addGroupMembersByUserId with the correct params', () => {
- expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(propsData.id, idPostData);
+ it('calls Api inviteGroupMembers with the correct params', () => {
+ expect(Api.inviteGroupMembers).toHaveBeenCalledWith(propsData.id, postData);
});
it('displays the successful toastMessage', () => {
@@ -655,12 +613,8 @@ describe('InviteMembersModal', () => {
clickInviteButton();
- expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(propsData.id, {
- ...emailPostData,
- invite_source: '_invite_source_',
- });
- expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(propsData.id, {
- ...idPostData,
+ expect(Api.inviteGroupMembers).toHaveBeenCalledWith(propsData.id, {
+ ...postData,
invite_source: '_invite_source_',
});
});
@@ -673,7 +627,6 @@ describe('InviteMembersModal', () => {
await triggerMembersTokenSelect([user1, user3]);
mockInvitationsApi(httpStatus.BAD_REQUEST, invitationsApiResponse.EMAIL_INVALID);
- mockMembersApi(httpStatus.OK, '200 OK');
clickInviteButton();
});
@@ -692,7 +645,7 @@ describe('InviteMembersModal', () => {
await triggerMembersTokenSelect([user3]);
wrapper.vm.$toast = { show: jest.fn() };
- jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({});
+ jest.spyOn(Api, 'inviteGroupMembers').mockResolvedValue({});
});
it('tracks the view for learn_gitlab source', () => {
diff --git a/spec/frontend/invite_members/components/invite_modal_base_spec.js b/spec/frontend/invite_members/components/invite_modal_base_spec.js
index 9e17112fb15..8355ae67f20 100644
--- a/spec/frontend/invite_members/components/invite_modal_base_spec.js
+++ b/spec/frontend/invite_members/components/invite_modal_base_spec.js
@@ -49,8 +49,6 @@ describe('InviteModalBase', () => {
const findDatepicker = () => wrapper.findComponent(GlDatepicker);
const findLink = () => wrapper.findComponent(GlLink);
const findIntroText = () => wrapper.findByTestId('modal-base-intro-text').text();
- const findCancelButton = () => wrapper.findByTestId('cancel-button');
- const findInviteButton = () => wrapper.findByTestId('invite-button');
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
describe('rendering the modal', () => {
@@ -67,15 +65,21 @@ describe('InviteModalBase', () => {
});
it('renders the Cancel button text correctly', () => {
- expect(findCancelButton().text()).toBe(CANCEL_BUTTON_TEXT);
- });
-
- it('renders the Invite button text correctly', () => {
- expect(findInviteButton().text()).toBe(INVITE_BUTTON_TEXT);
+ expect(wrapper.findComponent(GlModal).props('actionCancel')).toMatchObject({
+ text: CANCEL_BUTTON_TEXT,
+ });
});
- it('renders the Invite button modal without isLoading', () => {
- expect(findInviteButton().props('loading')).toBe(false);
+ it('renders the Invite button correctly', () => {
+ expect(wrapper.findComponent(GlModal).props('actionPrimary')).toMatchObject({
+ text: INVITE_BUTTON_TEXT,
+ attributes: {
+ variant: 'confirm',
+ disabled: false,
+ loading: false,
+ 'data-qa-selector': 'invite_button',
+ },
+ });
});
describe('rendering the access levels dropdown', () => {
@@ -114,7 +118,7 @@ describe('InviteModalBase', () => {
isLoading: true,
});
- expect(findInviteButton().props('loading')).toBe(true);
+ expect(wrapper.findComponent(GlModal).props('actionPrimary').attributes.loading).toBe(true);
});
it('with invalidFeedbackMessage, set members form group validation state', () => {
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 196a716d08c..bf5564e4d63 100644
--- a/spec/frontend/invite_members/components/members_token_select_spec.js
+++ b/spec/frontend/invite_members/components/members_token_select_spec.js
@@ -95,7 +95,7 @@ describe('MembersTokenSelect', () => {
expect(UserApi.getUsers).toHaveBeenCalledWith(searchParam, {
active: true,
- exclude_internal: true,
+ without_project_bots: true,
});
expect(tokenSelector.props('hideDropdownWithNoItems')).toBe(false);
});
@@ -172,7 +172,7 @@ describe('MembersTokenSelect', () => {
expect(UserApi.getUsers).toHaveBeenCalledWith(searchParam, {
active: true,
- exclude_internal: true,
+ without_project_bots: true,
saml_provider_id: samlProviderId,
});
});
diff --git a/spec/frontend/invite_members/components/user_limit_notification_spec.js b/spec/frontend/invite_members/components/user_limit_notification_spec.js
new file mode 100644
index 00000000000..c779cf2ee3f
--- /dev/null
+++ b/spec/frontend/invite_members/components/user_limit_notification_spec.js
@@ -0,0 +1,71 @@
+import { GlAlert, GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import UserLimitNotification from '~/invite_members/components/user_limit_notification.vue';
+
+describe('UserLimitNotification', () => {
+ let wrapper;
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ const createComponent = (providers = {}) => {
+ wrapper = shallowMountExtended(UserLimitNotification, {
+ provide: {
+ name: 'my group',
+ newTrialRegistrationPath: 'newTrialRegistrationPath',
+ purchasePath: 'purchasePath',
+ freeUsersLimit: 5,
+ membersCount: 1,
+ ...providers,
+ },
+ stubs: { GlSprintf },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when limit is not reached', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders empty block', () => {
+ expect(findAlert().exists()).toBe(false);
+ });
+ });
+
+ describe('when close to limit', () => {
+ beforeEach(() => {
+ createComponent({ membersCount: 3 });
+ });
+
+ it("renders user's limit notification", () => {
+ const alert = findAlert();
+
+ expect(alert.attributes('title')).toEqual(
+ 'You only have space for 2 more members in my group',
+ );
+
+ expect(alert.text()).toEqual(
+ 'To get more members an owner of this namespace can start a trial or upgrade to a paid tier.',
+ );
+ });
+ });
+
+ describe('when limit is reached', () => {
+ beforeEach(() => {
+ createComponent({ membersCount: 5 });
+ });
+
+ it("renders user's limit notification", () => {
+ const alert = findAlert();
+
+ expect(alert.attributes('title')).toEqual("You've reached your 5 members limit for my group");
+
+ expect(alert.text()).toEqual(
+ 'New members will be unable to participate. You can manage your members by removing ones you no longer need. To get more members an owner of this namespace can start a trial or upgrade to a paid tier.',
+ );
+ });
+ });
+});