1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe 'Project > Members > Invite group', :js do
6  include Select2Helper
7  include ActionView::Helpers::DateHelper
8  include Spec::Support::Helpers::Features::MembersHelpers
9  include Spec::Support::Helpers::Features::InviteMembersModalHelper
10
11  let(:maintainer) { create(:user) }
12
13  using RSpec::Parameterized::TableSyntax
14
15  where(:invite_members_group_modal_enabled, :expected_invite_group_selector) do
16    true  | 'button[data-qa-selector="invite_a_group_button"]' # rubocop:disable QA/SelectorUsage
17    false | '#invite-group-tab'
18  end
19
20  with_them do
21    before do
22      stub_feature_flags(invite_members_group_modal: invite_members_group_modal_enabled)
23    end
24
25    it 'displays either the invite group button or the form with tabs based on the feature flag' do
26      project = create(:project, namespace: create(:group))
27
28      project.add_maintainer(maintainer)
29      sign_in(maintainer)
30
31      visit project_project_members_path(project)
32
33      expect(page).to have_selector(expected_invite_group_selector)
34    end
35
36    it 'does not display either the form or the button when visiting the page not signed in' do
37      project = create(:project, namespace: create(:group))
38
39      visit project_project_members_path(project)
40
41      expect(page).not_to have_selector(expected_invite_group_selector)
42    end
43  end
44
45  describe 'Share with group lock' do
46    let(:invite_group_selector) { 'button[data-qa-selector="invite_a_group_button"]' } # rubocop:disable QA/SelectorUsage
47
48    shared_examples 'the project can be shared with groups' do
49      it 'the "Invite a group" button exists' do
50        visit project_project_members_path(project)
51        expect(page).to have_selector(invite_group_selector)
52      end
53    end
54
55    shared_examples 'the project cannot be shared with groups' do
56      it 'the "Invite a group" button does not exist' do
57        visit project_project_members_path(project)
58        expect(page).not_to have_selector(invite_group_selector)
59      end
60    end
61
62    context 'for a project in a root group' do
63      let!(:group_to_share_with) { create(:group) }
64      let(:project) { create(:project, namespace: create(:group)) }
65
66      before do
67        project.add_maintainer(maintainer)
68        group_to_share_with.add_guest(maintainer)
69        sign_in(maintainer)
70      end
71
72      context 'when the group has "Share with group lock" disabled' do
73        it_behaves_like 'the project can be shared with groups'
74
75        it 'the project can be shared with another group when the feature flag invite_members_group_modal is disabled' do
76          stub_feature_flags(invite_members_group_modal: false)
77
78          visit project_project_members_path(project)
79
80          expect(page).not_to have_link 'Groups'
81
82          click_on 'invite-group-tab'
83
84          select2 group_to_share_with.id, from: '#link_group_id'
85          page.find('body').click
86          find('.btn-confirm').click
87
88          click_link 'Groups'
89
90          expect(members_table).to have_content(group_to_share_with.name)
91        end
92
93        it 'the project can be shared with another group when the feature flag invite_members_group_modal is enabled' do
94          stub_feature_flags(invite_members_group_modal: true)
95
96          visit project_project_members_path(project)
97
98          expect(page).not_to have_link 'Groups'
99
100          invite_group(group_to_share_with.name)
101
102          visit project_project_members_path(project)
103
104          click_link 'Groups'
105
106          expect(members_table).to have_content(group_to_share_with.name)
107        end
108      end
109
110      context 'when the group has "Share with group lock" enabled' do
111        before do
112          project.namespace.update_column(:share_with_group_lock, true)
113        end
114
115        it_behaves_like 'the project cannot be shared with groups'
116      end
117    end
118
119    context 'for a project in a subgroup' do
120      let!(:group_to_share_with) { create(:group) }
121      let(:root_group) { create(:group) }
122      let(:subgroup) { create(:group, parent: root_group) }
123      let(:project) { create(:project, namespace: subgroup) }
124
125      before do
126        project.add_maintainer(maintainer)
127        sign_in(maintainer)
128      end
129
130      context 'when the root_group has "Share with group lock" disabled' do
131        context 'when the subgroup has "Share with group lock" disabled' do
132          it_behaves_like 'the project can be shared with groups'
133        end
134
135        context 'when the subgroup has "Share with group lock" enabled' do
136          before do
137            subgroup.update_column(:share_with_group_lock, true)
138          end
139
140          it_behaves_like 'the project cannot be shared with groups'
141        end
142      end
143
144      context 'when the root_group has "Share with group lock" enabled' do
145        before do
146          root_group.update_column(:share_with_group_lock, true)
147        end
148
149        context 'when the subgroup has "Share with group lock" disabled (parent overridden)' do
150          it_behaves_like 'the project can be shared with groups'
151        end
152
153        context 'when the subgroup has "Share with group lock" enabled' do
154          before do
155            subgroup.update_column(:share_with_group_lock, true)
156          end
157
158          it_behaves_like 'the project cannot be shared with groups'
159        end
160      end
161    end
162  end
163
164  describe 'setting an expiration date for a group link' do
165    let(:project) { create(:project) }
166    let!(:group) { create(:group) }
167
168    let_it_be(:expiration_date) { 5.days.from_now.to_date }
169
170    around do |example|
171      freeze_time { example.run }
172    end
173
174    def setup
175      project.add_maintainer(maintainer)
176      group.add_guest(maintainer)
177      sign_in(maintainer)
178
179      visit project_project_members_path(project)
180
181      invite_group(group.name, role: 'Guest', expires_at: expiration_date)
182    end
183
184    it 'the group link shows the expiration time with a warning class' do
185      setup
186      click_link 'Groups'
187
188      expect(page).to have_field('Expiration date', with: expiration_date)
189    end
190  end
191
192  describe 'the groups dropdown' do
193    context 'with multiple groups to choose from' do
194      let(:project) { create(:project) }
195
196      it 'includes multiple groups' do
197        project.add_maintainer(maintainer)
198        sign_in(maintainer)
199
200        group1 = create(:group)
201        group1.add_owner(maintainer)
202        group2 = create(:group)
203        group2.add_owner(maintainer)
204
205        visit project_project_members_path(project)
206
207        click_on 'Invite a group'
208        click_on 'Select a group'
209        wait_for_requests
210
211        expect(page).to have_button(group1.name)
212        expect(page).to have_button(group2.name)
213      end
214    end
215
216    context 'for a project in a nested group' do
217      let(:group) { create(:group) }
218      let!(:nested_group) { create(:group, parent: group) }
219      let!(:group_to_share_with) { create(:group) }
220      let!(:project) { create(:project, namespace: nested_group) }
221
222      before do
223        project.add_maintainer(maintainer)
224        sign_in(maintainer)
225        group.add_maintainer(maintainer)
226        group_to_share_with.add_maintainer(maintainer)
227      end
228
229      # This behavior should be changed to exclude the ancestor and project
230      # group from the options once issue is fixed for the modal:
231      # https://gitlab.com/gitlab-org/gitlab/-/issues/329835
232      it 'the groups dropdown does show ancestors and the project group' do
233        visit project_project_members_path(project)
234
235        click_on 'Invite a group'
236        click_on 'Select a group'
237        wait_for_requests
238
239        expect(page).to have_button(group_to_share_with.name)
240        expect(page).to have_button(group.name)
241        expect(page).to have_button(nested_group.name)
242      end
243    end
244  end
245end
246