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 {
GlAlert,
},
mixins: [glFeatureFlagMixin()],
inject: ['canAdminList'],
props: {
lists: {
type: Array,
required: false,
default: () => [],
},
canAdminList: {
type: Boolean,
required: true,
},
disabled: {
type: Boolean,
required: true,
......@@ -99,7 +96,7 @@ export default {
</script>
<template>
<div>
<div v-cloak data-qa-selector="boards_list">
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError">
{{ error }}
</gl-alert>
......
......@@ -22,13 +22,7 @@ export default {
import('ee_component/boards/components/board_settings_list_types.vue'),
},
mixins: [glFeatureFlagMixin()],
props: {
canAdminList: {
type: Boolean,
required: false,
default: false,
},
},
inject: ['canAdminList'],
data() {
return {
ListType,
......
......@@ -81,6 +81,7 @@ export default () => {
rootPath: $boardApp.dataset.rootPath,
currentUserId: gon.current_user_id || null,
canUpdate: parseBoolean($boardApp.dataset.canUpdate),
canAdminList: parseBoolean($boardApp.dataset.canAdminList),
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
labelsManagePath: $boardApp.dataset.labelsManagePath,
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,
......
......@@ -14,7 +14,8 @@ module BoardsHelper
root_path: root_path,
full_path: full_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,
recent_boards_endpoint: recent_boards_path,
parent: current_board_parent.model_name.param_key,
......@@ -88,6 +89,14 @@ module BoardsHelper
@current_board_parent ||= @group || @project
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?
can?(current_user, :admin_issue, current_board_parent)
end
......
- board = local_assigns.fetch(:board, nil)
- 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_container = true
- @content_class = "issue-boards-content js-focus-mode-board"
......@@ -15,11 +12,6 @@
= 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-content{ "v-cloak" => "true",
"ref" => "board_content",
":lists" => "state.lists",
":can-admin-list" => can_admin_list,
":disabled" => "disabled",
data: { qa_selector: "boards_list" } }
%board-content{ ":lists" => "state.lists", ":disabled" => "disabled" }
= 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 () => {
rootPath: $boardApp.dataset.rootPath,
currentUserId: gon.current_user_id || null,
canUpdate: $boardApp.dataset.canUpdate,
canAdminList: parseBoolean($boardApp.dataset.canAdminList),
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
labelsManagePath: $boardApp.dataset.labelsManagePath,
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,
......
......@@ -30,12 +30,28 @@ module EE
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,
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)
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
def board_base_url
return group_epic_boards_url(@group) if board.is_a?(::Boards::EpicBoard)
......
......@@ -13,10 +13,10 @@ describe('ee/BoardContent', () => {
store,
provide: {
timeTrackingLimitToHours: false,
canAdminList: false,
},
propsData: {
lists: [],
canAdminList: false,
disabled: false,
},
stubs: {
......
......@@ -38,6 +38,7 @@ describe('ee/BoardSettingsSidebar', () => {
glFeatures: {
wipLimits: isWipLimitsOn,
},
canAdminList: false,
},
stubs: {
'board-settings-sidebar-wip-limit': BoardSettingsWipLimit,
......
......@@ -50,31 +50,61 @@ RSpec.describe BoardsHelper do
let(:board_data) { helper.board_data }
before do
assign(:board, project_board)
assign(:project, project)
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
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
context 'issue board' do
before do
assign(:board, project_board)
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
context 'when board is scoped to an iteration' do
let_it_be(:iteration) { create(:iteration, group: group) }
context 'epic board' do
let_it_be(:epic_board) { create(:epic_board, group: group) }
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
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)
it 'returns the correct permission for administering the boards lists' do
expect(board_data[:can_admin_list]).to eq "true"
end
end
end
......
......@@ -32,7 +32,7 @@ module QA
element :labels_edit_button
end
view 'app/views/shared/boards/_show.html.haml' do
view 'app/assets/javascripts/boards/components/board_content.vue' do
element :boards_list
end
......
......@@ -33,7 +33,12 @@ describe('BoardContent', () => {
});
};
const createComponent = ({ state, props = {}, graphqlBoardListsEnabled = false } = {}) => {
const createComponent = ({
state,
props = {},
graphqlBoardListsEnabled = false,
canAdminList = true,
} = {}) => {
const store = createStore({
...defaultState,
...state,
......@@ -42,11 +47,11 @@ describe('BoardContent', () => {
localVue,
propsData: {
lists: mockListsWithModel,
canAdminList: true,
disabled: false,
...props,
},
provide: {
canAdminList,
glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled },
},
store,
......@@ -82,7 +87,7 @@ describe('BoardContent', () => {
describe('can admin list', () => {
beforeEach(() => {
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: true } });
createComponent({ graphqlBoardListsEnabled: true, canAdminList: true });
});
it('renders draggable component', () => {
......@@ -92,7 +97,7 @@ describe('BoardContent', () => {
describe('can not admin list', () => {
beforeEach(() => {
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: false } });
createComponent({ graphqlBoardListsEnabled: true, canAdminList: false });
});
it('does not render draggable component', () => {
......
......@@ -4,6 +4,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
import { inactiveId, LIST } from '~/boards/constants';
import { createStore } from '~/boards/stores';
......@@ -22,11 +23,18 @@ describe('BoardSettingsSidebar', () => {
const labelColor = '#FFFF';
const listId = 1;
const createComponent = () => {
wrapper = shallowMount(BoardSettingsSidebar, {
store,
localVue,
});
const findRemoveButton = () => wrapper.findByTestId('remove-list');
const createComponent = ({ canAdminList = false } = {}) => {
wrapper = extendedWrapper(
shallowMount(BoardSettingsSidebar, {
store,
localVue,
provide: {
canAdminList,
},
}),
);
};
const findLabel = () => wrapper.find(GlLabel);
const findDrawer = () => wrapper.find(GlDrawer);
......@@ -164,4 +172,29 @@ describe('BoardSettingsSidebar', () => {
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
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)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(false)
end
it 'returns a board_lists_path as lists_endpoint' do
......@@ -86,6 +87,17 @@ RSpec.describe BoardsHelper do
it 'returns the group id of a project' do
expect(helper.board_data[:group_id]).to eq(project.group.id)
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
context 'group board' do
......@@ -96,6 +108,7 @@ RSpec.describe BoardsHelper do
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, :admin_issue, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(false)
end
it 'returns correct path for base group' do
......@@ -110,6 +123,17 @@ RSpec.describe BoardsHelper do
it 'returns the group id' do
expect(helper.board_data[:group_id]).to eq(base_group.id)
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
......
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