Commit 5b721c53 authored by Florie Guibert's avatar Florie Guibert

Use labels widget on epic board and mr sidebar

Add MR support in labels widget
parent a9bae7b7
...@@ -54,6 +54,9 @@ export default { ...@@ -54,6 +54,9 @@ export default {
allowLabelEdit: { allowLabelEdit: {
default: false, default: false,
}, },
labelsFilterBasePath: {
default: '',
},
}, },
inheritAttrs: false, inheritAttrs: false,
computed: { computed: {
...@@ -90,6 +93,11 @@ export default { ...@@ -90,6 +93,11 @@ export default {
labelType() { labelType() {
return this.isGroupBoard ? LabelType.group : LabelType.project; return this.isGroupBoard ? LabelType.group : LabelType.project;
}, },
labelsFilterPath() {
return this.isGroupBoard
? this.labelsFilterBasePath.replace(':project_path', this.projectPathForActiveIssue)
: this.labelsFilterBasePath;
},
}, },
methods: { methods: {
...mapActions([ ...mapActions([
...@@ -212,7 +220,7 @@ export default { ...@@ -212,7 +220,7 @@ export default {
:footer-create-label-title="createLabelTitle" :footer-create-label-title="createLabelTitle"
:footer-manage-label-title="manageLabelTitle" :footer-manage-label-title="manageLabelTitle"
:labels-create-title="createLabelTitle" :labels-create-title="createLabelTitle"
:labels-filter-base-path="projectPathForActiveIssue" :labels-filter-base-path="labelsFilterPath"
:attr-workspace-path="attrWorkspacePath" :attr-workspace-path="attrWorkspacePath"
workspace-type="project" workspace-type="project"
:issuable-type="issuableType" :issuable-type="issuableType"
......
...@@ -37,6 +37,7 @@ import epicLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widge ...@@ -37,6 +37,7 @@ import epicLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widge
import updateEpicLabelsMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql'; import updateEpicLabelsMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
import groupLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql'; import groupLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql';
import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql'; import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql';
import mergeRequestLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql';
import projectLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql'; import projectLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql';
import getAlertAssignees from '~/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql'; import getAlertAssignees from '~/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql';
import getIssueAssignees from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql'; import getIssueAssignees from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql';
...@@ -128,6 +129,7 @@ export const issuableLabelsQueries = { ...@@ -128,6 +129,7 @@ export const issuableLabelsQueries = {
mutationName: 'updateIssue', mutationName: 'updateIssue',
}, },
[IssuableType.MergeRequest]: { [IssuableType.MergeRequest]: {
issuableQuery: mergeRequestLabelsQuery,
mutation: updateMergeRequestLabelsMutation, mutation: updateMergeRequestLabelsMutation,
mutationName: 'mergeRequestSetLabels', mutationName: 'mergeRequestSetLabels',
}, },
......
...@@ -260,6 +260,10 @@ export function mountSidebarLabels() { ...@@ -260,6 +260,10 @@ export function mountSidebarLabels() {
variant: DropdownVariant.Sidebar, variant: DropdownVariant.Sidebar,
canUpdate: parseBoolean(el.dataset.canEdit), canUpdate: parseBoolean(el.dataset.canEdit),
isClassicSidebar: true, isClassicSidebar: true,
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
: IssuableType.MergeRequest,
}, },
render: (createElement) => createElement(SidebarLabels), render: (createElement) => createElement(SidebarLabels),
}); });
......
...@@ -2,6 +2,7 @@ mutation mergeRequestSetLabels($input: MergeRequestSetLabelsInput!) { ...@@ -2,6 +2,7 @@ mutation mergeRequestSetLabels($input: MergeRequestSetLabelsInput!) {
mergeRequestSetLabels(input: $input) { mergeRequestSetLabels(input: $input) {
errors errors
mergeRequest { mergeRequest {
id
labels { labels {
nodes { nodes {
color color
......
...@@ -53,10 +53,6 @@ export default { ...@@ -53,10 +53,6 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
issuableType: {
type: String,
required: true,
},
isVisible: { isVisible: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -209,7 +205,6 @@ export default { ...@@ -209,7 +205,6 @@ export default {
v-model="localSelectedLabels" v-model="localSelectedLabels"
:search-key="searchKey" :search-key="searchKey"
:allow-multiselect="allowMultiselect" :allow-multiselect="allowMultiselect"
:issuable-type="issuableType"
:full-path="fullPath" :full-path="fullPath"
:workspace-type="workspaceType" :workspace-type="workspaceType"
:attr-workspace-path="attrWorkspacePath" :attr-workspace-path="attrWorkspacePath"
......
...@@ -32,11 +32,6 @@ export default { ...@@ -32,11 +32,6 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
issuableType: {
type: String,
required: false,
default: undefined,
},
workspaceType: { workspaceType: {
type: String, type: String,
required: true, required: true,
......
...@@ -23,10 +23,6 @@ export default { ...@@ -23,10 +23,6 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
issuableType: {
type: String,
required: true,
},
localSelectedLabels: { localSelectedLabels: {
type: Array, type: Array,
required: true, required: true,
...@@ -119,13 +115,7 @@ export default { ...@@ -119,13 +115,7 @@ export default {
({ id }) => id !== getIdFromGraphQLId(label.id) && id !== label.id, ({ id }) => id !== getIdFromGraphQLId(label.id) && id !== label.id,
); );
} else { } else {
labels = [ labels = [...this.localSelectedLabels, label];
...this.localSelectedLabels,
{
...label,
id: getIdFromGraphQLId(label.id),
},
];
} }
this.$emit('input', labels); this.$emit('input', labels);
}, },
......
<script> <script>
import { GlLabel } from '@gitlab/ui'; import { GlLabel } from '@gitlab/ui';
import { sortBy } from 'lodash'; import { sortBy } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { isScopedLabel } from '~/lib/utils/common_utils'; import { isScopedLabel } from '~/lib/utils/common_utils';
export default { export default {
...@@ -47,7 +46,7 @@ export default { ...@@ -47,7 +46,7 @@ export default {
return this.allowScopedLabels && isScopedLabel(label); return this.allowScopedLabels && isScopedLabel(label);
}, },
removeLabel(labelId) { removeLabel(labelId) {
this.$emit('onLabelRemove', getIdFromGraphQLId(labelId)); this.$emit('onLabelRemove', labelId);
}, },
}, },
}; };
......
#import "~/graphql_shared/fragments/label.fragment.graphql"
query mergeRequestLabels($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
issuable: mergeRequest(iid: $iid) {
id
labels {
nodes {
...Label
}
}
}
}
}
...@@ -207,7 +207,7 @@ export default { ...@@ -207,7 +207,7 @@ export default {
return { return {
iid: currentIid, iid: currentIid,
groupPath: this.fullPath, groupPath: this.fullPath,
addLabelIds: labelIds, addLabelIds: labelIds.map((id) => getIdFromGraphQLId(id)),
removeLabelIds: this.issuableLabelIds removeLabelIds: this.issuableLabelIds
.filter((id) => !labelIds.includes(id)) .filter((id) => !labelIds.includes(id))
.map((id) => getIdFromGraphQLId(id)), .map((id) => getIdFromGraphQLId(id)),
...@@ -232,8 +232,8 @@ export default { ...@@ -232,8 +232,8 @@ export default {
} }
this.$emit('updateSelectedLabels', { this.$emit('updateSelectedLabels', {
id: data[mutationName]?.[this.issuableType].id, id: data[mutationName]?.[this.issuableType]?.id,
labels: data[mutationName]?.[this.issuableType].labels?.nodes, labels: data[mutationName]?.[this.issuableType]?.labels?.nodes,
}); });
}) })
.catch((error) => .catch((error) =>
...@@ -268,7 +268,7 @@ export default { ...@@ -268,7 +268,7 @@ export default {
case IssuableType.Epic: case IssuableType.Epic:
return { return {
iid: this.iid, iid: this.iid,
removeLabelIds: [labelId], removeLabelIds: [getIdFromGraphQLId(labelId)],
groupPath: this.fullPath, groupPath: this.fullPath,
}; };
default: default:
......
...@@ -470,6 +470,10 @@ ...@@ -470,6 +470,10 @@
.labels-select-wrapper.is-embedded .labels-select-wrapper.is-embedded { .labels-select-wrapper.is-embedded .labels-select-wrapper.is-embedded {
width: auto; width: auto;
} }
.show.dropdown .dropdown-menu {
width: 100%;
}
} }
.board-header-collapsed-info-icon:hover { .board-header-collapsed-info-icon:hover {
......
...@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:restructured_mr_widget, project, default_enabled: :yaml) push_frontend_feature_flag(:restructured_mr_widget, project, default_enabled: :yaml)
push_frontend_feature_flag(:mr_changes_fluid_layout, project, default_enabled: :yaml) push_frontend_feature_flag(:mr_changes_fluid_layout, project, default_enabled: :yaml)
push_frontend_feature_flag(:mr_attention_requests, project, default_enabled: :yaml) push_frontend_feature_flag(:mr_attention_requests, project, default_enabled: :yaml)
push_frontend_feature_flag(:labels_widget, @project, default_enabled: :yaml)
# Usage data feature flags # Usage data feature flags
push_frontend_feature_flag(:users_expanding_widgets_usage_data, @project, default_enabled: :yaml) push_frontend_feature_flag(:users_expanding_widgets_usage_data, @project, default_enabled: :yaml)
......
...@@ -43,7 +43,7 @@ module BoardsHelper ...@@ -43,7 +43,7 @@ module BoardsHelper
def build_issue_link_base def build_issue_link_base
if board.group_board? if board.group_board?
"#{group_path(@board.group)}/:project_path/issues" "/:project_path/-/issues"
else else
project_issues_path(@project) project_issues_path(@project)
end end
......
...@@ -6,11 +6,14 @@ import SidebarAncestorsWidget from 'ee_component/sidebar/components/ancestors_tr ...@@ -6,11 +6,14 @@ import SidebarAncestorsWidget from 'ee_component/sidebar/components/ancestors_tr
import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue'; import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue'; import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
import { ISSUABLE } from '~/boards/constants'; import { ISSUABLE } from '~/boards/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue'; import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import SidebarLabelsWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default { export default {
components: { components: {
...@@ -18,6 +21,7 @@ export default { ...@@ -18,6 +21,7 @@ export default {
SidebarTodoWidget, SidebarTodoWidget,
BoardSidebarLabelsSelect, BoardSidebarLabelsSelect,
BoardSidebarTitle, BoardSidebarTitle,
SidebarLabelsWidget,
SidebarConfidentialityWidget, SidebarConfidentialityWidget,
SidebarDateWidget, SidebarDateWidget,
SidebarParticipantsWidget, SidebarParticipantsWidget,
...@@ -25,6 +29,8 @@ export default { ...@@ -25,6 +29,8 @@ export default {
SidebarAncestorsWidget, SidebarAncestorsWidget,
MountingPortal, MountingPortal,
}, },
mixins: [glFeatureFlagMixin()],
inject: ['canUpdate', 'labelsFilterBasePath'],
inheritAttrs: false, inheritAttrs: false,
computed: { computed: {
...mapGetters(['isSidebarOpen', 'activeBoardItem']), ...mapGetters(['isSidebarOpen', 'activeBoardItem']),
...@@ -40,10 +46,30 @@ export default { ...@@ -40,10 +46,30 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions(['toggleBoardItem', 'setActiveItemConfidential', 'setActiveItemSubscribed']), ...mapActions([
'toggleBoardItem',
'setActiveItemConfidential',
'setActiveItemSubscribed',
'setActiveBoardItemLabels',
]),
handleClose() { handleClose() {
this.toggleBoardItem({ boardItem: this.activeBoardItem, sidebarType: this.sidebarType }); this.toggleBoardItem({ boardItem: this.activeBoardItem, sidebarType: this.sidebarType });
}, },
handleUpdateSelectedLabels({ labels, id }) {
this.setActiveBoardItemLabels({
id,
groupPath: this.fullPath,
labelIds: labels.map((label) => getIdFromGraphQLId(label.id)),
labels,
});
},
handleLabelRemove(removeLabelId) {
this.setActiveBoardItemLabels({
iid: this.activeBoardItem.iid,
groupPath: this.fullPath,
removeLabelIds: [removeLabelId],
});
},
}, },
}; };
</script> </script>
...@@ -86,7 +112,25 @@ export default { ...@@ -86,7 +112,25 @@ export default {
:issuable-type="issuableType" :issuable-type="issuableType"
:can-inherit="true" :can-inherit="true"
/> />
<board-sidebar-labels-select class="labels" /> <sidebar-labels-widget
v-if="glFeatures.labelsWidget"
class="block labels"
data-testid="sidebar-labels"
:iid="activeBoardItem.iid"
:full-path="fullPath"
:allow-label-remove="canUpdate"
:allow-multiselect="true"
:labels-filter-base-path="labelsFilterBasePath"
:attr-workspace-path="fullPath"
workspace-type="group"
:issuable-type="issuableType"
label-create-type="group"
@onLabelRemove="handleLabelRemove"
@updateSelectedLabels="handleUpdateSelectedLabels"
>
{{ __('None') }}
</sidebar-labels-widget>
<board-sidebar-labels-select v-else class="labels" />
<sidebar-confidentiality-widget <sidebar-confidentiality-widget
:iid="activeBoardItem.iid" :iid="activeBoardItem.iid"
:full-path="fullPath" :full-path="fullPath"
......
...@@ -8,6 +8,7 @@ import { ...@@ -8,6 +8,7 @@ import {
GlFormInput, GlFormInput,
} from '@gitlab/ui'; } from '@gitlab/ui';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
...@@ -113,7 +114,7 @@ export default { ...@@ -113,7 +114,7 @@ export default {
}, },
handleUpdateSelectedLabels(labels) { handleUpdateSelectedLabels(labels) {
if (this.glFeatures.labelsWidget) { if (this.glFeatures.labelsWidget) {
this.labels = labels; this.labels = labels.map((label) => ({ ...label, id: getIdFromGraphQLId(label.id) }));
return; return;
} }
......
...@@ -97,6 +97,9 @@ function mountBoardApp(el) { ...@@ -97,6 +97,9 @@ function mountBoardApp(el) {
iterationListsAvailable: false, iterationListsAvailable: false,
issuableType: issuableTypes.epic, issuableType: issuableTypes.epic,
emailsDisabled: parseBoolean(el.dataset.emailsDisabled), emailsDisabled: parseBoolean(el.dataset.emailsDisabled),
allowLabelCreate: parseBoolean(el.dataset.canUpdate),
allowLabelEdit: parseBoolean(el.dataset.canUpdate),
allowScopedLabels: parseBoolean(el.dataset.scopedLabels),
}, },
render: (createComponent) => createComponent(BoardApp), render: (createComponent) => createComponent(BoardApp),
}); });
......
...@@ -8,6 +8,9 @@ class Groups::EpicBoardsController < Groups::ApplicationController ...@@ -8,6 +8,9 @@ class Groups::EpicBoardsController < Groups::ApplicationController
before_action :redirect_to_recent_board, only: [:index] before_action :redirect_to_recent_board, only: [:index]
before_action :assign_endpoint_vars before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:labels_widget, group, default_enabled: :yaml)
end
track_redis_hll_event :index, :show, name: 'g_project_management_users_viewing_epic_boards' track_redis_hll_event :index, :show, name: 'g_project_management_users_viewing_epic_boards'
......
...@@ -71,6 +71,13 @@ module EE ...@@ -71,6 +71,13 @@ module EE
super super
end end
override :build_issue_link_base
def build_issue_link_base
return group_epics_path(@group) if board.is_a?(::Boards::EpicBoard)
super
end
override :board_base_url override :board_base_url
def board_base_url def board_base_url
return group_epic_boards_url(@group) if board.is_a?(::Boards::EpicBoard) return group_epic_boards_url(@group) if board.is_a?(::Boards::EpicBoard)
......
...@@ -161,9 +161,9 @@ RSpec.describe 'Epic boards sidebar', :js do ...@@ -161,9 +161,9 @@ RSpec.describe 'Epic boards sidebar', :js do
wait_for_requests wait_for_requests
click_link bug.title click_on bug.title
find('[data-testid="close-icon"]').click click_button 'Close'
wait_for_requests wait_for_requests
......
...@@ -15,6 +15,8 @@ describe('ee/BoardContent', () => { ...@@ -15,6 +15,8 @@ describe('ee/BoardContent', () => {
provide: { provide: {
timeTrackingLimitToHours: false, timeTrackingLimitToHours: false,
canAdminList: false, canAdminList: false,
canUpdate: false,
labelsFilterBasePath: '',
}, },
propsData: { propsData: {
lists: [], lists: [],
......
...@@ -45,6 +45,7 @@ describe('EpicBoardContentSidebar', () => { ...@@ -45,6 +45,7 @@ describe('EpicBoardContentSidebar', () => {
canUpdate: true, canUpdate: true,
rootPath: '/', rootPath: '/',
groupId: 1, groupId: 1,
labelsFilterBasePath: '',
}, },
store, store,
stubs: { stubs: {
......
...@@ -10,7 +10,6 @@ import VueApollo from 'vue-apollo'; ...@@ -10,7 +10,6 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { IssuableType } from '~/issue_show/constants';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants'; import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue'; import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue';
...@@ -57,7 +56,6 @@ describe('DropdownContentsLabelsView', () => { ...@@ -57,7 +56,6 @@ describe('DropdownContentsLabelsView', () => {
propsData: { propsData: {
...initialState, ...initialState,
localSelectedLabels, localSelectedLabels,
issuableType: IssuableType.Issue,
searchKey, searchKey,
labelCreateType: 'project', labelCreateType: 'project',
workspaceType: 'project', workspaceType: 'project',
......
...@@ -39,7 +39,6 @@ describe('DropdownContent', () => { ...@@ -39,7 +39,6 @@ describe('DropdownContent', () => {
footerManageLabelTitle: 'manage', footerManageLabelTitle: 'manage',
dropdownButtonText: 'Labels', dropdownButtonText: 'Labels',
variant: 'sidebar', variant: 'sidebar',
issuableType: 'issue',
fullPath: 'test', fullPath: 'test',
workspaceType: 'project', workspaceType: 'project',
labelCreateType: 'project', labelCreateType: 'project',
......
...@@ -23,14 +23,14 @@ RSpec.describe BoardsHelper do ...@@ -23,14 +23,14 @@ RSpec.describe BoardsHelper do
it 'returns correct path for base group' do it 'returns correct path for base group' do
assign(:board, group_board) assign(:board, group_board)
expect(helper.build_issue_link_base).to eq('/base/:project_path/issues') expect(helper.build_issue_link_base).to eq('/:project_path/-/issues')
end end
it 'returns correct path for subgroup' do it 'returns correct path for subgroup' do
subgroup = create(:group, parent: base_group, path: 'sub') subgroup = create(:group, parent: base_group, path: 'sub')
assign(:board, create(:board, group: subgroup)) assign(:board, create(:board, group: subgroup))
expect(helper.build_issue_link_base).to eq('/base/sub/:project_path/issues') expect(helper.build_issue_link_base).to eq('/:project_path/-/issues')
end end
end end
end end
...@@ -149,7 +149,7 @@ RSpec.describe BoardsHelper do ...@@ -149,7 +149,7 @@ RSpec.describe BoardsHelper do
end end
it 'returns correct path for base group' do it 'returns correct path for base group' do
expect(helper.build_issue_link_base).to eq("/#{base_group.full_path}/:project_path/issues") expect(helper.build_issue_link_base).to eq("/:project_path/-/issues")
end end
it 'returns required label endpoints' do it 'returns required label endpoints' do
......
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