diff options
Diffstat (limited to 'spec/features')
87 files changed, 1353 insertions, 519 deletions
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb index ae3859280b1..f934736ced9 100644 --- a/spec/features/abuse_report_spec.rb +++ b/spec/features/abuse_report_spec.rb @@ -13,10 +13,19 @@ RSpec.describe 'Abuse reports', :js, feature_category: :insider_threat do before do sign_in(reporter1) stub_feature_flags(moved_mr_sidebar: false) - stub_feature_flags(user_profile_overflow_menu_vue: false) end describe 'report abuse to administrator' do + shared_examples 'cancel report' do + it 'redirects backs to user profile when cancel button is clicked' do + fill_and_submit_abuse_category_form + + click_link 'Cancel' + + expect(page).to have_current_path(user_path(abusive_user)) + end + end + context 'when reporting an issue for abuse' do before do visit project_issue_path(project, issue) @@ -46,54 +55,102 @@ RSpec.describe 'Abuse reports', :js, feature_category: :insider_threat do it_behaves_like 'reports the user with an abuse category' end - context 'when reporting a user profile for abuse' do - let_it_be(:reporter2) { create(:user) } + describe 'when user_profile_overflow_menu FF turned on' do + context 'when reporting a user profile for abuse' do + let_it_be(:reporter2) { create(:user) } - before do - visit user_path(abusive_user) - end + before do + visit user_path(abusive_user) + find_by_testid('base-dropdown-toggle').click + end - it_behaves_like 'reports the user with an abuse category' + it_behaves_like 'reports the user with an abuse category' - it 'allows the reporter to report the same user for different abuse categories' do - visit user_path(abusive_user) + it 'allows the reporter to report the same user for different abuse categories' do + visit user_path(abusive_user) - fill_and_submit_abuse_category_form - fill_and_submit_report_abuse_form + find_by_testid('base-dropdown-toggle').click + fill_and_submit_abuse_category_form + fill_and_submit_report_abuse_form - expect(page).to have_content 'Thank you for your report' + expect(page).to have_content 'Thank you for your report' - visit user_path(abusive_user) + visit user_path(abusive_user) - fill_and_submit_abuse_category_form("They're being offensive or abusive.") - fill_and_submit_report_abuse_form + find_by_testid('base-dropdown-toggle').click + fill_and_submit_abuse_category_form("They're being offensive or abusive.") + fill_and_submit_report_abuse_form - expect(page).to have_content 'Thank you for your report' - end + expect(page).to have_content 'Thank you for your report' + end - it 'allows multiple users to report the same user' do - fill_and_submit_abuse_category_form - fill_and_submit_report_abuse_form + it 'allows multiple users to report the same user' do + fill_and_submit_abuse_category_form + fill_and_submit_report_abuse_form - expect(page).to have_content 'Thank you for your report' + expect(page).to have_content 'Thank you for your report' - gitlab_sign_out - gitlab_sign_in(reporter2) + gitlab_sign_out + gitlab_sign_in(reporter2) - visit user_path(abusive_user) + visit user_path(abusive_user) - fill_and_submit_abuse_category_form - fill_and_submit_report_abuse_form + find_by_testid('base-dropdown-toggle').click + fill_and_submit_abuse_category_form + fill_and_submit_report_abuse_form - expect(page).to have_content 'Thank you for your report' + expect(page).to have_content 'Thank you for your report' + end + + it_behaves_like 'cancel report' end + end - it 'redirects backs to user profile when cancel button is clicked' do - fill_and_submit_abuse_category_form + describe 'when user_profile_overflow_menu FF turned off' do + context 'when reporting a user profile for abuse' do + let_it_be(:reporter2) { create(:user) } - click_link 'Cancel' + before do + stub_feature_flags(user_profile_overflow_menu_vue: false) + visit user_path(abusive_user) + end - expect(page).to have_current_path(user_path(abusive_user)) + it_behaves_like 'reports the user with an abuse category' + + it 'allows the reporter to report the same user for different abuse categories' do + visit user_path(abusive_user) + + fill_and_submit_abuse_category_form + fill_and_submit_report_abuse_form + + expect(page).to have_content 'Thank you for your report' + + visit user_path(abusive_user) + + fill_and_submit_abuse_category_form("They're being offensive or abusive.") + fill_and_submit_report_abuse_form + + expect(page).to have_content 'Thank you for your report' + end + + it 'allows multiple users to report the same user' do + fill_and_submit_abuse_category_form + fill_and_submit_report_abuse_form + + expect(page).to have_content 'Thank you for your report' + + gitlab_sign_out + gitlab_sign_in(reporter2) + + visit user_path(abusive_user) + + fill_and_submit_abuse_category_form + fill_and_submit_report_abuse_form + + expect(page).to have_content 'Thank you for your report' + end + + it_behaves_like 'cancel report' end end @@ -102,7 +159,7 @@ RSpec.describe 'Abuse reports', :js, feature_category: :insider_threat do before do visit project_merge_request_path(project, merge_request) - find('[data-testid="merge-request-actions"]').click + find_by_testid('merge-request-actions').click end it_behaves_like 'reports the user with an abuse category' diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb index 9739ea53f81..18bc851558d 100644 --- a/spec/features/admin/admin_abuse_reports_spec.rb +++ b/spec/features/admin/admin_abuse_reports_spec.rb @@ -2,27 +2,29 @@ require 'spec_helper' -RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do +RSpec.describe "Admin::AbuseReports", :js, feature_category: :insider_threat do let_it_be(:user) { create(:user) } let_it_be(:admin) { create(:admin) } - context 'as an admin' do - describe 'displayed reports' do - include FilteredSearchHelpers + let_it_be(:open_report) { create(:abuse_report, created_at: 5.days.ago, updated_at: 2.days.ago, category: 'spam', user: user) } + let_it_be(:open_report2) { create(:abuse_report, created_at: 4.days.ago, updated_at: 3.days.ago, category: 'phishing') } + let_it_be(:closed_report) { create(:abuse_report, :closed, user: user, category: 'spam') } - let_it_be(:open_report) { create(:abuse_report, created_at: 5.days.ago, updated_at: 2.days.ago) } - let_it_be(:open_report2) { create(:abuse_report, created_at: 4.days.ago, updated_at: 3.days.ago, category: 'phishing') } - let_it_be(:closed_report) { create(:abuse_report, :closed) } + describe 'as an admin' do + before do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + end - let(:abuse_report_row_selector) { '[data-testid="abuse-report-row"]' } + context 'when abuse_reports_list feature flag is enabled' do + include FilteredSearchHelpers before do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_abuse_reports_path end + let(:abuse_report_row_selector) { '[data-testid="abuse-report-row"]' } + it 'only includes open reports by default' do expect_displayed_reports_count(2) @@ -68,7 +70,8 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do end it 'can be sorted by created_at and updated_at in desc and asc order', :aggregate_failures do - # created_at desc (default) + sort_by 'Created date' + # created_at desc expect(report_rows[0].text).to include(report_text(open_report2)) expect(report_rows[1].text).to include(report_text(open_report)) @@ -78,25 +81,90 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do expect(report_rows[0].text).to include(report_text(open_report)) expect(report_rows[1].text).to include(report_text(open_report2)) - # updated_at ascending + # updated_at asc sort_by 'Updated date' expect(report_rows[0].text).to include(report_text(open_report2)) expect(report_rows[1].text).to include(report_text(open_report)) - # updated_at descending + # updated_at desc toggle_sort_direction expect(report_rows[0].text).to include(report_text(open_report)) expect(report_rows[1].text).to include(report_text(open_report2)) end + context 'when multiple reports for the same user are created' do + let_it_be(:open_report3) { create(:abuse_report, category: 'spam', user: user) } + let_it_be(:closed_report2) { create(:abuse_report, :closed, user: user, category: 'spam') } + + it 'aggregates open reports by user & category', :aggregate_failures do + expect_displayed_reports_count(2) + + expect_aggregated_report_shown(open_report, 2) + expect_report_shown(open_report2) + end + + it 'can sort aggregated reports by number_of_reports in desc order only', :aggregate_failures do + sort_by 'Number of Reports' + + expect(report_rows[0].text).to include(aggregated_report_text(open_report, 2)) + expect(report_rows[1].text).to include(report_text(open_report2)) + + toggle_sort_direction + + expect(report_rows[0].text).to include(aggregated_report_text(open_report, 2)) + expect(report_rows[1].text).to include(report_text(open_report2)) + end + + it 'can sort aggregated reports by created_at and updated_at in desc and asc order', :aggregate_failures do + # number_of_reports desc (default) + expect(report_rows[0].text).to include(aggregated_report_text(open_report, 2)) + expect(report_rows[1].text).to include(report_text(open_report2)) + + # created_at desc + sort_by 'Created date' + + expect(report_rows[0].text).to include(report_text(open_report2)) + expect(report_rows[1].text).to include(aggregated_report_text(open_report, 2)) + + # created_at asc + toggle_sort_direction + + expect(report_rows[0].text).to include(aggregated_report_text(open_report, 2)) + expect(report_rows[1].text).to include(report_text(open_report2)) + + sort_by 'Updated date' + + # updated_at asc + expect(report_rows[0].text).to include(report_text(open_report2)) + expect(report_rows[1].text).to include(aggregated_report_text(open_report, 2)) + + # updated_at desc + toggle_sort_direction + + expect(report_rows[0].text).to include(aggregated_report_text(open_report, 2)) + expect(report_rows[1].text).to include(report_text(open_report2)) + end + + it 'does not aggregate closed reports', :aggregate_failures do + filter %w[Status Closed] + + expect_displayed_reports_count(2) + expect_report_shown(closed_report, closed_report2) + end + end + def report_rows page.all(abuse_report_row_selector) end def report_text(report) - "#{report.user.name} reported for #{report.category}" + "#{report.user.name} reported for #{report.category} by #{report.reporter.name}" + end + + def aggregated_report_text(report, count) + "#{report.user.name} reported for #{report.category} by #{count} users" end def expect_report_shown(*reports) @@ -111,6 +179,12 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do end end + def expect_aggregated_report_shown(*reports, count) + reports.each do |r| + expect(page).to have_content(aggregated_report_text(r, count)) + end + end + def expect_displayed_reports_count(count) expect(page).to have_css(abuse_report_row_selector, count: count) end @@ -138,71 +212,30 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do before do stub_feature_flags(abuse_reports_list: false) - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + visit admin_abuse_reports_path end - describe 'if a user has been reported for abuse' do - let_it_be(:abuse_report) { create(:abuse_report, user: user) } - - describe 'in the abuse report view' do - before do - visit admin_abuse_reports_path - end - - it 'presents information about abuse report' do - expect(page).to have_content('Abuse Reports') - - expect(page).to have_content(user.name) - expect(page).to have_content(abuse_report.reporter.name) - expect(page).to have_content(abuse_report.message) - expect(page).to have_link(user.name, href: user_path(user)) - end - - it 'present actions items' do - expect(page).to have_link('Remove user & report') - expect(page).to have_link('Block user') - expect(page).to have_link('Remove user') - end - end + it 'displays all abuse reports', :aggregate_failures do + expect_report_shown(open_report) + expect_report_actions_shown(open_report) - describe 'in the profile page of the user' do - it 'shows a link to view user in the admin area' do - visit user_path(user) + expect_report_shown(open_report2) + expect_report_actions_shown(open_report2) - expect(page).to have_link 'View user in admin area', href: admin_user_path(user) - end - end + expect_report_shown(closed_report) + expect_report_actions_shown(closed_report) end - describe 'if an admin has been reported for abuse' do + context 'when an admin has been reported for abuse' do let_it_be(:admin_abuse_report) { create(:abuse_report, user: admin) } - describe 'in the abuse report view' do - before do - visit admin_abuse_reports_path - end - - it 'presents information about abuse report' do - page.within(:table_row, { "User" => admin.name }) do - expect(page).to have_content(admin.name) - expect(page).to have_content(admin_abuse_report.reporter.name) - expect(page).to have_content(admin_abuse_report.message) - expect(page).to have_link(admin.name, href: user_path(admin)) - end - end - - it 'does not present actions items' do - page.within(:table_row, { "User" => admin.name }) do - expect(page).not_to have_link('Remove user & report') - expect(page).not_to have_link('Block user') - expect(page).not_to have_link('Remove user') - end - end + it 'displays the abuse report without actions' do + expect_report_shown(admin_abuse_report) + expect_report_actions_not_shown(admin_abuse_report) end end - describe 'if a many users have been reported for abuse' do + context 'when multiple users have been reported for abuse' do let(:report_count) { AbuseReport.default_per_page + 3 } before do @@ -211,8 +244,8 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do end end - describe 'in the abuse report view' do - it 'presents information about abuse report' do + context 'in the abuse report view', :aggregate_failures do + it 'adds pagination' do visit admin_abuse_reports_path expect(page).to have_selector('.pagination') @@ -221,12 +254,8 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do end end - describe 'filtering by user' do - let!(:user2) { create(:user) } - let!(:abuse_report) { create(:abuse_report, user: user) } - let!(:abuse_report_2) { create(:abuse_report, user: user2) } - - it 'shows only single user report' do + context 'when filtering reports' do + it 'can be filtered by reported-user', :aggregate_failures do visit admin_abuse_reports_path page.within '.filter-form' do @@ -234,14 +263,39 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :shared do wait_for_requests page.within '.dropdown-menu-user' do - click_link user2.name + click_link user.name end wait_for_requests end - expect(page).to have_content(user2.name) - expect(page).not_to have_content(user.name) + expect_report_shown(open_report) + expect_report_shown(closed_report) + end + end + + def expect_report_shown(report) + page.within(:table_row, { "User" => report.user.name, "Reported by" => report.reporter.name }) do + expect(page).to have_content(report.user.name) + expect(page).to have_content(report.reporter.name) + expect(page).to have_content(report.message) + expect(page).to have_link(report.user.name, href: user_path(report.user)) + end + end + + def expect_report_actions_shown(report) + page.within(:table_row, { "User" => report.user.name, "Reported by" => report.reporter.name }) do + expect(page).to have_link('Remove user & report') + expect(page).to have_link('Block user') + expect(page).to have_link('Remove user') + end + end + + def expect_report_actions_not_shown(report) + page.within(:table_row, { "User" => report.user.name, "Reported by" => report.reporter.name }) do + expect(page).not_to have_link('Remove user & report') + expect(page).not_to have_link('Block user') + expect(page).not_to have_link('Remove user') end end end diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb index 68d63ac321e..47dc8577037 100644 --- a/spec/features/admin/admin_labels_spec.rb +++ b/spec/features/admin/admin_labels_spec.rb @@ -50,6 +50,10 @@ RSpec.describe 'admin issues labels', feature_category: :team_planning do expect(page).to have_content("Define your default set of project labels") expect(page).not_to have_content('bug') expect(page).not_to have_content('feature_label') + + page.within '.js-admin-labels-count' do + expect(page).to have_content('0') + end end end @@ -113,7 +117,7 @@ RSpec.describe 'admin issues labels', feature_category: :team_planning do click_link 'Delete label' end - expect(page).to have_content('Label was removed') + expect(page).to have_content("#{bug_label.title} was removed").and have_no_content("#{bug_label.title}</span>") end end end diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 7fb2202ca1d..af6ba318ac6 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -117,8 +117,8 @@ RSpec.describe "Admin Runners", feature_category: :runner_fleet do describe 'search' do before_all do - create(:ci_runner, :instance, description: 'runner-foo') - create(:ci_runner, :instance, description: 'runner-bar') + create(:ci_runner, :instance, description: 'runner foo') + create(:ci_runner, :instance, description: 'runner bar') end before do @@ -133,23 +133,23 @@ RSpec.describe "Admin Runners", feature_category: :runner_fleet do end it 'shows runners' do - expect(page).to have_content("runner-foo") - expect(page).to have_content("runner-bar") + expect(page).to have_content("runner foo") + expect(page).to have_content("runner bar") end it 'shows correct runner when description matches' do - input_filtered_search_keys('runner-foo') + input_filtered_search_keys('runner foo') expect(page).to have_link('All 1') expect(page).to have_link('Instance 1') - expect(page).to have_content("runner-foo") - expect(page).not_to have_content("runner-bar") + expect(page).to have_content("runner foo") + expect(page).not_to have_content("runner bar") end context 'when description does not match' do before do - input_filtered_search_keys('runner-baz') + input_filtered_search_keys('runner baz') end it_behaves_like 'shows no runners found' diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 0350c8ab066..543dc2cc2a6 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -19,6 +19,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js, feature_category: :s name = 'Hello World' visit admin_user_impersonation_tokens_path(user_id: user.username) + click_button 'Add new token' fill_in "Token name", with: name # Set date to 1st of next month diff --git a/spec/features/admin/broadcast_messages_spec.rb b/spec/features/admin/broadcast_messages_spec.rb index fca4cdb0ff4..b89ebc34d6a 100644 --- a/spec/features/admin/broadcast_messages_spec.rb +++ b/spec/features/admin/broadcast_messages_spec.rb @@ -12,6 +12,12 @@ RSpec.describe 'Admin Broadcast Messages', :js, feature_category: :onboarding do # create visit admin_broadcast_messages_path + click_button('Add new message') + + page.within(preview_container) do + expect(page).to have_content('Your message here') + end + fill_in 'Message', with: 'test message' wait_for_requests @@ -24,10 +30,6 @@ RSpec.describe 'Admin Broadcast Messages', :js, feature_category: :onboarding do wait_for_requests - page.within(preview_container) do - expect(page).to have_content('Your message here') - end - page.within(first_message_container) do expect(page).to have_content('test message') end @@ -53,10 +55,6 @@ RSpec.describe 'Admin Broadcast Messages', :js, feature_category: :onboarding do wait_for_requests - page.within(preview_container) do - expect(page).to have_content('Your message here') - end - page.within(first_message_container) do expect(page).to have_content('changed test message') end diff --git a/spec/features/admin_variables_spec.rb b/spec/features/admin_variables_spec.rb index 744d18a3b6d..91e7a46849c 100644 --- a/spec/features/admin_variables_spec.rb +++ b/spec/features/admin_variables_spec.rb @@ -13,6 +13,7 @@ RSpec.describe 'Instance variables', :js, feature_category: :secrets_management sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) + stub_feature_flags(ci_variable_drawer: false) visit page_path wait_for_requests end @@ -29,4 +30,14 @@ RSpec.describe 'Instance variables', :js, feature_category: :secrets_management it_behaves_like 'variable list', is_admin: true end + + context 'when ci_variable_drawer FF is enabled' do + before do + stub_feature_flags(ci_variable_drawer: true) + visit page_path + wait_for_requests + end + + it_behaves_like 'variable list drawer', is_admin: true + end end diff --git a/spec/features/broadcast_messages_spec.rb b/spec/features/broadcast_messages_spec.rb index 2e0f4e3b83b..98f87face15 100644 --- a/spec/features/broadcast_messages_spec.rb +++ b/spec/features/broadcast_messages_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Broadcast Messages', feature_category: :onboarding do + include Spec::Support::Helpers::ModalHelpers + let_it_be(:user) { create(:user) } let(:path) { explore_projects_path } @@ -127,6 +129,8 @@ RSpec.describe 'Broadcast Messages', feature_category: :onboarding do find("[data-testid='delete-message-#{message.id}']").click end + accept_gl_confirm(button_text: 'Delete message') + visit path expect_no_broadcast_message diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 04b7f1ca821..747d09f5d08 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -117,9 +117,7 @@ RSpec.describe 'Dashboard Projects', feature_category: :groups_and_projects do it 'shows the empty state when there are no starred projects' do visit(starred_dashboard_projects_path) - element = page.find('.row.empty-state') - - expect(element).to have_content("You don't have starred projects yet.") + expect(page).to have_text(s_("StarredProjectsEmptyState|You don't have starred projects yet.")) end it 'shows only starred projects' do diff --git a/spec/features/dashboard/todos/todos_filtering_spec.rb b/spec/features/dashboard/todos/todos_filtering_spec.rb index ea8c7e800c5..990b2f18120 100644 --- a/spec/features/dashboard/todos/todos_filtering_spec.rb +++ b/spec/features/dashboard/todos/todos_filtering_spec.rb @@ -130,7 +130,7 @@ RSpec.describe 'Dashboard > User filters todos', :js, feature_category: :team_pl before do create(:todo, :build_failed, user: user_1, author: user_2, project: project_1, target: merge_request) create(:todo, :marked, user: user_1, author: user_2, project: project_1, target: issue1) - create(:todo, :review_requested, user: user_1, author: user_2, project: project_1, target: issue1) + create(:todo, :review_requested, user: user_1, author: user_2, project: project_1, target: merge_request) end it 'filters by Assigned' do diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb index 3e87c90e7dc..b4a0678cb5f 100644 --- a/spec/features/group_variables_spec.rb +++ b/spec/features/group_variables_spec.rb @@ -11,6 +11,8 @@ RSpec.describe 'Group variables', :js, feature_category: :secrets_management do before do group.add_owner(user) gitlab_sign_in(user) + + stub_feature_flags(ci_variable_drawer: false) visit page_path wait_for_requests end @@ -27,4 +29,14 @@ RSpec.describe 'Group variables', :js, feature_category: :secrets_management do it_behaves_like 'variable list' end + + context 'when ci_variable_drawer FF is enabled' do + before do + stub_feature_flags(ci_variable_drawer: true) + visit page_path + wait_for_requests + end + + it_behaves_like 'variable list drawer' + end end diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb index ab8d8238bdc..d68b4ccf8f8 100644 --- a/spec/features/groups/container_registry_spec.rb +++ b/spec/features/groups/container_registry_spec.rb @@ -49,6 +49,7 @@ RSpec.describe 'Container Registry', :js, feature_category: :container_registry it 'list page has a list of images' do visit_container_registry + expect(page).to have_content '1 Image repository' expect(page).to have_content 'my/image' end diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb index 2cbe44e11bf..6e056d35435 100644 --- a/spec/features/groups/labels/edit_spec.rb +++ b/spec/features/groups/labels/edit_spec.rb @@ -32,6 +32,6 @@ RSpec.describe 'Edit group label', feature_category: :team_planning do click_link 'Delete label' end - expect(page).to have_content("#{label.title} deleted permanently") + expect(page).to have_content("#{label.title} was removed").and have_no_content("#{label.title}</span>") end end diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb index 4a4cb297fcf..1f16a288882 100644 --- a/spec/features/groups/settings/group_badges_spec.rb +++ b/spec/features/groups/settings/group_badges_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do page.within '.badge-settings' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 expect(rows[0]).to have_content badge_1.link_url expect(rows[1]).to have_content badge_2.link_url @@ -33,6 +33,7 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do context 'adding a badge', :js do it 'user can preview a badge' do + click_button 'Add badge' page.within '.badge-settings form' do fill_in 'badge-link-url', with: badge_link_url fill_in 'badge-image-url', with: badge_image_url @@ -44,6 +45,7 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do end it do + click_button 'Add badge' page.within '.badge-settings' do fill_in 'badge-link-url', with: badge_link_url fill_in 'badge-image-url', with: badge_image_url @@ -51,7 +53,7 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do click_button 'Add badge' wait_for_requests - within '.card-body' do + within '.gl-card-body' do expect(find('a')[:href]).to eq badge_link_url expect(find('a img')[:src]).to eq badge_image_url end @@ -63,32 +65,35 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do it 'form is shown when clicking edit button in list' do page.within '.badge-settings' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 rows[1].find('[aria-label="Edit"]').click + end - within 'form' do - expect(find('#badge-link-url').value).to eq badge_2.link_url - expect(find('#badge-image-url').value).to eq badge_2.image_url - end + page.within '.gl-modal' do + expect(find('#badge-link-url').value).to eq badge_2.link_url + expect(find('#badge-image-url').value).to eq badge_2.image_url end end it 'updates a badge when submitting the edit form' do page.within '.badge-settings' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 rows[1].find('[aria-label="Edit"]').click - within 'form' do - fill_in 'badge-link-url', with: badge_link_url - fill_in 'badge-image-url', with: badge_image_url + end - click_button 'Save changes' - wait_for_requests - end + page.within '.gl-modal' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url - rows = all('.card-body > div') + click_button 'Save changes' + wait_for_requests + end + + page.within '.badge-settings' do + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 expect(rows[1]).to have_content badge_link_url end @@ -102,7 +107,7 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do it 'shows a modal when deleting a badge' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 click_delete_button(rows[1]) @@ -112,14 +117,14 @@ RSpec.describe 'Group Badges', feature_category: :groups_and_projects do it 'deletes a badge when confirming the modal' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 click_delete_button(rows[1]) find('.modal .btn-danger').click wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 1 expect(rows[0]).to have_content badge_1.link_url end diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb index 2be0c95addd..d6feb008d47 100644 --- a/spec/features/incidents/incident_details_spec.rb +++ b/spec/features/incidents/incident_details_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'Incident details', :js, feature_category: :incident_management d private def dropdown_options - widget.all('[data-testid="status-dropdown-item"]', count: 3) + widget.all('[data-testid="escalation-status-dropdown"] .gl-new-dropdown-item', count: 3) end def select_resolved(options) diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb index bbf579b09a8..65bd88582db 100644 --- a/spec/features/incidents/user_views_incident_spec.rb +++ b/spec/features/incidents/user_views_incident_spec.rb @@ -75,35 +75,4 @@ RSpec.describe "User views incident", feature_category: :incident_management do expect(page).not_to have_button('Incident actions') end end - - describe 'user status' do - context 'when showing status of the author of the incident' do - subject { visit(incident_project_issues_path(project, incident)) } - - it_behaves_like 'showing user status' do - let(:user_with_status) { user } - end - end - - context 'when status message has an emoji', :js do - let_it_be(:message) { 'My status with an emoji' } - let_it_be(:message_emoji) { 'basketball' } - let_it_be(:status) { create(:user_status, user: user, emoji: 'smirk', message: "#{message} :#{message_emoji}:") } - - it 'correctly renders the emoji' do - wait_for_requests - - tooltip_span = page.first(".user-status-emoji[title^='#{message}']") - tooltip_span.hover - - wait_for_requests - - tooltip = page.find('.tooltip .tooltip-inner') - - page.within(tooltip) do - expect(page).to have_emoji(message_emoji) - end - end - end - end end diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb index 57270e8f7c7..0a06a052bc2 100644 --- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb @@ -92,7 +92,7 @@ RSpec.describe 'Dropdown assignee', :js, feature_category: :team_planning do sign_in(subgroup_user) end - it 'shows inherited, direct, and invited group members but not descendent members', :aggregate_failures do + it 'shows inherited, direct, and invited group members including descendent members', :aggregate_failures do visit issues_group_path(subgroup) select_tokens 'Assignee', '=' @@ -100,8 +100,8 @@ RSpec.describe 'Dropdown assignee', :js, feature_category: :team_planning do expect(page).to have_text group_user.name expect(page).to have_text subgroup_user.name expect(page).to have_text invited_to_group_group_user.name - expect(page).not_to have_text subsubgroup_user.name - expect(page).not_to have_text invited_to_project_group_user.name + expect(page).to have_text subsubgroup_user.name + expect(page).to have_text invited_to_project_group_user.name visit project_issues_path(subgroup_project) @@ -113,5 +113,33 @@ RSpec.describe 'Dropdown assignee', :js, feature_category: :team_planning do expect(page).to have_text invited_to_group_group_user.name expect(page).not_to have_text subsubgroup_user.name end + + context 'when new_graphql_users_autocomplete is disabled' do + before do + stub_feature_flags(new_graphql_users_autocomplete: false) + end + + it 'shows inherited, direct, and invited group members but not descendent members', :aggregate_failures do + visit issues_group_path(subgroup) + + select_tokens 'Assignee', '=' + + expect(page).to have_text group_user.name + expect(page).to have_text subgroup_user.name + expect(page).to have_text invited_to_group_group_user.name + expect(page).not_to have_text subsubgroup_user.name + expect(page).not_to have_text invited_to_project_group_user.name + + visit project_issues_path(subgroup_project) + + select_tokens 'Assignee', '=' + + expect(page).to have_text group_user.name + expect(page).to have_text subgroup_user.name + expect(page).to have_text invited_to_project_group_user.name + expect(page).to have_text invited_to_group_group_user.name + expect(page).not_to have_text subsubgroup_user.name + end + end end end diff --git a/spec/features/issues/incident_issue_spec.rb b/spec/features/issues/incident_issue_spec.rb index 145b51d207a..5197f5d1e33 100644 --- a/spec/features/issues/incident_issue_spec.rb +++ b/spec/features/issues/incident_issue_spec.rb @@ -70,10 +70,6 @@ RSpec.describe 'Incident Detail', :js, feature_category: :team_planning do # Linked Issues/MRs and comment box are hidden on page expect(hidden_items.count).to eq(0) - - # does not show the edit title and description button - edit_button = find_all('[aria-label="Edit title and description"]', wait: false) - expect(edit_button.count).to eq(0) end end end diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index 23f9347d726..a390dca6822 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -10,17 +10,31 @@ RSpec.describe 'Issue notes polling', :js, feature_category: :team_planning do let(:issue) { create(:issue, project: project) } describe 'creates' do - before do + it 'displays the new comment' do visit project_issue_path(project, issue) close_rich_text_promo_popover_if_present - end - it 'displays the new comment' do note = create(:note, noteable: issue, project: project, note: 'Looks good!') wait_for_requests expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') end + + context 'when action_cable_notes is disabled' do + before do + stub_feature_flags(action_cable_notes: false) + end + + it 'displays the new comment' do + visit project_issue_path(project, issue) + close_rich_text_promo_popover_if_present + + note = create(:note, noteable: issue, project: project, note: 'Looks good!') + wait_for_requests + + expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') + end + end end describe 'updates' do diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb index 923967c52c0..1b99c8b39d3 100644 --- a/spec/features/issues/service_desk_spec.rb +++ b/spec/features/issues/service_desk_spec.rb @@ -184,6 +184,42 @@ RSpec.describe 'Service Desk Issue Tracker', :js, feature_category: :service_des stub_feature_flags(frontend_caching: true) end + context 'when there are no issues' do + describe 'service desk empty state' do + it 'displays the large empty state, documentation, and the email address' do + visit service_desk_project_issues_path(project) + + 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('Learn more about Service Desk', href: help_page_path('user/project/service_desk/index')) + expect(page).not_to have_link('Enable Service Desk') + expect(page).to have_content(project.service_desk_address) + end + end + + context 'when user does not have permission to edit project settings' do + before do + user_2 = create(:user) + + project.add_guest(user_2) + sign_in(user_2) + visit service_desk_project_issues_path(project) + end + + it 'displays the large info box and the documentation link' 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('Learn more about Service Desk', href: help_page_path('user/project/service_desk/index')) + expect(page).not_to have_link('Enable Service Desk') + expect(page).not_to have_content(project.service_desk_address) + end + end + end + end + end + context 'when there are issues' do let_it_be(:project) { create(:project, :private, service_desk_enabled: true) } let_it_be(:other_user) { create(:user) } @@ -197,7 +233,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js, feature_category: :service_des it 'displays the small info box, documentation, a button to configure service desk, and the address' do aggregate_failures do - expect(page).to have_link('Learn more', href: help_page_path('user/project/service_desk')) + expect(page).to have_link('Learn more about Service Desk', href: help_page_path('user/project/service_desk/index')) expect(page).not_to have_link('Enable Service Desk') expect(page).to have_content(project.service_desk_address) end diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb index ecb899a7ca2..0c50b7b2475 100644 --- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb +++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb @@ -112,6 +112,18 @@ RSpec.describe 'User creates branch and merge request on issue page', :js, featu expect(page).to have_selector('.ref-selector', text: branch_name) expect(page).to have_current_path project_tree_path(project, branch_name), ignore_query: true end + + context 'when source branch is non-default' do + let(:source_branch) { 'feature' } + + it 'creates a branch' do + select_dropdown_option('create-branch', branch_name, source_branch) + wait_for_requests + + expect(page).to have_selector('.ref-selector', text: branch_name) + expect(page).to have_current_path project_tree_path(project, branch_name), ignore_query: true + end + end end context 'when branch name is invalid' do @@ -231,12 +243,13 @@ RSpec.describe 'User creates branch and merge request on issue page', :js, featu private - def select_dropdown_option(option, branch_name = nil) + def select_dropdown_option(option, branch_name = nil, source_branch = nil) find('.create-mr-dropdown-wrap .dropdown-toggle').click find("li[data-value='#{option}']").click - if branch_name - find('.js-branch-name').set(branch_name) + if branch_name || source_branch + find('.js-branch-name').set(branch_name) if branch_name + find('.js-ref').set(source_branch) if source_branch # Javascript debounces AJAX calls. # So we have to wait until AJAX requests are started. diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index 0938f9c7d12..45d95db8ff1 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -130,7 +130,7 @@ RSpec.describe "Issues > User edits issue", :js, feature_category: :team_plannin click_button("Switch to rich text editing") end - expect(issuable_form).not_to have_selector(content_editor_focused_selector) + expect(issuable_form).to have_selector(content_editor_focused_selector) refresh @@ -142,11 +142,11 @@ RSpec.describe "Issues > User edits issue", :js, feature_category: :team_plannin click_button("Switch to plain text editing") end - expect(issuable_form).not_to have_selector(markdown_field_focused_selector) + expect(issuable_form).to have_selector(markdown_field_focused_selector) end end - describe 'update labels' do + describe 'update labels', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/345229' do it 'will not send ajax request when no data is changed' do page.within '.labels' do click_on 'Edit' diff --git a/spec/features/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb index 00b04c10d33..af8a31afd5f 100644 --- a/spec/features/issues/user_toggles_subscription_spec.rb +++ b/spec/features/issues/user_toggles_subscription_spec.rb @@ -34,7 +34,7 @@ RSpec.describe "User toggles subscription", :js, feature_category: :team_plannin expect(subscription_button).to have_css("button.is-checked") # Toggle subscription. - find('[data-testid="subscription-toggle"]').click + subscription_button.find('button').click wait_for_requests # Check we're unsubscribed. @@ -42,7 +42,7 @@ RSpec.describe "User toggles subscription", :js, feature_category: :team_plannin end context 'when project emails are disabled' do - let(:project) { create(:project_empty_repo, :public, emails_disabled: true) } + let_it_be(:project) { create(:project_empty_repo, :public, emails_enabled: false) } it 'is disabled' do expect(page).to have_content('Disabled by project owner') @@ -66,7 +66,7 @@ RSpec.describe "User toggles subscription", :js, feature_category: :team_plannin expect(subscription_button).to have_css("button:not(.is-checked)") # Toggle subscription. - find('[data-testid="subscription-toggle"]').click + subscription_button.find('button').click wait_for_requests # Check we're subscribed. diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb index 17ff3e0c702..00aa7685e9d 100644 --- a/spec/features/issues/user_views_issue_spec.rb +++ b/spec/features/issues/user_views_issue_spec.rb @@ -39,42 +39,4 @@ RSpec.describe "User views issue", feature_category: :team_planning do expect(page).not_to have_link('Close issue') end end - - describe 'user status' do - subject { visit(project_issue_path(project, issue)) } - - context 'when showing status of the author of the issue' do - it_behaves_like 'showing user status' do - let(:user_with_status) { user } - end - end - - context 'when showing status of a user who commented on an issue', :js do - it_behaves_like 'showing user status' do - let(:user_with_status) { user } - end - end - - context 'when status message has an emoji', :js do - let_it_be(:message) { 'My status with an emoji' } - let_it_be(:message_emoji) { 'basketball' } - let_it_be(:status) { create(:user_status, user: user, emoji: 'smirk', message: "#{message} :#{message_emoji}:") } - - it 'correctly renders the emoji' do - wait_for_requests - - tooltip_span = page.first(".user-status-emoji[title^='#{message}']") - - tooltip_span.hover - - wait_for_requests - - tooltip = page.find('.tooltip .tooltip-inner') - - page.within(tooltip) do - expect(page).to have_emoji(message_emoji) - end - end - end - end end diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb index 36b02b17924..cd011315ed0 100644 --- a/spec/features/markdown/gitlab_flavored_markdown_spec.rb +++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb @@ -57,16 +57,21 @@ RSpec.describe "GitLab Flavored Markdown", feature_category: :team_planning do describe "for issues", :js do before do - @other_issue = create(:issue, - author: user, - assignees: [user], - project: project) - @issue = create(:issue, - author: user, - assignees: [user], - project: project, - title: "fix #{@other_issue.to_reference}", - description: "ask #{fred.to_reference} for details") + @other_issue = create( + :issue, + author: user, + assignees: [user], + project: project + ) + + @issue = create( + :issue, + author: user, + assignees: [user], + project: project, + title: "fix #{@other_issue.to_reference}", + description: "ask #{fred.to_reference} for details" + ) @note = create(:note_on_issue, noteable: @issue, project: @issue.project, note: "Hello world") end @@ -112,10 +117,12 @@ RSpec.describe "GitLab Flavored Markdown", feature_category: :team_planning do describe "for milestones" do before do - @milestone = create(:milestone, - project: project, - title: "fix #{issue.to_reference}", - description: "ask #{fred.to_reference} for details") + @milestone = create( + :milestone, + project: project, + title: "fix #{issue.to_reference}", + description: "ask #{fred.to_reference} for details" + ) end it "renders title in milestones#index" do diff --git a/spec/features/merge_request/creating_mr_for_projects_with_different_visibility_spec.rb b/spec/features/merge_request/creating_mr_for_projects_with_different_visibility_spec.rb new file mode 100644 index 00000000000..3597c2ed1c7 --- /dev/null +++ b/spec/features/merge_request/creating_mr_for_projects_with_different_visibility_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Merge Request > Selecting projects with different visibility', feature_category: :source_code_management do + include ProjectForksHelper + + let_it_be(:public_project) { create(:project, :public, :small_repo) } + let_it_be(:internal_project) { create(:project, :internal, :small_repo) } + let_it_be(:private_project) { create(:project, :private, :small_repo) } + let(:private_fork_public_project) do + fork_project(public_project, nil, target_project: create(:project, :private, :small_repo)) + end + + let(:private_fork_internal_project) do + fork_project(internal_project, nil, target_project: create(:project, :private, :small_repo)) + end + + let(:internal_fork_public_project) do + fork_project(public_project, nil, target_project: create(:project, :internal, :small_repo)) + end + + let(:user) { source_project.creator } + + before do + sign_in(user) + end + + describe 'warnings for more permissive visibility in target project', :js do + using RSpec::Parameterized::TableSyntax + + where(:source_project, :target_project, :warning_message) do + ref(:private_fork_internal_project) | + ref(:internal_project) | + _('This merge request is from a private project to an internal project.') + + ref(:private_fork_public_project) | + ref(:public_project) | + _('This merge request is from a private project to a public project.') + + ref(:internal_fork_public_project) | + ref(:public_project) | + _('This merge request is from an internal project to a public project.') + end + + with_them do + it 'shows a warning message' do + visit project_new_merge_request_path(source_project, + merge_request: { source_branch: 'master', target_project_id: target_project.id }) + expect(page).to have_content(warning_message) + end + end + + describe 'warnings for more permissive repository access level in target project' do + let(:source_project) do + fork_project(internal_project, nil, target_project: create(:project, :internal, :small_repo)) + end + + let(:target_project) { internal_project } + + let(:warning_message) do + "Project #{source_project.name_with_namespace} has more restricted access settings than " \ + "#{target_project.name_with_namespace}. To avoid exposing private changes, make sure " \ + "you're submitting changes to the correct project." + end + + context 'when the source repository access level is private' do + before do + source_access_level = Featurable::PRIVATE + source_project.project_feature.update!( + repository_access_level: source_access_level, + merge_requests_access_level: source_access_level, + builds_access_level: source_access_level + ) + end + + it 'shows a warning' do + visit project_new_merge_request_path(source_project, + merge_request: { source_branch: 'master', target_project_id: target_project.id }) + expect(page).to have_content(warning_message) + end + + context 'when target project is private' do + let(:source_project) do + fork_project(private_project, nil, target_project: create(:project, :private, :small_repo)) + end + + let(:target_project) { private_project } + + it 'does not show a warning' do + visit project_new_merge_request_path(source_project, + merge_request: { source_branch: 'master', target_project_id: target_project.id }) + + expect(page).not_to have_content(warning_message) + end + end + end + end + end +end diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb index ab7183775b9..bf237e07ac8 100644 --- a/spec/features/merge_request/user_edits_mr_spec.rb +++ b/spec/features/merge_request/user_edits_mr_spec.rb @@ -184,11 +184,6 @@ RSpec.describe 'Merge request > User edits MR', feature_category: :code_review_w it 'allows to unselect "Remove source branch"', :js do expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy - begin - visit edit_project_merge_request_path(target_project, merge_request) - rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError - end - uncheck 'Delete source branch when merge request is accepted' click_button 'Save changes' 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 8c4dbf5ebfd..add8e9f30de 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 @@ -28,12 +28,26 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', let(:expected_detached_mr_tag) { 'merge request' } before do + # rubocop:disable RSpec/AvoidConditionalStatements + stub_licensed_features(merge_request_approvers: true) if Gitlab.ee? + # rubocop:enable RSpec/AvoidConditionalStatements + stub_application_setting(auto_devops_enabled: false) stub_ci_pipeline_yaml_file(YAML.dump(config)) project.add_maintainer(user) sign_in(user) end + # rubocop:disable RSpec/AvoidConditionalStatements + def mr_widget_title + if Gitlab.ee? + 'to be merged automatically when all merge checks pass' + else + 'to be merged automatically when the pipeline succeeds' + end + end + # rubocop:enable RSpec/AvoidConditionalStatements + context 'when a user created a merge request in the parent project' do let!(:merge_request) do create( @@ -163,7 +177,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline is pending' do it 'waits the head pipeline' do - expect(page).to have_content('to be merged automatically when the pipeline succeeds') + expect(page).to have_content mr_widget_title expect(page).to have_button('Cancel auto-merge') end end @@ -177,7 +191,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', end it 'waits the head pipeline' do - expect(page).to have_content('to be merged automatically when the pipeline succeeds') + expect(page).to have_content mr_widget_title expect(page).to have_button('Cancel auto-merge') end end @@ -388,7 +402,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when detached merge request pipeline is pending' do it 'waits the head pipeline' do - expect(page).to have_content('to be merged automatically when the pipeline succeeds') + expect(page).to have_content mr_widget_title expect(page).to have_button('Cancel auto-merge') end end @@ -397,7 +411,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', before do detached_merge_request_pipeline.reload.succeed! - wait_for_requests + refresh end it 'merges the merge request' do @@ -414,7 +428,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', end it 'waits the head pipeline' do - expect(page).to have_content('to be merged automatically when the pipeline succeeds') + expect(page).to have_content mr_widget_title expect(page).to have_button('Cancel auto-merge') end end 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 3cac24838a3..75df93d1a6c 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -197,7 +197,8 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: it 'shows head pipeline information' do within '.ci-widget-content' do - expect(page).to have_content("Pipeline ##{pipeline.id} pending " \ + expect(page).to have_content("Pipeline ##{pipeline.id} pending") + expect(page).to have_content("Pipeline pending " \ "for #{pipeline.short_sha} " \ "on #{pipeline.ref}") end @@ -227,7 +228,8 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: shared_examples 'pipeline widget' do it 'shows head pipeline information', :sidekiq_might_not_need_inline do within '.ci-widget-content' do - expect(page).to have_content("Merge request pipeline ##{pipeline.id} pending for #{pipeline.short_sha}") + expect(page).to have_content("Merge request pipeline ##{pipeline.id} pending") + expect(page).to have_content("Merge request pipeline pending for #{pipeline.short_sha}") end end end @@ -266,7 +268,8 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: shared_examples 'pipeline widget' do it 'shows head pipeline information', :sidekiq_might_not_need_inline do within '.ci-widget-content' do - expect(page).to have_content("Merged result pipeline ##{pipeline.id} pending for #{pipeline.short_sha}") + expect(page).to have_content("Merged result pipeline ##{pipeline.id} pending") + expect(page).to have_content("Merged result pipeline pending for #{pipeline.short_sha}") end 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 5756218d20f..9883434eb68 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 @@ -97,8 +97,8 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js, feature_cat describe 'build list build item' do let(:build_item) do - find('.mini-pipeline-graph-dropdown-item') - first('.mini-pipeline-graph-dropdown-item') + find('.pipeline-job-item') + first('.pipeline-job-item') end it 'visits the build page when clicked' do diff --git a/spec/features/merge_requests/user_sees_note_updates_in_real_time_spec.rb b/spec/features/merge_requests/user_sees_note_updates_in_real_time_spec.rb new file mode 100644 index 00000000000..2c7567b1b40 --- /dev/null +++ b/spec/features/merge_requests/user_sees_note_updates_in_real_time_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Merge request note updates in real time', :js, feature_category: :code_review_workflow do + include NoteInteractionHelpers + include ContentEditorHelpers + + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:merge_request) { create(:merge_request, source_project: project) } + + before do + visit project_merge_request_path(project, merge_request) + close_rich_text_promo_popover_if_present + end + + describe 'new notes' do + it 'displays the new note' do + note = create(:note, noteable: merge_request, project: project, note: 'Looks good!') + + expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') + end + end + + describe 'updated notes' do + let(:note_text) { "Hello World" } + let(:updated_text) { "Bye World" } + let!(:existing_note) do + create(:discussion_note_on_merge_request, noteable: merge_request, project: project, note: note_text) + end + + it 'displays the updated note', :aggregate_failures do + expect(page).to have_selector("#note_#{existing_note.id}", text: note_text) + + existing_note.update!(note: updated_text) + expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text) + + existing_note.resolve!(merge_request.author) + expect(page).to have_selector( + "#note_#{existing_note.id} .discussion-resolved-text", + text: /\AResolved .* by #{merge_request.author.name}\z/ + ) + end + end +end diff --git a/spec/features/nav/top_nav_tooltip_spec.rb b/spec/features/nav/top_nav_tooltip_spec.rb index 17828778112..1afd1981a86 100644 --- a/spec/features/nav/top_nav_tooltip_spec.rb +++ b/spec/features/nav/top_nav_tooltip_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'top nav tooltips', :js, feature_category: :navigation do end it 'clicking new dropdown hides tooltip', :aggregate_failures, - quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/382786' do + quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/382786' do btn = '#js-onboarding-new-project-link' page.find(btn).hover diff --git a/spec/features/oauth_provider_authorize_spec.rb b/spec/features/oauth_provider_authorize_spec.rb index 7638563b4a3..310a2e4c2de 100644 --- a/spec/features/oauth_provider_authorize_spec.rb +++ b/spec/features/oauth_provider_authorize_spec.rb @@ -9,11 +9,13 @@ RSpec.describe 'OAuth Provider', feature_category: :system_access do before do sign_in(user) - visit oauth_authorization_path(client_id: application.uid, - redirect_uri: application.redirect_uri.split.first, - response_type: 'code', - state: 'my_state', - scope: 'read_user') + visit oauth_authorization_path( + client_id: application.uid, + redirect_uri: application.redirect_uri.split.first, + response_type: 'code', + state: 'my_state', + scope: 'read_user' + ) end it_behaves_like 'Secure OAuth Authorizations' diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index d8501116134..dbeca601617 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -62,8 +62,7 @@ RSpec.describe 'Member autocomplete', :js, feature_category: :groups_and_project context 'adding a new note on a Merge Request' do let(:noteable) do - create(:merge_request, source_project: project, - target_project: project, author: author) + create(:merge_request, source_project: project, target_project: project, author: author) end before do diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index e190dfda937..b6c96555767 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -38,8 +38,7 @@ RSpec.describe 'Profile account page', :js, feature_category: :user_profile do expect(page).to have_content('Account scheduled for removal') expect( - Users::GhostUserMigration.where(user: user, - initiator_user: user) + Users::GhostUserMigration.where(user: user, initiator_user: user) ).to be_exists end @@ -71,7 +70,7 @@ RSpec.describe 'Profile account page', :js, feature_category: :user_profile do previous_token = '' - within('[data-testid="feed-token-container"]') do + within_testid('feed-token-container') do previous_token = find_field('Feed token').value click_link('reset this token') @@ -79,7 +78,7 @@ RSpec.describe 'Profile account page', :js, feature_category: :user_profile do accept_gl_confirm - within('[data-testid="feed-token-container"]') do + within_testid('feed-token-container') do click_button('Click to reveal') expect(find_field('Feed token').value).not_to eq(previous_token) @@ -93,7 +92,7 @@ RSpec.describe 'Profile account page', :js, feature_category: :user_profile do previous_token = '' - within('[data-testid="incoming-email-token-container"]') do + within_testid('incoming-email-token-container') do previous_token = find_field('Incoming email token').value click_link('reset this token') @@ -101,7 +100,7 @@ RSpec.describe 'Profile account page', :js, feature_category: :user_profile do accept_gl_confirm - within('[data-testid="incoming-email-token-container"]') do + within_testid('incoming-email-token-container') do click_button('Click to reveal') expect(find_field('Incoming email token').value).not_to eq(previous_token) diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb index 0de4ad47f9a..2e800ae88b6 100644 --- a/spec/features/profiles/active_sessions_spec.rb +++ b/spec/features/profiles/active_sessions_spec.rb @@ -57,9 +57,7 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state, fe using_session :session1 do visit profile_active_sessions_path - expect(page).to( - have_selector('ul.list-group li.list-group-item', text: 'Signed in on', - count: 2)) + expect(page).to(have_selector('ul.list-group li.list-group-item', text: 'Signed in on', count: 2)) expect(page).to have_content( '127.0.0.1 ' \ diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb index f39d9ddaf56..38abf9d20b0 100644 --- a/spec/features/profiles/gpg_keys_spec.rb +++ b/spec/features/profiles/gpg_keys_spec.rb @@ -15,6 +15,7 @@ RSpec.describe 'Profile > GPG Keys', feature_category: :user_profile do end it 'saves the new key' do + click_button('Add new key') fill_in('Key', with: GpgHelpers::User2.public_key) click_button('Add key') @@ -24,6 +25,7 @@ RSpec.describe 'Profile > GPG Keys', feature_category: :user_profile do end it 'with multiple subkeys' do + click_button('Add new key') fill_in('Key', with: GpgHelpers::User3.public_key) click_button('Add key') @@ -52,7 +54,10 @@ RSpec.describe 'Profile > GPG Keys', feature_category: :user_profile do click_link('Remove') - expect(page).to have_content('Your GPG keys (0)') + expect(page).to have_content('Your GPG keys') + page.within('.gl-new-card-count') do + expect(page).to have_content('0') + end end it 'user revokes a key via the key index' do @@ -63,7 +68,10 @@ RSpec.describe 'Profile > GPG Keys', feature_category: :user_profile do click_link('Revoke') - expect(page).to have_content('Your GPG keys (0)') + expect(page).to have_content('Your GPG keys') + page.within('.gl-new-card-count') do + expect(page).to have_content('0') + end expect(gpg_signature.reload).to have_attributes( verification_status: 'unknown_key', diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb index ae61f1cf492..cb270b669d3 100644 --- a/spec/features/profiles/keys_spec.rb +++ b/spec/features/profiles/keys_spec.rb @@ -15,6 +15,7 @@ RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do end it 'auto-populates the title', :js do + click_button('Add new key') fill_in('Key', with: attributes_for(:key).fetch(:key)) expect(page).to have_field("Title", with: "dummy@gitlab.com") @@ -23,11 +24,12 @@ RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do it 'saves the new key' do attrs = attributes_for(:key) + click_button('Add new key') fill_in('Key', with: attrs[:key]) fill_in('Title', with: attrs[:title]) click_button('Add key') - expect(page).to have_content("Title: #{attrs[:title]}") + expect(page).to have_content(format(s_('Profiles|SSH Key: %{title}'), title: attrs[:title])) expect(page).to have_content(attrs[:key]) expect(find('[data-testid="breadcrumb-current-link"]')).to have_link(attrs[:title]) end @@ -35,6 +37,7 @@ RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do it 'shows a confirmable warning if the key begins with an algorithm name that is unsupported' do attrs = attributes_for(:key) + click_button('Add new key') fill_in('Key', with: 'unsupported-ssh-rsa key') fill_in('Title', with: attrs[:title]) click_button('Add key') @@ -60,6 +63,7 @@ RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do it 'shows a validation error' do attrs = attributes_for(:key) + click_button('Add new key') fill_in('Key', with: attrs[:key]) fill_in('Title', with: attrs[:title]) click_button('Add key') @@ -79,13 +83,16 @@ RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do def destroy_key(path, action, confirmation_button) visit path - page.click_button(action) + page.find("button[aria-label=\"#{action}\"]").click page.within('.modal') do page.click_button(confirmation_button) end - expect(page).to have_content('Your SSH keys (0)') + expect(page).to have_content('Your SSH keys') + page.within('.gl-new-card-count') do + expect(page).to have_content('0') + end end describe 'User removes a key', :js do @@ -111,11 +118,13 @@ RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do let!(:commit) { project.commit('ssh-signed-commit') } let!(:signature) do - create(:ssh_signature, - project: project, - key: key, - key_fingerprint_sha256: key.fingerprint_sha256, - commit_sha: commit.sha) + create( + :ssh_signature, + project: project, + key: key, + key_fingerprint_sha256: key.fingerprint_sha256, + commit_sha: commit.sha + ) end before do diff --git a/spec/features/profiles/list_users_comment_template_spec.rb b/spec/features/profiles/list_users_comment_template_spec.rb index 85e455ba988..b2faee3ae04 100644 --- a/spec/features/profiles/list_users_comment_template_spec.rb +++ b/spec/features/profiles/list_users_comment_template_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'Profile > Comment templates > List users comment templates', :js it 'shows the user a list of their comment templates' do visit profile_comment_templates_path - expect(page).to have_content('My comment templates (1)') + expect(page).to have_content('My comment templates') expect(page).to have_content(saved_reply.name) expect(page).to have_content(saved_reply.content) end diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb index d088f73f9df..0b1d67d00c9 100644 --- a/spec/features/profiles/oauth_applications_spec.rb +++ b/spec/features/profiles/oauth_applications_spec.rb @@ -25,15 +25,21 @@ RSpec.describe 'Profile > Applications', feature_category: :user_profile do visit oauth_applications_path page.within('.oauth-applications') do - expect(page).to have_content('Your applications (1)') + page.within('.gl-new-card-count') do + expect(page).to have_content('1') + end click_button 'Destroy' end accept_gl_confirm(button_text: 'Destroy') expect(page).to have_content('The application was deleted successfully') - expect(page).to have_content('Your applications (0)') - expect(page).to have_content('Authorized applications (0)') + page.within('.oauth-applications .gl-new-card-count') do + expect(page).to have_content('0') + end + page.within('.oauth-authorized-applications .gl-new-card-count') do + expect(page).to have_content('0') + end end end @@ -57,7 +63,9 @@ RSpec.describe 'Profile > Applications', feature_category: :user_profile do it 'displays the correct authorized applications' do visit oauth_applications_path - expect(page).to have_content('Authorized applications (2)') + page.within('.oauth-authorized-applications .gl-new-card-count') do + expect(page).to have_content('2') + end page.within('div.oauth-authorized-applications') do # Ensure the correct user's token details are displayed @@ -85,7 +93,9 @@ RSpec.describe 'Profile > Applications', feature_category: :user_profile do accept_gl_confirm(button_text: 'Revoke application') expect(page).to have_content('The application was revoked access.') - expect(page).to have_content('Authorized applications (0)') + page.within('.oauth-authorized-applications .gl-new-card-count') do + expect(page).to have_content('0') + end end it 'deletes an anonymous authorized application' do @@ -93,14 +103,18 @@ RSpec.describe 'Profile > Applications', feature_category: :user_profile do visit oauth_applications_path page.within('.oauth-authorized-applications') do - expect(page).to have_content('Authorized applications (1)') + page.within('.oauth-authorized-applications .gl-new-card-count') do + expect(page).to have_content('1') + end click_button 'Revoke' end accept_gl_confirm(button_text: 'Revoke application') expect(page).to have_content('The application was revoked access.') - expect(page).to have_content('Authorized applications (0)') + page.within('.oauth-authorized-applications .gl-new-card-count') do + expect(page).to have_content('0') + end end end end diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index 65fe1330be2..094855393be 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -18,6 +18,8 @@ RSpec.describe 'Profile > Personal Access Tokens', :js, feature_category: :user_ name = 'My PAT' visit profile_personal_access_tokens_path + + click_button 'Add new token' fill_in "Token name", with: name # Set date to 1st of next month @@ -43,6 +45,8 @@ RSpec.describe 'Profile > Personal Access Tokens', :js, feature_category: :user_ it "displays an error message" do number_tokens_before = PersonalAccessToken.count visit profile_personal_access_tokens_path + + click_button 'Add new token' fill_in "Token name", with: 'My PAT' click_on "Create personal access token" @@ -145,6 +149,7 @@ RSpec.describe 'Profile > Personal Access Tokens', :js, feature_category: :user_ visit profile_personal_access_tokens_path({ name: name, scopes: scopes }) + click_button 'Add new token' expect(page).to have_field("Token name", with: name) expect(find("#personal_access_token_scopes_api")).to be_checked expect(find("#personal_access_token_scopes_read_user")).to be_checked diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb index 89887cb4772..bdaf6262566 100644 --- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb +++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity setting', :js, -feature_category: :user_profile do + feature_category: :user_profile do let(:user) { create(:user) } before do diff --git a/spec/features/profiles/user_creates_comment_template_spec.rb b/spec/features/profiles/user_creates_comment_template_spec.rb index 44e2b932c00..dcaf47088b0 100644 --- a/spec/features/profiles/user_creates_comment_template_spec.rb +++ b/spec/features/profiles/user_creates_comment_template_spec.rb @@ -15,6 +15,7 @@ RSpec.describe 'Profile > Comment templates > User creates comment template', :j end it 'shows the user a list of their saved replies' do + click_button 'Add new' find('[data-testid="comment-template-name-input"]').set('test') find('[data-testid="comment-template-content-input"]').set('Test content') @@ -22,7 +23,7 @@ RSpec.describe 'Profile > Comment templates > User creates comment template', :j wait_for_requests - expect(page).to have_content('My comment templates (1)') + expect(page).to have_content('My comment templates') expect(page).to have_content('test') expect(page).to have_content('Test content') end diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index de8719630ee..a756c524cbb 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -83,7 +83,7 @@ RSpec.describe 'User edit profile', feature_category: :user_profile do page.within('.rspec-full-name') do expect(page).to have_css '.gl-field-error-outline' expect(find('.gl-field-error')).not_to have_selector('.hidden') - expect(find('.gl-field-error')).to have_content('Using emojis in names seems fun, but please try to set a status message instead') + expect(find('.gl-field-error')).to have_content('Using emoji in names seems fun, but please try to set a status message instead') end end diff --git a/spec/features/profiles/user_manages_emails_spec.rb b/spec/features/profiles/user_manages_emails_spec.rb index b875dfec217..35f2ccf0f34 100644 --- a/spec/features/profiles/user_manages_emails_spec.rb +++ b/spec/features/profiles/user_manages_emails_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'User manages emails', feature_category: :user_profile do it 'adds an email', :aggregate_failures do fill_in('email_email', with: 'my@email.com') - click_button('Add') + click_button('Add email address') email = user.emails.find_by(email: 'my@email.com') @@ -37,7 +37,7 @@ RSpec.describe 'User manages emails', feature_category: :user_profile do it 'does not add an email that is the primary email of another user', :aggregate_failures do fill_in('email_email', with: other_user.email) - click_button('Add') + click_button('Add email address') email = user.emails.find_by(email: other_user.email) @@ -51,7 +51,7 @@ RSpec.describe 'User manages emails', feature_category: :user_profile do it 'removes an email', :aggregate_failures do fill_in('email_email', with: 'my@email.com') - click_button('Add') + click_button('Add email address') email = user.emails.find_by(email: 'my@email.com') diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb index 1295a0b6150..7d858e3c92c 100644 --- a/spec/features/profiles/user_visits_notifications_tab_spec.rb +++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'User visits the notifications tab', :js, feature_category: :user end context 'when project emails are disabled' do - let(:project) { create(:project, emails_disabled: true) } + let_it_be(:project) { create(:project, emails_enabled: false) } it 'notification button is disabled' do expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled') diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb index c4f78bf4ea3..e2fa924af67 100644 --- a/spec/features/project_variables_spec.rb +++ b/spec/features/project_variables_spec.rb @@ -12,6 +12,8 @@ RSpec.describe 'Project variables', :js, feature_category: :secrets_management d sign_in(user) project.add_maintainer(user) project.variables << variable + + stub_feature_flags(ci_variable_drawer: false) visit page_path wait_for_requests end @@ -49,4 +51,14 @@ RSpec.describe 'Project variables', :js, feature_category: :secrets_management d expect(find('.js-ci-variable-row:first-child [data-label="Environments"]').text).to eq('review/*') end end + + context 'when ci_variable_drawer FF is enabled' do + before do + stub_feature_flags(ci_variable_drawer: true) + visit page_path + wait_for_requests + end + + it_behaves_like 'variable list drawer' + end end diff --git a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb index 1990526b5fc..cd7601aa94e 100644 --- a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb +++ b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb @@ -25,8 +25,6 @@ RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled end it 'displays suggest_gitlab_ci_yml popover' do - page.find(:css, '.gitlab-ci-yml-selector').click - popover_selector = '.suggest-gitlab-ci-yml' expect(page).to have_css(popover_selector, visible: true) diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb index 43da57c16d1..b09aa91f4ab 100644 --- a/spec/features/projects/ci/editor_spec.rb +++ b/spec/features/projects/ci/editor_spec.rb @@ -11,6 +11,7 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d let(:default_branch) { 'main' } let(:other_branch) { 'test' } let(:branch_with_invalid_ci) { 'despair' } + let(:branch_without_ci) { 'empty' } let(:default_content) { 'Default' } @@ -45,6 +46,7 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d project.repository.create_file(user, project.ci_config_path_or_default, default_content, message: 'Create CI file for main', branch_name: default_branch) project.repository.create_file(user, project.ci_config_path_or_default, valid_content, message: 'Create CI file for test', branch_name: other_branch) project.repository.create_file(user, project.ci_config_path_or_default, invalid_content, message: 'Create CI file for test', branch_name: branch_with_invalid_ci) + project.repository.create_file(user, 'index.js', "file", message: 'New js file', branch_name: branch_without_ci) visit project_ci_pipeline_editor_path(project) wait_for_requests @@ -62,6 +64,31 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d end end + describe 'when there are no CI config file' do + before do + visit project_ci_pipeline_editor_path(project, branch_name: branch_without_ci) + end + + it 'renders the empty page', :aggregate_failures do + expect(page).to have_content 'Optimize your workflow with CI/CD Pipelines' + expect(page).to have_selector '[data-testid="create_new_ci_button"]' + end + + context 'when clicking on the create new CI button' do + before do + click_button 'Configure pipeline' + end + + it 'renders the source editor with default content', :aggregate_failures do + expect(page).to have_selector('#source-editor-') + + page.within('#source-editor-') do + expect(page).to have_content('This file is a template, and might need editing before it works on your project.') + end + end + end + end + describe 'When CI yml has valid syntax' do before do visit project_ci_pipeline_editor_path(project, branch_name: other_branch) @@ -149,15 +176,6 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d end shared_examples 'default branch switcher behavior' do - def switch_to_branch(branch) - find('[data-testid="branch-selector"]').click - - page.within '[data-testid="branch-selector"]' do - click_button branch - wait_for_requests - end - end - it 'displays current branch' do page.within('[data-testid="branch-selector"]') do expect(page).to have_content(default_branch) @@ -195,12 +213,20 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d end describe 'Branch Switcher' do + def switch_to_branch(branch) + # close button for the popover + find('[data-testid="close-button"]').click + find('[data-testid="branch-selector"]').click + + page.within '[data-testid="branch-selector"]' do + click_button branch + wait_for_requests + end + end + before do visit project_ci_pipeline_editor_path(project) wait_for_requests - - # close button for the popover - find('[data-testid="close-button"]').click end it_behaves_like 'default branch switcher behavior' @@ -262,6 +288,24 @@ RSpec.describe 'Pipeline Editor', :js, feature_category: :pipeline_composition d end describe 'Commit Form' do + context 'when targetting the main branch' do + it 'does not show the option to create a Merge request', :aggregate_failures do + expect(page).not_to have_selector('[data-testid="new-mr-checkbox"]') + expect(page).not_to have_content('Start a new merge request with these changes') + end + end + + context 'when targetting any non-main branch' do + before do + find('#source-branch-field').set('new_branch', clear: :backspace) + end + + it 'shows the option to create a Merge request', :aggregate_failures do + expect(page).to have_selector('[data-testid="new-mr-checkbox"]') + expect(page).to have_content('Start a new merge request with these changes') + end + end + it 'is preserved when changing tabs' do find('#commit-message').set('message', clear: :backspace) find('#source-branch-field').set('new_branch', clear: :backspace) diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index d2104799e79..5bb3d1af924 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Mini Pipeline Graph in Commit View', :js, feature_category: :source_code_management do let(:project) { create(:project, :public, :repository) } - context 'when commit has pipelines' do + context 'when commit has pipelines and feature flag is enabled' do let(:pipeline) do create( :ci_pipeline, @@ -24,6 +24,33 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js, feature_category: :sou wait_for_requests end + it 'displays the graphql pipeline stage' do + expect(page).to have_selector('[data-testid="pipeline-stage"]') + + build.drop + end + end + + context 'when commit has pipelines and feature flag is disabled' do + let(:pipeline) do + create( + :ci_pipeline, + status: :running, + project: project, + ref: project.default_branch, + sha: project.commit.sha + ) + end + + let(:build) { create(:ci_build, pipeline: pipeline, status: :running) } + + before do + stub_feature_flags(ci_graphql_pipeline_mini_graph: false) + build.run + visit project_commit_path(project, project.commit.id) + wait_for_requests + end + it 'display icon with status' do expect(page).to have_selector('.ci-status-icon-running') end diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb index 493435d3439..0a77c671fce 100644 --- a/spec/features/projects/container_registry_spec.rb +++ b/spec/features/projects/container_registry_spec.rb @@ -67,6 +67,7 @@ RSpec.describe 'Container Registry', :js, feature_category: :groups_and_projects it 'list page has a list of images' do visit_container_registry + expect(page).to have_content '1 Image repository' expect(page).to have_content 'my/image' end @@ -189,8 +190,7 @@ RSpec.describe 'Container Registry', :js, feature_category: :groups_and_projects it 'pagination is preserved after navigating back from details' do visit_next_page click_link 'my/image' - breadcrumb = find '.breadcrumbs' - breadcrumb.click_link 'Container Registry' + page.go_back expect(page).to have_content 'my/image' end end diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb index a74cde35be6..55b15ad95d1 100644 --- a/spec/features/projects/files/dockerfile_dropdown_spec.rb +++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb @@ -12,20 +12,16 @@ RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', :js, fe end it 'user can pick a Dockerfile file from the dropdown' do - expect(page).to have_css('.dockerfile-selector') + click_button 'Apply a template' - find('.js-dockerfile-selector').click - - wait_for_requests - - within '.dockerfile-selector' do - find('.dropdown-input-field').set('HTTPd') - find('.dropdown-content li', text: 'HTTPd').click + within '.gl-new-dropdown-panel' do + find('.gl-listbox-search-input').set('HTTPd') + find('.gl-new-dropdown-contents li', text: 'HTTPd').click end wait_for_requests - expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'Apply a template') + expect(page).to have_css('.gl-new-dropdown-button-text', text: 'HTTPd') expect(find('.monaco-editor')).to have_content('COPY ./ /usr/local/apache2/htdocs/') end end diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb index 36b02b9b948..b1f7f1c5716 100644 --- a/spec/features/projects/files/gitignore_dropdown_spec.rb +++ b/spec/features/projects/files/gitignore_dropdown_spec.rb @@ -12,20 +12,16 @@ RSpec.describe 'Projects > Files > User wants to add a .gitignore file', :js, fe end it 'user can pick a .gitignore file from the dropdown' do - expect(page).to have_css('.gitignore-selector') + click_button 'Apply a template' - find('.js-gitignore-selector').click - - wait_for_requests - - within '.gitignore-selector' do - find('.dropdown-input-field').set('rails') - find('.dropdown-content li', text: 'Rails').click + within '.gl-new-dropdown-panel' do + find('.gl-listbox-search-input').set('rails') + find('.gl-new-dropdown-contents li', text: 'Rails').click end wait_for_requests - expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Apply a template') + expect(page).to have_css('.gl-new-dropdown-button-text', text: 'Rails') expect(find('.monaco-editor')).to have_content('/.bundle') expect(find('.monaco-editor')).to have_content('config/initializers/secret_token.rb') end diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb index 929554ff0d6..7bfff6b68e8 100644 --- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb @@ -16,20 +16,16 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js end it 'user can pick a template from the dropdown' do - expect(page).to have_css('.gitlab-ci-yml-selector') + click_button 'Apply a template' - find('.js-gitlab-ci-yml-selector').click - - wait_for_requests - - within '.gitlab-ci-yml-selector' do - find('.dropdown-input-field').set('Jekyll') - find('.dropdown-content li', text: 'Jekyll').click + within '.gl-new-dropdown-panel' do + find('.gl-listbox-search-input').set('Jekyll') + find('.gl-new-dropdown-contents li', text: 'Jekyll').click end wait_for_requests - expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') + expect(page).to have_css('.gl-new-dropdown-button-text', text: 'Jekyll') expect(find('.monaco-editor')).to have_content('This file is a template, and might need editing before it works on your project') expect(find('.monaco-editor')).to have_content('jekyll build -d test') end @@ -40,7 +36,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js it 'uses the given template' do wait_for_requests - expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') + expect(page).to have_css('.gl-new-dropdown-button-text', text: 'Jekyll') expect(find('.monaco-editor')).to have_content('This file is a template, and might need editing before it works on your project') expect(find('.monaco-editor')).to have_content('jekyll build -d test') end @@ -52,7 +48,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js it 'leaves the editor empty' do wait_for_requests - expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') + expect(page).to have_css('.gl-new-dropdown-button-text', text: 'Apply a template') expect(find('.monaco-editor')).to have_content('') end end diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb index 8ec9adaeb9a..95e96159744 100644 --- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb +++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb @@ -20,8 +20,6 @@ RSpec.describe 'Projects > Files > Project owner creates a license file', :js, f fill_in :file_name, with: 'LICENSE' - expect(page).to have_selector('.license-selector') - select_template('MIT License') file_content = first('.file-editor') @@ -44,7 +42,6 @@ RSpec.describe 'Projects > Files > Project owner creates a license file', :js, f expect(page).to have_current_path( project_new_blob_path(project, 'master'), ignore_query: true) expect(find('#file_name').value).to eq('LICENSE') - expect(page).to have_selector('.license-selector') select_template('MIT License') @@ -62,9 +59,9 @@ RSpec.describe 'Projects > Files > Project owner creates a license file', :js, f end def select_template(template) - page.within('.js-license-selector-wrap') do + page.within('.gl-new-dropdown') do click_button 'Apply a template' - click_link template + find('.gl-new-dropdown-contents li', text: template).click wait_for_requests end end diff --git a/spec/features/projects/files/template_selector_menu_spec.rb b/spec/features/projects/files/template_selector_menu_spec.rb index 46c4b69bc89..920da6e72ce 100644 --- a/spec/features/projects/files/template_selector_menu_spec.rb +++ b/spec/features/projects/files/template_selector_menu_spec.rb @@ -58,7 +58,7 @@ end def check_template_selector_menu_display(is_visible) count = is_visible ? 1 : 0 - expect(page).to have_css('.template-selectors-menu', count: count) + expect(page).to have_css('[data-testid="template-selector"]', count: count) end def create_and_edit_file(file_name) diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb index 4b6e6b7282c..d6f9acc68a0 100644 --- a/spec/features/projects/files/undo_template_spec.rb +++ b/spec/features/projects/files/undo_template_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Projects > Files > Template Undo Button', :js, feature_category: context 'editing a matching file and applying a template' do before do visit project_edit_blob_path(project, File.join(project.default_branch, "LICENSE")) - select_file_template('.js-license-selector', 'Apache License 2.0') + select_file_template('Apache License 2.0') end it 'reverts template application' do @@ -42,8 +42,8 @@ def check_content_reverted(template_content) expect(page).not_to have_content(template_content) end -def select_file_template(template_selector_selector, template_name) - find(template_selector_selector).click - find('.dropdown-content li', text: template_name).click +def select_file_template(template_name) + click_button 'Apply a template' + find('.gl-new-dropdown-contents li', text: template_name).click wait_for_requests end diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb index 3b30a620257..e93c9427c91 100644 --- a/spec/features/projects/files/user_browses_files_spec.rb +++ b/spec/features/projects/files/user_browses_files_spec.rb @@ -155,7 +155,7 @@ RSpec.describe "User browses files", :js, feature_category: :groups_and_projects click_link("d") end - expect(page).to have_link("..", href: project_tree_path(project, "markdown/")) + expect(page).to have_link("..", href: project_tree_path(project, "markdown")) page.within(".tree-table") do click_link("README.md") diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb index 7d734d5d2df..c159b40003c 100644 --- a/spec/features/projects/fork_spec.rb +++ b/spec/features/projects/fork_spec.rb @@ -14,23 +14,80 @@ RSpec.describe 'Project fork', feature_category: :groups_and_projects do end shared_examples 'fork button on project page' do - it 'allows user to fork project from the project page' do - visit project_path(project) + context 'when the user has access to only one namespace and has already forked the project', :js do + before do + fork_project(project, user, repository: true, namespace: user.namespace) + end - expect(page).not_to have_css('a.disabled', text: 'Fork') - end + it 'allows user to go to their fork' do + visit project_path(project) - context 'user has exceeded personal project limit' do - before do - user.update!(projects_limit: 0) + path = namespace_project_path(user, user.fork_of(project)) + + fork_button = find_link 'Fork' + expect(fork_button['href']).to include(path) + expect(fork_button['class']).not_to include('disabled') end + end - it 'disables fork button on project page' do + shared_examples 'fork button creates new fork' do + it 'allows user to fork the project from the project page' do visit project_path(project) - expect(page).to have_css('a.disabled', text: 'Fork') + path = new_project_fork_path(project) + + fork_button = find_link 'Fork' + expect(fork_button['href']).to include(path) + expect(fork_button['class']).not_to include('disabled') + end + + context 'when the user cannot fork the project' do + let(:project) do + # Disabling the repository makes sure that the user cannot fork the project + create(:project, :public, :repository, :repository_disabled, description: 'some description') + end + + it 'disables fork button on project page' do + visit project_path(project) + + path = new_project_fork_path(project) + + fork_button = find_link 'Fork' + expect(fork_button['href']).to include(path) + expect(fork_button['class']).to include('disabled') + end + 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 + visit project_path(project) + + path = new_project_fork_path(project) + + fork_button = find_link 'Fork' + expect(fork_button['href']).to include(path) + expect(fork_button['class']).to include('disabled') + end end end + + context 'when the user has not already forked the project', :js do + it_behaves_like 'fork button creates new fork' + end + + context 'when the user has access to more than one namespace', :js do + let(:group) { create(:group) } + + before do + group.add_developer(user) + end + + it_behaves_like 'fork button creates new fork' + end end shared_examples 'create fork page' do |fork_page_text| @@ -42,11 +99,11 @@ RSpec.describe 'Project fork', feature_category: :groups_and_projects do context 'forking is enabled' do let(:forking_access_level) { ProjectFeature::ENABLED } - it 'enables fork button' do + it 'enables fork button', :js do visit project_path(project) - expect(page).to have_css('a', text: 'Fork') - expect(page).not_to have_css('a.disabled', text: 'Select') + fork_button = find_link 'Fork' + expect(fork_button['class']).not_to include('disabled') end it 'renders new project fork page' do @@ -60,11 +117,13 @@ RSpec.describe 'Project fork', feature_category: :groups_and_projects do context 'forking is disabled' do let(:forking_access_level) { ProjectFeature::DISABLED } - it 'render a disabled fork button' do + it 'render a disabled fork button', :js do visit project_path(project) - expect(page).to have_css('a.disabled', text: 'Fork') - expect(page).to have_css('a.count', text: '0') + fork_button = find_link 'Fork' + + expect(fork_button['class']).to include('disabled') + expect(page).to have_selector('[data-testid="forks-count"]') end it 'does not render new project fork page' do @@ -82,11 +141,13 @@ RSpec.describe 'Project fork', feature_category: :groups_and_projects do end context 'user is not a team member' do - it 'render a disabled fork button' do + it 'render a disabled fork button', :js do visit project_path(project) - expect(page).to have_css('a.disabled', text: 'Fork') - expect(page).to have_css('a.count', text: '0') + fork_button = find_link 'Fork' + + expect(fork_button['class']).to include('disabled') + expect(page).to have_selector('[data-testid="forks-count"]') end it 'does not render new project fork page' do @@ -101,12 +162,13 @@ RSpec.describe 'Project fork', feature_category: :groups_and_projects do project.add_developer(user) end - it 'enables fork button' do + it 'enables fork button', :js do visit project_path(project) - expect(page).to have_css('a', text: 'Fork') - expect(page).to have_css('a.count', text: '0') - expect(page).not_to have_css('a.disabled', text: 'Fork') + fork_button = find_link 'Fork' + + expect(fork_button['class']).not_to include('disabled') + expect(page).to have_selector('[data-testid="forks-count"]') end it 'renders new project fork page' do @@ -185,7 +247,8 @@ RSpec.describe 'Project fork', feature_category: :groups_and_projects do visit project_path(project) - expect(page).to have_css('.fork-count', text: 2) + forks_count_button = find('[data-testid="forks-count"]') + expect(forks_count_button).to have_content("2") end end end @@ -195,7 +258,9 @@ private def create_fork(group_obj = group) visit project_path(project) - find('.fork-btn').click + + click_link 'Fork' + submit_form(group_obj) wait_for_requests end diff --git a/spec/features/projects/labels/user_edits_labels_spec.rb b/spec/features/projects/labels/user_edits_labels_spec.rb index f90f215f9fc..bf1182cfddd 100644 --- a/spec/features/projects/labels/user_edits_labels_spec.rb +++ b/spec/features/projects/labels/user_edits_labels_spec.rb @@ -36,6 +36,6 @@ RSpec.describe "User edits labels", feature_category: :team_planning do click_link 'Delete label' end - expect(page).to have_content('Label was removed') + expect(page).to have_content("#{label.title} was removed").and have_no_content("#{label.title}</span>") end end diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index 55dc52b8ccf..d0175c53951 100644 --- a/spec/features/projects/labels/user_removes_labels_spec.rb +++ b/spec/features/projects/labels/user_removes_labels_spec.rb @@ -30,7 +30,7 @@ RSpec.describe "User removes labels", feature_category: :team_planning do first(:link, "Delete label").click - expect(page).to have_content("Label was removed").and have_no_content(label.title) + expect(page).to have_content("#{label.title} was removed").and have_no_content("#{label.title}</span>") end end diff --git a/spec/features/projects/members/import_project_members_spec.rb b/spec/features/projects/members/import_project_members_spec.rb new file mode 100644 index 00000000000..20cf42cd135 --- /dev/null +++ b/spec/features/projects/members/import_project_members_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Projects > Members > Import project members', :js, feature_category: :groups_and_projects do + include Features::MembersHelpers + include ListboxHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:user_mike) { create(:user, name: 'Mike') } + let_it_be(:group) { create(:group) } + let_it_be(:project) do + create(:project, group: group).tap do |p| + p.add_maintainer(user) + p.add_developer(create(:user)) + end + end + + let_it_be(:project2) do + create(:project).tap do |p| + p.add_maintainer(user) + p.add_reporter(user_mike) + end + end + + before do + sign_in(user) + + visit(project_project_members_path(project)) + end + + it 'imports a team from another project' do + select_project(project2) + submit_import + + expect(find_member_row(user_mike)).to have_content('Reporter') + end + + it 'fails to import the other team when source project does not exist' do + select_project(project2) + submit_import { project2.destroy! } + + within import_project_members_modal_selector do + expect(page).to have_content('Unable to import project members') + end + end + + it 'fails to import some members' do + group.add_owner(user_mike) + + select_project(project2) + submit_import + + within import_project_members_modal_selector do + expect(page).to have_content "The following 1 out of 2 members could not be added" + expect(page).to have_content "@#{user_mike.username}: Access level should be greater than or equal to " \ + "Owner inherited membership from group #{group.name}" + end + end + + def select_project(source_project) + click_on 'Import from a project' + click_on 'Select a project' + wait_for_requests + + select_listbox_item(source_project.name_with_namespace) + end + + def submit_import + yield if block_given? # rubocop:disable RSpec/AvoidConditionalStatements + + click_button 'Import project members' + wait_for_requests + end + + def import_project_members_modal_selector + '[data-testid="import-project-members-modal"]' + end +end diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/members/user_manages_project_members_spec.rb index df571e13979..b1c3132767c 100644 --- a/spec/features/projects/settings/user_manages_project_members_spec.rb +++ b/spec/features/projects/members/user_manages_project_members_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Projects > Settings > User manages project members', feature_category: :groups_and_projects do +RSpec.describe 'Projects > Settings > User manages project members', :js, feature_category: :groups_and_projects do include Features::MembersHelpers include Spec::Support::Helpers::ModalHelpers include ListboxHelpers @@ -20,7 +20,7 @@ RSpec.describe 'Projects > Settings > User manages project members', feature_cat sign_in(user) end - it 'cancels a team member', :js do + it 'cancels a team member' do visit(project_project_members_path(project)) show_actions_for_username(user_dmitriy) @@ -37,24 +37,7 @@ RSpec.describe 'Projects > Settings > User manages project members', feature_cat expect(members_table).not_to have_content(user_dmitriy.username) end - it 'imports a team from another project', :js do - project2.add_maintainer(user) - project2.add_reporter(user_mike) - - visit(project_project_members_path(project)) - - click_on 'Import from a project' - click_on 'Select a project' - wait_for_requests - - select_listbox_item(project2.name_with_namespace) - click_button 'Import project members' - wait_for_requests - - expect(find_member_row(user_mike)).to have_content('Reporter') - end - - it 'shows all members of project shared group', :js do + it 'shows all members of project shared group' do group.add_owner(user) group.add_developer(user_dmitriy) diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index d05b7649f94..6e6d9ff4af9 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -46,7 +46,8 @@ RSpec.describe 'New project', :js, feature_category: :groups_and_projects do end it 'shows a message if multiple levels are restricted' do - Gitlab::CurrentSettings.update!( + stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::PUBLIC) + stub_application_setting( restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] ) @@ -56,15 +57,21 @@ RSpec.describe 'New project', :js, feature_category: :groups_and_projects do expect(page).to have_content 'Other visibility settings have been disabled by the administrator.' end - it 'shows a message if all levels are restricted' do - Gitlab::CurrentSettings.update!( - restricted_visibility_levels: Gitlab::VisibilityLevel.values - ) + context 'with prevent_visibility_restriction feature flag off' do + before do + stub_feature_flags(prevent_visibility_restriction: false) + end - visit new_project_path - click_link 'Create blank project' + it 'shows a message if all levels are restricted' do + Gitlab::CurrentSettings.update!( + restricted_visibility_levels: Gitlab::VisibilityLevel.values + ) - expect(page).to have_content 'Visibility settings have been disabled by the administrator.' + visit new_project_path + click_link 'Create blank project' + + expect(page).to have_content 'Visibility settings have been disabled by the administrator.' + end end end diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb index ae459197b38..14b01cb63d2 100644 --- a/spec/features/projects/pages/user_adds_domain_spec.rb +++ b/spec/features/projects/pages/user_adds_domain_spec.rb @@ -178,7 +178,12 @@ RSpec.describe 'User adds pages domain', :js, feature_category: :pages do visit project_pages_path(project) within('#content-body') { click_link 'Edit' } - expect(page).to have_field :domain_dns, with: "#{domain.domain} ALIAS namespace1.example.com." + expect(page).to have_field :domain_dns, with: format( + "%{domain} ALIAS %{namespace}.%{pages_host}.", + domain: domain.domain, + namespace: domain.project.root_namespace.path, + pages_host: Settings.pages.host + ) end end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 25eddf64f99..26fcd8ca3ca 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -559,7 +559,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do find(dropdown_selector).click within('.js-builds-dropdown-list') do - build_element = page.find('.mini-pipeline-graph-dropdown-item') + build_element = page.find('.pipeline-job-item') expect(build_element['title']).to eq('build - failed - (unknown failure)') end end @@ -818,7 +818,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do describe 'when the `ios_specific_templates` experiment is enabled and the "Set up a runner" button is clicked' do before do stub_experiments(ios_specific_templates: :candidate) - create(:project_setting, project: project, target_platforms: %w(ios)) + project.project_setting.update!(target_platforms: %w(ios)) visit project_pipelines_path(project) click_button 'Set up a runner' end diff --git a/spec/features/projects/settings/access_tokens_spec.rb b/spec/features/projects/settings/access_tokens_spec.rb index 210815f341c..9025bd9052e 100644 --- a/spec/features/projects/settings/access_tokens_spec.rb +++ b/spec/features/projects/settings/access_tokens_spec.rb @@ -49,6 +49,7 @@ RSpec.describe 'Project > Settings > Access Tokens', :js, feature_category: :use it 'shows Owner option' do visit resource_settings_access_tokens_path + click_button 'Add new token' expect(role_dropdown_options).to include('Owner') end end @@ -63,6 +64,7 @@ RSpec.describe 'Project > Settings > Access Tokens', :js, feature_category: :use it 'does not show Owner option for a maintainer' do visit resource_settings_access_tokens_path + click_button 'Add new token' expect(role_dropdown_options).not_to include('Owner') end end @@ -81,6 +83,7 @@ RSpec.describe 'Project > Settings > Access Tokens', :js, feature_category: :use it 'shows access token creation form and text' do visit project_settings_access_tokens_path(personal_project) + click_button 'Add new token' expect(page).to have_selector('#js-new-access-token-form') end end diff --git a/spec/features/projects/settings/packages_settings_spec.rb b/spec/features/projects/settings/packages_settings_spec.rb index 564a71e9a23..5277ede8e52 100644 --- a/spec/features/projects/settings/packages_settings_spec.rb +++ b/spec/features/projects/settings/packages_settings_spec.rb @@ -35,7 +35,7 @@ RSpec.describe 'Projects > Settings > Packages', :js, feature_category: :groups_ let(:packages_enabled) { false } it 'does not show up in UI' do - expect(page).not_to have_selector('[data-testid="toggle-label"]', text: 'Packages') + expect(page).not_to have_selector('[data-testid="toggle-label"]', text: 'Package registry') end end end diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb index ef1c03f4f27..59d9e6d105e 100644 --- a/spec/features/projects/settings/pipelines_settings_spec.rb +++ b/spec/features/projects/settings/pipelines_settings_spec.rb @@ -65,6 +65,46 @@ RSpec.describe "Projects > Settings > Pipelines settings", feature_category: :gr expect(checkbox).not_to be_checked end + it 'disables forward deployment rollback allowed when forward deployment enabled is unchecked', :js do + visit project_settings_ci_cd_path(project) + + forward_deployment_checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_enabled') + forward_deployment_rollback_checkbox = + find_field('project_ci_cd_settings_attributes_forward_deployment_rollback_allowed') + expect(forward_deployment_checkbox).to be_checked + expect(forward_deployment_rollback_checkbox).not_to be_disabled + + forward_deployment_checkbox.click + + expect(forward_deployment_rollback_checkbox).to be_disabled + + forward_deployment_checkbox.click + + expect(forward_deployment_rollback_checkbox).not_to be_disabled + end + + it 'updates forward_deployment_rollback_allowed' do + visit project_settings_ci_cd_path(project) + + checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_rollback_allowed') + expect(checkbox).to be_checked + + checkbox.set(false) + + page.within '#js-general-pipeline-settings' do + click_on 'Save changes' + end + + expect(page.status_code).to eq(200) + + page.within '#js-general-pipeline-settings' do + expect(page).to have_button('Save changes', disabled: false) + end + + checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_rollback_allowed') + expect(checkbox).not_to be_checked + end + describe 'Auto DevOps' do context 'when auto devops is turned on instance-wide' do before do diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb index 1f170300155..a66bf5cd3a9 100644 --- a/spec/features/projects/settings/project_badges_spec.rb +++ b/spec/features/projects/settings/project_badges_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do page.within '.badge-settings' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 expect(rows[0]).to have_content group_badge.link_url expect(rows[1]).to have_content project_badge.link_url @@ -33,6 +33,7 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do context 'adding a badge', :js do it 'user can preview a badge' do + click_button 'Add badge' page.within '.badge-settings form' do fill_in 'badge-link-url', with: badge_link_url fill_in 'badge-image-url', with: badge_image_url @@ -44,6 +45,7 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do end it do + click_button 'Add badge' page.within '.badge-settings' do fill_in 'badge-link-url', with: badge_link_url fill_in 'badge-image-url', with: badge_image_url @@ -51,7 +53,7 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do click_button 'Add badge' wait_for_requests - within '.card-body' do + within '.gl-card-body' do expect(find('a')[:href]).to eq badge_link_url expect(find('a img')[:src]).to eq badge_image_url end @@ -63,32 +65,35 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do it 'form is shown when clicking edit button in list' do page.within '.badge-settings' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 rows[1].find('[aria-label="Edit"]').click + end - within 'form' do - expect(find('#badge-link-url').value).to eq project_badge.link_url - expect(find('#badge-image-url').value).to eq project_badge.image_url - end + page.within '.gl-modal' do + expect(find('#badge-link-url').value).to eq project_badge.link_url + expect(find('#badge-image-url').value).to eq project_badge.image_url end end it 'updates a badge when submitting the edit form' do page.within '.badge-settings' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 rows[1].find('[aria-label="Edit"]').click - within 'form' do - fill_in 'badge-link-url', with: badge_link_url - fill_in 'badge-image-url', with: badge_image_url + end - click_button 'Save changes' - wait_for_requests - end + page.within '.gl-modal' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url - rows = all('.card-body > div') + click_button 'Save changes' + wait_for_requests + end + + page.within '.badge-settings' do + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 expect(rows[1]).to have_content badge_link_url end @@ -102,7 +107,7 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do it 'shows a modal when deleting a badge' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 click_delete_button(rows[1]) @@ -112,14 +117,14 @@ RSpec.describe 'Project Badges', feature_category: :groups_and_projects do it 'deletes a badge when confirming the modal' do wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 2 click_delete_button(rows[1]) find('.modal .btn-danger').click wait_for_requests - rows = all('.card-body > div') + rows = all('.gl-card-body tbody tr') expect(rows.length).to eq 1 expect(rows[0]).to have_content group_badge.link_url end diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index d53aefe5a4e..838ac67ee3d 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -156,6 +156,7 @@ RSpec.describe 'Projects > Settings > Repository settings', feature_category: :g before do visit project_settings_repository_path(project) + click_button 'Add new' end it 'shows push mirror settings', :js do diff --git a/spec/features/projects/settings/secure_files_spec.rb b/spec/features/projects/settings/secure_files_spec.rb index 7ff1a5f3568..5f94e215a5f 100644 --- a/spec/features/projects/settings/secure_files_spec.rb +++ b/spec/features/projects/settings/secure_files_spec.rb @@ -46,7 +46,7 @@ RSpec.describe 'Secure Files', :js, feature_category: :groups_and_projects do within '#js-secure-files' do expect(page).to have_content(file.name) - find('button.btn-danger-secondary').click + find('[data-testid="delete-button"]').click end expect(page).to have_content("Delete #{file.name}?") diff --git a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb index 0006762a971..4e8f42ae792 100644 --- a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb +++ b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb @@ -91,6 +91,7 @@ RSpec.describe "User interacts with deploy keys", :js, feature_category: :groups deploy_key_title = attributes_for(:key)[:title] deploy_key_body = attributes_for(:key)[:key] + click_button("Add new key") fill_in("deploy_key_title", with: deploy_key_title) fill_in("deploy_key_key", with: deploy_key_body) @@ -102,6 +103,16 @@ RSpec.describe "User interacts with deploy keys", :js, feature_category: :groups expect(page).to have_content(deploy_key_title) end end + + it "click on cancel hides the form" do + click_button("Add new key") + + expect(page).to have_css('.gl-new-card-add-form') + + click_button("Cancel") + + expect(page).not_to have_css('.gl-new-card-add-form') + end end context "attaching existing keys" do diff --git a/spec/features/projects/settings/user_renames_a_project_spec.rb b/spec/features/projects/settings/user_renames_a_project_spec.rb index 5a58c049601..b7ae595a3a9 100644 --- a/spec/features/projects/settings/user_renames_a_project_spec.rb +++ b/spec/features/projects/settings/user_renames_a_project_spec.rb @@ -61,7 +61,7 @@ RSpec.describe 'Projects > Settings > User renames a project', feature_category: it 'shows error for invalid project name' do change_name(project, '🧮 foo bar ☁️') expect(page).to have_field 'Project name', with: '🧮 foo bar ☁️' - expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'." + expect(page).not_to have_content "Name can contain only letters, digits, emoji '_', '.', dash and space. It must start with letter, digit, emoji or '_'." end 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 index 978b678c334..1ca4b761788 100644 --- a/spec/features/projects/settings/user_searches_in_settings_spec.rb +++ b/spec/features/projects/settings/user_searches_in_settings_spec.rb @@ -31,7 +31,7 @@ RSpec.describe 'User searches project settings', :js, feature_category: :groups_ visit project_settings_access_tokens_path(project) end - it_behaves_like 'can highlight results', 'Expiration date' + it_behaves_like 'can highlight results', 'Token name' end context 'in Repository page' do diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb index 7d41b60199c..890f514d3da 100644 --- a/spec/features/projects/settings/visibility_settings_spec.rb +++ b/spec/features/projects/settings/visibility_settings_spec.rb @@ -30,11 +30,11 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js, feature_categor context 'disable email notifications' do it 'is visible' do - expect(page).to have_selector('.js-emails-disabled', visible: true) + expect(page).to have_selector('.js-emails-enabled', visible: true) end it 'accepts the changed state' do - find('.js-emails-disabled input[type="checkbox"]').click + find('.js-emails-enabled input[type="checkbox"]').click expect { save_permissions_group }.to change { updated_emails_disabled? }.to(true) end @@ -59,7 +59,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js, feature_categor context 'disable email notifications' do it 'is not available' do - expect(page).not_to have_selector('.js-emails-disabled', visible: true) + expect(page).not_to have_selector('.js-emails-enabled', visible: true) end 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 455b931e7f3..bbf31c1e1e1 100644 --- a/spec/features/projects/show/user_manages_notifications_spec.rb +++ b/spec/features/projects/show/user_manages_notifications_spec.rb @@ -77,7 +77,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js, feature_cate end context 'when project emails are disabled' do - let(:project) { create(:project, :public, :repository, emails_disabled: true) } + let_it_be(:project) { create(:project, :public, :repository, emails_enabled: false) } it 'is disabled' do visit project_path(project) diff --git a/spec/features/projects/user_changes_project_visibility_spec.rb b/spec/features/projects/user_changes_project_visibility_spec.rb index f27a659f65f..24f24229f9c 100644 --- a/spec/features/projects/user_changes_project_visibility_spec.rb +++ b/spec/features/projects/user_changes_project_visibility_spec.rb @@ -66,8 +66,8 @@ RSpec.describe 'User changes public project visibility', :js, feature_category: let(:project) { create(:project, :empty_repo, :public) } it 'saves without confirmation' do - expect(page).to have_selector('.js-emails-disabled', visible: true) - find('.js-emails-disabled input[type="checkbox"]').click + expect(page).to have_selector('.js-emails-enabled', visible: true) + find('.js-emails-enabled input[type="checkbox"]').click page.within('#js-shared-permissions') do click_button 'Save changes' diff --git a/spec/features/projects/work_items/work_item_spec.rb b/spec/features/projects/work_items/work_item_spec.rb index e996a76b1c5..618d3e2efd0 100644 --- a/spec/features/projects/work_items/work_item_spec.rb +++ b/spec/features/projects/work_items/work_item_spec.rb @@ -40,7 +40,7 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do end it_behaves_like 'work items title' - it_behaves_like 'work items status' + it_behaves_like 'work items toggle status button' it_behaves_like 'work items assignees' it_behaves_like 'work items labels' it_behaves_like 'work items comments', :issue diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 9244cafbc0b..ee5d92b7cdb 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -40,6 +40,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem it 'allows to create a protected branch with name containing HTML tags' do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('foo<b>bar<\b>') click_on "Protect" @@ -89,6 +91,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem describe "explicit protected branches" do it "allows creating explicit protected branches" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some->branch') click_on "Protect" @@ -100,6 +104,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem it "shows success alert once protected branch is created" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some->branch') click_on "Protect" @@ -112,6 +118,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem project.repository.add_branch(admin, 'some-branch', commit.id) visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -124,6 +132,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem it "displays an error message if the named branch does not exist" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -135,6 +145,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem describe "wildcard protected branches" do it "allows creating protected branches with a wildcard" do visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -149,6 +161,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem project.repository.add_branch(admin, 'staging-stable', 'master') visit project_protected_branches_path(project) + + show_add_form set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -164,6 +178,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem project.repository.add_branch(admin, 'development', 'master') visit project_protected_branches_path(project) + + show_add_form set_protected_branch_name('*-stable') set_defaults click_on "Protect" diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb index 45315f53fd6..f5b463d63fa 100644 --- a/spec/features/protected_tags_spec.rb +++ b/spec/features/protected_tags_spec.rb @@ -15,6 +15,8 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c describe "explicit protected tags" do it "allows creating explicit protected tags" do visit project_protected_tags_path(project) + click_button('Add tag') + set_protected_tag_name('some-tag') set_allowed_to('create') click_on_protect @@ -29,6 +31,7 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c project.repository.add_tag(user, 'some-tag', commit.id) visit project_protected_tags_path(project) + click_button('Add tag') set_protected_tag_name('some-tag') set_allowed_to('create') click_on_protect @@ -38,6 +41,7 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c it "displays an error message if the named tag does not exist" do visit project_protected_tags_path(project) + click_button('Add tag') set_protected_tag_name('some-tag') set_allowed_to('create') click_on_protect @@ -49,6 +53,7 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c describe "wildcard protected tags" do it "allows creating protected tags with a wildcard" do visit project_protected_tags_path(project) + click_button('Add tag') set_protected_tag_name('*-stable') set_allowed_to('create') click_on_protect @@ -63,12 +68,16 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c project.repository.add_tag(user, 'staging-stable', 'master') visit project_protected_tags_path(project) + click_button('Add tag') set_protected_tag_name('*-stable') set_allowed_to('create') click_on_protect + within("#js-protected-tags-settings .gl-new-card-count") do + expect(page).to have_content("2") + end + within(".protected-tags-list") do - expect(page).to have_content("Protected tags (2)") expect(page).to have_content("2 matching tags") end end @@ -79,11 +88,13 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c project.repository.add_tag(user, 'development', 'master') visit project_protected_tags_path(project) + click_button('Add tag') set_protected_tag_name('*-stable') set_allowed_to('create') click_on_protect visit project_protected_tags_path(project) + click_button('Add tag') click_on "2 matching tags" within(".protected-tags-list") do diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 903211ec250..52df4bfece2 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -26,16 +26,18 @@ RSpec.describe 'Triggers', :js, feature_category: :continuous_integration do describe 'triggers page' do describe 'create trigger workflow' do it 'prevents adding new trigger with no description' do + click_button 'Add new token' fill_in 'trigger_description', with: '' - click_button 'Add trigger' + click_button 'Create pipeline trigger token' # See if input has error due to empty value expect(page.find('form.gl-show-field-errors .gl-field-error')).to be_visible end it 'adds new trigger with description' do + click_button 'Add new token' fill_in 'trigger_description', with: 'trigger desc' - click_button 'Add trigger' + click_button 'Create pipeline trigger token' aggregate_failures 'display creation notice and trigger is created' do expect(page.find('[data-testid="alert-info"]')).to have_content 'Trigger was created successfully.' @@ -100,6 +102,7 @@ RSpec.describe 'Triggers', :js, feature_category: :continuous_integration do describe 'show triggers workflow' do it 'contains trigger description placeholder' do + click_button 'Add new token' expect(page.find('#trigger_description')['placeholder']).to eq 'Trigger description' end 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 cc296259b80..cd181f73473 100644 --- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb +++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb @@ -6,34 +6,63 @@ RSpec.describe 'User uploads avatar to profile', feature_category: :user_profile let!(:user) { create(:user) } let(:avatar_file_path) { Rails.root.join('spec', 'fixtures', 'dk.png') } - before do - stub_feature_flags(edit_user_profile_vue: false) - sign_in user - visit profile_path - end + shared_examples 'upload avatar' do + it 'shows the new avatar immediately in the header and setting sidebar', :js do + expect(page.find('.avatar-image .gl-avatar')['src']).not_to include( + "/uploads/-/system/user/avatar/#{user.id}/avatar.png" + ) + find('.js-user-avatar-input', visible: false).set(avatar_file_path) + + click_button 'Set new profile picture' + click_button 'Update profile settings' - it 'they see their new avatar on their profile' do - attach_file('user_avatar', avatar_file_path, visible: false) - click_button 'Update profile settings' + wait_for_all_requests - visit user_path(user) + data_uri = find('.avatar-image .gl-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 + + visit profile_path + + expect(page.find('.avatar-image .gl-avatar')['src']).to include( + "/uploads/-/system/user/avatar/#{user.id}/avatar.png" + ) + end + end - expect(page).to have_selector(%(img[src$="/uploads/-/system/user/avatar/#{user.id}/dk.png?width=96"])) + context 'with "edit_user_profile_vue" turned on' do + before do + sign_in_and_visit_profile + end - # Cheating here to verify something that isn't user-facing, but is important - expect(user.reload.avatar.file).to exist + it_behaves_like 'upload avatar' end - 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) + context 'with "edit_user_profile_vue" turned off' do + before do + stub_feature_flags(edit_user_profile_vue: false) + sign_in_and_visit_profile + end - click_button 'Set new profile picture' - click_button 'Update profile settings' + it 'they see their new avatar on their profile' do + attach_file('user_avatar', avatar_file_path, visible: false) + click_button 'Update profile settings' - wait_for_all_requests + visit user_path(user) - data_uri = find('.avatar-image .gl-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 + expect(page).to have_selector(%(img[src$="/uploads/-/system/user/avatar/#{user.id}/dk.png?width=96"])) + + # Cheating here to verify something that isn't user-facing, but is important + expect(user.reload.avatar.file).to exist + end + + it_behaves_like 'upload avatar' + end + + private + + def sign_in_and_visit_profile + sign_in user + visit profile_path end end diff --git a/spec/features/users/email_verification_on_login_spec.rb b/spec/features/users/email_verification_on_login_spec.rb index 1854e812b73..7675de28f86 100644 --- a/spec/features/users/email_verification_on_login_spec.rb +++ b/spec/features/users/email_verification_on_login_spec.rb @@ -2,10 +2,12 @@ require 'spec_helper' -RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, feature_category: :system_access do +RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, :js, feature_category: :system_access do include EmailHelpers - let_it_be(:user) { create(:user) } + let_it_be_with_reload(:user) { create(:user) } + let_it_be(:another_user) { create(:user) } + let_it_be(:new_email) { build_stubbed(:user).email } let(:require_email_verification_enabled) { user } @@ -33,7 +35,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, # Expect to see the verification form on the login page expect(page).to have_current_path(new_user_session_path) - expect(page).to have_content('Help us protect your account') + expect(page).to have_content(s_('IdentityVerification|Help us protect your account')) # Expect an instructions email to be sent with a code code = expect_instructions_email_and_extract_code @@ -41,7 +43,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, # Signing in again prompts for the code and doesn't send a new one gitlab_sign_in(user) expect(page).to have_current_path(new_user_session_path) - expect(page).to have_content('Help us protect your account') + expect(page).to have_content(s_('IdentityVerification|Help us protect your account')) # Verify the code verify_code(code) @@ -54,7 +56,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, # Expect a confirmation page with a meta refresh tag for 3 seconds to the root expect(page).to have_current_path(users_successful_verification_path) - expect(page).to have_content('Verification successful') + expect(page).to have_content(s_('IdentityVerification|Verification successful')) expect(page).to have_selector("meta[http-equiv='refresh'][content='3; url=#{root_path}']", visible: false) end end @@ -69,7 +71,8 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, code = expect_instructions_email_and_extract_code # Request a new code - click_link 'Resend code' + click_button s_('IdentityVerification|Resend code') + expect(page).to have_content(s_('IdentityVerification|A new code has been sent.')) expect_log_message('Instructions Sent', 2) new_code = expect_instructions_email_and_extract_code @@ -83,22 +86,63 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, gitlab_sign_in(user) # It shows a resend button - expect(page).to have_link 'Resend code' + expect(page).to have_button s_('IdentityVerification|Resend code') # Resend more than the rate limited amount of times 10.times do - click_link 'Resend code' + click_button s_('IdentityVerification|Resend code') end - # Expect the link to be gone - expect(page).not_to have_link 'Resend code' + # Expect an error alert + expect(page).to have_content format(s_("IdentityVerification|You've reached the maximum amount of resends. "\ + 'Wait %{interval} and try again.'), interval: 'about 1 hour') + end + end - # Wait for 1 hour - travel 1.hour + describe 'updating the email address' do + it 'offers to update the email address' do + perform_enqueued_jobs do + # When logging in + gitlab_sign_in(user) - # Now it's visible again - gitlab_sign_in(user) - expect(page).to have_link 'Resend code' + # Expect an instructions email to be sent with a code + code = expect_instructions_email_and_extract_code + + # It shows an update email button + expect(page).to have_button s_('IdentityVerification|Update email') + + # Click Update email button + click_button s_('IdentityVerification|Update email') + + # Try to update with another user's email address + fill_in _('Email'), with: another_user.email + click_button s_('IdentityVerification|Update email') + expect(page).to have_content('Email has already been taken') + + # Update to a unique email address + fill_in _('Email'), with: new_email + click_button s_('IdentityVerification|Update email') + expect(page).to have_content(s_('IdentityVerification|A new code has been sent to ' \ + 'your updated email address.')) + expect_log_message('Instructions Sent', 2) + + new_code = expect_email_changed_notification_to_old_address_and_instructions_email_to_new_address + + # Verify the old code is different from the new code + expect(code).not_to eq(new_code) + verify_code(new_code) + + # Expect the user to be unlocked + expect_user_to_be_unlocked + expect_user_to_be_confirmed + + # When logging in again + gitlab_sign_out + gitlab_sign_in(user) + + # It does not show an update email button anymore + expect(page).not_to have_button s_('IdentityVerification|Update email') + end end end @@ -118,8 +162,9 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, # Expect an error message expect_log_message('Failed Attempt', reason: 'rate_limited') - expect(page).to have_content("You've reached the maximum amount of tries. "\ - 'Wait 10 minutes or send a new code and try again.') + expect(page).to have_content( + format(s_("IdentityVerification|You've reached the maximum amount of tries. "\ + 'Wait %{interval} or send a new code and try again.'), interval: '10 minutes')) # Wait for 10 minutes travel 10.minutes @@ -139,7 +184,8 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, # Expect an error message expect_log_message('Failed Attempt', reason: 'invalid') - expect(page).to have_content('The code is incorrect. Enter it again, or send a new code.') + expect(page).to have_content(s_('IdentityVerification|The code is incorrect. '\ + 'Enter it again, or send a new code.')) end it 'verifies expired codes' do @@ -156,7 +202,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, # Expect an error message expect_log_message('Failed Attempt', reason: 'expired') - expect(page).to have_content('The code has expired. Send a new code and try again.') + expect(page).to have_content(s_('IdentityVerification|The code has expired. Send a new code and try again.')) end end end @@ -250,7 +296,8 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, it 'shows an error message on on the login page' do expect(page).to have_current_path(new_user_session_path) - expect(page).to have_content('Maximum login attempts exceeded. Wait 10 minutes and try again.') + expect(page).to have_content(format(s_('IdentityVerification|Maximum login attempts exceeded. '\ + 'Wait %{interval} and try again.'), interval: '10 minutes')) end end @@ -271,7 +318,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, stub_feature_flags(require_email_verification: false) # Resending and veryfying the code work as expected - click_link 'Resend code' + click_button s_('IdentityVerification|Resend code') new_code = expect_instructions_email_and_extract_code verify_code(code) @@ -283,7 +330,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, verify_code(new_code) expect(page).to have_content(s_('IdentityVerification|The code has expired. Send a new code and try again.')) - click_link 'Resend code' + click_button s_('IdentityVerification|Resend code') another_code = expect_instructions_email_and_extract_code verify_code(another_code) @@ -341,6 +388,28 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting, end end + def expect_user_to_be_confirmed + aggregate_failures do + expect(user.email).to eq(new_email) + expect(user.unconfirmed_email).to be_nil + end + end + + def expect_email_changed_notification_to_old_address_and_instructions_email_to_new_address + changed_email = ActionMailer::Base.deliveries[0] + instructions_email = ActionMailer::Base.deliveries[1] + + expect(changed_email.to).to match_array([user.email]) + expect(changed_email.subject).to eq('Email Changed') + + expect(instructions_email.to).to match_array([new_email]) + expect(instructions_email.subject).to eq(s_('IdentityVerification|Verify your identity')) + + reset_delivered_emails! + + instructions_email.body.parts.first.to_s[/\d{#{Users::EmailVerification::GenerateTokenService::TOKEN_LENGTH}}/o] + end + def expect_instructions_email_and_extract_code mail = find_email_for(user) expect(mail.to).to match_array([user.email]) diff --git a/spec/features/users/google_syndication_csp_spec.rb b/spec/features/users/google_syndication_csp_spec.rb new file mode 100644 index 00000000000..e71539f87c8 --- /dev/null +++ b/spec/features/users/google_syndication_csp_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Google Syndication content security policy', feature_category: :purchase do + include ContentSecurityPolicyHelpers + + let_it_be(:connect_src) { 'https://other-cdn.test' } + + let_it_be(:google_analytics_src) do + 'localhost https://cdn.cookielaw.org https://*.onetrust.com *.google-analytics.com ' \ + '*.analytics.google.com *.googletagmanager.com' + end + + let_it_be(:allowed_src) do + '*.google.com/pagead/landing pagead2.googlesyndication.com/pagead/landing' + end + + let(:extra) { { google_tag_manager_nonce_id: 'google_tag_manager_nonce_id' } } + + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.connect_src(*connect_src.split) + end + end + + subject { response_headers['Content-Security-Policy'] } + + before do + setup_csp_for_controller(SessionsController, csp, any_time: true) + stub_config(extra: extra) + visit new_user_session_path + end + + context 'when self-hosted' do + context 'when there is no CSP config' do + let(:extra) { {} } + let(:csp) { ActionDispatch::ContentSecurityPolicy.new } + + it { is_expected.to be_blank } + end + + context 'when connect-src CSP config exists' do + it { is_expected.to include("connect-src #{connect_src} #{google_analytics_src}") } + it { is_expected.not_to include(allowed_src) } + end + end + + context 'when SaaS', :saas do + context 'when connect-src CSP config exists' do + it { is_expected.to include("connect-src #{connect_src} #{google_analytics_src} #{allowed_src}") } + end + end +end diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb index 39b6d049e43..2db58ce04a1 100644 --- a/spec/features/users/rss_spec.rb +++ b/spec/features/users/rss_spec.rb @@ -6,28 +6,53 @@ RSpec.describe 'User RSS', feature_category: :user_profile do let(:user) { create(:user) } let(:path) { user_path(create(:user)) } - before do - stub_feature_flags(user_profile_overflow_menu_vue: false) - end - - context 'when signed in' do + describe 'with "user_profile_overflow_menu_vue" feature flag off' do before do - sign_in(user) - visit path + stub_feature_flags(user_profile_overflow_menu_vue: false) end - it_behaves_like "it has an RSS button with current_user's feed token" - end + context 'when signed in' do + before do + sign_in(user) + visit path + end - context 'when signed out' do - before do - visit path + it_behaves_like "it has an RSS button with current_user's feed token" end - it_behaves_like "it has an RSS button without a feed token" + context 'when signed out' do + before do + visit path + end + + it_behaves_like "it has an RSS button without a feed token" + end end - # TODO: implement tests before the FF "user_profile_overflow_menu_vue" is turned on - # See: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122971 - # Related Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/416974 + describe 'with "user_profile_overflow_menu_vue" feature flag on', :js do + context 'when signed in' do + before do + sign_in(user) + visit path + end + + it 'shows the RSS link with overflow menu' do + find('[data-testid="base-dropdown-toggle"').click + + expect(page).to have_link 'Subscribe', href: /feed_token=glft-.*-#{user.id}/ + end + end + + context 'when signed out' do + before do + visit path + end + + it 'has an RSS without a feed token' do + find('[data-testid="base-dropdown-toggle"').click + + expect(page).not_to have_link 'Subscribe', href: /feed_token=glft-.*-#{user.id}/ + end + end + end end diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index 850dd0bbc5d..450b9fa46b1 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -36,7 +36,7 @@ RSpec.shared_examples 'Signup name validation' do |field, max_length, label| it 'shows an error message if the username contains emojis' do simulate_input("##{field}", 'Ehsan 🦋') - expect(page).to have_content("Invalid input, please avoid emojis") + expect(page).to have_content("Invalid input, please avoid emoji") end end end @@ -176,7 +176,7 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do it 'shows an error message if the username contains emojis' do simulate_input('#new_user_username', 'ehsan😀') - expect(page).to have_content("Invalid input, please avoid emojis") + expect(page).to have_content("Invalid input, please avoid emoji") end it 'shows a pending message if the username availability is being fetched', |