Commit a63d8ad6 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Brandon Labuschagne

When REMOVING a user, warn Admin user is part of an on-call schedule

parent 617cb171
...@@ -42,6 +42,11 @@ export default { ...@@ -42,6 +42,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
oncallSchedules: {
type: Object,
required: false,
default: () => {},
},
}, },
computed: { computed: {
...mapState({ ...mapState({
...@@ -52,6 +57,9 @@ export default { ...@@ -52,6 +57,9 @@ export default {
computedMemberPath() { computedMemberPath() {
return this.memberPath.replace(':id', this.memberId); return this.memberPath.replace(':id', this.memberId);
}, },
stringifiedSchedules() {
return JSON.stringify(this.oncallSchedules);
},
}, },
}; };
</script> </script>
...@@ -69,6 +77,7 @@ export default { ...@@ -69,6 +77,7 @@ export default {
:data-is-access-request="isAccessRequest" :data-is-access-request="isAccessRequest"
:data-is-invite="isInvite" :data-is-invite="isInvite"
:data-message="message" :data-message="message"
:data-oncall-schedules="stringifiedSchedules"
data-qa-selector="delete_member_button" data-qa-selector="delete_member_button"
/> />
</template> </template>
...@@ -33,7 +33,7 @@ export default { ...@@ -33,7 +33,7 @@ export default {
if (user) { if (user) {
return sprintf( return sprintf(
s__('Members|Are you sure you want to remove %{usersName} from "%{source}"'), s__('Members|Are you sure you want to remove %{usersName} from "%{source}"?'),
{ {
usersName: user.name, usersName: user.name,
source: source.fullName, source: source.fullName,
...@@ -42,12 +42,16 @@ export default { ...@@ -42,12 +42,16 @@ export default {
} }
return sprintf( return sprintf(
s__('Members|Are you sure you want to remove this orphaned member from "%{source}"'), s__('Members|Are you sure you want to remove this orphaned member from "%{source}"?'),
{ {
source: source.fullName, source: source.fullName,
}, },
); );
}, },
oncallScheduleUserData() {
const { user: { name, oncallSchedules: schedules } = {} } = this.member;
return { name, schedules };
},
}, },
}; };
</script> </script>
...@@ -60,6 +64,7 @@ export default { ...@@ -60,6 +64,7 @@ export default {
v-else v-else
:member-id="member.id" :member-id="member.id"
:member-type="member.type" :member-type="member.type"
:oncall-schedules="oncallScheduleUserData"
:message="message" :message="message"
:title="s__('Member|Remove member')" :title="s__('Member|Remove member')"
/> />
......
...@@ -3,6 +3,7 @@ import { GlModal, GlForm, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; ...@@ -3,6 +3,7 @@ import { GlModal, GlForm, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
import { LEAVE_MODAL_ID } from '../../constants'; import { LEAVE_MODAL_ID } from '../../constants';
export default { export default {
...@@ -19,7 +20,7 @@ export default { ...@@ -19,7 +20,7 @@ export default {
csrf, csrf,
modalId: LEAVE_MODAL_ID, modalId: LEAVE_MODAL_ID,
modalContent: s__('Members|Are you sure you want to leave "%{source}"?'), modalContent: s__('Members|Are you sure you want to leave "%{source}"?'),
components: { GlModal, GlForm, GlSprintf }, components: { GlModal, GlForm, GlSprintf, OncallSchedulesList },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
...@@ -42,6 +43,12 @@ export default { ...@@ -42,6 +43,12 @@ export default {
modalTitle() { modalTitle() {
return sprintf(s__('Members|Leave "%{source}"'), { source: this.member.source.fullName }); return sprintf(s__('Members|Leave "%{source}"'), { source: this.member.source.fullName });
}, },
schedules() {
return this.member.user?.oncallSchedules;
},
isPartOfOnCallSchedules() {
return this.schedules?.length;
},
}, },
methods: { methods: {
handlePrimary() { handlePrimary() {
...@@ -58,7 +65,6 @@ export default { ...@@ -58,7 +65,6 @@ export default {
:title="modalTitle" :title="modalTitle"
:action-primary="$options.actionPrimary" :action-primary="$options.actionPrimary"
:action-cancel="$options.actionCancel" :action-cancel="$options.actionCancel"
size="sm"
@primary="handlePrimary" @primary="handlePrimary"
> >
<gl-form ref="form" :action="leavePath" method="post"> <gl-form ref="form" :action="leavePath" method="post">
...@@ -68,6 +74,12 @@ export default { ...@@ -68,6 +74,12 @@ export default {
</gl-sprintf> </gl-sprintf>
</p> </p>
<oncall-schedules-list
v-if="isPartOfOnCallSchedules"
:schedules="schedules"
:is-current-user="true"
/>
<input type="hidden" name="_method" value="delete" /> <input type="hidden" name="_method" value="delete" />
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> <input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
</gl-form> </gl-form>
......
<script>
import { GlSprintf, GlLink } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
export default {
components: {
GlSprintf,
GlLink,
},
props: {
schedules: {
type: Array,
required: true,
},
userName: {
type: String,
required: false,
default: null,
},
isCurrentUser: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
title() {
return this.isCurrentUser
? s__('OnCallSchedules|You are currently a part of:')
: sprintf(s__('OnCallSchedules|User %{name} is currently part of:'), {
name: this.userName,
});
},
footer() {
return this.isCurrentUser
? s__(
'OnCallSchedules|Removing yourself may put your on-call team at risk of missing a notification.',
)
: s__(
'OnCallSchedules|Removing this user may put their on-call team at risk of missing a notification.',
);
},
},
};
</script>
<template>
<div>
<p data-testid="title">{{ title }}</p>
<ul data-testid="schedules-list">
<li v-for="(schedule, index) in schedules" :key="`${schedule.name}-${index}`">
<gl-sprintf
:message="s__('OnCallSchedules|On-call schedule %{schedule} in Project %{project}')"
>
<template #schedule>
<gl-link :href="schedule.scheduleUrl" target="_blank">{{ schedule.name }}</gl-link>
</template>
<template #project>
<gl-link :href="schedule.projectUrl" target="_blank">{{
schedule.projectName
}}</gl-link>
</template>
</gl-sprintf>
</li>
</ul>
<p data-testid="footer">{{ footer }}</p>
</div>
</template>
<script> <script>
import { GlFormCheckbox, GlModal } from '@gitlab/ui'; import { GlFormCheckbox, GlModal } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
export default { export default {
actionCancel: { actionCancel: {
...@@ -12,6 +14,7 @@ export default { ...@@ -12,6 +14,7 @@ export default {
components: { components: {
GlFormCheckbox, GlFormCheckbox,
GlModal, GlModal,
OncallSchedulesList,
}, },
data() { data() {
return { return {
...@@ -48,6 +51,18 @@ export default { ...@@ -48,6 +51,18 @@ export default {
showUnassignIssuablesCheckbox() { showUnassignIssuablesCheckbox() {
return !this.isAccessRequest && !this.isInvite; return !this.isAccessRequest && !this.isInvite;
}, },
isPartOfOncallSchedules() {
return !this.isAccessRequest && this.oncallSchedules.schedules?.length;
},
oncallSchedules() {
let schedules = {};
try {
schedules = JSON.parse(this.modalData.oncallSchedules);
} catch (e) {
Sentry.captureException(e);
}
return schedules;
},
}, },
mounted() { mounted() {
document.addEventListener('click', this.handleClick); document.addEventListener('click', this.handleClick);
...@@ -83,6 +98,12 @@ export default { ...@@ -83,6 +98,12 @@ export default {
<form ref="form" :action="modalData.memberPath" method="post"> <form ref="form" :action="modalData.memberPath" method="post">
<p data-testid="modal-message">{{ modalData.message }}</p> <p data-testid="modal-message">{{ modalData.message }}</p>
<oncall-schedules-list
v-if="isPartOfOncallSchedules"
:schedules="oncallSchedules.schedules"
:user-name="oncallSchedules.name"
/>
<input ref="method" type="hidden" name="_method" value="delete" /> <input ref="method" type="hidden" name="_method" value="delete" />
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> <input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
<gl-form-checkbox v-if="isGroupMember" name="remove_sub_memberships"> <gl-form-checkbox v-if="isGroupMember" name="remove_sub_memberships">
......
---
title: "When removing a user, warn Admin user is part of an on-call schedule"
merge_request: 57397
author:
type: added
...@@ -19640,10 +19640,10 @@ msgstr "" ...@@ -19640,10 +19640,10 @@ msgstr ""
msgid "Members|Are you sure you want to remove \"%{groupName}\"?" msgid "Members|Are you sure you want to remove \"%{groupName}\"?"
msgstr "" msgstr ""
msgid "Members|Are you sure you want to remove %{usersName} from \"%{source}\"" msgid "Members|Are you sure you want to remove %{usersName} from \"%{source}\"?"
msgstr "" msgstr ""
msgid "Members|Are you sure you want to remove this orphaned member from \"%{source}\"" msgid "Members|Are you sure you want to remove this orphaned member from \"%{source}\"?"
msgstr "" msgstr ""
msgid "Members|Are you sure you want to revoke the invitation for %{inviteEmail} to join \"%{source}\"" msgid "Members|Are you sure you want to revoke the invitation for %{inviteEmail} to join \"%{source}\""
...@@ -22011,12 +22011,21 @@ msgstr "" ...@@ -22011,12 +22011,21 @@ msgstr ""
msgid "OnCallSchedules|For this rotation, on-call will be:" msgid "OnCallSchedules|For this rotation, on-call will be:"
msgstr "" msgstr ""
msgid "OnCallSchedules|On-call schedule %{schedule} in Project %{project}"
msgstr ""
msgid "OnCallSchedules|On-call schedules" msgid "OnCallSchedules|On-call schedules"
msgstr "" msgstr ""
msgid "OnCallSchedules|Please note, rotations with shifts that are less than four hours are currently not supported in the weekly view." msgid "OnCallSchedules|Please note, rotations with shifts that are less than four hours are currently not supported in the weekly view."
msgstr "" msgstr ""
msgid "OnCallSchedules|Removing this user may put their on-call team at risk of missing a notification."
msgstr ""
msgid "OnCallSchedules|Removing yourself may put your on-call team at risk of missing a notification."
msgstr ""
msgid "OnCallSchedules|Restrict to time intervals" msgid "OnCallSchedules|Restrict to time intervals"
msgstr "" msgstr ""
...@@ -22071,12 +22080,18 @@ msgstr "" ...@@ -22071,12 +22080,18 @@ msgstr ""
msgid "OnCallSchedules|Try adding a rotation" msgid "OnCallSchedules|Try adding a rotation"
msgstr "" msgstr ""
msgid "OnCallSchedules|User %{name} is currently part of:"
msgstr ""
msgid "OnCallSchedules|View next timeframe" msgid "OnCallSchedules|View next timeframe"
msgstr "" msgstr ""
msgid "OnCallSchedules|View previous timeframe" msgid "OnCallSchedules|View previous timeframe"
msgstr "" msgstr ""
msgid "OnCallSchedules|You are currently a part of:"
msgstr ""
msgid "OnCallSchedules|Your schedule has been successfully created and all alerts from this project will now be routed to this schedule. Currently, only one schedule can be created per project. More coming soon! To add individual users to this schedule, use the add a rotation button." msgid "OnCallSchedules|Your schedule has been successfully created and all alerts from this project will now be routed to this schedule. Currently, only one schedule can be created per project. More coming soon! To add individual users to this schedule, use the add a rotation button."
msgstr "" msgstr ""
......
...@@ -38,6 +38,7 @@ describe('RemoveMemberButton', () => { ...@@ -38,6 +38,7 @@ describe('RemoveMemberButton', () => {
title: 'Remove member', title: 'Remove member',
isAccessRequest: true, isAccessRequest: true,
isInvite: true, isInvite: true,
oncallSchedules: { name: 'user', schedules: [] },
...propsData, ...propsData,
}, },
directives: { directives: {
...@@ -59,6 +60,7 @@ describe('RemoveMemberButton', () => { ...@@ -59,6 +60,7 @@ describe('RemoveMemberButton', () => {
'data-message': 'Are you sure you want to remove John Smith?', 'data-message': 'Are you sure you want to remove John Smith?',
'data-is-access-request': 'true', 'data-is-access-request': 'true',
'data-is-invite': 'true', 'data-is-invite': 'true',
'data-oncall-schedules': '{"name":"user","schedules":[]}',
'aria-label': 'Remove member', 'aria-label': 'Remove member',
title: 'Remove member', title: 'Remove member',
icon: 'remove', icon: 'remove',
......
...@@ -40,11 +40,15 @@ describe('UserActionButtons', () => { ...@@ -40,11 +40,15 @@ describe('UserActionButtons', () => {
expect(findRemoveMemberButton().props()).toEqual({ expect(findRemoveMemberButton().props()).toEqual({
memberId: member.id, memberId: member.id,
memberType: 'GroupMember', memberType: 'GroupMember',
message: `Are you sure you want to remove ${member.user.name} from "${member.source.fullName}"`, message: `Are you sure you want to remove ${member.user.name} from "${member.source.fullName}"?`,
title: 'Remove member', title: 'Remove member',
isAccessRequest: false, isAccessRequest: false,
isInvite: false, isInvite: false,
icon: 'remove', icon: 'remove',
oncallSchedules: {
name: member.user.name,
schedules: member.user.oncallSchedules,
},
}); });
}); });
...@@ -58,7 +62,7 @@ describe('UserActionButtons', () => { ...@@ -58,7 +62,7 @@ describe('UserActionButtons', () => {
}); });
expect(findRemoveMemberButton().props('message')).toBe( 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}"?`,
); );
}); });
}); });
......
import { GlModal, GlForm } from '@gitlab/ui'; import { GlModal, GlForm } from '@gitlab/ui';
import { within } from '@testing-library/dom'; import { within } from '@testing-library/dom';
import { mount, createLocalVue, createWrapper } from '@vue/test-utils'; import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
import { cloneDeep } from 'lodash';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import LeaveModal from '~/members/components/modals/leave_modal.vue'; import LeaveModal from '~/members/components/modals/leave_modal.vue';
import { LEAVE_MODAL_ID, MEMBER_TYPES } 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'; import { member } from '../../mock_data';
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
...@@ -47,9 +49,9 @@ describe('LeaveModal', () => { ...@@ -47,9 +49,9 @@ describe('LeaveModal', () => {
}); });
}; };
const findModal = () => wrapper.find(GlModal); const findModal = () => wrapper.findComponent(GlModal);
const findForm = () => findModal().findComponent(GlForm);
const findForm = () => findModal().find(GlForm); const findOncallSchedulesList = () => findModal().findComponent(OncallSchedulesList);
const getByText = (text, options) => const getByText = (text, options) =>
createWrapper(within(findModal().element).getByText(text, options)); createWrapper(within(findModal().element).getByText(text, options));
...@@ -87,6 +89,24 @@ describe('LeaveModal', () => { ...@@ -87,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', () => { it('submits the form when "Leave" button is clicked', () => {
const submitSpy = jest.spyOn(findForm().element, 'submit'); const submitSpy = jest.spyOn(findForm().element, 'submit');
......
...@@ -20,6 +20,7 @@ export const member = { ...@@ -20,6 +20,7 @@ export const member = {
avatarUrl: 'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon', avatarUrl: 'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon',
blocked: false, blocked: false,
twoFactorEnabled: false, twoFactorEnabled: false,
oncallSchedules: [{ name: 'schedule 1' }],
}, },
id: 238, id: 238,
createdAt: '2020-07-17T16:22:46.923Z', createdAt: '2020-07-17T16:22:46.923Z',
......
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue'; import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
const mockSchedules = JSON.stringify({
schedules: [
{
id: 1,
name: 'Schedule 1',
},
],
name: 'User1',
});
describe('RemoveMemberModal', () => { describe('RemoveMemberModal', () => {
const memberPath = '/gitlab-org/gitlab-test/-/project_members/90'; const memberPath = '/gitlab-org/gitlab-test/-/project_members/90';
let wrapper; let wrapper;
const findForm = () => wrapper.find({ ref: 'form' }); const findForm = () => wrapper.find({ ref: 'form' });
const findGlModal = () => wrapper.find(GlModal); const findGlModal = () => wrapper.findComponent(GlModal);
const findOnCallSchedulesList = () => wrapper.findComponent(OncallSchedulesList);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -15,11 +27,11 @@ describe('RemoveMemberModal', () => { ...@@ -15,11 +27,11 @@ describe('RemoveMemberModal', () => {
}); });
describe.each` describe.each`
state | memberType | isAccessRequest | isInvite | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message 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 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?'} ${'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?"} ${'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?'} ${'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', 'when $state',
({ ({
...@@ -30,6 +42,7 @@ describe('RemoveMemberModal', () => { ...@@ -30,6 +42,7 @@ describe('RemoveMemberModal', () => {
message, message,
removeSubMembershipsCheckboxExpected, removeSubMembershipsCheckboxExpected,
unassignIssuablesCheckboxExpected, unassignIssuablesCheckboxExpected,
onCallSchedules,
}) => { }) => {
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(RemoveMemberModal, { wrapper = shallowMount(RemoveMemberModal, {
...@@ -41,12 +54,16 @@ describe('RemoveMemberModal', () => { ...@@ -41,12 +54,16 @@ describe('RemoveMemberModal', () => {
message, message,
memberPath, memberPath,
memberType, memberType,
onCallSchedules,
}, },
}; };
}, },
}); });
}); });
const parsedSchedules = JSON.parse(onCallSchedules);
const isPartOfOncallSchedules = Boolean(isAccessRequest && parsedSchedules.schedules?.length);
it(`has the title ${actionText}`, () => { it(`has the title ${actionText}`, () => {
expect(findGlModal().attributes('title')).toBe(actionText); expect(findGlModal().attributes('title')).toBe(actionText);
}); });
...@@ -75,6 +92,10 @@ describe('RemoveMemberModal', () => { ...@@ -75,6 +92,10 @@ describe('RemoveMemberModal', () => {
); );
}); });
it(`shows ${isPartOfOncallSchedules ? 'all' : 'no'} related on-call schedules`, () => {
expect(findOnCallSchedulesList().exists()).toBe(isPartOfOncallSchedules);
});
it('submits the form when the modal is submitted', () => { it('submits the form when the modal is submitted', () => {
const spy = jest.spyOn(findForm().element, 'submit'); const spy = jest.spyOn(findForm().element, 'submit');
......
import { GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
const mockSchedules = [
{
name: 'Schedule 1',
scheduleUrl: 'http://gitlab.com/gitlab-org/gitlab-shell/-/oncall_schedules',
projectName: 'Shell',
projectUrl: 'http://gitlab.com/gitlab-org/gitlab-shell/',
},
{
name: 'Schedule 2',
scheduleUrl: 'http://gitlab.com/gitlab-org/gitlab-ui/-/oncall_schedules',
projectName: 'UI',
projectUrl: 'http://gitlab.com/gitlab-org/gitlab-ui/',
},
];
const userName = 'User 1';
describe('On-call schedules list', () => {
let wrapper;
function createComponent(props) {
wrapper = extendedWrapper(
shallowMount(OncallSchedulesList, {
propsData: {
schedules: mockSchedules,
userName,
...props,
},
stubs: {
GlSprintf,
},
}),
);
}
afterEach(() => {
wrapper.destroy();
});
const findLinks = () => wrapper.findAllComponents(GlLink);
const findTitle = () => wrapper.findByTestId('title');
const findFooter = () => wrapper.findByTestId('footer');
const findSchedules = () => wrapper.findByTestId('schedules-list');
describe.each`
isCurrentUser | titleText | footerText
${true} | ${'You are currently a part of:'} | ${'Removing yourself may put your on-call team at risk of missing a notification.'}
${false} | ${`User ${userName} is currently part of:`} | ${'Removing this user may put their on-call team at risk of missing a notification.'}
`('when current user ', ({ isCurrentUser, titleText, footerText }) => {
it(`${isCurrentUser ? 'is' : 'is not'} a part of on-call schedule`, async () => {
createComponent({
isCurrentUser,
});
expect(findTitle().text()).toBe(titleText);
expect(findFooter().text()).toBe(footerText);
});
});
describe.each(mockSchedules)(
'renders each on-call schedule data',
({ name, scheduleUrl, projectName, projectUrl }) => {
beforeEach(() => {
createComponent({ schedules: [{ name, scheduleUrl, projectName, projectUrl }] });
});
it(`renders schedule ${name}'s name and link`, () => {
const msg = findSchedules().text();
expect(msg).toContain(`On-call schedule ${name}`);
expect(findLinks().at(0).attributes('href')).toBe(scheduleUrl);
});
it(`renders project ${projectName}'s name and link`, () => {
const msg = findSchedules().text();
expect(msg).toContain(`in Project ${projectName}`);
expect(findLinks().at(1).attributes('href')).toBe(projectUrl);
});
},
);
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment