diff options
Diffstat (limited to 'spec/features')
74 files changed, 1299 insertions, 889 deletions
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb index e40f4c4678c..875eb9dd0ce 100644 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -22,9 +22,8 @@ RSpec.describe 'Admin Broadcast Messages' do it 'creates a customized broadcast banner message' do fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' - fill_in 'broadcast_message_color', with: '#f2dede' fill_in 'broadcast_message_target_path', with: '*/user_onboarded' - fill_in 'broadcast_message_font', with: '#b94a48' + select 'light-indigo', from: 'broadcast_message_theme' select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' check 'Guest' check 'Owner' @@ -35,7 +34,7 @@ RSpec.describe 'Admin Broadcast Messages' do expect(page).to have_content 'Guest, Owner' expect(page).to have_content '*/user_onboarded' expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' - expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) + expect(page).to have_selector %(.light-indigo[role=alert]) end it 'creates a customized broadcast notification message' do @@ -90,7 +89,7 @@ RSpec.describe 'Admin Broadcast Messages' do fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" select 'Notification', from: 'broadcast_message_broadcast_type' - page.within('.js-broadcast-notification-message-preview') do + page.within('#broadcast-message-preview') do expect(page).to have_selector('strong', text: 'Markdown') expect(page).to have_emoji('tada') end diff --git a/spec/features/admin/admin_dev_ops_report_spec.rb b/spec/features/admin/admin_dev_ops_reports_spec.rb index cee79f8f440..bf32819cb52 100644 --- a/spec/features/admin/admin_dev_ops_report_spec.rb +++ b/spec/features/admin/admin_dev_ops_reports_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'DevOps Report page', :js do end it 'has dismissable intro callout' do - visit admin_dev_ops_report_path + visit admin_dev_ops_reports_path expect(page).to have_content 'Introducing Your DevOps Report' @@ -32,13 +32,13 @@ RSpec.describe 'DevOps Report page', :js do end it 'shows empty state' do - visit admin_dev_ops_report_path + visit admin_dev_ops_reports_path expect(page).to have_text('Service ping is off') end it 'hides the intro callout' do - visit admin_dev_ops_report_path + visit admin_dev_ops_reports_path expect(page).not_to have_content 'Introducing Your DevOps Report' end @@ -48,7 +48,7 @@ RSpec.describe 'DevOps Report page', :js do it 'shows empty state' do stub_application_setting(usage_ping_enabled: true) - visit admin_dev_ops_report_path + visit admin_dev_ops_reports_path expect(page).to have_content('Data is still calculating') end @@ -59,7 +59,7 @@ RSpec.describe 'DevOps Report page', :js do stub_application_setting(usage_ping_enabled: true) create(:dev_ops_report_metric) - visit admin_dev_ops_report_path + visit admin_dev_ops_reports_path expect(page).to have_selector('[data-testid="devops-score-app"]') end diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 3f0c7e64a1f..7fe49c2571c 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -3,65 +3,71 @@ require 'spec_helper' RSpec.describe "Admin Runners" do - include StubENV - include Spec::Support::Helpers::ModalHelpers + include Spec::Support::Helpers::Features::RunnersHelpers + + let_it_be(:admin) { create(:admin) } before do - stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') - admin = create(:admin) sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) wait_for_requests end - describe "Runners page", :js do + describe "Admin Runners page", :js do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:namespace) { create(:namespace) } let_it_be(:project) { create(:project, namespace: namespace, creator: user) } - context "when there are runners" do - it 'has all necessary texts' do - create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: Time.zone.now) - create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.week.ago) - create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.year.ago) - + context "runners registration" do + before do visit admin_runners_path - - expect(page).to have_text "Register an instance runner" - expect(page).to have_text "Online runners 1" - expect(page).to have_text "Offline runners 2" - expect(page).to have_text "Stale runners 1" end - it 'with an instance runner shows an instance badge' do - runner = create(:ci_runner, :instance) + it_behaves_like "shows and resets runner registration token" do + let(:dropdown_text) { 'Register an instance runner' } + let(:registration_token) { Gitlab::CurrentSettings.runners_registration_token } + end + end - visit admin_runners_path + context "when there are runners" do + context "with an instance runner" do + let!(:instance_runner) { create(:ci_runner, :instance) } - within "[data-testid='runner-row-#{runner.id}']" do - expect(page).to have_selector '.badge', text: 'shared' + before do + visit admin_runners_path end - end - it 'with a group runner shows a group badge' do - runner = create(:ci_runner, :group, groups: [group]) + it_behaves_like 'shows runner in list' do + let(:runner) { instance_runner } + end - visit admin_runners_path + it_behaves_like 'pauses, resumes and deletes a runner' do + let(:runner) { instance_runner } + end - within "[data-testid='runner-row-#{runner.id}']" do - expect(page).to have_selector '.badge', text: 'group' + it 'shows an instance badge' do + within_runner_row(instance_runner.id) do + expect(page).to have_selector '.badge', text: 'shared' + end end end - it 'with a project runner shows a project badge' do - runner = create(:ci_runner, :project, projects: [project]) + context "with multiple runners" do + before do + create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: Time.zone.now) + create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.week.ago) + create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.year.ago) - visit admin_runners_path + visit admin_runners_path + end - within "[data-testid='runner-row-#{runner.id}']" do - expect(page).to have_selector '.badge', text: 'specific' + it 'has all necessary texts' do + expect(page).to have_text "Register an instance runner" + expect(page).to have_text "Online runners 1" + expect(page).to have_text "Offline runners 2" + expect(page).to have_text "Stale runners 1" end end @@ -73,44 +79,8 @@ RSpec.describe "Admin Runners" do visit admin_runners_path - within "[data-testid='runner-row-#{runner.id}'] [data-label='Jobs']" do - expect(page).to have_content '2' - end - end - - describe 'delete runner' do - let!(:runner) { create(:ci_runner, description: 'runner-foo') } - - before do - visit admin_runners_path - - within "[data-testid='runner-row-#{runner.id}']" do - click_on 'Delete runner' - end - end - - it 'shows a confirmation modal' do - expect(page).to have_text "Delete runner ##{runner.id} (#{runner.short_sha})?" - expect(page).to have_text "Are you sure you want to continue?" - end - - it 'deletes a runner' do - within '.modal' do - click_on 'Delete runner' - end - - expect(page.find('.gl-toast')).to have_text(/Runner .+ deleted/) - expect(page).not_to have_content 'runner-foo' - end - - it 'cancels runner deletion' do - within '.modal' do - click_on 'Cancel' - end - - wait_for_requests - - expect(page).to have_content 'runner-foo' + within_runner_row(runner.id) do + expect(find("[data-label='Jobs']")).to have_content '2' end end @@ -154,35 +124,69 @@ RSpec.describe "Admin Runners" do end end + describe 'filter by paused' do + before do + create(:ci_runner, :instance, description: 'runner-active') + create(:ci_runner, :instance, description: 'runner-paused', active: false) + + visit admin_runners_path + end + + it 'shows all runners' do + expect(page).to have_link('All 2') + + expect(page).to have_content 'runner-active' + expect(page).to have_content 'runner-paused' + end + + it 'shows paused runners' do + input_filtered_search_filter_is_only('Paused', 'Yes') + + expect(page).to have_link('All 1') + + expect(page).not_to have_content 'runner-active' + expect(page).to have_content 'runner-paused' + end + + it 'shows active runners' do + input_filtered_search_filter_is_only('Paused', 'No') + + expect(page).to have_link('All 1') + + expect(page).to have_content 'runner-active' + expect(page).not_to have_content 'runner-paused' + end + end + describe 'filter by status' do let!(:never_contacted) { create(:ci_runner, :instance, description: 'runner-never-contacted', contacted_at: nil) } before do create(:ci_runner, :instance, description: 'runner-1', contacted_at: Time.zone.now) create(:ci_runner, :instance, description: 'runner-2', contacted_at: Time.zone.now) - create(:ci_runner, :instance, description: 'runner-paused', active: false, contacted_at: Time.zone.now) + create(:ci_runner, :instance, description: 'runner-offline', contacted_at: 1.week.ago) visit admin_runners_path end it 'shows all runners' do + expect(page).to have_link('All 4') + expect(page).to have_content 'runner-1' expect(page).to have_content 'runner-2' - expect(page).to have_content 'runner-paused' + expect(page).to have_content 'runner-offline' expect(page).to have_content 'runner-never-contacted' - - expect(page).to have_link('All 4') end it 'shows correct runner when status matches' do - input_filtered_search_filter_is_only('Status', 'Active') + input_filtered_search_filter_is_only('Status', 'Online') - expect(page).to have_link('All 3') + expect(page).to have_link('All 2') expect(page).to have_content 'runner-1' expect(page).to have_content 'runner-2' - expect(page).to have_content 'runner-never-contacted' - expect(page).not_to have_content 'runner-paused' + expect(page).not_to have_content 'runner-offline' + expect(page).not_to have_content 'runner-never-contacted' end it 'shows no runner when status does not match' do @@ -194,15 +198,15 @@ RSpec.describe "Admin Runners" do end it 'shows correct runner when status is selected and search term is entered' do - input_filtered_search_filter_is_only('Status', 'Active') + input_filtered_search_filter_is_only('Status', 'Online') input_filtered_search_keys('runner-1') expect(page).to have_link('All 1') expect(page).to have_content 'runner-1' expect(page).not_to have_content 'runner-2' + expect(page).not_to have_content 'runner-offline' expect(page).not_to have_content 'runner-never-contacted' - expect(page).not_to have_content 'runner-paused' end it 'shows correct runner when status filter is entered' do @@ -216,7 +220,7 @@ RSpec.describe "Admin Runners" do expect(page).not_to have_content 'runner-paused' expect(page).to have_content 'runner-never-contacted' - within "[data-testid='runner-row-#{never_contacted.id}']" do + within_runner_row(never_contacted.id) do expect(page).to have_selector '.badge', text: 'never contacted' end end @@ -308,7 +312,7 @@ RSpec.describe "Admin Runners" do visit admin_runners_path - input_filtered_search_filter_is_only('Status', 'Active') + input_filtered_search_filter_is_only('Paused', 'No') expect(page).to have_content 'runner-project' expect(page).to have_content 'runner-group' @@ -330,6 +334,17 @@ RSpec.describe "Admin Runners" do create(:ci_runner, :instance, description: 'runner-red', tag_list: ['red']) end + it 'shows tags suggestions' do + visit admin_runners_path + + open_filtered_search_suggestions('Tags') + + page.within(search_bar_selector) do + expect(page).to have_content 'blue' + expect(page).to have_content 'red' + end + end + it 'shows correct runner when tag matches' do visit admin_runners_path @@ -403,15 +418,7 @@ RSpec.describe "Admin Runners" do visit admin_runners_path end - it 'has all necessary texts including no runner message' do - expect(page).to have_text "Register an instance runner" - - expect(page).to have_text "Online runners 0" - expect(page).to have_text "Offline runners 0" - expect(page).to have_text "Stale runners 0" - - expect(page).to have_text 'No runners found' - end + it_behaves_like "shows no runners" it 'shows tabs with total counts equal to 0' do expect(page).to have_link('All 0') @@ -427,65 +434,17 @@ RSpec.describe "Admin Runners" do expect(page).to have_current_path(admin_runners_path('status[]': 'NEVER_CONTACTED') ) end - end - describe 'runners registration' do - let!(:token) { Gitlab::CurrentSettings.runners_registration_token } - - before do - visit admin_runners_path + it 'updates ACTIVE runner status to paused=false' do + visit admin_runners_path('status[]': 'ACTIVE') - click_on 'Register an instance runner' + expect(page).to have_current_path(admin_runners_path('paused[]': 'false') ) end - describe 'show registration instructions' do - before do - click_on 'Show runner installation and registration instructions' - - wait_for_requests - end - - it 'opens runner installation modal' do - expect(page).to have_text "Install a runner" - - expect(page).to have_text "Environment" - expect(page).to have_text "Architecture" - expect(page).to have_text "Download and install binary" - end - - it 'dismisses runner installation modal' do - within_modal do - click_button('Close', match: :first) - end - - expect(page).not_to have_text "Install a runner" - end - end + it 'updates PAUSED runner status to paused=true' do + visit admin_runners_path('status[]': 'PAUSED') - it 'has a registration token' do - click_on 'Click to reveal' - expect(page.find('[data-testid="token-value"]')).to have_content(token) - end - - describe 'reset registration token' do - let(:page_token) { find('[data-testid="token-value"]').text } - - before do - click_on 'Reset registration token' - - within_modal do - click_button('Reset token', match: :first) - end - - wait_for_requests - end - - it 'changes registration token' do - click_on 'Register an instance runner' - - click_on 'Click to reveal' - expect(page_token).not_to eq token - end + expect(page).to have_current_path(admin_runners_path('paused[]': 'true') ) end end end @@ -637,47 +596,4 @@ RSpec.describe "Admin Runners" do end end end - - private - - def search_bar_selector - '[data-testid="runners-filtered-search"]' - end - - # The filters must be clicked first to be able to receive events - # See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1493 - def focus_filtered_search - page.within(search_bar_selector) do - page.find('.gl-filtered-search-term-token').click - end - end - - def input_filtered_search_keys(search_term) - focus_filtered_search - - page.within(search_bar_selector) do - page.find('input').send_keys(search_term) - click_on 'Search' - end - - wait_for_requests - end - - def input_filtered_search_filter_is_only(filter, value) - focus_filtered_search - - page.within(search_bar_selector) do - click_on filter - - # For OPERATOR_IS_ONLY, clicking the filter - # immediately preselects "=" operator - - page.find('input').send_keys(value) - page.find('input').send_keys(:enter) - - click_on 'Search' - end - - wait_for_requests - end end diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb index d05a09a79ef..432721d63ad 100644 --- a/spec/features/admin/admin_sees_background_migrations_spec.rb +++ b/spec/features/admin/admin_sees_background_migrations_spec.rb @@ -5,9 +5,9 @@ require 'spec_helper' RSpec.describe "Admin > Admin sees background migrations" do let_it_be(:admin) { create(:admin) } - let_it_be(:active_migration) { create(:batched_background_migration, table_name: 'active', status: :active) } - let_it_be(:failed_migration) { create(:batched_background_migration, table_name: 'failed', status: :failed, total_tuple_count: 100) } - let_it_be(:finished_migration) { create(:batched_background_migration, table_name: 'finished', status: :finished) } + let_it_be(:active_migration) { create(:batched_background_migration, :active, table_name: 'active') } + let_it_be(:failed_migration) { create(:batched_background_migration, :failed, table_name: 'failed', total_tuple_count: 100) } + let_it_be(:finished_migration) { create(:batched_background_migration, :finished, table_name: 'finished') } before_all do create(:batched_background_migration_job, :failed, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) @@ -81,7 +81,7 @@ RSpec.describe "Admin > Admin sees background migrations" do expect(page).to have_content(failed_migration.job_class_name) expect(page).to have_content(failed_migration.table_name) expect(page).to have_content('0.00%') - expect(page).to have_content(failed_migration.status.humanize) + expect(page).to have_content(failed_migration.status_name.to_s) click_button('Retry') expect(page).not_to have_content(failed_migration.job_class_name) @@ -106,7 +106,7 @@ RSpec.describe "Admin > Admin sees background migrations" do expect(page).to have_content(finished_migration.job_class_name) expect(page).to have_content(finished_migration.table_name) expect(page).to have_content('100.00%') - expect(page).to have_content(finished_migration.status.humanize) + expect(page).to have_content(finished_migration.status_name.to_s) end end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index df93bd773a6..4cdc3df978d 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -34,16 +34,16 @@ RSpec.describe 'Admin updates settings' do it 'uncheck all restricted visibility levels' do page.within('.as-visibility-access') do - find('#application_setting_visibility_level_0').set(false) - find('#application_setting_visibility_level_10').set(false) - find('#application_setting_visibility_level_20').set(false) + find('#application_setting_restricted_visibility_levels_0').set(false) + find('#application_setting_restricted_visibility_levels_10').set(false) + find('#application_setting_restricted_visibility_levels_20').set(false) click_button 'Save changes' end expect(page).to have_content "Application settings saved successfully" - expect(find('#application_setting_visibility_level_0')).not_to be_checked - expect(find('#application_setting_visibility_level_10')).not_to be_checked - expect(find('#application_setting_visibility_level_20')).not_to be_checked + expect(find('#application_setting_restricted_visibility_levels_0')).not_to be_checked + expect(find('#application_setting_restricted_visibility_levels_10')).not_to be_checked + expect(find('#application_setting_restricted_visibility_levels_20')).not_to be_checked end it 'modify import sources' do @@ -311,7 +311,9 @@ RSpec.describe 'Admin updates settings' do end context 'CI/CD page' do - it 'change CI/CD settings' do + let_it_be(:default_plan) { create(:default_plan) } + + it 'changes CI/CD settings' do visit ci_cd_admin_application_settings_path page.within('.as-ci-cd') do @@ -329,6 +331,33 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end + it 'changes CI/CD limits', :aggregate_failures do + visit ci_cd_admin_application_settings_path + + page.within('.as-ci-cd') do + fill_in 'plan_limits_ci_pipeline_size', with: 10 + fill_in 'plan_limits_ci_active_jobs', with: 20 + fill_in 'plan_limits_ci_active_pipelines', with: 25 + fill_in 'plan_limits_ci_project_subscriptions', with: 30 + fill_in 'plan_limits_ci_pipeline_schedules', with: 40 + fill_in 'plan_limits_ci_needs_size_limit', with: 50 + fill_in 'plan_limits_ci_registered_group_runners', with: 60 + fill_in 'plan_limits_ci_registered_project_runners', with: 70 + click_button 'Save Default limits' + end + + limits = default_plan.reload.limits + expect(limits.ci_pipeline_size).to eq(10) + expect(limits.ci_active_jobs).to eq(20) + expect(limits.ci_active_pipelines).to eq(25) + expect(limits.ci_project_subscriptions).to eq(30) + expect(limits.ci_pipeline_schedules).to eq(40) + expect(limits.ci_needs_size_limit).to eq(50) + expect(limits.ci_registered_group_runners).to eq(60) + expect(limits.ci_registered_project_runners).to eq(70) + expect(page).to have_content 'Application limits saved successfully' + end + context 'Runner Registration' do context 'when feature is enabled' do before do @@ -421,7 +450,7 @@ RSpec.describe 'Admin updates settings' do visit ci_cd_admin_application_settings_path page.within('.as-registry') do - find('#application_setting_container_registry_expiration_policies_caching.form-check-input').click + find('#application_setting_container_registry_expiration_policies_caching').click click_button 'Save changes' end @@ -489,8 +518,8 @@ RSpec.describe 'Admin updates settings' do page.within('.as-spam') do fill_in 'reCAPTCHA site key', with: 'key' fill_in 'reCAPTCHA private key', with: 'key' - check 'Enable reCAPTCHA' - check 'Enable reCAPTCHA for login' + find('#application_setting_recaptcha_enabled').set(true) + find('#application_setting_login_recaptcha_protection_enabled').set(true) fill_in 'IP addresses per user', with: 15 check 'Enable Spam Check via external API endpoint' fill_in 'URL of the external Spam Check endpoint', with: 'grpc://www.example.com/spamcheck' @@ -825,31 +854,45 @@ RSpec.describe 'Admin updates settings' do before do stub_usage_data_connections stub_database_flavor_check - - visit service_usage_data_admin_application_settings_path end - it 'loads usage ping payload on click', :js do - expected_payload_content = /(?=.*"uuid")(?=.*"hostname")/m + context 'when service data cached', :clean_gitlab_redis_cache do + before do + allow(Rails.cache).to receive(:exist?).with('usage_data').and_return(true) - expect(page).not_to have_content expected_payload_content + visit service_usage_data_admin_application_settings_path + end - click_button('Preview payload') + it 'loads usage ping payload on click', :js do + expected_payload_content = /(?=.*"uuid")(?=.*"hostname")/m - wait_for_requests + expect(page).not_to have_content expected_payload_content - expect(page).to have_button 'Hide payload' - expect(page).to have_content expected_payload_content - end + click_button('Preview payload') - it 'generates usage ping payload on button click', :js do - expect_next_instance_of(Admin::ApplicationSettingsController) do |instance| - expect(instance).to receive(:usage_data).and_call_original + wait_for_requests + + expect(page).to have_button 'Hide payload' + expect(page).to have_content expected_payload_content + end + + it 'generates usage ping payload on button click', :js do + expect_next_instance_of(Admin::ApplicationSettingsController) do |instance| + expect(instance).to receive(:usage_data).and_call_original + end + + click_button('Download payload') + + wait_for_requests end + end - click_button('Download payload') + context 'when service data not cached' do + it 'renders missing cache information' do + visit service_usage_data_admin_application_settings_path - wait_for_requests + expect(page).to have_text('Service Ping payload not found in the application cache') + end end end end diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 6643ebe82e6..15bc2318022 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -36,14 +36,14 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do click_on "1" # Scopes - check "api" + check "read_api" check "read_user" click_on "Create impersonation token" expect(active_impersonation_tokens).to have_text(name) expect(active_impersonation_tokens).to have_text('in') - expect(active_impersonation_tokens).to have_text('api') + expect(active_impersonation_tokens).to have_text('read_api') expect(active_impersonation_tokens).to have_text('read_user') expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1) expect(created_impersonation_token).not_to be_empty diff --git a/spec/features/admin/clusters/eks_spec.rb b/spec/features/admin/clusters/eks_spec.rb index 71d2bba73b1..4667f9c20a1 100644 --- a/spec/features/admin/clusters/eks_spec.rb +++ b/spec/features/admin/clusters/eks_spec.rb @@ -15,8 +15,8 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do before do visit admin_clusters_path - click_button 'Actions' - click_link 'Create a new cluster' + click_button(class: 'dropdown-toggle-split') + click_link 'Create a cluster (deprecated)' end context 'when user creates a cluster on AWS EKS' do diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 5dd627f3b76..bf976168bbe 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -282,7 +282,7 @@ RSpec.describe 'Project issue boards', :js do it 'shows issue count on the list' do page.within(find(".board:nth-child(2)")) do expect(page.find('[data-testid="board-items-count"]')).to have_text(total_planning_issues) - expect(page).not_to have_selector('.js-max-issue-size') + expect(page).not_to have_selector('.max-issue-size') end end end diff --git a/spec/features/boards/focus_mode_spec.rb b/spec/features/boards/focus_mode_spec.rb index 2bd1e625236..453a8d8870b 100644 --- a/spec/features/boards/focus_mode_spec.rb +++ b/spec/features/boards/focus_mode_spec.rb @@ -12,6 +12,6 @@ RSpec.describe 'Issue Boards focus mode', :js do end it 'shows focus mode button to anonymous users' do - expect(page).to have_selector('.js-focus-mode-btn') + expect(page).to have_button _('Toggle focus mode') end end diff --git a/spec/features/boards/multi_select_spec.rb b/spec/features/boards/multi_select_spec.rb index 9148fb23214..cad303a14e5 100644 --- a/spec/features/boards/multi_select_spec.rb +++ b/spec/features/boards/multi_select_spec.rb @@ -72,7 +72,7 @@ RSpec.describe 'Multi Select Issue', :js do wait_for_requests - page.within(all('.js-board-list')[2]) do + page.within(all('.board-list')[2]) do expect(find('.board-card:nth-child(1)')).to have_content(issue1.title) expect(find('.board-card:nth-child(2)')).to have_content(issue2.title) end @@ -87,7 +87,7 @@ RSpec.describe 'Multi Select Issue', :js do wait_for_requests - page.within(all('.js-board-list')[2]) do + page.within(all('.board-list')[2]) do expect(find('.board-card:nth-child(1)')).to have_content(issue1.title) expect(find('.board-card:nth-child(2)')).to have_content(issue2.title) expect(find('.board-card:nth-child(3)')).to have_content(issue3.title) @@ -102,7 +102,7 @@ RSpec.describe 'Multi Select Issue', :js do wait_for_requests - page.within(all('.js-board-list')[1]) do + page.within(all('.board-list')[1]) do expect(find('.board-card:nth-child(1)')).to have_content(issue1.title) expect(find('.board-card:nth-child(2)')).to have_content(issue2.title) expect(find('.board-card:nth-child(3)')).to have_content(issue5.title) diff --git a/spec/features/clusters/create_agent_spec.rb b/spec/features/clusters/create_agent_spec.rb index e03126d344e..c7326204bf6 100644 --- a/spec/features/clusters/create_agent_spec.rb +++ b/spec/features/clusters/create_agent_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'Cluster agent registration', :js do end it 'allows the user to select an agent to install, and displays the resulting agent token' do - click_button('Actions') + click_button('Connect a cluster') expect(page).to have_content('Register') click_button('Select an agent') @@ -34,7 +34,7 @@ RSpec.describe 'Cluster agent registration', :js do expect(page).to have_content('You cannot see this token again after you close this window.') expect(page).to have_content('example-agent-token') - expect(page).to have_content('docker run --pull=always --rm') + expect(page).to have_content('helm upgrade --install') within find('.modal-footer') do click_button('Close') diff --git a/spec/features/commit_spec.rb b/spec/features/commit_spec.rb index 3fd613ce393..c9fa10d58e6 100644 --- a/spec/features/commit_spec.rb +++ b/spec/features/commit_spec.rb @@ -33,6 +33,10 @@ RSpec.describe 'Commit' do it "reports the correct number of total changes" do expect(page).to have_content("Changes #{commit.diffs.size}") end + + it 'renders diff stats', :js do + expect(page).to have_selector(".diff-stats") + end end describe "pagination" do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index db841ffc627..4b38df175e2 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -10,6 +10,7 @@ RSpec.describe 'Commits' do before do sign_in(user) stub_ci_pipeline_to_return_yaml_file + stub_feature_flags(pipeline_tabs_vue: false) end let(:creator) { create(:user, developer_projects: [project]) } @@ -93,6 +94,7 @@ RSpec.describe 'Commits' do context 'Download artifacts', :js do before do + stub_feature_flags(pipeline_tabs_vue: false) create(:ci_job_artifact, :archive, file: artifacts_file, job: build) end @@ -122,6 +124,7 @@ RSpec.describe 'Commits' do context "when logged as reporter", :js do before do + stub_feature_flags(pipeline_tabs_vue: false) project.add_reporter(user) create(:ci_job_artifact, :archive, file: artifacts_file, job: build) visit builds_project_pipeline_path(project, pipeline) diff --git a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb index 89bf79ebb81..40718deed75 100644 --- a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb +++ b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'When a user searches for Sentry errors', :js, :use_clean_rails_m expect(results.count).to be(3) end - find('.gl-form-input').set('NotFound').native.send_keys(:return) + find('.filtered-search-input-container .gl-form-input').set('NotFound').native.send_keys(:return) page.within(find('.gl-table')) do results = page.all('.table-row') diff --git a/spec/features/groups/clusters/eks_spec.rb b/spec/features/groups/clusters/eks_spec.rb index 3cca2d0919c..0e64a2faf3e 100644 --- a/spec/features/groups/clusters/eks_spec.rb +++ b/spec/features/groups/clusters/eks_spec.rb @@ -20,8 +20,8 @@ RSpec.describe 'Group AWS EKS Cluster', :js do before do visit group_clusters_path(group) - click_button 'Actions' - click_link 'Create a new cluster' + click_button(class: 'dropdown-toggle-split') + click_link 'Create a cluster (deprecated)' end context 'when user creates a cluster on AWS EKS' do diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb index 2ed6ddc09ab..74ea72b238f 100644 --- a/spec/features/groups/clusters/user_spec.rb +++ b/spec/features/groups/clusters/user_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'User Cluster', :js do before do visit group_clusters_path(group) - click_link 'Connect with a certificate' + click_link 'Connect a cluster (deprecated)' end context 'when user filled form with valid parameters' do @@ -119,7 +119,6 @@ RSpec.describe 'User Cluster', :js do it 'user sees creation form with the successful message' do expect(page).to have_content('Kubernetes cluster integration was successfully removed.') - expect(page).to have_link('Connect with a certificate') end end end diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb new file mode 100644 index 00000000000..1d821edefa3 --- /dev/null +++ b/spec/features/groups/group_runners_spec.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe "Group Runners" do + include Spec::Support::Helpers::Features::RunnersHelpers + + let_it_be(:group_owner) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + + before do + group.add_owner(group_owner) + sign_in(group_owner) + end + + describe "Group runners page", :js do + let!(:group_registration_token) { group.runners_token } + + context "runners registration" do + before do + visit group_runners_path(group) + end + + it_behaves_like "shows and resets runner registration token" do + let(:dropdown_text) { 'Register a group runner' } + let(:registration_token) { group_registration_token } + end + end + + context "with no runners" do + before do + visit group_runners_path(group) + end + + it_behaves_like "shows no runners" + + it 'shows tabs with total counts equal to 0' do + expect(page).to have_link('All 0') + expect(page).to have_link('Group 0') + expect(page).to have_link('Project 0') + end + end + + context "with an online group runner" do + let!(:group_runner) do + create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now) + end + + before do + visit group_runners_path(group) + end + + it_behaves_like 'shows runner in list' do + let(:runner) { group_runner } + end + + it_behaves_like 'pauses, resumes and deletes a runner' do + let(:runner) { group_runner } + end + + it 'shows a group badge' do + within_runner_row(group_runner.id) do + expect(page).to have_selector '.badge', text: 'group' + end + end + + it 'can edit runner information' do + within_runner_row(group_runner.id) do + expect(find_link('Edit')[:href]).to end_with(edit_group_runner_path(group, group_runner)) + end + end + end + + context "with an online project runner" do + let!(:project_runner) do + create(:ci_runner, :project, projects: [project], description: 'runner-bar', contacted_at: Time.zone.now) + end + + before do + visit group_runners_path(group) + end + + it_behaves_like 'shows runner in list' do + let(:runner) { project_runner } + end + + it_behaves_like 'pauses, resumes and deletes a runner' do + let(:runner) { project_runner } + end + + it 'shows a project (specific) badge' do + within_runner_row(project_runner.id) do + expect(page).to have_selector '.badge', text: 'specific' + end + end + + it 'can edit runner information' do + within_runner_row(project_runner.id) do + expect(find_link('Edit')[:href]).to end_with(edit_group_runner_path(group, project_runner)) + end + end + end + + context 'with a multi-project runner' do + let(:project) { create(:project, group: group) } + let(:project_2) { create(:project, group: group) } + let!(:runner) { create(:ci_runner, :project, projects: [project, project_2], description: 'group-runner') } + + it 'user cannot remove the project runner' do + visit group_runners_path(group) + + within_runner_row(runner.id) do + expect(page).to have_button 'Delete runner', disabled: true + end + end + end + + context 'filtered search' do + before do + visit group_runners_path(group) + end + + it 'allows user to search by paused and status', :js do + focus_filtered_search + + page.within(search_bar_selector) do + expect(page).to have_link('Paused') + expect(page).to have_content('Status') + end + end + end + end + + describe "Group runner edit page", :js do + let!(:runner) do + create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now) + end + + it 'user edits the runner to be protected' do + visit edit_group_runner_path(group, runner) + + expect(page.find_field('runner[access_level]')).not_to be_checked + + check 'runner_access_level' + click_button 'Save changes' + + expect(page).to have_content 'Protected Yes' + end + + context 'when a runner has a tag' do + before do + runner.update!(tag_list: ['tag']) + end + + it 'user edits runner not to run untagged jobs' do + visit edit_group_runner_path(group, runner) + + expect(page.find_field('runner[run_untagged]')).to be_checked + + uncheck 'runner_run_untagged' + click_button 'Save changes' + + expect(page).to have_content 'Can run untagged jobs No' + end + end + end +end diff --git a/spec/features/groups/import_export/export_file_spec.rb b/spec/features/groups/import_export/export_file_spec.rb index 9feb8085e66..e3cb1ad77a7 100644 --- a/spec/features/groups/import_export/export_file_spec.rb +++ b/spec/features/groups/import_export/export_file_spec.rb @@ -26,22 +26,6 @@ RSpec.describe 'Group Export', :js do end end - context 'when the group import/export FF is disabled' do - before do - stub_feature_flags(group_import_export: false) - - group.add_owner(user) - sign_in(user) - end - - it 'does not show the group export options' do - visit edit_group_path(group) - - expect(page).to have_content('Advanced') - expect(page).not_to have_content('Export group') - end - end - context 'when the signed in user does not have the required permission level' do before do group.add_guest(user) diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb index 5ab5a7ea716..5a9223d9ee8 100644 --- a/spec/features/groups/members/manage_groups_spec.rb +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -3,7 +3,6 @@ require 'spec_helper' RSpec.describe 'Groups > Members > Manage groups', :js do - include Select2Helper include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::InviteMembersModalHelper include Spec::Support::Helpers::ModalHelpers @@ -119,16 +118,92 @@ RSpec.describe 'Groups > Members > Manage groups', :js do describe 'group search results' do let_it_be(:group, refind: true) { create(:group) } - let_it_be(:group_within_hierarchy) { create(:group, parent: group) } - let_it_be(:group_outside_hierarchy) { create(:group) } - before_all do - group.add_owner(user) - group_within_hierarchy.add_owner(user) - group_outside_hierarchy.add_owner(user) + context 'with instance admin considerations' do + let_it_be(:group_to_share) { create(:group) } + + context 'when user is an admin' do + let_it_be(:admin) { create(:admin) } + + before do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + end + + it 'shows groups where the admin has no direct membership' do + visit group_group_members_path(group) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_to_have_group(group_to_share) + expect_not_to_have_group(group) + end + end + + it 'shows groups where the admin has at least guest level membership' do + group_to_share.add_guest(admin) + + visit group_group_members_path(group) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_to_have_group(group_to_share) + expect_not_to_have_group(group) + end + end + end + + context 'when user is not an admin' do + before do + group.add_owner(user) + end + + it 'shows groups where the user has no direct membership' do + visit group_group_members_path(group) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_not_to_have_group(group_to_share) + expect_not_to_have_group(group) + end + end + + it 'shows groups where the user has at least guest level membership' do + group_to_share.add_guest(user) + + visit group_group_members_path(group) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_to_have_group(group_to_share) + expect_not_to_have_group(group) + end + end + end end - context 'when the invite members group modal is enabled' do + context 'when user is not an admin and there are hierarchy considerations' do + let_it_be(:group_within_hierarchy) { create(:group, parent: group) } + let_it_be(:group_outside_hierarchy) { create(:group) } + + before_all do + group.add_owner(user) + group_within_hierarchy.add_owner(user) + group_outside_hierarchy.add_owner(user) + end + it 'does not show self or ancestors', :aggregate_failures do group_sibbling = create(:group, parent: group) group_sibbling.add_owner(user) @@ -139,46 +214,46 @@ RSpec.describe 'Groups > Members > Manage groups', :js do click_on 'Select a group' wait_for_requests - page.within('[data-testid="group-select-dropdown"]') do - expect(page).to have_selector("[entity-id='#{group_outside_hierarchy.id}']") - expect(page).to have_selector("[entity-id='#{group_sibbling.id}']") - expect(page).not_to have_selector("[entity-id='#{group.id}']") - expect(page).not_to have_selector("[entity-id='#{group_within_hierarchy.id}']") + page.within(group_dropdown_selector) do + expect_to_have_group(group_outside_hierarchy) + expect_to_have_group(group_sibbling) + expect_not_to_have_group(group) + expect_not_to_have_group(group_within_hierarchy) end end - end - context 'when sharing with groups outside the hierarchy is enabled' do - it 'shows groups within and outside the hierarchy in search results' do - visit group_group_members_path(group) + context 'when sharing with groups outside the hierarchy is enabled' do + it 'shows groups within and outside the hierarchy in search results' do + visit group_group_members_path(group) - click_on 'Invite a group' - click_on 'Select a group' + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests - expect(page).to have_text group_within_hierarchy.name - expect(page).to have_text group_outside_hierarchy.name + page.within(group_dropdown_selector) do + expect_to_have_group(group_within_hierarchy) + expect_to_have_group(group_outside_hierarchy) + end + end end - end - context 'when sharing with groups outside the hierarchy is disabled' do - before do - group.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true) - end + context 'when sharing with groups outside the hierarchy is disabled' do + before do + group.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true) + end - it 'shows only groups within the hierarchy in search results' do - visit group_group_members_path(group) + it 'shows only groups within the hierarchy in search results' do + visit group_group_members_path(group) - click_on 'Invite a group' - click_on 'Select a group' + click_on 'Invite a group' + click_on 'Select a group' - expect(page).to have_text group_within_hierarchy.name - expect(page).not_to have_text group_outside_hierarchy.name + page.within(group_dropdown_selector) do + expect_to_have_group(group_within_hierarchy) + expect_not_to_have_group(group_outside_hierarchy) + end + end end end end - - def click_groups_tab - expect(page).to have_link 'Groups' - click_link "Groups" - end end diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index 533d2118b30..468001c3be6 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -42,46 +42,6 @@ RSpec.describe 'Groups > Members > Manage members' do end end - it 'add user to group', :js, :snowplow, :aggregate_failures do - group.add_owner(user1) - - visit group_group_members_path(group) - - invite_member(user2.name, role: 'Reporter') - - page.within(second_row) do - expect(page).to have_content(user2.name) - expect(page).to have_button('Reporter') - end - - expect_snowplow_event( - category: 'Members::CreateService', - action: 'create_member', - label: 'group-members-page', - property: 'existing_user', - user: user1 - ) - end - - it 'do not disclose email addresses', :js do - group.add_owner(user1) - create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe") - - visit group_group_members_path(group) - - click_on 'Invite members' - find('[data-testid="members-token-select-input"]').set('@gitlab.com') - - wait_for_requests - - expect(page).to have_content('No matches found') - - find('[data-testid="members-token-select-input"]').set('undisclosed_email@gitlab.com') - wait_for_requests - - expect(page).to have_content('Invite "undisclosed_email@gitlab.com" by email') - end - it 'remove user from group', :js do group.add_owner(user1) group.add_developer(user2) @@ -106,43 +66,29 @@ RSpec.describe 'Groups > Members > Manage members' do end end - it 'add yourself to group when already an owner', :js, :aggregate_failures do - group.add_owner(user1) - - visit group_group_members_path(group) - - invite_member(user1.name, role: 'Reporter') - - page.within(first_row) do - expect(page).to have_content(user1.name) - expect(page).to have_content('Owner') - end - end + context 'when inviting' do + it 'add yourself to group when already an owner', :js do + group.add_owner(user1) - it 'invite user to group', :js, :snowplow do - group.add_owner(user1) + visit group_group_members_path(group) - visit group_group_members_path(group) + invite_member(user1.name, role: 'Reporter', refresh: false) - invite_member('test@example.com', role: 'Reporter') + expect(page).to have_selector(invite_modal_selector) + expect(page).to have_content("not authorized to update member") - expect(page).to have_link 'Invited' - click_link 'Invited' + page.refresh - aggregate_failures do - page.within(members_table) do - expect(page).to have_content('test@example.com') - expect(page).to have_content('Invited') - expect(page).to have_button('Reporter') + page.within find_member_row(user1) do + expect(page).to have_content('Owner') end + end - expect_snowplow_event( - category: 'Members::InviteService', - action: 'create_member', - label: 'group-members-page', - property: 'net_new_user', - user: user1 - ) + it_behaves_like 'inviting members', 'group-members-page' do + let_it_be(:entity) { group } + let_it_be(:members_page_path) { group_group_members_path(entity) } + let_it_be(:subentity) { create(:group, parent: group) } + let_it_be(:subentity_members_page_path) { group_group_members_path(subentity) } end end @@ -169,4 +115,57 @@ RSpec.describe 'Groups > Members > Manage members' do end end end + + describe 'member search results', :js do + before do + group.add_owner(user1) + end + + it 'does not disclose email addresses' do + create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe") + + visit group_group_members_path(group) + + click_on 'Invite members' + find(member_dropdown_selector).set('@gitlab.com') + + wait_for_requests + + expect(page).to have_content('No matches found') + + find(member_dropdown_selector).set('undisclosed_email@gitlab.com') + wait_for_requests + + expect(page).to have_content('Invite "undisclosed_email@gitlab.com" by email') + end + + it 'does not show project_bots', :aggregate_failures do + internal_project_bot = create(:user, :project_bot, name: '_internal_project_bot_') + project = create(:project, group: group) + project.add_maintainer(internal_project_bot) + + external_group = create(:group) + external_project_bot = create(:user, :project_bot, name: '_external_project_bot_') + external_project = create(:project, group: external_group) + external_project.add_maintainer(external_project_bot) + external_project.add_maintainer(user1) + + visit group_group_members_path(group) + + click_on 'Invite members' + + page.within invite_modal_selector do + field = find(member_dropdown_selector) + field.native.send_keys :tab + field.click + + wait_for_requests + + expect(page).to have_content(user1.name) + expect(page).to have_content(user2.name) + expect(page).not_to have_content(internal_project_bot.name) + expect(page).not_to have_content(external_project_bot.name) + end + end + end end diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb index 03758e0d401..bf8e64fa1e2 100644 --- a/spec/features/groups/members/sort_members_spec.rb +++ b/spec/features/groups/members/sort_members_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' RSpec.describe 'Groups > Members > Sort members', :js do include Spec::Support::Helpers::Features::MembersHelpers - let(:owner) { create(:user, name: 'John Doe') } - let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } + let(:owner) { create(:user, name: 'John Doe', created_at: 5.days.ago, last_activity_on: Date.today) } + let(:developer) { create(:user, name: 'Mary Jane', created_at: 1.day.ago, last_sign_in_at: 5.days.ago, last_activity_on: Date.today - 5) } let(:group) { create(:group) } before do @@ -50,6 +50,42 @@ RSpec.describe 'Groups > Members > Sort members', :js do expect_sort_by('Max role', :desc) end + it 'sorts by user created on ascending' do + visit_members_list(sort: :oldest_created_user) + + expect(first_row.text).to include(owner.name) + expect(second_row.text).to include(developer.name) + + expect_sort_by('Created on', :asc) + end + + it 'sorts by user created on descending' do + visit_members_list(sort: :recent_created_user) + + expect(first_row.text).to include(developer.name) + expect(second_row.text).to include(owner.name) + + expect_sort_by('Created on', :desc) + end + + it 'sorts by last activity ascending' do + visit_members_list(sort: :oldest_last_activity) + + expect(first_row.text).to include(developer.name) + expect(second_row.text).to include(owner.name) + + expect_sort_by('Last activity', :asc) + end + + it 'sorts by last activity descending' do + visit_members_list(sort: :recent_last_activity) + + expect(first_row.text).to include(owner.name) + expect(second_row.text).to include(developer.name) + + expect_sort_by('Last activity', :desc) + end + it 'sorts by access granted ascending' do visit_members_list(sort: :last_joined) diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 4edf27e8fa4..42eaa8358a1 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -66,7 +66,7 @@ RSpec.describe 'Group milestones' do context 'when no milestones' do it 'renders no milestones text' do visit group_milestones_path(group) - expect(page).to have_content('No milestones to show') + expect(page).to have_content('Use milestones to track issues and merge requests') end end diff --git a/spec/features/groups/milestones_sorting_spec.rb b/spec/features/groups/milestones_sorting_spec.rb index a06e64fdee0..22d7ff91d41 100644 --- a/spec/features/groups/milestones_sorting_spec.rb +++ b/spec/features/groups/milestones_sorting_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'Milestones sorting', :js do sign_in(user) end - it 'visit group milestones and sort by due_date_asc' do + it 'visit group milestones and sort by due_date_asc', :js do visit group_milestones_path(group) expect(page).to have_button('Due soon') @@ -27,13 +27,13 @@ RSpec.describe 'Milestones sorting', :js do expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', 'v1.0']) end - click_button 'Due soon' + within '[data-testid=milestone_sort_by_dropdown]' do + click_button 'Due soon' + expect(find('.gl-new-dropdown-contents').all('.gl-new-dropdown-item-text-wrapper p').map(&:text)).to eq(['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending']) - expect(find('ul.dropdown-menu-sort li').all('a').map(&:text)).to eq(['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending']) - - click_link 'Due later' - - expect(page).to have_button('Due later') + click_button 'Due later' + expect(page).to have_button('Due later') + end # assert descending sorting within '.milestones' do diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb index 8851aeb6381..c5ad524e647 100644 --- a/spec/features/groups/settings/ci_cd_spec.rb +++ b/spec/features/groups/settings/ci_cd_spec.rb @@ -5,52 +5,73 @@ require 'spec_helper' RSpec.describe 'Group CI/CD settings' do include WaitForRequests - let(:user) { create(:user) } - let(:group) { create(:group) } + let_it_be(:user) { create(:user) } + let_it_be(:group, reload: true) { create(:group) } - before do + before_all do group.add_owner(user) + end + + before do sign_in(user) end - describe 'new group runners view banner' do - it 'displays banner' do - visit group_settings_ci_cd_path(group) + describe 'Runners section' do + let(:shared_runners_toggle) { page.find('[data-testid="enable-runners-toggle"]') } + + context 'with runner_list_group_view_vue_ui enabled' do + before do + visit group_settings_ci_cd_path(group) + end + + it 'displays the new group runners view banner' do + expect(page).to have_content(s_('Runners|New group runners view')) + expect(page).to have_link(href: group_runners_path(group)) + end - expect(page).to have_content(s_('Runners|New group runners view')) - expect(page).to have_link(href: group_runners_path(group)) + it 'has "Enable shared runners for this group" toggle', :js do + expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group')) + end end - it 'does not display banner' do - stub_feature_flags(runner_list_group_view_vue_ui: false) + context 'with runner_list_group_view_vue_ui disabled' do + before do + stub_feature_flags(runner_list_group_view_vue_ui: false) - visit group_settings_ci_cd_path(group) + visit group_settings_ci_cd_path(group) + end - expect(page).not_to have_content(s_('Runners|New group runners view')) - expect(page).not_to have_link(href: group_runners_path(group)) - end - end + it 'does not display the new group runners view banner' do + expect(page).not_to have_content(s_('Runners|New group runners view')) + expect(page).not_to have_link(href: group_runners_path(group)) + end - describe 'runners registration token' do - let!(:token) { group.runners_token } + it 'has "Enable shared runners for this group" toggle', :js do + expect(shared_runners_toggle).to have_content(_('Enable shared runners for this group')) + end - before do - visit group_settings_ci_cd_path(group) - end + context 'with runners registration token' do + let!(:token) { group.runners_token } - it 'has a registration token' do - expect(page.find('#registration_token')).to have_content(token) - end + before do + visit group_settings_ci_cd_path(group) + end - describe 'reload registration token' do - let(:page_token) { find('#registration_token').text } + it 'displays the registration token' do + expect(page.find('#registration_token')).to have_content(token) + end - before do - click_button 'Reset registration token' - end + describe 'reload registration token' do + let(:page_token) { find('#registration_token').text } + + before do + click_button 'Reset registration token' + end - it 'changes registration token' do - expect(page_token).not_to eq token + it 'changes the registration token' do + expect(page_token).not_to eq token + end + end end end end diff --git a/spec/features/issuables/shortcuts_issuable_spec.rb b/spec/features/issuables/shortcuts_issuable_spec.rb index 7e8f39c47a7..528420062dd 100644 --- a/spec/features/issuables/shortcuts_issuable_spec.rb +++ b/spec/features/issuables/shortcuts_issuable_spec.rb @@ -15,12 +15,20 @@ RSpec.describe 'Blob shortcuts', :js do end shared_examples "quotes the selected text" do - it "quotes the selected text", :quarantine do - select_element('.note-text') + it 'quotes the selected text in main comment form', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/356388' do + select_element('#notes-list .note:first-child .note-text') find('body').native.send_key('r') expect(find('.js-main-target-form .js-vue-comment-form').value).to include(note_text) end + + it 'quotes the selected text in the discussion reply form', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/356388' do + find('#notes-list .note:first-child .js-reply-button').click + select_element('#notes-list .note:first-child .note-text') + find('body').native.send_key('r') + + expect(find('#notes-list .note:first-child .js-vue-markdown-field .js-gfm-input').value).to include(note_text) + end end describe 'pressing "r"' do diff --git a/spec/features/issues/incident_issue_spec.rb b/spec/features/issues/incident_issue_spec.rb index 2956ddede2e..a2519a44604 100644 --- a/spec/features/issues/incident_issue_spec.rb +++ b/spec/features/issues/incident_issue_spec.rb @@ -56,5 +56,37 @@ RSpec.describe 'Incident Detail', :js do end end end + + context 'when on summary tab' do + before do + click_link 'Summary' + end + + it 'shows the summary tab with all components' do + page.within('.issuable-details') do + hidden_items = find_all('.js-issue-widgets') + + # Linked Issues/MRs and comment box + expect(hidden_items.count).to eq(2) + + expect(hidden_items).to all(be_visible) + end + end + end + + context 'when on alert details tab' do + before do + click_link 'Alert details' + end + + it 'does not show the linked issues and notes/comment components' do + page.within('.issuable-details') do + hidden_items = find_all('.js-issue-widgets') + + # Linked Issues/MRs and comment box are hidden on page + expect(hidden_items.count).to eq(0) + end + end + end end end diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 446f13dc4d0..8a5e33ba18c 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -71,6 +71,12 @@ RSpec.describe "User creates issue" do expect(preview).to have_css("gl-emoji") expect(textarea).not_to be_visible + + click_button("Write") + fill_in("Description", with: "/confidential") + click_button("Preview") + + expect(form).to have_content('Makes this issue confidential.') end end end diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index 8c906e6a27c..3b440002cb5 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -35,6 +35,12 @@ RSpec.describe "Issues > User edits issue", :js do end expect(form).to have_button("Write") + + click_button("Write") + fill_in("Description", with: "/confidential") + click_button("Preview") + + expect(form).to have_content('Makes this issue confidential.') end it 'allows user to select unassigned' do diff --git a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb index 6473fe01052..311818d2d15 100644 --- a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb +++ b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb @@ -6,6 +6,10 @@ RSpec.describe 'Issues > Real-time sidebar', :js do let_it_be(:project) { create(:project, :public) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:user) { create(:user) } + let_it_be(:label) { create(:label, project: project, name: 'Development') } + + let(:labels_widget) { find('[data-testid="sidebar-labels"]') } + let(:labels_value) { find('[data-testid="value-wrapper"]') } before_all do project.add_developer(user) @@ -32,4 +36,37 @@ RSpec.describe 'Issues > Real-time sidebar', :js do expect(page.find('.assignee')).to have_content user.name end end + + it 'updates the label in real-time' do + Capybara::Session.new(:other_session) + + using_session :other_session do + visit project_issue_path(project, issue) + wait_for_requests + expect(labels_value).to have_content('None') + end + + sign_in(user) + + visit project_issue_path(project, issue) + wait_for_requests + expect(labels_value).to have_content('None') + + page.within(labels_widget) do + click_on 'Edit' + end + + wait_for_all_requests + + click_button label.name + click_button 'Close' + + wait_for_requests + + expect(labels_value).to have_content(label.name) + + using_session :other_session do + expect(labels_value).to have_content(label.name) + end + end end diff --git a/spec/features/jira_connect/subscriptions_spec.rb b/spec/features/jira_connect/subscriptions_spec.rb index 0b7321bf271..94c293c88b9 100644 --- a/spec/features/jira_connect/subscriptions_spec.rb +++ b/spec/features/jira_connect/subscriptions_spec.rb @@ -36,7 +36,7 @@ RSpec.describe 'Subscriptions Content Security Policy' do it 'appends to CSP directives' do visit jira_connect_subscriptions_path(jwt: jwt) - is_expected.to include("frame-ancestors 'self' https://*.atlassian.net") + is_expected.to include("frame-ancestors 'self' https://*.atlassian.net https://*.jira.com") is_expected.to include("script-src 'self' https://some-cdn.test https://connect-cdn.atl-paas.net") is_expected.to include("style-src 'self' https://some-cdn.test 'unsafe-inline'") end diff --git a/spec/features/jira_oauth_provider_authorize_spec.rb b/spec/features/jira_oauth_provider_authorize_spec.rb index daecae56101..a216d2d44b2 100644 --- a/spec/features/jira_oauth_provider_authorize_spec.rb +++ b/spec/features/jira_oauth_provider_authorize_spec.rb @@ -4,13 +4,13 @@ require 'spec_helper' RSpec.describe 'JIRA OAuth Provider' do describe 'JIRA DVCS OAuth Authorization' do - let(:application) { create(:oauth_application, redirect_uri: oauth_jira_callback_url, scopes: 'read_user') } + let(:application) { create(:oauth_application, redirect_uri: oauth_jira_dvcs_callback_url, scopes: 'read_user') } before do sign_in(user) - visit oauth_jira_authorize_path(client_id: application.uid, - redirect_uri: oauth_jira_callback_url, + visit oauth_jira_dvcs_authorize_path(client_id: application.uid, + redirect_uri: oauth_jira_dvcs_callback_url, response_type: 'code', state: 'my_state', scope: 'read_user') diff --git a/spec/features/merge_request/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb index d1be93cae02..a861ca2eea5 100644 --- a/spec/features/merge_request/user_merges_merge_request_spec.rb +++ b/spec/features/merge_request/user_merges_merge_request_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "User merges a merge request", :js do end shared_examples "fast forward merge a merge request" do - it "merges a merge request", :sidekiq_might_not_need_inline do + it "merges a merge request", :sidekiq_inline do expect(page).to have_content("Fast-forward merge without a merge commit").and have_button("Merge") page.within(".mr-state-widget") do @@ -42,4 +42,23 @@ RSpec.describe "User merges a merge request", :js do it_behaves_like "fast forward merge a merge request" end end + + context 'sidebar merge requests counter' do + let(:project) { create(:project, :public, :repository) } + let!(:merge_request) { create(:merge_request, source_project: project) } + + it 'decrements the open MR count', :sidekiq_inline do + create(:merge_request, source_project: project, source_branch: 'branch-1') + + visit(merge_request_path(merge_request)) + + expect(page).to have_css('.js-merge-counter', text: '2') + + page.within(".mr-state-widget") do + click_button("Merge") + end + + expect(page).to have_css('.js-merge-counter', text: '1') + end + end end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index 1779567624c..ad602afe68a 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -169,7 +169,7 @@ RSpec.describe 'Merge request > User posts notes', :js do end page.within('.modal') do - click_button('OK', match: :first) + click_button('Cancel editing', match: :first) end expect(find('.js-note-text').text).to eq '' 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 27f7c699c50..c9b21d4a4ae 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -17,6 +17,9 @@ RSpec.describe 'Merge request > User sees merge widget', :js do project.add_maintainer(user) project_only_mwps.add_maintainer(user) sign_in(user) + + stub_feature_flags(refactor_mr_widgets_extensions: false) + stub_feature_flags(refactor_mr_widgets_extensions_user: false) end context 'new merge request', :sidekiq_might_not_need_inline do diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb index beb658bb7a0..f77a42ee506 100644 --- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb @@ -379,4 +379,41 @@ RSpec.describe 'User comments on a diff', :js do end end end + + context 'failed to load metadata' do + let(:dummy_controller) do + Class.new(Projects::MergeRequests::DiffsController) do + def diffs_metadata + render json: '', status: :internal_server_error + end + end + end + + before do + stub_const('Projects::MergeRequests::DiffsController', dummy_controller) + + click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']")) + + page.within('.js-discussion-note-form') do + fill_in('note_note', with: "```suggestion\n# change to a comment\n```") + click_button('Add comment now') + end + + wait_for_requests + + visit(project_merge_request_path(project, merge_request)) + + wait_for_requests + end + + it 'displays an error' do + page.within('.discussion-notes') do + click_button('Apply suggestion') + + wait_for_requests + + expect(page).to have_content('Unable to fully load the default commit message. You can still apply this suggestion and the commit message will be correct.') + end + end + end end diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb index f781ba0827c..a15b6072e70 100644 --- a/spec/features/merge_requests/user_mass_updates_spec.rb +++ b/spec/features/merge_requests/user_mass_updates_spec.rb @@ -70,7 +70,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do it 'updates merge request with assignee' do change_assignee(user.name) - expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user.name}, go to their profile." + expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user.name}" end end end diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb index ede9faed876..40626407642 100644 --- a/spec/features/milestones/user_deletes_milestone_spec.rb +++ b/spec/features/milestones/user_deletes_milestone_spec.rb @@ -21,7 +21,7 @@ RSpec.describe "User deletes milestone", :js do click_button("Delete") click_button("Delete milestone") - expect(page).to have_content("No milestones to show") + expect(page).to have_content("Use milestones to track issues and merge requests over a fixed period of time") visit(activity_project_path(project)) diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb index 93674057fed..ea5bb8c33b2 100644 --- a/spec/features/oauth_login_spec.rb +++ b/spec/features/oauth_login_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'OAuth Login', :js, :allow_forgery_protection do end providers = [:github, :twitter, :bitbucket, :gitlab, :google_oauth2, - :facebook, :cas3, :auth0, :authentiq, :salesforce, :dingtalk] + :facebook, :cas3, :auth0, :authentiq, :salesforce, :dingtalk, :alicloud] around do |example| with_omniauth_full_host { example.run } diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index f1e5658cd7b..8cbc0491441 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -47,14 +47,14 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do click_on "1" # Scopes - check "api" + check "read_api" check "read_user" click_on "Create personal access token" expect(active_personal_access_tokens).to have_text(name) expect(active_personal_access_tokens).to have_text('in') - expect(active_personal_access_tokens).to have_text('api') + expect(active_personal_access_tokens).to have_text('read_api') expect(active_personal_access_tokens).to have_text('read_user') expect(created_personal_access_token).not_to be_empty end diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index 026da5814e3..4b6ed458c68 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -8,8 +8,6 @@ RSpec.describe 'User edit profile' do let(:user) { create(:user) } before do - stub_feature_flags(improved_emoji_picker: false) - sign_in(user) visit(profile_path) end @@ -169,10 +167,9 @@ RSpec.describe 'User edit profile' do context 'user status', :js do def select_emoji(emoji_name, is_modal = false) - emoji_menu_class = is_modal ? '.js-modal-status-emoji-menu' : '.js-status-emoji-menu' - toggle_button = find('.js-toggle-emoji-menu') + toggle_button = find('.emoji-menu-toggle-button') toggle_button.click - emoji_button = find(%Q{#{emoji_menu_class} .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]}) + emoji_button = find("gl-emoji[data-name=\"#{emoji_name}\"]") emoji_button.click end @@ -207,7 +204,7 @@ RSpec.describe 'User edit profile' do end it 'adds message and emoji to user status' do - emoji = 'tanabata_tree' + emoji = '8ball' message = 'Playing outside' select_emoji(emoji) fill_in 'js-status-message-field', with: message @@ -356,7 +353,7 @@ RSpec.describe 'User edit profile' do end it 'adds emoji to user status' do - emoji = 'biohazard' + emoji = '8ball' open_user_status_modal select_emoji(emoji, true) set_user_status_in_modal @@ -387,18 +384,18 @@ RSpec.describe 'User edit profile' do it 'opens the emoji modal again after closing it' do open_user_status_modal - select_emoji('biohazard', true) + select_emoji('8ball', true) - find('.js-toggle-emoji-menu').click + find('.emoji-menu-toggle-button').click - expect(page).to have_selector('.emoji-menu') + expect(page).to have_selector('.emoji-picker-emoji') end it 'does not update the awards panel emoji' do project.add_maintainer(user) visit(project_issue_path(project, issue)) - emoji = 'biohazard' + emoji = '8ball' open_user_status_modal select_emoji(emoji, true) @@ -420,7 +417,7 @@ RSpec.describe 'User edit profile' do end it 'adds message and emoji to user status' do - emoji = 'tanabata_tree' + emoji = '8ball' message = 'Playing outside' open_user_status_modal select_emoji(emoji, true) @@ -495,9 +492,7 @@ RSpec.describe 'User edit profile' do open_user_status_modal find('.js-status-message-field').native.send_keys(message) - within('.js-toggle-emoji-menu') do - expect(page).to have_emoji('speech_balloon') - end + expect(page).to have_emoji('speech_balloon') end context 'note header' do diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb index e19e29bf63a..4c61e8d45e4 100644 --- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb +++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb @@ -18,14 +18,6 @@ RSpec.describe 'User visits the profile preferences page', :js do end describe 'User changes their syntax highlighting theme', :js do - it 'creates a flash message' do - choose 'user_color_scheme_id_5' - - wait_for_requests - - expect_preferences_saved_message - end - it 'updates their preference' do choose 'user_color_scheme_id_5' diff --git a/spec/features/projects/blobs/balsamiq_spec.rb b/spec/features/projects/blobs/balsamiq_spec.rb deleted file mode 100644 index bce60856544..00000000000 --- a/spec/features/projects/blobs/balsamiq_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Balsamiq file blob', :js do - let(:project) { create(:project, :public, :repository) } - - before do - visit project_blob_path(project, 'add-balsamiq-file/files/images/balsamiq.bmpr') - - wait_for_requests - end - - it 'displays Balsamiq file content' do - expect(page).to have_content("Mobile examples") - end -end diff --git a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb index 11e2d24c36a..9b0edcd09d2 100644 --- a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb +++ b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb @@ -39,7 +39,7 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do find('#L3').click find("#L5").click - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: "LC5"))) + expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: "L5"))) end it 'with initial fragment hash, changes fragment hash if line number clicked' do @@ -50,7 +50,7 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do find('#L3').click find("#L5").click - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: "LC5"))) + expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: "L5"))) end end @@ -75,7 +75,7 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do find('#L3').click find("#L5").click - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: "LC5"))) + expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: "L5"))) end it 'with initial fragment hash, changes fragment hash if line number clicked' do @@ -86,7 +86,7 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do find('#L3').click find("#L5").click - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: "LC5"))) + expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: "L5"))) end end end diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index 363d08da024..d906bb396be 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -36,6 +36,8 @@ RSpec.describe 'Branches' do expect(page).to have_content(sorted_branches(repository, count: 5, sort_by: :updated_desc, state: 'active')) expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_asc, state: 'stale')) + expect(page).to have_button('Copy branch name') + expect(page).to have_link('Show more active branches', href: project_branches_filtered_path(project, state: 'active')) expect(page).not_to have_content('Show more stale branches') end @@ -197,14 +199,6 @@ RSpec.describe 'Branches' do project.add_maintainer(user) end - describe 'Initial branches page' do - it 'shows description for admin' do - visit project_branches_filtered_path(project, state: 'all') - - expect(page).to have_content("Protected branches can be managed in project settings") - end - end - it 'shows the merge request button' do visit project_branches_path(project) diff --git a/spec/features/projects/cluster_agents_spec.rb b/spec/features/projects/cluster_agents_spec.rb index e9162359940..5d931afe4a7 100644 --- a/spec/features/projects/cluster_agents_spec.rb +++ b/spec/features/projects/cluster_agents_spec.rb @@ -27,7 +27,6 @@ RSpec.describe 'ClusterAgents', :js do end it 'displays empty state', :aggregate_failures do - expect(page).to have_content('Install a new agent') expect(page).to have_selector('.empty-state') end end diff --git a/spec/features/projects/clusters/eks_spec.rb b/spec/features/projects/clusters/eks_spec.rb index 0dd6effe551..7e599ff1198 100644 --- a/spec/features/projects/clusters/eks_spec.rb +++ b/spec/features/projects/clusters/eks_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'AWS EKS Cluster', :js do visit project_clusters_path(project) click_button(class: 'dropdown-toggle-split') - click_link 'Create a new cluster' + click_link 'Create a cluster (certificate - deprecated)' end context 'when user creates a cluster on AWS EKS' do diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 90d7e2d02e9..491121a3743 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -135,7 +135,7 @@ RSpec.describe 'Gcp Cluster', :js do visit project_clusters_path(project) click_button(class: 'dropdown-toggle-split') - click_link 'Connect with a certificate' + click_link 'Connect a cluster (certificate - deprecated)' end it 'user sees the "Environment scope" field' do @@ -154,7 +154,6 @@ RSpec.describe 'Gcp Cluster', :js do it 'user sees creation form with the successful message' do expect(page).to have_content('Kubernetes cluster integration was successfully removed.') - expect(page).to have_link('Connect with a certificate') end end end @@ -220,6 +219,6 @@ RSpec.describe 'Gcp Cluster', :js do def visit_create_cluster_page click_button(class: 'dropdown-toggle-split') - click_link 'Create a new cluster' + click_link 'Create a cluster (certificate - deprecated)' end end diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index 3fd78d338da..b6bfaa3a9b9 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -25,8 +25,8 @@ RSpec.describe 'User Cluster', :js do before do visit project_clusters_path(project) - click_link 'Certificate' - click_link 'Connect with a certificate' + click_button(class: 'dropdown-toggle-split') + click_link 'Connect a cluster (certificate - deprecated)' end context 'when user filled form with valid parameters' do @@ -108,7 +108,6 @@ RSpec.describe 'User Cluster', :js do it 'user sees creation form with the successful message' do expect(page).to have_content('Kubernetes cluster integration was successfully removed.') - expect(page).to have_link('Connect with a certificate') end end end diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index b9a544144c3..0ecd7795964 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -20,7 +20,6 @@ RSpec.describe 'Clusters', :js do end it 'sees empty state' do - expect(page).to have_link('Connect with a certificate') expect(page).to have_selector('.empty-state') end end @@ -222,11 +221,11 @@ RSpec.describe 'Clusters', :js do visit project_clusters_path(project) click_button(class: 'dropdown-toggle-split') - click_link 'Create a new cluster' + click_link 'Create a cluster (certificate - deprecated)' end def visit_connect_cluster_page click_button(class: 'dropdown-toggle-split') - click_link 'Connect with a certificate' + click_link 'Connect a cluster (certificate - deprecated)' end end diff --git a/spec/features/projects/commits/multi_view_diff_spec.rb b/spec/features/projects/commits/multi_view_diff_spec.rb index ecdd398c739..009dd05c6d1 100644 --- a/spec/features/projects/commits/multi_view_diff_spec.rb +++ b/spec/features/projects/commits/multi_view_diff_spec.rb @@ -27,17 +27,11 @@ RSpec.describe 'Multiple view Diffs', :js do context 'when :rendered_diffs_viewer is off' do context 'and diff does not have ipynb' do - include_examples "no multiple viewers", 'ddd0f15ae83993f5cb66a927a28673882e99100b' + it_behaves_like "no multiple viewers", 'ddd0f15ae83993f5cb66a927a28673882e99100b' end context 'and diff has ipynb' do - include_examples "no multiple viewers", '5d6ed1503801ca9dc28e95eeb85a7cf863527aee' - - it 'shows the transformed diff' do - diff = page.find('.diff-file, .file-holder', match: :first) - - expect(diff['innerHTML']).to include('%% Cell type:markdown id:0aac5da7-745c-4eda-847a-3d0d07a1bb9b tags:') - end + it_behaves_like "no multiple viewers", '5d6ed1503801ca9dc28e95eeb85a7cf863527aee' end end @@ -45,14 +39,28 @@ RSpec.describe 'Multiple view Diffs', :js do let(:feature_flag_on) { true } context 'and diff does not include ipynb' do - include_examples "no multiple viewers", 'ddd0f15ae83993f5cb66a927a28673882e99100b' - end + it_behaves_like "no multiple viewers", 'ddd0f15ae83993f5cb66a927a28673882e99100b' - context 'and opening a diff with ipynb' do - context 'but the changes are not renderable' do - include_examples "no multiple viewers", 'a867a602d2220e5891b310c07d174fbe12122830' + context 'and in inline diff' do + let(:ref) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' } + + it 'does not change display for non-ipynb' do + expect(page).to have_selector line_with_content('new', 1) + end end + context 'and in parallel diff' do + let(:ref) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' } + + it 'does not change display for non-ipynb' do + page.find('#parallel-diff-btn').click + + expect(page).to have_selector line_with_content('new', 1) + end + end + end + + context 'and opening a diff with ipynb' do it 'loads the rendered diff as hidden' do diff = page.find('.diff-file, .file-holder', match: :first) @@ -76,10 +84,55 @@ RSpec.describe 'Multiple view Diffs', :js do expect(classes_for_element(diff, 'toHideBtn')).not_to include('selected') expect(classes_for_element(diff, 'toShowBtn')).to include('selected') end + + it 'transforms the diff' do + diff = page.find('.diff-file, .file-holder', match: :first) + + expect(diff['innerHTML']).to include('%% Cell type:markdown id:0aac5da7-745c-4eda-847a-3d0d07a1bb9b tags:') + end + + context 'on parallel view' do + before do + page.find('#parallel-diff-btn').click + end + + it 'lines without mapping cannot receive comments' do + expect(page).not_to have_selector('td.line_content.nomappinginraw ~ td.diff-line-num > .add-diff-note') + expect(page).to have_selector('td.line_content:not(.nomappinginraw) ~ td.diff-line-num > .add-diff-note') + end + + it 'lines numbers without mapping are empty' do + expect(page).not_to have_selector('td.nomappinginraw + td.diff-line-num') + expect(page).to have_selector('td.nomappinginraw + td.diff-line-num', visible: false) + end + + it 'transforms the diff' do + diff = page.find('.diff-file, .file-holder', match: :first) + + expect(diff['innerHTML']).to include('%% Cell type:markdown id:0aac5da7-745c-4eda-847a-3d0d07a1bb9b tags:') + end + end + + context 'on inline view' do + it 'lines without mapping cannot receive comments' do + expect(page).not_to have_selector('tr.line_holder[class$="nomappinginraw"] > td.diff-line-num > .add-diff-note') + expect(page).to have_selector('tr.line_holder:not([class$="nomappinginraw"]) > td.diff-line-num > .add-diff-note') + end + + it 'lines numbers without mapping are empty' do + elements = page.all('tr.line_holder[class$="nomappinginraw"] > td.diff-line-num').map { |e| e.text(:all) } + + expect(elements).to all(be == "") + end + end end end def classes_for_element(node, data_diff_entity, visible: true) node.find("[data-diff-toggle-entity=\"#{data_diff_entity}\"]", visible: visible)[:class] end + + def line_with_content(old_or_new, line_number) + "td.#{old_or_new}_line.diff-line-num[data-linenumber=\"#{line_number}\"] > a[data-linenumber=\"#{line_number}\"]" + end end diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 99137018d6b..6cf59394af7 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -70,7 +70,7 @@ RSpec.describe 'Environments page', :js do it 'shows no environments' do visit_environments(project, scope: 'stopped') - expect(page).to have_content('You don\'t have any environments right now') + expect(page).to have_content(s_('Environments|You don\'t have any stopped environments.')) end end @@ -99,7 +99,7 @@ RSpec.describe 'Environments page', :js do it 'shows no environments' do visit_environments(project, scope: 'available') - expect(page).to have_content('You don\'t have any environments right now') + expect(page).to have_content(s_('Environments|You don\'t have any environments.')) end end @@ -120,7 +120,7 @@ RSpec.describe 'Environments page', :js do end it 'does not show environments and counters are set to zero' do - expect(page).to have_content('You don\'t have any environments right now') + expect(page).to have_content(s_('Environments|You don\'t have any environments.')) expect(page).to have_link("#{_('Available')} 0") expect(page).to have_link("#{_('Stopped')} 0") diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 1e5c5d33ad9..c7fbaa85483 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -24,9 +24,9 @@ RSpec.describe 'Import/Export - project import integration test', :js do context 'when selecting the namespace' do let(:user) { create(:admin) } let!(:namespace) { user.namespace } - let(:randomHex) { SecureRandom.hex } - let(:project_name) { 'Test Project Name' + randomHex } - let(:project_path) { 'test-project-name' + randomHex } + let(:random_hex) { SecureRandom.hex } + let(:project_name) { 'Test Project Name' + random_hex } + let(:project_path) { 'test-project-name' + random_hex } it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do visit new_project_path diff --git a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb index 762f9c33510..48ae70d3ec9 100644 --- a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb +++ b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb @@ -10,8 +10,8 @@ RSpec.describe 'User uploads new design', :js do let(:issue) { create(:issue, project: project) } before do - # Cause of raising query limiting threshold https://gitlab.com/gitlab-org/gitlab/-/issues/347334 - stub_const("Gitlab::QueryLimiting::Transaction::THRESHOLD", 102) + # Cause of raising query limiting threshold https://gitlab.com/gitlab-org/gitlab/-/issues/358845 + stub_const("Gitlab::QueryLimiting::Transaction::THRESHOLD", 106) sign_in(user) enable_design_management(feature_enabled) diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index fde6240d373..3b70d177fce 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -67,19 +67,8 @@ RSpec.describe 'User browses jobs' do expect(page.find('[data-testid="jobs-all-tab"] .badge').text).to include('0') end - it 'shows a tab for Pending jobs and count' do - expect(page.find('[data-testid="jobs-pending-tab"]').text).to include('Pending') - expect(page.find('[data-testid="jobs-pending-tab"] .badge').text).to include('0') - end - - it 'shows a tab for Running jobs and count' do - expect(page.find('[data-testid="jobs-running-tab"]').text).to include('Running') - expect(page.find('[data-testid="jobs-running-tab"] .badge').text).to include('0') - end - it 'shows a tab for Finished jobs and count' do expect(page.find('[data-testid="jobs-finished-tab"]').text).to include('Finished') - expect(page.find('[data-testid="jobs-finished-tab"] .badge').text).to include('0') end it 'updates the content when tab is clicked' do diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb index 6adc3503492..9bd6476f836 100644 --- a/spec/features/projects/members/groups_with_access_list_spec.rb +++ b/spec/features/projects/members/groups_with_access_list_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe 'Projects > Members > Groups with access list', :js do include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::ModalHelpers + include Spec::Support::Helpers::Features::InviteMembersModalHelper let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, :public) } @@ -95,8 +96,4 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do expect(members_table).to have_content(group.full_name) end end - - def click_groups_tab - click_link 'Groups' - end end diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index 9c256504934..a48229249e0 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -17,20 +17,18 @@ RSpec.describe 'Project > Members > Invite group', :js do visit project_project_members_path(project) - expect(page).to have_selector('button[data-test-id="invite-group-button"]') + expect(page).to have_selector(invite_group_selector) end - it 'does not display the button when visiting the page not signed in' do + it 'does not display the button when visiting the page not signed in' do project = create(:project, namespace: create(:group)) visit project_project_members_path(project) - expect(page).not_to have_selector('button[data-test-id="invite-group-button"]') + expect(page).not_to have_selector(invite_group_selector) end describe 'Share with group lock' do - let(:invite_group_selector) { 'button[data-test-id="invite-group-button"]' } - shared_examples 'the project can be shared with groups' do it 'the "Invite a group" button exists' do visit project_project_members_path(project) @@ -158,21 +156,95 @@ RSpec.describe 'Project > Members > Invite group', :js do describe 'the groups dropdown' do let_it_be(:parent_group) { create(:group, :public) } let_it_be(:project_group) { create(:group, :public, parent: parent_group) } - let_it_be(:public_sub_subgroup) { create(:group, :public, parent: project_group) } - let_it_be(:public_sibbling_group) { create(:group, :public, parent: parent_group) } - let_it_be(:private_sibbling_group) { create(:group, :private, parent: parent_group) } - let_it_be(:private_membership_group) { create(:group, :private) } - let_it_be(:public_membership_group) { create(:group, :public) } let_it_be(:project) { create(:project, group: project_group) } - before do - private_membership_group.add_guest(maintainer) - public_membership_group.add_maintainer(maintainer) + context 'with instance admin considerations' do + let_it_be(:group_to_share) { create(:group) } - sign_in(maintainer) + context 'when user is an admin' do + let_it_be(:admin) { create(:admin) } + + before do + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + end + + it 'shows groups where the admin has no direct membership' do + visit project_project_members_path(project) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_to_have_group(group_to_share) + end + end + + it 'shows groups where the admin has at least guest level membership' do + group_to_share.add_guest(admin) + + visit project_project_members_path(project) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_to_have_group(group_to_share) + end + end + end + + context 'when user is not an admin' do + before do + project.add_maintainer(maintainer) + sign_in(maintainer) + end + + it 'does not show groups where the user has no direct membership' do + visit project_project_members_path(project) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_not_to_have_group(group_to_share) + end + end + + it 'shows groups where the user has at least guest level membership' do + group_to_share.add_guest(maintainer) + + visit project_project_members_path(project) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within(group_dropdown_selector) do + expect_to_have_group(group_to_share) + end + end + end end context 'for a project in a nested group' do + let_it_be(:public_sub_subgroup) { create(:group, :public, parent: project_group) } + let_it_be(:public_sibbling_group) { create(:group, :public, parent: parent_group) } + let_it_be(:private_sibbling_group) { create(:group, :private, parent: parent_group) } + let_it_be(:private_membership_group) { create(:group, :private) } + let_it_be(:public_membership_group) { create(:group, :public) } + let_it_be(:project) { create(:project, group: project_group) } + + before do + private_membership_group.add_guest(maintainer) + public_membership_group.add_maintainer(maintainer) + + sign_in(maintainer) + end + it 'does not show the groups inherited from projects' do project.add_maintainer(maintainer) public_sibbling_group.add_maintainer(maintainer) @@ -183,7 +255,7 @@ RSpec.describe 'Project > Members > Invite group', :js do click_on 'Select a group' wait_for_requests - page.within('[data-testid="group-select-dropdown"]') do + page.within(group_dropdown_selector) do expect_to_have_group(public_membership_group) expect_to_have_group(public_sibbling_group) expect_to_have_group(private_membership_group) @@ -204,7 +276,7 @@ RSpec.describe 'Project > Members > Invite group', :js do click_on 'Select a group' wait_for_requests - page.within('[data-testid="group-select-dropdown"]') do + page.within(group_dropdown_selector) do expect_to_have_group(public_membership_group) expect_to_have_group(public_sibbling_group) expect_to_have_group(private_membership_group) @@ -215,14 +287,10 @@ RSpec.describe 'Project > Members > Invite group', :js do expect_not_to_have_group(project_group) end end - - def expect_to_have_group(group) - expect(page).to have_selector("[entity-id='#{group.id}']") - end - - def expect_not_to_have_group(group) - expect(page).not_to have_selector("[entity-id='#{group.id}']") - end end end + + def invite_group_selector + 'button[data-test-id="invite-group-button"]' + end end diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/manage_members_spec.rb index f2424a4acc3..0f4120e88e0 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/manage_members_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Project members list', :js do +RSpec.describe 'Projects > Members > Manage members', :js do include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::InviteMembersModalHelper include Spec::Support::Helpers::ModalHelpers @@ -48,24 +48,6 @@ RSpec.describe 'Project members list', :js do end end - it 'add user to project', :snowplow, :aggregate_failures do - visit_members_page - - invite_member(user2.name, role: 'Reporter') - - page.within find_member_row(user2) do - expect(page).to have_button('Reporter') - end - - expect_snowplow_event( - category: 'Members::CreateService', - action: 'create_member', - label: 'project-members-page', - property: 'existing_user', - user: user1 - ) - end - it 'uses ProjectMember access_level_roles for the invite members modal access option', :aggregate_failures do visit_members_page @@ -104,24 +86,41 @@ RSpec.describe 'Project members list', :js do expect(members_table).not_to have_content(other_user.name) end - it 'invite user to project', :snowplow, :aggregate_failures do - visit_members_page + it_behaves_like 'inviting members', 'project-members-page' do + let_it_be(:entity) { project } + let_it_be(:members_page_path) { project_project_members_path(entity) } + let_it_be(:subentity) { project } + let_it_be(:subentity_members_page_path) { project_project_members_path(entity) } + end - invite_member('test@example.com', role: 'Reporter') + describe 'member search results' do + it 'does not show project_bots', :aggregate_failures do + internal_project_bot = create(:user, :project_bot, name: '_internal_project_bot_') + project.add_maintainer(internal_project_bot) - click_link 'Invited' + external_group = create(:group) + external_project_bot = create(:user, :project_bot, name: '_external_project_bot_') + external_project = create(:project, group: external_group) + external_project.add_maintainer(external_project_bot) + external_project.add_maintainer(user1) - page.within find_invited_member_row('test@example.com') do - expect(page).to have_button('Reporter') - end + visit_members_page + + click_on 'Invite members' - expect_snowplow_event( - category: 'Members::InviteService', - action: 'create_member', - label: 'project-members-page', - property: 'net_new_user', - user: user1 - ) + page.within invite_modal_selector do + field = find(member_dropdown_selector) + field.native.send_keys :tab + field.click + + wait_for_requests + + expect(page).to have_content(user1.name) + expect(page).to have_content(user2.name) + expect(page).not_to have_content(internal_project_bot.name) + expect(page).not_to have_content(external_project_bot.name) + end + end end context 'as a signed out visitor viewing a public project' do diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index 653564d1566..8aadd6302d0 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' RSpec.describe 'Projects > Members > Sorting', :js do include Spec::Support::Helpers::Features::MembersHelpers - let(:maintainer) { create(:user, name: 'John Doe') } - let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } + let(:maintainer) { create(:user, name: 'John Doe', created_at: 5.days.ago, last_activity_on: Date.today) } + let(:developer) { create(:user, name: 'Mary Jane', created_at: 1.day.ago, last_sign_in_at: 5.days.ago, last_activity_on: Date.today - 5) } let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) } before do @@ -42,6 +42,42 @@ RSpec.describe 'Projects > Members > Sorting', :js do expect_sort_by('Max role', :desc) end + it 'sorts by user created on ascending' do + visit_members_list(sort: :oldest_created_user) + + expect(first_row.text).to have_content(maintainer.name) + expect(second_row.text).to have_content(developer.name) + + expect_sort_by('Created on', :asc) + end + + it 'sorts by user created on descending' do + visit_members_list(sort: :recent_created_user) + + expect(first_row.text).to have_content(developer.name) + expect(second_row.text).to have_content(maintainer.name) + + expect_sort_by('Created on', :desc) + end + + it 'sorts by last activity ascending' do + visit_members_list(sort: :oldest_last_activity) + + expect(first_row.text).to have_content(developer.name) + expect(second_row.text).to have_content(maintainer.name) + + expect_sort_by('Last activity', :asc) + end + + it 'sorts by last activity descending' do + visit_members_list(sort: :recent_last_activity) + + expect(first_row.text).to have_content(maintainer.name) + expect(second_row.text).to have_content(developer.name) + + expect_sort_by('Last activity', :desc) + end + it 'sorts by access granted ascending' do visit_members_list(sort: :last_joined) diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb index 565c61cfaa0..2ad820e4a06 100644 --- a/spec/features/projects/milestones/milestones_sorting_spec.rb +++ b/spec/features/projects/milestones/milestones_sorting_spec.rb @@ -5,49 +5,55 @@ require 'spec_helper' RSpec.describe 'Milestones sorting', :js do let(:user) { create(:user) } let(:project) { create(:project, name: 'test', namespace: user.namespace) } + let(:milestones_for_sort_by) do + { + 'Due later' => %w[b c a], + 'Name, ascending' => %w[a b c], + 'Name, descending' => %w[c b a], + 'Start later' => %w[a c b], + 'Start soon' => %w[b c a], + 'Due soon' => %w[a c b] + } + end + + let(:ordered_milestones) do + ['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending'] + end before do - # Milestones - create(:milestone, - due_date: 10.days.from_now, - created_at: 2.hours.ago, - title: "aaa", project: project) - create(:milestone, - due_date: 11.days.from_now, - created_at: 1.hour.ago, - title: "bbb", project: project) + create(:milestone, start_date: 7.days.from_now, due_date: 10.days.from_now, title: "a", project: project) + create(:milestone, start_date: 6.days.from_now, due_date: 11.days.from_now, title: "c", project: project) + create(:milestone, start_date: 5.days.from_now, due_date: 12.days.from_now, title: "b", project: project) sign_in(user) end - it 'visit project milestones and sort by due_date_asc' do + it 'visit project milestones and sort by various orders' do visit project_milestones_path(project) expect(page).to have_button('Due soon') - # assert default sorting + # assert default sorting order within '.milestones' do - expect(page.all('ul.content-list > li').first.text).to include('aaa') - expect(page.all('ul.content-list > li').last.text).to include('bbb') + expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(%w[a c b]) end - click_button 'Due soon' + # assert milestones listed for given sort order + selected_sort_order = 'Due soon' + milestones_for_sort_by.each do |sort_by, expected_milestones| + within '[data-testid=milestone_sort_by_dropdown]' do + click_button selected_sort_order + milestones = find('.gl-new-dropdown-contents').all('.gl-new-dropdown-item-text-wrapper p').map(&:text) + expect(milestones).to eq(ordered_milestones) - sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text) + click_button sort_by + expect(page).to have_button(sort_by) + end - expect(sort_options[0]).to eq('Due soon') - expect(sort_options[1]).to eq('Due later') - expect(sort_options[2]).to eq('Start soon') - expect(sort_options[3]).to eq('Start later') - expect(sort_options[4]).to eq('Name, ascending') - expect(sort_options[5]).to eq('Name, descending') + within '.milestones' do + expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(expected_milestones) + end - click_link 'Due later' - - expect(page).to have_button('Due later') - - within '.milestones' do - expect(page.all('ul.content-list > li').first.text).to include('bbb') - expect(page.all('ul.content-list > li').last.text).to include('aaa') + selected_sort_order = sort_by end end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index c57e39b6508..0046dfe436f 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -191,7 +191,8 @@ RSpec.describe 'New project', :js do click_link 'Create blank project' end - it 'selects the user namespace' do + it 'does not select the user namespace' do + click_on 'Pick a group or namespace' expect(page).to have_button user.username end end @@ -328,6 +329,14 @@ RSpec.describe 'New project', :js do click_on 'Create project' + expect(page).to have_content( + s_('ProjectsNew|Pick a group or namespace where you want to create this project.') + ) + + click_on 'Pick a group or namespace' + click_on user.username + click_on 'Create project' + expect(page).to have_css('#import-project-pane.active') expect(page).not_to have_css('.toggle-import-form.hide') end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 6b9dfdf3a7b..219c8ec0070 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -15,6 +15,7 @@ RSpec.describe 'Pipeline', :js do before do sign_in(user) project.add_role(user, role) + stub_feature_flags(pipeline_tabs_vue: false) end shared_context 'pipeline builds' do @@ -356,6 +357,7 @@ RSpec.describe 'Pipeline', :js do context 'page tabs' do before do + stub_feature_flags(pipeline_tabs_vue: false) visit_pipeline end @@ -388,6 +390,7 @@ RSpec.describe 'Pipeline', :js do let(:pipeline) { create(:ci_pipeline, :with_test_reports, :with_report_results, project: project) } before do + stub_feature_flags(pipeline_tabs_vue: false) visit_pipeline wait_for_requests end @@ -924,6 +927,7 @@ RSpec.describe 'Pipeline', :js do let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } before do + stub_feature_flags(pipeline_tabs_vue: false) visit builds_project_pipeline_path(project, pipeline) end @@ -944,6 +948,10 @@ RSpec.describe 'Pipeline', :js do end context 'page tabs' do + before do + stub_feature_flags(pipeline_tabs_vue: false) + end + it 'shows Pipeline, Jobs and DAG tabs with link' do expect(page).to have_link('Pipeline') expect(page).to have_link('Jobs') @@ -1014,6 +1022,10 @@ RSpec.describe 'Pipeline', :js do end describe 'GET /:project/-/pipelines/:id/failures' do + before do + stub_feature_flags(pipeline_tabs_vue: false) + end + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: '1234') } let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) } let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) } @@ -1139,6 +1151,7 @@ RSpec.describe 'Pipeline', :js do let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } before do + stub_feature_flags(pipeline_tabs_vue: false) visit dag_project_pipeline_path(project, pipeline) end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 0e1728858ec..8b1a22ae05a 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -623,6 +623,7 @@ RSpec.describe 'Pipelines', :js do create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3, ref: 'master') + stub_feature_flags(pipeline_tabs_vue: false) visit project_pipeline_path(project, pipeline) wait_for_requests end diff --git a/spec/features/projects/releases/user_views_releases_spec.rb b/spec/features/projects/releases/user_views_releases_spec.rb index 98935fdf872..a7348b62fc0 100644 --- a/spec/features/projects/releases/user_views_releases_spec.rb +++ b/spec/features/projects/releases/user_views_releases_spec.rb @@ -24,129 +24,111 @@ RSpec.describe 'User views releases', :js do stub_default_url_options(host: 'localhost') end - shared_examples 'releases index page' do - context('when the user is a maintainer') do - before do - sign_in(maintainer) + context('when the user is a maintainer') do + before do + sign_in(maintainer) - visit project_releases_path(project) + visit project_releases_path(project) - wait_for_requests - end + wait_for_requests + end - it 'sees the release' do - page.within("##{release_v1.tag}") do - expect(page).to have_content(release_v1.name) - expect(page).to have_content(release_v1.tag) - expect(page).not_to have_content('Upcoming Release') - end + it 'sees the release' do + page.within("##{release_v1.tag}") do + expect(page).to have_content(release_v1.name) + expect(page).to have_content(release_v1.tag) + expect(page).not_to have_content('Upcoming Release') end + end - it 'renders the correct links', :aggregate_failures do - page.within("##{release_v1.tag} .js-assets-list") do - external_link_indicator_selector = '[data-testid="external-link-indicator"]' + it 'renders the correct links', :aggregate_failures do + page.within("##{release_v1.tag} .js-assets-list") do + external_link_indicator_selector = '[data-testid="external-link-indicator"]' - expect(page).to have_link internal_link.name, href: internal_link.url - expect(find_link(internal_link.name)).not_to have_css(external_link_indicator_selector) + expect(page).to have_link internal_link.name, href: internal_link.url + expect(find_link(internal_link.name)).not_to have_css(external_link_indicator_selector) - expect(page).to have_link internal_link_with_redirect.name, href: Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << "/downloads#{internal_link_with_redirect.filepath}" - expect(find_link(internal_link_with_redirect.name)).not_to have_css(external_link_indicator_selector) + expect(page).to have_link internal_link_with_redirect.name, href: Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << "/downloads#{internal_link_with_redirect.filepath}" + expect(find_link(internal_link_with_redirect.name)).not_to have_css(external_link_indicator_selector) - expect(page).to have_link external_link.name, href: external_link.url - expect(find_link(external_link.name)).to have_css(external_link_indicator_selector) - end + expect(page).to have_link external_link.name, href: external_link.url + expect(find_link(external_link.name)).to have_css(external_link_indicator_selector) end + end - context 'with an upcoming release' do - it 'sees the upcoming tag' do - page.within("##{release_v3.tag}") do - expect(page).to have_content('Upcoming Release') - end + context 'with an upcoming release' do + it 'sees the upcoming tag' do + page.within("##{release_v3.tag}") do + expect(page).to have_content('Upcoming Release') end end + end - context 'with a tag containing a slash' do - it 'sees the release' do - page.within("##{release_v2.tag.parameterize}") do - expect(page).to have_content(release_v2.name) - expect(page).to have_content(release_v2.tag) - end + context 'with a tag containing a slash' do + it 'sees the release' do + page.within("##{release_v2.tag.parameterize}") do + expect(page).to have_content(release_v2.name) + expect(page).to have_content(release_v2.tag) end end + end - context 'sorting' do - def sort_page(by:, direction:) - within '[data-testid="releases-sort"]' do - find('.dropdown-toggle').click - - click_button(by, class: 'dropdown-item') - - find('.sorting-direction-button').click if direction == :ascending - end - end - - shared_examples 'releases sort order' do - it "sorts the releases #{description}" do - card_titles = page.all('.release-block .card-title', minimum: expected_releases.count) - - card_titles.each_with_index do |title, index| - expect(title).to have_content(expected_releases[index].name) - end - end - end + context 'sorting' do + def sort_page(by:, direction:) + within '[data-testid="releases-sort"]' do + find('.dropdown-toggle').click - context "when the page is sorted by the default sort order" do - let(:expected_releases) { [release_v3, release_v2, release_v1] } + click_button(by, class: 'dropdown-item') - it_behaves_like 'releases sort order' + find('.sorting-direction-button').click if direction == :ascending end + end - context "when the page is sorted by created_at ascending " do - let(:expected_releases) { [release_v2, release_v1, release_v3] } + shared_examples 'releases sort order' do + it "sorts the releases #{description}" do + card_titles = page.all('.release-block .card-title', minimum: expected_releases.count) - before do - sort_page by: 'Created date', direction: :ascending + card_titles.each_with_index do |title, index| + expect(title).to have_content(expected_releases[index].name) end - - it_behaves_like 'releases sort order' end end - end - context('when the user is a guest') do - before do - sign_in(guest) - end + context "when the page is sorted by the default sort order" do + let(:expected_releases) { [release_v3, release_v2, release_v1] } - it 'renders release info except for Git-related data' do - visit project_releases_path(project) + it_behaves_like 'releases sort order' + end - within('.release-block', match: :first) do - expect(page).to have_content(release_v3.description) - expect(page).to have_content(release_v3.tag) - expect(page).to have_content(release_v3.name) + context "when the page is sorted by created_at ascending " do + let(:expected_releases) { [release_v2, release_v1, release_v3] } - # The following properties (sometimes) include Git info, - # so they are not rendered for Guest users - expect(page).not_to have_content(release_v3.commit.short_id) + before do + sort_page by: 'Created date', direction: :ascending end + + it_behaves_like 'releases sort order' end end end - context 'when the releases_index_apollo_client feature flag is enabled' do + context('when the user is a guest') do before do - stub_feature_flags(releases_index_apollo_client: true) + sign_in(guest) end - it_behaves_like 'releases index page' - end + it 'renders release info except for Git-related data' do + visit project_releases_path(project) - context 'when the releases_index_apollo_client feature flag is disabled' do - before do - stub_feature_flags(releases_index_apollo_client: false) - end + within('.release-block', match: :first) do + expect(page).to have_content(release_v3.description) + expect(page).to have_content(release_v3.tag) + expect(page).to have_content(release_v3.name) - it_behaves_like 'releases index page' + # The following properties (sometimes) include Git info, + # so they are not rendered for Guest users + expect(page).not_to have_content(release_v3.commit.short_id) + end + end end end diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb index 2c63f2bfc02..d9e45b5e78e 100644 --- a/spec/features/projects/terraform_spec.rb +++ b/spec/features/projects/terraform_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'Terraform', :js do end it 'sees an empty state' do - expect(page).to have_content('Get started with Terraform') + expect(page).to have_content("Your project doesn't have any Terraform state files") end end diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index 6491a7425f7..b07f2d12660 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -33,29 +33,6 @@ RSpec.describe 'User creates a project', :js do end it 'creates a new project that is not blank' do - stub_experiments(new_project_sast_enabled: 'candidate') - - visit(new_project_path) - - click_link 'Create blank project' - fill_in(:project_name, with: 'With initial commits') - - expect(page).to have_checked_field 'Initialize repository with a README' - expect(page).to have_checked_field 'Enable Static Application Security Testing (SAST)' - - click_button('Create project') - - project = Project.last - - expect(page).to have_current_path(project_path(project), ignore_query: true) - expect(page).to have_content('With initial commits') - expect(page).to have_content('Configure SAST in `.gitlab-ci.yml`, creating this file if it does not already exist') - expect(page).to have_content('README.md Initial commit') - end - - it 'allows creating a new project when the new_project_sast_enabled is assigned the unchecked candidate' do - stub_experiments(new_project_sast_enabled: 'unchecked_candidate') - visit(new_project_path) click_link 'Create blank project' @@ -93,7 +70,7 @@ RSpec.describe 'User creates a project', :js do fill_in :project_name, with: 'A Subgroup Project' fill_in :project_path, with: 'a-subgroup-project' - click_button user.username + click_on 'Pick a group or namespace' click_button subgroup.full_path click_button('Create project') @@ -120,9 +97,6 @@ RSpec.describe 'User creates a project', :js do fill_in :project_name, with: 'a-new-project' fill_in :project_path, with: 'a-new-project' - click_button user.username - click_button group.full_path - page.within('#content-body') do click_button('Create project') end diff --git a/spec/features/projects/user_sorts_projects_spec.rb b/spec/features/projects/user_sorts_projects_spec.rb index 71e43467a39..7c970f7ee3d 100644 --- a/spec/features/projects/user_sorts_projects_spec.rb +++ b/spec/features/projects/user_sorts_projects_spec.rb @@ -14,25 +14,29 @@ RSpec.describe 'User sorts projects and order persists' do it "is set on the dashboard_projects_path" do visit(dashboard_projects_path) - expect(find('.dropdown-menu a.is-active', text: project_paths_label)).to have_content(project_paths_label) + expect(find('#sort-projects-dropdown')).to have_content(project_paths_label) end it "is set on the explore_projects_path" do visit(explore_projects_path) - expect(find('.dropdown-menu a.is-active', text: project_paths_label)).to have_content(project_paths_label) + expect(find('#sort-projects-dropdown')).to have_content(project_paths_label) end it "is set on the group_canonical_path" do visit(group_canonical_path(group)) - expect(find('.dropdown-menu a.is-active', text: group_paths_label)).to have_content(group_paths_label) + within '[data-testid=group_sort_by_dropdown]' do + expect(find('.gl-dropdown-toggle')).to have_content(group_paths_label) + end end it "is set on the details_group_path" do visit(details_group_path(group)) - expect(find('.dropdown-menu a.is-active', text: group_paths_label)).to have_content(group_paths_label) + within '[data-testid=group_sort_by_dropdown]' do + expect(find('.gl-dropdown-toggle')).to have_content(group_paths_label) + end end end @@ -58,23 +62,27 @@ RSpec.describe 'User sorts projects and order persists' do it_behaves_like "sort order persists across all views", "Name", "Name" end - context 'from group homepage' do + context 'from group homepage', :js do before do sign_in(user) visit(group_canonical_path(group)) - find('button.dropdown-menu-toggle').click - first(:link, 'Last created').click + within '[data-testid=group_sort_by_dropdown]' do + find('button.gl-dropdown-toggle').click + first(:button, 'Last created').click + end end it_behaves_like "sort order persists across all views", "Created date", "Last created" end - context 'from group details' do + context 'from group details', :js do before do sign_in(user) visit(details_group_path(group)) - find('button.dropdown-menu-toggle').click - first(:link, 'Most stars').click + within '[data-testid=group_sort_by_dropdown]' do + find('button.gl-dropdown-toggle').click + first(:button, 'Most stars').click + end end it_behaves_like "sort order persists across all views", "Stars", "Most stars" diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 1049f8bc18f..db64f84aa76 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -15,6 +15,12 @@ RSpec.describe 'Project' do end shared_examples 'creates from template' do |template, sub_template_tab = nil| + let(:selected_template) { page.find('.project-fields-form .selected-template') } + + choose_template_selector = '.choose-template' + template_option_selector = '.template-option' + template_name_selector = '.description strong' + it "is created from template", :js do click_link 'Create from template' find(".project-template #{sub_template_tab}").click if sub_template_tab @@ -27,6 +33,39 @@ RSpec.describe 'Project' do expect(page).to have_content template.name end + + it 'is created using keyboard navigation', :js do + click_link 'Create from template' + + first_template = first(template_option_selector) + first_template_name = first_template.find(template_name_selector).text + first_template.find(choose_template_selector).click + + expect(selected_template).to have_text(first_template_name) + + click_button "Change template" + find("#built-in").click + + # Jumps down 1 template, skipping the `preview` buttons + 2.times do + page.send_keys :tab + end + + # Ensure the template with focus is selected + project_name = "project from template" + focused_template = page.find(':focus').ancestor(template_option_selector) + focused_template_name = focused_template.find(template_name_selector).text + focused_template.find(choose_template_selector).send_keys :enter + fill_in "project_name", with: project_name + + expect(selected_template).to have_text(focused_template_name) + + page.within '#content-body' do + click_button "Create project" + end + + expect(page).to have_content project_name + end end context 'create with project template' do diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/balsamiq_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/balsamiq_spec.rb deleted file mode 100644 index 3638e98a08a..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/balsamiq_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Balsamiq file blob', :js do - let(:project) { create(:project, :public, :repository) } - - before do - stub_feature_flags(refactor_blob_viewer: false) - visit project_blob_path(project, 'add-balsamiq-file/files/images/balsamiq.bmpr') - - wait_for_requests - end - - it 'displays Balsamiq file content' do - expect(page).to have_content("Mobile examples") - end -end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 49c468976b9..2dddcd62a6c 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -352,6 +352,7 @@ RSpec.describe 'Runners' do before do group.add_owner(user) + stub_feature_flags(runner_list_group_view_vue_ui: false) end context 'group with no runners' do diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb index c38ad077cd0..562da56275c 100644 --- a/spec/features/search/user_searches_for_projects_spec.rb +++ b/spec/features/search/user_searches_for_projects_spec.rb @@ -8,6 +8,8 @@ RSpec.describe 'User searches for projects', :js do context 'when signed out' do context 'when block_anonymous_global_searches is disabled' do before do + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000) + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000) stub_feature_flags(block_anonymous_global_searches: false) end diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 8736f16b991..7350a54e8df 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -17,12 +17,15 @@ RSpec.describe 'User uses header search field', :js do end before do + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000) + allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000) sign_in(user) end shared_examples 'search field examples' do before do visit(url) + wait_for_all_requests end it 'starts searching by pressing the enter key' do @@ -37,7 +40,6 @@ RSpec.describe 'User uses header search field', :js do before do find('#search') find('body').native.send_keys('s') - wait_for_all_requests end @@ -49,6 +51,7 @@ RSpec.describe 'User uses header search field', :js do context 'when clicking the search field' do before do page.find('#search').click + wait_for_all_requests end it 'shows category search dropdown', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/250285' do @@ -59,7 +62,7 @@ RSpec.describe 'User uses header search field', :js do let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } it 'shows assigned issues' do - find('.search-input-container .dropdown-menu').click_link('Issues assigned to me') + find('[data-testid="header-search-dropdown-menu"]').click_link('Issues assigned to me') expect(page).to have_selector('.issues-list .issue') expect_tokens([assignee_token(user.name)]) @@ -67,7 +70,7 @@ RSpec.describe 'User uses header search field', :js do end it 'shows created issues' do - find('.search-input-container .dropdown-menu').click_link("Issues I've created") + find('[data-testid="header-search-dropdown-menu"]').click_link("Issues I've created") expect(page).to have_selector('.issues-list .issue') expect_tokens([author_token(user.name)]) @@ -79,7 +82,7 @@ RSpec.describe 'User uses header search field', :js do let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) } it 'shows assigned merge requests' do - find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me') + find('[data-testid="header-search-dropdown-menu"]').click_link('Merge requests assigned to me') expect(page).to have_selector('.mr-list .merge-request') expect_tokens([assignee_token(user.name)]) @@ -87,7 +90,7 @@ RSpec.describe 'User uses header search field', :js do end it 'shows created merge requests' do - find('.search-input-container .dropdown-menu').click_link("Merge requests I've created") + find('[data-testid="header-search-dropdown-menu"]').click_link("Merge requests I've created") expect(page).to have_selector('.mr-list .merge-request') expect_tokens([author_token(user.name)]) @@ -150,10 +153,9 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') - - expect(page).to have_selector(scoped_search_link('test')) - expect(page).to have_selector(scoped_search_link('test', group_id: group.id)) - expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id)) + expect(page).to have_selector(scoped_search_link('test', search_code: true)) + expect(page).to have_selector(scoped_search_link('test', group_id: group.id, search_code: true)) + expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id, search_code: true)) end end @@ -165,10 +167,9 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') - - expect(page).to have_selector(scoped_search_link('test')) - expect(page).not_to have_selector(scoped_search_link('test', group_id: project.namespace_id)) - expect(page).to have_selector(scoped_search_link('test', project_id: project.id)) + expect(page).to have_selector(scoped_search_link('test', search_code: true, repository_ref: 'master')) + expect(page).not_to have_selector(scoped_search_link('test', search_code: true, group_id: project.namespace_id, repository_ref: 'master')) + expect(page).to have_selector(scoped_search_link('test', search_code: true, project_id: project.id, repository_ref: 'master')) end it 'displays a link to project merge requests' do @@ -217,7 +218,6 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') - expect(page).to have_selector(scoped_search_link('test')) expect(page).to have_selector(scoped_search_link('test', group_id: group.id)) expect(page).not_to have_selector(scoped_search_link('test', project_id: project.id)) @@ -248,18 +248,20 @@ RSpec.describe 'User uses header search field', :js do end end - def scoped_search_link(term, project_id: nil, group_id: nil) + def scoped_search_link(term, project_id: nil, group_id: nil, search_code: nil, repository_ref: nil) # search_path will accept group_id and project_id but the order does not match # what is expected in the href, so the variable must be built manually href = search_path(search: term) + href.concat("&nav_source=navbar") href.concat("&project_id=#{project_id}") if project_id href.concat("&group_id=#{group_id}") if group_id - href.concat("&nav_source=navbar") + href.concat("&search_code=true") if search_code + href.concat("&repository_ref=#{repository_ref}") if repository_ref - ".dropdown a[href='#{href}']" + "[data-testid='header-search-dropdown-menu'] a[href='#{href}']" end def dashboard_search_options_popup_menu - "div[data-testid='dashboard-search-options']" + "[data-testid='header-search-dropdown-menu'] .header-search-dropdown-content" end end diff --git a/spec/features/static_site_editor_spec.rb b/spec/features/static_site_editor_spec.rb deleted file mode 100644 index 98313905a33..00000000000 --- a/spec/features/static_site_editor_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Static Site Editor' do - include ContentSecurityPolicyHelpers - - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, :public, :repository) } - - let(:sse_path) { project_show_sse_path(project, 'master/README.md') } - - before_all do - project.add_developer(user) - end - - before do - sign_in(user) - end - - context "when no config file is present" do - before do - visit sse_path - end - - it 'renders SSE page with all generated config values and default config file values' do - node = page.find('#static-site-editor') - - # assert generated config values are present - expect(node['data-base-url']).to eq("/#{project.full_path}/-/sse/master%2FREADME.md") - expect(node['data-branch']).to eq('master') - expect(node['data-commit-id']).to match(/\A[0-9a-f]{40}\z/) - expect(node['data-is-supported-content']).to eq('true') - expect(node['data-merge-requests-illustration-path']) - .to match(%r{/assets/illustrations/merge_requests-.*\.svg}) - expect(node['data-namespace']).to eq(project.namespace.full_path) - expect(node['data-project']).to eq(project.path) - expect(node['data-project-id']).to eq(project.id.to_s) - - # assert default config file values are present - expect(node['data-image-upload-path']).to eq('source/images') - expect(node['data-mounts']).to eq('[{"source":"source","target":""}]') - expect(node['data-static-site-generator']).to eq('middleman') - end - end - - context "when a config file is present" do - let(:config_file_yml) do - <<~YAML - image_upload_path: custom-image-upload-path - mounts: - - source: source1 - target: "" - - source: source2 - target: target2 - static_site_generator: middleman - YAML - end - - before do - allow_next_instance_of(Repository) do |repository| - allow(repository).to receive(:blob_data_at).and_return(config_file_yml) - end - - visit sse_path - end - - it 'renders Static Site Editor page values read from config file' do - node = page.find('#static-site-editor') - - # assert user-specified config file values are present - expected_mounts = '[{"source":"source1","target":""},{"source":"source2","target":"target2"}]' - expect(node['data-image-upload-path']).to eq('custom-image-upload-path') - expect(node['data-mounts']).to eq(expected_mounts) - expect(node['data-static-site-generator']).to eq('middleman') - end - end - - describe 'Static Site Editor Content Security Policy' do - subject { response_headers['Content-Security-Policy'] } - - context 'when no global CSP config exists' do - before do - setup_csp_for_controller(Projects::StaticSiteEditorController) - end - - it 'does not add CSP directives' do - visit sse_path - - is_expected.to be_blank - end - end - - context 'when a global CSP config exists' do - let_it_be(:cdn_url) { 'https://some-cdn.test' } - let_it_be(:youtube_url) { 'https://www.youtube.com' } - - before do - csp = ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src :self, cdn_url - end - - setup_existing_csp_for_controller(Projects::StaticSiteEditorController, csp) - end - - it 'appends youtube to the CSP frame-src policy' do - visit sse_path - - is_expected.to eql("frame-src 'self' #{cdn_url} #{youtube_url}") - end - end - end -end diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 0f8daaf8e15..6907701de9c 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'Task Lists', :js do MARKDOWN end - let(:singleIncompleteMarkdown) do + let(:single_incomplete_markdown) do <<-MARKDOWN.strip_heredoc This is a task list: @@ -30,7 +30,7 @@ RSpec.describe 'Task Lists', :js do MARKDOWN end - let(:singleCompleteMarkdown) do + let(:single_complete_markdown) do <<-MARKDOWN.strip_heredoc This is a task list: @@ -94,7 +94,7 @@ RSpec.describe 'Task Lists', :js do end describe 'single incomplete task' do - let!(:issue) { create(:issue, description: singleIncompleteMarkdown, author: user, project: project) } + let!(:issue) { create(:issue, description: single_incomplete_markdown, author: user, project: project) } it 'renders' do visit_issue(project, issue) @@ -113,7 +113,7 @@ RSpec.describe 'Task Lists', :js do end describe 'single complete task' do - let!(:issue) { create(:issue, description: singleCompleteMarkdown, author: user, project: project) } + let!(:issue) { create(:issue, description: single_complete_markdown, author: user, project: project) } it 'renders' do visit_issue(project, issue) @@ -171,7 +171,7 @@ RSpec.describe 'Task Lists', :js do describe 'single incomplete task' do let!(:note) do - create(:note, note: singleIncompleteMarkdown, noteable: issue, + create(:note, note: single_incomplete_markdown, noteable: issue, project: project, author: user) end @@ -186,7 +186,7 @@ RSpec.describe 'Task Lists', :js do describe 'single complete task' do let!(:note) do - create(:note, note: singleCompleteMarkdown, noteable: issue, + create(:note, note: single_complete_markdown, noteable: issue, project: project, author: user) end @@ -264,7 +264,7 @@ RSpec.describe 'Task Lists', :js do end describe 'single incomplete task' do - let!(:merge) { create(:merge_request, :simple, description: singleIncompleteMarkdown, author: user, source_project: project) } + let!(:merge) { create(:merge_request, :simple, description: single_incomplete_markdown, author: user, source_project: project) } it 'renders for description' do visit_merge_request(project, merge) @@ -283,7 +283,7 @@ RSpec.describe 'Task Lists', :js do end describe 'single complete task' do - let!(:merge) { create(:merge_request, :simple, description: singleCompleteMarkdown, author: user, source_project: project) } + let!(:merge) { create(:merge_request, :simple, description: single_complete_markdown, author: user, source_project: project) } it 'renders for description' do visit_merge_request(project, merge) diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 8610cae58a4..822bf898034 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -818,7 +818,6 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do context 'when 2FA is required for the user' do before do - stub_feature_flags(mr_attention_requests: false) group = create(:group, require_two_factor_authentication: true) group.add_developer(user) end @@ -840,7 +839,15 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do expect(page).to have_current_path(profile_two_factor_auth_path, ignore_query: true) - fill_in 'pin_code', with: user.reload.current_otp + # Use the secret shown on the page to generate the OTP that will be entered. + # This detects issues wherein a new secret gets generated after the + # page is shown. + wait_for_requests + + otp_secret = page.find('.two-factor-secret').text.gsub('Key:', '').delete(' ') + current_otp = ROTP::TOTP.new(otp_secret).now + + fill_in 'pin_code', with: current_otp fill_in 'current_password', with: user.password click_button 'Register with two-factor app' |