Commit a1c27e95 authored by Miguel Rincon's avatar Miguel Rincon

Merge branch '212329-related-issues-to-core_frontend-and-qa' into 'master'

Related Issues - move to FOSS [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!39779
parents aeac7c0e b2d14975
import initSidebarBundle from '~/sidebar/sidebar_bundle'; import initSidebarBundle from '~/sidebar/sidebar_bundle';
import initRelatedIssues from '~/related_issues';
import initShow from '../show'; import initShow from '../show';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
...@@ -6,4 +7,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -6,4 +7,5 @@ document.addEventListener('DOMContentLoaded', () => {
if (gon.features && !gon.features.vueIssuableSidebar) { if (gon.features && !gon.features.vueIssuableSidebar) {
initSidebarBundle(); initSidebarBundle();
} }
initRelatedIssues();
}); });
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import sortableConfig from 'ee/sortable/sortable_config'; import sortableConfig from 'ee_else_ce/sortable/sortable_config';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
......
...@@ -17,6 +17,9 @@ export default function initRelatedIssues() { ...@@ -17,6 +17,9 @@ export default function initRelatedIssues() {
endpoint: relatedIssuesRootElement.dataset.endpoint, endpoint: relatedIssuesRootElement.dataset.endpoint,
canAdmin: parseBoolean(relatedIssuesRootElement.dataset.canAddRelatedIssues), canAdmin: parseBoolean(relatedIssuesRootElement.dataset.canAddRelatedIssues),
helpPath: relatedIssuesRootElement.dataset.helpPath, helpPath: relatedIssuesRootElement.dataset.helpPath,
showCategorizedIssues: parseBoolean(
relatedIssuesRootElement.dataset.showCategorizedIssues,
),
}, },
}), }),
}); });
......
- if can?(current_user, :read_issue_link, @project)
.js-related-issues-root{ data: { endpoint: project_issue_links_path(@project, @issue),
can_add_related_issues: "#{can?(current_user, :admin_issue_link, @issue)}",
help_path: help_page_path('user/project/issues/related_issues'),
show_categorized_issues: "false" } }
- render('projects/issues/related_issues_block')
.related-issues-block
.card.card-slim
.card-header.panel-empty-heading.border-bottom-0
%h3.card-title.mt-0.mb-0.h5
= _('Linked issues')
---
title: Move related issues to core
merge_request: 39779
author:
type: changed
<script> <script>
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import { PathIdSeparator } from 'ee/related_issues/constants'; import { PathIdSeparator } from '~/related_issues/constants';
import IssuableBody from '~/issue_show/components/app.vue'; import IssuableBody from '~/issue_show/components/app.vue';
import IssuableSidebar from '~/issuable_sidebar/components/sidebar_app.vue'; import IssuableSidebar from '~/issuable_sidebar/components/sidebar_app.vue';
......
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
GlSprintf, GlSprintf,
} from '@gitlab/ui'; } from '@gitlab/ui';
import Api from 'ee/api'; import Api from 'ee/api';
import RelatedIssuesRoot from 'ee/related_issues/components/related_issues_root.vue'; import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { deprecatedCreateFlash as flash, FLASH_TYPES } from '~/flash'; import { deprecatedCreateFlash as flash, FLASH_TYPES } from '~/flash';
import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
......
import initSidebarBundle from 'ee/sidebar/sidebar_bundle'; import initSidebarBundle from 'ee/sidebar/sidebar_bundle';
import initRelatedIssues from 'ee/related_issues';
import trackShowInviteMemberLink from 'ee/projects/track_invite_members'; import trackShowInviteMemberLink from 'ee/projects/track_invite_members';
import initRelatedIssues from '~/related_issues';
import initShow from '~/pages/projects/issues/show'; import initShow from '~/pages/projects/issues/show';
import UserCallout from '~/user_callout'; import UserCallout from '~/user_callout';
......
<script> <script>
import RelatedIssuableInput from 'ee/related_issues/components/related_issuable_input.vue'; import RelatedIssuableInput from '~/related_issues/components/related_issuable_input.vue';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from '~/related_issues/constants';
export default { export default {
components: { components: {
......
...@@ -3,7 +3,7 @@ import { mapState, mapActions, mapGetters } from 'vuex'; ...@@ -3,7 +3,7 @@ import { mapState, mapActions, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import AddItemForm from 'ee/related_issues/components/add_issuable_form.vue'; import AddItemForm from '~/related_issues/components/add_issuable_form.vue';
import SlotSwitch from '~/vue_shared/components/slot_switch.vue'; import SlotSwitch from '~/vue_shared/components/slot_switch.vue';
import CreateEpicForm from './create_epic_form.vue'; import CreateEpicForm from './create_epic_form.vue';
import CreateIssueForm from './create_issue_form.vue'; import CreateIssueForm from './create_issue_form.vue';
......
...@@ -3,7 +3,7 @@ import { mapState, mapActions } from 'vuex'; ...@@ -3,7 +3,7 @@ import { mapState, mapActions } from 'vuex';
import { GlTooltip, GlIcon } from '@gitlab/ui'; import { GlTooltip, GlIcon } from '@gitlab/ui';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from '~/related_issues/constants';
import EpicActionsSplitButton from './epic_issue_actions_split_button.vue'; import EpicActionsSplitButton from './epic_issue_actions_split_button.vue';
import EpicHealthStatus from './epic_health_status.vue'; import EpicHealthStatus from './epic_health_status.vue';
......
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
itemAddFailureTypesMap, itemAddFailureTypesMap,
pathIndeterminateErrorMap, pathIndeterminateErrorMap,
relatedIssuesRemoveErrorMap, relatedIssuesRemoveErrorMap,
} from 'ee/related_issues/constants'; } from '~/related_issues/constants';
import { deprecatedCreateFlash as flash } from '~/flash'; import { deprecatedCreateFlash as flash } from '~/flash';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
......
import { issuableTypesMap, PathIdSeparator } from 'ee/related_issues/constants'; import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
export const autoCompleteSources = () => gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources; export const autoCompleteSources = () => gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources;
......
import Vue from 'vue'; import Vue from 'vue';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from '~/related_issues/constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
......
import { PathIdSeparator } from 'ee/related_issues/constants'; import { PathIdSeparator } from '~/related_issues/constants';
import createGqClient, { fetchPolicies } from '~/lib/graphql'; import createGqClient, { fetchPolicies } from '~/lib/graphql';
import { ChildType } from '../constants'; import { ChildType } from '../constants';
......
<script> <script>
import axios from 'axios'; import axios from 'axios';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import RelatedIssuesStore from 'ee/related_issues/stores/related_issues_store'; import RelatedIssuesStore from '~/related_issues/stores/related_issues_store';
import RelatedIssuesBlock from 'ee/related_issues/components/related_issues_block.vue'; import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue';
import { issuableTypesMap, PathIdSeparator } from 'ee/related_issues/constants'; import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
import { sprintf, __, s__ } from '~/locale'; import { sprintf, __, s__ } from '~/locale';
import { joinPaths, redirectTo } from '~/lib/utils/url_utility'; import { joinPaths, redirectTo } from '~/lib/utils/url_utility';
import { RELATED_ISSUES_ERRORS, FEEDBACK_TYPES } from '../constants'; import { RELATED_ISSUES_ERRORS, FEEDBACK_TYPES } from '../constants';
......
- if can?(current_user, :read_issue_link, @project) - if can?(current_user, :read_issue_link, @project)
.js-related-issues-root{ data: { endpoint: project_issue_links_path(@project, @issue), .js-related-issues-root{ data: { endpoint: project_issue_links_path(@project, @issue),
can_add_related_issues: "#{can?(current_user, :admin_issue_link, @issue)}", can_add_related_issues: "#{can?(current_user, :admin_issue_link, @issue)}",
help_path: help_page_path('user/project/issues/related_issues') } } help_path: help_page_path('user/project/issues/related_issues'),
.related-issues-block show_categorized_issues: "#{!!@project.feature_available?(:blocked_issues)}" } }
.card.card-slim - render('projects/issues/related_issues_block')
.card-header.panel-empty-heading.border-bottom-0
%h3.card-title.mt-0.mb-0.h5
= _('Linked issues')
...@@ -13,7 +13,7 @@ import { ...@@ -13,7 +13,7 @@ import {
LEGACY_FLAG, LEGACY_FLAG,
NEW_VERSION_FLAG, NEW_VERSION_FLAG,
} from 'ee/feature_flags/constants'; } from 'ee/feature_flags/constants';
import RelatedIssuesRoot from 'ee/related_issues/components/related_issues_root.vue'; import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue';
import ToggleButton from '~/vue_shared/components/toggle_button.vue'; import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import { featureFlag, userList, allUsersStrategy } from '../mock_data'; import { featureFlag, userList, allUsersStrategy } from '../mock_data';
......
import { mount, shallowMount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { issuable1 } from 'jest/vue_shared/components/issue/related_issuable_mock_data';
import IssueWeight from 'ee/boards/components/issue_card_weight.vue'; import IssueWeight from 'ee/boards/components/issue_card_weight.vue';
import RelatedIssuesList from 'ee/related_issues/components/related_issues_list.vue'; import RelatedIssuesList from '~/related_issues/components/related_issues_list.vue';
import { import { PathIdSeparator } from '~/related_issues/constants';
issuable1,
issuable2,
issuable3,
issuable4,
issuable5,
} from 'jest/vue_shared/components/issue/related_issuable_mock_data';
import { PathIdSeparator } from 'ee/related_issues/constants';
import IssueDueDate from '~/boards/components/issue_due_date.vue';
/*
* Here we only test the behavior of Related Issues with Weight, as weight is an EE-only feature.
*/
describe('RelatedIssuesList', () => { describe('RelatedIssuesList', () => {
let wrapper; let wrapper;
...@@ -18,156 +14,6 @@ describe('RelatedIssuesList', () => { ...@@ -18,156 +14,6 @@ describe('RelatedIssuesList', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('with defaults', () => {
const heading = 'Related to';
beforeEach(() => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'issue',
heading,
},
});
});
it('shows a heading', () => {
expect(wrapper.find('h4').text()).toContain(heading);
});
it('should not show loading icon', () => {
expect(wrapper.vm.$refs.loadingIcon).toBeUndefined();
});
});
describe('with isFetching=true', () => {
beforeEach(() => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
isFetching: true,
issuableType: 'issue',
},
});
});
it('should show loading icon', () => {
expect(wrapper.vm.$refs.loadingIcon).toBeDefined();
});
});
describe('methods', () => {
beforeEach(() => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
relatedIssues: [issuable1, issuable2, issuable3, issuable4, issuable5],
issuableType: 'issue',
},
});
});
it('updates the order correctly when an item is moved to the top', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:first-child'),
);
expect(beforeAfterIds.beforeId).toBeNull();
expect(beforeAfterIds.afterId).toBe(2);
});
it('updates the order correctly when an item is moved to the bottom', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:last-child'),
);
expect(beforeAfterIds.beforeId).toBe(4);
expect(beforeAfterIds.afterId).toBeNull();
});
it('updates the order correctly when an item is swapped with adjacent item', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:nth-child(3)'),
);
expect(beforeAfterIds.beforeId).toBe(2);
expect(beforeAfterIds.afterId).toBe(4);
});
it('updates the order correctly when an item is moved somewhere in the middle', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:nth-child(4)'),
);
expect(beforeAfterIds.beforeId).toBe(3);
expect(beforeAfterIds.afterId).toBe(5);
});
});
describe('issuableOrderingId returns correct issuable order id when', () => {
it('issuableType is epic', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'issue',
},
});
expect(wrapper.vm.issuableOrderingId(issuable1)).toBe(issuable1.epicIssueId);
});
it('issuableType is issue', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'epic',
},
});
expect(wrapper.vm.issuableOrderingId(issuable1)).toBe(issuable1.id);
});
});
describe('renders correct ordering id when', () => {
let relatedIssues;
beforeAll(() => {
relatedIssues = [issuable1, issuable2, issuable3, issuable4, issuable5];
});
it('issuableType is epic', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'epic',
relatedIssues,
},
});
const listItems = wrapper.vm.$el.querySelectorAll('.list-item');
Array.from(listItems).forEach((item, index) => {
expect(Number(item.dataset.orderingId)).toBe(relatedIssues[index].id);
});
});
it('issuableType is issue', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'issue',
relatedIssues,
},
});
const listItems = wrapper.vm.$el.querySelectorAll('.list-item');
Array.from(listItems).forEach((item, index) => {
expect(Number(item.dataset.orderingId)).toBe(relatedIssues[index].epicIssueId);
});
});
});
describe('related item contents', () => { describe('related item contents', () => {
beforeAll(() => { beforeAll(() => {
wrapper = mount(RelatedIssuesList, { wrapper = mount(RelatedIssuesList, {
...@@ -187,14 +33,5 @@ describe('RelatedIssuesList', () => { ...@@ -187,14 +33,5 @@ describe('RelatedIssuesList', () => {
.text(), .text(),
).toBe(issuable1.weight.toString()); ).toBe(issuable1.weight.toString());
}); });
it('shows due date', () => {
expect(
wrapper
.find(IssueDueDate)
.find('.board-card-info-text')
.text(),
).toBe('Nov 22, 2010');
});
}); });
}); });
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import BlockingMrInputRoot from 'ee/projects/merge_requests/blocking_mr_input_root.vue'; import BlockingMrInputRoot from 'ee/projects/merge_requests/blocking_mr_input_root.vue';
import RelatedIssuableInput from 'ee/related_issues/components/related_issuable_input.vue'; import RelatedIssuableInput from '~/related_issues/components/related_issuable_input.vue';
describe('blocking mr input root', () => { describe('blocking mr input root', () => {
let wrapper; let wrapper;
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { issuableTypesMap, PathIdSeparator } from 'ee/related_issues/constants'; import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
import RelatedIssuableInput from 'ee/related_issues/components/related_issuable_input.vue'; import RelatedIssuableInput from '~/related_issues/components/related_issuable_input.vue';
jest.mock('ee_else_ce/gfm_auto_complete', () => { jest.mock('ee_else_ce/gfm_auto_complete', () => {
return function gfmAutoComplete() { return function gfmAutoComplete() {
......
...@@ -5,10 +5,10 @@ import { GlLoadingIcon } from '@gitlab/ui'; ...@@ -5,10 +5,10 @@ import { GlLoadingIcon } from '@gitlab/ui';
import RelatedItemsTreeApp from 'ee/related_items_tree/components/related_items_tree_app.vue'; import RelatedItemsTreeApp from 'ee/related_items_tree/components/related_items_tree_app.vue';
import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue'; import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue';
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import { issuableTypesMap } from 'ee/related_issues/constants';
import CreateIssueForm from 'ee/related_items_tree/components/create_issue_form.vue'; import CreateIssueForm from 'ee/related_items_tree/components/create_issue_form.vue';
import AxiosMockAdapter from 'axios-mock-adapter'; import AxiosMockAdapter from 'axios-mock-adapter';
import { getJSONFixture } from 'helpers/fixtures'; import { getJSONFixture } from 'helpers/fixtures';
import { issuableTypesMap } from '~/related_issues/constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { mockInitialConfig, mockParentItem, mockEpics, mockIssues } from '../mock_data'; import { mockInitialConfig, mockParentItem, mockEpics, mockIssues } from '../mock_data';
......
...@@ -5,11 +5,11 @@ import { GlTooltip, GlIcon } from '@gitlab/ui'; ...@@ -5,11 +5,11 @@ import { GlTooltip, GlIcon } from '@gitlab/ui';
import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue'; import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue';
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { issuableTypesMap } from 'ee/related_issues/constants';
import EpicActionsSplitButton from 'ee/related_items_tree/components/epic_issue_actions_split_button.vue'; import EpicActionsSplitButton from 'ee/related_items_tree/components/epic_issue_actions_split_button.vue';
import EpicHealthStatus from 'ee/related_items_tree/components/epic_health_status.vue'; import EpicHealthStatus from 'ee/related_items_tree/components/epic_health_status.vue';
import { issuableTypesMap } from '~/related_issues/constants';
import { mockInitialConfig, mockParentItem, mockQueryResponse } from '../mock_data'; import { mockInitialConfig, mockParentItem, mockQueryResponse } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
...@@ -10,7 +10,7 @@ import StateTooltip from 'ee/related_items_tree/components/state_tooltip.vue'; ...@@ -10,7 +10,7 @@ import StateTooltip from 'ee/related_items_tree/components/state_tooltip.vue';
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { ChildType, ChildState } from 'ee/related_items_tree/constants'; import { ChildType, ChildState } from 'ee/related_items_tree/constants';
import { PathIdSeparator } from 'ee/related_issues/constants'; import { PathIdSeparator } from '~/related_issues/constants';
import ItemAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; import ItemAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
import ItemDueDate from '~/boards/components/issue_due_date.vue'; import ItemDueDate from '~/boards/components/issue_due_date.vue';
import ItemMilestone from '~/vue_shared/components/issue/issue_milestone.vue'; import ItemMilestone from '~/vue_shared/components/issue/issue_milestone.vue';
......
...@@ -7,7 +7,7 @@ import TreeItemRemoveModal from 'ee/related_items_tree/components/tree_item_remo ...@@ -7,7 +7,7 @@ import TreeItemRemoveModal from 'ee/related_items_tree/components/tree_item_remo
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { ChildType } from 'ee/related_items_tree/constants'; import { ChildType } from 'ee/related_items_tree/constants';
import { PathIdSeparator } from 'ee/related_issues/constants'; import { PathIdSeparator } from '~/related_issues/constants';
import { mockParentItem, mockQueryResponse, mockIssue1 } from '../mock_data'; import { mockParentItem, mockQueryResponse, mockIssue1 } from '../mock_data';
......
...@@ -9,7 +9,7 @@ import TreeRoot from 'ee/related_items_tree/components/tree_root.vue'; ...@@ -9,7 +9,7 @@ import TreeRoot from 'ee/related_items_tree/components/tree_root.vue';
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { ChildType, treeItemChevronBtnClassName } from 'ee/related_items_tree/constants'; import { ChildType, treeItemChevronBtnClassName } from 'ee/related_items_tree/constants';
import { PathIdSeparator } from 'ee/related_issues/constants'; import { PathIdSeparator } from '~/related_issues/constants';
import { mockParentItem, mockQueryResponse, mockEpic1 } from '../mock_data'; import { mockParentItem, mockQueryResponse, mockEpic1 } from '../mock_data';
......
...@@ -5,14 +5,14 @@ import * as types from 'ee/related_items_tree/store/mutation_types'; ...@@ -5,14 +5,14 @@ import * as types from 'ee/related_items_tree/store/mutation_types';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { ChildType, ChildState } from 'ee/related_items_tree/constants'; import { ChildType, ChildState } from 'ee/related_items_tree/constants';
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
import { import {
issuableTypesMap, issuableTypesMap,
itemAddFailureTypesMap, itemAddFailureTypesMap,
PathIdSeparator, PathIdSeparator,
} from 'ee/related_issues/constants'; } from '~/related_issues/constants';
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
......
import * as getters from 'ee/related_items_tree/store/getters'; import * as getters from 'ee/related_items_tree/store/getters';
import createDefaultState from 'ee/related_items_tree/store/state'; import createDefaultState from 'ee/related_items_tree/store/state';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from '~/related_issues/constants';
import { mockEpic1, mockEpic2 } from '../mock_data'; import { mockEpic1, mockEpic2 } from '../mock_data';
......
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { PathIdSeparator } from 'ee/related_issues/constants';
import { ChildType } from 'ee/related_items_tree/constants'; import { ChildType } from 'ee/related_items_tree/constants';
import { PathIdSeparator } from '~/related_issues/constants';
import { mockQueryResponse2, mockEpic1, mockIssue1 } from '../mock_data'; import { mockQueryResponse2, mockEpic1, mockIssue1 } from '../mock_data';
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import RelatedIssues from 'ee/vulnerabilities/components/related_issues.vue'; import RelatedIssues from 'ee/vulnerabilities/components/related_issues.vue';
import RelatedIssuesBlock from 'ee/related_issues/components/related_issues_block.vue';
import { issuableTypesMap, PathIdSeparator } from 'ee/related_issues/constants';
import { FEEDBACK_TYPES } from 'ee/vulnerabilities/constants'; import { FEEDBACK_TYPES } from 'ee/vulnerabilities/constants';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue';
import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status'; import httpStatusCodes from '~/lib/utils/http_status';
......
...@@ -12,11 +12,11 @@ module QA ...@@ -12,11 +12,11 @@ module QA
element :close_reopen_epic_button element :close_reopen_epic_button
end end
view 'ee/app/assets/javascripts/related_issues/components/add_issuable_form.vue' do view 'app/assets/javascripts/related_issues/components/add_issuable_form.vue' do
element :add_issue_button element :add_issue_button
end end
view 'ee/app/assets/javascripts/related_issues/components/related_issuable_input.vue' do view 'app/assets/javascripts/related_issues/components/related_issuable_input.vue' do
element :add_issue_input element :add_issue_input
end end
......
...@@ -12,23 +12,6 @@ module QA ...@@ -12,23 +12,6 @@ module QA
super super
base.class_eval do base.class_eval do
view 'ee/app/assets/javascripts/related_issues/components/add_issuable_form.vue' do
element :add_issue_button
end
view 'ee/app/assets/javascripts/related_issues/components/related_issuable_input.vue' do
element :add_issue_input
end
view 'ee/app/assets/javascripts/related_issues/components/related_issues_block.vue' do
element :related_issues_plus_button
end
view 'ee/app/assets/javascripts/related_issues/components/related_issues_list.vue' do
element :related_issuable_item
element :related_issues_loading_icon
end
view 'ee/app/assets/javascripts/sidebar/components/weight/weight.vue' do view 'ee/app/assets/javascripts/sidebar/components/weight/weight.vue' do
element :weight_label_value element :weight_label_value
element :edit_weight_link element :edit_weight_link
...@@ -43,26 +26,12 @@ module QA ...@@ -43,26 +26,12 @@ module QA
click_element(:remove_weight_link) click_element(:remove_weight_link)
end end
def relate_issue(issue)
click_element(:related_issues_plus_button)
fill_element(:add_issue_input, issue.web_url)
send_keys_to_element(:add_issue_input, :enter)
end
def related_issuable_item
find_element(:related_issuable_item)
end
def set_weight(weight) def set_weight(weight)
click_element(:edit_weight_link) click_element(:edit_weight_link)
fill_element(:weight_input_field, weight) fill_element(:weight_input_field, weight)
send_keys_to_element(:weight_input_field, :enter) send_keys_to_element(:weight_input_field, :enter)
end end
def wait_for_related_issues_to_load
has_no_element?(:related_issues_loading_icon, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
end
def weight_label_value def weight_label_value
find_element(:weight_label_value) find_element(:weight_label_value)
end end
......
...@@ -38,6 +38,37 @@ module QA ...@@ -38,6 +38,37 @@ module QA
element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern
end end
view 'app/assets/javascripts/related_issues/components/add_issuable_form.vue' do
element :add_issue_button
end
view 'app/assets/javascripts/related_issues/components/related_issuable_input.vue' do
element :add_issue_input
end
view 'app/assets/javascripts/related_issues/components/related_issues_block.vue' do
element :related_issues_plus_button
end
view 'app/assets/javascripts/related_issues/components/related_issues_list.vue' do
element :related_issuable_item
element :related_issues_loading_icon
end
def relate_issue(issue)
click_element(:related_issues_plus_button)
fill_element(:add_issue_input, issue.web_url)
send_keys_to_element(:add_issue_input, :enter)
end
def related_issuable_item
find_element(:related_issuable_item)
end
def wait_for_related_issues_to_load
has_no_element?(:related_issues_loading_icon, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
end
def click_remove_related_issue_button def click_remove_related_issue_button
click_element(:remove_related_issue_button) click_element(:remove_related_issue_button)
end end
......
This diff is collapsed.
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import { import { issuableTypesMap, linkedIssueTypesMap, PathIdSeparator } from '~/related_issues/constants';
issuableTypesMap, import AddIssuableForm from '~/related_issues/components/add_issuable_form.vue';
linkedIssueTypesMap,
PathIdSeparator,
} from 'ee/related_issues/constants';
import AddIssuableForm from 'ee/related_issues/components/add_issuable_form.vue';
const issuable1 = { const issuable1 = {
id: 200, id: 200,
......
import Vue from 'vue'; import Vue from 'vue';
import { PathIdSeparator } from 'ee/related_issues/constants'; import { PathIdSeparator } from '~/related_issues/constants';
import issueToken from 'ee/related_issues/components/issue_token.vue'; import issueToken from '~/related_issues/components/issue_token.vue';
describe('IssueToken', () => { describe('IssueToken', () => {
const idKey = 200; const idKey = 200;
......
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { GlButton, GlIcon } from '@gitlab/ui'; import { GlButton, GlIcon } from '@gitlab/ui';
import RelatedIssuesBlock from 'ee/related_issues/components/related_issues_block.vue';
import { import {
issuable1, issuable1,
issuable2, issuable2,
issuable3, issuable3,
} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; } from 'jest/vue_shared/components/issue/related_issuable_mock_data';
import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue';
import { import {
linkedIssueTypesMap, linkedIssueTypesMap,
linkedIssueTypesTextMap, linkedIssueTypesTextMap,
PathIdSeparator, PathIdSeparator,
} from 'ee/related_issues/constants'; } from '~/related_issues/constants';
describe('RelatedIssuesBlock', () => { describe('RelatedIssuesBlock', () => {
let wrapper; let wrapper;
......
import { mount, shallowMount } from '@vue/test-utils';
import {
issuable1,
issuable2,
issuable3,
issuable4,
issuable5,
} from 'jest/vue_shared/components/issue/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';
describe('RelatedIssuesList', () => {
let wrapper;
afterEach(() => {
wrapper.destroy();
});
describe('with defaults', () => {
const heading = 'Related to';
beforeEach(() => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'issue',
heading,
},
});
});
it('shows a heading', () => {
expect(wrapper.find('h4').text()).toContain(heading);
});
it('should not show loading icon', () => {
expect(wrapper.vm.$refs.loadingIcon).toBeUndefined();
});
});
describe('with isFetching=true', () => {
beforeEach(() => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
isFetching: true,
issuableType: 'issue',
},
});
});
it('should show loading icon', () => {
expect(wrapper.vm.$refs.loadingIcon).toBeDefined();
});
});
describe('methods', () => {
beforeEach(() => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
relatedIssues: [issuable1, issuable2, issuable3, issuable4, issuable5],
issuableType: 'issue',
},
});
});
it('updates the order correctly when an item is moved to the top', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:first-child'),
);
expect(beforeAfterIds.beforeId).toBeNull();
expect(beforeAfterIds.afterId).toBe(2);
});
it('updates the order correctly when an item is moved to the bottom', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:last-child'),
);
expect(beforeAfterIds.beforeId).toBe(4);
expect(beforeAfterIds.afterId).toBeNull();
});
it('updates the order correctly when an item is swapped with adjacent item', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:nth-child(3)'),
);
expect(beforeAfterIds.beforeId).toBe(2);
expect(beforeAfterIds.afterId).toBe(4);
});
it('updates the order correctly when an item is moved somewhere in the middle', () => {
const beforeAfterIds = wrapper.vm.getBeforeAfterId(
wrapper.vm.$el.querySelector('ul li:nth-child(4)'),
);
expect(beforeAfterIds.beforeId).toBe(3);
expect(beforeAfterIds.afterId).toBe(5);
});
});
describe('issuableOrderingId returns correct issuable order id when', () => {
it('issuableType is epic', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'issue',
},
});
expect(wrapper.vm.issuableOrderingId(issuable1)).toBe(issuable1.epicIssueId);
});
it('issuableType is issue', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'epic',
},
});
expect(wrapper.vm.issuableOrderingId(issuable1)).toBe(issuable1.id);
});
});
describe('renders correct ordering id when', () => {
let relatedIssues;
beforeAll(() => {
relatedIssues = [issuable1, issuable2, issuable3, issuable4, issuable5];
});
it('issuableType is epic', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'epic',
relatedIssues,
},
});
const listItems = wrapper.vm.$el.querySelectorAll('.list-item');
Array.from(listItems).forEach((item, index) => {
expect(Number(item.dataset.orderingId)).toBe(relatedIssues[index].id);
});
});
it('issuableType is issue', () => {
wrapper = shallowMount(RelatedIssuesList, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
issuableType: 'issue',
relatedIssues,
},
});
const listItems = wrapper.vm.$el.querySelectorAll('.list-item');
Array.from(listItems).forEach((item, index) => {
expect(Number(item.dataset.orderingId)).toBe(relatedIssues[index].epicIssueId);
});
});
});
describe('related item contents', () => {
beforeAll(() => {
wrapper = mount(RelatedIssuesList, {
propsData: {
issuableType: 'issue',
pathIdSeparator: PathIdSeparator.Issue,
relatedIssues: [issuable1],
},
});
});
it('shows due date', () => {
expect(
wrapper
.find(IssueDueDate)
.find('.board-card-info-text')
.text(),
).toBe('Nov 22, 2010');
});
});
});
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import RelatedIssuesRoot from 'ee/related_issues/components/related_issues_root.vue';
import relatedIssuesService from 'ee/related_issues/services/related_issues_service';
import { linkedIssueTypesMap } from 'ee/related_issues/constants';
import { import {
defaultProps, defaultProps,
issuable1, issuable1,
issuable2, issuable2,
} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; } from 'jest/vue_shared/components/issue/related_issuable_mock_data';
import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue';
import relatedIssuesService from '~/related_issues/services/related_issues_service';
import { linkedIssueTypesMap } from '~/related_issues/constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
......
import RelatedIssuesStore from 'ee/related_issues/stores/related_issues_store';
import { import {
issuable1, issuable1,
issuable2, issuable2,
...@@ -7,6 +5,7 @@ import { ...@@ -7,6 +5,7 @@ import {
issuable4, issuable4,
issuable5, issuable5,
} from 'jest/vue_shared/components/issue/related_issuable_mock_data'; } from 'jest/vue_shared/components/issue/related_issuable_mock_data';
import RelatedIssuesStore from '~/related_issues/stores/related_issues_store';
describe('RelatedIssuesStore', () => { describe('RelatedIssuesStore', () => {
let store; let store;
......
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