diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue b/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue index bfa46f54e771e84d773f06f4a8a5b275de69a9e6..a0ef1c02c30ec90790ad2710b2bd3aed8d2f4821 100644 --- a/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue +++ b/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue @@ -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> diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue b/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue index 48e3f21864e56bf5bb8e6e2071f5eb9aceacea71..bba9d8ec60195c00b457171af7bb9fd8989408d0 100644 --- a/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue +++ b/ee/app/assets/javascripts/roles_and_permissions/components/custom_roles_table.vue @@ -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> diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue b/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue index abd33aa31ec69880fa17b73f9b9932e1e5310a9e..22c61a34fc042ebba048bda0837ca82c481838d1 100644 --- a/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue +++ b/ee/app/assets/javascripts/roles_and_permissions/components/list_member_roles.vue @@ -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> diff --git a/ee/spec/features/admin/member_roles_spec.rb b/ee/spec/features/admin/member_roles_spec.rb index ab1afa28a7d80f1f26de29148ada43a623b1a0d9..633e898b67365fdeb08319fe9ba0d3eaca6d17e4 100644 --- a/ee/spec/features/admin/member_roles_spec.rb +++ b/ee/spec/features/admin/member_roles_spec.rb @@ -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 diff --git a/ee/spec/features/groups/member_roles_spec.rb b/ee/spec/features/groups/member_roles_spec.rb index e8a85f5c2fe5e96d43dea692160c4e5ecd2da415..636e229b66c93e78d74b79b3f2639a0a22cdbc52 100644 --- a/ee/spec/features/groups/member_roles_spec.rb +++ b/ee/spec/features/groups/member_roles_spec.rb @@ -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 diff --git a/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js b/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js index f0ba8e32fb89ebed3f114b9134103ae095055e55..38d2dab9b70e1bf126b201c358b4cee7c006984d 100644 --- a/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js +++ b/ee/spec/frontend/roles_and_permissions/components/custom_roles_table_spec.js @@ -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]); }); }); }); diff --git a/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js b/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js index e317c1d4a00cb7d28c817923c9cef9e7761a4681..02121b868f0942d02d6e1a57cf17d40d61f70856 100644 --- a/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js +++ b/ee/spec/frontend/roles_and_permissions/components/list_member_roles_spec.js @@ -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'); diff --git a/ee/spec/frontend/roles_and_permissions/mock_data.js b/ee/spec/frontend/roles_and_permissions/mock_data.js index ea076dd4fab2bb86cc0600c321da1888f0e8c54f..12adc01c019920cde778e58d207335a1ea3cca82 100644 --- a/ee/spec/frontend/roles_and_permissions/mock_data.js +++ b/ee/spec/frontend/roles_and_permissions/mock_data.js @@ -63,7 +63,7 @@ export const mockMemberRoles = { }, id: 'gid://gitlab/MemberRole/2', name: 'Test 2', - description: 'Test description', + description: '', membersCount: 1, enabledPermissions: { nodes: [ diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6889995bf840f94fbb4c3a4a6c7832810a841a75..1ee6afaef416f9df6ebd8be81c4f8b35e4dfca64 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31157,6 +31157,9 @@ msgstr "" msgid "MemberRole|No custom roles found" msgstr "" +msgid "MemberRole|No description" +msgstr "" + msgid "MemberRole|Permissions" msgstr ""