Commit b65a7c7a authored by Peter Hegman's avatar Peter Hegman Committed by Nick Thomas

Convert bootstrap tabs on group/members page to `GlTabs`

parent 8a6e9d96
......@@ -9,7 +9,17 @@ import MembersTable from './table/members_table.vue';
export default {
name: 'MembersApp',
components: { MembersTable, FilterSortContainer, GlAlert },
inject: ['namespace'],
provide() {
return {
namespace: this.namespace,
};
},
props: {
namespace: {
type: String,
required: true,
},
},
computed: {
...mapState({
showError(state) {
......
......@@ -2,20 +2,11 @@ import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import { parseDataAttributes } from '~/members/utils';
import App from './components/app.vue';
import MembersTabs from './components/members_tabs.vue';
import { MEMBER_TYPES } from './constants';
import membersStore from './store';
export const initMembersApp = (
el,
{
namespace,
tableFields = [],
tableAttrs = {},
tableSortableFields = [],
requestFormatter = () => {},
filteredSearchBar = { show: false },
},
) => {
export const initMembersApp = (el, options) => {
if (!el) {
return () => {};
}
......@@ -25,29 +16,45 @@ export const initMembersApp = (
const { sourceId, canManageMembers, ...vuexStoreAttributes } = parseDataAttributes(el);
const store = new Vuex.Store({
modules: {
const modules = Object.keys(MEMBER_TYPES).reduce((accumulator, namespace) => {
const namespacedOptions = options[namespace];
if (!namespacedOptions) {
return accumulator;
}
const {
tableFields = [],
tableAttrs = {},
tableSortableFields = [],
requestFormatter = () => {},
filteredSearchBar = { show: false },
} = namespacedOptions;
return {
...accumulator,
[namespace]: membersStore({
...vuexStoreAttributes,
...vuexStoreAttributes[namespace],
tableFields,
tableAttrs,
tableSortableFields,
requestFormatter,
filteredSearchBar,
}),
},
});
};
}, {});
const store = new Vuex.Store({ modules });
return new Vue({
el,
components: { App },
components: { MembersTabs },
store,
provide: {
namespace,
currentUserId: gon.current_user_id || null,
sourceId,
canManageMembers,
},
render: (createElement) => createElement('app'),
render: (createElement) => createElement('members-tabs'),
});
};
......@@ -29,46 +29,43 @@ function mountRemoveMemberModal() {
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
initMembersApp(document.querySelector('.js-group-members-list'), {
namespace: MEMBER_TYPES.user,
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: ['account', 'granted', 'maxRole', 'lastSignIn'],
requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: ['two_factor', 'with_inherited_permissions'],
searchParam: 'search',
placeholder: s__('Members|Filter members'),
recentSearchesStorageKey: 'group_members',
initMembersApp(document.querySelector('.js-group-members-list-app'), {
[MEMBER_TYPES.user]: {
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: ['account', 'granted', 'maxRole', 'lastSignIn'],
requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: ['two_factor', 'with_inherited_permissions'],
searchParam: 'search',
placeholder: s__('Members|Filter members'),
recentSearchesStorageKey: 'group_members',
},
},
});
initMembersApp(document.querySelector('.js-group-group-links-list'), {
namespace: MEMBER_TYPES.group,
tableFields: SHARED_FIELDS.concat('granted'),
tableAttrs: {
table: { 'data-qa-selector': 'groups_list' },
tr: { 'data-qa-selector': 'group_row' },
[MEMBER_TYPES.group]: {
tableFields: SHARED_FIELDS.concat('granted'),
tableAttrs: {
table: { 'data-qa-selector': 'groups_list' },
tr: { 'data-qa-selector': 'group_row' },
},
requestFormatter: groupLinkRequestFormatter,
},
requestFormatter: groupLinkRequestFormatter,
});
initMembersApp(document.querySelector('.js-group-invited-members-list'), {
namespace: MEMBER_TYPES.invite,
tableFields: SHARED_FIELDS.concat('invited'),
requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: [],
searchParam: 'search_invited',
placeholder: s__('Members|Search invited'),
recentSearchesStorageKey: 'group_invited_members',
[MEMBER_TYPES.invite]: {
tableFields: SHARED_FIELDS.concat('invited'),
requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: [],
searchParam: 'search_invited',
placeholder: s__('Members|Search invited'),
recentSearchesStorageKey: 'group_invited_members',
},
},
[MEMBER_TYPES.accessRequest]: {
tableFields: SHARED_FIELDS.concat('requested'),
requestFormatter: groupMemberRequestFormatter,
},
});
initMembersApp(document.querySelector('.js-group-access-requests-list'), {
namespace: MEMBER_TYPES.accessRequest,
tableFields: SHARED_FIELDS.concat('requested'),
requestFormatter: groupMemberRequestFormatter,
});
groupsSelect();
......
......@@ -42,46 +42,41 @@ initInviteMembersForm();
new UsersSelect(); // eslint-disable-line no-new
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
initMembersApp(document.querySelector('.js-project-members-list'), {
namespace: MEMBER_TYPES.user,
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: ['account', 'granted', 'maxRole', 'lastSignIn'],
requestFormatter: projectMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: ['with_inherited_permissions'],
searchParam: 'search',
placeholder: s__('Members|Filter members'),
recentSearchesStorageKey: 'project_members',
initMembersApp(document.querySelector('.js-project-members-list-app'), {
[MEMBER_TYPES.user]: {
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: ['account', 'granted', 'maxRole', 'lastSignIn'],
requestFormatter: projectMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: ['with_inherited_permissions'],
searchParam: 'search',
placeholder: s__('Members|Filter members'),
recentSearchesStorageKey: 'project_members',
},
},
});
initMembersApp(document.querySelector('.js-project-group-links-list'), {
namespace: MEMBER_TYPES.group,
tableFields: SHARED_FIELDS.concat('granted'),
tableAttrs: {
table: { 'data-qa-selector': 'groups_list' },
tr: { 'data-qa-selector': 'group_row' },
[MEMBER_TYPES.group]: {
tableFields: SHARED_FIELDS.concat('granted'),
tableAttrs: {
table: { 'data-qa-selector': 'groups_list' },
tr: { 'data-qa-selector': 'group_row' },
},
requestFormatter: groupLinkRequestFormatter,
filteredSearchBar: {
show: true,
tokens: [],
searchParam: 'search_groups',
placeholder: s__('Members|Search groups'),
recentSearchesStorageKey: 'project_group_links',
},
},
requestFormatter: groupLinkRequestFormatter,
filteredSearchBar: {
show: true,
tokens: [],
searchParam: 'search_groups',
placeholder: s__('Members|Search groups'),
recentSearchesStorageKey: 'project_group_links',
[MEMBER_TYPES.invite]: {
tableFields: SHARED_FIELDS.concat('invited'),
requestFormatter: projectMemberRequestFormatter,
},
[MEMBER_TYPES.accessRequest]: {
tableFields: SHARED_FIELDS.concat('requested'),
requestFormatter: projectMemberRequestFormatter,
},
});
initMembersApp(document.querySelector('.js-project-invited-members-list'), {
namespace: MEMBER_TYPES.invite,
tableFields: SHARED_FIELDS.concat('invited'),
requestFormatter: projectMemberRequestFormatter,
});
initMembersApp(document.querySelector('.js-project-access-requests-list'), {
namespace: MEMBER_TYPES.accessRequest,
tableFields: SHARED_FIELDS.concat('requested'),
requestFormatter: projectMemberRequestFormatter,
});
......@@ -13,12 +13,15 @@ module Groups::GroupMembersHelper
render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: group.access_level_roles, default_access_level: default_access_level
end
def group_members_list_data_json(group, members, pagination = {})
group_members_list_data(group, members, pagination).to_json
end
def group_group_links_list_data_json(group)
group_group_links_list_data(group).to_json
def group_members_app_data_json(group, members:, invited:, access_requests:)
{
user: group_members_list_data(group, members, { param_name: :page, params: { invited_members_page: nil, search_invited: nil } }),
group: group_group_links_list_data(group),
invite: group_members_list_data(group, invited.nil? ? [] : invited, { param_name: :invited_members_page, params: { page: nil } }),
access_request: group_members_list_data(group, access_requests.nil? ? [] : access_requests),
source_id: group.id,
can_manage_members: can?(current_user, :admin_group_member, group)
}.to_json
end
private
......@@ -32,13 +35,11 @@ module Groups::GroupMembersHelper
end
# Overridden in `ee/app/helpers/ee/groups/group_members_helper.rb`
def group_members_list_data(group, members, pagination)
def group_members_list_data(group, members, pagination = {})
{
members: group_members_serialized(group, members),
pagination: members_pagination_data(members, pagination),
member_path: group_group_member_path(group, ':id'),
source_id: group.id,
can_manage_members: can?(current_user, :admin_group_member, group)
member_path: group_group_member_path(group, ':id')
}
end
......@@ -48,8 +49,7 @@ module Groups::GroupMembersHelper
{
members: group_group_links_serialized(group_links),
pagination: members_pagination_data(group_links),
member_path: group_group_link_path(group, ':id'),
source_id: group.id
member_path: group_group_link_path(group, ':id')
}
end
end
......
......@@ -27,12 +27,15 @@ module Projects::ProjectMembersHelper
project.group.has_owner?(current_user)
end
def project_members_list_data_json(project, members, pagination = {})
project_members_list_data(project, members, pagination).to_json
end
def project_group_links_list_data_json(project, group_links)
project_group_links_list_data(project, group_links).to_json
def project_members_app_data_json(project, members:, group_links:, invited:, access_requests:)
{
user: project_members_list_data(project, members, { param_name: :page, params: { search_groups: nil } }),
group: project_group_links_list_data(project, group_links),
invite: project_members_list_data(project, invited.nil? ? [] : invited),
access_request: project_members_list_data(project, access_requests.nil? ? [] : access_requests),
source_id: project.id,
can_manage_members: can_manage_project_members?(project)
}.to_json
end
private
......@@ -45,13 +48,11 @@ module Projects::ProjectMembersHelper
GroupLink::ProjectGroupLinkSerializer.new.represent(group_links, { current_user: current_user })
end
def project_members_list_data(project, members, pagination)
def project_members_list_data(project, members, pagination = {})
{
members: project_members_serialized(project, members),
pagination: members_pagination_data(members, pagination),
member_path: project_project_member_path(project, ':id'),
source_id: project.id,
can_manage_members: can_manage_project_members?(project)
member_path: project_project_member_path(project, ':id')
}
end
......@@ -59,9 +60,7 @@ module Projects::ProjectMembersHelper
{
members: project_group_links_serialized(group_links),
pagination: members_pagination_data(group_links),
member_path: project_group_link_path(project, ':id'),
source_id: project.id,
can_manage_members: can_manage_project_members?(project)
member_path: project_group_link_path(project, ':id')
}
end
end
- add_page_specific_style 'page_bundles/members'
- page_title _('Group members')
- show_invited_members = can_manage_members? && @invited_members.load.any?
- show_access_requests = can_manage_members? && @requesters.load.any?
- invited_active = params[:search_invited].present? || params[:invited_members_page].present?
.js-remove-member-modal
.row.gl-mt-3
......@@ -35,47 +32,9 @@
= render_if_exists 'groups/group_members/ldap_sync'
%ul.nav-links.mobile-separator.nav.nav-tabs
%li.nav-item
= link_to '#tab-members', class: ['nav-link', ('active' unless invited_active)], data: { toggle: 'tab' } do
%span
= _('Members')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @members.total_count
- if @group.shared_with_group_links.present?
%li.nav-item
= link_to '#tab-groups', class: ['nav-link'] , data: { toggle: 'tab', qa_selector: 'groups_list_tab' } do
%span
= _('Groups')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @group.shared_with_group_links.count
- if show_invited_members
%li.nav-item
= link_to '#tab-invited-members', class: ['nav-link', ('active' if invited_active)], data: { toggle: 'tab' } do
%span
= _('Invited')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @invited_members.total_count
- if show_access_requests
%li.nav-item
= link_to '#tab-access-requests', class: 'nav-link', data: { toggle: 'tab' } do
%span
= _('Access requests')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @requesters.count
.tab-content
#tab-members.tab-pane{ class: ('active' unless invited_active) }
.js-group-members-list{ data: { members_data: group_members_list_data_json(@group, @members, { param_name: :page, params: { invited_members_page: nil, search_invited: nil } }) } }
.loading
.gl-spinner.gl-spinner-md
- if @group.shared_with_group_links.present?
#tab-groups.tab-pane
.js-group-group-links-list{ data: { members_data: group_group_links_list_data_json(@group) } }
.loading
.gl-spinner.gl-spinner-md
- if show_invited_members
#tab-invited-members.tab-pane{ class: ('active' if invited_active) }
.js-group-invited-members-list{ data: { members_data: group_members_list_data_json(@group, @invited_members, { param_name: :invited_members_page, params: { page: nil } }) } }
.loading
.gl-spinner.gl-spinner-md
- if show_access_requests
#tab-access-requests.tab-pane
.js-group-access-requests-list{ data: { members_data: group_members_list_data_json(@group, @requesters) } }
.loading
.gl-spinner.gl-spinner-md
.js-group-members-list-app{ data: { members_data: group_members_app_data_json(@group,
members: @members,
invited: @invited_members,
access_requests: @requesters) } }
.loading
.gl-spinner.gl-spinner-md
......@@ -56,47 +56,10 @@
.invite-member= render 'shared/members/invite_member', submit_url: project_project_members_path(@project), access_levels: ProjectMember.access_level_roles, default_access_level: @project_member.access_level, can_import_members?: can_import_members?, import_path: import_project_project_members_path(@project)
- elsif @project.allowed_to_share_with_group?
.invite-group= render 'shared/members/invite_group', access_levels: ProjectGroupLink.access_options, default_access_level: ProjectGroupLink.default_access, submit_url: project_group_links_path(@project), group_link_field: 'link_group_id', group_access_field: 'link_group_access'
%ul.nav-links.mobile-separator.nav.nav-tabs
%li.nav-item
= link_to '#tab-members', class: ['nav-link', ('active' unless groups_tab_active?)], data: { toggle: 'tab' } do
%span
= _('Members')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @project_members.total_count
- if show_groups?(@group_links)
%li.nav-item
= link_to '#tab-groups', class: ['nav-link', ('active' if groups_tab_active?)] , data: { toggle: 'tab', qa_selector: 'groups_list_tab' } do
%span
= _('Groups')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @group_links.count
- if show_invited_members?(@project, @invited_members)
%li.nav-item
= link_to '#tab-invited-members', class: 'nav-link', data: { toggle: 'tab' } do
%span
= _('Invited')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @invited_members.count
- if show_access_requests?(@project, @requesters)
%li.nav-item
= link_to '#tab-access-requests', class: 'nav-link', data: { toggle: 'tab' } do
%span
= _('Access requests')
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= @requesters.count
.tab-content
#tab-members.tab-pane{ class: ('active' unless groups_tab_active?) }
.js-project-members-list{ data: { members_data: project_members_list_data_json(@project, @project_members, { param_name: :page, params: { search_groups: nil } }) } }
.loading
.gl-spinner.gl-spinner-md
- if show_groups?(@group_links)
#tab-groups.tab-pane{ class: ('active' if groups_tab_active?) }
.js-project-group-links-list{ data: { members_data: project_group_links_list_data_json(@project, @group_links) } }
.loading
.gl-spinner.gl-spinner-md
- if show_invited_members?(@project, @invited_members)
#tab-invited-members.tab-pane
.js-project-invited-members-list{ data: { members_data: project_members_list_data_json(@project, @invited_members) } }
.loading
.gl-spinner.gl-spinner-md
- if show_access_requests?(@project, @requesters)
#tab-access-requests.tab-pane
.js-project-access-requests-list{ data: { members_data: project_members_list_data_json(@project, @requesters) } }
.loading
.gl-spinner.gl-spinner-md
.js-project-members-list-app{ data: { members_data: project_members_app_data_json(@project,
members: @project_members,
group_links: @group_links,
invited: @invited_members,
access_requests: @requesters) } }
.loading
.gl-spinner.gl-spinner-md
---
title: Update group/project member tabs to comply with Pajamas design system
merge_request:
author:
type: other
......@@ -8,7 +8,7 @@ describe('initMembersApp', () => {
const createVm = () => {
vm = initMembersApp(el, {
namespace: MEMBER_TYPES.user,
[MEMBER_TYPES.user]: {},
});
};
......
......@@ -22,8 +22,17 @@ RSpec.describe Groups::GroupMembersHelper do
end
end
describe '#group_members_list_data_json' do
subject { Gitlab::Json.parse(helper.group_members_list_data_json(group, [])) }
describe '#group_members_app_data_json' do
subject do
Gitlab::Json.parse(
helper.group_members_app_data_json(
group,
members: [],
invited: [],
access_requests: []
)
)
end
before do
allow(helper).to receive(:override_group_group_member_path).with(group, ':id').and_return('/groups/foo-bar/-/group_members/:id/override')
......@@ -32,7 +41,7 @@ RSpec.describe Groups::GroupMembersHelper do
end
it 'adds `ldap_override_path` to returned json' do
expect(subject['ldap_override_path']).to eq('/groups/foo-bar/-/group_members/:id/override')
expect(subject['user']['ldap_override_path']).to eq('/groups/foo-bar/-/group_members/:id/override')
end
end
end
......@@ -26,7 +26,7 @@ module QA
element :delete_member_button
end
view 'app/views/groups/group_members/index.html.haml' do
view 'app/assets/javascripts/members/components/members_tabs.vue' do
element :groups_list_tab
end
......
......@@ -6,7 +6,7 @@ module QA
class Members < Page::Base
include QA::Page::Component::InviteMembersModal
view 'app/views/projects/project_members/index.html.haml' do
view 'app/assets/javascripts/members/components/members_tabs.vue' do
element :groups_list_tab
end
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Groups > Members > Tabs' do
RSpec.describe 'Groups > Members > Tabs', :js do
using RSpec::Parameterized::TableSyntax
shared_examples 'active "Members" tab' do
......@@ -56,7 +56,7 @@ RSpec.describe 'Groups > Members > Tabs' do
it_behaves_like 'active "Members" tab'
end
context 'when searching "Invited"', :js do
context 'when searching "Invited"' do
before do
visit group_group_members_path(group)
......@@ -86,7 +86,7 @@ RSpec.describe 'Groups > Members > Tabs' do
end
end
context 'when using "Invited" pagination', :js do
context 'when using "Invited" pagination' do
before do
visit group_group_members_path(group)
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Tabs' do
RSpec.describe 'Projects > Members > Tabs', :js do
include Spec::Support::Helpers::Features::MembersHelpers
using RSpec::Parameterized::TableSyntax
......@@ -44,7 +44,7 @@ RSpec.describe 'Projects > Members > Tabs' do
end
end
context 'when searching "Groups"', :js do
context 'when searching "Groups"' do
before do
click_link 'Groups'
......
......@@ -33,7 +33,7 @@ describe('MembersApp', () => {
wrapper = shallowMount(MembersApp, {
localVue,
provide: {
propsData: {
namespace: MEMBER_TYPES.user,
},
store,
......
......@@ -6,7 +6,7 @@ import MembersTabs from '~/members/components/members_tabs.vue';
import { MEMBER_TYPES } from '~/members/constants';
import { pagination } from '../mock_data';
describe('MembersApp', () => {
describe('MembersTabs', () => {
Vue.use(Vuex);
let wrapper;
......@@ -111,10 +111,10 @@ describe('MembersApp', () => {
const membersApps = wrapper.findAllComponents(MembersApp).wrappers;
expect(membersApps[0].attributes('namespace')).toBe(MEMBER_TYPES.user);
expect(membersApps[1].attributes('namespace')).toBe(MEMBER_TYPES.group);
expect(membersApps[2].attributes('namespace')).toBe(MEMBER_TYPES.invite);
expect(membersApps[3].attributes('namespace')).toBe(MEMBER_TYPES.accessRequest);
expect(membersApps[0].props('namespace')).toBe(MEMBER_TYPES.user);
expect(membersApps[1].props('namespace')).toBe(MEMBER_TYPES.group);
expect(membersApps[2].props('namespace')).toBe(MEMBER_TYPES.invite);
expect(membersApps[3].props('namespace')).toBe(MEMBER_TYPES.accessRequest);
});
});
......
import { createWrapper } from '@vue/test-utils';
import MembersApp from '~/members/components/app.vue';
import MembersTabs from '~/members/components/members_tabs.vue';
import { MEMBER_TYPES } from '~/members/constants';
import { initMembersApp } from '~/members/index';
import { members, pagination, dataAttribute } from './mock_data';
......@@ -11,12 +11,13 @@ describe('initMembersApp', () => {
const setup = () => {
vm = initMembersApp(el, {
namespace: MEMBER_TYPES.user,
tableFields: ['account'],
tableAttrs: { table: { 'data-qa-selector': 'members_list' } },
tableSortableFields: ['account'],
requestFormatter: () => ({}),
filteredSearchBar: { show: false },
[MEMBER_TYPES.user]: {
tableFields: ['account'],
tableAttrs: { table: { 'data-qa-selector': 'members_list' } },
tableSortableFields: ['account'],
requestFormatter: () => ({}),
filteredSearchBar: { show: false },
},
});
wrapper = createWrapper(vm);
};
......@@ -35,10 +36,10 @@ describe('initMembersApp', () => {
wrapper = null;
});
it('renders `MembersApp`', () => {
it('renders `MembersTabs`', () => {
setup();
expect(wrapper.find(MembersApp).exists()).toBe(true);
expect(wrapper.find(MembersTabs).exists()).toBe(true);
});
it('parses and sets `members` in Vuex store', () => {
......
import { MEMBER_TYPES } from '~/members/constants';
export const member = {
requestedAt: null,
canUpdate: false,
......@@ -97,10 +99,12 @@ export const pagination = {
};
export const dataAttribute = JSON.stringify({
members,
pagination: paginationData,
[MEMBER_TYPES.user]: {
members,
pagination: paginationData,
member_path: '/groups/foo-bar/-/group_members/:id',
ldap_override_path: '/groups/ldap-group/-/group_members/:id/override',
},
source_id: 234,
can_manage_members: true,
member_path: '/groups/foo-bar/-/group_members/:id',
ldap_override_path: '/groups/ldap-group/-/group_members/:id/override',
});
import { DEFAULT_SORT } from '~/members/constants';
import { DEFAULT_SORT, MEMBER_TYPES } from '~/members/constants';
import {
generateBadges,
isGroup,
......@@ -268,11 +268,13 @@ describe('Members Utils', () => {
it('correctly parses the data attribute', () => {
expect(parseDataAttributes(el)).toMatchObject({
members,
pagination,
[MEMBER_TYPES.user]: {
members,
pagination,
memberPath: '/groups/foo-bar/-/group_members/:id',
},
sourceId: 234,
canManageMembers: true,
memberPath: '/groups/foo-bar/-/group_members/:id',
});
});
});
......
......@@ -23,58 +23,79 @@ RSpec.describe Groups::GroupMembersHelper do
end
end
describe '#group_members_list_data_json' do
let(:group_members) { create_list(:group_member, 2, group: group, created_by: current_user) }
let(:pagination) { {} }
let(:collection) { group_members }
let(:presented_members) { present_members(collection) }
describe '#group_members_app_data_json' do
include_context 'group_group_link'
subject { Gitlab::Json.parse(helper.group_members_list_data_json(group, presented_members, pagination)) }
let(:members) { create_list(:group_member, 2, group: shared_group, created_by: current_user) }
let(:invited) { create_list(:group_member, 2, :invited, group: shared_group, created_by: current_user) }
let!(:access_requests) { create_list(:group_member, 2, :access_request, group: shared_group, created_by: current_user) }
let(:members_collection) { members }
subject do
Gitlab::Json.parse(
helper.group_members_app_data_json(
shared_group,
members: present_members(members_collection),
invited: present_members(invited),
access_requests: present_members(access_requests)
)
)
end
shared_examples 'members.json' do
shared_examples 'members.json' do |member_type|
it 'returns `members` property that matches json schema' do
expect(subject['members'].to_json).to match_schema('members')
expect(subject[member_type]['members'].to_json).to match_schema('members')
end
it 'sets `member_path` property' do
expect(subject[member_type]['member_path']).to eq('/groups/foo-bar/-/group_members/:id')
end
end
before do
allow(helper).to receive(:group_group_member_path).with(group, ':id').and_return('/groups/foo-bar/-/group_members/:id')
allow(helper).to receive(:can?).with(current_user, :admin_group_member, group).and_return(true)
allow(helper).to receive(:group_group_member_path).with(shared_group, ':id').and_return('/groups/foo-bar/-/group_members/:id')
allow(helper).to receive(:group_group_link_path).with(shared_group, ':id').and_return('/groups/foo-bar/-/group_links/:id')
allow(helper).to receive(:can?).with(current_user, :admin_group_member, shared_group).and_return(true)
end
it 'returns expected json' do
expected = {
member_path: '/groups/foo-bar/-/group_members/:id',
source_id: group.id,
source_id: shared_group.id,
can_manage_members: true
}.as_json
expect(subject).to include(expected)
end
context 'for a group member' do
it_behaves_like 'members.json'
context 'group members' do
it_behaves_like 'members.json', 'user'
context 'with user status set' do
let(:user) { create(:user) }
let!(:status) { create(:user_status, user: user) }
let(:group_members) { [create(:group_member, group: group, user: user, created_by: current_user)] }
let(:members) { [create(:group_member, group: shared_group, user: user, created_by: current_user)] }
it_behaves_like 'members.json'
it_behaves_like 'members.json', 'user'
end
end
context 'for an invited group member' do
let(:group_members) { create_list(:group_member, 2, :invited, group: group, created_by: current_user) }
context 'invited group members' do
it_behaves_like 'members.json', 'invite'
end
it_behaves_like 'members.json'
context 'access requests' do
it_behaves_like 'members.json', 'access_request'
end
context 'for an access request' do
let(:group_members) { create_list(:group_member, 2, :access_request, group: group, created_by: current_user) }
context 'group links' do
it 'sets `group.members` property that matches json schema' do
expect(subject['group']['members'].to_json).to match_schema('group_link/group_group_links')
end
it_behaves_like 'members.json'
it 'sets `member_path` property' do
expect(subject['group']['member_path']).to eq('/groups/foo-bar/-/group_links/:id')
end
end
context 'when pagination is not available' do
......@@ -87,13 +108,12 @@ RSpec.describe Groups::GroupMembersHelper do
params: {}
}.as_json
expect(subject['pagination']).to include(expected)
expect(subject['access_request']['pagination']).to include(expected)
end
end
context 'when pagination is available' do
let(:collection) { Kaminari.paginate_array(group_members).page(1).per(1) }
let(:pagination) { { param_name: :page, params: { search_groups: nil } } }
let(:members_collection) { Kaminari.paginate_array(members).page(1).per(1) }
it 'sets `pagination` attribute to expected json' do
expected = {
......@@ -101,41 +121,11 @@ RSpec.describe Groups::GroupMembersHelper do
per_page: 1,
total_items: 2,
param_name: :page,
params: { search_groups: nil }
params: { invited_members_page: nil, search_invited: nil }
}.as_json
expect(subject['pagination']).to include(expected)
expect(subject['user']['pagination']).to include(expected)
end
end
end
describe '#group_group_links_list_data_json' do
include_context 'group_group_link'
subject { Gitlab::Json.parse(helper.group_group_links_list_data_json(shared_group)) }
before do
allow(helper).to receive(:group_group_link_path).with(shared_group, ':id').and_return('/groups/foo-bar/-/group_links/:id')
end
it 'returns expected json' do
expected = {
pagination: {
current_page: nil,
per_page: nil,
total_items: 1,
param_name: nil,
params: {}
},
member_path: '/groups/foo-bar/-/group_links/:id',
source_id: shared_group.id
}.as_json
expect(subject).to include(expected)
end
it 'returns `members` property that matches json schema' do
expect(subject['members'].to_json).to match_schema('group_link/group_group_links')
end
end
end
......@@ -147,16 +147,27 @@ RSpec.describe Projects::ProjectMembersHelper do
end
describe 'project members' do
let_it_be(:project_members) { create_list(:project_member, 2, project: project) }
let_it_be(:members) { create_list(:project_member, 2, project: project) }
let_it_be(:group_links) { create_list(:project_group_link, 1, project: project) }
let_it_be(:invited) { create_list(:project_member, 2, :invited, project: project) }
let_it_be(:access_requests) { create_list(:project_member, 2, :access_request, project: project) }
let(:collection) { project_members }
let(:presented_members) { present_members(collection) }
let(:members_collection) { members }
describe '#project_members_list_data_json' do
describe '#project_members_app_data_json' do
let(:allow_admin_project) { true }
let(:pagination) { {} }
subject { Gitlab::Json.parse(helper.project_members_list_data_json(project, presented_members, pagination)) }
subject do
Gitlab::Json.parse(
helper.project_members_app_data_json(
project,
members: present_members(members_collection),
group_links: group_links,
invited: present_members(invited),
access_requests: present_members(access_requests)
)
)
end
before do
allow(helper).to receive(:project_project_member_path).with(project, ':id').and_return('/foo-bar/-/project_members/:id')
......@@ -164,7 +175,6 @@ RSpec.describe Projects::ProjectMembersHelper do
it 'returns expected json' do
expected = {
member_path: '/foo-bar/-/project_members/:id',
source_id: project.id,
can_manage_members: true
}.as_json
......@@ -172,8 +182,12 @@ RSpec.describe Projects::ProjectMembersHelper do
expect(subject).to include(expected)
end
it 'returns `members` property that matches json schema' do
expect(subject['members'].to_json).to match_schema('members')
it 'sets `members` property that matches json schema' do
expect(subject['user']['members'].to_json).to match_schema('members')
end
it 'sets `member_path` property' do
expect(subject['user']['member_path']).to eq('/foo-bar/-/project_members/:id')
end
context 'when pagination is not available' do
......@@ -186,13 +200,12 @@ RSpec.describe Projects::ProjectMembersHelper do
params: {}
}.as_json
expect(subject['pagination']).to include(expected)
expect(subject['invite']['pagination']).to include(expected)
end
end
context 'when pagination is available' do
let(:collection) { Kaminari.paginate_array(project_members).page(1).per(1) }
let(:pagination) { { param_name: :page, params: { search_groups: nil } } }
let(:members_collection) { Kaminari.paginate_array(members).page(1).per(1) }
it 'sets `pagination` attribute to expected json' do
expected = {
......@@ -203,45 +216,9 @@ RSpec.describe Projects::ProjectMembersHelper do
params: { search_groups: nil }
}.as_json
expect(subject['pagination']).to match(expected)
expect(subject['user']['pagination']).to match(expected)
end
end
end
end
describe 'project group links' do
let_it_be(:project_group_links) { create_list(:project_group_link, 1, project: project) }
let(:allow_admin_project) { true }
describe '#project_group_links_list_data_json' do
subject { Gitlab::Json.parse(helper.project_group_links_list_data_json(project, project_group_links)) }
before do
allow(helper).to receive(:project_group_link_path).with(project, ':id').and_return('/foo-bar/-/group_links/:id')
allow(helper).to receive(:can?).with(current_user, :admin_project_member, project).and_return(true)
end
it 'returns expected json' do
expected = {
pagination: {
current_page: nil,
per_page: nil,
total_items: 1,
param_name: nil,
params: {}
},
member_path: '/foo-bar/-/group_links/:id',
source_id: project.id,
can_manage_members: true
}.as_json
expect(subject).to include(expected)
end
it 'returns `members` property that matches json schema' do
expect(subject['members'].to_json).to match_schema('group_link/project_group_links')
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