Commit 2c0f4f4f authored by Peter Hegman's avatar Peter Hegman Committed by Mayra Cabrera

Pass group member data from HAML to Vue

As MVC pass group membership data as JSON from HAML Vue.
parent 433a839a
<script>
export default {
name: 'GroupMembersApp',
props: {
groupId: {
type: Number,
required: true,
},
currentUserId: {
type: Number,
required: false,
default: null,
},
members: {
type: Array,
required: true,
},
},
};
</script>
<template>
<span>
<!-- Temporary empty template -->
</span>
</template>
import Vue from 'vue';
import App from './components/app.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default el => {
if (!el) {
return () => {};
}
return new Vue({
el,
components: { App },
data() {
const { members, groupId, currentUserId } = this.$options.el.dataset;
return {
members: convertObjectPropsToCamelCase(JSON.parse(members), { deep: true }),
groupId: parseInt(groupId, 10),
...(currentUserId ? { currentUserId: parseInt(currentUserId, 10) } : {}),
};
},
render(createElement) {
return createElement('app', {
props: {
members: this.members,
groupId: this.groupId,
currentUserId: this.currentUserId,
},
});
},
});
};
...@@ -4,6 +4,7 @@ import memberExpirationDate from '~/member_expiration_date'; ...@@ -4,6 +4,7 @@ import memberExpirationDate from '~/member_expiration_date';
import UsersSelect from '~/users_select'; import UsersSelect from '~/users_select';
import groupsSelect from '~/groups_select'; import groupsSelect from '~/groups_select';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue'; import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
import initGroupMembersApp from '~/groups/members';
function mountRemoveMemberModal() { function mountRemoveMemberModal() {
const el = document.querySelector('.js-remove-member-modal'); const el = document.querySelector('.js-remove-member-modal');
...@@ -25,6 +26,11 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -25,6 +26,11 @@ document.addEventListener('DOMContentLoaded', () => {
memberExpirationDate('.js-access-expiration-date-groups'); memberExpirationDate('.js-access-expiration-date-groups');
mountRemoveMemberModal(); mountRemoveMemberModal();
initGroupMembersApp(document.querySelector('.js-group-members-list'));
initGroupMembersApp(document.querySelector('.js-group-linked-list'));
initGroupMembersApp(document.querySelector('.js-group-invited-members-list'));
initGroupMembersApp(document.querySelector('.js-group-access-requests-list'));
new Members(); // eslint-disable-line no-new new Members(); // eslint-disable-line no-new
new UsersSelect(); // eslint-disable-line no-new new UsersSelect(); // eslint-disable-line no-new
}); });
# frozen_string_literal: true # frozen_string_literal: true
module Groups::GroupMembersHelper module Groups::GroupMembersHelper
include AvatarsHelper
AVATAR_SIZE = 40
def group_member_select_options def group_member_select_options
{ multiple: true, class: 'input-clamp qa-member-select-field ', scope: :all, email_user: true } { multiple: true, class: 'input-clamp qa-member-select-field ', scope: :all, email_user: true }
end end
...@@ -8,6 +12,81 @@ module Groups::GroupMembersHelper ...@@ -8,6 +12,81 @@ module Groups::GroupMembersHelper
def render_invite_member_for_group(group, default_access_level) def render_invite_member_for_group(group, default_access_level)
render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: GroupMember.access_level_roles, default_access_level: default_access_level render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: GroupMember.access_level_roles, default_access_level: default_access_level
end end
def linked_groups_data_json(group_links)
GroupGroupLinkSerializer.new.represent(group_links).to_json
end
def members_data_json(group, members)
members_data(group, members).to_json
end
private
def members_data(group, members)
members.map do |member|
user = member.user
source = member.source
data = {
id: member.id,
created_at: member.created_at,
expires_at: member.expires_at&.to_time,
requested_at: member.requested_at,
can_update: member.can_update?,
can_remove: member.can_remove?,
can_override: member.can_override?,
access_level: {
string_value: member.human_access,
integer_value: member.access_level
},
source: {
id: source.id,
name: source.full_name,
web_url: Gitlab::UrlBuilder.build(source)
}
}.merge(member_created_by_data(member.created_by))
if user.present?
data[:user] = member_user_data(user)
else
data[:invite] = member_invite_data(member)
end
data
end
end
def member_created_by_data(created_by)
return {} unless created_by.present?
{
created_by: {
name: created_by.name,
web_url: Gitlab::UrlBuilder.build(created_by)
}
}
end
def member_user_data(user)
{
id: user.id,
name: user.name,
username: user.username,
web_url: Gitlab::UrlBuilder.build(user),
avatar_url: avatar_icon_for_user(user, AVATAR_SIZE),
blocked: user.blocked?,
two_factor_enabled: user.two_factor_enabled?
}
end
def member_invite_data(member)
{
email: member.invite_email,
avatar_url: avatar_icon_for_email(member.invite_email, AVATAR_SIZE),
can_resend: member.can_resend_invite?
}
end
end end
Groups::GroupMembersHelper.prepend_if_ee('EE::Groups::GroupMembersHelper') Groups::GroupMembersHelper.prepend_if_ee('EE::Groups::GroupMembersHelper')
# frozen_string_literal: true
class GroupGroupLinkEntity < Grape::Entity
expose :id
expose :created_at
expose :expires_at do |group_link|
group_link.expires_at&.to_time
end
expose :access_level do
expose :human_access, as: :string_value
expose :group_access, as: :integer_value
end
expose :shared_with_group do
expose :avatar_url do |group_link|
group_link.shared_with_group.avatar_url(only_path: false)
end
expose :web_url do |group_link|
group_link.shared_with_group.web_url
end
expose :shared_with_group, merge: true, using: GroupBasicEntity
end
end
# frozen_string_literal: true
class GroupGroupLinkSerializer < BaseSerializer
entity GroupGroupLinkEntity
end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
- show_invited_members = can_manage_members && @invited_members.exists? - show_invited_members = can_manage_members && @invited_members.exists?
- show_access_requests = can_manage_members && @requesters.exists? - show_access_requests = can_manage_members && @requesters.exists?
- invited_active = params[:search_invited].present? || params[:invited_members_page].present? - invited_active = params[:search_invited].present? || params[:invited_members_page].present?
- vue_members_list_enabled = Feature.enabled?(:vue_group_members_list, @group)
- data_attributes = { group_id: @group.id, current_user_id: current_user&.id }
- form_item_label_css_class = 'label-bold gl-mr-2 gl-mb-0 gl-py-2 align-self-md-center' - form_item_label_css_class = 'label-bold gl-mr-2 gl-mb-0 gl-py-2 align-self-md-center'
...@@ -66,18 +68,24 @@ ...@@ -66,18 +68,24 @@
= render 'groups/group_members/tab_pane/form_item' do = render 'groups/group_members/tab_pane/form_item' do
= label_tag :sort_by, _('Sort by'), class: form_item_label_css_class = label_tag :sort_by, _('Sort by'), class: form_item_label_css_class
= render 'shared/members/sort_dropdown' = render 'shared/members/sort_dropdown'
%ul.content-list.members-list{ data: { qa_selector: 'members_list' } } - if vue_members_list_enabled
= render partial: 'shared/members/member', collection: @members, as: :member .js-group-members-list{ data: { members: members_data_json(@group, @members), **data_attributes } }
= paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil } - else
%ul.content-list.members-list{ data: { qa_selector: 'members_list' } }
= render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil }
- if @group.shared_with_group_links.any? - if @group.shared_with_group_links.any?
#tab-groups.tab-pane #tab-groups.tab-pane
.card.card-without-border .card.card-without-border
= render 'groups/group_members/tab_pane/header' do = render 'groups/group_members/tab_pane/header' do
= render 'groups/group_members/tab_pane/title' do = render 'groups/group_members/tab_pane/title' do
= html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } = html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
%ul.content-list.members-list{ data: { qa_selector: 'groups_list' } } - if vue_members_list_enabled
- @group.shared_with_group_links.each do |group_link| .js-group-linked-list{ data: { members: linked_groups_data_json(@group.shared_with_group_links), **data_attributes } }
= render 'shared/members/group', group_link: group_link, can_admin_member: can_manage_members, group_link_path: group_group_link_path(@group, group_link) - else
%ul.content-list.members-list{ data: { qa_selector: 'groups_list' } }
- @group.shared_with_group_links.each do |group_link|
= render 'shared/members/group', group_link: group_link, can_admin_member: can_manage_members, group_link_path: group_group_link_path(@group, group_link)
- if show_invited_members - if show_invited_members
#tab-invited-members.tab-pane{ class: ('active' if invited_active) } #tab-invited-members.tab-pane{ class: ('active' if invited_active) }
.card.card-without-border .card.card-without-border
...@@ -86,14 +94,20 @@ ...@@ -86,14 +94,20 @@
= html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } = html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do
= render 'shared/members/search_field', name: 'search_invited' = render 'shared/members/search_field', name: 'search_invited'
%ul.content-list.members-list - if vue_members_list_enabled
= render partial: 'shared/members/member', collection: @invited_members, as: :member .js-group-invited-members-list{ data: { members: members_data_json(@group, @invited_members), **data_attributes } }
= paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil } - else
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @invited_members, as: :member
= paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil }
- if show_access_requests - if show_access_requests
#tab-access-requests.tab-pane #tab-access-requests.tab-pane
.card.card-without-border .card.card-without-border
= render 'groups/group_members/tab_pane/header' do = render 'groups/group_members/tab_pane/header' do
= render 'groups/group_members/tab_pane/title' do = render 'groups/group_members/tab_pane/title' do
= html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } = html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
%ul.content-list.members-list - if vue_members_list_enabled
= render partial: 'shared/members/member', collection: @requesters, as: :member .js-group-access-requests-list{ data: { members: members_data_json(@group, @requesters), **data_attributes } }
- else
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @requesters, as: :member
---
name: vue_group_members_list
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40548
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241194
group: group::access
type: development
default_enabled: false
\ No newline at end of file
...@@ -7,4 +7,19 @@ module EE::Groups::GroupMembersHelper ...@@ -7,4 +7,19 @@ module EE::Groups::GroupMembersHelper
def group_member_select_options def group_member_select_options
super.merge(skip_ldap: @group.ldap_synced?) super.merge(skip_ldap: @group.ldap_synced?)
end end
private
override :members_data
def members_data(group, members)
ce_members = super(group, members)
members.map.with_index do |member, index|
ce_members[index].merge({
using_license: can?(current_user, :owner_access, group) && member.user&.using_gitlab_com_seat?(group),
group_sso: member.user&.group_sso?(group),
group_managed_account: member.user&.group_managed_account?
})
end
end
end end
...@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Audit Events', :js do ...@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Audit Events', :js do
let(:group) { create(:group) } let(:group) { create(:group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
group.add_owner(user) group.add_owner(user)
group.add_developer(alex) group.add_developer(alex)
sign_in(user) sign_in(user)
......
...@@ -6,6 +6,10 @@ RSpec.describe 'Groups > Members > List members' do ...@@ -6,6 +6,10 @@ RSpec.describe 'Groups > Members > List members' do
let(:user2) { create(:user, name: 'Mary Jane') } let(:user2) { create(:user, name: 'Mary Jane') }
let(:group) { create(:group) } let(:group) { create(:group) }
before do
stub_feature_flags(vue_group_members_list: false)
end
context 'with Group SAML identity linked for a user' do context 'with Group SAML identity linked for a user' do
let(:saml_provider) { create(:saml_provider) } let(:saml_provider) { create(:saml_provider) }
let(:group) { saml_provider.group } let(:group) { saml_provider.group }
......
...@@ -16,6 +16,8 @@ RSpec.describe 'Groups > Members > Maintainer/Owner can override LDAP access lev ...@@ -16,6 +16,8 @@ RSpec.describe 'Groups > Members > Maintainer/Owner can override LDAP access lev
let!(:regular_member) { create(:group_member, :guest, group: group, user: maryjane, ldap: false) } let!(:regular_member) { create(:group_member, :guest, group: group, user: maryjane, ldap: false) }
before do before do
stub_feature_flags(vue_group_members_list: false)
# We need to actually activate the LDAP config otherwise `Group#ldap_synced?` will always be false! # We need to actually activate the LDAP config otherwise `Group#ldap_synced?` will always be false!
allow(Gitlab.config.ldap).to receive_messages(enabled: true) allow(Gitlab.config.ldap).to receive_messages(enabled: true)
......
{
"type": "array",
"items": {
"allOf": [
{ "$ref": "../../../../../spec/fixtures/api/schemas/group_member.json" },
{
"required": ["using_license", "group_sso", "group_managed_account"],
"properties": {
"using_license": { "type": ["boolean", "null"] },
"group_sso": { "type": ["boolean", "null"] },
"group_managed_account": { "type": ["boolean", "null"] }
}
}
]
}
}
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require "spec_helper" require "spec_helper"
RSpec.describe Groups::GroupMembersHelper do RSpec.describe Groups::GroupMembersHelper do
include MembersPresentation
describe '.group_member_select_options' do describe '.group_member_select_options' do
let(:group) { create(:group) } let(:group) { create(:group) }
...@@ -14,4 +16,35 @@ RSpec.describe Groups::GroupMembersHelper do ...@@ -14,4 +16,35 @@ RSpec.describe Groups::GroupMembersHelper do
expect(helper.group_member_select_options).to include(skip_ldap: false) expect(helper.group_member_select_options).to include(skip_ldap: false)
end end
end end
describe '#members_data' do
let(:current_user) { create(:user) }
let(:group) { create(:group) }
let(:group_member) { create(:group_member, group: group, created_by: current_user) }
subject { helper.send('members_data', group, present_members([group_member])) }
before do
allow(helper).to receive(:can?).with(current_user, :owner_access, group).and_return(true)
allow(helper).to receive(:current_user).and_return(current_user)
end
it 'adds `using_license` property to hash' do
allow(group_member.user).to receive(:using_gitlab_com_seat?).with(group).and_return(true)
expect(subject.first).to include(using_license: true)
end
it 'adds `group_sso` property to hash' do
allow(group_member.user).to receive(:group_sso?).with(group).and_return(true)
expect(subject.first).to include(group_sso: true)
end
it 'adds `group_managed_account` property to hash' do
allow(group_member.user).to receive(:group_managed_account?).and_return(true)
expect(subject.first).to include(group_managed_account: true)
end
end
end end
...@@ -11,6 +11,8 @@ RSpec.describe 'Admin Groups' do ...@@ -11,6 +11,8 @@ RSpec.describe 'Admin Groups' do
let!(:current_user) { create(:admin) } let!(:current_user) { create(:admin) }
before do before do
stub_feature_flags(vue_group_members_list: false)
sign_in(current_user) sign_in(current_user)
stub_application_setting(default_group_visibility: internal) stub_application_setting(default_group_visibility: internal)
end end
......
...@@ -10,6 +10,8 @@ RSpec.describe 'Groups > Members > Filter members' do ...@@ -10,6 +10,8 @@ RSpec.describe 'Groups > Members > Filter members' do
let(:nested_group) { create(:group, parent: group) } let(:nested_group) { create(:group, parent: group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
group.add_owner(user) group.add_owner(user)
group.add_maintainer(user_with_2fa) group.add_maintainer(user_with_2fa)
nested_group.add_maintainer(nested_group_user) nested_group.add_maintainer(nested_group_user)
......
...@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Leave group' do ...@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Leave group' do
let(:group) { create(:group) } let(:group) { create(:group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
gitlab_sign_in(user) gitlab_sign_in(user)
end end
......
...@@ -12,6 +12,8 @@ RSpec.describe 'Groups > Members > List members' do ...@@ -12,6 +12,8 @@ RSpec.describe 'Groups > Members > List members' do
let(:nested_group) { create(:group, parent: group) } let(:nested_group) { create(:group, parent: group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
sign_in(user1) sign_in(user1)
end end
......
...@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage groups', :js do ...@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
let(:shared_group) { create(:group) } let(:shared_group) { create(:group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
shared_group.add_owner(user) shared_group.add_owner(user)
sign_in(user) sign_in(user)
end end
......
...@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage members' do ...@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage members' do
let(:group) { create(:group) } let(:group) { create(:group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
sign_in(user1) sign_in(user1)
end end
......
...@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js ...@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
let(:group) { create(:group) } let(:group) { create(:group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
group.add_owner(user1) group.add_owner(user1)
sign_in(user1) sign_in(user1)
end end
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Groups > Members > Maintainer manages access requests' do RSpec.describe 'Groups > Members > Maintainer manages access requests' do
before do
stub_feature_flags(vue_group_members_list: false)
end
it_behaves_like 'Maintainer manages access requests' do it_behaves_like 'Maintainer manages access requests' do
let(:has_tabs) { true } let(:has_tabs) { true }
let(:entity) { create(:group, :public) } let(:entity) { create(:group, :public) }
......
...@@ -14,6 +14,8 @@ RSpec.describe 'Search group member' do ...@@ -14,6 +14,8 @@ RSpec.describe 'Search group member' do
end end
before do before do
stub_feature_flags(vue_group_members_list: false)
sign_in(user) sign_in(user)
visit group_group_members_path(guest_group) visit group_group_members_path(guest_group)
end end
......
...@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Sort members' do ...@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Sort members' do
let(:group) { create(:group) } let(:group) { create(:group) }
before do before do
stub_feature_flags(vue_group_members_list: false)
create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago) create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago)
create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago) create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago)
......
{
"type": "object",
"required": ["id", "created_at", "expires_at", "access_level"],
"properties": {
"id": { "type": "integer" },
"created_at": { "type": "date-time" },
"expires_at": { "type": ["date-time", "null"] },
"access_level": {
"type": "object",
"required": ["integer_value", "string_value"],
"properties": {
"integer_value": { "type": "integer" },
"string_value": { "type": "string" }
}
},
"shared_with_group": {
"type": "object",
"required": ["id", "name", "full_name", "full_path", "avatar_url", "web_url"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"full_name": { "type": "string" },
"full_path": { "type": "string" },
"avatar_url": { "type": ["string", "null"] },
"web_url": { "type": "string" }
}
}
}
}
{
"type": "array",
"items": {
"$ref": "entities/group_group_link.json"
}
}
{
"type": "object",
"required": [
"id",
"created_at",
"expires_at",
"access_level",
"requested_at",
"source",
"can_update",
"can_remove",
"can_override"
],
"properties": {
"id": { "type": "integer" },
"created_at": { "type": "date-time" },
"expires_at": { "type": ["date-time", "null"] },
"requested_at": { "type": ["date-time", "null"] },
"can_update": { "type": "boolean" },
"can_remove": { "type": "boolean" },
"can_override": { "type": "boolean" },
"access_level": {
"type": "object",
"required": ["integer_value", "string_value"],
"properties": {
"integer_value": { "type": "integer" },
"string_value": { "type": "string" }
}
},
"source": {
"type": "object",
"required": ["id", "name", "web_url"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"web_url": { "type": "string" }
}
},
"created_by": {
"type": "object",
"required": ["name", "web_url"],
"properties": {
"name": { "type": "string" },
"web_url": { "type": "string" }
}
},
"user": {
"type": "object",
"required": [
"id",
"name",
"username",
"avatar_url",
"web_url",
"blocked",
"two_factor_enabled"
],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"username": { "type": "string" },
"avatar_url": { "type": ["string", "null"] },
"web_url": { "type": "string" },
"blocked": { "type": "boolean" },
"two_factor_enabled": { "type": "boolean" }
}
},
"invite": {
"type": "object",
"required": ["email", "avatar_url", "can_resend"],
"properties": {
"email": { "type": "string" },
"avatar_url": { "type": "string" },
"can_resend": { "type": "boolean" }
}
}
}
}
{
"type": "array",
"items": {
"$ref": "group_member.json"
}
}
import { createWrapper } from '@vue/test-utils';
import initGroupMembersApp from '~/groups/members';
import GroupMembersApp from '~/groups/members/components/app.vue';
import { membersJsonString, membersParsed } from './mock_data';
describe('initGroupMembersApp', () => {
let el;
let wrapper;
const setup = () => {
const vm = initGroupMembersApp(el);
wrapper = createWrapper(vm);
};
const getGroupMembersApp = () => wrapper.find(GroupMembersApp);
beforeEach(() => {
el = document.createElement('div');
el.setAttribute('data-members', membersJsonString);
el.setAttribute('data-current-user-id', '123');
el.setAttribute('data-group-id', '234');
document.body.appendChild(el);
});
afterEach(() => {
document.body.innerHTML = '';
el = null;
wrapper.destroy();
wrapper = null;
});
it('parses and passes `currentUserId` prop to `GroupMembersApp`', () => {
setup();
expect(getGroupMembersApp().props('currentUserId')).toBe(123);
});
it('does not pass `currentUserId` prop if not provided by the data attribute (user is not logged in)', () => {
el.removeAttribute('data-current-user-id');
setup();
expect(getGroupMembersApp().props('currentUserId')).toBeNull();
});
it('parses and passes `groupId` prop to `GroupMembersApp`', () => {
setup();
expect(getGroupMembersApp().props('groupId')).toBe(234);
});
it('parses and passes `members` prop to `GroupMembersApp`', () => {
setup();
expect(getGroupMembersApp().props('members')).toEqual(membersParsed);
});
});
export const membersJsonString =
'[{"requested_at":null,"can_update":true,"can_remove":true,"can_override":false,"access_level":{"integer_value":50,"string_value":"Owner"},"source":{"id":323,"name":"My group / my subgroup","web_url":"http://127.0.0.1:3000/groups/my-group/my-subgroup"},"user":{"id":1,"name":"Administrator","username":"root","web_url":"http://127.0.0.1:3000/root","avatar_url":"https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80\u0026d=identicon","blocked":false,"two_factor_enabled":false},"id":524,"created_at":"2020-08-21T21:33:27.631Z","expires_at":null,"using_license":false,"group_sso":false,"group_managed_account":false}]';
export const membersParsed = [
{
requestedAt: null,
canUpdate: true,
canRemove: true,
canOverride: false,
accessLevel: { integerValue: 50, stringValue: 'Owner' },
source: {
id: 323,
name: 'My group / my subgroup',
webUrl: 'http://127.0.0.1:3000/groups/my-group/my-subgroup',
},
user: {
id: 1,
name: 'Administrator',
username: 'root',
webUrl: 'http://127.0.0.1:3000/root',
avatarUrl:
'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon',
blocked: false,
twoFactorEnabled: false,
},
id: 524,
createdAt: '2020-08-21T21:33:27.631Z',
expiresAt: null,
usingLicense: false,
groupSso: false,
groupManagedAccount: false,
},
];
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require "spec_helper" require "spec_helper"
RSpec.describe Groups::GroupMembersHelper do RSpec.describe Groups::GroupMembersHelper do
include MembersPresentation
describe '.group_member_select_options' do describe '.group_member_select_options' do
let(:group) { create(:group) } let(:group) { create(:group) }
...@@ -14,4 +16,50 @@ RSpec.describe Groups::GroupMembersHelper do ...@@ -14,4 +16,50 @@ RSpec.describe Groups::GroupMembersHelper do
expect(helper.group_member_select_options).to include(multiple: true, scope: :all, email_user: true) expect(helper.group_member_select_options).to include(multiple: true, scope: :all, email_user: true)
end end
end end
describe '#linked_groups_data_json' do
include_context 'group_group_link'
it 'matches json schema' do
json = helper.linked_groups_data_json(shared_group.shared_with_group_links)
expect(json).to match_schema('group_group_links')
end
end
describe '#members_data_json' do
let(:current_user) { create(:user) }
let(:group) { create(:group) }
before do
allow(helper).to receive(:can?).with(current_user, :owner_access, group).and_return(true)
allow(helper).to receive(:current_user).and_return(current_user)
end
shared_examples 'group_members.json' do
it 'matches json schema' do
json = helper.members_data_json(group, present_members([group_member]))
expect(json).to match_schema('group_members')
end
end
context 'for a group member' do
let(:group_member) { create(:group_member, group: group, created_by: current_user) }
it_behaves_like 'group_members.json'
end
context 'for an invited group member' do
let(:group_member) { create(:group_member, :invited, group: group, created_by: current_user) }
it_behaves_like 'group_members.json'
end
context 'for an access request' do
let(:group_member) { create(:group_member, :access_request, group: group, created_by: current_user) }
it_behaves_like 'group_members.json'
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GroupGroupLinkEntity do
include_context 'group_group_link'
subject(:json) { described_class.new(group_group_link).to_json }
it 'matches json schema' do
expect(json).to match_schema('entities/group_group_link')
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GroupGroupLinkSerializer do
include_context 'group_group_link'
subject(:json) { described_class.new.represent(shared_group.shared_with_group_links).to_json }
it 'matches json schema' do
expect(json).to match_schema('group_group_links')
end
end
# frozen_string_literal: true
RSpec.shared_context 'group_group_link' do
let(:shared_with_group) { create(:group) }
let(:shared_group) { create(:group) }
let!(:group_group_link) do
create(
:group_group_link,
{
shared_group: shared_group,
shared_with_group: shared_with_group,
expires_at: '2020-05-12'
}
)
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