Commit 0b68aadb authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch...

Merge branch '325339-invite-members-modal-old-form-is-shown-when-member-lock-is-enabled' into 'master'

Fix invite group form being shown when member locked

See merge request gitlab-org/gitlab!60245
parents 731397ff 6a3f5943
...@@ -11,6 +11,10 @@ module InviteMembersHelper ...@@ -11,6 +11,10 @@ module InviteMembersHelper
Feature.enabled?(:invite_members_group_modal, project.group) && can_manage_project_members?(project) Feature.enabled?(:invite_members_group_modal, project.group) && can_manage_project_members?(project)
end end
def can_invite_group_for_project?(project)
Feature.enabled?(:invite_members_group_modal, project.group) && project.allowed_to_share_with_group?
end
def directly_invite_members? def directly_invite_members?
strong_memoize(:directly_invite_members) do strong_memoize(:directly_invite_members) do
can_import_members? can_import_members?
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.js-remove-member-modal .js-remove-member-modal
.row.gl-mt-3 .row.gl-mt-3
.col-lg-12 .col-lg-12
- if can_invite_members_for_project?(@project) - if can_invite_members_for_project?(@project) || can_invite_group_for_project?(@project)
.row .row
.col-md-12.col-lg-6.gl-display-flex .col-md-12.col-lg-6.gl-display-flex
.gl-flex-direction-column.gl-flex-wrap.align-items-baseline .gl-flex-direction-column.gl-flex-wrap.align-items-baseline
...@@ -18,11 +18,14 @@ ...@@ -18,11 +18,14 @@
= html_escape(_("Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}")) % { i_open: '<i>'.html_safe, i_close: '</i>'.html_safe } = html_escape(_("Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}")) % { i_open: '<i>'.html_safe, i_close: '</i>'.html_safe }
.col-md-12.col-lg-6 .col-md-12.col-lg-6
.gl-display-flex.gl-flex-wrap.gl-justify-content-end .gl-display-flex.gl-flex-wrap.gl-justify-content-end
- if can_import_members?
= link_to _("Import a project"), = link_to _("Import a project"),
import_project_project_members_path(@project), import_project_project_members_path(@project),
class: "btn btn-default btn-md gl-button gl-mt-3 gl-sm-w-auto gl-w-full", class: "btn btn-default btn-md gl-button gl-mt-3 gl-sm-w-auto gl-w-full",
title: _("Import members from another project") title: _("Import members from another project")
- if @project.allowed_to_share_with_group?
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } } .js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } }
- if !membership_locked?
.js-invite-members-trigger{ data: { variant: 'success', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite members') } } .js-invite-members-trigger{ data: { variant: 'success', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite members') } }
= render 'projects/invite_members_modal', project: @project = render 'projects/invite_members_modal', project: @project
...@@ -36,7 +39,7 @@ ...@@ -36,7 +39,7 @@
%p %p
= html_escape(_("Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}")) % { i_open: '<i>'.html_safe, i_close: '</i>'.html_safe } = html_escape(_("Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}")) % { i_open: '<i>'.html_safe, i_close: '</i>'.html_safe }
- if !can_invite_members_for_project?(@project) && can_manage_project_members?(@project) && project_can_be_shared? - if Feature.disabled?(:invite_members_group_modal, @project.group) && can_manage_project_members?(@project) && project_can_be_shared?
- if !membership_locked? && @project.allowed_to_share_with_group? - if !membership_locked? && @project.allowed_to_share_with_group?
%ul.nav-links.nav.nav-tabs.gitlab-tabs{ role: 'tablist' } %ul.nav-links.nav.nav-tabs.gitlab-tabs{ role: 'tablist' }
%li.nav-tab{ role: 'presentation' } %li.nav-tab{ role: 'presentation' }
......
...@@ -2,15 +2,37 @@ ...@@ -2,15 +2,37 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Project > Members > Invite group and members', :js do RSpec.describe 'Project > Members > Invite group and members' do
include Select2Helper include Select2Helper
include ActionView::Helpers::DateHelper include ActionView::Helpers::DateHelper
include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user) } let(:maintainer) { create(:user) }
using RSpec::Parameterized::TableSyntax
where(:invite_members_group_modal_enabled, :expected_invite_member_selector, :expected_invite_group_selector, :expected_import_button_text) do
true | '.js-invite-members-trigger' | '.js-invite-group-trigger' | 'Import a project'
false | '#invite-member-tab' | '#invite-group-tab' | 'Import'
end
with_them do
before do before do
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: invite_members_group_modal_enabled)
end
it 'displays either the invite modal button triggers or the form with tabs based on the feature flag' do
project = create(:project, namespace: create(:group))
project.add_maintainer(maintainer)
sign_in(maintainer)
visit project_project_members_path(project)
expect(page).to have_selector(expected_invite_member_selector)
expect(page).to have_selector(expected_invite_group_selector)
expect(page).to have_link(expected_import_button_text)
end
end end
describe 'Share group lock' do describe 'Share group lock' do
...@@ -18,10 +40,8 @@ RSpec.describe 'Project > Members > Invite group and members', :js do ...@@ -18,10 +40,8 @@ RSpec.describe 'Project > Members > Invite group and members', :js do
it 'user is only able to share with members' do it 'user is only able to share with members' do
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).not_to have_selector('#invite-member-tab') expect(page).not_to have_selector('.js-invite-group-trigger')
expect(page).not_to have_selector('#invite-group-tab') expect(page).to have_selector('.js-invite-members-trigger')
expect(page).not_to have_selector('.invite-group')
expect(page).to have_selector('.invite-member')
end end
end end
...@@ -29,32 +49,26 @@ RSpec.describe 'Project > Members > Invite group and members', :js do ...@@ -29,32 +49,26 @@ RSpec.describe 'Project > Members > Invite group and members', :js do
it 'user is only able to share with groups' do it 'user is only able to share with groups' do
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).not_to have_selector('#invite-member-tab') expect(page).not_to have_selector('.js-invite-members-trigger')
expect(page).not_to have_selector('#invite-group-tab') expect(page).to have_selector('.js-invite-group-trigger')
expect(page).not_to have_selector('.invite-member')
expect(page).to have_selector('.invite-group')
end end
end end
shared_examples 'the project cannot be shared with groups and members' do shared_examples 'the project cannot be shared with groups and members' do
it 'no tabs or share content exists' do it 'no invite member or invite group exists' do
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).not_to have_selector('#invite-member-tab') expect(page).not_to have_selector('.js-invite-members-trigger')
expect(page).not_to have_selector('#invite-group-tab') expect(page).not_to have_selector('.js-invite-group-trigger')
expect(page).not_to have_selector('.invite-member')
expect(page).not_to have_selector('.invite-group')
end end
end end
shared_examples 'the project can be shared with groups and members' do shared_examples 'the project can be shared with groups and members' do
it 'both member and group tabs exist' do it 'both member and group buttons exist' do
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).not_to have_selector('.invite-member') expect(page).to have_selector('.js-invite-members-trigger')
expect(page).not_to have_selector('.invite-group') expect(page).to have_selector('.js-invite-group-trigger')
expect(page).to have_selector('#invite-member-tab')
expect(page).to have_selector('#invite-group-tab')
end end
end end
...@@ -70,44 +84,6 @@ RSpec.describe 'Project > Members > Invite group and members', :js do ...@@ -70,44 +84,6 @@ RSpec.describe 'Project > Members > Invite group and members', :js do
context 'when the group has "Share with group lock" and "Member lock" disabled' do context 'when the group has "Share with group lock" and "Member lock" disabled' do
it_behaves_like 'the project can be shared with groups and members' it_behaves_like 'the project can be shared with groups and members'
it 'allows the project to be shared with another group using the invite form' do
stub_feature_flags(invite_members_group_modal: false)
visit project_project_members_path(project)
click_on 'invite-group-tab'
select2 group_to_share_with.id, from: '#link_group_id'
page.find('body').click
find('.btn-confirm').click
click_link 'Groups'
page.within(members_table) do
expect(page).to have_content(group_to_share_with.name)
end
end
it 'allows the project to be shared with another group using the invite modal' do
stub_feature_flags(invite_members_group_modal: true)
visit project_project_members_path(project)
click_on 'Invite a group'
click_on 'Select a group'
wait_for_requests
click_button group_to_share_with.name
click_button 'Invite'
visit project_project_members_path(project)
click_link 'Groups'
page.within(members_table) do
expect(page).to have_content(group_to_share_with.name)
end
end
end end
context 'when the group has "Share with group lock" enabled' do context 'when the group has "Share with group lock" enabled' do
......
...@@ -8,7 +8,6 @@ RSpec.describe "User manages members" do ...@@ -8,7 +8,6 @@ RSpec.describe "User manages members" do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
before do before do
stub_feature_flags(invite_members_group_modal: false)
sign_in(user) sign_in(user)
end end
...@@ -19,7 +18,9 @@ RSpec.describe "User manages members" do ...@@ -19,7 +18,9 @@ RSpec.describe "User manages members" do
visit(project_project_members_path(project)) visit(project_project_members_path(project))
end end
it { expect(page).to have_link("Import members").and have_selector(".project-access-select") } it { expect(page).to have_selector(".js-invite-members-trigger") }
it { expect(page).to have_selector(".js-invite-group-trigger") }
it { expect(page).to have_link("Import a project") }
end end
shared_examples "when group membership is locked" do shared_examples "when group membership is locked" do
...@@ -29,11 +30,56 @@ RSpec.describe "User manages members" do ...@@ -29,11 +30,56 @@ RSpec.describe "User manages members" do
visit(project_project_members_path(project)) visit(project_project_members_path(project))
end end
it { expect(page).to have_no_selector(".js-invite-members-trigger") }
it { expect(page).to have_selector(".js-invite-group-trigger") }
end
context "as project maintainer" do
before do
stub_feature_flags(invite_members_group_modal: true)
project.add_maintainer(user)
end
it_behaves_like "when group membership is unlocked"
it_behaves_like "when group membership is locked"
end
context "as group owner" do
before do
stub_feature_flags(invite_members_group_modal: true)
group.add_owner(user)
end
it_behaves_like "when group membership is unlocked"
it_behaves_like "when group membership is locked"
end
context 'when feature flag :invite_members_group_modal is disabled' do
shared_examples "when group membership is unlocked" do
before do
group.update!(membership_lock: false)
visit(project_project_members_path(project))
end
it { expect(page).to have_link("Import members").and have_selector(".project-access-select") }
end
shared_examples 'when group membership is locked' do
before do
group.update!(membership_lock: true)
project.add_maintainer(user)
visit(project_project_members_path(project))
end
it { expect(page).to have_no_selector(".invite-users-form") } it { expect(page).to have_no_selector(".invite-users-form") }
it { expect(page).to have_selector(".invite-group-form") }
end end
context "as project maintainer" do context "as project maintainer" do
before do before do
stub_feature_flags(invite_members_group_modal: false)
project.add_maintainer(user) project.add_maintainer(user)
end end
...@@ -43,10 +89,12 @@ RSpec.describe "User manages members" do ...@@ -43,10 +89,12 @@ RSpec.describe "User manages members" do
context "as group owner" do context "as group owner" do
before do before do
stub_feature_flags(invite_members_group_modal: false)
group.add_owner(user) group.add_owner(user)
end end
it_behaves_like "when group membership is unlocked" it_behaves_like "when group membership is unlocked"
it_behaves_like "when group membership is locked" it_behaves_like "when group membership is locked"
end end
end
end end
...@@ -12,6 +12,7 @@ RSpec.describe 'Groups > Members > Manage groups', :js do ...@@ -12,6 +12,7 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
sign_in(user) sign_in(user)
end end
context 'with invite_members_group_modal disabled' do
context 'when group link does not exist' do context 'when group link does not exist' do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:group_to_add) { create(:group) } let_it_be(:group_to_add) { create(:group) }
...@@ -33,6 +34,26 @@ RSpec.describe 'Groups > Members > Manage groups', :js do ...@@ -33,6 +34,26 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
end end
end end
end end
end
context 'when group link does not exist' do
it 'can share a group with group' do
group = create(:group)
group_to_add = create(:group)
group.add_owner(user)
group_to_add.add_owner(user)
visit group_group_members_path(group)
invite_group(group_to_add.name, 'Reporter')
click_groups_tab
page.within(first_row) do
expect(page).to have_content(group_to_add.name)
expect(page).to have_content('Reporter')
end
end
end
context 'when group link exists' do context 'when group link exists' do
let_it_be(:shared_with_group) { create(:group) } let_it_be(:shared_with_group) { create(:group) }
...@@ -126,6 +147,21 @@ RSpec.describe 'Groups > Members > Manage groups', :js do ...@@ -126,6 +147,21 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
end end
end end
def invite_group(name, role)
click_on 'Invite a group'
click_on 'Select a group'
wait_for_requests
click_button name
click_button 'Guest'
wait_for_requests
click_button role
click_button 'Invite'
page.refresh
end
def click_groups_tab def click_groups_tab
expect(page).to have_link 'Groups' expect(page).to have_link 'Groups'
click_link "Groups" click_link "Groups"
......
...@@ -9,22 +9,44 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -9,22 +9,44 @@ RSpec.describe 'Project > Members > Invite group', :js do
let(:maintainer) { create(:user) } let(:maintainer) { create(:user) }
using RSpec::Parameterized::TableSyntax
where(:invite_members_group_modal_enabled, :expected_invite_group_selector) do
true | 'button[data-qa-selector="invite_a_group_button"]'
false | '#invite-group-tab'
end
with_them do
before do before do
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: invite_members_group_modal_enabled)
end
it 'displays either the invite group button or the form with tabs based on the feature flag' do
project = create(:project, namespace: create(:group))
project.add_maintainer(maintainer)
sign_in(maintainer)
visit project_project_members_path(project)
expect(page).to have_selector(expected_invite_group_selector)
end
end end
describe 'Share with group lock' do describe 'Share with group lock' do
let(:invite_group_selector) { 'button[data-qa-selector="invite_a_group_button"]' }
shared_examples 'the project can be shared with groups' do shared_examples 'the project can be shared with groups' do
it 'the "Invite group" tab exists' do it 'the "Invite a group" button exists' do
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).to have_selector('#invite-group-tab') expect(page).to have_selector(invite_group_selector)
end end
end end
shared_examples 'the project cannot be shared with groups' do shared_examples 'the project cannot be shared with groups' do
it 'the "Invite group" tab does not exist' do it 'the "Invite a group" button does not exist' do
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).not_to have_selector('#invite-group-tab') expect(page).not_to have_selector(invite_group_selector)
end end
end end
...@@ -41,7 +63,9 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -41,7 +63,9 @@ RSpec.describe 'Project > Members > Invite group', :js do
context 'when the group has "Share with group lock" disabled' do context 'when the group has "Share with group lock" disabled' do
it_behaves_like 'the project can be shared with groups' it_behaves_like 'the project can be shared with groups'
it 'the project can be shared with another group' do it 'the project can be shared with another group when the feature flag invite_members_group_modal is disabled' do
stub_feature_flags(invite_members_group_modal: false)
visit project_project_members_path(project) visit project_project_members_path(project)
expect(page).not_to have_link 'Groups' expect(page).not_to have_link 'Groups'
...@@ -56,6 +80,27 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -56,6 +80,27 @@ RSpec.describe 'Project > Members > Invite group', :js do
expect(members_table).to have_content(group_to_share_with.name) expect(members_table).to have_content(group_to_share_with.name)
end end
it 'the project can be shared with another group when the feature flag invite_members_group_modal is enabled' do
stub_feature_flags(invite_members_group_modal: true)
visit project_project_members_path(project)
expect(page).not_to have_link 'Groups'
click_on 'Invite a group'
click_on 'Select a group'
wait_for_requests
click_button group_to_share_with.name
click_button 'Invite'
visit project_project_members_path(project)
click_link 'Groups'
expect(members_table).to have_content(group_to_share_with.name)
end
end end
context 'when the group has "Share with group lock" enabled' do context 'when the group has "Share with group lock" enabled' do
...@@ -127,13 +172,14 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -127,13 +172,14 @@ RSpec.describe 'Project > Members > Invite group', :js do
visit project_project_members_path(project) visit project_project_members_path(project)
click_on 'invite-group-tab' click_on 'Invite a group'
click_on 'Select a group'
select2 group.id, from: '#link_group_id' wait_for_requests
click_button group.name
fill_in 'YYYY-MM-DD', with: 5.days.from_now.strftime('%Y-%m-%d')
click_button 'Invite'
fill_in 'expires_at_groups', with: 5.days.from_now.strftime('%Y-%m-%d') page.refresh
click_on 'invite-group-tab'
find('.btn-confirm').click
end end
it 'the group link shows the expiration time with a warning class' do it 'the group link shows the expiration time with a warning class' do
...@@ -149,29 +195,23 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -149,29 +195,23 @@ RSpec.describe 'Project > Members > Invite group', :js do
context 'with multiple groups to choose from' do context 'with multiple groups to choose from' do
let(:project) { create(:project) } let(:project) { create(:project) }
before do it 'includes multiple groups' do
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
sign_in(maintainer) sign_in(maintainer)
create(:group).add_owner(maintainer) group1 = create(:group)
create(:group).add_owner(maintainer) group1.add_owner(maintainer)
group2 = create(:group)
group2.add_owner(maintainer)
visit project_project_members_path(project) visit project_project_members_path(project)
click_link 'Invite group' click_on 'Invite a group'
click_on 'Select a group'
find('.ajax-groups-select.select2-container') wait_for_requests
execute_script 'GROUP_SELECT_PER_PAGE = 1;'
open_select2 '#link_group_id'
end
it 'infinitely scrolls' do
expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 1)
scroll_select2_to_bottom('.select2-drop .select2-results:visible')
expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 2) expect(page).to have_button(group1.name)
expect(page).to have_button(group2.name)
end end
end end
...@@ -188,16 +228,19 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -188,16 +228,19 @@ RSpec.describe 'Project > Members > Invite group', :js do
group_to_share_with.add_maintainer(maintainer) group_to_share_with.add_maintainer(maintainer)
end end
it 'the groups dropdown does not show ancestors' do # This behavior should be changed to exclude the ancestor and project
# group from the options once issue is fixed for the modal:
# https://gitlab.com/gitlab-org/gitlab/-/issues/329835
it 'the groups dropdown does show ancestors and the project group' do
visit project_project_members_path(project) visit project_project_members_path(project)
click_on 'invite-group-tab' click_on 'Invite a group'
click_link 'Search for a group' click_on 'Select a group'
wait_for_requests
page.within '.select2-drop' do expect(page).to have_button(group_to_share_with.name)
expect(page).to have_content(group_to_share_with.name) expect(page).to have_button(group.name)
expect(page).not_to have_content(group.name) expect(page).to have_button(nested_group.name)
end
end end
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