Commit a98fec6d authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '263452-sidebar-display-busy-status' into 'master'

[FE] Set user availability - Add busy status to MR sidebar

See merge request gitlab-org/gitlab!47769
parents 14d6ca06 5c557194
<script> <script>
/* eslint-disable vue/no-v-html */ /* eslint-disable vue/no-v-html */
import { GlIcon, GlLoadingIcon, GlTooltipDirective, GlSprintf } from '@gitlab/ui'; import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import { isUserBusy } from '~/set_status_modal/utils';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserNameWithStatus from '../../sidebar/components/assignees/user_name_with_status.vue';
export default { export default {
components: { components: {
...@@ -12,7 +12,7 @@ export default { ...@@ -12,7 +12,7 @@ export default {
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'), import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
GlIcon, GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, UserNameWithStatus,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -90,10 +90,6 @@ export default { ...@@ -90,10 +90,6 @@ export default {
} }
return false; return false;
}, },
authorIsBusy() {
const { status } = this.author;
return status?.availability && isUserBusy(status.availability);
},
emojiElement() { emojiElement() {
return this.$refs?.authorStatus?.querySelector('gl-emoji'); return this.$refs?.authorStatus?.querySelector('gl-emoji');
}, },
...@@ -133,6 +129,9 @@ export default { ...@@ -133,6 +129,9 @@ export default {
this.$refs.authorNameLink.dispatchEvent(new Event('mouseleave')); this.$refs.authorNameLink.dispatchEvent(new Event('mouseleave'));
this.isUsernameLinkHovered = false; this.isUsernameLinkHovered = false;
}, },
userAvailability(selectedAuthor) {
return selectedAuthor?.availability || '';
},
}, },
}; };
</script> </script>
...@@ -158,12 +157,11 @@ export default { ...@@ -158,12 +157,11 @@ export default {
:data-username="author.username" :data-username="author.username"
> >
<slot name="note-header-info"></slot> <slot name="note-header-info"></slot>
<span class="note-header-author-name gl-font-weight-bold"> <user-name-with-status
<gl-sprintf v-if="authorIsBusy" :message="s__('UserAvailability|%{author} (Busy)')"> :name="authorName"
<template #author>{{ authorName }}</template> :availability="userAvailability(author)"
</gl-sprintf> container-classes="note-header-author-name gl-font-weight-bold"
<template v-else>{{ authorName }}</template> />
</span>
</a> </a>
<span <span
v-if="authorStatus" v-if="authorStatus"
......
<script>
import { AVAILABILITY_STATUS, isUserBusy, isValidAvailibility } from '../utils';
export default {
name: 'UserAvailabilityStatus',
props: {
availability: {
type: String,
required: true,
validator: isValidAvailibility,
},
},
computed: {
isBusy() {
const { availability = AVAILABILITY_STATUS.NOT_SET } = this;
return isUserBusy(availability);
},
},
};
</script>
<template>
<span v-if="isBusy" class="gl-font-weight-normal gl-text-gray-500">{{
s__('UserAvailability|(Busy)')
}}</span>
</template>
...@@ -10,7 +10,7 @@ import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; ...@@ -10,7 +10,7 @@ import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { updateUserStatus } from '~/rest_api'; import { updateUserStatus } from '~/rest_api';
import EmojiMenuInModal from './emoji_menu_in_modal'; import EmojiMenuInModal from './emoji_menu_in_modal';
import { isUserBusy, isValidAvailibility } from './utils'; import { isUserBusy } from './utils';
const emojiMenuClass = 'js-modal-status-emoji-menu'; const emojiMenuClass = 'js-modal-status-emoji-menu';
export const AVAILABILITY_STATUS = { export const AVAILABILITY_STATUS = {
...@@ -46,7 +46,6 @@ export default { ...@@ -46,7 +46,6 @@ export default {
currentAvailability: { currentAvailability: {
type: String, type: String,
required: false, required: false,
validator: isValidAvailibility,
default: '', default: '',
}, },
canSetUserAvailability: { canSetUserAvailability: {
......
...@@ -3,7 +3,5 @@ export const AVAILABILITY_STATUS = { ...@@ -3,7 +3,5 @@ export const AVAILABILITY_STATUS = {
NOT_SET: 'not_set', NOT_SET: 'not_set',
}; };
export const isUserBusy = (status) => status === AVAILABILITY_STATUS.BUSY; export const isUserBusy = (status = '') =>
Boolean(status.length && status.toLowerCase().trim() === AVAILABILITY_STATUS.BUSY);
export const isValidAvailibility = (availability) =>
availability.length ? Object.values(AVAILABILITY_STATUS).includes(availability) : true;
<script> <script>
import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { isUserBusy } from '~/set_status_modal/utils';
import AssigneeAvatar from './assignee_avatar.vue'; import AssigneeAvatar from './assignee_avatar.vue';
const I18N = {
BUSY: __('Busy'),
CANNOT_MERGE: __('Cannot merge'),
LC_CANNOT_MERGE: __('cannot merge'),
};
const paranthesize = (str) => `(${str})`;
const generateAssigneeTooltip = ({
name,
availability,
cannotMerge = true,
tooltipHasName = false,
}) => {
if (!tooltipHasName) {
return cannotMerge ? I18N.CANNOT_MERGE : '';
}
const statusInformation = [];
if (availability && isUserBusy(availability)) {
statusInformation.push(I18N.BUSY);
}
if (cannotMerge) {
statusInformation.push(I18N.LC_CANNOT_MERGE);
}
if (tooltipHasName && statusInformation.length) {
return sprintf(__('%{name} %{status}'), {
name,
status: statusInformation.map(paranthesize).join(' '),
});
}
return name;
};
export default { export default {
components: { components: {
AssigneeAvatar, AssigneeAvatar,
...@@ -37,15 +75,13 @@ export default { ...@@ -37,15 +75,13 @@ export default {
return this.issuableType === 'merge_request' && !this.user.can_merge; return this.issuableType === 'merge_request' && !this.user.can_merge;
}, },
tooltipTitle() { tooltipTitle() {
if (this.cannotMerge && this.tooltipHasName) { const { name = '', availability = '' } = this.user;
return sprintf(__('%{userName} (cannot merge)'), { userName: this.user.name }); return generateAssigneeTooltip({
} else if (this.cannotMerge) { name,
return __('Cannot merge'); availability,
} else if (this.tooltipHasName) { cannotMerge: this.cannotMerge,
return this.user.name; tooltipHasName: this.tooltipHasName,
} });
return '';
}, },
tooltipOption() { tooltipOption() {
return { return {
......
...@@ -36,7 +36,6 @@ export default { ...@@ -36,7 +36,6 @@ export default {
sortedAssigness() { sortedAssigness() {
const canMergeUsers = this.users.filter((user) => user.can_merge); const canMergeUsers = this.users.filter((user) => user.can_merge);
const canNotMergeUsers = this.users.filter((user) => !user.can_merge); const canNotMergeUsers = this.users.filter((user) => !user.can_merge);
return [...canMergeUsers, ...canNotMergeUsers]; return [...canMergeUsers, ...canNotMergeUsers];
}, },
}, },
......
<script> <script>
import AssigneeAvatar from './assignee_avatar.vue'; import AssigneeAvatar from './assignee_avatar.vue';
import UserNameWithStatus from './user_name_with_status.vue';
export default { export default {
components: { components: {
AssigneeAvatar, AssigneeAvatar,
UserNameWithStatus,
}, },
props: { props: {
user: { user: {
...@@ -16,12 +18,20 @@ export default { ...@@ -16,12 +18,20 @@ export default {
default: 'issue', default: 'issue',
}, },
}, },
computed: {
availability() {
return this.user?.availability || '';
},
},
}; };
</script> </script>
<template> <template>
<button type="button" class="btn-link"> <button type="button" class="btn-link">
<assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" /> <assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" />
<span class="author"> {{ user.name }} </span> <user-name-with-status
:name="user.name"
:availability="availability"
container-classes="author"
/>
</button> </button>
</template> </template>
<script> <script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { isUserBusy } from '~/set_status_modal/utils';
import CollapsedAssignee from './collapsed_assignee.vue'; import CollapsedAssignee from './collapsed_assignee.vue';
const DEFAULT_MAX_COUNTER = 99; const DEFAULT_MAX_COUNTER = 99;
const DEFAULT_RENDER_COUNT = 5; const DEFAULT_RENDER_COUNT = 5;
const generateCollapsedAssigneeTooltip = ({ renderUsers, allUsers, tooltipTitleMergeStatus }) => {
const names = renderUsers.map(({ name, availability }) => {
if (availability && isUserBusy(availability)) {
return sprintf(__('%{name} (Busy)'), { name });
}
return name;
});
if (!allUsers.length) {
return __('Assignee(s)');
}
if (allUsers.length > names.length) {
names.push(sprintf(__('+ %{amount} more'), { amount: allUsers.length - names.length }));
}
const text = names.join(', ');
return tooltipTitleMergeStatus ? `${text} (${tooltipTitleMergeStatus})` : text;
};
export default { export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -74,19 +93,11 @@ export default { ...@@ -74,19 +93,11 @@ export default {
tooltipTitle() { tooltipTitle() {
const maxRender = Math.min(DEFAULT_RENDER_COUNT, this.users.length); const maxRender = Math.min(DEFAULT_RENDER_COUNT, this.users.length);
const renderUsers = this.users.slice(0, maxRender); const renderUsers = this.users.slice(0, maxRender);
const names = renderUsers.map((u) => u.name); return generateCollapsedAssigneeTooltip({
renderUsers,
if (!this.users.length) { allUsers: this.users,
return __('Assignee(s)'); tooltipTitleMergeStatus: this.tooltipTitleMergeStatus,
} });
if (this.users.length > names.length) {
names.push(sprintf(__('+ %{amount} more'), { amount: this.users.length - names.length }));
}
const text = names.join(', ');
return this.tooltipTitleMergeStatus ? `${text} (${this.tooltipTitleMergeStatus})` : text;
}, },
tooltipOptions() { tooltipOptions() {
......
<script> <script>
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import AssigneeAvatarLink from './assignee_avatar_link.vue'; import AssigneeAvatarLink from './assignee_avatar_link.vue';
import UserNameWithStatus from './user_name_with_status.vue';
const DEFAULT_RENDER_COUNT = 5; const DEFAULT_RENDER_COUNT = 5;
export default { export default {
components: { components: {
AssigneeAvatarLink, AssigneeAvatarLink,
UserNameWithStatus,
}, },
props: { props: {
users: { users: {
...@@ -55,6 +57,9 @@ export default { ...@@ -55,6 +57,9 @@ export default {
toggleShowLess() { toggleShowLess() {
this.showLess = !this.showLess; this.showLess = !this.showLess;
}, },
userAvailability(u) {
return u?.availability || '';
},
}, },
}; };
</script> </script>
...@@ -68,7 +73,7 @@ export default { ...@@ -68,7 +73,7 @@ export default {
:issuable-type="issuableType" :issuable-type="issuableType"
> >
<div class="ml-2 gl-line-height-normal"> <div class="ml-2 gl-line-height-normal">
<div>{{ firstUser.name }}</div> <user-name-with-status :name="firstUser.name" :availability="userAvailability(firstUser)" />
<div>{{ username }}</div> <div>{{ username }}</div>
</div> </div>
</assignee-avatar-link> </assignee-avatar-link>
......
<script>
import { GlSprintf } from '@gitlab/ui';
import { isUserBusy } from '~/set_status_modal/utils';
export default {
name: 'UserNameWithStatus',
components: {
GlSprintf,
},
props: {
name: {
type: String,
required: true,
},
containerClasses: {
type: String,
required: false,
default: '',
},
availability: {
type: String,
required: false,
default: '',
},
},
computed: {
isBusy() {
return isUserBusy(this.availability);
},
},
};
</script>
<template>
<span :class="containerClasses">
<gl-sprintf v-if="isBusy" :message="s__('UserAvailability|%{author} (Busy)')">
<template #author>{{ name }}</template>
</gl-sprintf>
<template v-else>{{ name }}</template>
</span>
</template>
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlDeprecatedSkeletonLoading as GlSkeletonLoading,
GlIcon, GlIcon,
} from '@gitlab/ui'; } from '@gitlab/ui';
import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue'; import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import { glEmojiTag } from '../../../emoji'; import { glEmojiTag } from '../../../emoji';
import UserAvatarImage from '../user_avatar/user_avatar_image.vue'; import UserAvatarImage from '../user_avatar/user_avatar_image.vue';
...@@ -26,7 +26,7 @@ export default { ...@@ -26,7 +26,7 @@ export default {
GlPopover, GlPopover,
GlSkeletonLoading, GlSkeletonLoading,
UserAvatarImage, UserAvatarImage,
UserAvailabilityStatus, UserNameWithStatus,
}, },
props: { props: {
target: { target: {
...@@ -66,7 +66,7 @@ export default { ...@@ -66,7 +66,7 @@ export default {
); );
}, },
availabilityStatus() { availabilityStatus() {
return this.user?.status?.availability || null; return this.user?.status?.availability || '';
}, },
}, },
}; };
...@@ -93,11 +93,7 @@ export default { ...@@ -93,11 +93,7 @@ export default {
<template v-else> <template v-else>
<div class="gl-mb-3"> <div class="gl-mb-3">
<h5 class="gl-m-0"> <h5 class="gl-m-0">
{{ user.name }} <user-name-with-status :name="user.name" :availability="availabilityStatus" />
<user-availability-status
v-if="availabilityStatus"
:availability="availabilityStatus"
/>
</h5> </h5>
<span class="gl-text-gray-500">@{{ user.username }}</span> <span class="gl-text-gray-500">@{{ user.username }}</span>
</div> </div>
......
---
title: Display the user busy status in the MR sidebar
merge_request: 47769
author:
type: changed
...@@ -653,6 +653,12 @@ msgstr "" ...@@ -653,6 +653,12 @@ msgstr ""
msgid "%{name_with_link} has run out of Shared Runner Pipeline minutes so no new jobs or pipelines in its projects will run." msgid "%{name_with_link} has run out of Shared Runner Pipeline minutes so no new jobs or pipelines in its projects will run."
msgstr "" msgstr ""
msgid "%{name} %{status}"
msgstr ""
msgid "%{name} (Busy)"
msgstr ""
msgid "%{name} contained %{resultsString}" msgid "%{name} contained %{resultsString}"
msgstr "" msgstr ""
...@@ -5075,6 +5081,9 @@ msgstr "" ...@@ -5075,6 +5081,9 @@ msgstr ""
msgid "Business metrics (Custom)" msgid "Business metrics (Custom)"
msgstr "" msgstr ""
msgid "Busy"
msgstr ""
msgid "Buy License" msgid "Buy License"
msgstr "" msgstr ""
......
...@@ -4,6 +4,7 @@ import { nextTick } from 'vue'; ...@@ -4,6 +4,7 @@ import { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import NoteHeader from '~/notes/components/note_header.vue'; import NoteHeader from '~/notes/components/note_header.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/utils'; import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -36,9 +37,7 @@ describe('NoteHeader component', () => { ...@@ -36,9 +37,7 @@ describe('NoteHeader component', () => {
username: 'root', username: 'root',
show_status: true, show_status: true,
status_tooltip_html: statusHtml, status_tooltip_html: statusHtml,
status: { availability: '',
availability: '',
},
}; };
const createComponent = (props) => { const createComponent = (props) => {
...@@ -48,7 +47,7 @@ describe('NoteHeader component', () => { ...@@ -48,7 +47,7 @@ describe('NoteHeader component', () => {
actions, actions,
}), }),
propsData: { ...props }, propsData: { ...props },
stubs: { GlSprintf }, stubs: { GlSprintf, UserNameWithStatus },
}); });
}; };
...@@ -110,7 +109,7 @@ describe('NoteHeader component', () => { ...@@ -110,7 +109,7 @@ describe('NoteHeader component', () => {
}); });
it('renders busy status if author availability is set', () => { it('renders busy status if author availability is set', () => {
createComponent({ author: { ...author, status: { availability: AVAILABILITY_STATUS.BUSY } } }); createComponent({ author: { ...author, availability: AVAILABILITY_STATUS.BUSY } });
expect(wrapper.find('.js-user-link').text()).toContain('(Busy)'); expect(wrapper.find('.js-user-link').text()).toContain('(Busy)');
}); });
......
import { shallowMount } from '@vue/test-utils';
import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
describe('UserAvailabilityStatus', () => {
let wrapper;
const createComponent = (props = {}) => {
return shallowMount(UserAvailabilityStatus, {
propsData: {
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('with availability status', () => {
it(`set to ${AVAILABILITY_STATUS.BUSY}`, () => {
wrapper = createComponent({ availability: AVAILABILITY_STATUS.BUSY });
expect(wrapper.text()).toContain('(Busy)');
});
it(`set to ${AVAILABILITY_STATUS.NOT_SET}`, () => {
wrapper = createComponent({ availability: AVAILABILITY_STATUS.NOT_SET });
expect(wrapper.html()).toBe('');
});
});
});
import { AVAILABILITY_STATUS, isUserBusy } from '~/set_status_modal/utils';
describe('Set status modal utils', () => {
describe('isUserBusy', () => {
it.each`
value | result
${''} | ${false}
${'fake status'} | ${false}
${AVAILABILITY_STATUS.NOT_SET} | ${false}
${AVAILABILITY_STATUS.BUSY} | ${true}
`('with $value returns $result', ({ value, result }) => {
expect(isUserBusy(value)).toBe(result);
});
});
});
...@@ -79,4 +79,34 @@ describe('AssigneeAvatarLink component', () => { ...@@ -79,4 +79,34 @@ describe('AssigneeAvatarLink component', () => {
}); });
}, },
); );
describe.each`
tooltipHasName | availability | canMerge | expected
${true} | ${'Busy'} | ${false} | ${'Root (Busy) (cannot merge)'}
${true} | ${'Busy'} | ${true} | ${'Root (Busy)'}
${true} | ${''} | ${false} | ${'Root (cannot merge)'}
${true} | ${''} | ${true} | ${'Root'}
${false} | ${'Busy'} | ${false} | ${'Cannot merge'}
${false} | ${'Busy'} | ${true} | ${''}
${false} | ${''} | ${false} | ${'Cannot merge'}
${false} | ${''} | ${true} | ${''}
`(
"with tooltipHasName=$tooltipHasName and availability='$availability' and canMerge=$canMerge",
({ tooltipHasName, availability, canMerge, expected }) => {
beforeEach(() => {
createComponent({
tooltipHasName,
user: {
...userDataMock(),
can_merge: canMerge,
availability,
},
});
});
it('sets tooltip to $expected', () => {
expect(findTooltipText()).toBe(expected);
});
},
);
}); });
...@@ -187,4 +187,26 @@ describe('CollapsedAssigneeList component', () => { ...@@ -187,4 +187,26 @@ describe('CollapsedAssigneeList component', () => {
expect(findAvatarCounter().text()).toEqual(`${DEFAULT_MAX_COUNTER}+`); expect(findAvatarCounter().text()).toEqual(`${DEFAULT_MAX_COUNTER}+`);
}); });
}); });
const [busyUser] = UsersMockHelper.createNumberRandomUsers(1);
const [canMergeUser] = UsersMockHelper.createNumberRandomUsers(1);
busyUser.availability = 'busy';
canMergeUser.can_merge = true;
describe.each`
users | busy | canMerge | expected
${[busyUser, canMergeUser]} | ${1} | ${1} | ${`${busyUser.name} (Busy), ${canMergeUser.name} (1/2 can merge)`}
${[busyUser]} | ${1} | ${0} | ${`${busyUser.name} (Busy) (cannot merge)`}
${[canMergeUser]} | ${0} | ${1} | ${`${canMergeUser.name}`}
${[]} | ${0} | ${0} | ${'Assignee(s)'}
`(
'with $users.length users, $busy is busy and $canMerge that can merge',
({ users, expected }) => {
it('generates the tooltip text', () => {
createComponent({ users });
expect(getTooltipTitle()).toEqual(expected);
});
},
);
}); });
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue'; import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue';
import CollapsedAssignee from '~/sidebar/components/assignees/collapsed_assignee.vue'; import CollapsedAssignee from '~/sidebar/components/assignees/collapsed_assignee.vue';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import userDataMock from '../../user_data_mock'; import userDataMock from '../../user_data_mock';
const TEST_USER = userDataMock(); const TEST_USER = userDataMock();
...@@ -18,6 +19,9 @@ describe('CollapsedAssignee assignee component', () => { ...@@ -18,6 +19,9 @@ describe('CollapsedAssignee assignee component', () => {
wrapper = shallowMount(CollapsedAssignee, { wrapper = shallowMount(CollapsedAssignee, {
propsData, propsData,
stubs: {
UserNameWithStatus,
},
}); });
} }
......
import { GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
const name = 'Goku';
const containerClasses = 'gl-cool-class gl-over-9000';
describe('UserNameWithStatus', () => {
let wrapper;
function createComponent(props = {}) {
return shallowMount(UserNameWithStatus, {
propsData: { name, containerClasses, ...props },
stubs: {
GlSprintf,
},
});
}
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('will render the users name', () => {
expect(wrapper.html()).toContain(name);
});
it('will not render "Busy"', () => {
expect(wrapper.html()).not.toContain('Busy');
});
it('will render all relevant containerClasses', () => {
const classes = wrapper.find('span').classes().join(' ');
expect(classes).toBe(containerClasses);
});
describe(`with availability="${AVAILABILITY_STATUS.BUSY}"`, () => {
beforeEach(() => {
wrapper = createComponent({ availability: AVAILABILITY_STATUS.BUSY });
});
it('will render "Busy"', () => {
expect(wrapper.html()).toContain('Goku (Busy)');
});
});
});
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf, GlIcon } from '@gitlab/ui'; import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/utils'; import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue'; import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue';
const DEFAULT_PROPS = { const DEFAULT_PROPS = {
...@@ -36,7 +36,7 @@ describe('User Popover Component', () => { ...@@ -36,7 +36,7 @@ describe('User Popover Component', () => {
const findByTestId = (testid) => wrapper.find(`[data-testid="${testid}"]`); const findByTestId = (testid) => wrapper.find(`[data-testid="${testid}"]`);
const findUserStatus = () => wrapper.find('.js-user-status'); const findUserStatus = () => wrapper.find('.js-user-status');
const findTarget = () => document.querySelector('.js-user-link'); const findTarget = () => document.querySelector('.js-user-link');
const findAvailabilityStatus = () => wrapper.find(UserAvailabilityStatus); const findUserName = () => wrapper.find(UserNameWithStatus);
const createWrapper = (props = {}, options = {}) => { const createWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(UserPopover, { wrapper = shallowMount(UserPopover, {
...@@ -47,7 +47,7 @@ describe('User Popover Component', () => { ...@@ -47,7 +47,7 @@ describe('User Popover Component', () => {
}, },
stubs: { stubs: {
GlSprintf, GlSprintf,
UserAvailabilityStatus, UserNameWithStatus,
}, },
...options, ...options,
}); });
...@@ -213,7 +213,7 @@ describe('User Popover Component', () => { ...@@ -213,7 +213,7 @@ describe('User Popover Component', () => {
createWrapper({ user }); createWrapper({ user });
expect(findAvailabilityStatus().exists()).toBe(true); expect(findUserName().exists()).toBe(true);
expect(wrapper.text()).toContain(user.name); expect(wrapper.text()).toContain(user.name);
expect(wrapper.text()).toContain('(Busy)'); expect(wrapper.text()).toContain('(Busy)');
}); });
......
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