From d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 20 Oct 2021 08:43:02 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-4-stable-ee --- spec/features/admin/admin_appearance_spec.rb | 4 + spec/features/admin/admin_builds_spec.rb | 141 ----------- spec/features/admin/admin_hook_logs_spec.rb | 4 +- spec/features/admin/admin_jobs_spec.rb | 141 +++++++++++ spec/features/admin/admin_mode/login_spec.rb | 2 +- spec/features/admin/admin_projects_spec.rb | 2 +- .../admin/admin_serverless_domains_spec.rb | 89 ------- spec/features/admin/admin_settings_spec.rb | 66 +++-- spec/features/boards/new_issue_spec.rb | 22 +- .../boards/reload_boards_on_browser_back_spec.rb | 4 +- spec/features/boards/sidebar_labels_spec.rb | 50 +--- spec/features/clusters/cluster_detail_page_spec.rb | 2 +- spec/features/cycle_analytics_spec.rb | 59 ++++- spec/features/dashboard/activity_spec.rb | 6 +- spec/features/dashboard/issuables_counter_spec.rb | 2 +- spec/features/groups/board_spec.rb | 2 +- spec/features/groups/container_registry_spec.rb | 12 + .../groups/dependency_proxy_for_containers_spec.rb | 108 ++++++++ spec/features/groups/dependency_proxy_spec.rb | 50 ++-- .../groups/import_export/connect_instance_spec.rb | 34 +-- .../groups/import_export/migration_history_spec.rb | 30 +++ spec/features/groups/members/manage_groups_spec.rb | 11 +- ...master_adds_member_with_expiration_date_spec.rb | 15 +- spec/features/groups/milestone_spec.rb | 8 +- spec/features/groups/packages_spec.rb | 4 + spec/features/issues/related_issues_spec.rb | 98 +++---- spec/features/markdown/copy_as_gfm_spec.rb | 9 + spec/features/markdown/markdown_spec.rb | 8 +- .../merge_request/user_merges_immediately_spec.rb | 2 +- .../merge_request/user_resolves_conflicts_spec.rb | 19 +- .../user_sees_deployment_widget_spec.rb | 32 ++- .../merge_request/user_sees_merge_widget_spec.rb | 12 +- .../user_sees_suggest_pipeline_spec.rb | 11 +- .../user_selects_branches_for_new_mr_spec.rb | 2 +- .../user_suggests_changes_on_diff_spec.rb | 10 +- spec/features/profiles/password_spec.rb | 10 +- spec/features/profiles/two_factor_auths_spec.rb | 4 +- spec/features/profiles/user_edit_profile_spec.rb | 52 +++- spec/features/projects/badges/coverage_spec.rb | 129 +++++++++- .../projects/badges/pipeline_badge_spec.rb | 2 +- spec/features/projects/ci/lint_spec.rb | 2 +- spec/features/projects/container_registry_spec.rb | 12 + .../projects/environments/environments_spec.rb | 5 +- .../projects/files/user_creates_directory_spec.rb | 2 + .../projects/files/user_uploads_files_spec.rb | 14 +- .../projects/infrastructure_registry_spec.rb | 6 +- .../projects/jobs/user_browses_jobs_spec.rb | 282 +++++++++++++++++++-- spec/features/projects/jobs_spec.rb | 8 +- .../members/groups_with_access_list_spec.rb | 11 +- .../features/projects/members/invite_group_spec.rb | 7 +- ...master_adds_member_with_expiration_date_spec.rb | 18 +- spec/features/projects/navbar_spec.rb | 1 + spec/features/projects/new_project_spec.rb | 8 +- spec/features/projects/packages_spec.rb | 4 + .../projects/settings/monitor_settings_spec.rb | 35 ++- .../projects/settings/webhooks_settings_spec.rb | 4 +- .../projects/show/user_uploads_files_spec.rb | 22 +- .../features/projects/user_creates_project_spec.rb | 23 ++ .../security/project/internal_access_spec.rb | 1 + .../security/project/private_access_spec.rb | 1 + .../security/project/public_access_spec.rb | 1 + .../snippets/notes_on_personal_snippets_spec.rb | 4 +- spec/features/users/login_spec.rb | 18 +- spec/features/users/show_spec.rb | 19 +- 64 files changed, 1213 insertions(+), 563 deletions(-) delete mode 100644 spec/features/admin/admin_builds_spec.rb create mode 100644 spec/features/admin/admin_jobs_spec.rb delete mode 100644 spec/features/admin/admin_serverless_domains_spec.rb create mode 100644 spec/features/groups/dependency_proxy_for_containers_spec.rb create mode 100644 spec/features/groups/import_export/migration_history_spec.rb (limited to 'spec/features') diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index cd148642b90..cb69eac8035 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -34,6 +34,10 @@ RSpec.describe 'Admin Appearance' do visit admin_application_settings_appearances_path click_link "Sign-in page" + expect(find('#login')).to be_disabled + expect(find('#password')).to be_disabled + expect(find('button')).to be_disabled + expect_custom_sign_in_appearance(appearance) end diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb deleted file mode 100644 index 42827dd5b49..00000000000 --- a/spec/features/admin/admin_builds_spec.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Admin Builds' do - before do - admin = create(:admin) - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - end - - describe 'GET /admin/builds' do - let(:pipeline) { create(:ci_pipeline) } - - context 'All tab' do - context 'when have jobs' do - it 'shows all jobs', :js do - create(:ci_build, pipeline: pipeline, status: :pending) - create(:ci_build, pipeline: pipeline, status: :running) - create(:ci_build, pipeline: pipeline, status: :success) - create(:ci_build, pipeline: pipeline, status: :failed) - - visit admin_jobs_path - - expect(page).to have_selector('.nav-links li.active', text: 'All') - expect(page).to have_selector('.row-content-block', text: 'All jobs') - expect(page.all('.build-link').size).to eq(4) - expect(page).to have_button 'Stop all jobs' - - click_button 'Stop all jobs' - expect(page).to have_button 'Stop jobs' - expect(page).to have_content 'Stop all jobs?' - end - end - - context 'when have no jobs' do - it 'shows a message' do - visit admin_jobs_path - - expect(page).to have_selector('.nav-links li.active', text: 'All') - expect(page).to have_content 'No jobs to show' - expect(page).not_to have_button 'Stop all jobs' - end - end - end - - context 'Pending tab' do - context 'when have pending jobs' do - it 'shows pending jobs' do - build1 = create(:ci_build, pipeline: pipeline, status: :pending) - build2 = create(:ci_build, pipeline: pipeline, status: :running) - build3 = create(:ci_build, pipeline: pipeline, status: :success) - build4 = create(:ci_build, pipeline: pipeline, status: :failed) - - visit admin_jobs_path(scope: :pending) - - expect(page).to have_selector('.nav-links li.active', text: 'Pending') - expect(page.find('.build-link')).to have_content(build1.id) - expect(page.find('.build-link')).not_to have_content(build2.id) - expect(page.find('.build-link')).not_to have_content(build3.id) - expect(page.find('.build-link')).not_to have_content(build4.id) - expect(page).to have_button 'Stop all jobs' - end - end - - context 'when have no jobs pending' do - it 'shows a message' do - create(:ci_build, pipeline: pipeline, status: :success) - - visit admin_jobs_path(scope: :pending) - - expect(page).to have_selector('.nav-links li.active', text: 'Pending') - expect(page).to have_content 'No jobs to show' - expect(page).not_to have_button 'Stop all jobs' - end - end - end - - context 'Running tab' do - context 'when have running jobs' do - it 'shows running jobs' do - build1 = create(:ci_build, pipeline: pipeline, status: :running) - build2 = create(:ci_build, pipeline: pipeline, status: :success) - build3 = create(:ci_build, pipeline: pipeline, status: :failed) - build4 = create(:ci_build, pipeline: pipeline, status: :pending) - - visit admin_jobs_path(scope: :running) - - expect(page).to have_selector('.nav-links li.active', text: 'Running') - expect(page.find('.build-link')).to have_content(build1.id) - expect(page.find('.build-link')).not_to have_content(build2.id) - expect(page.find('.build-link')).not_to have_content(build3.id) - expect(page.find('.build-link')).not_to have_content(build4.id) - expect(page).to have_button 'Stop all jobs' - end - end - - context 'when have no jobs running' do - it 'shows a message' do - create(:ci_build, pipeline: pipeline, status: :success) - - visit admin_jobs_path(scope: :running) - - expect(page).to have_selector('.nav-links li.active', text: 'Running') - expect(page).to have_content 'No jobs to show' - expect(page).not_to have_button 'Stop all jobs' - end - end - end - - context 'Finished tab' do - context 'when have finished jobs' do - it 'shows finished jobs' do - build1 = create(:ci_build, pipeline: pipeline, status: :pending) - build2 = create(:ci_build, pipeline: pipeline, status: :running) - build3 = create(:ci_build, pipeline: pipeline, status: :success) - - visit admin_jobs_path(scope: :finished) - - expect(page).to have_selector('.nav-links li.active', text: 'Finished') - expect(page.find('.build-link')).not_to have_content(build1.id) - expect(page.find('.build-link')).not_to have_content(build2.id) - expect(page.find('.build-link')).to have_content(build3.id) - expect(page).to have_button 'Stop all jobs' - end - end - - context 'when have no jobs finished' do - it 'shows a message' do - create(:ci_build, pipeline: pipeline, status: :running) - - visit admin_jobs_path(scope: :finished) - - expect(page).to have_selector('.nav-links li.active', text: 'Finished') - expect(page).to have_content 'No jobs to show' - expect(page).to have_button 'Stop all jobs' - end - end - end - end -end diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb index 3f63bf9a15c..837cab49bd4 100644 --- a/spec/features/admin/admin_hook_logs_spec.rb +++ b/spec/features/admin/admin_hook_logs_spec.rb @@ -17,8 +17,8 @@ RSpec.describe 'Admin::HookLogs' do hook_log visit edit_admin_hook_path(system_hook) - expect(page).to have_content('Recent Deliveries') - expect(page).to have_content(hook_log.url) + expect(page).to have_content('Recent events') + expect(page).to have_link('View details', href: admin_hook_hook_log_path(system_hook, hook_log)) end it 'show hook log details' do diff --git a/spec/features/admin/admin_jobs_spec.rb b/spec/features/admin/admin_jobs_spec.rb new file mode 100644 index 00000000000..36822f89c12 --- /dev/null +++ b/spec/features/admin/admin_jobs_spec.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Admin Jobs' do + before do + admin = create(:admin) + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + end + + describe 'GET /admin/jobs' do + let(:pipeline) { create(:ci_pipeline) } + + context 'All tab' do + context 'when have jobs' do + it 'shows all jobs', :js do + create(:ci_build, pipeline: pipeline, status: :pending) + create(:ci_build, pipeline: pipeline, status: :running) + create(:ci_build, pipeline: pipeline, status: :success) + create(:ci_build, pipeline: pipeline, status: :failed) + + visit admin_jobs_path + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'All') + expect(page).to have_selector('.row-content-block', text: 'All jobs') + expect(page.all('.build-link').size).to eq(4) + expect(page).to have_button 'Stop all jobs' + + click_button 'Stop all jobs' + expect(page).to have_button 'Stop jobs' + expect(page).to have_content 'Stop all jobs?' + end + end + + context 'when have no jobs' do + it 'shows a message' do + visit admin_jobs_path + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'All') + expect(page).to have_content 'No jobs to show' + expect(page).not_to have_button 'Stop all jobs' + end + end + end + + context 'Pending tab' do + context 'when have pending jobs' do + it 'shows pending jobs' do + build1 = create(:ci_build, pipeline: pipeline, status: :pending) + build2 = create(:ci_build, pipeline: pipeline, status: :running) + build3 = create(:ci_build, pipeline: pipeline, status: :success) + build4 = create(:ci_build, pipeline: pipeline, status: :failed) + + visit admin_jobs_path(scope: :pending) + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Pending') + expect(page.find('.build-link')).to have_content(build1.id) + expect(page.find('.build-link')).not_to have_content(build2.id) + expect(page.find('.build-link')).not_to have_content(build3.id) + expect(page.find('.build-link')).not_to have_content(build4.id) + expect(page).to have_button 'Stop all jobs' + end + end + + context 'when have no jobs pending' do + it 'shows a message' do + create(:ci_build, pipeline: pipeline, status: :success) + + visit admin_jobs_path(scope: :pending) + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Pending') + expect(page).to have_content 'No jobs to show' + expect(page).not_to have_button 'Stop all jobs' + end + end + end + + context 'Running tab' do + context 'when have running jobs' do + it 'shows running jobs' do + build1 = create(:ci_build, pipeline: pipeline, status: :running) + build2 = create(:ci_build, pipeline: pipeline, status: :success) + build3 = create(:ci_build, pipeline: pipeline, status: :failed) + build4 = create(:ci_build, pipeline: pipeline, status: :pending) + + visit admin_jobs_path(scope: :running) + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Running') + expect(page.find('.build-link')).to have_content(build1.id) + expect(page.find('.build-link')).not_to have_content(build2.id) + expect(page.find('.build-link')).not_to have_content(build3.id) + expect(page.find('.build-link')).not_to have_content(build4.id) + expect(page).to have_button 'Stop all jobs' + end + end + + context 'when have no jobs running' do + it 'shows a message' do + create(:ci_build, pipeline: pipeline, status: :success) + + visit admin_jobs_path(scope: :running) + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Running') + expect(page).to have_content 'No jobs to show' + expect(page).not_to have_button 'Stop all jobs' + end + end + end + + context 'Finished tab' do + context 'when have finished jobs' do + it 'shows finished jobs' do + build1 = create(:ci_build, pipeline: pipeline, status: :pending) + build2 = create(:ci_build, pipeline: pipeline, status: :running) + build3 = create(:ci_build, pipeline: pipeline, status: :success) + + visit admin_jobs_path(scope: :finished) + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Finished') + expect(page.find('.build-link')).not_to have_content(build1.id) + expect(page.find('.build-link')).not_to have_content(build2.id) + expect(page.find('.build-link')).to have_content(build3.id) + expect(page).to have_button 'Stop all jobs' + end + end + + context 'when have no jobs finished' do + it 'shows a message' do + create(:ci_build, pipeline: pipeline, status: :running) + + visit admin_jobs_path(scope: :finished) + + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Finished') + expect(page).to have_content 'No jobs to show' + expect(page).to have_button 'Stop all jobs' + end + end + end + end +end diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb index 5b2dfdb2941..c8ee6c14499 100644 --- a/spec/features/admin/admin_mode/login_spec.rb +++ b/spec/features/admin/admin_mode/login_spec.rb @@ -121,7 +121,7 @@ RSpec.describe 'Admin Mode Login' do end context 'when logging in via omniauth' do - let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml')} + let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false)} let(:mock_saml_response) do File.read('spec/fixtures/authentication/saml_response.xml') end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 15def00f354..a50ef34d327 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -96,7 +96,7 @@ RSpec.describe "Admin::Projects" do visit admin_project_path(project) click_button 'Search for Namespace' - click_link 'group: web' + click_button 'group: web' click_button 'Transfer' expect(page).to have_content("Web / #{project.name}") diff --git a/spec/features/admin/admin_serverless_domains_spec.rb b/spec/features/admin/admin_serverless_domains_spec.rb deleted file mode 100644 index 0312e82e1ba..00000000000 --- a/spec/features/admin/admin_serverless_domains_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Admin Serverless Domains', :js do - let(:sample_domain) { build(:pages_domain) } - - before do - allow(Gitlab.config.pages).to receive(:enabled).and_return(true) - admin = create(:admin) - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - end - - it 'add domain with certificate' do - visit admin_serverless_domains_path - - fill_in 'pages_domain[domain]', with: 'foo.com' - fill_in 'pages_domain[user_provided_certificate]', with: sample_domain.certificate - fill_in 'pages_domain[user_provided_key]', with: sample_domain.key - click_button 'Add domain' - - expect(current_path).to eq admin_serverless_domains_path - - expect(page).to have_field('pages_domain[domain]', with: 'foo.com') - expect(page).to have_field('serverless_domain_dns', with: /^\*\.foo\.com CNAME /) - expect(page).to have_field('serverless_domain_verification', with: /^_gitlab-pages-verification-code.foo.com TXT /) - expect(page).not_to have_field('pages_domain[user_provided_certificate]') - expect(page).not_to have_field('pages_domain[user_provided_key]') - - expect(page).to have_content 'Unverified' - expect(page).to have_content '/CN=test-certificate' - end - - it 'update domain certificate' do - visit admin_serverless_domains_path - - fill_in 'pages_domain[domain]', with: 'foo.com' - fill_in 'pages_domain[user_provided_certificate]', with: sample_domain.certificate - fill_in 'pages_domain[user_provided_key]', with: sample_domain.key - click_button 'Add domain' - - expect(current_path).to eq admin_serverless_domains_path - - expect(page).not_to have_field('pages_domain[user_provided_certificate]') - expect(page).not_to have_field('pages_domain[user_provided_key]') - - click_button 'Replace' - - expect(page).to have_field('pages_domain[user_provided_certificate]') - expect(page).to have_field('pages_domain[user_provided_key]') - - fill_in 'pages_domain[user_provided_certificate]', with: sample_domain.certificate - fill_in 'pages_domain[user_provided_key]', with: sample_domain.key - - click_button 'Save changes' - - expect(page).to have_content 'Domain was successfully updated' - expect(page).to have_content '/CN=test-certificate' - end - - context 'when domain exists' do - let!(:domain) { create(:pages_domain, :instance_serverless) } - - it 'displays a modal when attempting to delete a domain' do - visit admin_serverless_domains_path - - click_button 'Delete domain' - - page.within '#modal-delete-domain' do - expect(page).to have_content "You are about to delete #{domain.domain} from your instance." - expect(page).to have_link('Delete domain') - end - end - - it 'displays a modal with disabled button if unable to delete a domain' do - create(:serverless_domain_cluster, pages_domain: domain) - - visit admin_serverless_domains_path - - click_button 'Delete domain' - - page.within '#modal-delete-domain' do - expect(page).to have_content "You must disassociate #{domain.domain} from all clusters it is attached to before deletion." - expect(page).to have_link('Delete domain') - end - end - end -end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index b25fc9f257a..1c50a7f891f 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -314,12 +314,14 @@ RSpec.describe 'Admin updates settings' do check 'Default to Auto DevOps pipeline for all projects' fill_in 'application_setting_auto_devops_domain', with: 'domain.com' uncheck 'Keep the latest artifacts for all jobs in the latest successful pipelines' + uncheck 'Enable pipeline suggestion banner' click_button 'Save changes' end expect(current_settings.auto_devops_enabled?).to be true expect(current_settings.auto_devops_domain).to eq('domain.com') expect(current_settings.keep_latest_artifact).to be false + expect(current_settings.suggest_pipeline_enabled).to be false expect(page).to have_content "Application settings saved successfully" end @@ -450,14 +452,14 @@ RSpec.describe 'Admin updates settings' do visit reporting_admin_application_settings_path page.within('.as-spam') do - fill_in 'reCAPTCHA Site Key', with: 'key' - fill_in 'reCAPTCHA Private Key', with: 'key' + fill_in 'reCAPTCHA site key', with: 'key' + fill_in 'reCAPTCHA private key', with: 'key' check 'Enable reCAPTCHA' check 'Enable reCAPTCHA for login' - fill_in 'IPs per user', with: 15 + 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' - fill_in 'Spam Check API Key', with: 'SPAM_CHECK_API_KEY' + fill_in 'Spam Check API key', with: 'SPAM_CHECK_API_KEY' click_button 'Save changes' end @@ -602,18 +604,54 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.issues_create_limit).to eq(0) end - it 'changes Files API rate limits settings' do - visit network_admin_application_settings_path + shared_examples 'regular throttle rate limit settings' do + it 'changes rate limit settings' do + visit network_admin_application_settings_path - page.within('[data-testid="files-limits-settings"]') do - check 'Enable unauthenticated API request rate limit' - fill_in 'Max unauthenticated API requests per period per IP', with: 10 - click_button 'Save changes' + page.within(".#{selector}") do + check 'Enable unauthenticated API request rate limit' + fill_in 'Maximum unauthenticated API requests per rate limit period per IP', with: 12 + fill_in 'Unauthenticated API rate limit period in seconds', with: 34 + + check 'Enable authenticated API request rate limit' + fill_in 'Maximum authenticated API requests per rate limit period per user', with: 56 + fill_in 'Authenticated API rate limit period in seconds', with: 78 + + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + + expect(current_settings).to have_attributes( + "throttle_unauthenticated_#{fragment}_enabled" => true, + "throttle_unauthenticated_#{fragment}_requests_per_period" => 12, + "throttle_unauthenticated_#{fragment}_period_in_seconds" => 34, + "throttle_authenticated_#{fragment}_enabled" => true, + "throttle_authenticated_#{fragment}_requests_per_period" => 56, + "throttle_authenticated_#{fragment}_period_in_seconds" => 78 + ) end + end - expect(page).to have_content "Application settings saved successfully" - expect(current_settings.throttle_unauthenticated_files_api_enabled).to be true - expect(current_settings.throttle_unauthenticated_files_api_requests_per_period).to eq(10) + context 'Package Registry API rate limits' do + let(:selector) { 'as-packages-limits' } + let(:fragment) { :packages_api } + + include_examples 'regular throttle rate limit settings' + end + + context 'Files API rate limits' do + let(:selector) { 'as-files-limits' } + let(:fragment) { :files_api } + + include_examples 'regular throttle rate limit settings' + end + + context 'Deprecated API rate limits' do + let(:selector) { 'as-deprecated-limits' } + let(:fragment) { :deprecated_api } + + include_examples 'regular throttle rate limit settings' end end @@ -623,8 +661,6 @@ RSpec.describe 'Admin updates settings' do end it 'change Help page' do - stub_feature_flags(help_page_documentation_redirect: true) - new_support_url = 'http://example.com/help' new_documentation_url = 'https://docs.gitlab.com' diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index e055e8092d4..f88d31bda88 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -56,7 +56,7 @@ RSpec.describe 'Issue Boards new issue', :js do end end - it 'creates new issue' do + it 'creates new issue and opens sidebar' do page.within(first('.board')) do click_button 'New issue' end @@ -68,7 +68,7 @@ RSpec.describe 'Issue Boards new issue', :js do wait_for_requests - page.within(first('.board .issue-count-badge-count')) do + page.within(first('.board [data-testid="issue-count-badge"]')) do expect(page).to have_content('1') end @@ -78,20 +78,6 @@ RSpec.describe 'Issue Boards new issue', :js do expect(page).to have_content(issue.to_reference) expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/) end - end - - # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/323446 - xit 'shows sidebar when creating new issue' do - page.within(first('.board')) do - click_button 'New issue' - end - - page.within(first('.board-new-issue-form')) do - find('.form-control').set('bug') - click_button 'Create issue' - end - - wait_for_requests expect(page).to have_selector('[data-testid="issue-boards-sidebar"]') end @@ -108,10 +94,6 @@ RSpec.describe 'Issue Boards new issue', :js do wait_for_requests - page.within(first('.board')) do - find('.board-card').click - end - page.within('[data-testid="sidebar-labels"]') do click_button 'Edit' diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb index 36682036d48..6a09e3c9506 100644 --- a/spec/features/boards/reload_boards_on_browser_back_spec.rb +++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do visit project_board_path(project, board) wait_for_requests - page.within(first('.board .issue-count-badge-count')) do + page.within(first('.board [data-testid="issue-count-badge"]')) do expect(page).to have_content('0') end end @@ -35,7 +35,7 @@ RSpec.describe 'Ensure Boards do not show stale data on browser back', :js do page.go_back wait_for_requests - page.within(first('.board .issue-count-badge-count')) do + page.within(first('.board [data-testid="issue-count-badge"]')) do expect(page).to have_content('1') end diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb index fa16f47f69a..511233b50c0 100644 --- a/spec/features/boards/sidebar_labels_spec.rb +++ b/spec/features/boards/sidebar_labels_spec.rb @@ -29,12 +29,11 @@ RSpec.describe 'Project issue boards sidebar labels', :js do end context 'labels' do - # https://gitlab.com/gitlab-org/gitlab/-/issues/322725 - xit 'shows current labels when editing' do + it 'shows current labels when editing' do click_card(card) page.within('.labels') do - click_link 'Edit' + click_button 'Edit' wait_for_requests @@ -54,9 +53,9 @@ RSpec.describe 'Project issue boards sidebar labels', :js do wait_for_requests - click_link bug.title + click_on bug.title - find('[data-testid="close-icon"]').click + click_button 'Close' wait_for_requests @@ -79,11 +78,11 @@ RSpec.describe 'Project issue boards sidebar labels', :js do wait_for_requests - click_link bug.title + click_on bug.title - click_link regression.title + click_on regression.title - find('[data-testid="close-icon"]').click + click_button 'Close' wait_for_requests @@ -108,9 +107,9 @@ RSpec.describe 'Project issue boards sidebar labels', :js do wait_for_requests - click_link stretch.title + click_button stretch.title - find('[data-testid="close-icon"]').click + click_button 'Close' wait_for_requests @@ -125,43 +124,22 @@ RSpec.describe 'Project issue boards sidebar labels', :js do expect(card).not_to have_content(stretch.title) end - # https://gitlab.com/gitlab-org/gitlab/-/issues/324290 - xit 'creates project label' do + it 'creates project label' do click_card(card) page.within('.labels') do - click_link 'Edit' + click_button 'Edit' wait_for_requests - click_link 'Create project label' - fill_in 'new_label_name', with: 'test label' + click_on 'Create project label' + fill_in 'Name new label', with: 'test label' first('.suggest-colors-dropdown a').click click_button 'Create' wait_for_requests - expect(page).to have_link 'test label' + expect(page).to have_button 'test label' end expect(page).to have_selector('.board', count: 3) end - - # https://gitlab.com/gitlab-org/gitlab/-/issues/324290 - xit 'creates project label and list' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - wait_for_requests - - click_link 'Create project label' - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - first('.js-add-list').click - click_button 'Create' - wait_for_requests - - expect(page).to have_link 'test label' - end - expect(page).to have_selector('.board', count: 4) - end end end diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index cba8aaef1ef..06e3e00db7d 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -34,7 +34,7 @@ RSpec.describe 'Clusterable > Show page' do it 'does not show the environments tab' do visit cluster_path - expect(page).not_to have_selector('.js-cluster-nav-environments', text: 'Environments') + expect(page).not_to have_selector('[data-testid="cluster-environments-tab"]') end end diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index bec474f6cfe..34a55118cb3 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -7,10 +7,13 @@ RSpec.describe 'Value Stream Analytics', :js do let_it_be(:guest) { create(:user) } let_it_be(:stage_table_selector) { '[data-testid="vsa-stage-table"]' } let_it_be(:stage_table_event_selector) { '[data-testid="vsa-stage-event"]' } + let_it_be(:stage_table_event_title_selector) { '[data-testid="vsa-stage-event-title"]' } + let_it_be(:stage_table_pagination_selector) { '[data-testid="vsa-stage-pagination"]' } + let_it_be(:stage_table_duration_column_header_selector) { '[data-testid="vsa-stage-header-duration"]' } let_it_be(:metrics_selector) { "[data-testid='vsa-time-metrics']" } let_it_be(:metric_value_selector) { "[data-testid='displayValue']" } - let(:stage_table) { page.find(stage_table_selector) } + let(:stage_table) { find(stage_table_selector) } let(:project) { create(:project, :repository) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } let(:milestone) { create(:milestone, project: project) } @@ -53,6 +56,7 @@ RSpec.describe 'Value Stream Analytics', :js do # So setting the date range to be the last 2 days should skip past the existing data from = 2.days.ago.strftime("%Y-%m-%d") to = 1.day.ago.strftime("%Y-%m-%d") + max_items_per_page = 20 around do |example| travel_to(5.days.ago) { example.run } @@ -60,9 +64,8 @@ RSpec.describe 'Value Stream Analytics', :js do before do project.add_maintainer(user) - create_list(:issue, 2, project: project, created_at: 2.weeks.ago, milestone: milestone) - create_cycle(user, project, issue, mr, milestone, pipeline) + create_list(:issue, max_items_per_page, project: project, created_at: 2.weeks.ago, milestone: milestone) deploy_master(user, project) issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.hour) @@ -81,6 +84,8 @@ RSpec.describe 'Value Stream Analytics', :js do wait_for_requests end + let(:stage_table_events) { stage_table.all(stage_table_event_selector) } + it 'displays metrics' do metrics_tiles = page.find(metrics_selector) @@ -112,20 +117,62 @@ RSpec.describe 'Value Stream Analytics', :js do end it 'can filter the issues by date' do - expect(stage_table.all(stage_table_event_selector).length).to eq(3) + expect(page).to have_selector(stage_table_event_selector) set_daterange(from, to) - expect(stage_table.all(stage_table_event_selector).length).to eq(0) + expect(page).not_to have_selector(stage_table_event_selector) + expect(page).not_to have_selector(stage_table_pagination_selector) end it 'can filter the metrics by date' do - expect(metrics_values).to eq(["3.0", "2.0", "1.0", "0.0"]) + expect(metrics_values).to match_array(["21.0", "2.0", "1.0", "0.0"]) set_daterange(from, to) expect(metrics_values).to eq(['-'] * 4) end + + it 'can sort records' do + # NOTE: checking that the string changes should suffice + # depending on the order the tests are run we might run into problems with hard coded strings + original_first_title = first_stage_title + stage_time_column.click + + expect_to_be_sorted "descending" + expect(first_stage_title).not_to have_text(original_first_title, exact: true) + + stage_time_column.click + + expect_to_be_sorted "ascending" + expect(first_stage_title).to have_text(original_first_title, exact: true) + end + + it 'paginates the results' do + original_first_title = first_stage_title + + expect(page).to have_selector(stage_table_pagination_selector) + + go_to_next_page + + expect(page).not_to have_text(original_first_title, exact: true) + end + + def stage_time_column + stage_table.find(stage_table_duration_column_header_selector).ancestor("th") + end + + def first_stage_title + stage_table.all(stage_table_event_title_selector).first.text + end + + def expect_to_be_sorted(direction) + expect(stage_time_column['aria-sort']).to eq(direction) + end + + def go_to_next_page + page.find(stage_table_pagination_selector).find_link("Next").click + end end end diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb index e75e661b513..7390edc3c47 100644 --- a/spec/features/dashboard/activity_spec.rb +++ b/spec/features/dashboard/activity_spec.rb @@ -13,19 +13,19 @@ RSpec.describe 'Dashboard > Activity' do it 'shows Your Projects' do visit activity_dashboard_path - expect(find('.top-area .nav-tabs li.active')).to have_content('Your projects') + expect(find('[data-testid="dashboard-activity-tabs"] a.active')).to have_content('Your projects') end it 'shows Starred Projects' do visit activity_dashboard_path(filter: 'starred') - expect(find('.top-area .nav-tabs li.active')).to have_content('Starred projects') + expect(find('[data-testid="dashboard-activity-tabs"] a.active')).to have_content('Starred projects') end it 'shows Followed Projects' do visit activity_dashboard_path(filter: 'followed') - expect(find('.top-area .nav-tabs li.active')).to have_content('Followed users') + expect(find('[data-testid="dashboard-activity-tabs"] a.active')).to have_content('Followed users') end end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index d4c6b6faa79..8e938fef155 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -55,7 +55,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d end def expect_counters(issuable_type, count) - dashboard_count = find('.nav-links li.active') + dashboard_count = find('.gl-tabs-nav li a.active') nav_count = find(".dashboard-shortcuts-#{issuable_type}") header_count = find(".header-content .#{issuable_type.tr('_', '-')}-count") diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb index afe36dabcb5..aece6d790b5 100644 --- a/spec/features/groups/board_spec.rb +++ b/spec/features/groups/board_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Group Boards' do it 'adds an issue to the backlog' do page.within(find('.board', match: :first)) do issue_title = 'New Issue' - find(:css, '.issue-count-badge-add-button').click + click_button 'New issue' wait_for_requests diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb index 65374263f45..098559dc3f8 100644 --- a/spec/features/groups/container_registry_spec.rb +++ b/spec/features/groups/container_registry_spec.rb @@ -16,6 +16,7 @@ RSpec.describe 'Container Registry', :js do sign_in(user) stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: []) + stub_container_registry_info end it 'has a page title set' do @@ -57,6 +58,16 @@ RSpec.describe 'Container Registry', :js do expect(page).to have_content 'latest' end + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + before do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + end + + it_behaves_like 'handling feature network errors with the container registry' + end + end + describe 'image repo details' do before do visit_container_registry_details 'my/image' @@ -81,6 +92,7 @@ RSpec.describe 'Container Registry', :js do expect(service).to receive(:execute).with(container_repository) { { status: :success } } expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['latest']) { service } + first('[data-testid="additional-actions"]').click first('[data-testid="single-delete-button"]').click expect(find('.modal .modal-title')).to have_content _('Remove tag') find('.modal .modal-footer .btn-danger').click diff --git a/spec/features/groups/dependency_proxy_for_containers_spec.rb b/spec/features/groups/dependency_proxy_for_containers_spec.rb new file mode 100644 index 00000000000..a4cd6d0f503 --- /dev/null +++ b/spec/features/groups/dependency_proxy_for_containers_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Group Dependency Proxy for containers', :js do + include DependencyProxyHelpers + + include_context 'file upload requests helpers' + + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' } + let_it_be(:content) { fixture_file_upload("spec/fixtures/dependency_proxy/#{sha}.gz").read } + + let(:image) { 'alpine' } + let(:url) { capybara_url("/v2/#{group.full_path}/dependency_proxy/containers/#{image}/blobs/sha256:#{sha}") } + let(:token) { 'token' } + let(:headers) { { 'Authorization' => "Bearer #{build_jwt(user).encoded}" } } + + subject do + HTTParty.get(url, headers: headers) + end + + def run_server(handler) + default_server = Capybara.server + + Capybara.server = Capybara.servers[:puma] + server = Capybara::Server.new(handler) + server.boot + server + ensure + Capybara.server = default_server + end + + let_it_be(:external_server) do + handler = lambda do |env| + if env['REQUEST_PATH'] == '/token' + [200, {}, [{ token: 'token' }.to_json]] + else + [200, {}, [content]] + end + end + + run_server(handler) + end + + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + stub_config(dependency_proxy: { enabled: true }) + group.add_developer(user) + + stub_const("DependencyProxy::Registry::AUTH_URL", external_server.base_url) + stub_const("DependencyProxy::Registry::LIBRARY_URL", external_server.base_url) + end + + shared_examples 'responds with the file' do + it 'sends file' do + expect(subject.code).to eq(200) + expect(subject.body).to eq(content) + expect(subject.headers.to_h).to include( + "content-type" => ["application/gzip"], + "content-disposition" => ["attachment; filename=\"#{sha}.gz\"; filename*=UTF-8''#{sha}.gz"], + "content-length" => ["32"] + ) + end + end + + shared_examples 'caches the file' do + it 'caches the file' do + expect { subject }.to change { + group.dependency_proxy_blobs.count + }.from(0).to(1) + + expect(subject.code).to eq(200) + expect(group.dependency_proxy_blobs.first.file.read).to eq(content) + end + end + + context 'fetching a blob' do + context 'when the blob is cached for the group' do + let!(:dependency_proxy_blob) { create(:dependency_proxy_blob, group: group) } + + it_behaves_like 'responds with the file' + + context 'dependency_proxy_workhorse feature flag disabled' do + before do + stub_feature_flags({ dependency_proxy_workhorse: false }) + end + + it_behaves_like 'responds with the file' + end + end + end + + context 'when the blob must be downloaded' do + it_behaves_like 'responds with the file' + it_behaves_like 'caches the file' + + context 'dependency_proxy_workhorse feature flag disabled' do + before do + stub_feature_flags({ dependency_proxy_workhorse: false }) + end + + it_behaves_like 'responds with the file' + it_behaves_like 'caches the file' + end + end +end diff --git a/spec/features/groups/dependency_proxy_spec.rb b/spec/features/groups/dependency_proxy_spec.rb index 51371ddc532..d6b0bdc8ea4 100644 --- a/spec/features/groups/dependency_proxy_spec.rb +++ b/spec/features/groups/dependency_proxy_spec.rb @@ -3,13 +3,14 @@ require 'spec_helper' RSpec.describe 'Group Dependency Proxy' do - let(:developer) { create(:user) } + let(:owner) { create(:user) } let(:reporter) { create(:user) } let(:group) { create(:group) } let(:path) { group_dependency_proxy_path(group) } + let(:settings_path) { group_settings_packages_and_registries_path(group) } before do - group.add_developer(developer) + group.add_owner(owner) group.add_reporter(reporter) enable_feature @@ -22,42 +23,46 @@ RSpec.describe 'Group Dependency Proxy' do visit path - expect(page).not_to have_css('.js-dependency-proxy-toggle-area') - expect(page).not_to have_css('.js-dependency-proxy-url') + expect(page).not_to have_css('[data-testid="proxy-url"]') end end context 'feature is available', :js do - context 'when logged in as group developer' do + context 'when logged in as group owner' do before do - sign_in(developer) - visit path + sign_in(owner) end it 'sidebar menu is open' do + visit path + sidebar = find('.nav-sidebar') expect(sidebar).to have_link _('Dependency Proxy') end it 'toggles defaults to enabled' do - page.within('.js-dependency-proxy-toggle-area') do - expect(find('.js-project-feature-toggle-input', visible: false).value).to eq('true') - end + visit path + + expect(page).to have_css('[data-testid="proxy-url"]') end it 'shows the proxy URL' do - page.within('.edit_dependency_proxy_group_setting') do - expect(find('.js-dependency-proxy-url').value).to have_content('/dependency_proxy/containers') - end + visit path + + expect(find('input[data-testid="proxy-url"]').value).to have_content('/dependency_proxy/containers') end it 'hides the proxy URL when feature is disabled' do - page.within('.edit_dependency_proxy_group_setting') do - find('.js-project-feature-toggle').click - end + visit settings_path + wait_for_requests + + click_button 'Enable Proxy' + + expect(page).to have_button 'Enable Proxy', class: '!is-checked' + + visit path - expect(page).not_to have_css('.js-dependency-proxy-url') - expect(find('.js-project-feature-toggle-input', visible: false).value).to eq('false') + expect(page).not_to have_css('input[data-testid="proxy-url"]') end end @@ -68,18 +73,17 @@ RSpec.describe 'Group Dependency Proxy' do end it 'does not show the feature toggle but shows the proxy URL' do - expect(page).not_to have_css('.js-dependency-proxy-toggle-area') - expect(find('.js-dependency-proxy-url').value).to have_content('/dependency_proxy/containers') + expect(find('input[data-testid="proxy-url"]').value).to have_content('/dependency_proxy/containers') end end end context 'feature is not avaible' do before do - sign_in(developer) + sign_in(owner) end - context 'feature flag is disabled' do + context 'feature flag is disabled', :js do before do stub_feature_flags(dependency_proxy_for_private_groups: false) end @@ -90,7 +94,7 @@ RSpec.describe 'Group Dependency Proxy' do it 'informs user that feature is only available for public groups' do visit path - expect(page).to have_content('Dependency proxy feature is limited to public groups for now.') + expect(page).to have_content('Dependency Proxy feature is limited to public groups for now.') end end end diff --git a/spec/features/groups/import_export/connect_instance_spec.rb b/spec/features/groups/import_export/connect_instance_spec.rb index cf893e444c4..552b599a3f3 100644 --- a/spec/features/groups/import_export/connect_instance_spec.rb +++ b/spec/features/groups/import_export/connect_instance_spec.rb @@ -19,34 +19,12 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do end context 'when the user provides valid credentials' do + source_url = 'https://gitlab.com' + + include_context 'bulk imports requests context', source_url + it 'successfully connects to remote instance' do - source_url = 'https://gitlab.com' pat = 'demo-pat' - stub_path = 'stub-group' - total = 37 - - stub_request(:get, "%{url}/api/v4/groups?page=1&per_page=20&top_level_only=true&min_access_level=50&search=" % { url: source_url }).to_return( - body: [{ - id: 2595438, - web_url: 'https://gitlab.com/groups/auto-breakfast', - name: 'Stub', - path: stub_path, - full_name: 'Stub', - full_path: stub_path - }].to_json, - headers: { - 'Content-Type' => 'application/json', - 'X-Next-Page' => 2, - 'X-Page' => 1, - 'X-Per-Page' => 20, - 'X-Total' => total, - 'X-Total-Pages' => 2 - } - ) - - allow_next_instance_of(BulkImports::Clients::HTTP) do |client| - allow(client).to receive(:validate_instance_version!).and_return(true) - end expect(page).to have_content 'Import groups from another instance of GitLab' expect(page).to have_content 'Not all related objects are migrated' @@ -56,8 +34,8 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do click_on 'Connect instance' - expect(page).to have_content 'Showing 1-1 of %{total} groups from %{url}' % { url: source_url, total: total } - expect(page).to have_content stub_path + expect(page).to have_content 'Showing 1-1 of 42 groups from %{url}' % { url: source_url } + expect(page).to have_content 'stub-group' visit '/' diff --git a/spec/features/groups/import_export/migration_history_spec.rb b/spec/features/groups/import_export/migration_history_spec.rb new file mode 100644 index 00000000000..243bdcc13a9 --- /dev/null +++ b/spec/features/groups/import_export/migration_history_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Import/Export - GitLab migration history', :js do + let_it_be(:user) { create(:user) } + + let_it_be(:user_import_1) { create(:bulk_import, user: user) } + let_it_be(:finished_entity_1) { create(:bulk_import_entity, :finished, bulk_import: user_import_1) } + + let_it_be(:user_import_2) { create(:bulk_import, user: user) } + let_it_be(:failed_entity_2) { create(:bulk_import_entity, :failed, bulk_import: user_import_2) } + + before do + gitlab_sign_in(user) + + visit new_group_path + + click_link 'Import group' + end + + it 'successfully displays import history' do + click_link 'History' + + wait_for_requests + + expect(page).to have_content 'Group import history' + expect(page.find('tbody')).to have_css('tr', count: 2) + end +end diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb index 2dfcd941b4f..d822a5ea871 100644 --- a/spec/features/groups/members/manage_groups_spec.rb +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -63,6 +63,7 @@ RSpec.describe 'Groups > Members > Manage groups', :js do context 'when group link exists' do let_it_be(:shared_with_group) { create(:group) } let_it_be(:shared_group) { create(:group) } + let_it_be(:expiration_date) { 5.days.from_now.to_date } let(:additional_link_attrs) { {} } @@ -115,29 +116,29 @@ RSpec.describe 'Groups > Members > Manage groups', :js do click_groups_tab page.within first_row do - fill_in 'Expiration date', with: 5.days.from_now.to_date + fill_in 'Expiration date', with: expiration_date find_field('Expiration date').native.send_keys :enter wait_for_requests - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) end end context 'when expiry date is set' do - let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } } + let(:additional_link_attrs) { { expires_at: expiration_date } } it 'clears expiry date' do click_groups_tab page.within first_row do - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) find('[data-testid="clear-button"]').click wait_for_requests - expect(page).to have_content('No expiration set') + expect(page).to have_field('Expiration date', with: '') end end end diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb index ddf3c6d8f9b..86185b8dd32 100644 --- a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js let_it_be(:user1) { create(:user, name: 'John Doe') } let_it_be(:group) { create(:group) } + let_it_be(:expiration_date) { 5.days.from_now.to_date } let(:new_member) { create(:user, name: 'Mary Jane') } @@ -19,10 +20,10 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js it 'expiration date is displayed in the members list' do visit group_group_members_path(group) - invite_member(new_member.name, role: 'Guest', expires_at: 5.days.from_now.to_date) + invite_member(new_member.name, role: 'Guest', expires_at: expiration_date) page.within second_row do - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) end end @@ -31,27 +32,27 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js visit group_group_members_path(group) page.within second_row do - fill_in 'Expiration date', with: 5.days.from_now.to_date + fill_in 'Expiration date', with: expiration_date find_field('Expiration date').native.send_keys :enter wait_for_requests - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) end end it 'clears expiration date' do - create(:group_member, :developer, user: new_member, group: group, expires_at: 5.days.from_now.to_date) + create(:group_member, :developer, user: new_member, group: group, expires_at: expiration_date) visit group_group_members_path(group) page.within second_row do - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) find('[data-testid="clear-button"]').click wait_for_requests - expect(page).to have_content('No expiration set') + expect(page).to have_field('Expiration date', with: '') end end end diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index c51ee250331..4edf27e8fa4 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -98,9 +98,11 @@ RSpec.describe 'Group milestones' do end it 'counts milestones correctly' do - expect(find('.top-area .active .badge').text).to eq("3") - expect(find('.top-area .closed .badge').text).to eq("3") - expect(find('.top-area .all .badge').text).to eq("6") + page.within '[data-testid="milestones-filter"]' do + expect(page).to have_content('Open 3') + expect(page).to have_content('Closed 3') + expect(page).to have_content('All 6') + end end it 'lists group and project milestones' do diff --git a/spec/features/groups/packages_spec.rb b/spec/features/groups/packages_spec.rb index 3c2ade6b274..0dfc7180187 100644 --- a/spec/features/groups/packages_spec.rb +++ b/spec/features/groups/packages_spec.rb @@ -28,6 +28,10 @@ RSpec.describe 'Group Packages' do context 'when feature is available', :js do before do + # we are simply setting the featrure flag to false because the new UI has nothing to test yet + # when the refactor is complete or almost complete we will turn on the feature tests + # see https://gitlab.com/gitlab-org/gitlab/-/issues/330846 for status of this work + stub_feature_flags(package_list_apollo: false) visit_group_packages end diff --git a/spec/features/issues/related_issues_spec.rb b/spec/features/issues/related_issues_spec.rb index 837859bbe26..a95229d4f1b 100644 --- a/spec/features/issues/related_issues_spec.rb +++ b/spec/features/issues/related_issues_spec.rb @@ -41,13 +41,13 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end end context 'when logged in but not a member' do before do - gitlab_sign_in(user) + sign_in(user) end it 'shows widget when internal project' do @@ -57,7 +57,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end it 'does not show widget when private project' do @@ -76,7 +76,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end it 'shows widget on their own public issue' do @@ -86,13 +86,13 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end end context 'when logged in and a guest' do before do - gitlab_sign_in(user) + sign_in(user) end it 'shows widget when internal project' do @@ -103,7 +103,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end it 'shows widget when private project' do @@ -114,7 +114,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end it 'shows widget when public project' do @@ -125,13 +125,13 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).not_to have_selector('.js-issue-count-badge-add-button') + expect(page).not_to have_button 'Add a related issue' end end context 'when logged in and a reporter' do before do - gitlab_sign_in(user) + sign_in(user) end it 'shows widget when internal project' do @@ -142,7 +142,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).to have_selector('.js-issue-count-badge-add-button') + expect(page).to have_button 'Add a related issue' end it 'shows widget when private project' do @@ -153,7 +153,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).to have_selector('.js-issue-count-badge-add-button') + expect(page).to have_button 'Add a related issue' end it 'shows widget when public project' do @@ -164,7 +164,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).to have_selector('.js-issue-count-badge-add-button') + expect(page).to have_button 'Add a related issue' end it 'shows widget on their own public issue' do @@ -175,7 +175,7 @@ RSpec.describe 'Related issues', :js do visit project_issue_path(project, issue) expect(page).to have_css('.related-issues-block') - expect(page).to have_selector('.js-issue-count-badge-add-button') + expect(page).to have_button 'Add a related issue' end end end @@ -186,7 +186,7 @@ RSpec.describe 'Related issues', :js do before do project.add_guest(user) - gitlab_sign_in(user) + sign_in(user) end context 'visiting some issue someone else created' do @@ -216,7 +216,7 @@ RSpec.describe 'Related issues', :js do before do project.add_maintainer(user) project_b.add_maintainer(user) - gitlab_sign_in(user) + sign_in(user) end context 'without existing related issues' do @@ -230,9 +230,9 @@ RSpec.describe 'Related issues', :js do end it 'add related issue' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set "#{issue_b.to_reference(project)} " - find('.js-add-issuable-form-add-button').click + click_button 'Add a related issue' + fill_in 'Paste issue link', with: "#{issue_b.to_reference(project)} " + click_button 'Add' wait_for_requests @@ -247,9 +247,9 @@ RSpec.describe 'Related issues', :js do end it 'add cross-project related issue' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} " - find('.js-add-issuable-form-add-button').click + click_button 'Add a related issue' + fill_in 'Paste issue link', with: "#{issue_project_b_a.to_reference(project)} " + click_button 'Add' wait_for_requests @@ -261,9 +261,9 @@ RSpec.describe 'Related issues', :js do end it 'pressing enter should submit the form' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} " - find('.js-add-issuable-form-input').native.send_key(:enter) + click_button 'Add a related issue' + fill_in 'Paste issue link', with: "#{issue_project_b_a.to_reference(project)} " + find_field('Paste issue link').native.send_key(:enter) wait_for_requests @@ -275,10 +275,10 @@ RSpec.describe 'Related issues', :js do end it 'disallows duplicate entries' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set 'duplicate duplicate duplicate' + click_button 'Add a related issue' + fill_in 'Paste issue link', with: 'duplicate duplicate duplicate' - items = all('.js-add-issuable-form-token-list-item') + items = all('.issue-token') expect(items.count).to eq(1) expect(items[0].text).to eq('duplicate') @@ -288,29 +288,35 @@ RSpec.describe 'Related issues', :js do it 'allows us to remove pending issues' do # Tests against https://gitlab.com/gitlab-org/gitlab/issues/11625 - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set 'issue1 issue2 issue3 ' + click_button 'Add a related issue' + fill_in 'Paste issue link', with: 'issue1 issue2 issue3 ' - items = all('.js-add-issuable-form-token-list-item') + items = all('.issue-token') expect(items.count).to eq(3) expect(items[0].text).to eq('issue1') expect(items[1].text).to eq('issue2') expect(items[2].text).to eq('issue3') # Remove pending issues left to right to make sure none get stuck - items[0].find('.js-issue-token-remove-button').click - items = all('.js-add-issuable-form-token-list-item') + within items[0] do + click_button 'Remove' + end + items = all('.issue-token') expect(items.count).to eq(2) expect(items[0].text).to eq('issue2') expect(items[1].text).to eq('issue3') - items[0].find('.js-issue-token-remove-button').click - items = all('.js-add-issuable-form-token-list-item') + within items[0] do + click_button 'Remove' + end + items = all('.issue-token') expect(items.count).to eq(1) expect(items[0].text).to eq('issue3') - items[0].find('.js-issue-token-remove-button').click - items = all('.js-add-issuable-form-token-list-item') + within items[0] do + click_button 'Remove' + end + items = all('.issue-token') expect(items.count).to eq(0) end end @@ -351,9 +357,9 @@ RSpec.describe 'Related issues', :js do end it 'add related issue' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set "##{issue_d.iid} " - find('.js-add-issuable-form-add-button').click + click_button 'Add a related issue' + fill_in 'Paste issue link', with: "##{issue_d.iid} " + click_button 'Add' wait_for_requests @@ -367,9 +373,9 @@ RSpec.describe 'Related issues', :js do end it 'add invalid related issue' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set "#9999999 " - find('.js-add-issuable-form-add-button').click + click_button 'Add a related issue' + fill_in 'Paste issue link', with: '#9999999 ' + click_button 'Add' wait_for_requests @@ -382,9 +388,9 @@ RSpec.describe 'Related issues', :js do end it 'add unauthorized related issue' do - find('.js-issue-count-badge-add-button').click - find('.js-add-issuable-form-input').set "#{issue_project_unauthorized_a.to_reference(project)} " - find('.js-add-issuable-form-add-button').click + click_button 'Add a related issue' + fill_in 'Paste issue link', with: "#{issue_project_unauthorized_a.to_reference(project)} " + click_button 'Add' wait_for_requests diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index c700f878df6..d3aaf339421 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -201,6 +201,15 @@ RSpec.describe 'Copy as GFM', :js do GFM ) + aggregate_failures('CustomEmojiFilter') do + gfm = ':custom_emoji:' + + html = '' + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do gfm = '$`c = \pm\sqrt{a^2 + b^2}`$' diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index 3208ad82c03..9eff02a8c1b 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -133,8 +133,9 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do expect(doc.at_css('td:contains("Baz")')['align']).to eq 'left' end + # note that 2 are from the hardcoded , and 2 from footnotes aggregate_failures 'permits superscript elements' do - expect(doc).to have_selector('sup', count: 2) + expect(doc).to have_selector('sup', count: 4) end aggregate_failures 'permits subscript elements' do @@ -148,6 +149,11 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do aggregate_failures "removes `href` from `a` elements if it's fishy" do expect(doc).not_to have_selector('a[href*="javascript"]') end + + aggregate_failures 'permits footnotes' do + expect(doc).to have_selector('section.footnotes ol li p:contains("Footnote 1")') + expect(doc).to have_selector('section.footnotes ol li p:contains("Footnote with w")') + end end describe 'Escaping' do diff --git a/spec/features/merge_request/user_merges_immediately_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb index bca6e6ceba5..3a05f35a671 100644 --- a/spec/features/merge_request/user_merges_immediately_spec.rb +++ b/spec/features/merge_request/user_merges_immediately_spec.rb @@ -36,7 +36,7 @@ RSpec.describe 'Merge requests > User merges immediately', :js do Sidekiq::Testing.fake! do click_button 'Merge immediately' - expect(find('.accept-merge-request.btn-confirm')).to have_content('Merge in progress') + expect(find('.media-body h4')).to have_content('Merging!') wait_for_requests end diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb index 03ab42aaccd..982e75760d7 100644 --- a/spec/features/merge_request/user_resolves_conflicts_spec.rb +++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Merge request > User resolves conflicts', :js do let(:user) { project.creator } def create_merge_request(source_branch) - create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr| + create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project, merge_status: :unchecked, reviewers: [user]) do |mr| mr.mark_as_unmergeable end end @@ -178,6 +178,23 @@ RSpec.describe 'Merge request > User resolves conflicts', :js do end end + context 'sidebar' do + let(:merge_request) { create_merge_request('conflict-resolvable') } + + before do + project.add_developer(user) + sign_in(user) + + visit conflicts_project_merge_request_path(project, merge_request) + end + + it 'displays reviewers' do + page.within '.issuable-sidebar' do + expect(page).to have_selector('[data-testid="reviewer"]', count: 1) + end + end + end + unresolvable_conflicts = { 'conflict-too-large' => 'when the conflicts contain a large file', 'conflict-binary-file' => 'when the conflicts contain a binary file', diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb index 1e547d504ef..873cc0a89c6 100644 --- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb +++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do let(:sha) { project.commit(ref).id } let(:pipeline) { create(:ci_pipeline, sha: sha, project: project, ref: ref) } let!(:manual) { } + let(:build) { create(:ci_build, :with_deployment, environment: environment.name, pipeline: pipeline) } + let!(:deployment) { build.deployment } before do merge_request.update!(merge_commit_sha: sha) @@ -21,8 +23,9 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do end context 'when deployment succeeded' do - let(:build) { create(:ci_build, :success, pipeline: pipeline) } - let!(:deployment) { create(:deployment, :succeed, environment: environment, sha: sha, ref: ref, deployable: build) } + before do + build.success! + end it 'displays that the environment is deployed' do visit project_merge_request_path(project, merge_request) @@ -34,9 +37,8 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do context 'when a user created a new merge request with the same SHA' do let(:pipeline2) { create(:ci_pipeline, sha: sha, project: project, ref: 'video') } - let(:build2) { create(:ci_build, :success, pipeline: pipeline2) } let(:environment2) { create(:environment, project: project) } - let!(:deployment2) { create(:deployment, environment: environment2, sha: sha, ref: 'video', deployable: build2) } + let!(:build2) { create(:ci_build, :with_deployment, :success, environment: environment2.name, pipeline: pipeline2) } it 'displays one environment which is related to the pipeline' do visit project_merge_request_path(project, merge_request) @@ -50,8 +52,9 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do end context 'when deployment failed' do - let(:build) { create(:ci_build, :failed, pipeline: pipeline) } - let!(:deployment) { create(:deployment, :failed, environment: environment, sha: sha, ref: ref, deployable: build) } + before do + build.drop! + end it 'displays that the deployment failed' do visit project_merge_request_path(project, merge_request) @@ -63,8 +66,9 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do end context 'when deployment running' do - let(:build) { create(:ci_build, :running, pipeline: pipeline) } - let!(:deployment) { create(:deployment, :running, environment: environment, sha: sha, ref: ref, deployable: build) } + before do + build.run! + end it 'displays that the running deployment' do visit project_merge_request_path(project, merge_request) @@ -76,8 +80,8 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do end context 'when deployment will happen' do - let(:build) { create(:ci_build, :created, pipeline: pipeline) } - let!(:deployment) { create(:deployment, environment: environment, sha: sha, ref: ref, deployable: build) } + let(:build) { create(:ci_build, :with_deployment, environment: environment.name, pipeline: pipeline) } + let!(:deployment) { build.deployment } it 'displays that the environment name' do visit project_merge_request_path(project, merge_request) @@ -89,8 +93,9 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do end context 'when deployment was cancelled' do - let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } - let!(:deployment) { create(:deployment, :canceled, environment: environment, sha: sha, ref: ref, deployable: build) } + before do + build.cancel! + end it 'displays that the environment name' do visit project_merge_request_path(project, merge_request) @@ -102,11 +107,10 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do end context 'with stop action' do - let(:build) { create(:ci_build, :success, pipeline: pipeline) } - let!(:deployment) { create(:deployment, :succeed, environment: environment, sha: sha, ref: ref, deployable: build) } let(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') } before do + build.success! deployment.update!(on_stop: manual.name) visit project_merge_request_path(project, merge_request) wait_for_requests 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 2f7758143a1..f74b097ab3e 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -45,18 +45,12 @@ RSpec.describe 'Merge request > User sees merge widget', :js do let!(:environment) { create(:environment, project: project) } let(:sha) { project.commit(merge_request.source_branch).sha } let(:pipeline) { create(:ci_pipeline, status: 'success', sha: sha, project: project, ref: merge_request.source_branch) } - let(:build) { create(:ci_build, :success, pipeline: pipeline) } - - let!(:deployment) do - create(:deployment, :succeed, - environment: environment, - ref: merge_request.source_branch, - deployable: build, - sha: sha) - end + let!(:build) { create(:ci_build, :with_deployment, :success, environment: environment.name, pipeline: pipeline) } + let!(:deployment) { build.deployment } before do merge_request.update!(head_pipeline: pipeline) + deployment.update!(status: :success) visit project_merge_request_path(project, merge_request) end diff --git a/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb b/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb index 4bb6c3265a4..3893a9cdf28 100644 --- a/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb +++ b/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb @@ -6,9 +6,10 @@ RSpec.describe 'Merge request > User sees suggest pipeline', :js do let(:merge_request) { create(:merge_request) } let(:project) { merge_request.source_project } let(:user) { project.creator } + let(:suggest_pipeline_enabled) { true } before do - stub_application_setting(auto_devops_enabled: false) + stub_application_setting(suggest_pipeline_enabled: suggest_pipeline_enabled, auto_devops_enabled: false) project.add_maintainer(user) sign_in(user) visit project_merge_request_path(project, merge_request) @@ -66,4 +67,12 @@ RSpec.describe 'Merge request > User sees suggest pipeline', :js do # nudge 4 expect(page).to have_content("That's it, well done!") end + + context 'when feature setting is disabled' do + let(:suggest_pipeline_enabled) { false } + + it 'does not show the suggest pipeline widget' do + expect(page).not_to have_content('Are you adding technical debt or code vulnerabilities?') + end + end end diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb index 275a87ca391..d2bde320c54 100644 --- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb +++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb @@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js do click_button "Check out branch" - expect(page).to have_content 'git checkout -b "orphaned-branch" "origin/orphaned-branch"' + expect(page).to have_content 'git checkout -b \'orphaned-branch\' \'origin/orphaned-branch\'' end it 'allows filtering multiple dropdowns' 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 dbc88d0cce2..690a292937a 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 @@ -159,7 +159,12 @@ RSpec.describe 'User comments on a diff', :js do wait_for_requests expect(page).to have_content('Remove from batch') - expect(page).to have_content("Apply suggestions #{index + 1}") + + if index < 1 + expect(page).to have_content("Apply suggestion") + else + expect(page).to have_content("Apply #{index + 1} suggestions") + end end end @@ -167,13 +172,12 @@ RSpec.describe 'User comments on a diff', :js do click_button('Remove from batch') wait_for_requests - expect(page).to have_content('Apply suggestion') expect(page).to have_content('Add suggestion to batch') end page.within("[id='#{files[1][:hash]}']") do expect(page).to have_content('Remove from batch') - expect(page).to have_content('Apply suggestions 1') + expect(page).to have_content('Apply suggestion') end end diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 893dd2c76e0..7059697354d 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -89,7 +89,7 @@ RSpec.describe 'Profile > Password' do shared_examples 'user enters an incorrect current password' do subject do page.within '.update-password' do - fill_in 'user_current_password', with: user_current_password + fill_in 'user_password', with: user_current_password fill_passwords(new_password, new_password) end end @@ -131,7 +131,7 @@ RSpec.describe 'Profile > Password' do end context 'when current password is incorrect' do - let(:user_current_password) {'invalid' } + let(:user_current_password) { 'invalid' } it_behaves_like 'user enters an incorrect current password' end @@ -139,7 +139,7 @@ RSpec.describe 'Profile > Password' do context 'when the password reset is successful' do subject do page.within '.update-password' do - fill_in "user_current_password", with: user.password + fill_in "user_password", with: user.password fill_passwords(new_password, new_password) end end @@ -169,8 +169,8 @@ RSpec.describe 'Profile > Password' do expect(current_path).to eq new_profile_password_path - fill_in :user_current_password, with: user.password - fill_in :user_password, with: '12345678' + fill_in :user_password, with: user.password + fill_in :user_new_password, with: '12345678' fill_in :user_password_confirmation, with: '12345678' click_button 'Set new password' diff --git a/spec/features/profiles/two_factor_auths_spec.rb b/spec/features/profiles/two_factor_auths_spec.rb index 7f3ce617846..3f5789e119a 100644 --- a/spec/features/profiles/two_factor_auths_spec.rb +++ b/spec/features/profiles/two_factor_auths_spec.rb @@ -29,7 +29,7 @@ RSpec.describe 'Two factor auths' do end context 'when user authenticates with an external service' do - let_it_be(:user) { create(:omniauth_user, password_automatically_set: true) } + let_it_be(:user) { create(:omniauth_user) } it 'does not require the current password to set up two factor authentication', :js do visit profile_two_factor_auth_path @@ -88,7 +88,7 @@ RSpec.describe 'Two factor auths' do end context 'when user authenticates with an external service' do - let_it_be(:user) { create(:omniauth_user, :two_factor, password_automatically_set: true) } + let_it_be(:user) { create(:omniauth_user, :two_factor) } it 'does not require the current_password to disable two-factor authentication', :js do visit profile_two_factor_auth_path diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index af085b63155..026da5814e3 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -19,6 +19,17 @@ RSpec.describe 'User edit profile' do wait_for_requests if respond_to?(:wait_for_requests) end + def update_user_email + fill_in 'user_email', with: 'new-email@example.com' + click_button 'Update profile settings' + end + + def confirm_password(password) + fill_in 'password-confirmation', with: password + click_button 'Confirm password' + wait_for_requests if respond_to?(:wait_for_requests) + end + def visit_user visit user_path(user) wait_for_requests @@ -88,16 +99,42 @@ RSpec.describe 'User edit profile' do expect(page).to have_content('Website url is not a valid URL') end - describe 'when I change my email' do + describe 'when I change my email', :js do before do user.send_reset_password_instructions end + it 'will prompt to confirm my password' do + expect(user.reset_password_token?).to be true + + update_user_email + + expect(page).to have_selector('[data-testid="password-prompt-modal"]') + end + + context 'when prompted to confirm password' do + before do + update_user_email + end + + it 'with the correct password successfully updates' do + confirm_password(user.password) + + expect(page).to have_text("Profile was successfully updated") + end + + it 'with the incorrect password fails to update' do + confirm_password("Fake password") + + expect(page).to have_text("Invalid password") + end + end + it 'clears the reset password token' do expect(user.reset_password_token?).to be true - fill_in 'user_email', with: 'new-email@example.com' - submit_settings + update_user_email + confirm_password(user.password) user.reload expect(user.confirmation_token).not_to be_nil @@ -524,14 +561,11 @@ RSpec.describe 'User edit profile' do page.find("a", text: "Nuku'alofa").click - tz = page.find('.user-time-preferences #user_timezone', visible: false) - - expect(tz.value).to eq('Pacific/Tongatapu') + expect(page).to have_field(:user_timezone, with: 'Pacific/Tongatapu', type: :hidden) end - it 'timezone defaults to servers default' do - timezone_name = Time.zone.tzinfo.name - expect(page.find('.user-time-preferences #user_timezone', visible: false).value).to eq(timezone_name) + it 'timezone defaults to empty' do + expect(page).to have_field(:user_timezone, with: '', type: :hidden) end end end diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb index 1760ec880bc..5c1bc1ad239 100644 --- a/spec/features/projects/badges/coverage_spec.rb +++ b/spec/features/projects/badges/coverage_spec.rb @@ -12,6 +12,120 @@ RSpec.describe 'test coverage badge' do sign_in(user) end + it 'user requests coverage badge image for pipeline with custom limits - 80% good' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 80, name: 'test:1') + end + + show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25) + + expect_coverage_badge_color(:good) + expect_coverage_badge('80.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 74% - bad config' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 74, name: 'test:1') + end + # User sets a minimum good value that is lower than min acceptable and min medium, + # in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1 + show_test_coverage_badge(min_good: 75, min_acceptable: 76, min_medium: 77) + + expect_coverage_badge_color(:acceptable) + expect_coverage_badge('74.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 73% - bad config' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 73, name: 'test:1') + end + # User sets a minimum good value that is lower than min acceptable and min medium, + # in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1 + show_test_coverage_badge(min_good: 75, min_acceptable: 76, min_medium: 77) + + expect_coverage_badge_color(:medium) + expect_coverage_badge('73.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 72% - partial config - low' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 72, name: 'test:1') + end + # User only sets good to 75 and leaves the others on the default settings, + # in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1 + show_test_coverage_badge(min_good: 75) + + expect_coverage_badge_color(:low) + expect_coverage_badge('72.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 72% - partial config - medium' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 72, name: 'test:1') + end + # User only sets good to 74 and leaves the others on the default settings, + # in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1 + show_test_coverage_badge(min_good: 74) + + expect_coverage_badge_color(:medium) + expect_coverage_badge('72.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 72% - partial config - medium v2' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 72, name: 'test:1') + end + # User only sets medium to 72 and leaves the others on the defaults good as 95 and acceptable as 90 + show_test_coverage_badge(min_medium: 72) + + expect_coverage_badge_color(:medium) + expect_coverage_badge('72.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 70% acceptable' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 70, name: 'test:1') + end + + show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25) + + expect_coverage_badge_color(:acceptable) + expect_coverage_badge('70.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 30% medium' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 30, name: 'test:1') + end + + show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25) + + expect_coverage_badge_color(:medium) + expect_coverage_badge('30.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - 20% low' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 20, name: 'test:1') + end + + show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25) + + expect_coverage_badge_color(:low) + expect_coverage_badge('20.00%') + end + + it 'user requests coverage badge image for pipeline with custom limits - nonsense values which use the defaults' do + create_pipeline do |pipeline| + create_build(pipeline, coverage: 92, name: 'test:1') + end + + show_test_coverage_badge(min_good: "nonsense", min_acceptable: "rubbish", min_medium: "NaN") + + expect_coverage_badge_color(:acceptable) + expect_coverage_badge('92.00%') + end + it 'user requests coverage badge image for pipeline' do create_pipeline do |pipeline| create_build(pipeline, coverage: 100, name: 'test:1') @@ -20,6 +134,7 @@ RSpec.describe 'test coverage badge' do show_test_coverage_badge + expect_coverage_badge_color(:good) expect_coverage_badge('95.00%') end @@ -32,6 +147,7 @@ RSpec.describe 'test coverage badge' do show_test_coverage_badge(job: 'coverage') + expect_coverage_badge_color(:medium) expect_coverage_badge('85.00%') end @@ -73,8 +189,9 @@ RSpec.describe 'test coverage badge' do create(:ci_build, :success, opts) end - def show_test_coverage_badge(job: nil) - visit coverage_project_badges_path(project, ref: :master, job: job, format: :svg) + def show_test_coverage_badge(job: nil, min_good: nil, min_acceptable: nil, min_medium: nil) + visit coverage_project_badges_path(project, ref: :master, job: job, min_good: min_good, + min_acceptable: min_acceptable, min_medium: min_medium, format: :svg) end def expect_coverage_badge(coverage) @@ -82,4 +199,12 @@ RSpec.describe 'test coverage badge' do expect(page.response_headers['Content-Type']).to include('image/svg+xml') expect(svg.at(%Q{text:contains("#{coverage}")})).to be_truthy end + + def expect_coverage_badge_color(color) + svg = Nokogiri::HTML(page.body) + expect(page.response_headers['Content-Type']).to include('image/svg+xml') + badge_color = svg.xpath("//path[starts-with(@d, 'M62')]")[0].attributes['fill'].to_s + expected_badge_color = Gitlab::Ci::Badge::Coverage::Template::STATUS_COLOR[color] + expect(badge_color).to eq(expected_badge_color) + end end diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb index 9d8f9872a1a..e3a01ab6fa2 100644 --- a/spec/features/projects/badges/pipeline_badge_spec.rb +++ b/spec/features/projects/badges/pipeline_badge_spec.rb @@ -68,7 +68,7 @@ RSpec.describe 'Pipeline Badge' do visit pipeline_project_badges_path(project, ref: ref, format: :svg) expect(page.status_code).to eq(200) - expect(page.response_headers['Cache-Control']).to eq('no-store') + expect(page.response_headers['Cache-Control']).to eq('private, no-store') end end diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb index 0d9ea6331a7..7f10c6afcd5 100644 --- a/spec/features/projects/ci/lint_spec.rb +++ b/spec/features/projects/ci/lint_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'CI Lint', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297782' do +RSpec.describe 'CI Lint', :js do include Spec::Support::Helpers::Features::SourceEditorSpecHelpers let(:project) { create(:project, :repository) } diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb index 40d0260eafd..eec50c3a66a 100644 --- a/spec/features/projects/container_registry_spec.rb +++ b/spec/features/projects/container_registry_spec.rb @@ -20,6 +20,7 @@ RSpec.describe 'Container Registry', :js do sign_in(user) project.add_developer(user) stub_container_registry_config(enabled: true) + stub_container_registry_info stub_container_registry_tags(repository: :any, tags: []) end @@ -96,6 +97,7 @@ RSpec.describe 'Container Registry', :js do expect(service).to receive(:execute).with(container_repository) { { status: :success } } expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(container_repository.project, user, tags: ['1']) { service } + first('[data-testid="additional-actions"]').click first('[data-testid="single-delete-button"]').click expect(find('.modal .modal-title')).to have_content _('Remove tag') find('.modal .modal-footer .btn-danger').click @@ -121,6 +123,16 @@ RSpec.describe 'Container Registry', :js do expect(page).to have_content('Digest: N/A') end end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + before do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + end + + it_behaves_like 'handling feature network errors with the container registry' + end + end end describe 'image repo details when image has no name' do diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 9413fae02e0..34e2ca7c8a7 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -226,6 +226,7 @@ RSpec.describe 'Environments page', :js do end it 'does not show terminal button' do + expect(page).not_to have_button(_('More actions')) expect(page).not_to have_terminal_button end @@ -273,6 +274,7 @@ RSpec.describe 'Environments page', :js do let(:role) { :maintainer } it 'shows the terminal button' do + click_button(_('More actions')) expect(page).to have_terminal_button end end @@ -281,6 +283,7 @@ RSpec.describe 'Environments page', :js do let(:role) { :developer } it 'does not show terminal button' do + expect(page).not_to have_button(_('More actions')) expect(page).not_to have_terminal_button end end @@ -515,7 +518,7 @@ RSpec.describe 'Environments page', :js do end def have_terminal_button - have_link(nil, href: terminal_project_environment_path(project, environment)) + have_link(_('Terminal'), href: terminal_project_environment_path(project, environment)) end def visit_environments(project, **opts) diff --git a/spec/features/projects/files/user_creates_directory_spec.rb b/spec/features/projects/files/user_creates_directory_spec.rb index 46b93d738e1..5ad7641a5be 100644 --- a/spec/features/projects/files/user_creates_directory_spec.rb +++ b/spec/features/projects/files/user_creates_directory_spec.rb @@ -98,12 +98,14 @@ RSpec.describe 'Projects > Files > User creates a directory', :js do expect(page).to have_content(fork_message) find('.add-to-tree').click + wait_for_requests click_link('New directory') fill_in(:dir_name, with: 'new_directory') fill_in(:commit_message, with: 'New commit message', visible: true) click_button('Create directory') fork = user.fork_of(project2.reload) + wait_for_requests expect(current_path).to eq(project_new_merge_request_path(fork)) end diff --git a/spec/features/projects/files/user_uploads_files_spec.rb b/spec/features/projects/files/user_uploads_files_spec.rb index 54e816d3d13..cc621dfd9f8 100644 --- a/spec/features/projects/files/user_uploads_files_spec.rb +++ b/spec/features/projects/files/user_uploads_files_spec.rb @@ -19,13 +19,15 @@ RSpec.describe 'Projects > Files > User uploads files' do wait_for_requests end - include_examples 'it uploads and commits a new text file' + [true, false].each do |value| + include_examples 'it uploads and commits a new text file', drop: value - include_examples 'it uploads and commits a new image file' + include_examples 'it uploads and commits a new image file', drop: value - include_examples 'it uploads and commits a new pdf file' + include_examples 'it uploads and commits a new pdf file', drop: value - include_examples 'it uploads a file to a sub-directory' + include_examples 'it uploads a file to a sub-directory', drop: value + end end context 'when a user does not have write access' do @@ -35,6 +37,8 @@ RSpec.describe 'Projects > Files > User uploads files' do visit(project_tree_path(project2)) end - include_examples 'it uploads and commits a new file to a forked project' + [true, false].each do |value| + include_examples 'it uploads and commits a new file to a forked project', drop: value + end end end diff --git a/spec/features/projects/infrastructure_registry_spec.rb b/spec/features/projects/infrastructure_registry_spec.rb index 16dd96e6c02..ee35e02b5e8 100644 --- a/spec/features/projects/infrastructure_registry_spec.rb +++ b/spec/features/projects/infrastructure_registry_spec.rb @@ -45,10 +45,8 @@ RSpec.describe 'Infrastructure Registry' do expect(page).to have_css('.packages-app h1[data-testid="title"]', text: terraform_module.name) - page.within(%Q([name="#{terraform_module.name}"])) do - expect(page).to have_content('Provision instructions') - expect(page).to have_content('Registry setup') - end + expect(page).to have_content('Provision instructions') + expect(page).to have_content('Registry setup') end end diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index dbcd7b5caf5..8538b894869 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -2,36 +2,276 @@ require 'spec_helper' +def visit_jobs_page + visit(project_jobs_path(project)) + + wait_for_requests +end + RSpec.describe 'User browses jobs' do - let!(:build) { create(:ci_build, :coverage, pipeline: pipeline) } - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } - let(:project) { create(:project, :repository, namespace: user.namespace) } - let(:user) { create(:user) } + describe 'with jobs_table_vue feature flag turned off' do + let!(:build) { create(:ci_build, :coverage, pipeline: pipeline) } + let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } + let(:project) { create(:project, :repository, namespace: user.namespace) } + let(:user) { create(:user) } - before do - stub_feature_flags(jobs_table_vue: false) - project.add_maintainer(user) - project.enable_ci - project.update_attribute(:build_coverage_regex, /Coverage (\d+)%/) + before do + stub_feature_flags(jobs_table_vue: false) + project.add_maintainer(user) + project.enable_ci + project.update_attribute(:build_coverage_regex, /Coverage (\d+)%/) - sign_in(user) + sign_in(user) - visit(project_jobs_path(project)) - end + visit(project_jobs_path(project)) + end - it 'shows the coverage' do - page.within('td.coverage') do - expect(page).to have_content('99.9%') + it 'shows the coverage' do + page.within('td.coverage') do + expect(page).to have_content('99.9%') + end + end + + context 'with a failed job' do + let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) } + + it 'displays a tooltip with the failure reason' do + page.within('.ci-table') do + failed_job_link = page.find('.ci-failed') + expect(failed_job_link[:title]).to eq('Failed - (unknown failure)') + end + end end end - context 'with a failed job' do - let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) } + describe 'with jobs_table_vue feature flag turned on', :js do + let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + + before do + stub_feature_flags(jobs_table_vue: true) + + project.add_maintainer(user) + project.enable_ci + + sign_in(user) + end + + describe 'header tabs' do + before do + visit_jobs_page + end + + it 'shows a tab for All jobs and count' do + expect(page.find('[data-testid="jobs-all-tab"]').text).to include('All') + 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 + page.find('[data-testid="jobs-finished-tab"]').click + wait_for_requests + + expect(page).to have_content('No jobs to show') + end + end + + describe 'Empty state' do + before do + visit_jobs_page + end + + it 'renders an empty state' do + expect(page).to have_content 'Use jobs to automate your tasks' + expect(page).to have_content 'Create CI/CD configuration file' + end + end + + describe 'Job actions' do + let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: 'master') } + + context 'when a job can be canceled' do + let!(:job) do + create(:ci_build, pipeline: pipeline, + stage: 'test') + end + + before do + job.run + + visit_jobs_page + end + + it 'cancels a job successfully' do + page.find('[data-testid="cancel-button"]').click + + wait_for_requests + + expect(page).to have_selector('.ci-canceled') + end + end + + context 'when a job can be retried' do + let!(:job) do + create(:ci_build, pipeline: pipeline, + stage: 'test') + end + + before do + job.drop + + visit_jobs_page + end + + it 'retries a job successfully' do + page.find('[data-testid="retry"]').click + + wait_for_requests + + expect(page).to have_selector('.ci-pending') + end + end + + context 'with a scheduled job' do + let!(:scheduled_job) { create(:ci_build, :scheduled, pipeline: pipeline, name: 'build') } + + before do + visit_jobs_page + end + + it 'plays a job successfully' do + page.find('[data-testid="play-scheduled"]').click + + page.within '#play-job-modal' do + page.find_button('OK').click + end + + wait_for_requests + + expect(page).to have_selector('.ci-pending') + end + + it 'unschedules a job successfully' do + page.find('[data-testid="unschedule"]').click + + wait_for_requests + + expect(page).to have_selector('.ci-manual') + end + end + + context 'with downloadable artifacts' do + let!(:with_artifacts) do + build = create(:ci_build, :success, + pipeline: pipeline, + name: 'rspec tests', + stage: 'test') + + create(:ci_job_artifact, :codequality, job: build) + end + + before do + visit_jobs_page + end + + it 'shows the download artifacts button' do + expect(page).to have_selector('[data-testid="download-artifacts"]') + end + end + + context 'with artifacts expired' do + let!(:with_artifacts_expired) do + create(:ci_build, :expired, :success, + pipeline: pipeline, + name: 'rspec', + stage: 'test') + end + + before do + visit_jobs_page + end + + it 'does not show the download artifacts button' do + expect(page).not_to have_selector('[data-testid="download-artifacts"]') + end + end + end + + describe 'Jobs table' do + let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: 'master') } + + context 'column links' do + let!(:job) do + create(:ci_build, pipeline: pipeline, + stage: 'test') + end + + before do + job.run + + visit_jobs_page + end + + it 'contains a link to the pipeline' do + expect(page.find('[data-testid="pipeline-id"]')).to have_content "##{pipeline.id}" + end + + it 'contains a link to the job sha' do + expect(page.find('[data-testid="job-sha"]')).to have_content "#{job.sha[0..7]}" + end + + it 'contains a link to the job id' do + expect(page.find('[data-testid="job-id-link"]')).to have_content "#{job.id}" + end + + it 'contains a link to the job ref' do + expect(page.find('[data-testid="job-ref"]')).to have_content "#{job.ref}" + end + end + end + + describe 'when user is not logged in' do + before do + sign_out(user) + end + + context 'when project is public' do + let(:public_project) { create(:project, :public, :repository) } + + context 'without jobs' do + it 'shows an empty state' do + visit project_jobs_path(public_project) + wait_for_requests + + expect(page).to have_content 'Use jobs to automate your tasks' + end + end + end + + context 'when project is private' do + let(:private_project) { create(:project, :private, :repository) } + + it 'redirects the user to sign_in and displays the flash alert' do + visit project_jobs_path(private_project) + wait_for_requests - it 'displays a tooltip with the failure reason' do - page.within('.ci-table') do - failed_job_link = page.find('.ci-failed') - expect(failed_job_link[:title]).to eq('Failed - (unknown failure)') + expect(page).to have_content 'You need to sign in' + expect(page.current_path).to eq("/users/sign_in") + end end end end diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index a1416f3f563..7ccd5c51493 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -46,7 +46,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do end it "shows Pending tab jobs" do - expect(page).to have_selector('.nav-links li.active', text: 'Pending') + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Pending') expect(page).to have_content job.short_sha expect(page).to have_content job.ref expect(page).to have_content job.name @@ -60,7 +60,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do end it "shows Running tab jobs" do - expect(page).to have_selector('.nav-links li.active', text: 'Running') + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Running') expect(page).to have_content job.short_sha expect(page).to have_content job.ref expect(page).to have_content job.name @@ -74,7 +74,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do end it "shows Finished tab jobs" do - expect(page).to have_selector('.nav-links li.active', text: 'Finished') + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Finished') expect(page).to have_content('Use jobs to automate your tasks') end end @@ -86,7 +86,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do end it "shows All tab jobs" do - expect(page).to have_selector('.nav-links li.active', text: 'All') + expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'All') expect(page).to have_content job.short_sha expect(page).to have_content job.ref expect(page).to have_content job.name 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 84a972b3027..eb32570448b 100644 --- a/spec/features/projects/members/groups_with_access_list_spec.rb +++ b/spec/features/projects/members/groups_with_access_list_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, :public) } let_it_be(:project) { create(:project, :public) } + let_it_be(:expiration_date) { 5.days.from_now.to_date } let(:additional_link_attrs) { {} } let!(:group_link) { create(:project_group_link, project: project, group: group, **additional_link_attrs) } @@ -37,27 +38,27 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do it 'updates expiry date' do page.within find_group_row(group) do - fill_in 'Expiration date', with: 5.days.from_now.to_date + fill_in 'Expiration date', with: expiration_date find_field('Expiration date').native.send_keys :enter wait_for_requests - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) end end context 'when link has expiry date set' do - let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } } + let(:additional_link_attrs) { { expires_at: expiration_date } } it 'clears expiry date' do page.within find_group_row(group) do - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: expiration_date) find('[data-testid="clear-button"]').click wait_for_requests - expect(page).to have_content('No expiration set') + expect(page).to have_field('Expiration date', with: '') end end end diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index 8c3646125a5..b674cad0312 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -165,6 +165,8 @@ RSpec.describe 'Project > Members > Invite group', :js do let(:project) { create(:project) } let!(:group) { create(:group) } + let_it_be(:expiration_date) { 5.days.from_now.to_date } + around do |example| freeze_time { example.run } end @@ -176,15 +178,14 @@ RSpec.describe 'Project > Members > Invite group', :js do visit project_project_members_path(project) - invite_group(group.name, role: 'Guest', expires_at: 5.days.from_now) + invite_group(group.name, role: 'Guest', expires_at: expiration_date) end it 'the group link shows the expiration time with a warning class' do setup click_link 'Groups' - expect(find_group_row(group)).to have_content(/in \d days/) - expect(find_group_row(group)).to have_selector('.gl-text-orange-500') + expect(page).to have_field('Expiration date', with: expiration_date) end end diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index c1b14cf60e7..830ada29a2e 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -9,6 +9,8 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date let_it_be(:maintainer) { create(:user) } let_it_be(:project) { create(:project) } + let_it_be(:three_days_from_now) { 3.days.from_now.to_date } + let_it_be(:five_days_from_now) { 5.days.from_now.to_date } let(:new_member) { create(:user) } @@ -22,39 +24,39 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date it 'expiration date is displayed in the members list' do visit project_project_members_path(project) - invite_member(new_member.name, role: 'Guest', expires_at: 5.days.from_now.to_date) + invite_member(new_member.name, role: 'Guest', expires_at: five_days_from_now) page.within find_member_row(new_member) do - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: five_days_from_now) end end it 'changes expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date) + project.team.add_users([new_member.id], :developer, expires_at: three_days_from_now) visit project_project_members_path(project) page.within find_member_row(new_member) do - fill_in 'Expiration date', with: 5.days.from_now.to_date + fill_in 'Expiration date', with: five_days_from_now find_field('Expiration date').native.send_keys :enter wait_for_requests - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: five_days_from_now) end end it 'clears expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.to_date) + project.team.add_users([new_member.id], :developer, expires_at: five_days_from_now) visit project_project_members_path(project) page.within find_member_row(new_member) do - expect(page).to have_content(/in \d days/) + expect(page).to have_field('Expiration date', with: five_days_from_now) find('[data-testid="clear-button"]').click wait_for_requests - expect(page).to have_content('No expiration set') + expect(page).to have_field('Expiration date', with: '') end end diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb index 876bc82d16c..f61eaccf5b9 100644 --- a/spec/features/projects/navbar_spec.rb +++ b/spec/features/projects/navbar_spec.rb @@ -18,6 +18,7 @@ RSpec.describe 'Project navbar' do stub_config(registry: { enabled: false }) insert_package_nav(_('Infrastructure')) insert_infrastructure_registry_nav + insert_infrastructure_google_cloud_nav end it_behaves_like 'verified navigation bar' do diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 39f9d3b331b..dacbaa826a0 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -296,12 +296,16 @@ RSpec.describe 'New project', :js do expect(git_import_instructions).to have_content 'Git repository URL' end - it 'reports error if repo URL does not end with .git' do + it 'reports error if repo URL is not a valid Git repository' do + stub_request(:get, "http://foo/bar/info/refs?service=git-upload-pack").to_return(status: 200, body: "not-a-git-repo") + fill_in 'project_import_url', with: 'http://foo/bar' # simulate blur event find('body').click - expect(page).to have_text('A repository URL usually ends in a .git suffix') + wait_for_requests + + expect(page).to have_text('There is not a valid Git repository at this URL') end it 'keeps "Import project" tab open after form validation error' do diff --git a/spec/features/projects/packages_spec.rb b/spec/features/projects/packages_spec.rb index 7fcc8200b1c..9b1e87192f5 100644 --- a/spec/features/projects/packages_spec.rb +++ b/spec/features/projects/packages_spec.rb @@ -27,6 +27,10 @@ RSpec.describe 'Packages' do context 'when feature is available', :js do before do + # we are simply setting the featrure flag to false because the new UI has nothing to test yet + # when the refactor is complete or almost complete we will turn on the feature tests + # see https://gitlab.com/gitlab-org/gitlab/-/issues/330846 for status of this work + stub_feature_flags(package_list_apollo: false) visit_project_packages end diff --git a/spec/features/projects/settings/monitor_settings_spec.rb b/spec/features/projects/settings/monitor_settings_spec.rb index e3d75c30e5e..3f6c4646f00 100644 --- a/spec/features/projects/settings/monitor_settings_spec.rb +++ b/spec/features/projects/settings/monitor_settings_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Projects > Settings > For a forked project', :js do let_it_be(:project) { create(:project, :repository, create_templates: :issue) } - let(:user) { project.owner} + let(:user) { project.owner } before do sign_in(user) @@ -16,7 +16,8 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do visit project_path(project) wait_for_requests - expect(page).to have_selector('.sidebar-sub-level-items a[aria-label="Monitor"]', text: 'Monitor', visible: false) + expect(page).to have_selector('.sidebar-sub-level-items a[aria-label="Monitor"]', + text: 'Monitor', visible: :hidden) end end @@ -42,7 +43,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do expect(find_field(send_email)).to be_checked end - it 'updates form values', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/333665' do + it 'updates form values' do check(create_issue) uncheck(send_email) click_on('No template selected') @@ -52,10 +53,8 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do click_settings_tab expect(find_field(create_issue)).to be_checked - expect(page).to have_selector(:id, 'alert-integration-settings-issue-template', text: 'bug') - - click_settings_tab expect(find_field(send_email)).not_to be_checked + expect(page).to have_selector(:id, 'alert-integration-settings-issue-template', text: 'bug') end def click_settings_tab @@ -68,13 +67,15 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do page.within '[data-testid="alert-integration-settings"]' do click_button 'Save changes' end + + wait_for_all_requests end end - context 'error tracking settings form' do + describe 'error tracking settings form' do let(:sentry_list_projects_url) { 'http://sentry.example.com/api/0/projects/' } - context 'success path' do + context 'when project dropdown is loaded' do let(:projects_sample_response) do Gitlab::Utils.deep_indifferent_access( Gitlab::Json.parse(fixture_file('sentry/list_projects_sample_response.json')) @@ -97,7 +98,9 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do within '.js-error-tracking-settings' do click_button('Expand') + choose('cloud-hosted Sentry') end + expect(page).to have_content('Sentry API URL') expect(page.body).to include('Error Tracking') expect(page).to have_button('Connect') @@ -121,7 +124,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do end end - context 'project dropdown fails to load' do + context 'when project dropdown fails to load' do before do WebMock.stub_request(:get, sentry_list_projects_url) .to_return( @@ -140,8 +143,10 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do within '.js-error-tracking-settings' do click_button('Expand') + choose('cloud-hosted Sentry') + check('Active') end - check('Active') + fill_in('error-tracking-api-host', with: 'http://sentry.example.com') fill_in('error-tracking-token', with: 'token') @@ -151,7 +156,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do end end - context 'integrated error tracking backend' do + context 'with integrated error tracking backend' do it 'successfully fills and submits the form' do visit project_settings_operations_path(project) @@ -175,11 +180,17 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do wait_for_requests assert_text('Your changes have been saved') + + within '.js-error-tracking-settings' do + click_button('Expand') + end + + expect(page).to have_content('Paste this DSN into your Sentry SDK') end end end - context 'grafana integration settings form' do + describe 'grafana integration settings form' do it 'successfully fills and completes the form' do visit project_settings_operations_path(project) diff --git a/spec/features/projects/settings/webhooks_settings_spec.rb b/spec/features/projects/settings/webhooks_settings_spec.rb index 528fd58cbe6..8d73ffecd46 100644 --- a/spec/features/projects/settings/webhooks_settings_spec.rb +++ b/spec/features/projects/settings/webhooks_settings_spec.rb @@ -115,8 +115,8 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do hook_log visit edit_project_hook_path(project, hook) - expect(page).to have_content('Recent Deliveries') - expect(page).to have_content(hook_log.url) + expect(page).to have_content('Recent events') + expect(page).to have_link('View details', href: hook_log.present.details_path) end it 'show hook log details' do diff --git a/spec/features/projects/show/user_uploads_files_spec.rb b/spec/features/projects/show/user_uploads_files_spec.rb index eb230082bfa..51e41397439 100644 --- a/spec/features/projects/show/user_uploads_files_spec.rb +++ b/spec/features/projects/show/user_uploads_files_spec.rb @@ -21,13 +21,15 @@ RSpec.describe 'Projects > Show > User uploads files' do wait_for_requests end - include_examples 'it uploads and commits a new text file' + [true, false].each do |value| + include_examples 'it uploads and commits a new text file', drop: value - include_examples 'it uploads and commits a new image file' + include_examples 'it uploads and commits a new image file', drop: value - include_examples 'it uploads and commits a new pdf file' + include_examples 'it uploads and commits a new pdf file', drop: value - include_examples 'it uploads a file to a sub-directory' + include_examples 'it uploads a file to a sub-directory', drop: value + end end context 'when a user does not have write access' do @@ -37,7 +39,9 @@ RSpec.describe 'Projects > Show > User uploads files' do visit(project_path(project2)) end - include_examples 'it uploads and commits a new file to a forked project' + [true, false].each do |value| + include_examples 'it uploads and commits a new file to a forked project', drop: value + end end context 'when in the empty_repo_upload experiment' do @@ -50,13 +54,17 @@ RSpec.describe 'Projects > Show > User uploads files' do context 'with an empty repo' do let(:project) { create(:project, :empty_repo, creator: user) } - include_examples 'uploads and commits a new text file via "upload file" button' + [true, false].each do |value| + include_examples 'uploads and commits a new text file via "upload file" button', drop: value + end end context 'with a nonempty repo' do let(:project) { create(:project, :repository, creator: user) } - include_examples 'uploads and commits a new text file via "upload file" button' + [true, false].each do |value| + include_examples 'uploads and commits a new text file via "upload file" button', drop: value + end end end end diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index 9f08759603e..5d482f9fbd0 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -33,6 +33,29 @@ RSpec.describe 'User creates a project', :js do expect(page).to have_content(project.url_to_repo) end + it 'creates a new project that is not blank' do + stub_experiments(new_project_sast_enabled: 'candidate') + + visit(new_project_path) + + find('[data-qa-panel-name="blank_project"]').click # rubocop:disable QA/SelectorUsage + 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)' + + page.within('#content-body') do + click_button('Create project') + end + + project = Project.last + + expect(current_path).to eq(project_path(project)) + 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 + context 'in a subgroup they do not own' do let(:parent) { create(:group) } let!(:subgroup) { create(:group, parent: parent) } diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 9dcef13757a..4012a302196 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -553,6 +553,7 @@ RSpec.describe "Internal Project Access" do before do stub_container_registry_tags(repository: :any, tags: ['latest']) stub_container_registry_config(enabled: true) + stub_container_registry_info project.container_repositories << container_repository end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 5a200bea80a..aa34ccce2c1 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -570,6 +570,7 @@ RSpec.describe "Private Project Access" do before do stub_container_registry_tags(repository: :any, tags: ['latest']) stub_container_registry_config(enabled: true) + stub_container_registry_info project.container_repositories << container_repository end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 8ceb6920e77..abe128c6f78 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -552,6 +552,7 @@ RSpec.describe "Public Project Access" do before do stub_container_registry_tags(repository: :any, tags: ['latest']) stub_container_registry_config(enabled: true) + stub_container_registry_info project.container_repositories << container_repository end diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb index e03f71c5352..fc88cd9205c 100644 --- a/spec/features/snippets/notes_on_personal_snippets_spec.rb +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -70,8 +70,8 @@ RSpec.describe 'Comments on personal snippets', :js do context 'when submitting a note' do it 'shows a valid form' do is_expected.to have_css('.js-main-target-form', visible: true, count: 1) - expect(find('.js-main-target-form .js-comment-button').value) - .to eq('Comment') + expect(find('.js-main-target-form .js-comment-button button', match: :first)) + .to have_content('Comment') page.within('.js-main-target-form') do expect(page).not_to have_link('Cancel') diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 79c4057a8b9..10c1c2cb26e 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -171,6 +171,18 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do end end + describe 'with OneTrust authentication' do + before do + stub_config(extra: { one_trust_id: SecureRandom.uuid }) + end + + it 'has proper Content-Security-Policy headers' do + visit root_path + + expect(response_headers['Content-Security-Policy']).to include('https://cdn.cookielaw.org https://*.onetrust.com') + end + end + describe 'with two-factor authentication', :js do def enter_code(code) fill_in 'user_otp_attempt', with: code @@ -866,8 +878,8 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do expect(current_path).to eq(new_profile_password_path) - fill_in 'user_current_password', with: '12345678' - fill_in 'user_password', with: 'new password' + fill_in 'user_password', with: '12345678' + fill_in 'user_new_password', with: 'new password' fill_in 'user_password_confirmation', with: 'new password' click_button 'Set new password' @@ -875,7 +887,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do end end - context 'when the user does not have an email configured' do + context 'when the user does not have an email configured', :js do let(:user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'saml', email: 'temp-email-for-oauth-user@gitlab.localhost') } before do diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index e629d329033..61672662fbe 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -81,6 +81,7 @@ RSpec.describe 'User page' do context 'timezone' do let_it_be(:timezone) { 'America/Los_Angeles' } + let_it_be(:local_time_selector) { '[data-testid="user-local-time"]' } before do travel_to Time.find_zone(timezone).local(2021, 7, 20, 15, 30, 45) @@ -92,7 +93,19 @@ RSpec.describe 'User page' do it 'shows local time' do subject - expect(page).to have_content('3:30 PM') + within local_time_selector do + expect(page).to have_content('3:30 PM') + end + end + end + + context 'when timezone is not set' do + let_it_be(:user) { create(:user, timezone: nil) } + + it 'does not show local time' do + subject + + expect(page).not_to have_selector(local_time_selector) end end @@ -102,7 +115,9 @@ RSpec.describe 'User page' do it 'shows local time using the configured default timezone (UTC in this case)' do subject - expect(page).to have_content('10:30 PM') + within local_time_selector do + expect(page).to have_content('10:30 PM') + end end end end -- cgit v1.2.3