gitlab-org--gitlab-foss/spec/frontend/invite_members/components/invite_modal_base_spec.js

268 lines
8.4 KiB
JavaScript

import {
GlDropdown,
GlDropdownItem,
GlDatepicker,
GlFormGroup,
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_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 = {}, stubs = {}) => {
wrapper = shallowMountExtended(InviteModalBase, {
propsData: {
...propsData,
...props,
},
stubs: {
ContentTransition,
GlModal: stubComponent(GlModal, {
template:
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
}),
GlDropdown: true,
GlDropdownItem: true,
GlSprintf,
GlFormGroup: stubComponent(GlFormGroup, {
props: ['state', 'invalidFeedback'],
}),
...stubs,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findDropdown = () => wrapper.findComponent(GlDropdown);
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(() => {
createComponent();
});
it('renders the modal with the correct title', () => {
expect(wrapper.findComponent(GlModal).props('title')).toBe(propsData.modalTitle);
});
it('displays the introText', () => {
expect(findIntroText()).toBe(propsData.labelIntroText);
});
it('renders the Cancel button text correctly', () => {
expect(wrapper.findComponent(GlModal).props('actionCancel')).toMatchObject({
text: CANCEL_BUTTON_TEXT,
});
});
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', () => {
it('sets the default dropdown text to the default access level name', () => {
expect(findDropdown().attributes('text')).toBe('Guest');
});
it('renders dropdown items for each accessLevel', () => {
expect(findDropdownItems()).toHaveLength(5);
});
});
describe('rendering the help link', () => {
it('renders the correct link', () => {
expect(findLink().attributes('href')).toBe(propsData.helpLink);
});
});
describe('rendering the access expiration date field', () => {
it('renders the datepicker', () => {
expect(findDatepicker().exists()).toBe(true);
});
});
it('renders the members form group', () => {
expect(findMembersFormGroup().props()).toEqual({
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 user limit is close on a personal namespace', () => {
beforeEach(() => {
createComponent(
{
closeToLimit: true,
reachedLimit: false,
usersLimitDataset: { membersPath, userNamespace: true },
},
{ GlModal, GlFormGroup },
);
});
it('renders correct buttons', () => {
const cancelButton = findCancelButton();
const actionButton = findActionButton();
expect(cancelButton.text()).toBe(INVITE_BUTTON_TEXT_DISABLED);
expect(cancelButton.attributes('href')).toBe(membersPath);
expect(actionButton.text()).toBe(INVITE_BUTTON_TEXT);
expect(actionButton.attributes('href')).toBe(); // default submit button
});
});
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', () => {
createComponent({
isLoading: true,
});
expect(wrapper.findComponent(GlModal).props('actionPrimary').attributes.loading).toBe(true);
});
it('with invalidFeedbackMessage, set members form group exception state', () => {
createComponent({
invalidFeedbackMessage: 'invalid message!',
});
expect(findMembersFormGroup().props()).toEqual({
invalidFeedback: 'invalid message!',
state: false,
});
});
});