diff --git a/app/assets/javascripts/invite_members/components/invite_modal_base.vue b/app/assets/javascripts/invite_members/components/invite_modal_base.vue index 04dc1cd376d8a4968ac14df6c598c1b04bec2d17..5fef12f964f80fdf97a4d561ac34647bb377d36a 100644 --- a/app/assets/javascripts/invite_members/components/invite_modal_base.vue +++ b/app/assets/javascripts/invite_members/components/invite_modal_base.vue @@ -335,6 +335,15 @@ export default { :loading="isLoadingRoles" block > + <template #list-item="{ item }"> + <div :class="{ 'gl-font-weight-bold': item.memberRoleId }">{{ item.text }}</div> + <div + v-if="item.description" + class="gl-text-gray-700 gl-font-sm gl-pt-1 gl-line-clamp-2" + > + {{ item.description }} + </div> + </template> <template #footer> <manage-roles-dropdown-footer /> </template> diff --git a/app/assets/javascripts/members/components/table/max_role.vue b/app/assets/javascripts/members/components/table/max_role.vue index 92006e73678d78b0a242292f67d33a979d30b193..d6a798d37ce3d3a138208de65baa62a6caf032d8 100644 --- a/app/assets/javascripts/members/components/table/max_role.vue +++ b/app/assets/javascripts/members/components/table/max_role.vue @@ -1,5 +1,5 @@ <script> -import { GlBadge, GlCollapsibleListbox } from '@gitlab/ui'; +import { GlBadge, GlCollapsibleListbox, GlTooltipDirective } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; // eslint-disable-next-line no-restricted-imports import { mapActions } from 'vuex'; @@ -15,10 +15,12 @@ export default { GlBadge, LdapDropdownFooter: () => import('ee_component/members/components/action_dropdowns/ldap_dropdown_footer.vue'), - CustomPermissions: () => import('ee_component/members/components/table/custom_permissions.vue'), ManageRolesDropdownFooter: () => import('ee_component/members/components/action_dropdowns/manage_roles_dropdown_footer.vue'), }, + directives: { + GlTooltip: GlTooltipDirective, + }, inject: ['namespace', 'group'], props: { member: { @@ -98,6 +100,9 @@ export default { } }, }, + i18n: { + customRole: s__('MemberRole|Custom role'), + }, }; </script> @@ -115,7 +120,12 @@ export default { @select="handleSelect" > <template #list-item="{ item }"> - <span data-testid="access-level-link">{{ item.text }}</span> + <div data-testid="access-level-link" :class="{ 'gl-font-weight-bold': item.memberRoleId }"> + {{ item.text }} + </div> + <div v-if="item.description" class="gl-text-gray-700 gl-font-sm gl-pt-1 gl-line-clamp-2"> + {{ item.description }} + </div> </template> <template #footer> <ldap-dropdown-footer @@ -126,12 +136,10 @@ export default { </template> </gl-collapsible-listbox> - <gl-badge v-else>{{ member.accessLevel.stringValue }}</gl-badge> + <div v-else v-gl-tooltip.ds0="member.accessLevel.description" data-testid="role-text"> + {{ member.accessLevel.stringValue }} + </div> - <custom-permissions - v-if="memberRoleId !== null" - :member-role-id="memberRoleId" - :custom-permissions="customPermissions" - /> + <gl-badge v-if="memberRoleId" class="gl-mt-3">{{ $options.i18n.customRole }}</gl-badge> </div> </template> diff --git a/ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue b/ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue index d70736ab6debd1d8c94ae19ac24c6a36fe15fa5c..833e9935e2627f0352ed3fea9a5186c5f0b5dcf9 100644 --- a/ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue +++ b/ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue @@ -108,9 +108,10 @@ export default { update(data) { const memberRoles = data?.memberRoles?.nodes || []; - return memberRoles.map(({ id, name, baseAccessLevel }) => ({ + return memberRoles.map(({ id, name, description, baseAccessLevel }) => ({ baseAccessLevel: baseAccessLevel.integerValue, name, + description, memberRoleId: getIdFromGraphQLId(id), })); }, @@ -133,10 +134,10 @@ export default { }, update(data) { const memberRoles = data?.namespace?.memberRoles?.nodes || []; - - return memberRoles.map(({ id, name, baseAccessLevel }) => ({ + return memberRoles.map(({ id, name, description, baseAccessLevel }) => ({ baseAccessLevel: baseAccessLevel.integerValue, name, + description, memberRoleId: getIdFromGraphQLId(id), })); }, diff --git a/ee/app/assets/javascripts/invite_members/graphql/fragments/member_roles.fragment.graphql b/ee/app/assets/javascripts/invite_members/graphql/fragments/member_roles.fragment.graphql index 2d6c868263bc7caa589708ec3ce07140c8ef819f..cfa76711489058c89dd8de22330bfcc5c27a24b1 100644 --- a/ee/app/assets/javascripts/invite_members/graphql/fragments/member_roles.fragment.graphql +++ b/ee/app/assets/javascripts/invite_members/graphql/fragments/member_roles.fragment.graphql @@ -6,6 +6,7 @@ fragment MemberRoles on MemberRoleConnection { } id name + description enabledPermissions { nodes { name diff --git a/ee/app/assets/javascripts/members/components/table/custom_permissions.vue b/ee/app/assets/javascripts/members/components/table/custom_permissions.vue deleted file mode 100644 index 78432678c7864313357f81c165a4756d40e5c71e..0000000000000000000000000000000000000000 --- a/ee/app/assets/javascripts/members/components/table/custom_permissions.vue +++ /dev/null @@ -1,61 +0,0 @@ -<script> -import { GlBadge } from '@gitlab/ui'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; -import { TYPENAME_MEMBER_ROLE } from '~/graphql_shared/constants'; -import { s__ } from '~/locale'; -import * as Sentry from '~/sentry/sentry_browser_wrapper'; -import enabledMemberRolePermissions from '../../graphql/queries/enabled_member_role_permissions.query.graphql'; - -export default { - components: { - GlBadge, - }, - props: { - memberRoleId: { - type: Number, - required: true, - }, - customPermissions: { - type: Array, - required: true, - }, - }, - data() { - return { - enabledPermissions: [], - permissions: this.customPermissions, - }; - }, - watch: { - memberRoleId(newMemberRoleId) { - this.$apollo.addSmartQuery('enabledPermissions', { - query: enabledMemberRolePermissions, - variables: () => ({ - id: convertToGraphQLId(TYPENAME_MEMBER_ROLE, newMemberRoleId), - }), - update: (data) => data.memberRole.enabledPermissions, - result: () => { - this.permissions = this.enabledPermissions.nodes; - }, - error: (error) => { - Sentry.captureException(error); - }, - }); - }, - }, - i18n: { - title: s__('MemberRole|Custom permissions:'), - }, -}; -</script> - -<template> - <div - class="gl-mt-3 gl-display-flex gl-flex-wrap gl-justify-content-end gl-lg-justify-content-start gl-gap-2" - > - <span data-testid="title">{{ $options.i18n.title }}</span> - <gl-badge v-for="permission in permissions" :key="permission.name" variant="success" size="sm"> - {{ permission.name }} - </gl-badge> - </div> -</template> diff --git a/ee/app/assets/javascripts/members/graphql/queries/enabled_member_role_permissions.query.graphql b/ee/app/assets/javascripts/members/graphql/queries/enabled_member_role_permissions.query.graphql deleted file mode 100644 index 0981e5136e7fc6ba0dcc0d41671653dd19315768..0000000000000000000000000000000000000000 --- a/ee/app/assets/javascripts/members/graphql/queries/enabled_member_role_permissions.query.graphql +++ /dev/null @@ -1,11 +0,0 @@ -query enabledMemberRolePermissions($id: MemberRoleID!) { - memberRole(id: $id) { - id - enabledPermissions { - nodes { - name - value - } - } - } -} diff --git a/ee/app/assets/javascripts/members/utils.js b/ee/app/assets/javascripts/members/utils.js index 2467c9d8505f4c713004eb8f223a5650d2469d37..df494b09d8c4fb021cc85308e31cb6203065f90a 100644 --- a/ee/app/assets/javascripts/members/utils.js +++ b/ee/app/assets/javascripts/members/utils.js @@ -1,6 +1,5 @@ -import { groupBy, uniqueId } from 'lodash'; -import { ACCESS_LEVEL_LABELS } from '~/access_level/constants'; -import { __, s__, sprintf } from '~/locale'; +import { uniqueId } from 'lodash'; +import { __, s__ } from '~/locale'; import { generateBadges as CEGenerateBadges, roleDropdownItems as CERoleDropdownItems, @@ -60,21 +59,15 @@ export const roleDropdownItems = ({ validRoles, customRoles }) => { const { flatten: staticRoleDropdownItems } = CERoleDropdownItems({ validRoles }); - const customRoleDropdownItems = customRoles.map(({ baseAccessLevel, name, memberRoleId }) => ({ - accessLevel: baseAccessLevel, - memberRoleId, - text: name, - value: uniqueId('role-custom-'), - })); - - const customRoleDropdownGroups = Object.entries( - groupBy(customRoleDropdownItems, 'accessLevel'), - ).map(([accessLevel, options]) => ({ - text: sprintf(s__('MemberRole|Custom roles based on %{accessLevel}'), { - accessLevel: ACCESS_LEVEL_LABELS[accessLevel], + const customRoleDropdownItems = customRoles.map( + ({ baseAccessLevel, name, memberRoleId, description }) => ({ + accessLevel: baseAccessLevel, + memberRoleId, + text: name, + value: uniqueId('role-custom-'), + description, }), - options, - })); + ); return { flatten: [...staticRoleDropdownItems, ...customRoleDropdownItems], @@ -83,7 +76,10 @@ export const roleDropdownItems = ({ validRoles, customRoles }) => { text: s__('MemberRole|Standard roles'), options: staticRoleDropdownItems, }, - ...customRoleDropdownGroups, + { + text: s__('MemberRole|Custom roles'), + options: customRoleDropdownItems, + }, ], }; }; diff --git a/ee/app/presenters/ee/member_presenter.rb b/ee/app/presenters/ee/member_presenter.rb index 1c3e0093856baa0d9d9b778158cfebe4b48e7655..adaa6b01cfc2c58186303b8763e894753837a2da 100644 --- a/ee/app/presenters/ee/member_presenter.rb +++ b/ee/app/presenters/ee/member_presenter.rb @@ -37,7 +37,8 @@ def valid_member_roles { base_access_level: member_role.base_access_level, member_role_id: member_role.id, - name: member_role.name + name: member_role.name, + description: member_role.description } end end diff --git a/ee/spec/frontend/invite_members/components/invite_modal_base_spec.js b/ee/spec/frontend/invite_members/components/invite_modal_base_spec.js index 7e2824b3dc716efd243daf2d4325525732efa0d2..b02fe484134457daa95358b3b2970eb978f9af13 100644 --- a/ee/spec/frontend/invite_members/components/invite_modal_base_spec.js +++ b/ee/spec/frontend/invite_members/components/invite_modal_base_spec.js @@ -158,8 +158,18 @@ describe('EEInviteModalBase', () => { await waitForPromises(); expect(findCEBase().props('accessLevels').customRoles).toMatchObject([ - { baseAccessLevel: 10, memberRoleId: 103, name: 'My role project 1' }, - { baseAccessLevel: 10, memberRoleId: 104, name: 'My role instance 1' }, + { + baseAccessLevel: 10, + memberRoleId: 103, + name: 'My role project 1', + description: 'My role project 1 description', + }, + { + baseAccessLevel: 10, + memberRoleId: 104, + name: 'My role instance 1', + description: 'My role instance 1 description', + }, ]); }); }); @@ -170,9 +180,24 @@ describe('EEInviteModalBase', () => { await waitForPromises(); expect(findCEBase().props('accessLevels').customRoles).toMatchObject([ - { baseAccessLevel: 10, memberRoleId: 100, name: 'My role group 1' }, - { baseAccessLevel: 20, memberRoleId: 101, name: 'My role group 2' }, - { baseAccessLevel: 10, memberRoleId: 104, name: 'My role instance 1' }, + { + baseAccessLevel: 10, + memberRoleId: 100, + name: 'My role group 1', + description: 'My role group 1 description', + }, + { + baseAccessLevel: 20, + memberRoleId: 101, + name: 'My role group 2', + description: 'My role group 2 description', + }, + { + baseAccessLevel: 10, + memberRoleId: 104, + name: 'My role instance 1', + description: 'My role instance 1 description', + }, ]); }); }); diff --git a/ee/spec/frontend/invite_members/mock_data.js b/ee/spec/frontend/invite_members/mock_data.js index f834fab90f0efd2e343fd1dddf31637bc094a989..d1b286609fdba0b14cb7be6b53ebe346236bd5ff 100644 --- a/ee/spec/frontend/invite_members/mock_data.js +++ b/ee/spec/frontend/invite_members/mock_data.js @@ -62,6 +62,7 @@ export const mockGroupMemberRoles = { }, id: 'gid://gitlab/MemberRole/100', name: 'My role group 1', + description: 'My role group 1 description', enabledPermissions: { nodes: [ { @@ -80,6 +81,7 @@ export const mockGroupMemberRoles = { }, id: 'gid://gitlab/MemberRole/101', name: 'My role group 2', + description: 'My role group 2 description', enabledPermissions: { nodes: [ { @@ -112,6 +114,7 @@ export const mockProjectMemberRoles = { }, id: 'gid://gitlab/MemberRole/103', name: 'My role project 1', + description: 'My role project 1 description', enabledPermissions: { nodes: [ { @@ -141,6 +144,7 @@ export const mockInstanceMemberRoles = { }, id: 'gid://gitlab/MemberRole/104', name: 'My role instance 1', + description: 'My role instance 1 description', enabledPermissions: { nodes: [ { diff --git a/ee/spec/frontend/members/components/table/custom_permissions_spec.js b/ee/spec/frontend/members/components/table/custom_permissions_spec.js deleted file mode 100644 index 60ab9c83e981ef9b9814c0905b316b88ac984b71..0000000000000000000000000000000000000000 --- a/ee/spec/frontend/members/components/table/custom_permissions_spec.js +++ /dev/null @@ -1,96 +0,0 @@ -import { GlBadge } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import CustomPermissions from 'ee/members/components/table/custom_permissions.vue'; -import enabledMemberRolePermissions from 'ee/members/graphql/queries/enabled_member_role_permissions.query.graphql'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; -import { TYPENAME_MEMBER_ROLE } from '~/graphql_shared/constants'; -import * as Sentry from '~/sentry/sentry_browser_wrapper'; - -Vue.use(VueApollo); - -jest.mock('~/sentry/sentry_browser_wrapper'); - -describe('CustomPermissions', () => { - let wrapper; - - const customPermissions = [{ name: 'Read code' }, { name: 'Read vulnerability' }]; - const enabledMemberRoleResponse = jest.fn().mockResolvedValue({ - data: { - memberRole: { - id: 'gid://gitlab/MemberRole/400', - enabledPermissions: { - nodes: [ - { - name: 'Admin merge request', - value: 'ADMIN_MERGE_REQUEST', - }, - ], - }, - }, - }, - }); - - const createComponent = ({ mockedResponse = enabledMemberRoleResponse } = {}) => { - const apolloProvider = createMockApollo([[enabledMemberRolePermissions, mockedResponse]]); - - wrapper = shallowMountExtended(CustomPermissions, { - apolloProvider, - propsData: { - memberRoleId: 10, - customPermissions, - }, - }); - }; - - const findBadges = () => wrapper.findAllComponents(GlBadge); - - describe('when GraphQL queries success', () => { - beforeEach(() => { - createComponent(); - waitForPromises(); - }); - - it('shows a title', () => { - expect(wrapper.findByTestId('title').text()).toBe(wrapper.vm.$options.i18n.title); - }); - - it('shows badges', () => { - const badges = findBadges(); - expect(badges).toHaveLength(2); - - expect(badges.at(0).props()).toMatchObject({ variant: 'success', size: 'sm' }); - expect(badges.at(0).text()).toBe('Read code'); - - expect(badges.at(1).props()).toMatchObject({ variant: 'success', size: 'sm' }); - expect(badges.at(1).text()).toBe('Read vulnerability'); - }); - - it('update badges', async () => { - const memberRoleId = 400; - wrapper.setProps({ memberRoleId }); - await waitForPromises(); - - expect(enabledMemberRoleResponse).toHaveBeenCalledWith({ - id: convertToGraphQLId(TYPENAME_MEMBER_ROLE, memberRoleId), - }); - expect(findBadges()).toHaveLength(1); - }); - }); - - describe('when GraphQL queries fail', () => { - it('reports fetching errors to Sentry', async () => { - const myError = new Error('dummy'); - createComponent({ mockedResponse: jest.fn().mockRejectedValue(myError) }); - - const memberRoleId = 400; - wrapper.setProps({ memberRoleId }); - await waitForPromises(); - - expect(Sentry.captureException).toHaveBeenCalledWith(myError); - }); - }); -}); diff --git a/ee/spec/frontend/members/components/table/max_role_spec.js b/ee/spec/frontend/members/components/table/max_role_spec.js index d5df495d05fd7a8ac59fbd208ababf35ff74152e..4eb928af614b74aeded1ea1fee5386990ac22f39 100644 --- a/ee/spec/frontend/members/components/table/max_role_spec.js +++ b/ee/spec/frontend/members/components/table/max_role_spec.js @@ -1,8 +1,8 @@ -import { GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { GlCollapsibleListbox, GlListboxItem, GlBadge } from '@gitlab/ui'; import Vue from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; import LdapDropdownFooter from 'ee/members/components/action_dropdowns/ldap_dropdown_footer.vue'; import ManageRolesDropdownFooter from 'ee/members/components/action_dropdowns/manage_roles_dropdown_footer.vue'; import { guestOverageConfirmAction } from 'ee/members/guest_overage_confirm_action'; @@ -10,6 +10,8 @@ import waitForPromises from 'helpers/wait_for_promises'; import MaxRole from '~/members/components/table/max_role.vue'; import { MEMBER_TYPES } from '~/members/constants'; import * as Sentry from '~/sentry/sentry_browser_wrapper'; +import { s__ } from '~/locale'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { upgradedMember as member } from '../../mock_data'; Vue.use(Vuex); @@ -39,7 +41,7 @@ describe('MaxRole', () => { }; const createComponent = (propsData = {}, store = createStore()) => { - wrapper = mount(MaxRole, { + wrapper = mountExtended(MaxRole, { provide: { namespace: MEMBER_TYPES.user, group: { @@ -56,22 +58,41 @@ describe('MaxRole', () => { stubs: { CustomPermissions: CustomPermissionsStub, }, + directives: { + GlTooltip: createMockDirective('gl-tooltip'), + }, }); return waitForPromises(); }; - const findCustomPermissions = () => wrapper.findComponent(CustomPermissionsStub); + const findBadge = () => wrapper.findComponent(GlBadge); const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); const findListboxItems = () => wrapper.findAllComponents(GlListboxItem); const findListboxItemByText = (text) => findListboxItems().wrappers.find((item) => item.text() === text); + const findRoleText = () => wrapper.findByTestId('role-text'); describe('when a member has custom permissions', () => { - it('renders an initial list', async () => { - await createComponent(); + beforeEach(() => { + createComponent({ + permissions: { + canUpdate: false, + }, + }); + }); + + it('renders role text and a custom role badge', () => { + expect(findRoleText().text()).toBe('custom role 1'); + + expect(findBadge().exists()).toBe(true); + expect(findBadge().text()).toBe(s__('MemberRole|Custom role')); + }); - expect(findCustomPermissions().exists()).toBe(true); + it('renders a tooltip', () => { + const tooltip = getBinding(findRoleText().element, 'gl-tooltip'); + expect(tooltip).toBeDefined(); + expect(tooltip.value).toBe('custom role 1 description'); }); }); @@ -91,8 +112,8 @@ describe('MaxRole', () => { ); }); - it('does not render a list', () => { - expect(findCustomPermissions().exists()).toBe(false); + it('does not render a custom role badge', () => { + expect(findBadge().exists()).toBe(false); }); describe('after unsuccessful role assignment', () => { @@ -109,8 +130,8 @@ describe('MaxRole', () => { expect(findListbox().find('[aria-selected=true]').text()).toBe('Owner'); }); - it('resets list of permissions', () => { - expect(findCustomPermissions().exists()).toBe(false); + it('resets custom role badge', () => { + expect(findBadge().exists()).toBe(false); }); }); }); @@ -169,10 +190,8 @@ describe('MaxRole', () => { expect(findListbox().props('items')[0].text).toBe('Standard roles'); expect(findListbox().props('items')[0].options).toHaveLength(6); - expect(findListbox().props('items')[1].text).toBe('Custom roles based on Guest'); - expect(findListbox().props('items')[1].options).toHaveLength(2); - expect(findListbox().props('items')[2].text).toBe('Custom roles based on Reporter'); - expect(findListbox().props('items')[2].options).toHaveLength(1); + expect(findListbox().props('items')[1].text).toBe('Custom roles'); + expect(findListbox().props('items')[1].options).toHaveLength(3); }); it('calls `updateMemberRole` Vuex action', async () => { diff --git a/ee/spec/frontend/members/mock_data.js b/ee/spec/frontend/members/mock_data.js index ecb34c2646ac38d4e5bd9526168c2116462d70c7..c56d6cf4a30f0fe71196a634504f41111d81f126 100644 --- a/ee/spec/frontend/members/mock_data.js +++ b/ee/spec/frontend/members/mock_data.js @@ -9,7 +9,12 @@ export const bannedMember = { export const customRoles = [ { baseAccessLevel: 20, name: 'custom role 3', memberRoleId: 103 }, - { baseAccessLevel: 10, name: 'custom role 1', memberRoleId: 101 }, + { + baseAccessLevel: 10, + name: 'custom role 1', + description: 'custom role 1 description', + memberRoleId: 101, + }, { baseAccessLevel: 10, name: 'custom role 2', memberRoleId: 102 }, ]; @@ -17,7 +22,12 @@ export const customPermissions = [{ name: 'Read code' }, { name: 'Read vulnerabi export const upgradedMember = { ...member, - accessLevel: { integerValue: 10, stringValue: 'custom role 1', memberRoleId: 101 }, + accessLevel: { + integerValue: 10, + stringValue: 'custom role 1', + memberRoleId: 101, + description: 'custom role 1 description', + }, customPermissions, customRoles, }; diff --git a/ee/spec/frontend/members/utils_spec.js b/ee/spec/frontend/members/utils_spec.js index 3d8f429bd96a88e185b27d1ad59c179b363c5580..c4b031a1669a370ee2000b510ef0a086c9555c17 100644 --- a/ee/spec/frontend/members/utils_spec.js +++ b/ee/spec/frontend/members/utils_spec.js @@ -107,6 +107,7 @@ describe('Members Utils', () => { expect(flatten).toContainEqual({ text: 'custom role 1', value: 'role-custom-0', + description: 'custom role 1 description', accessLevel: 10, memberRoleId: 101, }); @@ -115,13 +116,11 @@ describe('Members Utils', () => { it('returns properly formatted dropdowns', () => { const { formatted } = roleDropdownItems({ ...memberMock, customRoles }); - expect(formatted).toHaveLength(3); + expect(formatted).toHaveLength(2); expect(formatted[0].text).toBe('Standard roles'); expect(formatted[0].options).toHaveLength(0); - expect(formatted[1].text).toBe('Custom roles based on Guest'); - expect(formatted[1].options).toHaveLength(2); - expect(formatted[2].text).toBe('Custom roles based on Reporter'); - expect(formatted[2].options).toHaveLength(1); + expect(formatted[1].text).toBe('Custom roles'); + expect(formatted[1].options).toHaveLength(3); }); }); }); diff --git a/ee/spec/frontend/roles_and_permissions/mock_data.js b/ee/spec/frontend/roles_and_permissions/mock_data.js index ba1eb8d2b9ffd5f88efc8c331cf13efa815bf2c8..8af66f44d3f6bc5534792562914bbaf8727e7e86 100644 --- a/ee/spec/frontend/roles_and_permissions/mock_data.js +++ b/ee/spec/frontend/roles_and_permissions/mock_data.js @@ -41,6 +41,7 @@ export const mockMemberRoles = { }, id: 'gid://gitlab/MemberRole/1', name: 'Test', + description: 'Test description', enabledPermissions: { nodes: [ { @@ -75,6 +76,7 @@ export const mockInstanceMemberRoles = { }, id: 'gid://gitlab/MemberRole/2', name: 'Instance Test', + description: 'Instance Test description', enabledPermissions: { nodes: [ { diff --git a/ee/spec/helpers/ee/groups/group_members_helper_spec.rb b/ee/spec/helpers/ee/groups/group_members_helper_spec.rb index e0990fc2cb18b67d0dca8a7e8838ce5229c12e60..f5aeca63fdcadae44a4d475c5fc7119139c20ec9 100644 --- a/ee/spec/helpers/ee/groups/group_members_helper_spec.rb +++ b/ee/spec/helpers/ee/groups/group_members_helper_spec.rb @@ -113,7 +113,7 @@ end context 'when member has custom role' do - let(:member_role) { create(:member_role, :guest, name: 'guest plus', namespace: group, read_code: true) } + let(:member_role) { create(:member_role, :guest, name: 'guest plus', namespace: group, read_code: true, description: 'My custom role') } let(:user) { create(:user) } let(:banned) { present_members(create_list(:group_member, 1, :guest, group: group, user: user, member_role: member_role)) } diff --git a/ee/spec/presenters/member_presenter_spec.rb b/ee/spec/presenters/member_presenter_spec.rb index 9d65e8058118f18d5989d4df4224d91e0e4c1314..d5fc654c193dde7b199a30eaaa086f1d330595c0 100644 --- a/ee/spec/presenters/member_presenter_spec.rb +++ b/ee/spec/presenters/member_presenter_spec.rb @@ -48,7 +48,9 @@ describe '#valid_member_roles' do let_it_be(:member_role_guest) { create(:member_role, :guest, name: 'guest plus', namespace: root_group) } - let_it_be(:member_role_reporter) { create(:member_role, :reporter, name: 'reporter plus', namespace: root_group) } + let_it_be(:member_role_reporter) do + create(:member_role, :reporter, name: 'reporter plus', namespace: root_group, description: 'My custom role') + end let_it_be(:member_role_instance) do create(:member_role, :guest, :instance, name: 'guest plus (instance-level)') @@ -70,7 +72,8 @@ { base_access_level: Gitlab::Access::REPORTER, member_role_id: member_role_reporter.id, - name: 'reporter plus' + name: 'reporter plus', + description: 'My custom role' } ] ) @@ -82,17 +85,20 @@ { base_access_level: Gitlab::Access::REPORTER, member_role_id: member_role_reporter.id, - name: 'reporter plus' + name: 'reporter plus', + description: 'My custom role' }, { base_access_level: Gitlab::Access::GUEST, member_role_id: member_role_guest.id, - name: 'guest plus' + name: 'guest plus', + description: nil }, { base_access_level: Gitlab::Access::GUEST, member_role_id: member_role_instance.id, - name: 'guest plus (instance-level)' + name: 'guest plus (instance-level)', + description: nil } ] ) diff --git a/ee/spec/serializers/member_entity_spec.rb b/ee/spec/serializers/member_entity_spec.rb index ca14ce019b1c476e1fc428009fc71356d95dacd5..e576e9abea029839eea7a79f3f60b372f12984b3 100644 --- a/ee/spec/serializers/member_entity_spec.rb +++ b/ee/spec/serializers/member_entity_spec.rb @@ -86,7 +86,7 @@ it_behaves_like 'member.json' context 'with custom role' do - let(:member_role) { create(:member_role, :guest, name: 'guest plus', namespace: group, read_code: true) } + let(:member_role) { create(:member_role, :guest, name: 'guest plus', description: 'My custom role', namespace: group, read_code: true) } let(:member) { GroupMemberPresenter.new(create(:group_member, :guest, group: group, member_role: member_role, user: current_user), current_user: current_user) } it_behaves_like 'member.json' @@ -101,7 +101,7 @@ it_behaves_like 'member.json' context 'with custom role' do - let_it_be(:member_role) { create(:member_role, :guest, name: 'guest plus', namespace: group, read_code: true) } + let_it_be(:member_role) { create(:member_role, :guest, name: 'guest plus', description: 'My custom role', namespace: group, read_code: true) } let_it_be(:member) { ProjectMemberPresenter.new(create(:project_member, :guest, project: project, member_role: member_role, user: current_user), current_user: current_user) } it_behaves_like 'member.json' diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0f02c1a5683214909bcd2114bde0b7dffd71b48d..92ab60c0f9e364e128e6ecc53fd7b08d56379c64 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -30170,15 +30170,12 @@ msgstr "" msgid "MemberRole|Create new role" msgstr "" -msgid "MemberRole|Custom permissions:" +msgid "MemberRole|Custom role" msgstr "" msgid "MemberRole|Custom roles" msgstr "" -msgid "MemberRole|Custom roles based on %{accessLevel}" -msgstr "" - msgid "MemberRole|Delete role" msgstr "" diff --git a/spec/fixtures/api/schemas/entities/member.json b/spec/fixtures/api/schemas/entities/member.json index cfbcefef1cb93898d20f2005e3995b53e910f816..63bbf1e183d6a2340b43573ae9aac810802c5a43 100644 --- a/spec/fixtures/api/schemas/entities/member.json +++ b/spec/fixtures/api/schemas/entities/member.json @@ -168,6 +168,9 @@ }, "name": { "type": "string" + }, + "description": { + "type": "string" } }, "additionalProperties": false diff --git a/spec/frontend/members/components/table/max_role_spec.js b/spec/frontend/members/components/table/max_role_spec.js index 75e1e05afb1fa4fd4c6e249776181509aeb76549..7bd20a9670f18357cd0d23e334f808802d5859e9 100644 --- a/spec/frontend/members/components/table/max_role_spec.js +++ b/spec/frontend/members/components/table/max_role_spec.js @@ -1,4 +1,4 @@ -import { GlBadge, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui'; +import { GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import { mount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; @@ -60,7 +60,6 @@ describe('MaxRole', () => { }); }; - const findBadge = () => wrapper.findComponent(GlBadge); const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); const findListboxItems = () => wrapper.findAllComponents(GlListboxItem); const findListboxItemByText = (text) => @@ -71,14 +70,14 @@ describe('MaxRole', () => { }); describe('when member can not be updated', () => { - it('renders a badge instead of a collapsible listbox', () => { + it('renders the role name instead of a collapsible listbox', () => { createComponent({ permissions: { canUpdate: false, }, }); - expect(findBadge().text()).toBe('Owner'); + expect(wrapper.text()).toContain('Owner'); }); });