debian-mirror-gitlab/spec/frontend/boards/board_card_inner_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

617 lines
16 KiB
JavaScript
Raw Normal View History

2021-09-30 23:02:18 +05:30
import { GlLabel, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
2020-03-13 15:44:24 +05:30
import { range } from 'lodash';
2023-03-17 16:20:25 +05:30
import Vue, { nextTick } from 'vue';
2023-04-23 21:23:45 +05:30
import VueApollo from 'vue-apollo';
2021-04-29 21:17:54 +05:30
import Vuex from 'vuex';
2023-04-23 21:23:45 +05:30
import createMockApollo from 'helpers/mock_apollo_helper';
2022-07-16 23:28:13 +05:30
import setWindowLocation from 'helpers/set_window_location_helper';
2021-11-11 11:23:49 +05:30
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
2021-09-30 23:02:18 +05:30
import { mountExtended } from 'helpers/vue_test_utils_helper';
2022-11-25 23:54:43 +05:30
import IssuableBlockedIcon from '~/vue_shared/components/issuable_blocked_icon/issuable_blocked_icon.vue';
2021-04-17 20:07:23 +05:30
import BoardCardInner from '~/boards/components/board_card_inner.vue';
2022-10-11 01:57:18 +05:30
import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
2022-07-16 23:28:13 +05:30
import eventHub from '~/boards/eventhub';
2021-03-11 19:13:27 +05:30
import defaultStore from '~/boards/stores';
2023-04-23 21:23:45 +05:30
import { TYPE_ISSUE } from '~/issues/constants';
2022-07-16 23:28:13 +05:30
import { updateHistory } from '~/lib/utils/url_utility';
2021-10-27 15:23:28 +05:30
import { mockLabelList, mockIssue, mockIssueFullPath } from './mock_data';
2021-03-08 18:12:59 +05:30
jest.mock('~/lib/utils/url_utility');
jest.mock('~/boards/eventhub');
2019-12-26 22:10:19 +05:30
2023-03-17 16:20:25 +05:30
Vue.use(Vuex);
2023-04-23 21:23:45 +05:30
Vue.use(VueApollo);
2023-03-17 16:20:25 +05:30
2021-04-17 20:07:23 +05:30
describe('Board card component', () => {
2021-03-08 18:12:59 +05:30
const user = {
2019-12-26 22:10:19 +05:30
id: 1,
name: 'testing 123',
username: 'test',
2021-03-08 18:12:59 +05:30
avatarUrl: 'test_image',
};
2019-12-26 22:10:19 +05:30
2021-03-08 18:12:59 +05:30
const label1 = {
2019-12-26 22:10:19 +05:30
id: 3,
title: 'testing 123',
2020-04-08 14:13:33 +05:30
color: '#000CFF',
2021-03-08 18:12:59 +05:30
textColor: 'white',
2019-12-26 22:10:19 +05:30
description: 'test',
2021-03-08 18:12:59 +05:30
};
2019-12-26 22:10:19 +05:30
let wrapper;
let issue;
let list;
2021-04-29 21:17:54 +05:30
let store;
2022-11-25 23:54:43 +05:30
const findIssuableBlockedIcon = () => wrapper.findComponent(IssuableBlockedIcon);
2021-09-30 23:02:18 +05:30
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findEpicCountablesTotalTooltip = () => wrapper.findComponent(GlTooltip);
const findEpicCountables = () => wrapper.findByTestId('epic-countables');
const findEpicCountablesBadgeIssues = () => wrapper.findByTestId('epic-countables-counts-issues');
const findEpicCountablesBadgeWeight = () => wrapper.findByTestId('epic-countables-weight-issues');
const findEpicBadgeProgress = () => wrapper.findByTestId('epic-progress');
const findEpicCountablesTotalWeight = () => wrapper.findByTestId('epic-countables-total-weight');
const findEpicProgressTooltip = () => wrapper.findByTestId('epic-progress-tooltip-content');
2021-11-11 11:23:49 +05:30
const findHiddenIssueIcon = () => wrapper.findByTestId('hidden-icon');
2022-10-11 01:57:18 +05:30
const findWorkItemIcon = () => wrapper.findComponent(WorkItemTypeIcon);
2021-09-30 23:02:18 +05:30
2022-07-16 23:28:13 +05:30
const performSearchMock = jest.fn();
2023-03-17 16:20:25 +05:30
const createStore = () => {
2021-04-29 21:17:54 +05:30
store = new Vuex.Store({
2022-07-16 23:28:13 +05:30
actions: {
performSearch: performSearchMock,
},
2021-04-29 21:17:54 +05:30
state: {
...defaultStore.state,
2022-04-04 11:22:00 +05:30
isShowingLabels: true,
2021-04-29 21:17:54 +05:30
},
});
};
2023-03-17 16:20:25 +05:30
const createWrapper = ({ props = {}, isEpicBoard = false, isGroupBoard = true } = {}) => {
2021-09-30 23:02:18 +05:30
wrapper = mountExtended(BoardCardInner, {
2021-03-08 18:12:59 +05:30
store,
2023-04-23 21:23:45 +05:30
apolloProvider: createMockApollo(),
2019-12-26 22:10:19 +05:30
propsData: {
list,
2021-04-17 20:07:23 +05:30
item: issue,
2022-10-11 01:57:18 +05:30
index: 0,
2021-03-08 18:12:59 +05:30
...props,
2019-12-26 22:10:19 +05:30
},
2020-04-08 14:13:33 +05:30
stubs: {
2021-09-04 01:27:46 +05:30
GlLoadingIcon: true,
2022-10-11 01:57:18 +05:30
BoardCardMoveToPosition: true,
2020-04-08 14:13:33 +05:30
},
2021-11-11 11:23:49 +05:30
directives: {
2023-05-27 22:25:52 +05:30
GlTooltip: createMockDirective('gl-tooltip'),
2021-11-11 11:23:49 +05:30
},
2020-11-24 15:15:51 +05:30
provide: {
rootPath: '/',
2021-03-08 18:12:59 +05:30
scopedLabelsAvailable: false,
2023-01-13 00:05:48 +05:30
isEpicBoard,
2023-04-23 21:23:45 +05:30
issuableType: TYPE_ISSUE,
2023-03-17 16:20:25 +05:30
isGroupBoard,
2020-11-24 15:15:51 +05:30
},
2019-12-26 22:10:19 +05:30
});
2021-03-08 18:12:59 +05:30
};
beforeEach(() => {
list = mockLabelList;
issue = {
2021-04-29 21:17:54 +05:30
...mockIssue,
2021-03-08 18:12:59 +05:30
labels: [list.label],
assignees: [],
weight: 1,
};
2021-09-30 23:02:18 +05:30
createStore();
2023-01-13 00:05:48 +05:30
createWrapper({ props: { item: issue, list } });
2021-03-08 18:12:59 +05:30
});
afterEach(() => {
2021-04-29 21:17:54 +05:30
store = null;
2021-03-08 18:12:59 +05:30
jest.clearAllMocks();
2019-12-26 22:10:19 +05:30
});
it('renders issue title', () => {
expect(wrapper.find('.board-card-title').text()).toContain(issue.title);
});
it('includes issue base in link', () => {
expect(wrapper.find('.board-card-title a').attributes('href')).toContain('/test');
});
it('includes issue title on link', () => {
expect(wrapper.find('.board-card-title a').attributes('title')).toBe(issue.title);
});
it('does not render confidential icon', () => {
2020-03-13 15:44:24 +05:30
expect(wrapper.find('.confidential-icon').exists()).toBe(false);
});
2021-11-11 11:23:49 +05:30
it('does not render hidden issue icon', () => {
expect(findHiddenIssueIcon().exists()).toBe(false);
});
2022-10-11 01:57:18 +05:30
it('does not render the work type icon by default', () => {
expect(findWorkItemIcon().exists()).toBe(false);
});
it('renders the work type icon when props is passed', () => {
2023-01-13 00:05:48 +05:30
createWrapper({ props: { item: issue, list, showWorkItemTypeIcon: true } });
2022-10-11 01:57:18 +05:30
expect(findWorkItemIcon().exists()).toBe(true);
expect(findWorkItemIcon().props('workItemType')).toBe(issue.type);
});
2019-12-26 22:10:19 +05:30
it('renders issue ID with #', () => {
2021-04-29 21:17:54 +05:30
expect(wrapper.find('.board-card-number').text()).toContain(`#${issue.iid}`);
2019-12-26 22:10:19 +05:30
});
2021-03-08 18:12:59 +05:30
it('does not render assignee', () => {
expect(wrapper.find('.board-card-assignee .avatar').exists()).toBe(false);
});
2021-09-04 01:27:46 +05:30
it('does not render loading icon', () => {
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
});
2021-10-27 15:23:28 +05:30
it('does not render item reference path', () => {
2023-03-17 16:20:25 +05:30
createStore();
createWrapper({ isGroupBoard: false });
2021-10-27 15:23:28 +05:30
expect(wrapper.find('.board-card-number').text()).not.toContain(mockIssueFullPath);
});
it('renders item reference path', () => {
expect(wrapper.find('.board-card-number').text()).toContain(mockIssueFullPath);
});
2021-04-29 21:17:54 +05:30
describe('blocked', () => {
2023-06-20 00:43:36 +05:30
it('renders blocked icon if issue is blocked', () => {
2021-04-29 21:17:54 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
blocked: true,
},
2021-04-29 21:17:54 +05:30
},
});
2022-11-25 23:54:43 +05:30
expect(findIssuableBlockedIcon().exists()).toBe(true);
2021-04-29 21:17:54 +05:30
});
it('does not show blocked icon if issue is not blocked', () => {
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
blocked: false,
},
2021-04-29 21:17:54 +05:30
},
});
2022-11-25 23:54:43 +05:30
expect(findIssuableBlockedIcon().exists()).toBe(false);
2021-04-29 21:17:54 +05:30
});
});
2021-03-08 18:12:59 +05:30
describe('confidential issue', () => {
beforeEach(() => {
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
confidential: true,
},
2021-03-08 18:12:59 +05:30
},
});
2019-12-26 22:10:19 +05:30
});
2021-03-08 18:12:59 +05:30
it('renders confidential icon', () => {
expect(wrapper.find('.confidential-icon').exists()).toBe(true);
});
});
2021-11-11 11:23:49 +05:30
describe('hidden issue', () => {
beforeEach(() => {
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
hidden: true,
},
2021-11-11 11:23:49 +05:30
},
});
});
it('renders hidden issue icon', () => {
expect(findHiddenIssueIcon().exists()).toBe(true);
});
it('displays a tooltip which explains the meaning of the icon', () => {
const tooltip = getBinding(findHiddenIssueIcon().element, 'gl-tooltip');
expect(tooltip).toBeDefined();
expect(findHiddenIssueIcon().attributes('title')).toBe(
'This issue is hidden because its author has been banned',
);
});
});
2021-03-08 18:12:59 +05:30
describe('with assignee', () => {
describe('with avatar', () => {
beforeEach(() => {
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
assignees: [user],
updateData(newData) {
Object.assign(this, newData);
},
2020-03-13 15:44:24 +05:30
},
2019-12-26 22:10:19 +05:30
},
});
});
it('renders assignee', () => {
2022-08-27 11:52:29 +05:30
expect(wrapper.find('.board-card-assignee .gl-avatar').exists()).toBe(true);
2019-12-26 22:10:19 +05:30
});
it('sets title', () => {
expect(wrapper.find('.js-assignee-tooltip').text()).toContain(`${user.name}`);
});
it('sets users path', () => {
expect(wrapper.find('.board-card-assignee a').attributes('href')).toBe('/test');
});
it('renders avatar', () => {
expect(wrapper.find('.board-card-assignee img').exists()).toBe(true);
});
2020-03-13 15:44:24 +05:30
2021-03-08 18:12:59 +05:30
it('renders the avatar using avatarUrl property', async () => {
2021-04-17 20:07:23 +05:30
wrapper.props('item').updateData({
...wrapper.props('item'),
2020-03-13 15:44:24 +05:30
assignees: [
{
id: '1',
name: 'test',
state: 'active',
username: 'test_name',
2021-03-08 18:12:59 +05:30
avatarUrl: 'test_image_from_avatar_url',
2020-03-13 15:44:24 +05:30
},
],
});
2022-04-04 11:22:00 +05:30
await nextTick();
2021-03-08 18:12:59 +05:30
expect(wrapper.find('.board-card-assignee img').attributes('src')).toBe(
'test_image_from_avatar_url?width=24',
);
2020-03-13 15:44:24 +05:30
});
2019-12-26 22:10:19 +05:30
});
2021-03-08 18:12:59 +05:30
describe('with default avatar', () => {
beforeEach(() => {
2020-04-08 14:13:33 +05:30
global.gon.default_avatar_url = 'default_avatar';
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
assignees: [
{
id: 1,
name: 'testing 123',
username: 'test',
},
],
},
2019-12-26 22:10:19 +05:30
},
});
});
it('displays defaults avatar if users avatar is null', () => {
expect(wrapper.find('.board-card-assignee img').exists()).toBe(true);
expect(wrapper.find('.board-card-assignee img').attributes('src')).toBe(
'default_avatar?width=24',
);
});
});
});
describe('multiple assignees', () => {
2021-03-08 18:12:59 +05:30
beforeEach(() => {
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
assignees: [
{
id: 2,
name: 'user2',
username: 'user2',
avatarUrl: 'test_image',
},
{
id: 3,
name: 'user3',
username: 'user3',
avatarUrl: 'test_image',
},
{
id: 4,
name: 'user4',
username: 'user4',
avatarUrl: 'test_image',
},
],
},
2019-12-26 22:10:19 +05:30
},
});
});
it('renders all three assignees', () => {
2022-08-27 11:52:29 +05:30
expect(wrapper.findAll('.board-card-assignee .gl-avatar').length).toEqual(3);
2019-12-26 22:10:19 +05:30
});
describe('more than three assignees', () => {
2021-03-08 18:12:59 +05:30
beforeEach(() => {
2021-04-17 20:07:23 +05:30
const { assignees } = wrapper.props('item');
2021-03-08 18:12:59 +05:30
assignees.push({
id: 5,
name: 'user5',
username: 'user5',
avatarUrl: 'test_image',
});
2019-12-26 22:10:19 +05:30
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
assignees,
},
2019-12-26 22:10:19 +05:30
},
});
});
it('renders more avatar counter', () => {
2021-03-08 18:12:59 +05:30
expect(wrapper.find('.board-card-assignee .avatar-counter').text().trim()).toEqual('+2');
2019-12-26 22:10:19 +05:30
});
it('renders two assignees', () => {
2022-08-27 11:52:29 +05:30
expect(wrapper.findAll('.board-card-assignee .gl-avatar').length).toEqual(2);
2019-12-26 22:10:19 +05:30
});
2021-03-08 18:12:59 +05:30
it('renders 99+ avatar counter', async () => {
2019-12-26 22:10:19 +05:30
const assignees = [
2021-04-17 20:07:23 +05:30
...wrapper.props('item').assignees,
2021-03-08 18:12:59 +05:30
...range(5, 103).map((i) => ({
id: i,
name: 'name',
username: 'username',
avatarUrl: 'test_image',
})),
2019-12-26 22:10:19 +05:30
];
2022-07-16 23:28:13 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...wrapper.props('item'),
assignees,
},
2019-12-26 22:10:19 +05:30
},
});
2022-04-04 11:22:00 +05:30
await nextTick();
2021-03-08 18:12:59 +05:30
expect(wrapper.find('.board-card-assignee .avatar-counter').text().trim()).toEqual('99+');
2019-12-26 22:10:19 +05:30
});
});
});
describe('labels', () => {
2021-03-08 18:12:59 +05:30
beforeEach(() => {
2023-01-13 00:05:48 +05:30
createWrapper({ props: { item: { ...issue, labels: [list.label, label1] } } });
2019-12-26 22:10:19 +05:30
});
it('does not render list label but renders all other labels', () => {
2022-07-16 23:28:13 +05:30
expect(wrapper.findAllComponents(GlLabel).length).toBe(1);
const label = wrapper.findComponent(GlLabel);
2020-04-08 14:13:33 +05:30
expect(label.props('title')).toEqual(label1.title);
expect(label.props('description')).toEqual(label1.description);
expect(label.props('backgroundColor')).toEqual(label1.color);
2019-12-26 22:10:19 +05:30
});
2021-03-08 18:12:59 +05:30
it('does not render label if label does not have an ID', async () => {
2023-01-13 00:05:48 +05:30
createWrapper({ props: { item: { ...issue, labels: [label1, { title: 'closed' }] } } });
2021-03-08 18:12:59 +05:30
2022-04-04 11:22:00 +05:30
await nextTick();
2021-03-08 18:12:59 +05:30
2022-07-16 23:28:13 +05:30
expect(wrapper.findAllComponents(GlLabel).length).toBe(1);
2021-03-08 18:12:59 +05:30
expect(wrapper.text()).not.toContain('closed');
2019-12-26 22:10:19 +05:30
});
2022-07-16 23:28:13 +05:30
});
2021-03-08 18:12:59 +05:30
2022-07-16 23:28:13 +05:30
describe('filterByLabel method', () => {
beforeEach(() => {
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
labels: [label1],
},
updateFilters: true,
2022-07-16 23:28:13 +05:30
},
});
});
describe('when selected label is not in the filter', () => {
beforeEach(() => {
setWindowLocation('?');
wrapper.findComponent(GlLabel).vm.$emit('click', label1);
});
it('calls updateHistory', () => {
expect(updateHistory).toHaveBeenCalledTimes(1);
});
it('dispatches performSearch vuex action', () => {
expect(performSearchMock).toHaveBeenCalledTimes(1);
});
it('emits updateTokens event', () => {
expect(eventHub.$emit).toHaveBeenCalledTimes(1);
expect(eventHub.$emit).toHaveBeenCalledWith('updateTokens');
});
});
describe('when selected label is already in the filter', () => {
beforeEach(() => {
setWindowLocation('?label_name[]=testing%20123');
wrapper.findComponent(GlLabel).vm.$emit('click', label1);
});
it('does not call updateHistory', () => {
expect(updateHistory).not.toHaveBeenCalled();
});
it('does not dispatch performSearch vuex action', () => {
expect(performSearchMock).not.toHaveBeenCalled();
});
it('does not emit updateTokens event', () => {
expect(eventHub.$emit).not.toHaveBeenCalled();
2021-03-08 18:12:59 +05:30
});
});
});
2021-09-04 01:27:46 +05:30
describe('loading', () => {
2023-06-20 00:43:36 +05:30
it('renders loading icon', () => {
2021-09-04 01:27:46 +05:30
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
isLoading: true,
},
2021-09-04 01:27:46 +05:30
},
});
2021-09-30 23:02:18 +05:30
expect(findLoadingIcon().exists()).toBe(true);
});
});
describe('is an epic board', () => {
const descendantCounts = {
closedEpics: 0,
closedIssues: 0,
openedEpics: 0,
openedIssues: 0,
};
const descendantWeightSum = {
closedIssues: 0,
openedIssues: 0,
};
beforeEach(() => {
2023-01-13 00:05:48 +05:30
createStore();
2021-09-30 23:02:18 +05:30
});
it('should render if the item has issues', () => {
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
descendantCounts,
descendantWeightSum,
hasIssues: true,
},
2021-09-30 23:02:18 +05:30
},
2023-01-13 00:05:48 +05:30
isEpicBoard: true,
2021-09-30 23:02:18 +05:30
});
expect(findEpicCountables().exists()).toBe(true);
});
it('should not render if the item does not have issues', () => {
createWrapper({
item: {
...issue,
descendantCounts,
descendantWeightSum,
hasIssues: false,
},
});
expect(findEpicCountablesBadgeIssues().exists()).toBe(false);
});
it('shows render item countBadge, weights, and progress correctly', () => {
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
descendantCounts: {
...descendantCounts,
openedIssues: 1,
},
descendantWeightSum: {
closedIssues: 10,
openedIssues: 5,
},
hasIssues: true,
2021-09-30 23:02:18 +05:30
},
},
2023-01-13 00:05:48 +05:30
isEpicBoard: true,
2021-09-30 23:02:18 +05:30
});
expect(findEpicCountablesBadgeIssues().text()).toBe('1');
expect(findEpicCountablesBadgeWeight().text()).toBe('15');
expect(findEpicBadgeProgress().text()).toBe('67%');
});
it('does not render progress when weight is zero', () => {
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
descendantCounts: {
...descendantCounts,
openedIssues: 1,
},
descendantWeightSum,
hasIssues: true,
2021-09-30 23:02:18 +05:30
},
},
2023-01-13 00:05:48 +05:30
isEpicBoard: true,
2021-09-30 23:02:18 +05:30
});
expect(findEpicBadgeProgress().exists()).toBe(false);
});
it('renders the tooltip with the correct data', () => {
createWrapper({
2023-01-13 00:05:48 +05:30
props: {
item: {
...issue,
descendantCounts,
descendantWeightSum: {
closedIssues: 10,
openedIssues: 5,
},
hasIssues: true,
2021-09-30 23:02:18 +05:30
},
},
2023-01-13 00:05:48 +05:30
isEpicBoard: true,
2021-09-30 23:02:18 +05:30
});
const tooltip = findEpicCountablesTotalTooltip();
expect(tooltip).toBeDefined();
expect(findEpicCountablesTotalWeight().text()).toBe('15');
expect(findEpicProgressTooltip().text()).toBe('10 of 15 weight completed');
2021-09-04 01:27:46 +05:30
});
});
2019-12-26 22:10:19 +05:30
});