diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /spec/frontend/issuable | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'spec/frontend/issuable')
11 files changed, 864 insertions, 4 deletions
diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/status_select_spec.js b/spec/frontend/issuable/bulk_update_sidebar/components/status_select_spec.js new file mode 100644 index 00000000000..8ecbf41ce56 --- /dev/null +++ b/spec/frontend/issuable/bulk_update_sidebar/components/status_select_spec.js @@ -0,0 +1,77 @@ +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import StatusSelect from '~/issuable/bulk_update_sidebar/components/status_select.vue'; +import { ISSUE_STATUS_SELECT_OPTIONS } from '~/issuable/bulk_update_sidebar/constants'; + +describe('StatusSelect', () => { + let wrapper; + + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findHiddenInput = () => wrapper.find('input'); + + function createComponent() { + wrapper = shallowMount(StatusSelect); + } + + afterEach(() => { + wrapper.destroy(); + }); + + describe('with no value selected', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders default text', () => { + expect(findDropdown().props('text')).toBe('Select status'); + }); + + it('renders dropdown items with `is-checked` prop set to `false`', () => { + const dropdownItems = findAllDropdownItems(); + + expect(dropdownItems.at(0).props('isChecked')).toBe(false); + expect(dropdownItems.at(1).props('isChecked')).toBe(false); + }); + }); + + describe('when selecting a value', () => { + const selectItemAtIndex = 0; + + beforeEach(async () => { + createComponent(); + await findAllDropdownItems().at(selectItemAtIndex).vm.$emit('click'); + }); + + it('updates value of the hidden input', () => { + expect(findHiddenInput().attributes('value')).toBe( + ISSUE_STATUS_SELECT_OPTIONS[selectItemAtIndex].value, + ); + }); + + it('updates the dropdown text prop', () => { + expect(findDropdown().props('text')).toBe( + ISSUE_STATUS_SELECT_OPTIONS[selectItemAtIndex].text, + ); + }); + + it('sets dropdown item `is-checked` prop to `true`', () => { + const dropdownItems = findAllDropdownItems(); + + expect(dropdownItems.at(0).props('isChecked')).toBe(true); + expect(dropdownItems.at(1).props('isChecked')).toBe(false); + }); + + describe('when selecting the value that is already selected', () => { + it('clears dropdown selection', async () => { + await findAllDropdownItems().at(selectItemAtIndex).vm.$emit('click'); + + const dropdownItems = findAllDropdownItems(); + + expect(dropdownItems.at(0).props('isChecked')).toBe(false); + expect(dropdownItems.at(1).props('isChecked')).toBe(false); + expect(findDropdown().props('text')).toBe('Select status'); + }); + }); + }); +}); diff --git a/spec/frontend/issuable/components/issuable_header_warnings_spec.js b/spec/frontend/issuable/components/issuable_header_warnings_spec.js new file mode 100644 index 00000000000..c8380e42787 --- /dev/null +++ b/spec/frontend/issuable/components/issuable_header_warnings_spec.js @@ -0,0 +1,87 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { createStore as createMrStore } from '~/mr_notes/stores'; +import createIssueStore from '~/notes/stores'; +import IssuableHeaderWarnings from '~/issuable/components/issuable_header_warnings.vue'; + +const ISSUABLE_TYPE_ISSUE = 'issue'; +const ISSUABLE_TYPE_MR = 'merge request'; + +Vue.use(Vuex); + +describe('IssuableHeaderWarnings', () => { + let wrapper; + + const findConfidentialIcon = () => wrapper.findByTestId('confidential'); + const findLockedIcon = () => wrapper.findByTestId('locked'); + const findHiddenIcon = () => wrapper.findByTestId('hidden'); + + const renderTestMessage = (renders) => (renders ? 'renders' : 'does not render'); + + const createComponent = ({ store, provide }) => { + wrapper = shallowMountExtended(IssuableHeaderWarnings, { + store, + provide, + directives: { + GlTooltip: createMockDirective(), + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe.each` + issuableType + ${ISSUABLE_TYPE_ISSUE} | ${ISSUABLE_TYPE_MR} + `(`when issuableType=$issuableType`, ({ issuableType }) => { + describe.each` + lockStatus | confidentialStatus | hiddenStatus + ${true} | ${true} | ${false} + ${true} | ${false} | ${false} + ${false} | ${true} | ${false} + ${false} | ${false} | ${false} + ${true} | ${true} | ${true} + ${true} | ${false} | ${true} + ${false} | ${true} | ${true} + ${false} | ${false} | ${true} + `( + `when locked=$lockStatus, confidential=$confidentialStatus, and hidden=$hiddenStatus`, + ({ lockStatus, confidentialStatus, hiddenStatus }) => { + const store = issuableType === ISSUABLE_TYPE_ISSUE ? createIssueStore() : createMrStore(); + + beforeEach(() => { + store.getters.getNoteableData.confidential = confidentialStatus; + store.getters.getNoteableData.discussion_locked = lockStatus; + + createComponent({ store, provide: { hidden: hiddenStatus } }); + }); + + it(`${renderTestMessage(lockStatus)} the locked icon`, () => { + expect(findLockedIcon().exists()).toBe(lockStatus); + }); + + it(`${renderTestMessage(confidentialStatus)} the confidential icon`, () => { + expect(findConfidentialIcon().exists()).toBe(confidentialStatus); + }); + + it(`${renderTestMessage(confidentialStatus)} the hidden icon`, () => { + const hiddenIcon = findHiddenIcon(); + + expect(hiddenIcon.exists()).toBe(hiddenStatus); + + if (hiddenStatus) { + expect(hiddenIcon.attributes('title')).toBe( + 'This issue is hidden because its author has been banned', + ); + expect(getBinding(hiddenIcon.element, 'gl-tooltip')).not.toBeUndefined(); + } + }); + }, + ); + }); +}); diff --git a/spec/frontend/issuable/components/issue_assignees_spec.js b/spec/frontend/issuable/components/issue_assignees_spec.js new file mode 100644 index 00000000000..713c8b1dfdd --- /dev/null +++ b/spec/frontend/issuable/components/issue_assignees_spec.js @@ -0,0 +1,145 @@ +import { shallowMount } from '@vue/test-utils'; +import { mockAssigneesList } from 'jest/boards/mock_data'; +import IssueAssignees from '~/issuable/components/issue_assignees.vue'; +import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; + +const TEST_CSS_CLASSES = 'test-classes'; +const TEST_MAX_VISIBLE = 4; +const TEST_ICON_SIZE = 16; + +describe('IssueAssigneesComponent', () => { + let wrapper; + let vm; + + const factory = (props) => { + wrapper = shallowMount(IssueAssignees, { + propsData: { + assignees: mockAssigneesList, + ...props, + }, + }); + vm = wrapper.vm; + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findTooltipText = () => wrapper.find('.js-assignee-tooltip').text(); + const findAvatars = () => wrapper.findAll(UserAvatarLink); + const findOverflowCounter = () => wrapper.find('.avatar-counter'); + + it('returns default data props', () => { + factory({ assignees: mockAssigneesList }); + expect(vm.iconSize).toBe(24); + expect(vm.maxVisible).toBe(3); + expect(vm.maxAssignees).toBe(99); + }); + + describe.each` + numAssignees | maxVisible | expectedShown | expectedHidden + ${0} | ${3} | ${0} | ${''} + ${1} | ${3} | ${1} | ${''} + ${2} | ${3} | ${2} | ${''} + ${3} | ${3} | ${3} | ${''} + ${4} | ${3} | ${2} | ${'+2'} + ${5} | ${2} | ${1} | ${'+4'} + ${1000} | ${5} | ${4} | ${'99+'} + `( + 'with assignees ($numAssignees) and maxVisible ($maxVisible)', + ({ numAssignees, maxVisible, expectedShown, expectedHidden }) => { + beforeEach(() => { + factory({ assignees: Array(numAssignees).fill({}), maxVisible }); + }); + + if (expectedShown) { + it('shows assignee avatars', () => { + expect(findAvatars().length).toEqual(expectedShown); + }); + } else { + it('does not show assignee avatars', () => { + expect(findAvatars().length).toEqual(0); + }); + } + + if (expectedHidden) { + it('shows overflow counter', () => { + const hiddenCount = numAssignees - expectedShown; + + expect(findOverflowCounter().exists()).toBe(true); + expect(findOverflowCounter().text()).toEqual(expectedHidden.toString()); + expect(findOverflowCounter().attributes('title')).toEqual( + `${hiddenCount} more assignees`, + ); + }); + } else { + it('does not show overflow counter', () => { + expect(findOverflowCounter().exists()).toBe(false); + }); + } + }, + ); + + describe('when mounted', () => { + beforeEach(() => { + factory({ + imgCssClasses: TEST_CSS_CLASSES, + maxVisible: TEST_MAX_VISIBLE, + iconSize: TEST_ICON_SIZE, + }); + }); + + it('computes alt text for assignee avatar', () => { + expect(vm.avatarUrlTitle(mockAssigneesList[0])).toBe('Assigned to Terrell Graham'); + }); + + it('renders assignee', () => { + const data = findAvatars().wrappers.map((x) => ({ + ...x.props(), + })); + + const expected = mockAssigneesList.slice(0, TEST_MAX_VISIBLE - 1).map((x) => + expect.objectContaining({ + linkHref: x.web_url, + imgAlt: `Assigned to ${x.name}`, + imgCssClasses: TEST_CSS_CLASSES, + imgSrc: x.avatar_url, + imgSize: TEST_ICON_SIZE, + }), + ); + + expect(data).toEqual(expected); + }); + + describe('assignee tooltips', () => { + it('renders "Assignee" header', () => { + expect(findTooltipText()).toContain('Assignee'); + }); + + it('renders assignee name', () => { + expect(findTooltipText()).toContain('Terrell Graham'); + }); + + it('renders assignee @username', () => { + expect(findTooltipText()).toContain('@monserrate.gleichner'); + }); + + it('does not render `@` when username not available', () => { + const userName = 'User without username'; + factory({ + assignees: [ + { + name: userName, + }, + ], + }); + + const tooltipText = findTooltipText(); + + expect(tooltipText).toContain(userName); + expect(tooltipText).not.toContain('@'); + }); + }); + }); +}); diff --git a/spec/frontend/issuable/components/issue_milestone_spec.js b/spec/frontend/issuable/components/issue_milestone_spec.js new file mode 100644 index 00000000000..44416676180 --- /dev/null +++ b/spec/frontend/issuable/components/issue_milestone_spec.js @@ -0,0 +1,160 @@ +import { GlIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; + +import { mockMilestone } from 'jest/boards/mock_data'; +import IssueMilestone from '~/issuable/components/issue_milestone.vue'; + +const createComponent = (milestone = mockMilestone) => { + const Component = Vue.extend(IssueMilestone); + + return shallowMount(Component, { + propsData: { + milestone, + }, + }); +}; + +describe('IssueMilestoneComponent', () => { + let wrapper; + let vm; + + beforeEach((done) => { + wrapper = createComponent(); + + ({ vm } = wrapper); + + Vue.nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('computed', () => { + describe('isMilestoneStarted', () => { + it('should return `false` when milestoneStart prop is not defined', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, start_date: '' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isMilestoneStarted).toBe(false); + }); + + it('should return `true` when milestone start date is past current date', async () => { + await wrapper.setProps({ + milestone: { ...mockMilestone, start_date: '1990-07-22' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isMilestoneStarted).toBe(true); + }); + }); + + describe('isMilestonePastDue', () => { + it('should return `false` when milestoneDue prop is not defined', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, due_date: '' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isMilestonePastDue).toBe(false); + }); + + it('should return `true` when milestone due is past current date', () => { + wrapper.setProps({ + milestone: { ...mockMilestone, due_date: '1990-07-22' }, + }); + + expect(wrapper.vm.isMilestonePastDue).toBe(true); + }); + }); + + describe('milestoneDatesAbsolute', () => { + it('returns string containing absolute milestone due date', () => { + expect(vm.milestoneDatesAbsolute).toBe('(December 31, 2019)'); + }); + + it('returns string containing absolute milestone start date when due date is not present', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, due_date: '' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.milestoneDatesAbsolute).toBe('(January 1, 2018)'); + }); + + it('returns empty string when both milestone start and due dates are not present', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, start_date: '', due_date: '' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.milestoneDatesAbsolute).toBe(''); + }); + }); + + describe('milestoneDatesHuman', () => { + it('returns string containing milestone due date when date is yet to be due', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, due_date: `${new Date().getFullYear() + 10}-01-01` }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.milestoneDatesHuman).toContain('years remaining'); + }); + + it('returns string containing milestone start date when date has already started and due date is not present', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, start_date: '1990-07-22', due_date: '' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.milestoneDatesHuman).toContain('Started'); + }); + + it('returns string containing milestone start date when date is yet to start and due date is not present', async () => { + wrapper.setProps({ + milestone: { + ...mockMilestone, + start_date: `${new Date().getFullYear() + 10}-01-01`, + due_date: '', + }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.milestoneDatesHuman).toContain('Starts'); + }); + + it('returns empty string when milestone start and due dates are not present', async () => { + wrapper.setProps({ + milestone: { ...mockMilestone, start_date: '', due_date: '' }, + }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.milestoneDatesHuman).toBe(''); + }); + }); + }); + + describe('template', () => { + it('renders component root element with class `issue-milestone-details`', () => { + expect(vm.$el.classList.contains('issue-milestone-details')).toBe(true); + }); + + it('renders milestone icon', () => { + expect(wrapper.find(GlIcon).props('name')).toBe('clock'); + }); + + it('renders milestone title', () => { + expect(vm.$el.querySelector('.milestone-title').innerText.trim()).toBe(mockMilestone.title); + }); + + it('renders milestone tooltip', () => { + expect(vm.$el.querySelector('.js-item-milestone').innerText.trim()).toContain( + mockMilestone.title, + ); + }); + }); +}); diff --git a/spec/frontend/issuable/components/related_issuable_item_spec.js b/spec/frontend/issuable/components/related_issuable_item_spec.js new file mode 100644 index 00000000000..6ac4c9e8546 --- /dev/null +++ b/spec/frontend/issuable/components/related_issuable_item_spec.js @@ -0,0 +1,206 @@ +import { mount } from '@vue/test-utils'; +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: '<div class="js-due-date-slot"></div>', + weight: '<div class="js-weight-slot"></div>', + }; + + 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('<span class="bold">Created</span>'); + expect(stateTitle).toContain(`<span class="text-tertiary">${formattedCreateDate}</span>`); + }); + + 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 wrapper.vm.$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 wrapper.vm.$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 () => { + wrapper.setData({ removeDisabled: true }); + await wrapper.vm.$nextTick(); + + expect(findRemoveButton().attributes('disabled')).toEqual('disabled'); + }); + + it('triggers onRemoveRequest when clicked', async () => { + findRemoveButton().trigger('click'); + await wrapper.vm.$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); + }); + }); +}); diff --git a/spec/frontend/issuable/components/related_issuable_mock_data.js b/spec/frontend/issuable/components/related_issuable_mock_data.js new file mode 100644 index 00000000000..6cdb945ec20 --- /dev/null +++ b/spec/frontend/issuable/components/related_issuable_mock_data.js @@ -0,0 +1,123 @@ +import { TEST_HOST } from 'helpers/test_constants'; + +export const defaultProps = { + endpoint: '/foo/bar/issues/1/related_issues', + currentNamespacePath: 'foo', + currentProjectPath: 'bar', +}; + +export const issuable1 = { + id: 200, + epicIssueId: 1, + confidential: false, + reference: 'foo/bar#123', + displayReference: '#123', + title: 'some title', + path: '/foo/bar/issues/123', + relationPath: '/foo/bar/issues/123/relation', + state: 'opened', + linkType: 'relates_to', + dueDate: '2010-11-22', + weight: 5, +}; + +export const issuable2 = { + id: 201, + epicIssueId: 2, + confidential: false, + reference: 'foo/bar#124', + displayReference: '#124', + title: 'some other thing', + path: '/foo/bar/issues/124', + relationPath: '/foo/bar/issues/124/relation', + state: 'opened', + linkType: 'blocks', +}; + +export const issuable3 = { + id: 202, + epicIssueId: 3, + confidential: false, + reference: 'foo/bar#125', + displayReference: '#125', + title: 'some other other thing', + path: '/foo/bar/issues/125', + relationPath: '/foo/bar/issues/125/relation', + state: 'opened', + linkType: 'is_blocked_by', +}; + +export const issuable4 = { + id: 203, + epicIssueId: 4, + confidential: false, + reference: 'foo/bar#126', + displayReference: '#126', + title: 'some other other other thing', + path: '/foo/bar/issues/126', + relationPath: '/foo/bar/issues/126/relation', + state: 'opened', +}; + +export const issuable5 = { + id: 204, + epicIssueId: 5, + confidential: false, + reference: 'foo/bar#127', + displayReference: '#127', + title: 'some other other other thing', + path: '/foo/bar/issues/127', + relationPath: '/foo/bar/issues/127/relation', + state: 'opened', +}; + +export const defaultMilestone = { + id: 1, + state: 'active', + title: 'Milestone title', + start_date: '2018-01-01', + due_date: '2019-12-31', +}; + +export const defaultAssignees = [ + { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: `${TEST_HOST}`, + web_url: `${TEST_HOST}/root`, + status_tooltip_html: null, + path: '/root', + }, + { + id: 13, + name: 'Brooks Beatty', + username: 'brynn_champlin', + state: 'active', + avatar_url: `${TEST_HOST}`, + web_url: `${TEST_HOST}/brynn_champlin`, + status_tooltip_html: null, + path: '/brynn_champlin', + }, + { + id: 6, + name: 'Bryce Turcotte', + username: 'melynda', + state: 'active', + avatar_url: `${TEST_HOST}`, + web_url: `${TEST_HOST}/melynda`, + status_tooltip_html: null, + path: '/melynda', + }, + { + id: 20, + name: 'Conchita Eichmann', + username: 'juliana_gulgowski', + state: 'active', + avatar_url: `${TEST_HOST}`, + web_url: `${TEST_HOST}/juliana_gulgowski`, + status_tooltip_html: null, + path: '/juliana_gulgowski', + }, +]; diff --git a/spec/frontend/issuable/issuable_form_spec.js b/spec/frontend/issuable/issuable_form_spec.js new file mode 100644 index 00000000000..321c61ead1e --- /dev/null +++ b/spec/frontend/issuable/issuable_form_spec.js @@ -0,0 +1,62 @@ +import $ from 'jquery'; + +import IssuableForm from '~/issuable/issuable_form'; + +function createIssuable() { + const instance = new IssuableForm($(document.createElement('form'))); + + instance.titleField = $(document.createElement('input')); + + return instance; +} + +describe('IssuableForm', () => { + let instance; + + beforeEach(() => { + instance = createIssuable(); + }); + + describe('removeWip', () => { + it.each` + prefix + ${'draFT: '} + ${' [DRaft] '} + ${'drAft:'} + ${'[draFT]'} + ${'(draft) '} + ${' (DrafT)'} + ${'draft: [draft] (draft)'} + `('removes "$prefix" from the beginning of the title', ({ prefix }) => { + instance.titleField.val(`${prefix}The Issuable's Title Value`); + + instance.removeWip(); + + expect(instance.titleField.val()).toBe("The Issuable's Title Value"); + }); + }); + + describe('addWip', () => { + it("properly adds the work in progress prefix to the Issuable's title", () => { + instance.titleField.val("The Issuable's Title Value"); + + instance.addWip(); + + expect(instance.titleField.val()).toBe("Draft: The Issuable's Title Value"); + }); + }); + + describe('workInProgress', () => { + it.each` + title | expected + ${'draFT: something is happening'} | ${true} + ${'draft something is happening'} | ${false} + ${'something is happening to drafts'} | ${false} + ${'something is happening'} | ${false} + `('returns $expected with "$title"', ({ title, expected }) => { + instance.titleField.val(title); + + expect(instance.workInProgress()).toBe(expected); + }); + }); +}); diff --git a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js index a450f912c4e..608fec45bbd 100644 --- a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js +++ b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js @@ -4,7 +4,7 @@ import { issuable1, issuable2, issuable3, -} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; +} from 'jest/issuable/components/related_issuable_mock_data'; import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue'; import { linkedIssueTypesMap, diff --git a/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js index ffd9683cd6b..c7df3755e88 100644 --- a/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js +++ b/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js @@ -5,7 +5,7 @@ import { issuable3, issuable4, issuable5, -} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; +} from 'jest/issuable/components/related_issuable_mock_data'; import IssueDueDate from '~/boards/components/issue_due_date.vue'; import RelatedIssuesList from '~/related_issues/components/related_issues_list.vue'; import { PathIdSeparator } from '~/related_issues/constants'; diff --git a/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js index 3099e0b639b..01de4da7900 100644 --- a/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js +++ b/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js @@ -5,7 +5,7 @@ import { defaultProps, issuable1, issuable2, -} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; +} from 'jest/issuable/components/related_issuable_mock_data'; import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue'; diff --git a/spec/frontend/issuable/related_issues/stores/related_issues_store_spec.js b/spec/frontend/issuable/related_issues/stores/related_issues_store_spec.js index ada1c44560f..4a6bd832fba 100644 --- a/spec/frontend/issuable/related_issues/stores/related_issues_store_spec.js +++ b/spec/frontend/issuable/related_issues/stores/related_issues_store_spec.js @@ -4,7 +4,7 @@ import { issuable3, issuable4, issuable5, -} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; +} from 'jest/issuable/components/related_issuable_mock_data'; import RelatedIssuesStore from '~/related_issues/stores/related_issues_store'; describe('RelatedIssuesStore', () => { |