diff options
Diffstat (limited to 'spec/features/projects')
29 files changed, 862 insertions, 1226 deletions
diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb index 8001ce0f454..86fe59f003f 100644 --- a/spec/features/projects/active_tabs_spec.rb +++ b/spec/features/projects/active_tabs_spec.rb @@ -132,13 +132,13 @@ RSpec.describe 'Project active tab' do it_behaves_like 'page has active sub tab', _('Value Stream') end - context 'on project Analytics/"CI / CD"' do + context 'on project Analytics/"CI/CD"' do before do - click_tab(_('CI / CD')) + click_tab(_('CI/CD')) end it_behaves_like 'page has active tab', _('Analytics') - it_behaves_like 'page has active sub tab', _('CI / CD') + it_behaves_like 'page has active sub tab', _('CI/CD') end end end diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb index ccffe25f45e..353c8558185 100644 --- a/spec/features/projects/ci/lint_spec.rb +++ b/spec/features/projects/ci/lint_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'CI Lint', :js do +RSpec.describe 'CI Lint', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297782' do include Spec::Support::Helpers::Features::EditorLiteSpecHelpers let(:project) { create(:project, :repository) } diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb index d0ad6668c07..40d0260eafd 100644 --- a/spec/features/projects/container_registry_spec.rb +++ b/spec/features/projects/container_registry_spec.rb @@ -82,7 +82,13 @@ RSpec.describe 'Container Registry', :js do end it 'shows the image title' do - expect(page).to have_content 'my/image tags' + expect(page).to have_content 'my/image' + end + + it 'shows the image tags' do + expect(page).to have_content 'Image tags' + first_tag = first('[data-testid="name"]') + expect(first_tag).to have_content '1' end it 'user removes a specific tag from container repository' do diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 27167f95104..de7ff1c473d 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -429,37 +429,67 @@ RSpec.describe 'Environments page', :js do end describe 'environments folders' do - before do - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-1', - state: :available) - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-2', - state: :available) - end + describe 'available environments' do + before do + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-1', + state: :available) + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-2', + state: :available) + end - it 'users unfurls an environment folder' do - visit_environments(project) + it 'users unfurls an environment folder' do + visit_environments(project) - expect(page).not_to have_content 'review-1' - expect(page).not_to have_content 'review-2' - expect(page).to have_content 'staging 2' + expect(page).not_to have_content 'review-1' + expect(page).not_to have_content 'review-2' + expect(page).to have_content 'staging 2' - within('.folder-row') do - find('.folder-name', text: 'staging').click - end + within('.folder-row') do + find('.folder-name', text: 'staging').click + end - expect(page).to have_content 'review-1' - expect(page).to have_content 'review-2' - within('.ci-table') do - within('[data-qa-selector="environment_item"]', text: 'review-1') do - expect(find('.js-auto-stop').text).not_to be_empty + expect(page).to have_content 'review-1' + expect(page).to have_content 'review-2' + within('.ci-table') do + within('[data-qa-selector="environment_item"]', text: 'review-1') do + expect(find('.js-auto-stop').text).not_to be_empty + end + within('[data-qa-selector="environment_item"]', text: 'review-2') do + expect(find('.js-auto-stop').text).not_to be_empty + end end - within('[data-qa-selector="environment_item"]', text: 'review-2') do - expect(find('.js-auto-stop').text).not_to be_empty + end + end + + describe 'stopped environments' do + before do + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-1', + state: :stopped) + create(:environment, :will_auto_stop, + project: project, + name: 'staging/review-2', + state: :stopped) + end + + it 'users unfurls an environment folder' do + visit_environments(project, scope: 'stopped') + + expect(page).not_to have_content 'review-1' + expect(page).not_to have_content 'review-2' + expect(page).to have_content 'staging 2' + + within('.folder-row') do + find('.folder-name', text: 'staging').click end + + expect(page).to have_content 'review-1' + expect(page).to have_content 'review-2' end end end diff --git a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb index f5941d0ff15..50fc7bb0753 100644 --- a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb +++ b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb @@ -104,7 +104,7 @@ RSpec.describe 'User sees feature flag list', :js do it 'shows empty page' do expect(page).to have_text 'Get started with feature flags' - expect(page).to have_selector('.btn-success', text: 'New feature flag') + expect(page).to have_selector('.btn-confirm', text: 'New feature flag') expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure') end end diff --git a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb index ca6f03472dd..cd796d45aba 100644 --- a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb @@ -5,11 +5,13 @@ require 'spec_helper' RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do include Spec::Support::Helpers::Features::EditorLiteSpecHelpers + let_it_be(:namespace) { create(:namespace) } + let(:project) { create(:project, :repository, namespace: namespace) } + before do - project = create(:project, :repository) sign_in project.owner - stub_experiment(ci_syntax_templates: experiment_active) - stub_experiment_for_subject(ci_syntax_templates: in_experiment_group) + stub_experiment(ci_syntax_templates_b: experiment_active) + stub_experiment_for_subject(ci_syntax_templates_b: in_experiment_group) visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml') end @@ -23,35 +25,45 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do end end - context 'when experiment is active and the user is in the control group' do + context 'when experiment is active' do let(:experiment_active) { true } - let(:in_experiment_group) { false } - it 'does not show the "Learn CI/CD syntax" template dropdown' do - expect(page).not_to have_css('.gitlab-ci-syntax-yml-selector') + context 'when the user is in the control group' do + let(:in_experiment_group) { false } + + it 'does not show the "Learn CI/CD syntax" template dropdown' do + expect(page).not_to have_css('.gitlab-ci-syntax-yml-selector') + end end - end - context 'when experiment is active and the user is in the experimental group' do - let(:experiment_active) { true } - let(:in_experiment_group) { true } + context 'when the user is in the experimental group' do + let(:in_experiment_group) { true } + + it 'allows the user to pick a "Learn CI/CD syntax" template from the dropdown', :js do + expect(page).to have_css('.gitlab-ci-syntax-yml-selector') - it 'allows the user to pick a "Learn CI/CD syntax" template from the dropdown', :js do - expect(page).to have_css('.gitlab-ci-syntax-yml-selector') + find('.js-gitlab-ci-syntax-yml-selector').click - find('.js-gitlab-ci-syntax-yml-selector').click + wait_for_requests - wait_for_requests + within '.gitlab-ci-syntax-yml-selector' do + find('.dropdown-input-field').set('Artifacts example') + find('.dropdown-content .is-focused', text: 'Artifacts example').click + end - within '.gitlab-ci-syntax-yml-selector' do - find('.dropdown-input-field').set('Artifacts example') - find('.dropdown-content .is-focused', text: 'Artifacts example').click + wait_for_requests + + expect(page).to have_css('.gitlab-ci-syntax-yml-selector .dropdown-toggle-text', text: 'Learn CI/CD syntax') + expect(editor_get_value).to have_content('You can use artifacts to pass data to jobs in later stages.') end - wait_for_requests + context 'when the group is created longer than 90 days ago' do + let(:namespace) { create(:namespace, created_at: 91.days.ago) } - expect(page).to have_css('.gitlab-ci-syntax-yml-selector .dropdown-toggle-text', text: 'Learn CI/CD syntax') - expect(editor_get_value).to have_content('You can use artifacts to pass data to jobs in later stages.') + it 'does not show the "Learn CI/CD syntax" template dropdown' do + expect(page).not_to have_css('.gitlab-ci-syntax-yml-selector') + end + end end end end diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb index 8d0500f5e13..7abbd207b24 100644 --- a/spec/features/projects/fork_spec.rb +++ b/spec/features/projects/fork_spec.rb @@ -12,45 +12,27 @@ RSpec.describe 'Project fork' do sign_in(user) end - it 'allows user to fork project from the project page' do - visit project_path(project) - - expect(page).not_to have_css('a.disabled', text: 'Fork') - end - - context 'user has exceeded personal project limit' do - before do - user.update!(projects_limit: 0) - end - - it 'disables fork button on project page' do + shared_examples 'fork button on project page' do + it 'allows user to fork project from the project page' do visit project_path(project) - expect(page).to have_css('a.disabled', text: 'Fork') + expect(page).not_to have_css('a.disabled', text: 'Fork') end - context 'with a group to fork to' do - let!(:group) { create(:group).tap { |group| group.add_owner(user) } } - - it 'enables fork button on project page' do - visit project_path(project) - - expect(page).not_to have_css('a.disabled', text: 'Fork') + context 'user has exceeded personal project limit' do + before do + user.update!(projects_limit: 0) end - it 'allows user to fork only to the group on fork page', :js do - visit new_project_fork_path(project) - - to_personal_namespace = find('[data-qa-selector=fork_namespace_button].disabled') - to_group = find(".fork-groups button[data-qa-name=#{group.name}]") + it 'disables fork button on project page' do + visit project_path(project) - expect(to_personal_namespace).not_to be_nil - expect(to_group).not_to be_disabled + expect(page).to have_css('a.disabled', text: 'Fork') end end end - context 'forking enabled / disabled in project settings' do + shared_examples 'create fork page' do |fork_page_text| before do project.project_feature.update_attribute( :forking_access_level, forking_access_level) @@ -70,7 +52,7 @@ RSpec.describe 'Project fork' do visit new_project_fork_path(project) expect(page.status_code).to eq(200) - expect(page).to have_text(' Select a namespace to fork the project ') + expect(page).to have_text(fork_page_text) end end @@ -127,92 +109,88 @@ RSpec.describe 'Project fork' do visit new_project_fork_path(project) expect(page.status_code).to eq(200) - expect(page).to have_text(' Select a namespace to fork the project ') + expect(page).to have_text(fork_page_text) end end end end - it 'forks the project', :sidekiq_might_not_need_inline do - visit project_path(project) - - click_link 'Fork' + it_behaves_like 'fork button on project page' + it_behaves_like 'create fork page', 'Fork project' - page.within '.fork-thumbnail-container' do - click_link 'Select' + context 'with fork_project_form feature flag disabled' do + before do + stub_feature_flags(fork_project_form: false) + sign_in(user) end - expect(page).to have_content 'Forked from' + it_behaves_like 'fork button on project page' - visit project_path(project) + context 'user has exceeded personal project limit' do + before do + user.update!(projects_limit: 0) + end - expect(page).to have_content(/new merge request/i) + context 'with a group to fork to' do + let!(:group) { create(:group).tap { |group| group.add_owner(user) } } - page.within '.nav-sidebar' do - first(:link, 'Merge Requests').click - end + it 'allows user to fork only to the group on fork page', :js do + visit new_project_fork_path(project) - expect(page).to have_content(/new merge request/i) + to_personal_namespace = find('[data-qa-selector=fork_namespace_button].disabled') + to_group = find(".fork-groups button[data-qa-name=#{group.name}]") - page.within '#content-body' do - click_link('New merge request') + expect(to_personal_namespace).not_to be_nil + expect(to_group).not_to be_disabled + end + end end - expect(current_path).to have_content(/#{user.namespace.path}/i) - end + it_behaves_like 'create fork page', ' Select a namespace to fork the project ' - it 'shows avatars when Gravatar is disabled' do - stub_application_setting(gravatar_enabled: false) + it 'forks the project', :sidekiq_might_not_need_inline do + visit project_path(project) - visit project_path(project) + click_link 'Fork' - click_link 'Fork' + page.within '.fork-thumbnail-container' do + click_link 'Select' + end - page.within('.fork-thumbnail-container') do - expect(page).to have_css('div.identicon') - end - end + expect(page).to have_content 'Forked from' - it 'shows the forked project on the list' do - visit project_path(project) + visit project_path(project) - click_link 'Fork' + expect(page).to have_content(/new merge request/i) - page.within '.fork-thumbnail-container' do - click_link 'Select' - end + page.within '.nav-sidebar' do + first(:link, 'Merge Requests').click + end - visit project_forks_path(project) + expect(page).to have_content(/new merge request/i) - forked_project = user.fork_of(project.reload) + page.within '#content-body' do + click_link('New merge request') + end - page.within('.js-projects-list-holder') do - expect(page).to have_content("#{forked_project.namespace.human_name} / #{forked_project.name}") + expect(current_path).to have_content(/#{user.namespace.path}/i) end - forked_project.update!(path: 'test-crappy-path') - - visit project_forks_path(project) + it 'shows avatars when Gravatar is disabled' do + stub_application_setting(gravatar_enabled: false) - page.within('.js-projects-list-holder') do - expect(page).to have_content("#{forked_project.namespace.human_name} / #{forked_project.name}") - end - end + visit project_path(project) - context 'when the project is private' do - let(:project) { create(:project, :repository) } - let(:another_user) { create(:user, name: 'Mike') } + click_link 'Fork' - before do - project.add_reporter(user) - project.add_reporter(another_user) + page.within('.fork-thumbnail-container') do + expect(page).to have_css('div.identicon') + end end - it 'renders private forks of the project' do + it 'shows the forked project on the list' do visit project_path(project) - another_project_fork = Projects::ForkService.new(project, another_user).execute - click_link 'Fork' page.within '.fork-thumbnail-container' do @@ -221,79 +199,117 @@ RSpec.describe 'Project fork' do visit project_forks_path(project) + forked_project = user.fork_of(project.reload) + page.within('.js-projects-list-holder') do - user_project_fork = user.fork_of(project.reload) - expect(page).to have_content("#{user_project_fork.namespace.human_name} / #{user_project_fork.name}") + expect(page).to have_content("#{forked_project.namespace.human_name} / #{forked_project.name}") end - expect(page).not_to have_content("#{another_project_fork.namespace.human_name} / #{another_project_fork.name}") - end - end + forked_project.update!(path: 'test-crappy-path') - context 'when the user already forked the project' do - before do - create(:project, :repository, name: project.name, namespace: user.namespace) - end + visit project_forks_path(project) - it 'renders error' do - visit project_path(project) + page.within('.js-projects-list-holder') do + expect(page).to have_content("#{forked_project.namespace.human_name} / #{forked_project.name}") + end + end - click_link 'Fork' + context 'when the project is private' do + let(:project) { create(:project, :repository) } + let(:another_user) { create(:user, name: 'Mike') } - page.within '.fork-thumbnail-container' do - click_link 'Select' + before do + project.add_reporter(user) + project.add_reporter(another_user) end - expect(page).to have_content "Name has already been taken" - end - end + it 'renders private forks of the project' do + visit project_path(project) - context 'maintainer in group' do - let(:group) { create(:group) } + another_project_fork = Projects::ForkService.new(project, another_user).execute - before do - group.add_maintainer(user) - end + click_link 'Fork' - it 'allows user to fork project to group or to user namespace', :js do - visit project_path(project) - wait_for_requests + page.within '.fork-thumbnail-container' do + click_link 'Select' + end - expect(page).not_to have_css('a.disabled', text: 'Fork') + visit project_forks_path(project) - click_link 'Fork' + page.within('.js-projects-list-holder') do + user_project_fork = user.fork_of(project.reload) + expect(page).to have_content("#{user_project_fork.namespace.human_name} / #{user_project_fork.name}") + end - expect(page).to have_css('.fork-thumbnail') - expect(page).to have_css('.group-row') - expect(page).not_to have_css('.fork-thumbnail.disabled') + expect(page).not_to have_content("#{another_project_fork.namespace.human_name} / #{another_project_fork.name}") + end end - it 'allows user to fork project to group and not user when exceeded project limit', :js do - user.projects_limit = 0 - user.save! + context 'when the user already forked the project' do + before do + create(:project, :repository, name: project.name, namespace: user.namespace) + end - visit project_path(project) - wait_for_requests + it 'renders error' do + visit project_path(project) - expect(page).not_to have_css('a.disabled', text: 'Fork') + click_link 'Fork' - click_link 'Fork' + page.within '.fork-thumbnail-container' do + click_link 'Select' + end - expect(page).to have_css('.fork-thumbnail.disabled') - expect(page).to have_css('.group-row') + expect(page).to have_content "Name has already been taken" + end end - it 'links to the fork if the project was already forked within that namespace', :sidekiq_might_not_need_inline, :js do - forked_project = fork_project(project, user, namespace: group, repository: true) + context 'maintainer in group' do + let(:group) { create(:group) } + + before do + group.add_maintainer(user) + end + + it 'allows user to fork project to group or to user namespace', :js do + visit project_path(project) + wait_for_requests + + expect(page).not_to have_css('a.disabled', text: 'Fork') + + click_link 'Fork' + + expect(page).to have_css('.fork-thumbnail') + expect(page).to have_css('.group-row') + expect(page).not_to have_css('.fork-thumbnail.disabled') + end + + it 'allows user to fork project to group and not user when exceeded project limit', :js do + user.projects_limit = 0 + user.save! + + visit project_path(project) + wait_for_requests + + expect(page).not_to have_css('a.disabled', text: 'Fork') - visit new_project_fork_path(project) - wait_for_requests + click_link 'Fork' - expect(page).to have_css('.group-row a.btn', text: 'Go to fork') + expect(page).to have_css('.fork-thumbnail.disabled') + expect(page).to have_css('.group-row') + end + + it 'links to the fork if the project was already forked within that namespace', :sidekiq_might_not_need_inline, :js do + forked_project = fork_project(project, user, namespace: group, repository: true) + + visit new_project_fork_path(project) + wait_for_requests + + expect(page).to have_css('.group-row a.btn', text: 'Go to fork') - click_link 'Go to fork' + click_link 'Go to fork' - expect(current_path).to eq(project_path(forked_project)) + expect(current_path).to eq(project_path(forked_project)) + end end end end diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb index d710ecf6c88..6b92581d704 100644 --- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb +++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb @@ -14,25 +14,9 @@ RSpec.describe 'Projects > Members > Anonymous user sees members' do create(:project_group_link, project: project, group: group) end - context 'when `vue_project_members_list` feature flag is enabled', :js do - it "anonymous user visits the project's members page and sees the list of members" do - visit project_project_members_path(project) + it "anonymous user visits the project's members page and sees the list of members", :js do + visit project_project_members_path(project) - expect(find_member_row(user)).to have_content(user.name) - end - end - - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - end - - it "anonymous user visits the project's members page and sees the list of members" do - visit project_project_members_path(project) - - expect(current_path).to eq( - project_project_members_path(project)) - expect(page).to have_content(user.name) - end + expect(find_member_row(user)).to have_content(user.name) end end diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb index 1abd00421ec..94ce18fef93 100644 --- a/spec/features/projects/members/group_members_spec.rb +++ b/spec/features/projects/members/group_members_spec.rb @@ -20,218 +20,96 @@ RSpec.describe 'Projects members', :js do sign_in(user) end - context 'when `vue_project_members_list` feature flag is enabled' do - context 'with a group invitee' do - before do - group_invitee - visit project_project_members_path(project) - end - - it 'does not appear in the project members page' do - expect(members_table).not_to have_content('test2@abc.com') - end + context 'with a group invitee' do + before do + group_invitee + visit project_project_members_path(project) end - context 'with a group' do - it 'shows group and project members by default' do - visit project_project_members_path(project) - - expect(members_table).to have_content(developer.name) - expect(members_table).to have_content(user.name) - expect(members_table).to have_content(group.name) - end - - it 'shows project members only if requested' do - visit project_project_members_path(project, with_inherited_permissions: 'exclude') - - expect(members_table).to have_content(developer.name) - expect(members_table).not_to have_content(user.name) - expect(members_table).not_to have_content(group.name) - end + it 'does not appear in the project members page' do + expect(members_table).not_to have_content('test2@abc.com') + end + end - it 'shows group members only if requested' do - visit project_project_members_path(project, with_inherited_permissions: 'only') + context 'with a group' do + it 'shows group and project members by default' do + visit project_project_members_path(project) - expect(members_table).not_to have_content(developer.name) - expect(members_table).to have_content(user.name) - expect(members_table).to have_content(group.name) - end + expect(members_table).to have_content(developer.name) + expect(members_table).to have_content(user.name) + expect(members_table).to have_content(group.name) end - context 'with a group, a project invitee, and a project requester' do - before do - group.request_access(group_requester) - project.request_access(project_requester) - group_invitee - project_invitee - visit project_project_members_path(project) - end - - it 'shows the group owner' do - expect(members_table).to have_content(user.name) - expect(members_table).to have_content(group.name) - end - - it 'shows the project developer' do - expect(members_table).to have_content(developer.name) - end - - it 'shows the project invitee' do - click_link 'Invited' - - expect(members_table).to have_content('test1@abc.com') - expect(members_table).not_to have_content('test2@abc.com') - end - - it 'shows the project requester' do - click_link 'Access requests' - - expect(members_table).to have_content(project_requester.name) - expect(members_table).not_to have_content(group_requester.name) - end - end + it 'shows project members only if requested' do + visit project_project_members_path(project, with_inherited_permissions: 'exclude') - context 'with a group requester' do - before do - stub_feature_flags(invite_members_group_modal: false) - group.request_access(group_requester) - visit project_project_members_path(project) - end - - it 'does not appear in the project members page' do - expect(page).not_to have_link('Access requests') - expect(members_table).not_to have_content(group_requester.name) - end + expect(members_table).to have_content(developer.name) + expect(members_table).not_to have_content(user.name) + expect(members_table).not_to have_content(group.name) end - context 'showing status of members' do - it 'shows the status' do - create(:user_status, user: user, emoji: 'smirk', message: 'Authoring this object') + it 'shows group members only if requested' do + visit project_project_members_path(project, with_inherited_permissions: 'only') - visit project_project_members_path(project) - - expect(first_row).to have_selector('gl-emoji[data-name="smirk"]') - end + expect(members_table).not_to have_content(developer.name) + expect(members_table).to have_content(user.name) + expect(members_table).to have_content(group.name) end end - context 'when `vue_project_members_list` feature flag is disabled' do + context 'with a group, a project invitee, and a project requester' do before do - stub_feature_flags(vue_project_members_list: false) + group.request_access(group_requester) + project.request_access(project_requester) + group_invitee + project_invitee + visit project_project_members_path(project) end - context 'with a group invitee' do - before do - group_invitee - visit project_project_members_path(project) - end - - it 'does not appear in the project members page' do - page.within first('.content-list') do - expect(page).not_to have_content('test2@abc.com') - end - end + it 'shows the group owner' do + expect(members_table).to have_content(user.name) + expect(members_table).to have_content(group.name) end - context 'with a group' do - it 'shows group and project members by default' do - visit project_project_members_path(project) - - page.within first('.content-list') do - expect(page).to have_content(developer.name) - - expect(page).to have_content(user.name) - expect(page).to have_content(group.name) - end - end - - it 'shows project members only if requested' do - visit project_project_members_path(project, with_inherited_permissions: 'exclude') - - page.within first('.content-list') do - expect(page).to have_content(developer.name) + it 'shows the project developer' do + expect(members_table).to have_content(developer.name) + end - expect(page).not_to have_content(user.name) - expect(page).not_to have_content(group.name) - end - end + it 'shows the project invitee' do + click_link 'Invited' - it 'shows group members only if requested' do - visit project_project_members_path(project, with_inherited_permissions: 'only') + expect(members_table).to have_content('test1@abc.com') + expect(members_table).not_to have_content('test2@abc.com') + end - page.within first('.content-list') do - expect(page).not_to have_content(developer.name) + it 'shows the project requester' do + click_link 'Access requests' - expect(page).to have_content(user.name) - expect(page).to have_content(group.name) - end - end + expect(members_table).to have_content(project_requester.name) + expect(members_table).not_to have_content(group_requester.name) end + end - context 'with a group, a project invitee, and a project requester' do - before do - group.request_access(group_requester) - project.request_access(project_requester) - group_invitee - project_invitee - visit project_project_members_path(project) - end - - it 'shows the group owner' do - page.within first('.content-list') do - # Group owner - expect(page).to have_content(user.name) - expect(page).to have_content(group.name) - end - end - - it 'shows the project developer' do - page.within first('.content-list') do - # Project developer - expect(page).to have_content(developer.name) - end - end - - it 'shows the project invitee' do - click_link 'Invited' - - page.within first('.content-list') do - expect(page).to have_content('test1@abc.com') - expect(page).not_to have_content('test2@abc.com') - end - end - - it 'shows the project requester' do - click_link 'Access requests' - - page.within first('.content-list') do - expect(page).to have_content(project_requester.name) - expect(page).not_to have_content(group_requester.name) - end - end + context 'with a group requester' do + before do + stub_feature_flags(invite_members_group_modal: false) + group.request_access(group_requester) + visit project_project_members_path(project) end - context 'with a group requester' do - before do - stub_feature_flags(invite_members_group_modal: false) - group.request_access(group_requester) - visit project_project_members_path(project) - end - - it 'does not appear in the project members page' do - expect(page).not_to have_link('Access requests') - page.within first('.content-list') do - expect(page).not_to have_content(group_requester.name) - end - end + it 'does not appear in the project members page' do + expect(page).not_to have_link('Access requests') + expect(members_table).not_to have_content(group_requester.name) end + end + + context 'showing status of members' do + it 'shows the status' do + create(:user_status, user: user, emoji: 'smirk', message: 'Authoring this object') - context 'showing status of members' do - it_behaves_like 'showing user status' do - let(:user_with_status) { developer } + visit project_project_members_path(project) - subject { visit project_project_members_path(project) } - end + expect(first_row).to have_selector('gl-emoji[data-name="smirk"]') end end end diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb index 9d087dfd5f6..6a1d26983b5 100644 --- a/spec/features/projects/members/groups_with_access_list_spec.rb +++ b/spec/features/projects/members/groups_with_access_list_spec.rb @@ -17,172 +17,80 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do project.add_maintainer(user) sign_in(user) - end - - context 'when `vue_project_members_list` feature flag is enabled' do - before do - visit project_project_members_path(project) - click_groups_tab - end - - it 'updates group access level' do - click_button group_link.human_access - click_button 'Guest' - - wait_for_requests - - visit project_project_members_path(project) - click_groups_tab - - expect(find_group_row(group)).to have_content('Guest') - end + visit project_project_members_path(project) + click_groups_tab + end - it 'updates expiry date' do - page.within find_group_row(group) do - fill_in 'Expiration date', with: 5.days.from_now.to_date - find_field('Expiration date').native.send_keys :enter + it 'updates group access level' do + click_button group_link.human_access + click_button 'Guest' - wait_for_requests + wait_for_requests - expect(page).to have_content(/in \d days/) - end - end + visit project_project_members_path(project) - context 'when link has expiry date set' do - let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } } + click_groups_tab - it 'clears expiry date' do - page.within find_group_row(group) do - expect(page).to have_content(/in \d days/) + expect(find_group_row(group)).to have_content('Guest') + end - find('[data-testid="clear-button"]').click + it 'updates expiry date' do + page.within find_group_row(group) do + fill_in 'Expiration date', with: 5.days.from_now.to_date + find_field('Expiration date').native.send_keys :enter - wait_for_requests + wait_for_requests - expect(page).to have_content('No expiration set') - end - end + expect(page).to have_content(/in \d days/) end + end - it 'deletes group link' do - expect(page).to have_content(group.full_name) + context 'when link has expiry date set' do + let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } } + it 'clears expiry date' do page.within find_group_row(group) do - click_button 'Remove group' - end - - page.within('[role="dialog"]') do - click_button('Remove group') - end - - expect(page).not_to have_content(group.full_name) - end - - context 'search in existing members' do - it 'finds no results' do - fill_in_filtered_search 'Search groups', with: 'testing 123' - - click_groups_tab - - expect(page).not_to have_content(group.full_name) - end + expect(page).to have_content(/in \d days/) - it 'finds results' do - fill_in_filtered_search 'Search groups', with: group.full_name + find('[data-testid="clear-button"]').click - click_groups_tab + wait_for_requests - expect(members_table).to have_content(group.full_name) + expect(page).to have_content('No expiration set') end end end - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - - visit project_project_members_path(project) - click_groups_tab - end - - it 'updates group access level' do - click_button group_link.human_access - - page.within '.dropdown-menu' do - click_link 'Guest' - end - - wait_for_requests - - visit project_project_members_path(project) - - click_groups_tab + it 'deletes group link' do + expect(page).to have_content(group.full_name) - expect(first('.group_member')).to have_content('Guest') + page.within find_group_row(group) do + click_button 'Remove group' end - it 'updates expiry date' do - expires_at_field = "member_expires_at_#{group.id}" - fill_in expires_at_field, with: 3.days.from_now.to_date - - find_field(expires_at_field).native.send_keys :enter - wait_for_requests - - page.within(find('li.group_member')) do - expect(page).to have_content('Expires in 3 days') - end + page.within('[role="dialog"]') do + click_button('Remove group') end - context 'when link has expiry date set' do - let(:additional_link_attrs) { { expires_at: 3.days.from_now.to_date } } - - it 'clears expiry date' do - page.within(find('li.group_member')) do - expect(page).to have_content('Expires in 3 days') - - page.within(find('.js-edit-member-form')) do - find('.js-clear-input').click - end - - wait_for_requests + expect(page).not_to have_content(group.full_name) + end - expect(page).not_to have_content('Expires in') - end - end - end + context 'search in existing members' do + it 'finds no results' do + fill_in_filtered_search 'Search groups', with: 'testing 123' - it 'deletes group link' do - page.within(first('.group_member')) do - accept_confirm { find('.btn-danger').click } - end - wait_for_requests + click_groups_tab - expect(page).not_to have_selector('.group_member') + expect(page).not_to have_content(group.full_name) end - context 'search in existing members' do - it 'finds no results' do - page.within '.user-search-form' do - fill_in 'search_groups', with: 'testing 123' - find('.user-search-btn').click - end - - click_groups_tab - - expect(page).not_to have_selector('.group_member') - end - - it 'finds results' do - page.within '.user-search-form' do - fill_in 'search_groups', with: group.name - find('.user-search-btn').click - end + it 'finds results' do + fill_in_filtered_search 'Search groups', with: group.full_name - click_groups_tab + click_groups_tab - expect(page).to have_selector('.group_member', count: 1) - end + expect(members_table).to have_content(group.full_name) end end diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index f0d115fef1d..83ba2533a73 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -41,46 +41,20 @@ RSpec.describe 'Project > Members > Invite group', :js do context 'when the group has "Share with group lock" disabled' do it_behaves_like 'the project can be shared with groups' - context 'when `vue_project_members_list` feature flag is enabled' do - it 'the project can be shared with another group' do - visit project_project_members_path(project) + it 'the project can be shared with another group' do + visit project_project_members_path(project) - expect(page).not_to have_link 'Groups' + expect(page).not_to have_link 'Groups' - click_on 'invite-group-tab' + click_on 'invite-group-tab' - select2 group_to_share_with.id, from: '#link_group_id' - page.find('body').click - find('.btn-success').click + select2 group_to_share_with.id, from: '#link_group_id' + page.find('body').click + find('.btn-confirm').click - click_link 'Groups' + click_link 'Groups' - expect(members_table).to have_content(group_to_share_with.name) - end - end - - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - end - - it 'the project can be shared with another group' do - visit project_project_members_path(project) - - expect(page).not_to have_link 'Groups' - - click_on 'invite-group-tab' - - select2 group_to_share_with.id, from: '#link_group_id' - page.find('body').click - find('.btn-success').click - - click_link 'Groups' - - page.within('[data-testid="project-member-groups"]') do - expect(page).to have_content(group_to_share_with.name) - end - end + expect(members_table).to have_content(group_to_share_with.name) end end @@ -159,36 +133,15 @@ RSpec.describe 'Project > Members > Invite group', :js do fill_in 'expires_at_groups', with: 5.days.from_now.strftime('%Y-%m-%d') click_on 'invite-group-tab' - find('.btn-success').click - end - - context 'when `vue_project_members_list` feature flag is enabled' do - it 'the group link shows the expiration time with a warning class' do - setup - click_link 'Groups' - - expect(find_group_row(group)).to have_content(/in \d days/) - expect(find_group_row(group)).to have_selector('.gl-text-orange-500') - end + find('.btn-confirm').click end - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - end - - it 'the group link shows the expiration time with a warning class' do - setup - click_link 'Groups' + it 'the group link shows the expiration time with a warning class' do + setup + click_link 'Groups' - page.within('[data-testid="project-member-groups"]') do - # Using distance_of_time_in_words_to_now because it is not the same as - # subtraction, and this way avoids time zone issues as well - expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at) - expect(page).to have_content(expires_in_text) - expect(page).to have_selector('.text-warning') - end - end + expect(find_group_row(group)).to have_content(/in \d days/) + expect(find_group_row(group)).to have_selector('.gl-text-orange-500') end end diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb index b0fe5b9c48a..0830585da9b 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/list_spec.rb @@ -2,232 +2,192 @@ require 'spec_helper' -RSpec.describe 'Project members list' do +RSpec.describe 'Project members list', :js do include Select2Helper + include Spec::Support::Helpers::Features::MembersHelpers let(:user1) { create(:user, name: 'John Doe') } let(:user2) { create(:user, name: 'Mary Jane') } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:project, :internal, namespace: group) } before do - stub_feature_flags(invite_members_group_modal: false) + stub_feature_flags(invite_members_group_modal: true) sign_in(user1) group.add_owner(user1) end - context 'when `vue_project_members_list` feature flag is enabled', :js do - include Spec::Support::Helpers::Features::MembersHelpers + it 'show members from project and group' do + project.add_developer(user2) - it 'pushes `vue_project_members_list` feature flag to the frontend' do - visit_members_page - - expect(page).to have_pushed_frontend_feature_flags(vueProjectMembersList: true) - end + visit_members_page - it 'show members from project and group' do - project.add_developer(user2) - - visit_members_page - - expect(first_row).to have_content(user1.name) - expect(second_row).to have_content(user2.name) - end + expect(first_row).to have_content(user1.name) + expect(second_row).to have_content(user2.name) + end - it 'show user once if member of both group and project' do - project.add_developer(user1) + it 'show user once if member of both group and project' do + project.add_developer(user1) - visit_members_page + visit_members_page - expect(first_row).to have_content(user1.name) - expect(second_row).to be_blank - end + expect(first_row).to have_content(user1.name) + expect(second_row).to be_blank + end - it 'update user access level', :js do - project.add_developer(user2) + it 'update user access level' do + project.add_developer(user2) - visit_members_page + visit_members_page - page.within find_member_row(user2) do - click_button('Developer') - click_button('Reporter') + page.within find_member_row(user2) do + click_button('Developer') + click_button('Reporter') - expect(page).to have_button('Reporter') - end + expect(page).to have_button('Reporter') end + end - it 'add user to project', :js do - visit_members_page + it 'add user to project' do + visit_members_page - add_user(user2.id, 'Reporter') + add_user(user2.name, 'Reporter') - page.within find_member_row(user2) do - expect(page).to have_button('Reporter') - end + page.within find_member_row(user2) do + expect(page).to have_button('Reporter') end + end - it 'remove user from project', :js do - other_user = create(:user) - project.add_developer(other_user) - - visit_members_page - - # Open modal - page.within find_member_row(other_user) do - click_button 'Remove member' - end + it 'uses ProjectMember access_level_roles for the invite members modal access option' do + visit_members_page - page.within('[role="dialog"]') do - expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' - click_button('Remove member') - end + click_on 'Invite members' - wait_for_requests + click_on 'Guest' + wait_for_requests - expect(members_table).not_to have_content(other_user.name) + page.within '.dropdown-menu' do + expect(page).to have_button('Guest') + expect(page).to have_button('Reporter') + expect(page).to have_button('Developer') + expect(page).to have_button('Maintainer') + expect(page).not_to have_button('Owner') end + end - it 'invite user to project', :js do - visit_members_page + it 'remove user from project' do + other_user = create(:user) + project.add_developer(other_user) - add_user('test@example.com', 'Reporter') + visit_members_page - click_link 'Invited' + # Open modal + page.within find_member_row(other_user) do + click_button 'Remove member' + end - page.within find_invited_member_row('test@example.com') do - expect(page).to have_button('Reporter') - end + page.within('[role="dialog"]') do + expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' + click_button('Remove member') end - context 'project bots' do - let(:project_bot) { create(:user, :project_bot, name: 'project_bot') } + wait_for_requests - before do - project.add_maintainer(project_bot) - end + expect(members_table).not_to have_content(other_user.name) + end - it 'does not show form used to change roles and "Expiration date" or the remove user button' do - visit_members_page + it 'invite user to project' do + visit_members_page - page.within find_member_row(project_bot) do - expect(page).not_to have_button('Maintainer') - expect(page).to have_field('Expiration date', disabled: true) - expect(page).not_to have_button('Remove member') - end - end + add_user('test@example.com', 'Reporter') + + click_link 'Invited' + + page.within find_invited_member_row('test@example.com') do + expect(page).to have_button('Reporter') end end - context 'when `vue_project_members_list` feature flag is disabled' do - include Spec::Support::Helpers::Features::ListRowsHelpers + context 'project bots' do + let(:project_bot) { create(:user, :project_bot, name: 'project_bot') } before do - stub_feature_flags(vue_project_members_list: false) + project.add_maintainer(project_bot) end - it 'show members from project and group' do - project.add_developer(user2) - + it 'does not show form used to change roles and "Expiration date" or the remove user button' do visit_members_page - expect(first_row.text).to include(user1.name) - expect(second_row.text).to include(user2.name) + page.within find_member_row(project_bot) do + expect(page).not_to have_button('Maintainer') + expect(page).to have_field('Expiration date', disabled: true) + expect(page).not_to have_button('Remove member') + end end + end - it 'show user once if member of both group and project' do - project.add_developer(user1) - - visit_members_page + describe 'when user has 2FA enabled' do + let_it_be(:admin) { create(:admin) } + let_it_be(:user_with_2fa) { create(:user, :two_factor_via_otp) } - expect(first_row.text).to include(user1.name) - expect(second_row).to be_blank + before do + project.add_guest(user_with_2fa) end - it 'update user access level', :js do - project.add_developer(user2) + it 'shows 2FA badge to user with "Maintainer" access level' do + project.add_maintainer(user1) visit_members_page - page.within(second_row) do - click_button('Developer') - click_link('Reporter') - - expect(page).to have_button('Reporter') - end + expect(find_member_row(user_with_2fa)).to have_content('2FA') end - it 'add user to project', :js do - visit_members_page + it 'shows 2FA badge to admins' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) - add_user(user2.id, 'Reporter') + visit_members_page - page.within(second_row) do - expect(page).to have_content(user2.name) - expect(page).to have_button('Reporter') - end + expect(find_member_row(user_with_2fa)).to have_content('2FA') end - it 'remove user from project', :js do - other_user = create(:user) - project.add_developer(other_user) + it 'does not show 2FA badge to users with access level below "Maintainer"' do + group.add_developer(user1) visit_members_page - # Open modal - find(:css, 'li.project_member', text: other_user.name).find(:css, 'button.btn-danger').click - - expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' - - click_on('Remove member') - - wait_for_requests - - expect(page).not_to have_content(other_user.name) - expect(project.users).not_to include(other_user) + expect(find_member_row(user_with_2fa)).not_to have_content('2FA') end - it 'invite user to project', :js do - visit_members_page - - add_user('test@example.com', 'Reporter') + it 'shows 2FA badge to themselves' do + sign_in(user_with_2fa) - click_link 'Invited' + visit_members_page - page.within(first_row) do - expect(page).to have_content('test@example.com') - expect(page).to have_content('Invited') - expect(page).to have_button('Reporter') - end + expect(find_member_row(user_with_2fa)).to have_content('2FA') end + end - context 'project bots' do - let(:project_bot) { create(:user, :project_bot, name: 'project_bot') } - - before do - project.add_maintainer(project_bot) - end + private - it 'does not show form used to change roles and "Expiration date" or the remove user button' do - project_member = project.project_members.find_by(user_id: project_bot.id) + def add_user(id, role) + click_on 'Invite members' - visit_members_page + page.within '#invite-members-modal' do + fill_in 'Search for members to invite', with: id - expect(page).not_to have_selector("#edit_project_member_#{project_member.id}") - expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger") - end - end - end + wait_for_requests + click_button id - private + click_button 'Guest' + wait_for_requests + click_button role - def add_user(id, role) - page.within ".invite-users-form" do - select2(id, from: "#user_ids", multiple: true) - select(role, from: "access_level") + click_button 'Invite' end - click_button "Invite" + page.refresh end def visit_members_page diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index 1127c64e0c7..d22097a2f6f 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -18,107 +18,51 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date sign_in(maintainer) end - context 'when `vue_project_members_list` feature flag is enabled' do - it 'expiration date is displayed in the members list' do - stub_feature_flags(invite_members_group_modal: false) + it 'expiration date is displayed in the members list' do + stub_feature_flags(invite_members_group_modal: false) - visit project_project_members_path(project) + visit project_project_members_path(project) - page.within '.invite-users-form' do - select2(new_member.id, from: '#user_ids', multiple: true) + page.within '.invite-users-form' do + select2(new_member.id, from: '#user_ids', multiple: true) - fill_in 'expires_at', with: 5.days.from_now.to_date - find_field('expires_at').native.send_keys :enter + fill_in 'expires_at', with: 5.days.from_now.to_date + find_field('expires_at').native.send_keys :enter - click_on 'Invite' - end - - page.within find_member_row(new_member) do - expect(page).to have_content(/in \d days/) - end - end - - it 'changes expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date) - visit project_project_members_path(project) - - page.within find_member_row(new_member) do - fill_in 'Expiration date', with: 5.days.from_now.to_date - find_field('Expiration date').native.send_keys :enter - - wait_for_requests - - expect(page).to have_content(/in \d days/) - end + click_on 'Invite' end - it 'clears expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.to_date) - visit project_project_members_path(project) - - page.within find_member_row(new_member) do - expect(page).to have_content(/in \d days/) - - find('[data-testid="clear-button"]').click - - wait_for_requests - - expect(page).to have_content('No expiration set') - end + page.within find_member_row(new_member) do + expect(page).to have_content(/in \d days/) end end - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - end - - it 'expiration date is displayed in the members list' do - stub_feature_flags(invite_members_group_modal: false) - - visit project_project_members_path(project) - - page.within '.invite-users-form' do - select2(new_member.id, from: '#user_ids', multiple: true) - - fill_in 'expires_at', with: 3.days.from_now.to_date - find_field('expires_at').native.send_keys :enter + it 'changes expiration date' do + project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date) + visit project_project_members_path(project) - click_on 'Invite' - end + page.within find_member_row(new_member) do + fill_in 'Expiration date', with: 5.days.from_now.to_date + find_field('Expiration date').native.send_keys :enter - page.within "#project_member_#{project_member_id}" do - expect(page).to have_content('Expires in 3 days') - end - end - - it 'changes expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: 1.day.from_now.to_date) - visit project_project_members_path(project) - - page.within "#project_member_#{project_member_id}" do - fill_in 'Expiration date', with: 3.days.from_now.to_date - find_field('Expiration date').native.send_keys :enter + wait_for_requests - wait_for_requests - - expect(page).to have_content('Expires in 3 days') - end + expect(page).to have_content(/in \d days/) end + end - it 'clears expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date) - visit project_project_members_path(project) + it 'clears expiration date' do + project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.to_date) + visit project_project_members_path(project) - page.within "#project_member_#{project_member_id}" do - expect(page).to have_content('Expires in 3 days') + page.within find_member_row(new_member) do + expect(page).to have_content(/in \d days/) - find('.js-clear-input').click + find('[data-testid="clear-button"]').click - wait_for_requests + wait_for_requests - expect(page).not_to have_content('Expires in') - end + expect(page).to have_content('No expiration set') end end diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index 3c132747bc4..653564d1566 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Projects > Members > Sorting' do +RSpec.describe 'Projects > Members > Sorting', :js do include Spec::Support::Helpers::Features::MembersHelpers let(:maintainer) { create(:user, name: 'John Doe') } @@ -15,165 +15,85 @@ RSpec.describe 'Projects > Members > Sorting' do sign_in(maintainer) end - context 'when `vue_project_members_list` feature flag is enabled', :js do - it 'sorts by account by default' do - visit_members_list(sort: nil) + it 'sorts by account by default' do + visit_members_list(sort: nil) - expect(first_row).to have_content(maintainer.name) - expect(second_row).to have_content(developer.name) + expect(first_row).to have_content(maintainer.name) + expect(second_row).to have_content(developer.name) - expect_sort_by('Account', :asc) - end - - it 'sorts by max role ascending' do - visit_members_list(sort: :access_level_asc) - - expect(first_row).to have_content(developer.name) - expect(second_row).to have_content(maintainer.name) - - expect_sort_by('Max role', :asc) - end - - it 'sorts by max role descending' do - visit_members_list(sort: :access_level_desc) - - expect(first_row).to have_content(maintainer.name) - expect(second_row).to have_content(developer.name) - - expect_sort_by('Max role', :desc) - end - - it 'sorts by access granted ascending' do - visit_members_list(sort: :last_joined) - - expect(first_row).to have_content(maintainer.name) - expect(second_row).to have_content(developer.name) - - expect_sort_by('Access granted', :asc) - end - - it 'sorts by access granted descending' do - visit_members_list(sort: :oldest_joined) - - expect(first_row).to have_content(developer.name) - expect(second_row).to have_content(maintainer.name) - - expect_sort_by('Access granted', :desc) - end - - it 'sorts by account ascending' do - visit_members_list(sort: :name_asc) - - expect(first_row).to have_content(maintainer.name) - expect(second_row).to have_content(developer.name) - - expect_sort_by('Account', :asc) - end - - it 'sorts by account descending' do - visit_members_list(sort: :name_desc) - - expect(first_row).to have_content(developer.name) - expect(second_row).to have_content(maintainer.name) - - expect_sort_by('Account', :desc) - end + expect_sort_by('Account', :asc) + end - it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do - visit_members_list(sort: :recent_sign_in) + it 'sorts by max role ascending' do + visit_members_list(sort: :access_level_asc) - expect(first_row).to have_content(maintainer.name) - expect(second_row).to have_content(developer.name) + expect(first_row).to have_content(developer.name) + expect(second_row).to have_content(maintainer.name) - expect_sort_by('Last sign-in', :asc) - end + expect_sort_by('Max role', :asc) + end - it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do - visit_members_list(sort: :oldest_sign_in) + it 'sorts by max role descending' do + visit_members_list(sort: :access_level_desc) - expect(first_row).to have_content(developer.name) - expect(second_row).to have_content(maintainer.name) + expect(first_row).to have_content(maintainer.name) + expect(second_row).to have_content(developer.name) - expect_sort_by('Last sign-in', :desc) - end + expect_sort_by('Max role', :desc) end - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - end - - it 'sorts alphabetically by default' do - visit_members_list(sort: nil) + it 'sorts by access granted ascending' do + visit_members_list(sort: :last_joined) - expect(first_member).to include(maintainer.name) - expect(second_member).to include(developer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending') - end + expect(first_row).to have_content(maintainer.name) + expect(second_row).to have_content(developer.name) - it 'sorts by access level ascending' do - visit_members_list(sort: :access_level_asc) + expect_sort_by('Access granted', :asc) + end - expect(first_member).to include(developer.name) - expect(second_member).to include(maintainer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending') - end + it 'sorts by access granted descending' do + visit_members_list(sort: :oldest_joined) - it 'sorts by access level descending' do - visit_members_list(sort: :access_level_desc) + expect(first_row).to have_content(developer.name) + expect(second_row).to have_content(maintainer.name) - expect(first_member).to include(maintainer.name) - expect(second_member).to include(developer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending') - end + expect_sort_by('Access granted', :desc) + end - it 'sorts by last joined' do - visit_members_list(sort: :last_joined) + it 'sorts by account ascending' do + visit_members_list(sort: :name_asc) - expect(first_member).to include(maintainer.name) - expect(second_member).to include(developer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined') - end + expect(first_row).to have_content(maintainer.name) + expect(second_row).to have_content(developer.name) - it 'sorts by oldest joined' do - visit_members_list(sort: :oldest_joined) + expect_sort_by('Account', :asc) + end - expect(first_member).to include(developer.name) - expect(second_member).to include(maintainer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined') - end + it 'sorts by account descending' do + visit_members_list(sort: :name_desc) - it 'sorts by name ascending' do - visit_members_list(sort: :name_asc) + expect(first_row).to have_content(developer.name) + expect(second_row).to have_content(maintainer.name) - expect(first_member).to include(maintainer.name) - expect(second_member).to include(developer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending') - end + expect_sort_by('Account', :desc) + end - it 'sorts by name descending' do - visit_members_list(sort: :name_desc) + it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do + visit_members_list(sort: :recent_sign_in) - expect(first_member).to include(developer.name) - expect(second_member).to include(maintainer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending') - end + expect(first_row).to have_content(maintainer.name) + expect(second_row).to have_content(developer.name) - it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do - visit_members_list(sort: :recent_sign_in) + expect_sort_by('Last sign-in', :asc) + end - expect(first_member).to include(maintainer.name) - expect(second_member).to include(developer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in') - end + it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do + visit_members_list(sort: :oldest_sign_in) - it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do - visit_members_list(sort: :oldest_sign_in) + expect(first_row).to have_content(developer.name) + expect(second_row).to have_content(maintainer.name) - expect(first_member).to include(developer.name) - expect(second_member).to include(maintainer.name) - expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in') - end + expect_sort_by('Last sign-in', :desc) end private diff --git a/spec/features/projects/members/tabs_spec.rb b/spec/features/projects/members/tabs_spec.rb index eef3395de91..471be26e126 100644 --- a/spec/features/projects/members/tabs_spec.rb +++ b/spec/features/projects/members/tabs_spec.rb @@ -14,6 +14,11 @@ RSpec.describe 'Projects > Members > Tabs' do let_it_be(:invites) { create_list(:project_member, 2, :invited, project: project) } let_it_be(:project_group_links) { create_list(:project_group_link, 2, project: project) } + before do + sign_in(user) + visit project_project_members_path(project) + end + shared_examples 'active "Members" tab' do it 'displays "Members" tab' do expect(page).to have_selector('.nav-link.active', text: 'Members') @@ -21,11 +26,6 @@ RSpec.describe 'Projects > Members > Tabs' do end context 'tabs' do - before do - sign_in(user) - visit project_project_members_path(project) - end - where(:tab, :count) do 'Members' | 3 'Invited' | 2 @@ -44,69 +44,25 @@ RSpec.describe 'Projects > Members > Tabs' do end end - context 'when `vue_project_members_list` feature flag is enabled' do + context 'when searching "Groups"', :js do before do - sign_in(user) - visit project_project_members_path(project) - end - - context 'when searching "Groups"', :js do - before do - click_link 'Groups' - - fill_in_filtered_search 'Search groups', with: 'group' - end - - it 'displays "Groups" tab' do - expect(page).to have_selector('.nav-link.active', text: 'Groups') - end + click_link 'Groups' - context 'and then searching "Members"' do - before do - click_link 'Members 3' - - fill_in_filtered_search 'Filter members', with: 'user' - end - - it_behaves_like 'active "Members" tab' - end + fill_in_filtered_search 'Search groups', with: 'group' end - end - - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - sign_in(user) - visit project_project_members_path(project) + it 'displays "Groups" tab' do + expect(page).to have_selector('.nav-link.active', text: 'Groups') end - context 'when searching "Groups"', :js do + context 'and then searching "Members"' do before do - click_link 'Groups' + click_link 'Members 3' - page.within '[data-testid="group-link-search-form"]' do - fill_in 'search_groups', with: 'group' - find('button[type="submit"]').click - end + fill_in_filtered_search 'Filter members', with: 'user' end - it 'displays "Groups" tab' do - expect(page).to have_selector('.nav-link.active', text: 'Groups') - end - - context 'and then searching "Members"' do - before do - click_link 'Members 3' - - page.within '[data-testid="user-search-form"]' do - fill_in 'search', with: 'user' - find('button[type="submit"]').click - end - end - - it_behaves_like 'active "Members" tab' - end + it_behaves_like 'active "Members" tab' end end end diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index e3d8534ace9..9547ba8a390 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -3,11 +3,13 @@ require 'spec_helper' RSpec.describe 'Merge Request button' do - shared_examples 'Merge request button only shown when allowed' do - let(:user) { create(:user) } - let(:project) { create(:project, :public, :repository) } - let(:forked_project) { create(:project, :public, :repository, forked_from_project: project) } + include ProjectForksHelper + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public, :repository) } + let(:forked_project) { fork_project(project, user, repository: true) } + shared_examples 'Merge request button only shown when allowed' do context 'not logged in' do it 'does not show Create merge request button' do visit url @@ -22,10 +24,16 @@ RSpec.describe 'Merge Request button' do project.add_developer(user) end - it 'shows Create merge request button' do - href = project_new_merge_request_path(project, - merge_request: { source_branch: 'feature', - target_branch: 'master' }) + it 'shows Create merge request button', :js do + href = project_new_merge_request_path( + project, + merge_request: { + source_project_id: project.id, + source_branch: 'feature', + target_project_id: project.id, + target_branch: 'master' + } + ) visit url @@ -75,12 +83,16 @@ RSpec.describe 'Merge Request button' do end context 'on own fork of project' do - let(:user) { forked_project.owner } - - it 'shows Create merge request button' do - href = project_new_merge_request_path(forked_project, - merge_request: { source_branch: 'feature', - target_branch: 'master' }) + it 'shows Create merge request button', :js do + href = project_new_merge_request_path( + forked_project, + merge_request: { + source_project_id: forked_project.id, + source_branch: 'feature', + target_project_id: forked_project.id, + target_branch: 'master' + } + ) visit fork_url @@ -101,11 +113,33 @@ RSpec.describe 'Merge Request button' do end context 'on compare page' do + let(:label) { 'Create merge request' } + it_behaves_like 'Merge request button only shown when allowed' do - let(:label) { 'Create merge request' } let(:url) { project_compare_path(project, from: 'master', to: 'feature') } let(:fork_url) { project_compare_path(forked_project, from: 'master', to: 'feature') } end + + it 'shows the correct merge request button when viewing across forks', :js do + sign_in(user) + project.add_developer(user) + + href = project_new_merge_request_path( + project, + merge_request: { + source_project_id: forked_project.id, + source_branch: 'feature', + target_project_id: project.id, + target_branch: 'master' + } + ) + + visit project_compare_path(forked_project, from: 'master', to: 'feature', from_project_id: project.id) + + within("#content-body") do + expect(page).to have_link(label, href: href) + end + end end context 'on commits page' do diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 4aabf040655..ec34640bd00 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'New project', :js do visit new_project_path find('[data-qa-selector="blank_project_link"]').click - choose(s_(key)) + choose(key) click_button('Create project') page.within('#blank-project-pane') do expect(find_field("project_visibility_level_#{level}")).to be_checked @@ -95,33 +95,55 @@ RSpec.describe 'New project', :js do end context 'when group visibility is private but default is internal' do + let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + before do stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) end - it 'has private selected' do - group = create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) - visit new_project_path(namespace_id: group.id) - find('[data-qa-selector="blank_project_link"]').click + context 'when admin mode is enabled', :enable_admin_mode do + it 'has private selected' do + visit new_project_path(namespace_id: group.id) + find('[data-qa-selector="blank_project_link"]').click - page.within('#blank-project-pane') do - expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked + end + end + end + + context 'when admin mode is disabled' do + it 'is not allowed' do + visit new_project_path(namespace_id: group.id) + + expect(page).to have_content('Not Found') end end end context 'when group visibility is public but user requests private' do + let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + before do stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) end - it 'has private selected' do - group = create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) - visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) - find('[data-qa-selector="blank_project_link"]').click + context 'when admin mode is enabled', :enable_admin_mode do + it 'has private selected' do + visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) + find('[data-qa-selector="blank_project_link"]').click - page.within('#blank-project-pane') do - expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked + end + end + end + + context 'when admin mode is disabled' do + it 'is not allowed' do + visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) + + expect(page).to have_content('Not Found') end end end diff --git a/spec/features/projects/pages/user_edits_settings_spec.rb b/spec/features/projects/pages/user_edits_settings_spec.rb index 3649fae17ce..6156b5243de 100644 --- a/spec/features/projects/pages/user_edits_settings_spec.rb +++ b/spec/features/projects/pages/user_edits_settings_spec.rb @@ -140,7 +140,7 @@ RSpec.describe 'Pages edits pages settings', :js do before do allow(Projects::UpdateService).to receive(:new).and_return(service) - allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured') + allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occurred') end it 'tries to change the setting' do @@ -150,7 +150,7 @@ RSpec.describe 'Pages edits pages settings', :js do click_button 'Save' - expect(page).to have_text('Some error has occured') + expect(page).to have_text('Some error has occurred') end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 6421d3db2cd..9037aa5c9a8 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -14,6 +14,7 @@ RSpec.describe 'Pipelines', :js do sign_in(user) stub_feature_flags(graphql_pipeline_details: false) stub_feature_flags(graphql_pipeline_details_users: false) + stub_feature_flags(new_pipelines_table: false) project.add_developer(user) project.update!(auto_devops_attributes: { enabled: false }) @@ -519,75 +520,58 @@ RSpec.describe 'Pipelines', :js do end end - shared_examples 'mini pipeline renders' do |ci_mini_pipeline_gl_dropdown_enabled| - context 'mini pipeline graph' do - let!(:build) do - create(:ci_build, :pending, pipeline: pipeline, - stage: 'build', - name: 'build') - end + context 'mini pipeline graph' do + let!(:build) do + create(:ci_build, :pending, pipeline: pipeline, + stage: 'build', + name: 'build') + end - before do - stub_feature_flags(ci_mini_pipeline_gl_dropdown: ci_mini_pipeline_gl_dropdown_enabled) - visit_project_pipelines - end + dropdown_selector = '[data-testid="mini-pipeline-graph-dropdown"]' - let_it_be(:dropdown_toggle_selector) do - if ci_mini_pipeline_gl_dropdown_enabled - '[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle' - else - '[data-testid="mini-pipeline-graph-dropdown-toggle"]' - end - end + before do + visit_project_pipelines + end - it 'renders a mini pipeline graph' do - expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]') - expect(page).to have_selector(dropdown_toggle_selector) - end + it 'renders a mini pipeline graph' do + expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]') + expect(page).to have_selector(dropdown_selector) + end - context 'when clicking a stage badge' do - it 'opens a dropdown' do - find(dropdown_toggle_selector).click + context 'when clicking a stage badge' do + it 'opens a dropdown' do + find(dropdown_selector).click - expect(page).to have_link build.name - end + expect(page).to have_link build.name + end - it 'is possible to cancel pending build' do - find(dropdown_toggle_selector).click - find('.js-ci-action').click - wait_for_requests + it 'is possible to cancel pending build' do + find(dropdown_selector).click + find('.js-ci-action').click + wait_for_requests - expect(build.reload).to be_canceled - end + expect(build.reload).to be_canceled end + end - context 'for a failed pipeline' do - let!(:build) do - create(:ci_build, :failed, pipeline: pipeline, - stage: 'build', - name: 'build') - end + context 'for a failed pipeline' do + let!(:build) do + create(:ci_build, :failed, pipeline: pipeline, + stage: 'build', + name: 'build') + end - it 'displays the failure reason' do - find(dropdown_toggle_selector).click + it 'displays the failure reason' do + find(dropdown_selector).click - within('.js-builds-dropdown-list') do - build_element = page.find('.mini-pipeline-graph-dropdown-item') - expect(build_element['title']).to eq('build - failed - (unknown failure)') - end + within('.js-builds-dropdown-list') do + build_element = page.find('.mini-pipeline-graph-dropdown-item') + expect(build_element['title']).to eq('build - failed - (unknown failure)') end end end end - context 'with ci_mini_pipeline_gl_dropdown disabled' do - it_behaves_like "mini pipeline renders", false - end - - context 'with ci_mini_pipeline_gl_dropdown enabled' do - it_behaves_like "mini pipeline renders", true - end - context 'with pagination' do before do allow(Ci::Pipeline).to receive(:default_per_page).and_return(1) diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb index 2acdf983cf2..9e428a0623d 100644 --- a/spec/features/projects/releases/user_creates_release_spec.rb +++ b/spec/features/projects/releases/user_creates_release_spec.rb @@ -33,11 +33,11 @@ RSpec.describe 'User creates release', :js do end it 'defaults the "Create from" dropdown to the project\'s default branch' do - expect(page.find('.ref-selector button')).to have_content(project.default_branch) + expect(page.find('[data-testid="create-from-field"] .ref-selector button')).to have_content(project.default_branch) end - context 'when the "Save release" button is clicked', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297507' do - let(:tag_name) { 'v1.0' } + context 'when the "Save release" button is clicked' do + let(:tag_name) { 'v2.0.31' } let(:release_title) { 'A most magnificent release' } let(:release_notes) { 'Best. Release. **Ever.** :rocket:' } let(:link_1) { { url: 'https://gitlab.example.com/runbook', title: 'An example runbook', type: 'runbook' } } @@ -47,7 +47,7 @@ RSpec.describe 'User creates release', :js do fill_out_form_and_submit end - it 'creates a new release when "Create release" is clicked', :aggregate_failures do + it 'creates a new release when "Create release" is clicked and redirects to the release\'s dedicated page', :aggregate_failures do release = project.releases.last expect(release.tag).to eq(tag_name) @@ -65,10 +65,6 @@ RSpec.describe 'User creates release', :js do link = release.links.find { |l| l.link_type == link_2[:type] } expect(link.url).to eq(link_2[:url]) expect(link.name).to eq(link_2[:title]) - end - - it 'redirects to the dedicated page for the newly created release' do - release = project.releases.last expect(page).to have_current_path(project_release_path(project, release)) end @@ -116,30 +112,27 @@ RSpec.describe 'User creates release', :js do end def fill_out_form_and_submit - fill_tag_name(tag_name) + select_new_tag_name(tag_name) select_create_from(branch.name) fill_release_title(release_title) - select_milestone(milestone_1.title, and_tab: false) + select_milestone(milestone_1.title) select_milestone(milestone_2.title) - # Focus the "Release notes" field by clicking instead of tabbing - # because tabbing to the field requires too many tabs - # (see https://gitlab.com/gitlab-org/gitlab/-/issues/238619) - find_field('Release notes').click fill_release_notes(release_notes) - # Tab past the "assets" documentation link - focused_element.send_keys(:tab) - fill_asset_link(link_1) add_another_asset_link fill_asset_link(link_2) - # Submit using the Control+Enter shortcut - focused_element.send_keys([:control, :enter]) + # Click on the body in order to trigger a `blur` event on the current field. + # This triggers the form's validation to run so that the + # "Create release" button is enabled and clickable. + page.find('body').click + + click_button('Create release') wait_for_all_requests end diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb index 1d9f256a819..e3b36348f25 100644 --- a/spec/features/projects/settings/operations_settings_spec.rb +++ b/spec/features/projects/settings/operations_settings_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do describe 'Settings > Operations' do describe 'Incidents' do let(:create_issue) { 'Create an incident. Incidents are created for each alert triggered.' } - let(:send_email) { 'Send a separate email notification to Developers.' } + let(:send_email) { 'Send a single email notification to Owners and Maintainers for new alerts.' } before do create(:project_incident_management_setting, send_email: true, project: project) diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb index d31913d2dcf..50451075db5 100644 --- a/spec/features/projects/settings/service_desk_setting_spec.rb +++ b/spec/features/projects/settings/service_desk_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Service Desk Setting', :js do +RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do let(:project) { create(:project_empty_repo, :private, service_desk_enabled: false) } let(:presenter) { project.present(current_user: user) } let(:user) { create(:user) } @@ -66,5 +66,48 @@ RSpec.describe 'Service Desk Setting', :js do expect(find('[data-testid="incoming-email"]').value).to eq('address-suffix@example.com') end + + context 'issue description templates' do + let_it_be(:issuable_project_template_files) do + { + '.gitlab/issue_templates/project-issue-bar.md' => 'Project Issue Template Bar', + '.gitlab/issue_templates/project-issue-foo.md' => 'Project Issue Template Foo' + } + end + + let_it_be(:issuable_group_template_files) do + { + '.gitlab/issue_templates/group-issue-bar.md' => 'Group Issue Template Bar', + '.gitlab/issue_templates/group-issue-foo.md' => 'Group Issue Template Foo' + } + end + + let_it_be_with_reload(:group) { create(:group)} + let_it_be_with_reload(:project) { create(:project, :custom_repo, group: group, files: issuable_project_template_files) } + let_it_be(:group_template_repo) { create(:project, :custom_repo, group: group, files: issuable_group_template_files) } + + before do + stub_licensed_features(custom_file_templates_for_namespace: false, custom_file_templates: false) + group.update_columns(file_template_project_id: group_template_repo.id) + end + + context 'when inherited_issuable_templates enabled' do + before do + stub_feature_flags(inherited_issuable_templates: true) + visit edit_project_path(project) + end + + it_behaves_like 'issue description templates from current project only' + end + + context 'when inherited_issuable_templates disabled' do + before do + stub_feature_flags(inherited_issuable_templates: false) + visit edit_project_path(project) + end + + it_behaves_like 'issue description templates from current project only' + end + end end end diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb index e8e32d93f7b..397c334a2b8 100644 --- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb +++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb @@ -133,7 +133,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do it 'when unchecked sets :remove_source_branch_after_merge to false' do uncheck('project_remove_source_branch_after_merge') within('.merge-request-settings-form') do - find('.qa-save-merge-request-changes') + find('.rspec-save-merge-request-changes') click_on('Save changes') end @@ -157,7 +157,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do choose('project_project_setting_attributes_squash_option_default_on') within('.merge-request-settings-form') do - find('.qa-save-merge-request-changes') + find('.rspec-save-merge-request-changes') click_on('Save changes') end @@ -172,7 +172,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do choose('project_project_setting_attributes_squash_option_always') within('.merge-request-settings-form') do - find('.qa-save-merge-request-changes') + find('.rspec-save-merge-request-changes') click_on('Save changes') end @@ -187,7 +187,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do choose('project_project_setting_attributes_squash_option_never') within('.merge-request-settings-form') do - find('.qa-save-merge-request-changes') + find('.rspec-save-merge-request-changes') click_on('Save changes') end diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb index 0d22da34b91..b237e7e8ce7 100644 --- a/spec/features/projects/settings/user_manages_project_members_spec.rb +++ b/spec/features/projects/settings/user_manages_project_members_spec.rb @@ -19,123 +19,54 @@ RSpec.describe 'Projects > Settings > User manages project members' do sign_in(user) end - context 'when `vue_project_members_list` feature flag is enabled' do - it 'cancels a team member', :js do - visit(project_project_members_path(project)) + it 'cancels a team member', :js do + visit(project_project_members_path(project)) - page.within find_member_row(user_dmitriy) do - click_button 'Remove member' - end - - page.within('[role="dialog"]') do - expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' - click_button('Remove member') - end - - visit(project_project_members_path(project)) - - expect(members_table).not_to have_content(user_dmitriy.name) - expect(members_table).not_to have_content(user_dmitriy.username) + page.within find_member_row(user_dmitriy) do + click_button 'Remove member' end - it 'imports a team from another project', :js do - stub_feature_flags(invite_members_group_modal: false) - - project2.add_maintainer(user) - project2.add_reporter(user_mike) - - visit(project_project_members_path(project)) - - page.within('.invite-users-form') do - click_link('Import') - end - - select2(project2.id, from: '#source_project_id') - click_button('Import project members') - - expect(find_member_row(user_mike)).to have_content('Reporter') + page.within('[role="dialog"]') do + expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' + click_button('Remove member') end - it 'shows all members of project shared group', :js do - group.add_owner(user) - group.add_developer(user_dmitriy) - - share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER) - share_link.group_id = group.id - share_link.save! - - visit(project_project_members_path(project)) + visit(project_project_members_path(project)) - click_link 'Groups' - - expect(find_group_row(group)).to have_content('Maintainer') - end + expect(members_table).not_to have_content(user_dmitriy.name) + expect(members_table).not_to have_content(user_dmitriy.username) end - context 'when `vue_project_members_list` feature flag is disabled' do - before do - stub_feature_flags(vue_project_members_list: false) - end + it 'imports a team from another project', :js do + stub_feature_flags(invite_members_group_modal: false) - it 'cancels a team member', :js do - visit(project_project_members_path(project)) + project2.add_maintainer(user) + project2.add_reporter(user_mike) - project_member = project.project_members.find_by(user_id: user_dmitriy.id) + visit(project_project_members_path(project)) - page.within("#project_member_#{project_member.id}") do - # Open modal - click_on('Remove user from project') - end - - expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests' - - click_on('Remove member') - - visit(project_project_members_path(project)) - - expect(page).not_to have_content(user_dmitriy.name) - expect(page).not_to have_content(user_dmitriy.username) + page.within('.invite-users-form') do + click_link('Import') end - it 'imports a team from another project' do - stub_feature_flags(invite_members_group_modal: false) - - project2.add_maintainer(user) - project2.add_reporter(user_mike) - - visit(project_project_members_path(project)) + select2(project2.id, from: '#source_project_id') + click_button('Import project members') - page.within('.invite-users-form') do - click_link('Import') - end - - select(project2.full_name, from: 'source_project_id') - click_button('Import') - - project_member = project.project_members.find_by(user_id: user_mike.id) - - page.within("#project_member_#{project_member.id}") do - expect(page).to have_content('Mike') - expect(page).to have_content('Reporter') - end - end + expect(find_member_row(user_mike)).to have_content('Reporter') + end - it 'shows all members of project shared group', :js do - group.add_owner(user) - group.add_developer(user_dmitriy) + it 'shows all members of project shared group', :js do + group.add_owner(user) + group.add_developer(user_dmitriy) - share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER) - share_link.group_id = group.id - share_link.save! + share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER) + share_link.group_id = group.id + share_link.save! - visit(project_project_members_path(project)) + visit(project_project_members_path(project)) - click_link 'Groups' + click_link 'Groups' - page.within('[data-testid="project-member-groups"]') do - expect(page).to have_content('OpenSource') - expect(first('.group_member')).to have_content('Maintainer') - end - end + expect(find_group_row(group)).to have_content('Maintainer') end end diff --git a/spec/features/projects/settings/user_searches_in_settings_spec.rb b/spec/features/projects/settings/user_searches_in_settings_spec.rb new file mode 100644 index 00000000000..4c5b39d5282 --- /dev/null +++ b/spec/features/projects/settings/user_searches_in_settings_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User searches project settings', :js do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, namespace: user.namespace) } + + before do + sign_in(user) + end + + context 'in general settings page' do + let(:visit_path) { edit_project_path(project) } + + it_behaves_like 'can search settings with feature flag check', 'Naming', 'Visibility' + end + + context 'in Repository page' do + before do + visit project_settings_repository_path(project) + end + + it_behaves_like 'can search settings', 'Deploy keys', 'Mirroring repositories' + end + + context 'in CI/CD page' do + before do + visit project_settings_ci_cd_path(project) + end + + it_behaves_like 'can search settings', 'General pipelines', 'Auto DevOps' + end + + context 'in Operations page' do + before do + visit project_settings_operations_path(project) + end + + it_behaves_like 'can search settings', 'Alerts', 'Error tracking' + end +end diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb index 5f7d9b0963b..80dae4ec386 100644 --- a/spec/features/projects/show/user_manages_notifications_spec.rb +++ b/spec/features/projects/show/user_manages_notifications_spec.rb @@ -6,38 +6,36 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do let(:project) { create(:project, :public, :repository) } before do - stub_feature_flags(vue_notification_dropdown: false) sign_in(project.owner) end def click_notifications_button - first('.notifications-btn').click + first('[data-testid="notification-dropdown"]').click end it 'changes the notification setting' do visit project_path(project) click_notifications_button - click_link 'On mention' + click_button 'On mention' - page.within('.notification-dropdown') do - expect(page).not_to have_css('.gl-spinner') - end + wait_for_requests click_notifications_button - expect(find('.update-notification.is-active')).to have_content('On mention') - expect(page).to have_css('.notifications-icon[data-testid="notifications-icon"]') + + page.within first('[data-testid="notification-dropdown"]') do + expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention') + expect(page).to have_css('[data-testid="notifications-icon"]') + end end it 'changes the notification setting to disabled' do visit project_path(project) click_notifications_button - click_link 'Disabled' + click_button 'Disabled' - page.within('.notification-dropdown') do - expect(page).not_to have_css('.gl-spinner') + page.within first('[data-testid="notification-dropdown"]') do + expect(page).to have_css('[data-testid="notifications-off-icon"]') end - - expect(page).to have_css('.notifications-icon[data-testid="notifications-off-icon"]') end context 'custom notification settings' do @@ -65,11 +63,13 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do it 'shows notification settings checkbox' do visit project_path(project) click_notifications_button - page.find('a[data-notification-level="custom"]').click + click_button 'Custom' + + wait_for_requests - page.within('.custom-notifications-form') do + page.within('#custom-notifications-modal') do email_events.each do |event_name| - expect(page).to have_selector("input[name='notification_setting[#{event_name}]']") + expect(page).to have_selector("[data-testid='notification-setting-#{event_name}']") end end end @@ -80,7 +80,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do it 'is disabled' do visit project_path(project) - expect(page).to have_selector('.notifications-btn.disabled', visible: true) + expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled', visible: true) end end end diff --git a/spec/features/projects/show/user_uploads_files_spec.rb b/spec/features/projects/show/user_uploads_files_spec.rb index 053598a528e..2030c4d998a 100644 --- a/spec/features/projects/show/user_uploads_files_spec.rb +++ b/spec/features/projects/show/user_uploads_files_spec.rb @@ -33,4 +33,24 @@ RSpec.describe 'Projects > Show > User uploads files' do include_examples 'it uploads and commit a new file to a forked project' end + + context 'when in the empty_repo_upload experiment' do + before do + stub_experiments(empty_repo_upload: :candidate) + + visit(project_path(project)) + end + + context 'with an empty repo' do + let(:project) { create(:project, :empty_repo, creator: user) } + + include_examples 'uploads and commits a new text file via "upload file" button' + end + + context 'with a nonempty repo' do + let(:project) { create(:project, :repository, creator: user) } + + include_examples 'uploads and commits a new text file via "upload file" button' + end + end end diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb index 616c5065c07..e5ba6b503cc 100644 --- a/spec/features/projects/user_sees_sidebar_spec.rb +++ b/spec/features/projects/user_sees_sidebar_spec.rb @@ -201,7 +201,7 @@ RSpec.describe 'Projects > User sees sidebar' do expect(page).to have_content 'Operations' expect(page).not_to have_content 'Repository' - expect(page).not_to have_content 'CI / CD' + expect(page).not_to have_content 'CI/CD' expect(page).not_to have_content 'Merge Requests' end end @@ -213,7 +213,7 @@ RSpec.describe 'Projects > User sees sidebar' do visit project_path(project) within('.nav-sidebar') do - expect(page).to have_content 'CI / CD' + expect(page).to have_content 'CI/CD' end end diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb index 13ae035e8ef..f97c8d820e3 100644 --- a/spec/features/projects/user_uses_shortcuts_spec.rb +++ b/spec/features/projects/user_uses_shortcuts_spec.rb @@ -155,12 +155,12 @@ RSpec.describe 'User uses shortcuts', :js do end end - context 'when navigating to the CI / CD pages' do + context 'when navigating to the CI/CD pages' do it 'redirects to the Jobs page' do find('body').native.send_key('g') find('body').native.send_key('j') - expect(page).to have_active_navigation('CI / CD') + expect(page).to have_active_navigation('CI/CD') expect(page).to have_active_sub_navigation('Jobs') end end |