import { mount } from '@vue/test-utils'; import { nextTick } from 'vue'; import { TEST_HOST } from 'helpers/test_constants'; import IssueDueDate from '~/boards/components/issue_due_date.vue'; import { formatDate } from '~/lib/utils/datetime_utility'; import RelatedIssuableItem from '~/issuable/components/related_issuable_item.vue'; import { defaultAssignees, defaultMilestone } from './related_issuable_mock_data'; describe('RelatedIssuableItem', () => { let wrapper; function mountComponent({ mountMethod = mount, stubs = {}, props = {}, slots = {} } = {}) { wrapper = mountMethod(RelatedIssuableItem, { propsData: props, slots, stubs, }); } const props = { idKey: 1, displayReference: 'gitlab-org/gitlab-test#1', pathIdSeparator: '#', path: `${TEST_HOST}/path`, title: 'title', confidential: true, dueDate: '1990-12-31', weight: 10, createdAt: '2018-12-01T00:00:00.00Z', milestone: defaultMilestone, assignees: defaultAssignees, eventNamespace: 'relatedIssue', }; const slots = { dueDate: '
', weight: '', }; const findRemoveButton = () => wrapper.find({ ref: 'removeButton' }); const findLockIcon = () => wrapper.find({ ref: 'lockIcon' }); beforeEach(() => { mountComponent({ props, slots }); }); afterEach(() => { wrapper.destroy(); }); it('contains issuable-info-container class when canReorder is false', () => { expect(wrapper.props('canReorder')).toBe(false); expect(wrapper.find('.issuable-info-container').exists()).toBe(true); }); it('does not render token state', () => { expect(wrapper.find('.text-secondary svg').exists()).toBe(false); }); it('does not render remove button', () => { expect(wrapper.find({ ref: 'removeButton' }).exists()).toBe(false); }); describe('token title', () => { it('links to computedPath', () => { expect(wrapper.find('.item-title a').attributes('href')).toEqual(wrapper.props('path')); }); it('renders confidential icon', () => { expect(wrapper.find('.confidential-icon').exists()).toBe(true); }); it('renders title', () => { expect(wrapper.find('.item-title a').text()).toEqual(props.title); }); }); describe('token state', () => { const tokenState = () => wrapper.find({ ref: 'iconElementXL' }); beforeEach(() => { wrapper.setProps({ state: 'opened' }); }); it('renders if hasState', () => { expect(tokenState().exists()).toBe(true); }); it('renders state title', () => { const stateTitle = tokenState().attributes('title'); const formattedCreateDate = formatDate(props.createdAt); expect(stateTitle).toContain('Created'); expect(stateTitle).toContain(`${formattedCreateDate}`); }); it('renders aria label', () => { expect(tokenState().attributes('aria-label')).toEqual('opened'); }); it('renders open icon when open state', () => { expect(tokenState().classes('issue-token-state-icon-open')).toBe(true); }); it('renders close icon when close state', async () => { wrapper.setProps({ state: 'closed', closedAt: '2018-12-01T00:00:00.00Z', }); await nextTick(); expect(tokenState().classes('issue-token-state-icon-closed')).toBe(true); }); }); describe('token metadata', () => { const tokenMetadata = () => wrapper.find('.item-meta'); it('renders item path and ID', () => { const pathAndID = tokenMetadata().find('.item-path-id').text(); expect(pathAndID).toContain('gitlab-org/gitlab-test'); expect(pathAndID).toContain('#1'); }); it('renders milestone icon and name', () => { const milestoneIcon = tokenMetadata().find('.item-milestone svg'); const milestoneTitle = tokenMetadata().find('.item-milestone .milestone-title'); expect(milestoneIcon.attributes('data-testid')).toBe('clock-icon'); expect(milestoneTitle.text()).toContain('Milestone title'); }); it('renders due date component with correct due date', () => { expect(wrapper.find(IssueDueDate).props('date')).toBe(props.dueDate); }); it('does not render red icon for overdue issue that is closed', async () => { mountComponent({ props: { ...props, closedAt: '2018-12-01T00:00:00.00Z', }, }); await nextTick(); expect(wrapper.find(IssueDueDate).props('closed')).toBe(true); }); }); describe('token assignees', () => { it('renders assignees avatars', () => { // Expect 2 times 2 because assignees are rendered twice, due to layout issues expect(wrapper.findAll('.item-assignees .user-avatar-link').length).toBeDefined(); expect(wrapper.find('.item-assignees .avatar-counter').text()).toContain('+2'); }); }); describe('remove button', () => { beforeEach(() => { wrapper.setProps({ canRemove: true }); }); it('renders if canRemove', () => { expect(findRemoveButton().exists()).toBe(true); }); it('does not render the lock icon', () => { expect(findLockIcon().exists()).toBe(false); }); it('renders disabled button when removeDisabled', async () => { // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details // eslint-disable-next-line no-restricted-syntax wrapper.setData({ removeDisabled: true }); await nextTick(); expect(findRemoveButton().attributes('disabled')).toEqual('disabled'); }); it('triggers onRemoveRequest when clicked', async () => { findRemoveButton().trigger('click'); await nextTick(); const { relatedIssueRemoveRequest } = wrapper.emitted(); expect(relatedIssueRemoveRequest.length).toBe(1); expect(relatedIssueRemoveRequest[0]).toEqual([props.idKey]); }); }); describe('when issue is locked', () => { const lockedMessage = 'Issues created from a vulnerability cannot be removed'; beforeEach(() => { wrapper.setProps({ isLocked: true, lockedMessage, }); }); it('does not render the remove button', () => { expect(findRemoveButton().exists()).toBe(false); }); it('renders the lock icon with the correct title', () => { expect(findLockIcon().attributes('title')).toBe(lockedMessage); }); }); });