Commit 5c557194 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Martin Wortschack

Add user busy status to sidebar

Add the user busy status to the note header

Adds the busy status indicator to the sidebar
assignees dropdown and the note header in
dicusssions

Minor cleanup specs
parent 72b6be71
<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
import { shallowMount } from '@vue/test-utils';
import { GlModal, GlFormCheckbox } from '@gitlab/ui'; import { GlModal, GlFormCheckbox } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import ExportRequirementsModal from 'ee/requirements/components/export_requirements_modal.vue'; import ExportRequirementsModal from 'ee/requirements/components/export_requirements_modal.vue';
......
...@@ -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