Commit 74f002a1 authored by Eulyeon Ko's avatar Eulyeon Ko Committed by Scott Hampton

Provide `can_admin_list` to the boards app

- Include can_admin_list in the data returned by
boards_helper

- Add missing spec coverage for remove list button
(board_settings_sidebar_spec.js)

Fix trailing whitespace

Provide can_admin_list for epic boards
parent 77883c43
...@@ -22,16 +22,13 @@ export default { ...@@ -22,16 +22,13 @@ export default {
GlAlert, GlAlert,
}, },
mixins: [glFeatureFlagMixin()], mixins: [glFeatureFlagMixin()],
inject: ['canAdminList'],
props: { props: {
lists: { lists: {
type: Array, type: Array,
required: false, required: false,
default: () => [], default: () => [],
}, },
canAdminList: {
type: Boolean,
required: true,
},
disabled: { disabled: {
type: Boolean, type: Boolean,
required: true, required: true,
...@@ -99,7 +96,7 @@ export default { ...@@ -99,7 +96,7 @@ export default {
</script> </script>
<template> <template>
<div> <div v-cloak data-qa-selector="boards_list">
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError"> <gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError">
{{ error }} {{ error }}
</gl-alert> </gl-alert>
......
...@@ -22,13 +22,7 @@ export default { ...@@ -22,13 +22,7 @@ export default {
import('ee_component/boards/components/board_settings_list_types.vue'), import('ee_component/boards/components/board_settings_list_types.vue'),
}, },
mixins: [glFeatureFlagMixin()], mixins: [glFeatureFlagMixin()],
props: { inject: ['canAdminList'],
canAdminList: {
type: Boolean,
required: false,
default: false,
},
},
data() { data() {
return { return {
ListType, ListType,
......
...@@ -81,6 +81,7 @@ export default () => { ...@@ -81,6 +81,7 @@ export default () => {
rootPath: $boardApp.dataset.rootPath, rootPath: $boardApp.dataset.rootPath,
currentUserId: gon.current_user_id || null, currentUserId: gon.current_user_id || null,
canUpdate: parseBoolean($boardApp.dataset.canUpdate), canUpdate: parseBoolean($boardApp.dataset.canUpdate),
canAdminList: parseBoolean($boardApp.dataset.canAdminList),
labelsFetchPath: $boardApp.dataset.labelsFetchPath, labelsFetchPath: $boardApp.dataset.labelsFetchPath,
labelsManagePath: $boardApp.dataset.labelsManagePath, labelsManagePath: $boardApp.dataset.labelsManagePath,
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath, labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,
......
...@@ -14,7 +14,8 @@ module BoardsHelper ...@@ -14,7 +14,8 @@ module BoardsHelper
root_path: root_path, root_path: root_path,
full_path: full_path, full_path: full_path,
bulk_update_path: @bulk_issues_path, bulk_update_path: @bulk_issues_path,
can_update: (!!can?(current_user, :admin_issue, board)).to_s, can_update: can_update?.to_s,
can_admin_list: can_admin_list?.to_s,
time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s, time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s,
recent_boards_endpoint: recent_boards_path, recent_boards_endpoint: recent_boards_path,
parent: current_board_parent.model_name.param_key, parent: current_board_parent.model_name.param_key,
...@@ -88,6 +89,14 @@ module BoardsHelper ...@@ -88,6 +89,14 @@ module BoardsHelper
@current_board_parent ||= @group || @project @current_board_parent ||= @group || @project
end end
def can_update?
can?(current_user, :admin_issue, board)
end
def can_admin_list?
can?(current_user, :admin_issue_board_list, current_board_parent)
end
def can_admin_issue? def can_admin_issue?
can?(current_user, :admin_issue, current_board_parent) can?(current_user, :admin_issue, current_board_parent)
end end
......
- board = local_assigns.fetch(:board, nil) - board = local_assigns.fetch(:board, nil)
- group = local_assigns.fetch(:group, false) - group = local_assigns.fetch(:group, false)
-# TODO: Move group_id and can_admin_list to the board store
See: https://gitlab.com/gitlab-org/gitlab/-/issues/213082
- can_admin_list = can?(current_user, :admin_issue_board_list, current_board_parent) == true
- @no_breadcrumb_container = true - @no_breadcrumb_container = true
- @no_container = true - @no_container = true
- @content_class = "issue-boards-content js-focus-mode-board" - @content_class = "issue-boards-content js-focus-mode-board"
...@@ -15,11 +12,6 @@ ...@@ -15,11 +12,6 @@
= render 'shared/issuable/search_bar', type: :boards, board: board = render 'shared/issuable/search_bar', type: :boards, board: board
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" } #board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
%board-content{ "v-cloak" => "true", %board-content{ ":lists" => "state.lists", ":disabled" => "disabled" }
"ref" => "board_content",
":lists" => "state.lists",
":can-admin-list" => can_admin_list,
":disabled" => "disabled",
data: { qa_selector: "boards_list" } }
= render "shared/boards/components/sidebar", group: group = render "shared/boards/components/sidebar", group: group
%board-settings-sidebar{ ":can-admin-list" => can_admin_list } %board-settings-sidebar
...@@ -63,6 +63,7 @@ export default () => { ...@@ -63,6 +63,7 @@ export default () => {
rootPath: $boardApp.dataset.rootPath, rootPath: $boardApp.dataset.rootPath,
currentUserId: gon.current_user_id || null, currentUserId: gon.current_user_id || null,
canUpdate: $boardApp.dataset.canUpdate, canUpdate: $boardApp.dataset.canUpdate,
canAdminList: parseBoolean($boardApp.dataset.canAdminList),
labelsFetchPath: $boardApp.dataset.labelsFetchPath, labelsFetchPath: $boardApp.dataset.labelsFetchPath,
labelsManagePath: $boardApp.dataset.labelsManagePath, labelsManagePath: $boardApp.dataset.labelsManagePath,
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath, labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,
......
...@@ -30,12 +30,28 @@ module EE ...@@ -30,12 +30,28 @@ module EE
assignee_lists_available: current_board_parent.feature_available?(:board_assignee_lists).to_s, assignee_lists_available: current_board_parent.feature_available?(:board_assignee_lists).to_s,
iteration_lists_available: current_board_parent.feature_available?(:board_iteration_lists).to_s, iteration_lists_available: current_board_parent.feature_available?(:board_iteration_lists).to_s,
show_promotion: show_feature_promotion, show_promotion: show_feature_promotion,
scoped_labels: current_board_parent.feature_available?(:scoped_labels)&.to_s scoped_labels: current_board_parent.feature_available?(:scoped_labels)&.to_s,
can_update: can_update?.to_s,
can_admin_list: can_admin_list?.to_s
} }
super.merge(data) super.merge(data)
end end
override :can_update?
def can_update?
return can?(current_user, :admin_epic, board) if board.is_a?(::Boards::EpicBoard)
super
end
override :can_admin_list?
def can_admin_list?
return can?(current_user, :admin_epic_board_list, current_board_parent) 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)
......
...@@ -13,10 +13,10 @@ describe('ee/BoardContent', () => { ...@@ -13,10 +13,10 @@ describe('ee/BoardContent', () => {
store, store,
provide: { provide: {
timeTrackingLimitToHours: false, timeTrackingLimitToHours: false,
canAdminList: false,
}, },
propsData: { propsData: {
lists: [], lists: [],
canAdminList: false,
disabled: false, disabled: false,
}, },
stubs: { stubs: {
......
...@@ -38,6 +38,7 @@ describe('ee/BoardSettingsSidebar', () => { ...@@ -38,6 +38,7 @@ describe('ee/BoardSettingsSidebar', () => {
glFeatures: { glFeatures: {
wipLimits: isWipLimitsOn, wipLimits: isWipLimitsOn,
}, },
canAdminList: false,
}, },
stubs: { stubs: {
'board-settings-sidebar-wip-limit': BoardSettingsWipLimit, 'board-settings-sidebar-wip-limit': BoardSettingsWipLimit,
......
...@@ -50,31 +50,61 @@ RSpec.describe BoardsHelper do ...@@ -50,31 +50,61 @@ RSpec.describe BoardsHelper do
let(:board_data) { helper.board_data } let(:board_data) { helper.board_data }
before do before do
assign(:board, project_board)
assign(:project, project)
allow(helper).to receive(:current_user) { user } allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true)
end end
context 'when no iteration', :aggregate_failures do context 'issue board' do
it 'serializes board without iteration' do before do
expect(board_data[:board_iteration_title]).to be_nil assign(:board, project_board)
expect(board_data[:board_iteration_id]).to be_nil assign(:project, project)
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(false)
end
context 'when no iteration', :aggregate_failures do
it 'serializes board without iteration' do
expect(board_data[:board_iteration_title]).to be_nil
expect(board_data[:board_iteration_id]).to be_nil
end
end
context 'when board is scoped to an iteration' do
let_it_be(:iteration) { create(:iteration, group: group) }
before do
project_board.update!(iteration: iteration)
end
it 'serializes board with iteration' do
expect(board_data[:board_iteration_title]).to eq(iteration.title)
expect(board_data[:board_iteration_id]).to eq(iteration.id)
end
end end
end end
context 'when board is scoped to an iteration' do context 'epic board' do
let_it_be(:iteration) { create(:iteration, group: group) } let_it_be(:epic_board) { create(:epic_board, group: group) }
before do before do
project_board.update!(iteration: iteration) assign(:board, epic_board)
assign(:group, group)
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, epic_board).and_return(false)
allow(helper).to receive(:can?).with(user, :admin_epic, epic_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_epic_board_list, group).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, group).and_return(false)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, group).and_return(false)
end
it 'returns the correct permission for updating the board' do
expect(board_data[:can_update]).to eq "true"
end end
it 'serializes board with iteration' do it 'returns the correct permission for administering the boards lists' do
expect(board_data[:board_iteration_title]).to eq(iteration.title) expect(board_data[:can_admin_list]).to eq "true"
expect(board_data[:board_iteration_id]).to eq(iteration.id)
end end
end end
end end
......
...@@ -32,7 +32,7 @@ module QA ...@@ -32,7 +32,7 @@ module QA
element :labels_edit_button element :labels_edit_button
end end
view 'app/views/shared/boards/_show.html.haml' do view 'app/assets/javascripts/boards/components/board_content.vue' do
element :boards_list element :boards_list
end end
......
...@@ -33,7 +33,12 @@ describe('BoardContent', () => { ...@@ -33,7 +33,12 @@ describe('BoardContent', () => {
}); });
}; };
const createComponent = ({ state, props = {}, graphqlBoardListsEnabled = false } = {}) => { const createComponent = ({
state,
props = {},
graphqlBoardListsEnabled = false,
canAdminList = true,
} = {}) => {
const store = createStore({ const store = createStore({
...defaultState, ...defaultState,
...state, ...state,
...@@ -42,11 +47,11 @@ describe('BoardContent', () => { ...@@ -42,11 +47,11 @@ describe('BoardContent', () => {
localVue, localVue,
propsData: { propsData: {
lists: mockListsWithModel, lists: mockListsWithModel,
canAdminList: true,
disabled: false, disabled: false,
...props, ...props,
}, },
provide: { provide: {
canAdminList,
glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled }, glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled },
}, },
store, store,
...@@ -82,7 +87,7 @@ describe('BoardContent', () => { ...@@ -82,7 +87,7 @@ describe('BoardContent', () => {
describe('can admin list', () => { describe('can admin list', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: true } }); createComponent({ graphqlBoardListsEnabled: true, canAdminList: true });
}); });
it('renders draggable component', () => { it('renders draggable component', () => {
...@@ -92,7 +97,7 @@ describe('BoardContent', () => { ...@@ -92,7 +97,7 @@ describe('BoardContent', () => {
describe('can not admin list', () => { describe('can not admin list', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: false } }); createComponent({ graphqlBoardListsEnabled: true, canAdminList: false });
}); });
it('does not render draggable component', () => { it('does not render draggable component', () => {
......
...@@ -4,6 +4,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; ...@@ -4,6 +4,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue'; import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
import { inactiveId, LIST } from '~/boards/constants'; import { inactiveId, LIST } from '~/boards/constants';
import { createStore } from '~/boards/stores'; import { createStore } from '~/boards/stores';
...@@ -22,11 +23,18 @@ describe('BoardSettingsSidebar', () => { ...@@ -22,11 +23,18 @@ describe('BoardSettingsSidebar', () => {
const labelColor = '#FFFF'; const labelColor = '#FFFF';
const listId = 1; const listId = 1;
const createComponent = () => { const findRemoveButton = () => wrapper.findByTestId('remove-list');
wrapper = shallowMount(BoardSettingsSidebar, {
store, const createComponent = ({ canAdminList = false } = {}) => {
localVue, wrapper = extendedWrapper(
}); shallowMount(BoardSettingsSidebar, {
store,
localVue,
provide: {
canAdminList,
},
}),
);
}; };
const findLabel = () => wrapper.find(GlLabel); const findLabel = () => wrapper.find(GlLabel);
const findDrawer = () => wrapper.find(GlDrawer); const findDrawer = () => wrapper.find(GlDrawer);
...@@ -164,4 +172,29 @@ describe('BoardSettingsSidebar', () => { ...@@ -164,4 +172,29 @@ describe('BoardSettingsSidebar', () => {
expect(findDrawer().exists()).toBe(false); expect(findDrawer().exists()).toBe(false);
}); });
}); });
it('does not render "Remove list" when user cannot admin the boards list', () => {
createComponent();
expect(findRemoveButton().exists()).toBe(false);
});
describe('when user can admin the boards list', () => {
beforeEach(() => {
store.state.activeId = listId;
store.state.sidebarType = LIST;
boardsStore.addList({
id: listId,
label: { title: labelTitle, color: labelColor },
list_type: 'label',
});
createComponent({ canAdminList: true });
});
it('renders "Remove list" button', () => {
expect(findRemoveButton().exists()).toBe(true);
});
});
}); });
...@@ -64,6 +64,7 @@ RSpec.describe BoardsHelper do ...@@ -64,6 +64,7 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:current_user) { user } allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true) allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true) allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(false)
end end
it 'returns a board_lists_path as lists_endpoint' do it 'returns a board_lists_path as lists_endpoint' do
...@@ -86,6 +87,17 @@ RSpec.describe BoardsHelper do ...@@ -86,6 +87,17 @@ RSpec.describe BoardsHelper do
it 'returns the group id of a project' do it 'returns the group id of a project' do
expect(helper.board_data[:group_id]).to eq(project.group.id) expect(helper.board_data[:group_id]).to eq(project.group.id)
end end
context 'can_admin_list' do
it 'returns can_admin_list as false by default' do
expect(helper.board_data[:can_admin_list]).to eq('false')
end
it 'returns can_admin_list as true when user can admin the board' do
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(true)
expect(helper.board_data[:can_admin_list]).to eq('true')
end
end
end end
context 'group board' do context 'group board' do
...@@ -96,6 +108,7 @@ RSpec.describe BoardsHelper do ...@@ -96,6 +108,7 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:current_user) { user } allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, group_board).and_return(true) allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, group_board).and_return(true) allow(helper).to receive(:can?).with(user, :admin_issue, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(false)
end end
it 'returns correct path for base group' do it 'returns correct path for base group' do
...@@ -110,6 +123,17 @@ RSpec.describe BoardsHelper do ...@@ -110,6 +123,17 @@ RSpec.describe BoardsHelper do
it 'returns the group id' do it 'returns the group id' do
expect(helper.board_data[:group_id]).to eq(base_group.id) expect(helper.board_data[:group_id]).to eq(base_group.id)
end end
context 'can_admin_list' do
it 'returns can_admin_list as false by default' do
expect(helper.board_data[:can_admin_list]).to eq('false')
end
it 'returns can_admin_list as true when user can admin the board' do
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(true)
expect(helper.board_data[:can_admin_list]).to eq('true')
end
end
end end
end end
......
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