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')
-rw-r--r--spec/frontend/invite_members/components/invite_members_modal_spec.js80
-rw-r--r--spec/frontend/invite_members/components/invite_modal_base_spec.js120
-rw-r--r--spec/frontend/invite_members/components/user_limit_notification_spec.js59
-rw-r--r--spec/frontend/invite_members/mock_data/member_modal.js2
-rw-r--r--spec/frontend/invite_members/mock_data/modal_base.js3
5 files changed, 224 insertions, 40 deletions
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 84317da39e6..13985ce7d74 100644
--- a/spec/frontend/invite_members/components/invite_members_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js
@@ -1,4 +1,4 @@
-import { GlLink, GlModal, GlSprintf } from '@gitlab/ui';
+import { GlLink, GlModal, GlSprintf, GlFormGroup } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { stubComponent } from 'helpers/stub_component';
@@ -15,6 +15,7 @@ import {
MEMBERS_MODAL_CELEBRATE_INTRO,
MEMBERS_MODAL_CELEBRATE_TITLE,
MEMBERS_PLACEHOLDER,
+ MEMBERS_PLACEHOLDER_DISABLED,
MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT,
LEARN_GITLAB,
} from '~/invite_members/constants';
@@ -28,6 +29,8 @@ import {
propsData,
inviteSource,
newProjectPath,
+ freeUsersLimit,
+ membersCount,
user1,
user2,
user3,
@@ -45,12 +48,13 @@ describe('InviteMembersModal', () => {
let wrapper;
let mock;
- const createComponent = (props = {}) => {
+ const createComponent = (props = {}, stubs = {}) => {
wrapper = shallowMountExtended(InviteMembersModal, {
provide: {
newProjectPath,
},
propsData: {
+ usersLimitDataset: {},
...propsData,
...props,
},
@@ -62,16 +66,17 @@ describe('InviteMembersModal', () => {
template: '<div><slot></slot><slot name="modal-footer"></slot></div>',
}),
GlEmoji,
+ ...stubs,
},
});
};
- const createInviteMembersToProjectWrapper = () => {
- createComponent({ isProject: true });
+ const createInviteMembersToProjectWrapper = (usersLimitDataset = {}, stubs = {}) => {
+ createComponent({ usersLimitDataset, isProject: true }, stubs);
};
- const createInviteMembersToGroupWrapper = () => {
- createComponent({ isProject: false });
+ const createInviteMembersToGroupWrapper = (usersLimitDataset = {}, stubs = {}) => {
+ createComponent({ usersLimitDataset, isProject: false }, stubs);
};
beforeEach(() => {
@@ -95,7 +100,7 @@ describe('InviteMembersModal', () => {
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
const membersFormGroupInvalidFeedback = () =>
findMembersFormGroup().attributes('invalid-feedback');
- const membersFormGroupDescription = () => findMembersFormGroup().attributes('description');
+ const membersFormGroupText = () => findMembersFormGroup().text();
const findMembersSelect = () => wrapper.findComponent(MembersTokenSelect);
const findTasksToBeDone = () => wrapper.findByTestId('invite-members-modal-tasks-to-be-done');
const findTasks = () => wrapper.findByTestId('invite-members-modal-tasks');
@@ -259,16 +264,33 @@ describe('InviteMembersModal', () => {
expect(wrapper.findComponent(ModalConfetti).exists()).toBe(false);
});
- it('includes the correct invitee, type, and formatted name', () => {
+ it('includes the correct invitee', () => {
expect(findIntroText()).toBe("You're inviting members to the test name project.");
expect(findCelebrationEmoji().exists()).toBe(false);
- expect(membersFormGroupDescription()).toBe(MEMBERS_PLACEHOLDER);
+ });
+
+ describe('members form group description', () => {
+ it('renders correct description', () => {
+ createInviteMembersToProjectWrapper({ freeUsersLimit, membersCount }, { GlFormGroup });
+ expect(membersFormGroupText()).toContain(MEMBERS_PLACEHOLDER);
+ });
+
+ describe('when reached user limit', () => {
+ it('renders correct description', () => {
+ createInviteMembersToProjectWrapper(
+ { freeUsersLimit, membersCount: 5 },
+ { GlFormGroup },
+ );
+
+ expect(membersFormGroupText()).toContain(MEMBERS_PLACEHOLDER_DISABLED);
+ });
+ });
});
});
describe('when inviting members with celebration', () => {
beforeEach(async () => {
- createComponent({ isProject: true });
+ createInviteMembersToProjectWrapper();
await triggerOpenModal({ mode: 'celebrate' });
});
@@ -285,7 +307,28 @@ describe('InviteMembersModal', () => {
`${MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT} ${MEMBERS_MODAL_CELEBRATE_INTRO}`,
);
expect(findCelebrationEmoji().exists()).toBe(true);
- expect(membersFormGroupDescription()).toBe(MEMBERS_PLACEHOLDER);
+ });
+
+ describe('members form group description', () => {
+ it('renders correct description', async () => {
+ createInviteMembersToProjectWrapper({ freeUsersLimit, membersCount }, { GlFormGroup });
+ await triggerOpenModal({ mode: 'celebrate' });
+
+ expect(membersFormGroupText()).toContain(MEMBERS_PLACEHOLDER);
+ });
+
+ describe('when reached user limit', () => {
+ it('renders correct description', async () => {
+ createInviteMembersToProjectWrapper(
+ { freeUsersLimit, membersCount: 5 },
+ { GlFormGroup },
+ );
+
+ await triggerOpenModal({ mode: 'celebrate' });
+
+ expect(membersFormGroupText()).toContain(MEMBERS_PLACEHOLDER_DISABLED);
+ });
+ });
});
});
});
@@ -295,7 +338,20 @@ describe('InviteMembersModal', () => {
createInviteMembersToGroupWrapper();
expect(findIntroText()).toBe("You're inviting members to the test name group.");
- expect(membersFormGroupDescription()).toBe(MEMBERS_PLACEHOLDER);
+ });
+
+ describe('members form group description', () => {
+ it('renders correct description', () => {
+ createInviteMembersToGroupWrapper({ freeUsersLimit, membersCount }, { GlFormGroup });
+ expect(membersFormGroupText()).toContain(MEMBERS_PLACEHOLDER);
+ });
+
+ describe('when reached user limit', () => {
+ it('renders correct description', () => {
+ createInviteMembersToGroupWrapper({ freeUsersLimit, membersCount: 5 }, { GlFormGroup });
+ expect(membersFormGroupText()).toContain(MEMBERS_PLACEHOLDER_DISABLED);
+ });
+ });
});
});
});
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 8355ae67f20..010f7b999fc 100644
--- a/spec/frontend/invite_members/components/invite_modal_base_spec.js
+++ b/spec/frontend/invite_members/components/invite_modal_base_spec.js
@@ -6,18 +6,30 @@ import {
GlSprintf,
GlLink,
GlModal,
+ GlIcon,
} from '@gitlab/ui';
import { stubComponent } from 'helpers/stub_component';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import InviteModalBase from '~/invite_members/components/invite_modal_base.vue';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
-import { CANCEL_BUTTON_TEXT, INVITE_BUTTON_TEXT } from '~/invite_members/constants';
-import { propsData } from '../mock_data/modal_base';
+
+import {
+ CANCEL_BUTTON_TEXT,
+ INVITE_BUTTON_TEXT_DISABLED,
+ INVITE_BUTTON_TEXT,
+ CANCEL_BUTTON_TEXT_DISABLED,
+ ON_SHOW_TRACK_LABEL,
+ ON_CLOSE_TRACK_LABEL,
+ ON_SUBMIT_TRACK_LABEL,
+} from '~/invite_members/constants';
+
+import { propsData, membersPath, purchasePath } from '../mock_data/modal_base';
describe('InviteModalBase', () => {
let wrapper;
- const createComponent = (props = {}) => {
+ const createComponent = (props = {}, stubs = {}) => {
wrapper = shallowMountExtended(InviteModalBase, {
propsData: {
...propsData,
@@ -33,8 +45,9 @@ describe('InviteModalBase', () => {
GlDropdownItem: true,
GlSprintf,
GlFormGroup: stubComponent(GlFormGroup, {
- props: ['state', 'invalidFeedback', 'description'],
+ props: ['state', 'invalidFeedback'],
}),
+ ...stubs,
},
});
};
@@ -48,8 +61,12 @@ describe('InviteModalBase', () => {
const findDropdownItems = () => findDropdown().findAllComponents(GlDropdownItem);
const findDatepicker = () => wrapper.findComponent(GlDatepicker);
const findLink = () => wrapper.findComponent(GlLink);
+ const findIcon = () => wrapper.findComponent(GlIcon);
const findIntroText = () => wrapper.findByTestId('modal-base-intro-text').text();
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
+ const findDisabledInput = () => wrapper.findByTestId('disabled-input');
+ const findCancelButton = () => wrapper.find('.js-modal-action-cancel');
+ const findActionButton = () => wrapper.find('.js-modal-action-primary');
describe('rendering the modal', () => {
beforeEach(() => {
@@ -106,11 +123,103 @@ describe('InviteModalBase', () => {
it('renders the members form group', () => {
expect(findMembersFormGroup().props()).toEqual({
- description: propsData.formGroupDescription,
invalidFeedback: '',
state: null,
});
});
+
+ it('renders description', () => {
+ createComponent({}, { GlFormGroup });
+
+ expect(findMembersFormGroup().text()).toContain(propsData.formGroupDescription);
+ });
+
+ describe('when users limit is reached', () => {
+ let trackingSpy;
+
+ const expectTracking = (action, label) =>
+ expect(trackingSpy).toHaveBeenCalledWith('default', action, {
+ label,
+ category: 'default',
+ });
+
+ beforeEach(() => {
+ createComponent(
+ { usersLimitDataset: { membersPath, purchasePath }, reachedLimit: true },
+ { GlModal, GlFormGroup },
+ );
+ });
+
+ it('renders correct blocks', () => {
+ expect(findIcon().exists()).toBe(true);
+ expect(findDisabledInput().exists()).toBe(true);
+ expect(findDropdown().exists()).toBe(false);
+ expect(findDatepicker().exists()).toBe(false);
+ });
+
+ it('renders correct buttons', () => {
+ const cancelButton = findCancelButton();
+ const actionButton = findActionButton();
+
+ expect(cancelButton.attributes('href')).toBe(purchasePath);
+ expect(cancelButton.text()).toBe(CANCEL_BUTTON_TEXT_DISABLED);
+ expect(actionButton.attributes('href')).toBe(membersPath);
+ expect(actionButton.text()).toBe(INVITE_BUTTON_TEXT_DISABLED);
+ });
+
+ it('tracks actions', () => {
+ createComponent({ reachedLimit: true }, { GlFormGroup, GlModal });
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+ const modal = wrapper.findComponent(GlModal);
+
+ modal.vm.$emit('shown');
+ expectTracking('render', ON_SHOW_TRACK_LABEL);
+
+ modal.vm.$emit('cancel', { preventDefault: jest.fn() });
+ expectTracking('click_button', ON_CLOSE_TRACK_LABEL);
+
+ modal.vm.$emit('primary', { preventDefault: jest.fn() });
+ expectTracking('click_button', ON_SUBMIT_TRACK_LABEL);
+
+ unmockTracking();
+ });
+
+ describe('when free user namespace', () => {
+ it('hides cancel button', () => {
+ createComponent(
+ {
+ usersLimitDataset: { membersPath, purchasePath, userNamespace: true },
+ reachedLimit: true,
+ },
+ { GlModal, GlFormGroup },
+ );
+
+ expect(findCancelButton().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('when users limit is not reached', () => {
+ const textRegex = /Select a role.+Read more about role permissions Access expiration date \(optional\)/;
+
+ beforeEach(() => {
+ createComponent({ reachedLimit: false }, { GlModal, GlFormGroup });
+ });
+
+ it('renders correct blocks', () => {
+ expect(findIcon().exists()).toBe(false);
+ expect(findDisabledInput().exists()).toBe(false);
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDatepicker().exists()).toBe(true);
+ expect(wrapper.findComponent(GlModal).text()).toMatch(textRegex);
+ });
+
+ it('renders correct buttons', () => {
+ expect(findCancelButton().text()).toBe(CANCEL_BUTTON_TEXT);
+ expect(findActionButton().text()).toBe(INVITE_BUTTON_TEXT);
+ });
+ });
});
it('with isLoading, shows loading for invite button', () => {
@@ -127,7 +236,6 @@ describe('InviteModalBase', () => {
});
expect(findMembersFormGroup().props()).toEqual({
- description: propsData.formGroupDescription,
invalidFeedback: 'invalid message!',
state: false,
});
diff --git a/spec/frontend/invite_members/components/user_limit_notification_spec.js b/spec/frontend/invite_members/components/user_limit_notification_spec.js
index c779cf2ee3f..4c9adbfcc44 100644
--- a/spec/frontend/invite_members/components/user_limit_notification_spec.js
+++ b/spec/frontend/invite_members/components/user_limit_notification_spec.js
@@ -2,21 +2,31 @@ import { GlAlert, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import UserLimitNotification from '~/invite_members/components/user_limit_notification.vue';
+import {
+ REACHED_LIMIT_MESSAGE,
+ REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
+} from '~/invite_members/constants';
+
+import { freeUsersLimit, membersCount } from '../mock_data/member_modal';
+
describe('UserLimitNotification', () => {
let wrapper;
const findAlert = () => wrapper.findComponent(GlAlert);
- const createComponent = (providers = {}) => {
+ const createComponent = (reachedLimit = false, usersLimitDataset = {}) => {
wrapper = shallowMountExtended(UserLimitNotification, {
- provide: {
- name: 'my group',
- newTrialRegistrationPath: 'newTrialRegistrationPath',
- purchasePath: 'purchasePath',
- freeUsersLimit: 5,
- membersCount: 1,
- ...providers,
+ propsData: {
+ reachedLimit,
+ usersLimitDataset: {
+ freeUsersLimit,
+ membersCount,
+ newTrialRegistrationPath: 'newTrialRegistrationPath',
+ purchasePath: 'purchasePath',
+ ...usersLimitDataset,
+ },
},
+ provide: { name: 'my group' },
stubs: { GlSprintf },
});
};
@@ -26,21 +36,17 @@ describe('UserLimitNotification', () => {
});
describe('when limit is not reached', () => {
- beforeEach(() => {
+ it('renders empty block', () => {
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", () => {
+ createComponent(false, { membersCount: 3 });
+
const alert = findAlert();
expect(alert.attributes('title')).toEqual(
@@ -54,18 +60,27 @@ describe('UserLimitNotification', () => {
});
describe('when limit is reached', () => {
- beforeEach(() => {
- createComponent({ membersCount: 5 });
- });
-
it("renders user's limit notification", () => {
+ createComponent(true);
+
const alert = findAlert();
expect(alert.attributes('title')).toEqual("You've reached your 5 members limit for my group");
+ expect(alert.text()).toEqual(REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE);
+ });
- 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.',
- );
+ describe('when free user namespace', () => {
+ it("renders user's limit notification", () => {
+ createComponent(true, { userNamespace: true });
+
+ const alert = findAlert();
+
+ expect(alert.attributes('title')).toEqual(
+ "You've reached your 5 members limit for my group",
+ );
+
+ expect(alert.text()).toEqual(REACHED_LIMIT_MESSAGE);
+ });
});
});
});
diff --git a/spec/frontend/invite_members/mock_data/member_modal.js b/spec/frontend/invite_members/mock_data/member_modal.js
index 1b0cc57fb5b..474234cfacb 100644
--- a/spec/frontend/invite_members/mock_data/member_modal.js
+++ b/spec/frontend/invite_members/mock_data/member_modal.js
@@ -18,6 +18,8 @@ export const propsData = {
export const inviteSource = 'unknown';
export const newProjectPath = 'projects/new';
+export const freeUsersLimit = 5;
+export const membersCount = 1;
export const user1 = { id: 1, name: 'Name One', username: 'one_1', avatar_url: '' };
export const user2 = { id: 2, name: 'Name Two', username: 'one_2', avatar_url: '' };
diff --git a/spec/frontend/invite_members/mock_data/modal_base.js b/spec/frontend/invite_members/mock_data/modal_base.js
index ea5a8d2b00d..565e8d4df1e 100644
--- a/spec/frontend/invite_members/mock_data/modal_base.js
+++ b/spec/frontend/invite_members/mock_data/modal_base.js
@@ -9,3 +9,6 @@ export const propsData = {
labelSearchField: '_label_search_field_',
formGroupDescription: '_form_group_description_',
};
+
+export const membersPath = '/members_path';
+export const purchasePath = '/purchase_path';