diff options
Diffstat (limited to 'spec/features')
89 files changed, 1722 insertions, 2103 deletions
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index aab2e6d7cef..bf280595ec7 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -92,97 +92,46 @@ RSpec.describe "Admin::Projects" do end end - context 'when `vue_project_members_list` feature flag is enabled', :js do - describe 'admin adds themselves to the project' do - before do - project.add_maintainer(user) - stub_feature_flags(invite_members_group_modal: false) - end - - it 'adds admin to the project as developer', :js do - visit project_project_members_path(project) - - page.within '.invite-users-form' do - select2(current_user.id, from: '#user_ids', multiple: true) - select 'Developer', from: 'access_level' - end - - click_button 'Invite' - - expect(find_member_row(current_user)).to have_content('Developer') - end + describe 'admin adds themselves to the project', :js do + before do + project.add_maintainer(user) + stub_feature_flags(invite_members_group_modal: false) end - describe 'admin removes themselves from the project' do - before do - project.add_maintainer(user) - project.add_developer(current_user) - end - - it 'removes admin from the project' do - visit project_project_members_path(project) - - expect(find_member_row(current_user)).to have_content('Developer') + it 'adds admin to the project as developer' do + visit project_project_members_path(project) - page.within find_member_row(current_user) do - click_button 'Leave' - end + page.within '.invite-users-form' do + select2(current_user.id, from: '#user_ids', multiple: true) + select 'Developer', from: 'access_level' + end - page.within('[role="dialog"]') do - click_button('Leave') - end + click_button 'Invite' - expect(current_path).to match dashboard_projects_path - end + expect(find_member_row(current_user)).to have_content('Developer') end end - context 'when `vue_project_members_list` feature flag is disabled' do + describe 'admin removes themselves from the project', :js do before do - stub_feature_flags(vue_project_members_list: false) + project.add_maintainer(user) + project.add_developer(current_user) end - describe 'admin adds themselves to the project' do - before do - project.add_maintainer(user) - stub_feature_flags(invite_members_group_modal: false) - end - - it 'adds admin to the project as developer', :js do - visit project_project_members_path(project) - - page.within '.invite-users-form' do - select2(current_user.id, from: '#user_ids', multiple: true) - select 'Developer', from: 'access_level' - end + it 'removes admin from the project' do + visit project_project_members_path(project) - click_button 'Invite' + expect(find_member_row(current_user)).to have_content('Developer') - page.within '.content-list' do - expect(page).to have_content(current_user.name) - expect(page).to have_content('Developer') - end + page.within find_member_row(current_user) do + click_button 'Leave' end - end - describe 'admin removes themselves from the project' do - before do - project.add_maintainer(user) - project.add_developer(current_user) + page.within('[role="dialog"]') do + click_button('Leave') end - it 'removes admin from the project' do - visit project_project_members_path(project) - - page.within '.content-list' do - expect(page).to have_content(current_user.name) - expect(page).to have_content('Developer') - end - - find(:css, '.content-list li', text: current_user.name).find(:css, 'a.btn-danger').click - - expect(page).not_to have_selector(:css, '.content-list') - end + expect(current_path).to match dashboard_projects_path end end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 52f39f65bd0..249621f5835 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -384,7 +384,20 @@ RSpec.describe 'Admin updates settings' do click_button 'Save changes' end - expect(current_settings.repository_storages_weighted_default).to be 50 + expect(current_settings.repository_storages_weighted).to eq('default' => 50) + end + + it 'still saves when settings are outdated' do + current_settings.update_attribute :repository_storages_weighted, { 'default' => 100, 'outdated' => 100 } + + visit repository_admin_application_settings_path + + page.within('.as-repository-storage') do + fill_in 'application_setting_repository_storages_weighted_default', with: 50 + click_button 'Save changes' + end + + expect(current_settings.repository_storages_weighted).to eq('default' => 50) end end diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb index c040811ada1..618fae3e46b 100644 --- a/spec/features/admin/dashboard_spec.rb +++ b/spec/features/admin/dashboard_spec.rb @@ -30,7 +30,6 @@ RSpec.describe 'admin visits dashboard' do describe 'Users statistic' do let_it_be(:users_statistics) { create(:users_statistics) } - let_it_be(:users_count_label) { Gitlab.ee? ? 'Billable users 71' : 'Active users 71' } it 'shows correct amounts of users', :aggregate_failures do visit admin_dashboard_stats_path @@ -42,9 +41,16 @@ RSpec.describe 'admin visits dashboard' do expect(page).to have_content('Users with highest role Maintainer 6') expect(page).to have_content('Users with highest role Owner 5') expect(page).to have_content('Bots 2') + + if Gitlab.ee? + expect(page).to have_content('Billable users 69') + else + expect(page).not_to have_content('Billable users 69') + end + expect(page).to have_content('Blocked users 7') expect(page).to have_content('Total users 78') - expect(page).to have_content(users_count_label) + expect(page).to have_content('Active users 71') end end end diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb index 07c87f98eb6..60f2f776595 100644 --- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb +++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb @@ -19,7 +19,6 @@ RSpec.describe 'Alert integrations settings form', :js do describe 'when viewing alert integrations as a maintainer' do context 'with the default page permissions' do before do - stub_feature_flags(multiple_http_integrations_custom_mapping: false) visit project_settings_operations_path(project, anchor: 'js-alert-management-settings') wait_for_requests end @@ -30,8 +29,8 @@ RSpec.describe 'Alert integrations settings form', :js do end end - it 'shows the new alerts setting form' do - expect(page).to have_content('1. Select integration type') + it 'shows the integrations list title' do + expect(page).to have_content('Current integrations') end end end @@ -44,7 +43,7 @@ RSpec.describe 'Alert integrations settings form', :js do wait_for_requests end - it 'shows the old alerts setting form' do + it 'does not have rights to access the setting form' do expect(page).not_to have_selector('.incident-management-list') expect(page).not_to have_selector('#js-alert-management-settings') end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 2d6b669f28b..2392f9d2f8a 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -13,8 +13,6 @@ RSpec.describe 'Issue Boards', :js do let_it_be(:user2) { create(:user) } before do - stub_feature_flags(board_new_list: false) - project.add_maintainer(user) project.add_maintainer(user2) @@ -68,6 +66,8 @@ RSpec.describe 'Issue Boards', :js do let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) } before do + stub_feature_flags(board_new_list: false) + visit project_board_path(project, board) wait_for_requests @@ -168,19 +168,6 @@ RSpec.describe 'Issue Boards', :js do expect(page).to have_selector('.board', count: 3) end - it 'removes checkmark in new list dropdown after deleting' do - click_button 'Add list' - wait_for_requests - - find('.js-new-board-list').click - - remove_list - - wait_for_requests - - expect(page).to have_selector('.board', count: 3) - end - it 'infinite scrolls list' do create_list(:labeled_issue, 50, project: project, labels: [planning]) @@ -311,7 +298,7 @@ RSpec.describe 'Issue Boards', :js do it 'shows issue count on the list' do page.within(find(".board:nth-child(2)")) do - expect(page.find('.js-issue-size')).to have_text(total_planning_issues) + expect(page.find('[data-testid="board-items-count"]')).to have_text(total_planning_issues) expect(page).not_to have_selector('.js-max-issue-size') end end @@ -321,6 +308,7 @@ RSpec.describe 'Issue Boards', :js do context 'new list' do it 'shows all labels in new list dropdown' do click_button 'Add list' + wait_for_requests page.within('.dropdown-menu-issues-board-new') do diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 08bc70d7116..c79bf2abff1 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -72,36 +72,6 @@ RSpec.describe 'Issue Boards', :js do end end - it 'removes card from board when clicking' do - click_card(card) - - page.within('.issue-boards-sidebar') do - click_button 'Remove from board' - end - - wait_for_requests - - page.within(find('.board:nth-child(2)')) do - expect(page).to have_selector('.board-card', count: 1) - end - end - - it 'does not show remove button for backlog or closed issues' do - create(:issue, project: project) - create(:issue, :closed, project: project) - - visit project_board_path(project, board) - wait_for_requests - - click_card(find('.board:nth-child(1)').first('.board-card')) - - expect(find('.issue-boards-sidebar')).not_to have_button 'Remove from board' - - click_card(find('.board:nth-child(3)').first('.board-card')) - - expect(find('.issue-boards-sidebar')).not_to have_button 'Remove from board' - end - context 'assignee' do it 'updates the issues assignee' do click_card(card) diff --git a/spec/features/boards/user_adds_lists_to_board_spec.rb b/spec/features/boards/user_adds_lists_to_board_spec.rb new file mode 100644 index 00000000000..b9945207bb2 --- /dev/null +++ b/spec/features/boards/user_adds_lists_to_board_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User adds lists', :js do + using RSpec::Parameterized::TableSyntax + + let_it_be(:group) { create(:group, :nested) } + let_it_be(:project) { create(:project, :public, namespace: group) } + let_it_be(:group_board) { create(:board, group: group) } + let_it_be(:project_board) { create(:board, project: project) } + let_it_be(:user) { create(:user) } + + let_it_be(:milestone) { create(:milestone, project: project) } + + let_it_be(:group_label) { create(:group_label, group: group) } + let_it_be(:project_label) { create(:label, project: project) } + let_it_be(:group_backlog_list) { create(:backlog_list, board: group_board) } + let_it_be(:project_backlog_list) { create(:backlog_list, board: project_board) } + + let_it_be(:issue) { create(:labeled_issue, project: project, labels: [group_label, project_label]) } + + before_all do + project.add_maintainer(user) + group.add_owner(user) + end + + where(:board_type, :graphql_board_lists_enabled, :board_new_list_enabled) do + :project | true | true + :project | false | true + :project | true | false + :project | false | false + :group | true | true + :group | false | true + :group | true | false + :group | false | false + end + + with_them do + before do + sign_in(user) + + set_cookie('sidebar_collapsed', 'true') + + stub_feature_flags( + graphql_board_lists: graphql_board_lists_enabled, + board_new_list: board_new_list_enabled + ) + + if board_type == :project + visit project_board_path(project, project_board) + elsif board_type == :group + visit group_board_path(group, group_board) + end + + wait_for_all_requests + end + + it 'creates new column for label containing labeled issue' do + click_button button_text(board_new_list_enabled) + wait_for_all_requests + + select_label(board_new_list_enabled, group_label) + + wait_for_all_requests + + expect(page).to have_selector('.board', text: group_label.title) + expect(find('.board:nth-child(2) .board-card')).to have_content(issue.title) + end + end + + def select_label(board_new_list_enabled, label) + if board_new_list_enabled + page.within('.board-add-new-list') do + find('label', text: label.title).click + click_button 'Add' + end + else + page.within('.dropdown-menu-issues-board-new') do + click_link label.title + end + end + end + + def button_text(board_new_list_enabled) + if board_new_list_enabled + 'Create list' + else + 'Add list' + end + end +end diff --git a/spec/features/commit_spec.rb b/spec/features/commit_spec.rb index 02754cc803e..80a30ab01b2 100644 --- a/spec/features/commit_spec.rb +++ b/spec/features/commit_spec.rb @@ -35,9 +35,8 @@ RSpec.describe 'Commit' do end end - context "pagination enabled" do + describe "pagination" do before do - stub_feature_flags(paginate_commit_view: true) stub_const("Projects::CommitController::COMMIT_DIFFS_PER_PAGE", 1) visit project_commit_path(project, commit) @@ -61,18 +60,5 @@ RSpec.describe 'Commit' do expect(page).to have_selector(".files ##{files[1].file_hash}") end end - - context "pagination disabled" do - before do - stub_feature_flags(paginate_commit_view: false) - - visit project_commit_path(project, commit) - end - - it "shows both diffs on the page" do - expect(page).to have_selector(".files ##{files[0].file_hash}") - expect(page).to have_selector(".files ##{files[1].file_hash}") - end - end end end diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb index 0b99fed2a2d..bc6f449edc5 100644 --- a/spec/features/dashboard/group_spec.rb +++ b/spec/features/dashboard/group_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Dashboard Group' do it 'creates new group', :js do visit dashboard_groups_path - find('.btn-success').click + find('[data-testid="new-group-button"]').click new_name = 'Samurai' fill_in 'group_name', with: new_name diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 8705c22c41a..d7330b5267b 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -198,14 +198,6 @@ RSpec.describe 'Dashboard Projects' do it_behaves_like 'hidden pipeline status' end - context 'when dashboard_pipeline_status is disabled' do - before do - stub_feature_flags(dashboard_pipeline_status: false) - end - - it_behaves_like 'hidden pipeline status' - end - context "when last_pipeline is missing" do before do project.last_pipeline.delete diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb index 32c0ba2a9a7..261e9fb9f3b 100644 --- a/spec/features/discussion_comments/commit_spec.rb +++ b/spec/features/discussion_comments/commit_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Thread Comments Commit', :js do visit project_commit_path(project, sample_commit.id) end - it_behaves_like 'thread comments', 'commit' + it_behaves_like 'thread comments for commit and snippet', 'commit' it 'has class .js-note-emoji' do expect(page).to have_css('.js-note-emoji') diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb index 86743e31fbd..ebb57b37918 100644 --- a/spec/features/discussion_comments/issue_spec.rb +++ b/spec/features/discussion_comments/issue_spec.rb @@ -8,13 +8,11 @@ RSpec.describe 'Thread Comments Issue', :js do let(:issue) { create(:issue, project: project) } before do - stub_feature_flags(remove_comment_close_reopen: false) - project.add_maintainer(user) sign_in(user) visit project_issue_path(project, issue) end - it_behaves_like 'thread comments', 'issue' + it_behaves_like 'thread comments for issue, epic and merge request', 'issue' end diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb index 82dcdf9f918..f60d7da6a30 100644 --- a/spec/features/discussion_comments/merge_request_spec.rb +++ b/spec/features/discussion_comments/merge_request_spec.rb @@ -9,7 +9,6 @@ RSpec.describe 'Thread Comments Merge Request', :js do before do stub_feature_flags(remove_resolve_note: false) - stub_feature_flags(remove_comment_close_reopen: false) project.add_maintainer(user) sign_in(user) @@ -20,5 +19,5 @@ RSpec.describe 'Thread Comments Merge Request', :js do wait_for_requests end - it_behaves_like 'thread comments', 'merge request' + it_behaves_like 'thread comments for issue, epic and merge request', 'merge request' end diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb index 42053e571e9..ca0a6d6e1c5 100644 --- a/spec/features/discussion_comments/snippets_spec.rb +++ b/spec/features/discussion_comments/snippets_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'Thread Comments Snippet', :js do visit project_snippet_path(project, snippet) end - it_behaves_like 'thread comments', 'snippet' + it_behaves_like 'thread comments for commit and snippet', 'snippet' end context 'with personal snippets' do @@ -32,6 +32,6 @@ RSpec.describe 'Thread Comments Snippet', :js do visit snippet_path(snippet) end - it_behaves_like 'thread comments', 'snippet' + it_behaves_like 'thread comments for commit and snippet', 'snippet' end end diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 55bdf4c244e..cbd1ae628d1 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -17,7 +17,6 @@ RSpec.describe 'Expand and collapse diffs', :js do # Ensure that undiffable.md is in .gitattributes project.repository.copy_gitattributes(branch) visit project_commit_path(project, project.commit(branch)) - execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });') end def file_container(filename) @@ -191,10 +190,6 @@ RSpec.describe 'Expand and collapse diffs', :js do expect(small_diff).to have_selector('.code') expect(small_diff).not_to have_selector('.nothing-here-block') end - - it 'does not make a new HTTP request' do - expect(evaluate_script('ajaxUris')).not_to include(a_string_matching('small_diff.md')) - end end end @@ -264,7 +259,6 @@ RSpec.describe 'Expand and collapse diffs', :js do find('.note-textarea') wait_for_requests - execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });') end it 'reloads the page with all diffs expanded' do @@ -300,10 +294,6 @@ RSpec.describe 'Expand and collapse diffs', :js do expect(small_diff).to have_selector('.code') expect(small_diff).not_to have_selector('.nothing-here-block') end - - it 'does not make a new HTTP request' do - expect(evaluate_script('ajaxUris')).not_to include(a_string_matching('small_diff.md')) - end end end end diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb index cacabdda22d..65374263f45 100644 --- a/spec/features/groups/container_registry_spec.rb +++ b/spec/features/groups/container_registry_spec.rb @@ -67,7 +67,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 'latest' end it 'user removes a specific tag from container repository' do diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb index b0a896ec8cb..b81949da85d 100644 --- a/spec/features/groups/members/list_members_spec.rb +++ b/spec/features/groups/members/list_members_spec.rb @@ -47,4 +47,46 @@ RSpec.describe 'Groups > Members > List members', :js do expect(first_row).to have_selector('gl-emoji[data-name="smirk"]') end end + + 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) } + + before do + group.add_guest(user_with_2fa) + end + + it 'shows 2FA badge to user with "Owner" access level' do + group.add_owner(user1) + + visit group_group_members_path(group) + + expect(find_member_row(user_with_2fa)).to have_content('2FA') + end + + it 'shows 2FA badge to admins' do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + + visit group_group_members_path(group) + + expect(find_member_row(user_with_2fa)).to have_content('2FA') + end + + it 'does not show 2FA badge to users with access level below "Owner"' do + group.add_maintainer(user1) + + visit group_group_members_path(group) + + expect(find_member_row(user_with_2fa)).not_to have_content('2FA') + end + + it 'shows 2FA badge to themselves' do + sign_in(user_with_2fa) + + visit group_group_members_path(group) + + expect(find_member_row(user_with_2fa)).to have_content('2FA') + end + end end diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index c27d0afba6f..3b637a10abe 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Groups > Members > Manage members' do sign_in(user1) end - shared_examples 'includes the correct Invite Members link' do |should_include, should_not_include| + shared_examples 'includes the correct Invite link' do |should_include, should_not_include| it 'includes either the form or the modal trigger' do group.add_owner(user1) @@ -31,15 +31,13 @@ RSpec.describe 'Groups > Members > Manage members' do stub_feature_flags(invite_members_group_modal: true) end - it_behaves_like 'includes the correct Invite Members link', '.js-invite-members-trigger', '.invite-users-form' + it_behaves_like 'includes the correct Invite link', '.js-invite-members-trigger', '.invite-users-form' + it_behaves_like 'includes the correct Invite link', '.js-invite-group-trigger', '.invite-group-form' end context 'when Invite Members modal is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - it_behaves_like 'includes the correct Invite Members link', '.invite-users-form', '.js-invite-members-trigger' + it_behaves_like 'includes the correct Invite link', '.invite-users-form', '.js-invite-members-trigger' + it_behaves_like 'includes the correct Invite link', '.invite-group-form', '.js-invite-group-trigger' end it 'update user to owner level', :js do diff --git a/spec/features/groups/settings/user_searches_in_settings_spec.rb b/spec/features/groups/settings/user_searches_in_settings_spec.rb new file mode 100644 index 00000000000..819d0c4faba --- /dev/null +++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User searches group settings', :js do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, namespace: group) } + + before do + group.add_owner(user) + sign_in(user) + end + + context 'in general settings page' do + let(:visit_path) { edit_group_path(group) } + + it_behaves_like 'can search settings with feature flag check', 'Naming', 'Permissions' + end + + context 'in Repository page' do + before do + visit group_settings_repository_path(group) + end + + it_behaves_like 'can search settings', 'Deploy tokens', 'Default initial branch name' + end + + context 'in CI/CD page' do + before do + visit group_settings_ci_cd_path(group) + end + + it_behaves_like 'can search settings', 'Variables', 'Runners' + end +end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 5067f11be67..4bcba4c21ed 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -163,7 +163,6 @@ RSpec.describe 'Group show page' do let!(:project) { create(:project, namespace: group) } before do - stub_feature_flags(vue_notification_dropdown: false) group.add_maintainer(maintainer) sign_in(maintainer) end @@ -171,14 +170,14 @@ RSpec.describe 'Group show page' do it 'is enabled by default' do visit path - expect(page).to have_selector('.notifications-btn:not(.disabled)', visible: true) + expect(page).to have_selector('[data-testid="notification-dropdown"] button:not(.disabled)') end it 'is disabled if emails are disabled' do group.update_attribute(:emails_disabled, true) visit path - expect(page).to have_selector('.notifications-btn.disabled', visible: true) + expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled') end end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index c9a0844932a..28b22860f0a 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -143,7 +143,7 @@ RSpec.describe 'Group' do end end - describe 'create a nested group', :js do + describe 'create a nested group' do let_it_be(:group) { create(:group, path: 'foo') } context 'as admin' do @@ -153,13 +153,21 @@ RSpec.describe 'Group' do visit new_group_path(group, parent_id: group.id) end - it 'creates a nested group' do - fill_in 'Group name', with: 'bar' - fill_in 'Group URL', with: 'bar' - click_button 'Create group' + context 'when admin mode is enabled', :enable_admin_mode do + it 'creates a nested group' do + fill_in 'Group name', with: 'bar' + fill_in 'Group URL', with: 'bar' + click_button 'Create group' - expect(current_path).to eq(group_path('foo/bar')) - expect(page).to have_content("Group 'bar' was successfully created.") + expect(current_path).to eq(group_path('foo/bar')) + expect(page).to have_content("Group 'bar' was successfully created.") + end + end + + context 'when admin mode is disabled' do + it 'is not allowed' do + expect(page).to have_gitlab_http_status(:not_found) + end end end diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb index d773126e00c..a4e9df604a9 100644 --- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb @@ -89,6 +89,8 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j before do page.within '.mr-widget-body' do page.click_link 'Resolve all threads in new issue', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + + wait_for_all_requests end end diff --git a/spec/features/issues/csv_spec.rb b/spec/features/issues/csv_spec.rb index c93693ec40a..d41a41c4383 100644 --- a/spec/features/issues/csv_spec.rb +++ b/spec/features/issues/csv_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Issues csv' do +RSpec.describe 'Issues csv', :js do let(:user) { create(:user) } let(:project) { create(:project, :public) } let(:milestone) { create(:milestone, title: 'v1.0', project: project) } @@ -17,7 +17,7 @@ RSpec.describe 'Issues csv' do def request_csv(params = {}) visit project_issues_path(project, params) page.within('.nav-controls') do - click_on 'Export as CSV' + find('[data-testid="export-csv-button"]').click end click_on 'Export issues' end diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index e2087868035..e6ebc37ba59 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'GFM autocomplete', :js do let_it_be(:user_xss_title) { 'eve <img src=x onerror=alert(2)<img src=x onerror=alert(1)>' } let_it_be(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') } let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } + let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') } let_it_be(:group) { create(:group, name: 'Ancestor') } let_it_be(:child_group) { create(:group, parent: group, name: 'My group') } let_it_be(:project) { create(:project, group: child_group) } @@ -16,6 +17,7 @@ RSpec.describe 'GFM autocomplete', :js do before_all do project.add_maintainer(user) project.add_maintainer(user_xss) + project.add_maintainer(user2) end describe 'when tribute_autocomplete feature flag is off' do @@ -29,289 +31,218 @@ RSpec.describe 'GFM autocomplete', :js do end it 'updates issue description with GFM reference' do - find('.js-issuable-edit').click + click_button 'Edit title and description' wait_for_requests - simulate_input('#issue-description', "@#{user.name[0...3]}") + fill_in 'Description', with: "@#{user.name[0...3]}" wait_for_requests - find('.atwho-view .cur').click + find_highlighted_autocomplete_item.click click_button 'Save changes' wait_for_requests - expect(find('.description')).to have_content(user.to_reference) + expect(find('.description')).to have_text(user.to_reference) end it 'opens quick action autocomplete when updating description' do - find('.js-issuable-edit').click + click_button 'Edit title and description' - find('#issue-description').native.send_keys('/') + fill_in 'Description', with: '/' - expect(page).to have_selector('.atwho-container') + expect(find_autocomplete_menu).to be_visible end it 'opens autocomplete menu when field starts with text' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@') - end + fill_in 'Comment', with: '@' - expect(page).to have_selector('.atwho-container') + expect(find_autocomplete_menu).to be_visible end it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' create(:issue, project: project, title: issue_xss_title) - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('#') - end + fill_in 'Comment', with: '#' wait_for_requests - expect(page).to have_selector('.atwho-container') - - page.within '.atwho-container #at-view-issues' do - expect(page.all('li').first.text).to include(issue_xss_title) - end + expect(find_autocomplete_menu).to have_text(issue_xss_title) end it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@ev') - end + fill_in 'Comment', with: '@ev' wait_for_requests - expect(page).to have_selector('.atwho-container') - - page.within '.atwho-container #at-view-users' do - expect(find('li').text).to have_content(user_xss.username) - end + expect(find_highlighted_autocomplete_item).to have_text(user_xss.username) end it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do milestone_xss_title = 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' create(:milestone, project: project, title: milestone_xss_title) - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('%') - end + fill_in 'Comment', with: '%' wait_for_requests - expect(page).to have_selector('.atwho-container') - - page.within '.atwho-container #at-view-milestones' do - expect(find('li').text).to have_content('alert milestone') - end + expect(find_autocomplete_menu).to have_text('alert milestone') end it 'doesnt open autocomplete menu character is prefixed with text' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('testing') - find('#note-body').native.send_keys('@') - end + fill_in 'Comment', with: 'testing@' - expect(page).not_to have_selector('.atwho-view') + expect(page).not_to have_css('.atwho-view') end it 'doesnt select the first item for non-assignee dropdowns' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys(':') - end - - expect(page).to have_selector('.atwho-container') + fill_in 'Comment', with: ':' wait_for_requests - expect(find('#at-view-58')).not_to have_selector('.cur:first-of-type') + expect(find_autocomplete_menu).not_to have_css('.cur') end it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do - note = find('#note-body') - # Number. - page.within '.timeline-content-form' do - note.native.send_keys('7:') - end - - expect(page).not_to have_selector('.atwho-view') + fill_in 'Comment', with: '7:' + expect(page).not_to have_css('.atwho-view') # ASCII letter. - page.within '.timeline-content-form' do - note.set('') - note.native.send_keys('w:') - end - - expect(page).not_to have_selector('.atwho-view') + fill_in 'Comment', with: 'w:' + expect(page).not_to have_css('.atwho-view') # Non-ASCII letter. - page.within '.timeline-content-form' do - note.set('') - note.native.send_keys('Ё:') - end - - expect(page).not_to have_selector('.atwho-view') + fill_in 'Comment', with: 'Ё:' + expect(page).not_to have_css('.atwho-view') end it 'selects the first item for assignee dropdowns' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@') - end - - expect(page).to have_selector('.atwho-container') + fill_in 'Comment', with: '@' wait_for_requests - expect(find('#at-view-users')).to have_selector('.cur:first-of-type') + expect(find_autocomplete_menu).to have_css('.cur:first-of-type') end it 'includes items for assignee dropdowns with non-ASCII characters in name' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') - simulate_input('#note-body', "@#{user.name[0...8]}") - end - - expect(page).to have_selector('.atwho-container') + fill_in 'Comment', with: "@#{user.name[0...8]}" wait_for_requests - expect(find('#at-view-users')).to have_content(user.name) + expect(find_autocomplete_menu).to have_text(user.name) end it 'searches across full name for assignees' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@speciąlsome') - end + fill_in 'Comment', with: '@speciąlsome' wait_for_requests - expect(find('.atwho-view li', visible: true)).to have_content(user.name) + expect(find_highlighted_autocomplete_item).to have_text(user.name) end - it 'selects the first item for non-assignee dropdowns if a query is entered' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys(':1') - end + it 'shows names that start with the query as the top result' do + fill_in 'Comment', with: '@mar' + + wait_for_requests + + expect(find_highlighted_autocomplete_item).to have_text(user2.name) + end + + it 'shows usernames that start with the query as the top result' do + fill_in 'Comment', with: '@msi' + + wait_for_requests + + expect(find_highlighted_autocomplete_item).to have_text(user2.name) + end + + # Regression test for https://gitlab.com/gitlab-org/gitlab/-/issues/321925 + it 'shows username when pasting then pressing Enter' do + fill_in 'Comment', with: "@#{user.username}\n" + + expect(find_field('Comment').value).to have_text "@#{user.username}" + end - expect(page).to have_selector('.atwho-container') + it 'does not show `@undefined` when pressing `@` then Enter' do + fill_in 'Comment', with: "@\n" + + expect(find_field('Comment').value).to have_text '@' + expect(find_field('Comment').value).not_to have_text '@undefined' + end + + it 'selects the first item for non-assignee dropdowns if a query is entered' do + fill_in 'Comment', with: ':1' wait_for_requests - expect(find('#at-view-58')).to have_selector('.cur:first-of-type') + expect(find_autocomplete_menu).to have_css('.cur:first-of-type') end context 'if a selected value has special characters' do it 'wraps the result in double quotes' do - note = find('#note-body') - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') - simulate_input('#note-body', "~#{label.title[0]}") - end + fill_in 'Comment', with: "~#{label.title[0]}" - label_item = find('.atwho-view li', text: label.title) + find_highlighted_autocomplete_item.click - expect_to_wrap(true, label_item, note, label.title) + expect(find_field('Comment').value).to have_text("~\"#{label.title}\"") end it "shows dropdown after a new line" do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('test') - note.native.send_keys(:enter) - note.native.send_keys(:enter) - note.native.send_keys('@') - end + fill_in 'Comment', with: "test\n\n@" - expect(page).to have_selector('.atwho-container') + expect(find_autocomplete_menu).to be_visible end it "does not show dropdown when preceded with a special character" do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys("@") - end - - expect(page).to have_selector('.atwho-container') - - page.within '.timeline-content-form' do - note.native.send_keys("@") - end + fill_in 'Comment', with: '@@' - expect(page).to have_selector('.atwho-container', visible: false) - end - - it "does not throw an error if no labels exist" do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('~') - end - - expect(page).to have_selector('.atwho-container', visible: false) + expect(page).not_to have_css('.atwho-view') end it 'doesn\'t wrap for assignee values' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys("@#{user.username[0]}") - end + fill_in 'Comment', with: "@#{user.username[0]}" - user_item = find('.atwho-view li', text: user.username) + find_highlighted_autocomplete_item.click - expect_to_wrap(false, user_item, note, user.username) + expect(find_field('Comment').value).to have_text("@#{user.username}") end it 'doesn\'t wrap for emoji values' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys(":cartwheel_") - end + fill_in 'Comment', with: ':cartwheel_' - emoji_item = find('.atwho-view li', text: 'cartwheel_tone1') + find_highlighted_autocomplete_item.click - expect_to_wrap(false, emoji_item, note, 'cartwheel_tone1') + expect(find_field('Comment').value).to have_text('cartwheel_tone1') end it 'doesn\'t open autocomplete after non-word character' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys("@#{user.username[0..2]}!") - end + fill_in 'Comment', with: "@#{user.username[0..2]}!" - expect(page).not_to have_selector('.atwho-view') + expect(page).not_to have_css('.atwho-view') end it 'doesn\'t open autocomplete if there is no space before' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys("hello:#{user.username[0..2]}") - end + fill_in 'Comment', with: "hello:#{user.username[0..2]}" - expect(page).not_to have_selector('.atwho-view') + expect(page).not_to have_css('.atwho-view') end it 'triggers autocomplete after selecting a quick action' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/as') - end + fill_in 'Comment', with: '/as' - find('.atwho-view li', text: '/assign') - note.native.send_keys(:tab) + find_highlighted_autocomplete_item.click - user_item = find('.atwho-view li', text: user.username) - expect(user_item).to have_content(user.username) + expect(find_autocomplete_menu).to have_text(user.username) end it 'does not limit quick actions autocomplete list to 5' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/') - end + fill_in 'Comment', with: '/' - expect(page).to have_selector('.atwho-view li', minimum: 6, visible: true) + expect(find_autocomplete_menu).to have_css('li', minimum: 6) end end @@ -328,30 +259,23 @@ RSpec.describe 'GFM autocomplete', :js do it 'lists users who are currently not assigned to the issue when using /assign' do visit project_issue_path(project, issue_assignee) - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/as') - end - - find('.atwho-view li', text: '/assign') - note.native.send_keys(:tab) + fill_in 'Comment', with: '/as' - wait_for_requests + find_highlighted_autocomplete_item.click - expect(find('#at-view-users .atwho-view-ul')).not_to have_content(user.username) - expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username) + expect(find_autocomplete_menu).not_to have_text(user.username) + expect(find_autocomplete_menu).to have_text(unassigned_user.username) end it 'shows dropdown on new issue form' do visit new_project_issue_path(project) - textarea = find('#issue_description') - textarea.native.send_keys('/ass') - find('.atwho-view li', text: '/assign') - textarea.native.send_keys(:tab) + fill_in 'Description', with: '/ass' - expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username) - expect(find('#at-view-users .atwho-view-ul')).to have_content(user.username) + find_highlighted_autocomplete_item.click + + expect(find_autocomplete_menu).to have_text(unassigned_user.username) + expect(find_autocomplete_menu).to have_text(user.username) end end @@ -360,80 +284,62 @@ RSpec.describe 'GFM autocomplete', :js do label_xss_title = 'alert label <img src=x onerror="alert(\'Hello xss\');" a' create(:label, project: project, title: label_xss_title) - note = find('#note-body') - - # It should show all the labels on "~". - type(note, '~') + fill_in 'Comment', with: '~' wait_for_requests - page.within '.atwho-container #at-view-labels' do - expect(find('.atwho-view-ul').text).to have_content('alert label') - end + expect(find_autocomplete_menu).to have_text('alert label') end it 'allows colons when autocompleting scoped labels' do create(:label, project: project, title: 'scoped:label') - note = find('#note-body') - type(note, '~scoped:') + fill_in 'Comment', with: '~scoped:' wait_for_requests - page.within '.atwho-container #at-view-labels' do - expect(find('.atwho-view-ul').text).to have_content('scoped:label') - end + expect(find_autocomplete_menu).to have_text('scoped:label') end it 'allows colons when autocompleting scoped labels with double colons' do create(:label, project: project, title: 'scoped::label') - note = find('#note-body') - type(note, '~scoped::') + fill_in 'Comment', with: '~scoped::' wait_for_requests - page.within '.atwho-container #at-view-labels' do - expect(find('.atwho-view-ul').text).to have_content('scoped::label') - end + expect(find_autocomplete_menu).to have_text('scoped::label') end it 'allows spaces when autocompleting multi-word labels' do create(:label, project: project, title: 'Accepting merge requests') - note = find('#note-body') - type(note, '~Accepting merge') + fill_in 'Comment', with: '~Accepting merge' wait_for_requests - page.within '.atwho-container #at-view-labels' do - expect(find('.atwho-view-ul').text).to have_content('Accepting merge requests') - end + expect(find_autocomplete_menu).to have_text('Accepting merge requests') end it 'only autocompletes the latest label' do create(:label, project: project, title: 'Accepting merge requests') create(:label, project: project, title: 'Accepting job applicants') - note = find('#note-body') - type(note, '~Accepting merge requests foo bar ~Accepting job') + fill_in 'Comment', with: '~Accepting merge requests foo bar ~Accepting job' wait_for_requests - page.within '.atwho-container #at-view-labels' do - expect(find('.atwho-view-ul').text).to have_content('Accepting job applicants') - end + expect(find_autocomplete_menu).to have_text('Accepting job applicants') end it 'does not autocomplete labels if no tilde is typed' do create(:label, project: project, title: 'Accepting merge requests') - note = find('#note-body') - type(note, 'Accepting merge') + fill_in 'Comment', with: 'Accepting merge' wait_for_requests - expect(page).not_to have_css('.atwho-container #at-view-labels') + expect(page).not_to have_css('.atwho-view') end end @@ -443,7 +349,7 @@ RSpec.describe 'GFM autocomplete', :js do # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729 it 'keeps autocomplete key listeners' do visit project_issue_path(project, issue) - note = find('#note-body') + note = find_field('Comment') start_comment_with_emoji(note, '.atwho-view li') @@ -459,17 +365,11 @@ RSpec.describe 'GFM autocomplete', :js do shared_examples 'autocomplete suggestions' do it 'suggests objects correctly' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys(object.class.reference_prefix) - end - - page.within '.atwho-container' do - expect(page).to have_content(object.title) + fill_in 'Comment', with: object.class.reference_prefix - find('ul li').click - end + find_autocomplete_menu.find('li').click - expect(find('.new-note #note-body').value).to include(expected_body) + expect(find_field('Comment').value).to have_text(expected_body) end end @@ -502,10 +402,40 @@ RSpec.describe 'GFM autocomplete', :js do end context 'milestone' do - let!(:object) { create(:milestone, project: project) } - let(:expected_body) { object.to_reference } + let_it_be(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) } + let_it_be(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') } + let_it_be(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) } + let_it_be(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) } + let_it_be(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) } - it_behaves_like 'autocomplete suggestions' + before do + fill_in 'Comment', with: '/milestone %' + + wait_for_requests + end + + it 'shows milestons list in the autocomplete menu' do + page.within(find_autocomplete_menu) do + expect(page).to have_selector('li', count: 5) + end + end + + it 'shows expired milestone at the bottom of the list' do + page.within(find_autocomplete_menu) do + expect(page.find('li:last-child')).to have_content milestone_expired.title + end + end + + it 'shows milestone due earliest at the top of the list' do + page.within(find_autocomplete_menu) do + aggregate_failures do + expect(page.all('li')[0]).to have_content milestone3.title + expect(page.all('li')[1]).to have_content milestone2.title + expect(page.all('li')[2]).to have_content milestone1.title + expect(page.all('li')[3]).to have_content milestone_no_duedate.title + end + end + end end end @@ -520,237 +450,160 @@ RSpec.describe 'GFM autocomplete', :js do end it 'updates issue description with GFM reference' do - find('.js-issuable-edit').click + click_button 'Edit title and description' wait_for_requests - simulate_input('#issue-description', "@#{user.name[0...3]}") + fill_in 'Description', with: "@#{user.name[0...3]}" wait_for_requests - find('.tribute-container .highlight', visible: true).click + find_highlighted_tribute_autocomplete_menu.click click_button 'Save changes' wait_for_requests - expect(find('.description')).to have_content(user.to_reference) + expect(find('.description')).to have_text(user.to_reference) end it 'opens autocomplete menu when field starts with text' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@') - end + fill_in 'Comment', with: '@' - expect(page).to have_selector('.tribute-container', visible: true) + expect(find_tribute_autocomplete_menu).to be_visible end it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' create(:issue, project: project, title: issue_xss_title) - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('#') - end + fill_in 'Comment', with: '#' wait_for_requests - expect(page).to have_selector('.tribute-container', visible: true) - - page.within '.tribute-container ul' do - expect(page.all('li').first.text).to include(issue_xss_title) - end + expect(find_tribute_autocomplete_menu).to have_text(issue_xss_title) end it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@ev') - end + fill_in 'Comment', with: '@ev' wait_for_requests - expect(page).to have_selector('.tribute-container', visible: true) - - expect(find('.tribute-container ul', visible: true)).to have_text(user_xss.username) + expect(find_tribute_autocomplete_menu).to have_text(user_xss.username) end it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do milestone_xss_title = 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' create(:milestone, project: project, title: milestone_xss_title) - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('%') - end + fill_in 'Comment', with: '%' wait_for_requests - expect(page).to have_selector('.tribute-container', visible: true) - - expect(find('.tribute-container ul', visible: true)).to have_text('alert milestone') + expect(find_tribute_autocomplete_menu).to have_text('alert milestone') end it 'does not open autocomplete menu when trigger character is prefixed with text' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('testing') - find('#note-body').native.send_keys('@') - end + fill_in 'Comment', with: 'testing@' - expect(page).not_to have_selector('.tribute-container', visible: true) + expect(page).not_to have_css('.tribute-container') end it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do - note = find('#note-body') - # Number. - page.within '.timeline-content-form' do - note.native.send_keys('7:') - end - - expect(page).not_to have_selector('.tribute-container', visible: true) + fill_in 'Comment', with: '7:' + expect(page).not_to have_css('.tribute-container') # ASCII letter. - page.within '.timeline-content-form' do - note.set('') - note.native.send_keys('w:') - end - - expect(page).not_to have_selector('.tribute-container', visible: true) + fill_in 'Comment', with: 'w:' + expect(page).not_to have_css('.tribute-container') # Non-ASCII letter. - page.within '.timeline-content-form' do - note.set('') - note.native.send_keys('Ё:') - end - - expect(page).not_to have_selector('.tribute-container', visible: true) + fill_in 'Comment', with: 'Ё:' + expect(page).not_to have_css('.tribute-container') end it 'selects the first item for assignee dropdowns' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@') - end - - expect(page).to have_selector('.tribute-container', visible: true) + fill_in 'Comment', with: '@' wait_for_requests - expect(find('.tribute-container ul', visible: true)).to have_selector('.highlight:first-of-type') + expect(find_tribute_autocomplete_menu).to have_css('.highlight:first-of-type') end it 'includes items for assignee dropdowns with non-ASCII characters in name' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') - simulate_input('#note-body', "@#{user.name[0...8]}") - end - - expect(page).to have_selector('.tribute-container', visible: true) + fill_in 'Comment', with: "@#{user.name[0...8]}" wait_for_requests - expect(find('.tribute-container ul', visible: true)).to have_content(user.name) + expect(find_tribute_autocomplete_menu).to have_text(user.name) end it 'selects the first item for non-assignee dropdowns if a query is entered' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys(':1') - end + fill_in 'Comment', with: ':1' wait_for_requests - expect(find('.tribute-container ul', visible: true)).to have_selector('.highlight:first-of-type') + expect(find_tribute_autocomplete_menu).to have_css('.highlight:first-of-type') end context 'when autocompleting for groups' do it 'shows the group when searching for the name of the group' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@mygroup') - end + fill_in 'Comment', with: '@mygroup' - expect(find('.tribute-container ul', visible: true)).to have_text('My group') + expect(find_tribute_autocomplete_menu).to have_text('My group') end it 'does not show the group when searching for the name of the parent of the group' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('@ancestor') - end + fill_in 'Comment', with: '@ancestor' - expect(find('.tribute-container ul', visible: true)).not_to have_text('My group') + expect(find_tribute_autocomplete_menu).not_to have_text('My group') end end context 'if a selected value has special characters' do it 'wraps the result in double quotes' do - note = find('#note-body') - page.within '.timeline-content-form' do - find('#note-body').native.send_keys('') - simulate_input('#note-body', "~#{label.title[0]}") - end + fill_in 'Comment', with: "~#{label.title[0]}" - label_item = find('.tribute-container ul', text: label.title, visible: true) + find_highlighted_tribute_autocomplete_menu.click - expect_to_wrap(true, label_item, note, label.title) + expect(find_field('Comment').value).to have_text("~\"#{label.title}\"") end it "shows dropdown after a new line" do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('test') - note.native.send_keys(:enter) - note.native.send_keys(:enter) - note.native.send_keys('@') - end - - expect(page).to have_selector('.tribute-container', visible: true) - end - - it "does not throw an error if no labels exist" do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('~') - end + fill_in 'Comment', with: "test\n\n@" - expect(page).to have_selector('.tribute-container', visible: false) + expect(find_tribute_autocomplete_menu).to be_visible end it 'doesn\'t wrap for assignee values' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys("@#{user.username[0]}") - end + fill_in 'Comment', with: "@#{user.username[0..2]}" - user_item = find('.tribute-container ul', text: user.username, visible: true) + find_highlighted_tribute_autocomplete_menu.click - expect_to_wrap(false, user_item, note, user.username) + expect(find_field('Comment').value).to have_text("@#{user.username}") end it 'does not wrap for emoji values' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys(":cartwheel_") - end + fill_in 'Comment', with: ':cartwheel_' - emoji_item = first('.tribute-container li', text: 'cartwheel_tone1', visible: true) + find_highlighted_tribute_autocomplete_menu.click - expect_to_wrap(false, emoji_item, note, 'cartwheel_tone1') + expect(find_field('Comment').value).to have_text('cartwheel_tone1') end it 'does not open autocomplete if there is no space before' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys("hello:#{user.username[0..2]}") - end + fill_in 'Comment', with: "hello:#{user.username[0..2]}" - expect(page).not_to have_selector('.tribute-container') + expect(page).not_to have_css('.tribute-container') end it 'autocompletes for quick actions' do - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/as') - wait_for_requests - note.native.send_keys(:tab) - end + fill_in 'Comment', with: '/as' + + find_highlighted_tribute_autocomplete_menu.click - expect(note.value).to have_text('/assign') + expect(find_field('Comment').value).to have_text('/assign') end end @@ -767,37 +620,33 @@ RSpec.describe 'GFM autocomplete', :js do it 'lists users who are currently not assigned to the issue when using /assign' do visit project_issue_path(project, issue_assignee) - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/assign ') - # The `/assign` ajax response might replace the one by `@` below causing a failed test - # so we need to wait for the `/assign` ajax request to finish first - wait_for_requests - note.native.send_keys('@') - wait_for_requests - end + note = find_field('Comment') + note.native.send_keys('/assign ') + # The `/assign` ajax response might replace the one by `@` below causing a failed test + # so we need to wait for the `/assign` ajax request to finish first + wait_for_requests + note.native.send_keys('@') + wait_for_requests - expect(find('.tribute-container ul', visible: true)).not_to have_content(user.username) - expect(find('.tribute-container ul', visible: true)).to have_content(unassigned_user.username) + expect(find_tribute_autocomplete_menu).not_to have_text(user.username) + expect(find_tribute_autocomplete_menu).to have_text(unassigned_user.username) end it 'lists users who are currently not assigned to the issue when using /assign on the second line' do visit project_issue_path(project, issue_assignee) - note = find('#note-body') - page.within '.timeline-content-form' do - note.native.send_keys('/assign @user2') - note.native.send_keys(:enter) - note.native.send_keys('/assign ') - # The `/assign` ajax response might replace the one by `@` below causing a failed test - # so we need to wait for the `/assign` ajax request to finish first - wait_for_requests - note.native.send_keys('@') - wait_for_requests - end + note = find_field('Comment') + note.native.send_keys('/assign @user2') + note.native.send_keys(:enter) + note.native.send_keys('/assign ') + # The `/assign` ajax response might replace the one by `@` below causing a failed test + # so we need to wait for the `/assign` ajax request to finish first + wait_for_requests + note.native.send_keys('@') + wait_for_requests - expect(find('.tribute-container ul', visible: true)).not_to have_content(user.username) - expect(find('.tribute-container ul', visible: true)).to have_content(unassigned_user.username) + expect(find_tribute_autocomplete_menu).not_to have_text(user.username) + expect(find_tribute_autocomplete_menu).to have_text(unassigned_user.username) end end @@ -806,72 +655,65 @@ RSpec.describe 'GFM autocomplete', :js do label_xss_title = 'alert label <img src=x onerror="alert(\'Hello xss\');" a' create(:label, project: project, title: label_xss_title) - note = find('#note-body') - - # It should show all the labels on "~". - type(note, '~') + fill_in 'Comment', with: '~' wait_for_requests - expect(find('.tribute-container ul', visible: true).text).to have_content('alert label') + expect(find_tribute_autocomplete_menu).to have_text('alert label') end it 'allows colons when autocompleting scoped labels' do create(:label, project: project, title: 'scoped:label') - note = find('#note-body') - type(note, '~scoped:') + fill_in 'Comment', with: '~scoped:' wait_for_requests - expect(find('.tribute-container ul', visible: true).text).to have_content('scoped:label') + expect(find_tribute_autocomplete_menu).to have_text('scoped:label') end it 'allows colons when autocompleting scoped labels with double colons' do create(:label, project: project, title: 'scoped::label') - note = find('#note-body') - type(note, '~scoped::') + fill_in 'Comment', with: '~scoped::' wait_for_requests - expect(find('.tribute-container ul', visible: true).text).to have_content('scoped::label') + expect(find_tribute_autocomplete_menu).to have_text('scoped::label') end it 'autocompletes multi-word labels' do create(:label, project: project, title: 'Accepting merge requests') - note = find('#note-body') - type(note, '~Acceptingmerge') + fill_in 'Comment', with: '~Acceptingmerge' wait_for_requests - expect(find('.tribute-container ul', visible: true).text).to have_content('Accepting merge requests') + expect(find_tribute_autocomplete_menu).to have_text('Accepting merge requests') end it 'only autocompletes the latest label' do create(:label, project: project, title: 'documentation') create(:label, project: project, title: 'feature') - note = find('#note-body') - type(note, '~documentation foo bar ~feat') - note.native.send_keys(:right) + fill_in 'Comment', with: '~documentation foo bar ~feat' + # Invoke autocompletion + find_field('Comment').native.send_keys(:right) wait_for_requests - expect(find('.tribute-container ul', visible: true).text).to have_content('feature') - expect(find('.tribute-container ul', visible: true).text).not_to have_content('documentation') + expect(find_tribute_autocomplete_menu).to have_text('feature') + expect(find_tribute_autocomplete_menu).not_to have_text('documentation') end it 'does not autocomplete labels if no tilde is typed' do create(:label, project: project, title: 'documentation') - note = find('#note-body') - type(note, 'document') + fill_in 'Comment', with: 'document' wait_for_requests - expect(page).not_to have_selector('.tribute-container') + expect(page).not_to have_css('.tribute-container') end end @@ -881,7 +723,7 @@ RSpec.describe 'GFM autocomplete', :js do # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729 it 'keeps autocomplete key listeners' do visit project_issue_path(project, issue) - note = find('#note-body') + note = find_field('Comment') start_comment_with_emoji(note, '.tribute-container li') @@ -897,17 +739,11 @@ RSpec.describe 'GFM autocomplete', :js do shared_examples 'autocomplete suggestions' do it 'suggests objects correctly' do - page.within '.timeline-content-form' do - find('#note-body').native.send_keys(object.class.reference_prefix) - end - - page.within '.tribute-container' do - expect(page).to have_content(object.title) + fill_in 'Comment', with: object.class.reference_prefix - find('ul li').click - end + find_tribute_autocomplete_menu.find('li').click - expect(find('.new-note #note-body').value).to include(expected_body) + expect(find_field('Comment').value).to have_text(expected_body) end end @@ -949,42 +785,6 @@ RSpec.describe 'GFM autocomplete', :js do private - def expect_to_wrap(should_wrap, item, note, value) - expect(item).to have_content(value) - expect(item).not_to have_content("\"#{value}\"") - - item.click - - if should_wrap - expect(note.value).to include("\"#{value}\"") - else - expect(note.value).not_to include("\"#{value}\"") - end - end - - def expect_labels(shown: nil, not_shown: nil) - page.within('.atwho-container') do - if shown - expect(page).to have_selector('.atwho-view li', count: shown.size) - shown.each { |label| expect(page).to have_content(label.title) } - end - - if not_shown - expect(page).not_to have_selector('.atwho-view li') unless shown - not_shown.each { |label| expect(page).not_to have_content(label.title) } - end - end - end - - # `note` is a textarea where the given text should be typed. - # We don't want to find it each time this function gets called. - def type(note, text) - page.within('.timeline-content-form') do - note.set('') - note.native.send_keys(text) - end - end - def start_comment_with_emoji(note, selector) note.native.send_keys('Hello :10') @@ -994,9 +794,7 @@ RSpec.describe 'GFM autocomplete', :js do end def start_and_cancel_discussion - click_button('Reply...') - - fill_in('note_note', with: 'Whoops!') + fill_in('Reply to comment', with: 'Whoops!') page.accept_alert 'Are you sure you want to cancel creating this comment?' do click_button('Cancel') @@ -1004,4 +802,20 @@ RSpec.describe 'GFM autocomplete', :js do wait_for_requests end + + def find_autocomplete_menu + find('.atwho-view ul', visible: true) + end + + def find_highlighted_autocomplete_item + find('.atwho-view li.cur', visible: true) + end + + def find_tribute_autocomplete_menu + find('.tribute-container ul', visible: true) + end + + def find_highlighted_tribute_autocomplete_menu + find('.tribute-container li.highlight', visible: true) + end end diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb index 409f498798b..d5a115433aa 100644 --- a/spec/features/issues/issue_state_spec.rb +++ b/spec/features/issues/issue_state_spec.rb @@ -42,15 +42,9 @@ RSpec.describe 'issue state', :js do end describe 'when open', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297348' do - let(:open_issue) { create(:issue, project: project) } - - it_behaves_like 'page with comment and close button', 'Close issue' do - def setup - visit project_issue_path(project, open_issue) - end - end - context 'when clicking the top `Close issue` button', :aggregate_failures do + let(:open_issue) { create(:issue, project: project) } + before do visit project_issue_path(project, open_issue) end @@ -59,8 +53,9 @@ RSpec.describe 'issue state', :js do end context 'when clicking the bottom `Close issue` button', :aggregate_failures do + let(:open_issue) { create(:issue, project: project) } + before do - stub_feature_flags(remove_comment_close_reopen: false) visit project_issue_path(project, open_issue) end @@ -69,15 +64,9 @@ RSpec.describe 'issue state', :js do end describe 'when closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297201' do - let(:closed_issue) { create(:issue, project: project, state: 'closed') } - - it_behaves_like 'page with comment and close button', 'Reopen issue' do - def setup - visit project_issue_path(project, closed_issue) - end - end - context 'when clicking the top `Reopen issue` button', :aggregate_failures do + let(:closed_issue) { create(:issue, project: project, state: 'closed') } + before do visit project_issue_path(project, closed_issue) end @@ -86,8 +75,9 @@ RSpec.describe 'issue state', :js do end context 'when clicking the bottom `Reopen issue` button', :aggregate_failures do + let(:closed_issue) { create(:issue, project: project, state: 'closed') } + before do - stub_feature_flags(remove_comment_close_reopen: false) visit project_issue_path(project, closed_issue) end diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb index 02804d84a21..75ea8c14f7f 100644 --- a/spec/features/issues/service_desk_spec.rb +++ b/spec/features/issues/service_desk_spec.rb @@ -49,8 +49,8 @@ RSpec.describe 'Service Desk Issue Tracker', :js do aggregate_failures do expect(page).to have_css('.empty-state') expect(page).to have_text('Use Service Desk to connect with your users') - expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk')) - expect(page).not_to have_link('Turn on Service Desk') + expect(page).to have_link('Learn more.', href: help_page_path('user/project/service_desk')) + expect(page).not_to have_link('Enable Service Desk') expect(page).to have_content(project.service_desk_address) end end @@ -68,8 +68,8 @@ RSpec.describe 'Service Desk Issue Tracker', :js do aggregate_failures do expect(page).to have_css('.empty-state') expect(page).to have_text('Use Service Desk to connect with your users') - expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk')) - expect(page).not_to have_link('Turn on Service Desk') + expect(page).to have_link('Learn more.', href: help_page_path('user/project/service_desk')) + expect(page).not_to have_link('Enable Service Desk') expect(page).not_to have_content(project.service_desk_address) end end @@ -91,8 +91,8 @@ RSpec.describe 'Service Desk Issue Tracker', :js do it 'displays the small info box, documentation, a button to configure service desk, and the address' do aggregate_failures do expect(page).to have_css('.non-empty-state') - expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk')) - expect(page).not_to have_link('Turn on Service Desk') + expect(page).to have_link('Learn more.', href: help_page_path('user/project/service_desk')) + expect(page).not_to have_link('Enable Service Desk') expect(page).to have_content(project.service_desk_address) end end @@ -156,8 +156,8 @@ RSpec.describe 'Service Desk Issue Tracker', :js do aggregate_failures do expect(page).to have_css('.empty-state') expect(page).to have_text('Service Desk is not supported') - expect(page).to have_text('In order to enable Service Desk for your instance, you must first set up incoming email.') - expect(page).to have_link('More information', href: help_page_path('administration/incoming_email', anchor: 'set-it-up')) + expect(page).to have_text('To enable Service Desk on this instance, an instance administrator must first set up incoming email.') + expect(page).to have_link('Learn more.', href: help_page_path('administration/incoming_email', anchor: 'set-it-up')) end end end diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb index fec603e466a..1c7bc5f239f 100644 --- a/spec/features/issues/user_interacts_with_awards_spec.rb +++ b/spec/features/issues/user_interacts_with_awards_spec.rb @@ -135,11 +135,9 @@ RSpec.describe 'User interacts with awards' do it 'allows adding a new emoji' do page.within('.note-actions') do - find('a.js-add-award').click - end - page.within('.emoji-menu-content') do - find('gl-emoji[data-name="8ball"]').click + find('.note-emoji-button').click end + find('gl-emoji[data-name="8ball"]').click wait_for_requests page.within('.note-awards') do @@ -157,7 +155,7 @@ RSpec.describe 'User interacts with awards' do end page.within('.note-actions') do - expect(page).not_to have_css('a.js-add-award') + expect(page).not_to have_css('.btn.js-add-award') end end diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index aeb42cc2edb..0a2f81986be 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -160,7 +160,7 @@ RSpec.describe 'Labels Hierarchy', :js do find('a.label-item', text: parent_group_label.title).click find('a.label-item', text: project_label_1.title).click - find('.btn-success').click + find('.btn-confirm').click expect(page.find('.issue-details h2.title')).to have_content('new created issue') expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title) diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index 8e28f89f49e..e84b300a748 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -290,7 +290,7 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do path = 'images/example.jpg' gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path) - expect(@wiki).to receive(:find_file).with(path).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) + expect(@wiki).to receive(:find_file).with(path, load_content: false).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) allow(@wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } @html = markdown(@feat.raw_markdown, { pipeline: :wiki, wiki: @wiki, page_slug: @wiki_page.slug }) diff --git a/spec/features/markdown/math_spec.rb b/spec/features/markdown/math_spec.rb index e5fb9131ce0..441cff7045f 100644 --- a/spec/features/markdown/math_spec.rb +++ b/spec/features/markdown/math_spec.rb @@ -39,4 +39,20 @@ RSpec.describe 'Math rendering', :js do expect(page).to have_selector('.katex-html a', text: 'Gitlab') end end + + it 'renders lazy load button' do + description = <<~MATH + ```math + \Huge \sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} + ``` + MATH + + issue = create(:issue, project: project, description: description) + + visit project_issue_path(project, issue) + + page.within '.description > .md' do + expect(page).to have_selector('.js-lazy-render-math') + end + end end diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index c8fc23bebf9..25f2707146d 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -223,7 +223,7 @@ end def write_reply_to_discussion(button_text: 'Start a review', text: 'Line is wrong', resolve: false, unresolve: false) page.within(first('.diff-files-holder .discussion-reply-holder')) do - click_button('Reply...') + find_field('Reply…', match: :first).click fill_in('note_note', with: text) diff --git a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb index ab3ef7c1ac0..70951982c22 100644 --- a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb +++ b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb @@ -12,15 +12,9 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https:// end describe 'when open' do - let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) } - - it_behaves_like 'page with comment and close button', 'Close merge request' do - def setup - visit merge_request_path(open_merge_request) - end - end - context 'when clicking the top `Close merge request` link', :aggregate_failures do + let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) } + before do visit merge_request_path(open_merge_request) end @@ -40,8 +34,9 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https:// end context 'when clicking the bottom `Close merge request` button', :aggregate_failures do + let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) } + before do - stub_feature_flags(remove_comment_close_reopen: false) visit merge_request_path(open_merge_request) end @@ -61,22 +56,9 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https:// end describe 'when closed' do - let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') } - - it_behaves_like 'page with comment and close button', 'Close merge request' do - def setup - visit merge_request_path(closed_merge_request) - - within '.detail-page-header' do - click_button 'Toggle dropdown' - click_link 'Reopen merge request' - end - - wait_for_requests - end - end - context 'when clicking the top `Reopen merge request` link', :aggregate_failures do + let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') } + before do visit merge_request_path(closed_merge_request) end @@ -96,8 +78,9 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https:// end context 'when clicking the bottom `Reopen merge request` button', :aggregate_failures do + let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') } + before do - stub_feature_flags(remove_comment_close_reopen: false) visit merge_request_path(closed_merge_request) end diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index 794dfd7c8da..163ce10132e 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -192,7 +192,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do it 'adds as discussion' do should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false) expect(page).to have_css('.notes_holder .note.note-discussion', count: 1) - expect(page).to have_button('Reply...') + expect(page).to have_field('Reply…') end end end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index e629bc0dc53..3099a893dc2 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -44,7 +44,10 @@ RSpec.describe 'Merge request > User posts notes', :js do it 'has enable submit button, preview button and saves content to local storage' do page.within('.js-main-target-form') do - expect(page).not_to have_css('.js-comment-button[disabled]') + page.within('[data-testid="comment-button"]') do + expect(page).to have_css('.split-content-button') + expect(page).not_to have_css('.split-content-button[disabled]') + end expect(page).to have_css('.js-md-preview-button', visible: true) end diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index b86586d53e2..caa04059469 100644 --- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb +++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb @@ -149,7 +149,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to comment' do page.within '.diff-content' do - click_button 'Reply...' + find_field('Reply…').click find(".js-unresolve-checkbox").set false find('.js-note-text').set 'testing' @@ -179,7 +179,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to comment & unresolve thread' do page.within '.diff-content' do - click_button 'Reply...' + find_field('Reply…').click find('.js-note-text').set 'testing' @@ -208,7 +208,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to comment & resolve thread' do page.within '.diff-content' do - click_button 'Reply...' + find_field('Reply…').click find('.js-note-text').set 'testing' @@ -442,7 +442,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do it 'allows user to comment & resolve thread' do page.within '.diff-content' do - click_button 'Reply...' + find_field('Reply…').click find('.js-note-text').set 'testing' @@ -461,7 +461,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do page.within '.diff-content' do click_button 'Resolve thread' - click_button 'Reply...' + find_field('Reply…').click find('.js-note-text').set 'testing' diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb index d15d5b3bc73..90cdc28d1bd 100644 --- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb +++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb @@ -37,7 +37,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do end it 'does not render avatars after commenting on discussion tab' do - click_button 'Reply...' + find_field('Reply…').click page.within('.js-discussion-note-form') do find('.note-textarea').native.send_keys('Test comment') @@ -132,7 +132,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do end it 'adds avatar when commenting' do - click_button 'Reply...' + find_field('Reply…', match: :first).click page.within '.js-discussion-note-form' do find('.js-note-text').native.send_keys('Test') @@ -151,7 +151,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do it 'adds multiple comments' do 3.times do - click_button 'Reply...' + find_field('Reply…', match: :first).click page.within '.js-discussion-note-form' do find('.js-note-text').native.send_keys('Test') diff --git a/spec/features/merge_request/user_sees_discussions_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb index 289c861739f..d79763ba5e0 100644 --- a/spec/features/merge_request/user_sees_discussions_spec.rb +++ b/spec/features/merge_request/user_sees_discussions_spec.rb @@ -60,7 +60,7 @@ RSpec.describe 'Merge request > User sees threads', :js do it 'can be replied to' do within(".discussion[data-discussion-id='#{discussion_id}']") do - click_button 'Reply...' + find_field('Reply…').click fill_in 'note[note]', with: 'Test!' click_button 'Comment' diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb index 708ce53b4fe..ad0e9b48903 100644 --- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb @@ -26,6 +26,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', end before do + stub_feature_flags(new_pipelines_table: false) stub_application_setting(auto_devops_enabled: false) stub_ci_pipeline_yaml_file(YAML.dump(config)) project.add_maintainer(user) diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index 0854a8b9fb7..05fa5459e06 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -651,7 +651,6 @@ RSpec.describe 'Merge request > User sees merge widget', :js do within(".js-report-section-container") do expect(page).to have_content('rspec found 1 failed out of 1 total test') expect(page).to have_content('junit found no changed test results out of 1 total test') - expect(page).not_to have_content('New') expect(page).to have_content('Test#sum when a is 1 and b is 3 returns summary') end end @@ -792,7 +791,6 @@ RSpec.describe 'Merge request > User sees merge widget', :js do within(".js-report-section-container") do expect(page).to have_content('rspec found 1 error out of 1 total test') expect(page).to have_content('junit found no changed test results out of 1 total test') - expect(page).not_to have_content('New') expect(page).to have_content('Test#sum when a is 4 and b is 4 returns summary') end end diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb index 1ef6d2a1068..c0dc2ec3baf 100644 --- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb +++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb @@ -9,166 +9,149 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running', sha: project.commit.id) } let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test') } - shared_examples 'mini pipeline renders' do |ci_mini_pipeline_gl_dropdown_enabled| - before do - build.run - build.trace.set('hello') - sign_in(user) - stub_feature_flags(ci_mini_pipeline_gl_dropdown: ci_mini_pipeline_gl_dropdown_enabled) - visit_merge_request - 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 + build.run + build.trace.set('hello') + sign_in(user) + visit_merge_request + end - def visit_merge_request(format: :html, serializer: nil) - visit project_merge_request_path(project, merge_request, format: format, serializer: serializer) - end + def visit_merge_request(format: :html, serializer: nil) + visit project_merge_request_path(project, merge_request, format: format, serializer: serializer) + end - it 'displays a mini pipeline graph' do - expect(page).to have_selector('.mr-widget-pipeline-graph') - end + it 'displays a mini pipeline graph' do + expect(page).to have_selector('.mr-widget-pipeline-graph') + end - context 'as json' do - let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') } - let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') } + context 'as json' do + let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') } + let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') } - before do - job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline) - create(:ci_job_artifact, :archive, file: artifacts_file1, job: job) - create(:ci_build, :manual, pipeline: pipeline, when: 'manual') - end + before do + job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline) + create(:ci_job_artifact, :archive, file: artifacts_file1, job: job) + create(:ci_build, :manual, pipeline: pipeline, when: 'manual') + end - # TODO: https://gitlab.com/gitlab-org/gitlab-foss/issues/48034 - xit 'avoids repeated database queries' do - before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } + # TODO: https://gitlab.com/gitlab-org/gitlab-foss/issues/48034 + xit 'avoids repeated database queries' do + before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } - job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline) - create(:ci_job_artifact, :archive, file: artifacts_file2, job: job) - create(:ci_build, :manual, pipeline: pipeline, when: 'manual') + job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline) + create(:ci_job_artifact, :archive, file: artifacts_file2, job: job) + create(:ci_build, :manual, pipeline: pipeline, when: 'manual') - after = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } + after = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } - expect(before.count).to eq(after.count) - expect(before.cached_count).to eq(after.cached_count) - end + expect(before.count).to eq(after.count) + expect(before.cached_count).to eq(after.cached_count) end + end - describe 'build list toggle' do - let(:toggle) do - find(dropdown_toggle_selector) - first(dropdown_toggle_selector) - end + describe 'build list toggle' do + let(:toggle) do + find(dropdown_selector) + first(dropdown_selector) + end - # Status icon button styles should update as described in - # https://gitlab.com/gitlab-org/gitlab-foss/issues/42769 - it 'has unique styles for default, :hover, :active, and :focus states' do - default_background_color, default_foreground_color, default_box_shadow = get_toggle_colors(dropdown_toggle_selector) + # Status icon button styles should update as described in + # https://gitlab.com/gitlab-org/gitlab-foss/issues/42769 + it 'has unique styles for default, :hover, :active, and :focus states' do + default_background_color, default_foreground_color, default_box_shadow = get_toggle_colors(dropdown_selector) - toggle.hover - hover_background_color, hover_foreground_color, hover_box_shadow = get_toggle_colors(dropdown_toggle_selector) + toggle.hover + hover_background_color, hover_foreground_color, hover_box_shadow = get_toggle_colors(dropdown_selector) - page.driver.browser.action.click_and_hold(toggle.native).perform - active_background_color, active_foreground_color, active_box_shadow = get_toggle_colors(dropdown_toggle_selector) - page.driver.browser.action.release(toggle.native).perform + page.driver.browser.action.click_and_hold(toggle.native).perform + active_background_color, active_foreground_color, active_box_shadow = get_toggle_colors(dropdown_selector) + page.driver.browser.action.release(toggle.native).perform - page.driver.browser.action.click(toggle.native).move_by(100, 100).perform - focus_background_color, focus_foreground_color, focus_box_shadow = get_toggle_colors(dropdown_toggle_selector) + page.driver.browser.action.click(toggle.native).move_by(100, 100).perform + focus_background_color, focus_foreground_color, focus_box_shadow = get_toggle_colors(dropdown_selector) - expect(default_background_color).not_to eq(hover_background_color) - expect(hover_background_color).not_to eq(active_background_color) - expect(default_background_color).not_to eq(active_background_color) + expect(default_background_color).not_to eq(hover_background_color) + expect(hover_background_color).not_to eq(active_background_color) + expect(default_background_color).not_to eq(active_background_color) - expect(default_foreground_color).not_to eq(hover_foreground_color) - expect(hover_foreground_color).not_to eq(active_foreground_color) - expect(default_foreground_color).not_to eq(active_foreground_color) + expect(default_foreground_color).not_to eq(hover_foreground_color) + expect(hover_foreground_color).not_to eq(active_foreground_color) + expect(default_foreground_color).not_to eq(active_foreground_color) - expect(focus_background_color).to eq(hover_background_color) - expect(focus_foreground_color).to eq(hover_foreground_color) + expect(focus_background_color).to eq(hover_background_color) + expect(focus_foreground_color).to eq(hover_foreground_color) - expect(default_box_shadow).to eq('none') - expect(hover_box_shadow).to eq('none') - expect(active_box_shadow).not_to eq('none') - expect(focus_box_shadow).not_to eq('none') - end + expect(default_box_shadow).to eq('none') + expect(hover_box_shadow).to eq('none') + expect(active_box_shadow).not_to eq('none') + expect(focus_box_shadow).not_to eq('none') + end - it 'shows tooltip when hovered' do - toggle.hover + it 'shows tooltip when hovered' do + toggle.hover - expect(page).to have_selector('.tooltip') - end + expect(page).to have_selector('.tooltip') end + end - describe 'builds list menu' do - let(:toggle) do - find(dropdown_toggle_selector) - first(dropdown_toggle_selector) - end + describe 'builds list menu' do + let(:toggle) do + find(dropdown_selector) + first(dropdown_selector) + end - before do - toggle.click - wait_for_requests - end + before do + toggle.click + wait_for_requests + end - it 'pens when toggle is clicked' do - expect(toggle.find(:xpath, '..')).to have_selector('.mini-pipeline-graph-dropdown-menu') - end + it 'pens when toggle is clicked' do + expect(toggle.find(:xpath, '..')).to have_selector('.mini-pipeline-graph-dropdown-menu') + end - it 'closes when toggle is clicked again' do - toggle.click + it 'closes when toggle is clicked again' do + toggle.click - expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu') - end + expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu') + end - it 'closes when clicking somewhere else' do - find('body').click + it 'closes when clicking somewhere else' do + find('body').click - expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu') - end + expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu') + end - describe 'build list build item' do - let(:build_item) do - find('.mini-pipeline-graph-dropdown-item') - first('.mini-pipeline-graph-dropdown-item') - end + describe 'build list build item' do + let(:build_item) do + find('.mini-pipeline-graph-dropdown-item') + first('.mini-pipeline-graph-dropdown-item') + end - it 'visits the build page when clicked' do - build_item.click - find('.build-page') + it 'visits the build page when clicked' do + build_item.click + find('.build-page') - expect(current_path).to eql(project_job_path(project, build)) - end + expect(current_path).to eql(project_job_path(project, build)) + end - it 'shows tooltip when hovered' do - build_item.hover + it 'shows tooltip when hovered' do + build_item.hover - expect(page).to have_selector('.tooltip') - end + expect(page).to have_selector('.tooltip') 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 - private def get_toggle_colors(selector) find(selector) [ - evaluate_script("$('#{selector}:visible').css('background-color');"), - evaluate_script("$('#{selector}:visible svg').css('fill');"), - evaluate_script("$('#{selector}:visible').css('box-shadow');") + evaluate_script("$('#{selector} button:visible').css('background-color');"), + evaluate_script("$('#{selector} button:visible svg').css('fill');"), + evaluate_script("$('#{selector} button:visible').css('box-shadow');") ] end end diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb index 20c45a1d652..ea46ae06329 100644 --- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb +++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Merge request > User sees notes from forked project', :js do expect(page).to have_content('A commit comment') page.within('.discussion-notes') do - find('.btn-text-field').click + find_field('Reply…').click scroll_to(page.find('#note_note', visible: false)) find('#note_note').send_keys('A reply comment') find('.js-comment-button').click diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb index bf445de44ba..9850ca3f173 100644 --- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb +++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb @@ -121,14 +121,14 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js do click_link 'Changes' - expect(page).to have_css('a.btn.active', text: 'Inline') - expect(page).not_to have_css('a.btn.active', text: 'Side-by-side') + expect(page).to have_css('a.btn.selected', text: 'Inline') + expect(page).not_to have_css('a.btn.selected', text: 'Side-by-side') click_link 'Side-by-side' within '.merge-request' do - expect(page).not_to have_css('a.btn.active', text: 'Inline') - expect(page).to have_css('a.btn.active', text: 'Side-by-side') + expect(page).not_to have_css('a.btn.selected', text: 'Inline') + expect(page).to have_css('a.btn.selected', text: 'Side-by-side') end end diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb index bbeb91bbd19..dbc88d0cce2 100644 --- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb @@ -83,7 +83,7 @@ RSpec.describe 'User comments on a diff', :js do wait_for_requests - click_button 'Reply...' + find_field('Reply…', match: :first).click find('.js-suggestion-btn').click diff --git a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb index 05f4c16ef60..b72ac071ecb 100644 --- a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb +++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb @@ -21,13 +21,13 @@ RSpec.describe 'Merge request > User toggles whitespace changes', :js do describe 'clicking "Hide whitespace changes" button' do it 'toggles the "Hide whitespace changes" button' do - find('#show-whitespace').click + find('[data-testid="show-whitespace"]').click visit diffs_project_merge_request_path(project, merge_request) find('.js-show-diff-settings').click - expect(find('#show-whitespace')).not_to be_checked + expect(find('[data-testid="show-whitespace"]')).not_to be_checked end end end diff --git a/spec/features/merge_requests/user_exports_as_csv_spec.rb b/spec/features/merge_requests/user_exports_as_csv_spec.rb index a86ff9d7335..725b8366d04 100644 --- a/spec/features/merge_requests/user_exports_as_csv_spec.rb +++ b/spec/features/merge_requests/user_exports_as_csv_spec.rb @@ -14,11 +14,13 @@ RSpec.describe 'Merge Requests > Exports as CSV', :js do subject { page.find('.nav-controls') } - it { is_expected.to have_button('Export as CSV') } + it { is_expected.to have_selector '[data-testid="export-csv-button"]' } context 'button is clicked' do before do - click_button('Export as CSV') + page.within('.nav-controls') do + find('[data-testid="export-csv-button"]').click + end end it 'shows a success message' do diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index d6f23b21d65..b22778012a8 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -85,6 +85,7 @@ RSpec.describe 'Member autocomplete', :js do let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) } before do + allow(User).to receive(:find_by_any_email).and_call_original allow(User).to receive(:find_by_any_email) .with(noteable.author_email.downcase, confirmed: true).and_return(author) diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index 88bfc71cfbe..9e56ef087ae 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -138,4 +138,10 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do end end end + + it 'pushes `personal_access_tokens_scoped_to_projects` feature flag to the frontend' do + visit profile_personal_access_tokens_path + + expect(page).to have_pushed_frontend_feature_flags(personalAccessTokensScopedToProjects: true) + end end diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb index 289fbff0404..939e791c75d 100644 --- a/spec/features/profiles/user_visits_notifications_tab_spec.rb +++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb @@ -7,7 +7,6 @@ RSpec.describe 'User visits the notifications tab', :js do let(:user) { create(:user) } before do - stub_feature_flags(vue_notification_dropdown: false) project.add_maintainer(user) sign_in(user) visit(profile_notifications_path) @@ -16,17 +15,17 @@ RSpec.describe 'User visits the notifications tab', :js do it 'changes the project notifications setting' do expect(page).to have_content('Notifications') - first('#notifications-button').click - click_link('On mention') + first('[data-testid="notification-dropdown"]').click + click_button('On mention') - expect(page).to have_selector('#notifications-button', text: 'On mention') + expect(page).to have_selector('[data-testid="notification-dropdown"]', text: 'On mention') end context 'when project emails are disabled' do let(:project) { create(:project, emails_disabled: true) } it 'notification button is disabled' do - expect(page).to have_selector('.notifications-btn.disabled', visible: true) + expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled') end end end diff --git a/spec/features/project_group_variables_spec.rb b/spec/features/project_group_variables_spec.rb index d8eba20ac18..fc482261fb1 100644 --- a/spec/features/project_group_variables_spec.rb +++ b/spec/features/project_group_variables_spec.rb @@ -57,7 +57,7 @@ RSpec.describe 'Project group variables', :js do wait_for_requests - page.within('.ci-variable-table') do + page.within('[data-testid="ci-variable-table"]') do expect(find('.js-ci-variable-row:nth-child(1) [data-label="Key"]').text).to eq(key1) end end diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb index a7f94f38d85..327d8133411 100644 --- a/spec/features/project_variables_spec.rb +++ b/spec/features/project_variables_spec.rb @@ -24,7 +24,6 @@ RSpec.describe 'Project variables', :js do find('[data-qa-selector="ci_variable_key_field"] input').set('akey') find('#ci-variable-value').set('akey_value') find('[data-testid="environment-scope"]').click - find_button('clear').click find('[data-testid="ci-environment-search"]').set('review/*') find('[data-testid="create-wildcard-button"]').click @@ -33,7 +32,7 @@ RSpec.describe 'Project variables', :js do wait_for_requests - page.within('.ci-variable-table') do + page.within('[data-testid="ci-variable-table"]') do expect(find('.js-ci-variable-row:first-child [data-label="Environments"]').text).to eq('review/*') end end 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/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index cf9b86f16bb..7d206f76031 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -7,37 +7,55 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do context 'when commit has pipelines' do let(:pipeline) do - create(:ci_empty_pipeline, + create(:ci_pipeline, + status: :running, project: project, ref: project.default_branch, sha: project.commit.sha) end - let(:build) { create(:ci_build, pipeline: pipeline) } + let(:build) { create(:ci_build, pipeline: pipeline, status: :running) } - it 'display icon with status' do - build.run - visit project_commit_path(project, project.commit.id) + shared_examples 'shows ci icon and mini pipeline' do + before do + build.run + visit project_commit_path(project, project.commit.id) + end - expect(page).to have_selector('.ci-status-icon-running') - end + it 'display icon with status' do + expect(page).to have_selector('.ci-status-icon-running') + end - it 'displays a mini pipeline graph' do - build.run - visit project_commit_path(project, project.commit.id) + it 'displays a mini pipeline graph' do + expect(page).to have_selector('.mr-widget-pipeline-graph') - expect(page).to have_selector('.mr-widget-pipeline-graph') + first('.mini-pipeline-graph-dropdown-toggle').click - first('.mini-pipeline-graph-dropdown-toggle').click + wait_for_requests - wait_for_requests + page.within '.js-builds-dropdown-list' do + expect(page).to have_selector('.ci-status-icon-running') + expect(page).to have_content(build.stage) + end - page.within '.js-builds-dropdown-list' do - expect(page).to have_selector('.ci-status-icon-running') - expect(page).to have_content(build.stage) + build.drop + end + end + + context 'when ci_commit_pipeline_mini_graph_vue is disabled' do + before do + stub_feature_flags(ci_commit_pipeline_mini_graph_vue: false) + end + + it_behaves_like 'shows ci icon and mini pipeline' + end + + context 'when ci_commit_pipeline_mini_graph_vue is enabled' do + before do + stub_feature_flags(ci_commit_pipeline_mini_graph_vue: true) end - build.drop + it_behaves_like 'shows ci icon and mini pipeline' end end 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/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb index 32519b14d4e..88812fc188b 100644 --- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb @@ -113,7 +113,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js do click_link 'Add to Mattermost' - expect(page).to have_selector('.alert') + expect(page).to have_selector('.gl-alert') expect(page).to have_content('test mattermost error message') end diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb index 1d9f256a819..fe0ee52e4fa 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) @@ -162,13 +162,13 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do end expect(page).to have_content('Grafana URL') - expect(page).to have_content('API Token') - expect(page).to have_button('Save Changes') + expect(page).to have_content('API token') + expect(page).to have_button('Save changes') fill_in('grafana-url', with: 'http://gitlab-test.grafana.net') fill_in('grafana-token', with: 'token') - click_button('Save Changes') + click_button('Save changes') wait_for_requests 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 diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 618a256d4fb..4730679feb8 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -49,7 +49,7 @@ RSpec.describe 'Project' do it 'shows the command in a popover', :js do click_link 'Show command' - expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip') + expect(page).to have_css('.popover #push-to-create-tip') expect(page).to have_content 'Private projects can be created in your personal namespace with:' end end diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index c146ac1e8ee..755f170a93e 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -24,7 +24,12 @@ RSpec.describe 'Internal Group access' do describe 'GET /groups/:path' do subject { group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -39,7 +44,12 @@ RSpec.describe 'Internal Group access' do describe 'GET /groups/:path/-/issues' do subject { issues_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -56,7 +66,12 @@ RSpec.describe 'Internal Group access' do subject { merge_requests_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -71,7 +86,12 @@ RSpec.describe 'Internal Group access' do describe 'GET /groups/:path/-/group_members' do subject { group_group_members_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -86,7 +106,12 @@ RSpec.describe 'Internal Group access' do describe 'GET /groups/:path/-/edit' do subject { edit_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_denied_for(:maintainer).of(group) } it { is_expected.to be_denied_for(:developer).of(group) } diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb index de05b4d3d16..fc1fb3e3848 100644 --- a/spec/features/security/group/private_access_spec.rb +++ b/spec/features/security/group/private_access_spec.rb @@ -24,7 +24,12 @@ RSpec.describe 'Private Group access' do describe 'GET /groups/:path' do subject { group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -39,7 +44,12 @@ RSpec.describe 'Private Group access' do describe 'GET /groups/:path/-/issues' do subject { issues_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -56,7 +66,12 @@ RSpec.describe 'Private Group access' do subject { merge_requests_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -71,7 +86,12 @@ RSpec.describe 'Private Group access' do describe 'GET /groups/:path/-/group_members' do subject { group_group_members_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -86,7 +106,12 @@ RSpec.describe 'Private Group access' do describe 'GET /groups/:path/-/edit' do subject { edit_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_denied_for(:maintainer).of(group) } it { is_expected.to be_denied_for(:developer).of(group) } @@ -107,7 +132,12 @@ RSpec.describe 'Private Group access' do subject { group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb index ee72b84616a..90de2b58044 100644 --- a/spec/features/security/group/public_access_spec.rb +++ b/spec/features/security/group/public_access_spec.rb @@ -24,7 +24,12 @@ RSpec.describe 'Public Group access' do describe 'GET /groups/:path' do subject { group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -39,7 +44,12 @@ RSpec.describe 'Public Group access' do describe 'GET /groups/:path/-/issues' do subject { issues_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -56,7 +66,12 @@ RSpec.describe 'Public Group access' do subject { merge_requests_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -71,7 +86,12 @@ RSpec.describe 'Public Group access' do describe 'GET /groups/:path/-/group_members' do subject { group_group_members_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_allowed_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_allowed_for(:maintainer).of(group) } it { is_expected.to be_allowed_for(:developer).of(group) } @@ -86,7 +106,12 @@ RSpec.describe 'Public Group access' do describe 'GET /groups/:path/-/edit' do subject { edit_group_path(group) } - it { is_expected.to be_allowed_for(:admin) } + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed_for(:admin) } + end + context 'when admin mode is disabled' do + it { is_expected.to be_denied_for(:admin) } + end it { is_expected.to be_allowed_for(:owner).of(group) } it { is_expected.to be_denied_for(:maintainer).of(group) } it { is_expected.to be_denied_for(:developer).of(group) } diff --git a/spec/features/sentry_js_spec.rb b/spec/features/sentry_js_spec.rb index aa0ad17340a..1d277ba7b3c 100644 --- a/spec/features/sentry_js_spec.rb +++ b/spec/features/sentry_js_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Sentry' do expect(has_requested_sentry).to eq(false) end - xit 'loads sentry if sentry is enabled' do + it 'loads sentry if sentry is enabled' do stub_sentry_settings visit new_user_session_path diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index e17521e1d02..0f8daaf8e15 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -69,13 +69,7 @@ RSpec.describe 'Task Lists', :js do wait_for_requests expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox") - end - - it_behaves_like 'page with comment and close button', 'Close issue' do - def setup - visit_issue(project, issue) - wait_for_requests - end + expect(page).to have_selector('.btn-close') end it 'is only editable by author' do diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb index 31e29810c65..900cd72c17f 100644 --- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb +++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'User uploads avatar to profile' do expect(user.reload.avatar.file).to exist end - it 'their new avatar is immediately visible in the header', :js do + it 'their new avatar is immediately visible in the header and setting sidebar', :js do find('.js-user-avatar-input', visible: false).set(avatar_file_path) click_button 'Set new profile picture' @@ -33,5 +33,6 @@ RSpec.describe 'User uploads avatar to profile' do data_uri = find('.avatar-image .avatar')['src'] expect(page.find('.header-user-avatar')['src']).to eq data_uri + expect(page.find('[data-testid="sidebar-user-avatar"]')['src']).to eq data_uri end end diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb index 9c67523f88f..b8f41925156 100644 --- a/spec/features/user_can_display_performance_bar_spec.rb +++ b/spec/features/user_can_display_performance_bar_spec.rb @@ -49,6 +49,10 @@ RSpec.describe 'User can display performance bar', :js do let(:group) { create(:group) } + before do + allow(GitlabPerformanceBarStatsWorker).to receive(:perform_in) + end + context 'when user is logged-out' do before do visit root_path @@ -97,6 +101,26 @@ RSpec.describe 'User can display performance bar', :js do it_behaves_like 'performance bar is enabled by default in development' it_behaves_like 'performance bar can be displayed' + + it 'does not show Stats link by default' do + find('body').native.send_keys('pb') + + expect(page).not_to have_link('Stats', visible: :all) + end + + context 'when GITLAB_PERFORMANCE_BAR_STATS_URL environment variable is set' do + let(:stats_url) { 'https://log.gprd.gitlab.net/app/dashboards#/view/' } + + before do + stub_env('GITLAB_PERFORMANCE_BAR_STATS_URL', stats_url) + end + + it 'shows Stats link' do + find('body').native.send_keys('pb') + + expect(page).to have_link('Stats', href: stats_url, visible: :all) + end + end end end end |