Skip to content
Snippets Groups Projects
Verified Commit 51f6b11d authored by Jacques Erasmus's avatar Jacques Erasmus :speech_balloon: Committed by GitLab
Browse files

Merge branch '451024-add-description-column-for-custom-roles' into 'master'

Add description column for custom roles

See merge request !147620



Merged-by: default avatarJacques Erasmus <jerasmus@gitlab.com>
Approved-by: default avatarIlonah Pelaez <ipelaez@gitlab.com>
Approved-by: default avatarRudy Crespo <rcrespo@gitlab.com>
Approved-by: default avatarJacques Erasmus <jerasmus@gitlab.com>
Reviewed-by: default avatarRudy Crespo <rcrespo@gitlab.com>
Reviewed-by: default avatarIlonah Pelaez <ipelaez@gitlab.com>
Co-authored-by: default avatarDaniel Tian <dtian@gitlab.com>
parents 847c29a8 986418ba
No related branches found
No related tags found
1 merge request!147620Add description column for custom roles
Pipeline #1236602064 passed
......@@ -218,8 +218,12 @@ export default {
/>
</gl-form-group>
<gl-form-group class="col-lg-8" :label="$options.i18n.descriptionLabel">
<gl-form-textarea v-model="description" />
<gl-form-group
class="col-lg-8"
:label="$options.i18n.descriptionLabel"
label-for="description"
>
<gl-form-textarea id="description" v-model="description" />
</gl-form-group>
</div>
......
......@@ -6,7 +6,6 @@ import { TABLE_FIELDS } from '../constants';
import CustomRolesActions from './custom_roles_actions.vue';
export default {
name: 'CustomRolesTable',
components: {
GlTable,
CustomRolesActions,
......@@ -33,6 +32,11 @@ export default {
{{ $options.getIdFromGraphQLId(id) }}
</template>
<template #cell(description)="{ item: { description } }">
<template v-if="description">{{ description }}</template>
<span v-else class="gl-text-gray-400">{{ s__('MemberRole|No description') }}</span>
</template>
<template #cell(baseRole)="{ item: { baseAccessLevel } }">
{{ getBaseRoleName(baseAccessLevel) }}
</template>
......
......@@ -19,8 +19,9 @@ import deleteMemberRoleMutation from '../graphql/delete_member_role.mutation.gra
import CreateMemberRole from './create_member_role.vue';
export const FIELDS = [
{ key: 'name', label: s__('MemberRole|Name'), sortable: true },
{ key: 'id', label: s__('MemberRole|ID'), sortable: true },
{ key: 'name', label: s__('MemberRole|Name'), sortable: true },
{ key: 'description', label: s__('MemberRole|Description') },
{ key: 'baseAccessLevel', label: s__('MemberRole|Base role'), sortable: true },
{ key: 'permissions', label: s__('MemberRole|Permissions') },
{
......@@ -57,6 +58,7 @@ export default {
'MemberRole|To delete custom role, remove role from all group members.',
),
createSuccess: s__('MemberRole|Role successfully created.'),
noDescription: s__('MemberRole|No description'),
},
components: {
CreateMemberRole,
......@@ -104,10 +106,10 @@ export default {
const memberRoles = nodes || [];
return memberRoles.map((member) => ({
return memberRoles.map(({ baseAccessLevel, enabledPermissions, ...member }) => ({
...member,
baseAccessLevel: ACCESS_LEVEL_LABELS[member.baseAccessLevel.integerValue],
permissions: member.enabledPermissions.nodes,
baseAccessLevel: ACCESS_LEVEL_LABELS[baseAccessLevel.integerValue],
permissions: enabledPermissions.nodes,
}));
},
error() {
......@@ -236,6 +238,10 @@ export default {
<template #cell(id)="{ item }">
{{ $options.getIdFromGraphQLId(item.id) }}
</template>
<template #cell(description)="{ item: { description } }">
<template v-if="description">{{ description }}</template>
<span v-else class="gl-text-gray-400">{{ $options.i18n.noDescription }}</span>
</template>
<template #cell(baseAccessLevel)="{ item: { baseAccessLevel } }">
<gl-badge class="gl-my-n4">{{ baseAccessLevel }}</gl-badge>
</template>
......
......@@ -6,6 +6,7 @@
let_it_be(:admin) { create(:admin) }
let(:name) { 'My custom role' }
let(:description) { 'My role description' }
let(:permissions) { { read_vulnerability: { name: 'read_vulnerability' } } }
let(:permission) { :read_vulnerability }
let(:permission_name) { permission.to_s.humanize }
......@@ -15,18 +16,19 @@
stub_licensed_features(custom_roles: true)
end
def create_role(access_level, name, permissions)
def create_role(access_level, name, description, permissions)
click_button 'New role'
select access_level, from: 'Base role to use as template'
fill_in 'Role name', with: name
fill_in 'Description', with: description
permissions.each do |permission|
page.check permission
end
click_button 'Create role'
end
def created_role(name, id, access_level, permissions)
[name, id, access_level, *permissions].join(' ')
def created_role(id, name, description, access_level, permissions)
[id, name, description, access_level, *permissions].join(' ')
end
describe 'adding a new custom role', :enable_admin_mode do
......@@ -38,14 +40,14 @@ def created_role(name, id, access_level, permissions)
shared_examples 'creates a new custom role' do
it 'and displays it' do
create_role(access_level, name, [permission_name])
create_role(access_level, name, description, [permission_name])
created_member_role = MemberRole.permissions_where(permission => true)
.find_by(name: name, base_access_level: Gitlab::Access.options[access_level])
expect(created_member_role).not_to be_nil
role = created_role(name, created_member_role.id, access_level, [permission_name])
role = created_role(created_member_role.id, name, description, access_level, [permission_name])
expect(page).to have_content(role)
end
end
......
......@@ -7,6 +7,7 @@
let_it_be(:group) { create(:group) }
let(:name) { 'My custom role' }
let(:description) { 'My role description' }
let(:permissions) { { read_vulnerability: { name: 'read_vulnerability' } } }
let(:permission) { :read_vulnerability }
let(:permission_name) { permission.to_s.humanize }
......@@ -20,18 +21,19 @@
stub_licensed_features(custom_roles: true)
end
def create_role(access_level, name, permissions)
def create_role(access_level, name, description, permissions)
click_button 'New role'
select access_level, from: 'Base role to use as template'
fill_in 'Role name', with: name
fill_in 'Description', with: description
permissions.each do |permission|
page.check permission
end
click_button 'Create role'
end
def created_role(name, id, access_level, permissions)
[name, id, access_level, *permissions].join(' ')
def created_role(id, name, description, access_level, permissions)
[id, name, description, access_level, *permissions].join(' ')
end
describe 'adding a new custom role' do
......@@ -43,14 +45,14 @@ def created_role(name, id, access_level, permissions)
shared_examples 'creates a new custom role' do
it 'and displays it' do
create_role(access_level, name, [permission_name])
create_role(access_level, name, description, [permission_name])
created_member_role = MemberRole.permissions_where(permission => true)
.find_by(name: name, base_access_level: Gitlab::Access.options[access_level])
expect(created_member_role).not_to be_nil
role = created_role(name, created_member_role.id, access_level, [permission_name])
role = created_role(created_member_role.id, name, description, access_level, [permission_name])
expect(page).to have_content(role)
end
end
......@@ -88,7 +90,7 @@ def created_role(name, id, access_level, permissions)
end
it 'shows an error message' do
create_role(access_level, name, [permission_name])
create_role(access_level, name, description, [permission_name])
expect(page).to have_content('Failed to create role')
end
......
......@@ -6,21 +6,18 @@ import { mockMemberRoles } from '../mock_data';
describe('CustomRolesTable', () => {
let wrapper;
const mockCustomRoles = mockMemberRoles.data.namespace.memberRoles.nodes;
const mockCustomRole = mockCustomRoles[0];
const customRoles = mockMemberRoles.data.namespace.memberRoles.nodes;
const createComponent = (props = {}) => {
const createComponent = () => {
wrapper = mountExtended(CustomRolesTable, {
propsData: {
customRoles: mockCustomRoles,
...props,
},
propsData: { customRoles },
});
};
const findHeaders = () => wrapper.find('thead').find('tr').findAll('th');
const findCells = () => wrapper.findAllByRole('cell');
const findActions = () => wrapper.findAllComponents(CustomRolesActions).at(0);
const findRowCell = ({ row = 0, cell }) =>
wrapper.findAll('tbody tr').at(row).findAll('td').at(cell);
const findActions = () => wrapper.findComponent(CustomRolesActions);
beforeEach(() => {
createComponent();
......@@ -38,35 +35,39 @@ describe('CustomRolesTable', () => {
});
it('renders the id', () => {
expect(findCells().at(0).text()).toContain('1');
expect(findRowCell({ cell: 0 }).text()).toContain('1');
});
it('renders the name', () => {
expect(findCells().at(1).text()).toContain('Test');
expect(findRowCell({ cell: 1 }).text()).toContain('Test');
});
it('renders the description', () => {
expect(findCells().at(2).text()).toContain('Test description');
});
it.each`
row | expectedDescription
${0} | ${'Test description'}
${1} | ${'No description'}
`(
'renders the description "$expectedDescription" for row $row',
({ row, expectedDescription }) => {
expect(findRowCell({ row, cell: 2 }).text()).toBe(expectedDescription);
},
);
it('renders the base access level', () => {
expect(findCells().at(3).text()).toContain('Reporter');
expect(findRowCell({ cell: 3 }).text()).toContain('Reporter');
});
it('renders the permissions', () => {
expect(findCells().at(4).text()).toContain('Read code');
expect(findCells().at(4).text()).toContain('Read vulnerability');
expect(findRowCell({ cell: 4 }).text()).toContain('Read code');
expect(findRowCell({ cell: 4 }).text()).toContain('Read vulnerability');
});
it('renders the member count', () => {
expect(findCells().at(5).text()).toContain('0');
expect(findRowCell({ cell: 5 }).text()).toContain('0');
});
it('renders the actions', () => {
expect(findActions().exists()).toBe(true);
expect(findCells().at(6).text()).toContain('Edit role');
expect(findCells().at(6).text()).toContain('Delete role');
});
});
......@@ -76,7 +77,7 @@ describe('CustomRolesTable', () => {
});
it('emits `delete-role` event', () => {
expect(wrapper.emitted('delete-role')).toEqual([[mockCustomRole]]);
expect(wrapper.emitted('delete-role')[0][0]).toBe(customRoles[0]);
});
});
});
......@@ -67,7 +67,8 @@ describe('ListMemberRoles', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findTable = () => wrapper.findComponent(GlTable);
const findCellByText = (text) => wrapper.findByRole('cell', { name: text });
const findCells = () => wrapper.findAllByRole('cell');
const findRowCell = ({ row = 0, cell }) =>
wrapper.findAll('tbody tr').at(row).findAll('td').at(cell);
const expectSortableColumn = (fieldKey) => {
const fields = findTable().props('fields');
......@@ -225,8 +226,19 @@ describe('ListMemberRoles', () => {
expectSortableColumn('baseAccessLevel');
});
it.each`
row | expectedDescription
${0} | ${'Test description'}
${1} | ${'No description'}
`(
'renders the description "$expectedDescription" for row $row',
({ row, expectedDescription }) => {
expect(findRowCell({ row, cell: 2 }).text()).toBe(expectedDescription);
},
);
it('shows list of permissions', () => {
const permissionsText = findCells().at(3).text();
const permissionsText = findRowCell({ cell: 4 }).text();
expect(permissionsText).toContain('Read code');
expect(permissionsText).toContain('Read vulnerability');
......
......@@ -63,7 +63,7 @@ export const mockMemberRoles = {
},
id: 'gid://gitlab/MemberRole/2',
name: 'Test 2',
description: 'Test description',
description: '',
membersCount: 1,
enabledPermissions: {
nodes: [
......
......@@ -31157,6 +31157,9 @@ msgstr ""
msgid "MemberRole|No custom roles found"
msgstr ""
 
msgid "MemberRole|No description"
msgstr ""
msgid "MemberRole|Permissions"
msgstr ""
 
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment