summaryrefslogtreecommitdiff
path: root/spec/features
diff options
context:
space:
mode:
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_sees_project_statistics_spec.rb2
-rw-r--r--spec/features/admin/admin_users_spec.rb8
-rw-r--r--spec/features/admin/services/admin_visits_service_templates_spec.rb30
-rw-r--r--spec/features/clusters/cluster_health_dashboard_spec.rb93
-rw-r--r--spec/features/clusters/installing_applications_shared_examples.rb53
-rw-r--r--spec/features/dashboard/projects_spec.rb8
-rw-r--r--spec/features/discussion_comments/commit_spec.rb2
-rw-r--r--spec/features/groups/container_registry_spec.rb2
-rw-r--r--spec/features/groups/empty_states_spec.rb2
-rw-r--r--spec/features/groups/import_export/import_file_spec.rb2
-rw-r--r--spec/features/groups/members/manage_members_spec.rb9
-rw-r--r--spec/features/groups/navbar_spec.rb1
-rw-r--r--spec/features/groups_spec.rb29
-rw-r--r--spec/features/invites_spec.rb260
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb22
-rw-r--r--spec/features/issuables/issuable_list_spec.rb10
-rw-r--r--spec/features/issuables/sorting_list_spec.rb4
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb45
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb752
-rw-r--r--spec/features/issues/issue_detail_spec.rb2
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb2
-rw-r--r--spec/features/issues/move_spec.rb39
-rw-r--r--spec/features/issues/service_desk_spec.rb163
-rw-r--r--spec/features/issues/update_issues_spec.rb1
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb8
-rw-r--r--spec/features/issues/user_filters_issues_spec.rb2
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb4
-rw-r--r--spec/features/issues/user_sees_breadcrumb_links_spec.rb2
-rw-r--r--spec/features/issues/user_sees_empty_state_spec.rb4
-rw-r--r--spec/features/issues/user_sorts_issues_spec.rb2
-rw-r--r--spec/features/issues/user_views_issues_spec.rb29
-rw-r--r--spec/features/labels_hierarchy_spec.rb2
-rw-r--r--spec/features/markdown/metrics_spec.rb38
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb4
-rw-r--r--spec/features/merge_request/user_approves_spec.rb41
-rw-r--r--spec/features/merge_request/user_closes_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb53
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_edits_merge_request_spec.rb69
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb4
-rw-r--r--spec/features/merge_request/user_reopens_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb10
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb113
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb5
-rw-r--r--spec/features/merge_request/user_toggles_whitespace_changes_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_file_by_file_spec.rb33
-rw-r--r--spec/features/participants_autocomplete_spec.rb23
-rw-r--r--spec/features/profiles/user_edit_preferences_spec.rb13
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb5
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb2
-rw-r--r--spec/features/projects/activity/user_sees_design_activity_spec.rb78
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb2
-rw-r--r--spec/features/projects/blobs/edit_spec.rb16
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb3
-rw-r--r--spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb26
-rw-r--r--spec/features/projects/clusters_spec.rb162
-rw-r--r--spec/features/projects/commit/builds_spec.rb25
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb27
-rw-r--r--spec/features/projects/confluence/user_views_confluence_page_spec.rb28
-rw-r--r--spec/features/projects/container_registry_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_metrics_spec.rb11
-rw-r--r--spec/features/projects/environments_pod_logs_spec.rb5
-rw-r--r--spec/features/projects/files/edit_file_soft_wrap_spec.rb24
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb20
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb16
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb20
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin3360 -> 3176 bytes
-rw-r--r--spec/features/projects/issues/design_management/user_paginates_designs_spec.rb56
-rw-r--r--spec/features/projects/issues/design_management/user_permissions_upload_spec.rb29
-rw-r--r--spec/features/projects/issues/design_management/user_uploads_designs_spec.rb62
-rw-r--r--spec/features/projects/issues/design_management/user_views_design_spec.rb39
-rw-r--r--spec/features/projects/issues/design_management/user_views_designs_spec.rb79
-rw-r--r--spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb1
-rw-r--r--spec/features/projects/jobs_spec.rb4
-rw-r--r--spec/features/projects/labels/issues_sorted_by_priority_spec.rb4
-rw-r--r--spec/features/projects/members/list_spec.rb9
-rw-r--r--spec/features/projects/members/member_leaves_project_spec.rb2
-rw-r--r--spec/features/projects/navbar_spec.rb2
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb90
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb17
-rw-r--r--spec/features/projects/services/disable_triggers_spec.rb6
-rw-r--r--spec/features/projects/services/user_activates_alerts_spec.rb51
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb6
-rw-r--r--spec/features/projects/services/user_activates_jira_spec.rb30
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb4
-rw-r--r--spec/features/projects/settings/operations_settings_spec.rb5
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb10
-rw-r--r--spec/features/projects/settings/service_desk_setting_spec.rb33
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb68
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb9
-rw-r--r--spec/features/projects/show/user_sees_git_instructions_spec.rb22
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb17
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb26
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb4
-rw-r--r--spec/features/projects/user_sees_user_popover_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_empty_spec.rb42
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb84
-rw-r--r--spec/features/promotion_spec.rb53
-rw-r--r--spec/features/runners_spec.rb120
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb9
-rw-r--r--spec/features/signed_commits_spec.rb7
-rw-r--r--spec/features/task_lists_spec.rb2
-rw-r--r--spec/features/triggers_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb6
-rw-r--r--spec/features/users/signup_spec.rb9
112 files changed, 2718 insertions, 805 deletions
diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb
index 6e8211a9b4e..d94889b825a 100644
--- a/spec/features/admin/admin_sees_project_statistics_spec.rb
+++ b/spec/features/admin/admin_sees_project_statistics_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe "Admin > Admin sees project statistics" do
let(:project) { create(:project, :repository) }
it "shows project statistics" do
- expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes)")
+ expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes / Snippets: 0 Bytes)")
end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 369f91c6faa..6cd18f2755c 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe "Admin::Users" do
end
describe "view extra user information" do
- it 'shows the user popover on hover', :js, :quarantine do
+ it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
expect(page).not_to have_selector('#__BV_popover_1__')
first_user_link = page.first('.js-user-link')
@@ -512,14 +512,14 @@ RSpec.describe "Admin::Users" do
end
it "lists group projects" do
- within(:css, '.append-bottom-default + .card') do
+ within(:css, '.gl-mb-3 + .card') do
expect(page).to have_content 'Group projects'
expect(page).to have_link group.name, href: admin_group_path(group)
end
end
it 'allows navigation to the group details' do
- within(:css, '.append-bottom-default + .card') do
+ within(:css, '.gl-mb-3 + .card') do
click_link group.name
end
within(:css, 'h3.page-title') do
@@ -529,7 +529,7 @@ RSpec.describe "Admin::Users" do
end
it 'shows the group access level' do
- within(:css, '.append-bottom-default + .card') do
+ within(:css, '.gl-mb-3 + .card') do
expect(page).to have_content 'Developer'
end
end
diff --git a/spec/features/admin/services/admin_visits_service_templates_spec.rb b/spec/features/admin/services/admin_visits_service_templates_spec.rb
new file mode 100644
index 00000000000..8e02538ece0
--- /dev/null
+++ b/spec/features/admin/services/admin_visits_service_templates_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Admin visits service templates' do
+ let(:admin) { create(:user, :admin) }
+ let(:slack_service) { Service.templates.find { |s| s.type == 'SlackService' } }
+
+ before do
+ sign_in(admin)
+
+ visit(admin_application_settings_services_path)
+ end
+
+ context 'without instance-level integration' do
+ it 'shows a link to service template' do
+ expect(page).to have_link('Slack', href: edit_admin_application_settings_service_path(slack_service.id))
+ expect(page).not_to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_service))
+ end
+ end
+
+ context 'with instance-level integration' do
+ let_it_be(:slack_instance_integration) { create(:slack_service, instance: true, project: nil) }
+
+ it 'shows a link to instance-level integration' do
+ expect(page).not_to have_link('Slack', href: edit_admin_application_settings_service_path(slack_service.id))
+ expect(page).to have_link('Slack', href: edit_admin_application_settings_integration_path(slack_service))
+ end
+ end
+end
diff --git a/spec/features/clusters/cluster_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb
new file mode 100644
index 00000000000..e9e3b48e9c0
--- /dev/null
+++ b/spec/features/clusters/cluster_health_dashboard_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+ include KubernetesHelpers
+ include PrometheusHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:clusterable) { create(:project) }
+ let_it_be(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [clusterable]) }
+ let_it_be(:cluster_path) { project_cluster_path(clusterable, cluster) }
+
+ before do
+ clusterable.add_maintainer(current_user)
+
+ sign_in(current_user)
+ end
+
+ it 'shows cluster board section within the page' do
+ visit cluster_path
+
+ expect(page).to have_text('Health')
+
+ click_link 'Health'
+
+ expect(page).to have_css('.cluster-health-graphs')
+ end
+
+ context 'no prometheus installed' do
+ it 'shows install prometheus message' do
+ visit cluster_path
+
+ click_link 'Health'
+
+ expect(page).to have_text('you must first install Prometheus in the Applications tab')
+ end
+ end
+
+ context 'when there is cluster with installed prometheus' do
+ before do
+ create(:clusters_applications_prometheus, :installed, cluster: cluster)
+ stub_kubeclient_discover(cluster.platform.api_url)
+ end
+
+ context 'waiting for data' do
+ before do
+ stub_empty_response
+ end
+
+ it 'shows container and waiting for data message' do
+ visit cluster_path
+
+ click_link 'Health'
+
+ wait_for_requests
+
+ expect(page).to have_css('.prometheus-graphs')
+ expect(page).to have_text('Waiting for performance data')
+ end
+ end
+
+ context 'connected, prometheus returns data' do
+ before do
+ stub_connected
+ end
+
+ it 'renders charts' do
+ visit cluster_path
+
+ click_link 'Health'
+
+ wait_for_requests
+
+ expect(page).to have_css('.prometheus-graphs')
+ expect(page).to have_css('.prometheus-graph')
+ expect(page).to have_css('.prometheus-graph-title')
+ expect(page).to have_css('[_echarts_instance_]')
+ expect(page).to have_content('Avg')
+ end
+ end
+
+ def stub_empty_response
+ stub_prometheus_request(/prometheus-prometheus-server/, status: 204, body: {})
+ stub_prometheus_request(/prometheus\/api\/v1/, status: 204, body: {})
+ end
+
+ def stub_connected
+ stub_prometheus_request(/prometheus-prometheus-server/, body: prometheus_values_body)
+ stub_prometheus_request(/prometheus\/api\/v1/, body: prometheus_values_body)
+ end
+ end
+end
diff --git a/spec/features/clusters/installing_applications_shared_examples.rb b/spec/features/clusters/installing_applications_shared_examples.rb
index d2f28f5b219..74150c42519 100644
--- a/spec/features/clusters/installing_applications_shared_examples.rb
+++ b/spec/features/clusters/installing_applications_shared_examples.rb
@@ -2,6 +2,9 @@
RSpec.shared_examples "installing applications for a cluster" do |managed_apps_local_tiller|
before do
+ # Reduce interval from 10 seconds which is too long for an automated test
+ stub_const("#{Clusters::ClustersController}::STATUS_POLLING_INTERVAL", 500)
+
stub_feature_flags(managed_apps_local_tiller: managed_apps_local_tiller)
visit cluster_path
@@ -107,10 +110,6 @@ RSpec.shared_examples "installing applications for a cluster" do |managed_apps_l
end
describe 'when user clicks install button' do
- def domainname_form_value
- page.find('.js-knative-domainname').value
- end
-
before do
allow(ClusterInstallAppWorker).to receive(:perform_async)
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
@@ -135,7 +134,7 @@ RSpec.shared_examples "installing applications for a cluster" do |managed_apps_l
it 'shows status transition' do
page.within('.js-cluster-application-row-knative') do
- expect(domainname_form_value).to eq('domain.example.org')
+ expect(page).to have_field('Knative Domain Name:', with: 'domain.example.org')
expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall')
end
@@ -147,7 +146,7 @@ RSpec.shared_examples "installing applications for a cluster" do |managed_apps_l
page.within('.js-cluster-application-row-knative') do
expect(ClusterPatchAppWorker).to receive(:perform_async)
- expect(domainname_form_value).to eq('domain.example.org')
+ expect(page).to have_field('Knative Domain Name:', with: 'domain.example.org')
page.find('.js-knative-domainname').set("new.domain.example.org")
@@ -155,7 +154,7 @@ RSpec.shared_examples "installing applications for a cluster" do |managed_apps_l
wait_for_requests
- expect(domainname_form_value).to eq('new.domain.example.org')
+ expect(page).to have_field('Knative Domain Name:', with: 'new.domain.example.org')
end
end
end
@@ -169,34 +168,54 @@ RSpec.shared_examples "installing applications for a cluster" do |managed_apps_l
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
create(:clusters_applications_helm, :installed, cluster: cluster) unless managed_apps_local_tiller
+ end
+ it 'shows status transition' do
page.within('.js-cluster-application-row-cert_manager') do
click_button 'Install'
+ wait_for_requests
+
+ expect(page).to have_field('Issuer Email', with: cluster.user.email)
+ expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
+
+ Clusters::Cluster.last.application_cert_manager.make_installing!
+
+ expect(page).to have_field('Issuer Email', with: cluster.user.email)
+ expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
+
+ Clusters::Cluster.last.application_cert_manager.make_installed!
+
+ expect(page).to have_field('Issuer Email', with: cluster.user.email)
+ expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall')
end
+
+ expect(page).to have_content('Cert-Manager was successfully installed on your Kubernetes cluster')
end
- it 'shows status transition' do
- def email_form_value
- page.find('.js-email').value
- end
+ it 'installs with custom email' do
+ custom_email = 'new_email@example.org'
page.within('.js-cluster-application-row-cert_manager') do
- expect(email_form_value).to eq(cluster.user.email)
+ # Wait for the polling to finish
+ wait_for_requests
+
+ page.find('.js-email').set(custom_email)
+ click_button 'Install'
+ wait_for_requests
+
+ expect(page).to have_field('Issuer Email', with: custom_email)
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
- page.find('.js-email').set("new_email@example.org")
Clusters::Cluster.last.application_cert_manager.make_installing!
- expect(email_form_value).to eq('new_email@example.org')
+ expect(page).to have_field('Issuer Email', with: custom_email)
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
Clusters::Cluster.last.application_cert_manager.make_installed!
- expect(email_form_value).to eq('new_email@example.org')
+ expect(page).to have_field('Issuer Email', with: custom_email)
expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall')
end
-
- expect(page).to have_content('Cert-Manager was successfully installed on your Kubernetes cluster')
end
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index e1beaf923e8..f870adbbdb6 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -205,6 +205,14 @@ RSpec.describe 'Dashboard Projects' do
it_behaves_like 'hidden pipeline status'
end
+
+ context "when last_pipeline is missing" do
+ before do
+ project.last_pipeline.delete
+ end
+
+ it_behaves_like 'hidden pipeline status'
+ end
end
context 'last push widget', :use_clean_rails_memory_store_caching do
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
index 5a744e43bb6..32c0ba2a9a7 100644
--- a/spec/features/discussion_comments/commit_spec.rb
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe 'Thread Comments Commit', :js do
expect(page).to have_css('.js-note-emoji')
end
- it 'adds award to the correct note', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/207973' do
+ it 'adds award to the correct note', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/207973' do
find("#note_#{commit_discussion_note2.id} .js-note-emoji").click
first('.emoji-menu .js-emoji-btn').click
diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb
index 87ef2131211..acac8724edf 100644
--- a/spec/features/groups/container_registry_spec.rb
+++ b/spec/features/groups/container_registry_spec.rb
@@ -82,7 +82,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="singleDeleteButton"]').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
end
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index d76cf993004..aaa59108b95 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'Group empty states' do
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
before do
+ stub_feature_flags(vue_issuables_list: false)
+
sign_in(user)
end
diff --git a/spec/features/groups/import_export/import_file_spec.rb b/spec/features/groups/import_export/import_file_spec.rb
index 577198ef3f1..ee4f2740f9f 100644
--- a/spec/features/groups/import_export/import_file_spec.rb
+++ b/spec/features/groups/import_export/import_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'Import/Export - Group Import', :js do
+RSpec.describe 'Import/Export - Group Import', :js do
let_it_be(:user) { create(:user) }
let_it_be(:import_path) { "#{Dir.tmpdir}/group_import_spec" }
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index e29d8fd651e..99846ecee27 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -68,9 +68,12 @@ RSpec.describe 'Groups > Members > Manage members' do
visit group_group_members_path(group)
- accept_confirm do
- find(:css, '.project-members-page li', text: user2.name).find(:css, 'a.btn-remove').click
- end
+ # Open modal
+ find(:css, '.project-members-page li', text: user2.name).find(:css, 'button.btn-remove').click
+
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
wait_for_requests
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index cfa1f3338a1..06ff33ff0eb 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe 'Group navbar' do
before do
stub_feature_flags(group_push_rules: false)
stub_feature_flags(group_iterations: false)
+ stub_feature_flags(group_wiki: false)
group.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 78a35fe1d3f..8104ff3f987 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -68,6 +68,35 @@ RSpec.describe 'Group' do
end
end
+ describe 'real-time group url validation', :js do
+ it 'shows a message if group url is available' do
+ fill_in 'group_path', with: 'az'
+ wait_for_requests
+
+ expect(page).to have_content('Group path is available')
+ end
+
+ it 'shows an error if group url is taken' do
+ fill_in 'group_path', with: user.username
+ wait_for_requests
+
+ expect(page).to have_content('Group path is already taken')
+ end
+
+ it 'does not break after an invalid form submit' do
+ fill_in 'group_name', with: 'MyGroup'
+ fill_in 'group_path', with: 'z'
+ click_button 'Create group'
+
+ expect(page).to have_content('Group URL is too short')
+
+ fill_in 'group_path', with: 'az'
+ wait_for_requests
+
+ expect(page).to have_content('Group path is available')
+ end
+ end
+
describe 'Mattermost team creation' do
before do
stub_mattermost_setting(enabled: mattermost_enabled)
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index f85b4b78e35..d91fae5cdfd 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Invites' do
- let(:user) { create(:user) }
+RSpec.describe 'Invites', :aggregate_failures do
+ let(:user) { create(:user, email: 'user@example.com') }
let(:owner) { create(:user, name: 'John Doe') }
let(:group) { create(:group, name: 'Owned') }
let(:project) { create(:project, :repository, namespace: group) }
@@ -11,7 +11,7 @@ RSpec.describe 'Invites' do
before do
project.add_maintainer(owner)
- group.add_user(owner, Gitlab::Access::OWNER)
+ group.add_owner(owner)
group.add_developer('user@example.com', owner)
group_invite.generate_invite_token!
end
@@ -23,12 +23,12 @@ RSpec.describe 'Invites' do
end
def fill_in_sign_up_form(new_user)
- fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_username', with: new_user.username
- fill_in 'new_user_email', with: new_user.email
- fill_in 'new_user_email_confirmation', with: new_user.email
- fill_in 'new_user_password', with: new_user.password
- click_button "Register"
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button 'Register'
end
def fill_in_sign_in_form(user)
@@ -48,19 +48,15 @@ RSpec.describe 'Invites' do
expect(page).to have_content('To accept this invitation, sign in')
end
- it 'sign in and redirects to invitation page' do
+ it 'sign in, grants access and redirects to group page' do
fill_in_sign_in_form(user)
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
- expect(page).to have_content(
- 'You have been invited by John Doe to join group Owned as Developer.'
- )
- expect(page).to have_link('Accept invitation')
- expect(page).to have_link('Decline')
+ expect(current_path).to eq(group_path(group))
+ expect(page).to have_content('You have been granted Developer access to group Owned.')
end
end
- context 'when signed in as an exists member' do
+ context 'when signed in as an existing member' do
before do
sign_in(owner)
end
@@ -71,166 +67,166 @@ RSpec.describe 'Invites' do
end
end
- describe 'accepting the invitation' do
- before do
- sign_in(user)
- visit invite_path(group_invite.raw_invite_token)
- end
-
- it 'grants access and redirects to group page' do
- page.click_link 'Accept invitation'
- expect(current_path).to eq(group_path(group))
- expect(page).to have_content(
- 'You have been granted Developer access to group Owned.'
- )
- end
- end
-
- describe 'declining the application' do
- context 'when signed in' do
- before do
- sign_in(user)
- visit invite_path(group_invite.raw_invite_token)
- end
-
- it 'declines application and redirects to dashboard' do
- page.click_link 'Decline'
- expect(current_path).to eq(dashboard_projects_path)
- expect(page).to have_content(
- 'You have declined the invitation to join group Owned.'
- )
- end
- end
-
- context 'when signed out' do
- before do
- visit decline_invite_path(group_invite.raw_invite_token)
- end
-
- it 'declines application and redirects to sign in page' do
- expect(current_path).to eq(new_user_session_path)
- expect(page).to have_content(
- 'You have declined the invitation to join group Owned.'
- )
- end
- end
- end
-
- describe 'invite an user using their email address' do
+ context 'when inviting a user using their email address' do
let(:new_user) { build_stubbed(:user) }
let(:invite_email) { new_user.email }
let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email) }
let!(:project_invite) { create(:project_member, :invited, project: project, invite_email: invite_email) }
- before do
- stub_application_setting(send_user_confirmation_email: send_email_confirmation)
- visit invite_path(group_invite.raw_invite_token)
- end
-
- context 'email confirmation disabled' do
- let(:send_email_confirmation) { false }
-
- it 'signs up and redirects to the dashboard page with all the projects/groups invitations automatically accepted' do
- fill_in_sign_up_form(new_user)
-
- expect(current_path).to eq(dashboard_projects_path)
- expect(page).to have_content(project.full_name)
-
- visit group_path(group)
-
- expect(page).to have_content(group.full_name)
+ context 'when user has not signed in yet' do
+ before do
+ stub_application_setting(send_user_confirmation_email: send_email_confirmation)
+ visit invite_path(group_invite.raw_invite_token)
end
- context 'the user sign-up using a different email address' do
- let(:invite_email) { build_stubbed(:user).email }
+ context 'email confirmation disabled' do
+ let(:send_email_confirmation) { false }
- it 'signs up and redirects to the invitation page' do
+ it 'signs up and redirects to the dashboard page with all the projects/groups invitations automatically accepted' do
fill_in_sign_up_form(new_user)
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
- end
- end
- end
-
- context 'email confirmation enabled' do
- let(:send_email_confirmation) { true }
-
- context 'when soft email confirmation is not enabled' do
- before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
- end
-
- it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
- fill_in_sign_up_form(new_user)
- confirm_email(new_user)
- fill_in_sign_in_form(new_user)
-
- expect(current_path).to eq(root_path)
+ expect(current_path).to eq(dashboard_projects_path)
expect(page).to have_content(project.full_name)
visit group_path(group)
expect(page).to have_content(group.full_name)
end
- end
- context 'when soft email confirmation is enabled' do
- before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
- end
+ context 'the user sign-up using a different email address' do
+ let(:invite_email) { build_stubbed(:user).email }
- it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
- fill_in_sign_up_form(new_user)
- confirm_email(new_user)
-
- expect(current_path).to eq(root_path)
- expect(page).to have_content(project.full_name)
-
- visit group_path(group)
+ it 'signs up and redirects to the invitation page' do
+ fill_in_sign_up_form(new_user)
- expect(page).to have_content(group.full_name)
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ end
end
end
- it "doesn't accept invitations until the user confirms their email" do
- fill_in_sign_up_form(new_user)
- sign_in(owner)
-
- visit project_project_members_path(project)
- expect(page).to have_content 'Invited'
- end
-
- context 'the user sign-up using a different email address' do
- let(:invite_email) { build_stubbed(:user).email }
+ context 'email confirmation enabled' do
+ let(:send_email_confirmation) { true }
context 'when soft email confirmation is not enabled' do
before do
- stub_feature_flags(soft_email_confirmation: false)
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
end
- it 'signs up and redirects to the invitation page' do
+ it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
fill_in_sign_up_form(new_user)
confirm_email(new_user)
fill_in_sign_in_form(new_user)
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(current_path).to eq(root_path)
+ expect(page).to have_content(project.full_name)
+
+ visit group_path(group)
+
+ expect(page).to have_content(group.full_name)
end
end
context 'when soft email confirmation is enabled' do
before do
- stub_feature_flags(soft_email_confirmation: true)
allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
end
- it 'signs up and redirects to the invitation page' do
+ it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
fill_in_sign_up_form(new_user)
+ confirm_email(new_user)
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ expect(current_path).to eq(root_path)
+ expect(page).to have_content(project.full_name)
+
+ visit group_path(group)
+
+ expect(page).to have_content(group.full_name)
+ end
+ end
+
+ it "doesn't accept invitations until the user confirms their email" do
+ fill_in_sign_up_form(new_user)
+ sign_in(owner)
+
+ visit project_project_members_path(project)
+ expect(page).to have_content 'Invited'
+ end
+
+ context 'the user sign-up using a different email address' do
+ let(:invite_email) { build_stubbed(:user).email }
+
+ context 'when soft email confirmation is not enabled' do
+ before do
+ stub_feature_flags(soft_email_confirmation: false)
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
+ end
+
+ it 'signs up and redirects to the invitation page' do
+ fill_in_sign_up_form(new_user)
+ confirm_email(new_user)
+ fill_in_sign_in_form(new_user)
+
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ end
+ end
+
+ context 'when soft email confirmation is enabled' do
+ before do
+ stub_feature_flags(soft_email_confirmation: true)
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
+ end
+
+ it 'signs up and redirects to the invitation page' do
+ fill_in_sign_up_form(new_user)
+
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ end
end
end
end
end
+
+ context 'when declining the invitation' do
+ let(:send_email_confirmation) { true }
+
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ visit invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'declines application and redirects to dashboard' do
+ page.click_link 'Decline'
+ expect(current_path).to eq(dashboard_projects_path)
+ expect(page).to have_content('You have declined the invitation to join group Owned.')
+ end
+ end
+
+ context 'when signed out' do
+ before do
+ visit decline_invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'declines application and redirects to sign in page' do
+ expect(current_path).to eq(new_user_session_path)
+ expect(page).to have_content('You have declined the invitation to join group Owned.')
+ end
+ end
+ end
+
+ context 'when accepting the invitation' do
+ let(:send_email_confirmation) { true }
+
+ before do
+ sign_in(user)
+ visit invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'grants access and redirects to group page' do
+ page.click_link 'Accept invitation'
+ expect(current_path).to eq(group_path(group))
+ expect(page).to have_content('You have been granted Owner access to group Owned.')
+ end
+ end
end
end
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
index cf3028ec4c9..f442b25f593 100644
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:human_model_name) { issuable.model_name.human.downcase }
it 'shows toggle' do
- expect(page).to have_link("Close #{human_model_name}")
+ expect(page).to have_button("Close #{human_model_name}")
expect(page).to have_selector('.issuable-close-dropdown')
end
@@ -63,7 +63,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:issue, :closed, :locked, project: project) }
it 'hides the reopen button' do
- expect(page).not_to have_link('Reopen issue')
+ expect(page).not_to have_button('Reopen issue')
end
context 'when the issue author is the current user' do
@@ -72,7 +72,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
end
it 'hides the reopen button' do
- expect(page).not_to have_link('Reopen issue')
+ expect(page).not_to have_button('Reopen issue')
end
end
end
@@ -91,8 +91,8 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'only shows the `Report abuse` and `New issue` buttons' do
expect(page).to have_link('Report abuse')
expect(page).to have_link('New issue')
- expect(page).not_to have_link('Close issue')
- expect(page).not_to have_link('Reopen issue')
+ expect(page).not_to have_button('Close issue')
+ expect(page).not_to have_button('Reopen issue')
expect(page).not_to have_link('Edit')
end
end
@@ -120,8 +120,8 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'shows only the `Report abuse` and `Edit` button' do
expect(page).to have_link('Report abuse')
expect(page).to have_link('Edit')
- expect(page).not_to have_link('Close merge request')
- expect(page).not_to have_link('Reopen merge request')
+ expect(page).not_to have_button('Close merge request')
+ expect(page).not_to have_button('Reopen merge request')
end
context 'when the merge request author is the current user' do
@@ -130,8 +130,8 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'shows only the `Edit` button' do
expect(page).to have_link('Edit')
expect(page).not_to have_link('Report abuse')
- expect(page).not_to have_link('Close merge request')
- expect(page).not_to have_link('Reopen merge request')
+ expect(page).not_to have_button('Close merge request')
+ expect(page).not_to have_button('Reopen merge request')
end
end
end
@@ -149,8 +149,8 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'only shows a `Report abuse` button' do
expect(page).to have_link('Report abuse')
- expect(page).not_to have_link('Close merge request')
- expect(page).not_to have_link('Reopen merge request')
+ expect(page).not_to have_button('Close merge request')
+ expect(page).not_to have_button('Reopen merge request')
expect(page).not_to have_link('Edit')
end
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 382a7a9321c..259c09b9d11 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -2,13 +2,15 @@
require 'spec_helper'
-RSpec.describe 'issuable list' do
+RSpec.describe 'issuable list', :js do
let(:project) { create(:project) }
let(:user) { create(:user) }
issuable_types = [:issue, :merge_request]
before do
+ stub_feature_flags(vue_issuables_list: false)
+ # something is going on
project.add_user(user, :developer)
sign_in(user)
issuable_types.each { |type| create_issuables(type) }
@@ -26,9 +28,9 @@ RSpec.describe 'issuable list' do
it "counts upvotes, downvotes and notes count for each #{issuable_type.to_s.humanize}" do
visit_issuable_list(issuable_type)
- expect(first('.fa-thumbs-up').find(:xpath, '..')).to have_content(1)
- expect(first('.fa-thumbs-down').find(:xpath, '..')).to have_content(1)
- expect(first('.fa-comments').find(:xpath, '..')).to have_content(2)
+ expect(first('.issuable-upvotes')).to have_content(1)
+ expect(first('.issuable-downvotes')).to have_content(1)
+ expect(first('.issuable-comments')).to have_content(2)
end
it 'sorts labels alphabetically' do
diff --git a/spec/features/issuables/sorting_list_spec.rb b/spec/features/issuables/sorting_list_spec.rb
index 59518723740..ff92fe369d4 100644
--- a/spec/features/issuables/sorting_list_spec.rb
+++ b/spec/features/issuables/sorting_list_spec.rb
@@ -10,6 +10,10 @@ RSpec.describe 'Sort Issuable List' do
let(:first_updated_issuable) { issuables.order_updated_asc.first }
let(:last_updated_issuable) { issuables.order_updated_desc.first }
+ before do
+ stub_feature_flags(vue_issuables_list: false)
+ end
+
context 'for merge requests' do
include MergeRequestHelpers
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 84a786e91a7..91f0e983fa8 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -5,14 +5,18 @@ require 'spec_helper'
RSpec.describe 'Issues > Labels bulk assignment' do
let(:user) { create(:user) }
let!(:project) { create(:project) }
- let!(:issue1) { create(:issue, project: project, title: "Issue 1") }
- let!(:issue2) { create(:issue, project: project, title: "Issue 2") }
let!(:bug) { create(:label, project: project, title: 'bug') }
let!(:feature) { create(:label, project: project, title: 'feature') }
+ let!(:frontend) { create(:label, project: project, title: 'frontend') }
let!(:wontfix) { create(:label, project: project, title: 'wontfix') }
+ let!(:issue1) { create(:issue, project: project, title: "Issue 1", labels: [frontend]) }
+ let!(:issue2) { create(:issue, project: project, title: "Issue 2") }
context 'as an allowed user', :js do
before do
+ # Make sure that issuables list FF is not turned on.
+ stub_feature_flags(vue_issuables_list: false)
+
project.add_maintainer(user)
sign_in user
@@ -48,11 +52,29 @@ RSpec.describe 'Issues > Labels bulk assignment' do
it do
expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+ expect(find("#issue_#{issue1.id}")).to have_content 'frontend'
expect(find("#issue_#{issue2.id}")).to have_content 'bug'
+ expect(find("#issue_#{issue2.id}")).not_to have_content 'frontend'
end
end
- context 'to a issue' do
+ context 'to some issues' do
+ before do
+ check "selected_issue_#{issue1.id}"
+ check "selected_issue_#{issue2.id}"
+ open_labels_dropdown ['bug']
+ update_issues
+ end
+
+ it do
+ expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+ expect(find("#issue_#{issue1.id}")).to have_content 'frontend'
+ expect(find("#issue_#{issue2.id}")).to have_content 'bug'
+ expect(find("#issue_#{issue2.id}")).not_to have_content 'frontend'
+ end
+ end
+
+ context 'to an issue' do
before do
check "selected_issue_#{issue1.id}"
open_labels_dropdown ['bug']
@@ -61,7 +83,24 @@ RSpec.describe 'Issues > Labels bulk assignment' do
it do
expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+ expect(find("#issue_#{issue1.id}")).to have_content 'frontend'
+ expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
+ expect(find("#issue_#{issue2.id}")).not_to have_content 'frontend'
+ end
+ end
+
+ context 'to an issue by selecting the label first' do
+ before do
+ open_labels_dropdown ['bug']
+ check "selected_issue_#{issue1.id}"
+ update_issues
+ end
+
+ it do
+ expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+ expect(find("#issue_#{issue1.id}")).to have_content 'frontend'
expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
+ expect(find("#issue_#{issue2.id}")).not_to have_content 'frontend'
end
end
end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 5b5348d4069..080943da185 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -20,9 +20,7 @@ RSpec.describe 'Filter issues', :js do
let!(:milestone) { create(:milestone, title: "8", project: project, start_date: 2.days.ago) }
def expect_no_issues_list
- page.within '.issues-list' do
- expect(page).to have_no_selector('.issue')
- end
+ expect(page).to have_no_selector('.issue')
end
before do
@@ -88,7 +86,7 @@ RSpec.describe 'Filter issues', :js do
end
it 'does not have the != option' do
- input_filtered_search("label:", submit: false)
+ input_filtered_search("label:", submit: false, extra_space: false)
wait_for_requests
within('#js-dropdown-operator') do
@@ -344,7 +342,7 @@ RSpec.describe 'Filter issues', :js do
context 'issue label clicked' do
it 'filters and displays in search bar' do
- find('.issues-list .issue .issuable-main-info .issuable-info a .gl-label-text', text: multiple_words_label.title).click
+ find('[data-qa-selector="issuable-label"]', text: multiple_words_label.title).click
expect_issues_list_count(1)
expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 167fecc5ab1..2a094281133 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -88,7 +88,7 @@ RSpec.describe 'Search bar', :js do
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size)
end
- it 'resets the dropdown filters', :quarantine do
+ it 'resets the dropdown filters', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/9985' do
filtered_search.click
hint_offset = get_left_style(find('#js-dropdown-hint')['style'])
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 4a7e1ba99e9..0b2e8013304 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -3,455 +3,647 @@
require 'spec_helper'
RSpec.describe 'GFM autocomplete', :js do
- let(:issue_xss_title) { 'This will execute alert<img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
- let(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
- let(:label_xss_title) { 'alert label &lt;img src=x onerror="alert(\'Hello xss\');" a' }
- let(:milestone_xss_title) { 'alert milestone &lt;img src=x onerror="alert(\'Hello xss\');" a' }
-
- let(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
- let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
- let(:project) { create(:project) }
- let(:label) { create(:label, project: project, title: 'special+') }
+ let_it_be(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
+ let_it_be(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
+ let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:label) { create(:label, project: project, title: 'special+') }
+
let(:issue) { create(:issue, project: project) }
- before do
+ before_all do
project.add_maintainer(user)
project.add_maintainer(user_xss)
+ end
- sign_in(user)
- visit project_issue_path(project, issue)
+ describe 'when tribute_autocomplete feature flag is off' do
+ before do
+ stub_feature_flags(tribute_autocomplete: false)
- wait_for_requests
- end
+ sign_in(user)
+ visit project_issue_path(project, issue)
- it 'updates issue description with GFM reference' do
- find('.js-issuable-edit').click
+ wait_for_requests
+ end
- wait_for_requests
+ it 'updates issue description with GFM reference' do
+ find('.js-issuable-edit').click
- simulate_input('#issue-description', "@#{user.name[0...3]}")
+ wait_for_requests
- wait_for_requests
+ simulate_input('#issue-description', "@#{user.name[0...3]}")
- find('.atwho-view .cur').click
+ wait_for_requests
- click_button 'Save changes'
+ find('.atwho-view .cur').click
- wait_for_requests
+ click_button 'Save changes'
- expect(find('.description')).to have_content(user.to_reference)
- end
+ wait_for_requests
- it 'opens autocomplete menu when field starts with text' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('@')
+ expect(find('.description')).to have_content(user.to_reference)
end
- expect(page).to have_selector('.atwho-container')
- end
+ it 'opens quick action autocomplete when updating description' do
+ find('.js-issuable-edit').click
- it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do
- create(:issue, project: project, title: issue_xss_title)
+ find('#issue-description').native.send_keys('/')
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('#')
+ expect(page).to have_selector('.atwho-container')
end
- wait_for_requests
-
- expect(page).to have_selector('.atwho-container')
+ it 'opens autocomplete menu when field starts with text' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@')
+ end
- page.within '.atwho-container #at-view-issues' do
- expect(page.all('li').first.text).to include(issue_xss_title)
+ expect(page).to have_selector('.atwho-container')
end
- end
- it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('@ev')
- end
+ it 'opens autocomplete menu for Issues when field starts with text with item escaping HTML characters' do
+ issue_xss_title = 'This will execute alert<img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;'
+ create(:issue, project: project, title: issue_xss_title)
+
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('#')
+ end
- wait_for_requests
+ wait_for_requests
- expect(page).to have_selector('.atwho-container')
+ expect(page).to have_selector('.atwho-container')
- page.within '.atwho-container #at-view-users' do
- expect(find('li').text).to have_content(user_xss.username)
+ page.within '.atwho-container #at-view-issues' do
+ expect(page.all('li').first.text).to include(issue_xss_title)
+ end
end
- end
- it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do
- create(:milestone, project: project, title: milestone_xss_title)
+ it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@ev')
+ end
+
+ wait_for_requests
+
+ expect(page).to have_selector('.atwho-container')
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('%')
+ page.within '.atwho-container #at-view-users' do
+ expect(find('li').text).to have_content(user_xss.username)
+ end
end
- wait_for_requests
+ it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do
+ milestone_xss_title = 'alert milestone &lt;img src=x onerror="alert(\'Hello xss\');" a'
+ create(:milestone, project: project, title: milestone_xss_title)
+
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('%')
+ end
+
+ wait_for_requests
- expect(page).to have_selector('.atwho-container')
+ expect(page).to have_selector('.atwho-container')
- page.within '.atwho-container #at-view-milestones' do
- expect(find('li').text).to have_content('alert milestone')
+ page.within '.atwho-container #at-view-milestones' do
+ expect(find('li').text).to have_content('alert milestone')
+ end
end
- end
- it 'doesnt open autocomplete menu character is prefixed with text' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('testing')
- find('#note-body').native.send_keys('@')
+ it 'doesnt open autocomplete menu character is prefixed with text' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('testing')
+ find('#note-body').native.send_keys('@')
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
end
- expect(page).not_to have_selector('.atwho-view')
- end
+ it 'doesnt select the first item for non-assignee dropdowns' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys(':')
+ end
+
+ expect(page).to have_selector('.atwho-container')
- it 'doesnt select the first item for non-assignee dropdowns' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys(':')
+ wait_for_requests
+
+ expect(find('#at-view-58')).not_to have_selector('.cur:first-of-type')
end
- expect(page).to have_selector('.atwho-container')
+ it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do
+ note = find('#note-body')
- wait_for_requests
+ # Number.
+ page.within '.timeline-content-form' do
+ note.native.send_keys('7:')
+ end
- expect(find('#at-view-58')).not_to have_selector('.cur:first-of-type')
- end
+ expect(page).not_to have_selector('.atwho-view')
- it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do
- note = find('#note-body')
+ # ASCII letter.
+ page.within '.timeline-content-form' do
+ note.set('')
+ note.native.send_keys('w:')
+ end
- # Number.
- page.within '.timeline-content-form' do
- note.native.send_keys('7:')
- end
+ expect(page).not_to have_selector('.atwho-view')
- expect(page).not_to have_selector('.atwho-view')
+ # Non-ASCII letter.
+ page.within '.timeline-content-form' do
+ note.set('')
+ note.native.send_keys('Ё:')
+ end
- # ASCII letter.
- page.within '.timeline-content-form' do
- note.set('')
- note.native.send_keys('w:')
+ expect(page).not_to have_selector('.atwho-view')
end
- expect(page).not_to have_selector('.atwho-view')
+ it 'selects the first item for assignee dropdowns' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@')
+ end
- # Non-ASCII letter.
- page.within '.timeline-content-form' do
- note.set('')
- note.native.send_keys('Ё:')
- end
+ expect(page).to have_selector('.atwho-container')
- expect(page).not_to have_selector('.atwho-view')
- end
+ wait_for_requests
- it 'selects the first item for assignee dropdowns' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('@')
+ expect(find('#at-view-users')).to have_selector('.cur:first-of-type')
end
- expect(page).to have_selector('.atwho-container')
+ it 'includes items for assignee dropdowns with non-ASCII characters in name' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('')
+ simulate_input('#note-body', "@#{user.name[0...8]}")
+ end
- wait_for_requests
+ expect(page).to have_selector('.atwho-container')
- expect(find('#at-view-users')).to have_selector('.cur:first-of-type')
- end
+ wait_for_requests
- it 'includes items for assignee dropdowns with non-ASCII characters in name' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('')
- simulate_input('#note-body', "@#{user.name[0...8]}")
+ expect(find('#at-view-users')).to have_content(user.name)
end
- expect(page).to have_selector('.atwho-container')
+ it 'selects the first item for non-assignee dropdowns if a query is entered' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys(':1')
+ end
- wait_for_requests
+ expect(page).to have_selector('.atwho-container')
- expect(find('#at-view-users')).to have_content(user.name)
- end
+ wait_for_requests
- it 'selects the first item for non-assignee dropdowns if a query is entered' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys(':1')
+ expect(find('#at-view-58')).to have_selector('.cur:first-of-type')
end
- expect(page).to have_selector('.atwho-container')
+ context 'if a selected value has special characters' do
+ it 'wraps the result in double quotes' do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('')
+ simulate_input('#note-body', "~#{label.title[0]}")
+ end
- wait_for_requests
+ label_item = find('.atwho-view li', text: label.title)
- expect(find('#at-view-58')).to have_selector('.cur:first-of-type')
- end
+ expect_to_wrap(true, label_item, note, label.title)
+ end
- context 'if a selected value has special characters' do
- it 'wraps the result in double quotes' do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys('')
- simulate_input('#note-body', "~#{label.title[0]}")
+ it "shows dropdown after a new line" do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('test')
+ note.native.send_keys(:enter)
+ note.native.send_keys(:enter)
+ note.native.send_keys('@')
+ end
+
+ expect(page).to have_selector('.atwho-container')
end
- label_item = find('.atwho-view li', text: label.title)
+ it "does not show dropdown when preceded with a special character" do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys("@")
+ end
- expect_to_wrap(true, label_item, note, label.title)
- end
+ expect(page).to have_selector('.atwho-container')
- it "shows dropdown after a new line" do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys('test')
- note.native.send_keys(:enter)
- note.native.send_keys(:enter)
- note.native.send_keys('@')
+ page.within '.timeline-content-form' do
+ note.native.send_keys("@")
+ end
+
+ expect(page).to have_selector('.atwho-container', visible: false)
end
- expect(page).to have_selector('.atwho-container')
- end
+ it "does not throw an error if no labels exist" do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('~')
+ end
- it "does not show dropdown when preceded with a special character" do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys("@")
+ expect(page).to have_selector('.atwho-container', visible: false)
end
- expect(page).to have_selector('.atwho-container')
+ it 'doesn\'t wrap for assignee values' do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys("@#{user.username[0]}")
+ end
- page.within '.timeline-content-form' do
- note.native.send_keys("@")
+ user_item = find('.atwho-view li', text: user.username)
+
+ expect_to_wrap(false, user_item, note, user.username)
end
- expect(page).to have_selector('.atwho-container', visible: false)
- end
+ it 'doesn\'t wrap for emoji values' do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys(":cartwheel_")
+ end
- it "does not throw an error if no labels exist" do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys('~')
+ emoji_item = find('.atwho-view li', text: 'cartwheel_tone1')
+
+ expect_to_wrap(false, emoji_item, note, 'cartwheel_tone1')
end
- expect(page).to have_selector('.atwho-container', visible: false)
- end
+ it 'doesn\'t open autocomplete after non-word character' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys("@#{user.username[0..2]}!")
+ end
- it 'doesn\'t wrap for assignee values' do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys("@#{user.username[0]}")
+ expect(page).not_to have_selector('.atwho-view')
+ end
+
+ it 'doesn\'t open autocomplete if there is no space before' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys("hello:#{user.username[0..2]}")
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
end
- user_item = find('.atwho-view li', text: user.username)
+ it 'triggers autocomplete after selecting a quick action' do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('/as')
+ end
+
+ find('.atwho-view li', text: '/assign')
+ note.native.send_keys(:tab)
- expect_to_wrap(false, user_item, note, user.username)
+ user_item = find('.atwho-view li', text: user.username)
+ expect(user_item).to have_content(user.username)
+ end
end
- it 'doesn\'t wrap for emoji values' do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys(":cartwheel_")
+ context 'assignees' do
+ let(:issue_assignee) { create(:issue, project: project) }
+ let(:unassigned_user) { create(:user) }
+
+ before do
+ issue_assignee.update(assignees: [user])
+
+ project.add_maintainer(unassigned_user)
end
- emoji_item = find('.atwho-view li', text: 'cartwheel_tone1')
+ it 'lists users who are currently not assigned to the issue when using /assign' do
+ visit project_issue_path(project, issue_assignee)
- expect_to_wrap(false, emoji_item, note, 'cartwheel_tone1')
- end
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('/as')
+ end
- it 'doesn\'t open autocomplete after non-word character' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys("@#{user.username[0..2]}!")
+ find('.atwho-view li', text: '/assign')
+ note.native.send_keys(:tab)
+
+ wait_for_requests
+
+ expect(find('#at-view-users .atwho-view-ul')).not_to have_content(user.username)
+ expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username)
end
- expect(page).not_to have_selector('.atwho-view')
+ it 'shows dropdown on new issue form' do
+ visit new_project_issue_path(project)
+
+ textarea = find('#issue_description')
+ textarea.native.send_keys('/ass')
+ find('.atwho-view li', text: '/assign')
+ textarea.native.send_keys(:tab)
+
+ expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username)
+ expect(find('#at-view-users .atwho-view-ul')).to have_content(user.username)
+ end
end
- it 'doesn\'t open autocomplete if there is no space before' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys("hello:#{user.username[0..2]}")
+ context 'labels' do
+ it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do
+ label_xss_title = 'alert label &lt;img src=x onerror="alert(\'Hello xss\');" a'
+ create(:label, project: project, title: label_xss_title)
+
+ note = find('#note-body')
+
+ # It should show all the labels on "~".
+ type(note, '~')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('alert label')
+ end
end
- expect(page).not_to have_selector('.atwho-view')
- end
+ it 'allows colons when autocompleting scoped labels' do
+ create(:label, project: project, title: 'scoped:label')
- it 'triggers autocomplete after selecting a quick action' do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys('/as')
+ note = find('#note-body')
+ type(note, '~scoped:')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('scoped:label')
+ end
end
- find('.atwho-view li', text: '/assign')
- note.native.send_keys(:tab)
+ it 'allows colons when autocompleting scoped labels with double colons' do
+ create(:label, project: project, title: 'scoped::label')
- user_item = find('.atwho-view li', text: user.username)
- expect(user_item).to have_content(user.username)
- end
- end
+ note = find('#note-body')
+ type(note, '~scoped::')
- context 'assignees' do
- let(:issue_assignee) { create(:issue, project: project) }
- let(:unassigned_user) { create(:user) }
+ wait_for_requests
- before do
- issue_assignee.update(assignees: [user])
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('scoped::label')
+ end
+ end
+
+ it 'allows spaces when autocompleting multi-word labels' do
+ create(:label, project: project, title: 'Accepting merge requests')
- project.add_maintainer(unassigned_user)
+ note = find('#note-body')
+ type(note, '~Accepting merge')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('Accepting merge requests')
+ end
+ end
+
+ it 'only autocompletes the latest label' do
+ create(:label, project: project, title: 'Accepting merge requests')
+ create(:label, project: project, title: 'Accepting job applicants')
+
+ note = find('#note-body')
+ type(note, '~Accepting merge requests foo bar ~Accepting job')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('Accepting job applicants')
+ end
+ end
+
+ it 'does not autocomplete labels if no tilde is typed' do
+ create(:label, project: project, title: 'Accepting merge requests')
+
+ note = find('#note-body')
+ type(note, 'Accepting merge')
+
+ wait_for_requests
+
+ expect(page).not_to have_css('.atwho-container #at-view-labels')
+ end
end
- it 'lists users who are currently not assigned to the issue when using /assign' do
- visit project_issue_path(project, issue_assignee)
+ shared_examples 'autocomplete suggestions' do
+ it 'suggests objects correctly' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys(object.class.reference_prefix)
+ end
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys('/as')
+ page.within '.atwho-container' do
+ expect(page).to have_content(object.title)
+
+ find('ul li').click
+ end
+
+ expect(find('.new-note #note-body').value).to include(expected_body)
end
+ end
- find('.atwho-view li', text: '/assign')
- note.native.send_keys(:tab)
+ context 'issues' do
+ let(:object) { issue }
+ let(:expected_body) { object.to_reference }
- wait_for_requests
+ it_behaves_like 'autocomplete suggestions'
+ end
+
+ context 'merge requests' do
+ let(:object) { create(:merge_request, source_project: project) }
+ let(:expected_body) { object.to_reference }
- expect(find('#at-view-users .atwho-view-ul')).not_to have_content(user.username)
- expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username)
+ it_behaves_like 'autocomplete suggestions'
end
- it 'shows dropdown on new issue form' do
- visit new_project_issue_path(project)
+ context 'project snippets' do
+ let!(:object) { create(:project_snippet, project: project, title: 'code snippet') }
+ let(:expected_body) { object.to_reference }
- textarea = find('#issue_description')
- textarea.native.send_keys('/ass')
- find('.atwho-view li', text: '/assign')
- textarea.native.send_keys(:tab)
+ it_behaves_like 'autocomplete suggestions'
+ end
+
+ context 'label' do
+ let!(:object) { label }
+ let(:expected_body) { object.title }
+
+ it_behaves_like 'autocomplete suggestions'
+ end
+
+ context 'milestone' do
+ let!(:object) { create(:milestone, project: project) }
+ let(:expected_body) { object.to_reference }
- expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username)
- expect(find('#at-view-users .atwho-view-ul')).to have_content(user.username)
+ it_behaves_like 'autocomplete suggestions'
end
end
- context 'labels' do
- it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do
- create(:label, project: project, title: label_xss_title)
+ describe 'when tribute_autocomplete feature flag is on' do
+ before do
+ stub_feature_flags(tribute_autocomplete: true)
+
+ sign_in(user)
+ visit project_issue_path(project, issue)
- note = find('#note-body')
+ wait_for_requests
+ end
- # It should show all the labels on "~".
- type(note, '~')
+ it 'updates issue description with GFM reference' do
+ find('.js-issuable-edit').click
wait_for_requests
- page.within '.atwho-container #at-view-labels' do
- expect(find('.atwho-view-ul').text).to have_content('alert label')
- end
- end
+ simulate_input('#issue-description', "@#{user.name[0...3]}")
+
+ wait_for_requests
- it 'allows colons when autocompleting scoped labels' do
- create(:label, project: project, title: 'scoped:label')
+ find('.tribute-container .highlight').click
- note = find('#note-body')
- type(note, '~scoped:')
+ click_button 'Save changes'
wait_for_requests
- page.within '.atwho-container #at-view-labels' do
- expect(find('.atwho-view-ul').text).to have_content('scoped:label')
- end
+ expect(find('.description')).to have_content(user.to_reference)
end
- it 'allows colons when autocompleting scoped labels with double colons' do
- create(:label, project: project, title: 'scoped::label')
+ it 'opens autocomplete menu when field starts with text' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@')
+ end
- note = find('#note-body')
- type(note, '~scoped::')
+ expect(page).to have_selector('.tribute-container')
+ end
+
+ it 'opens autocomplete menu for Username when field starts with text with item escaping HTML characters' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@ev')
+ end
wait_for_requests
- page.within '.atwho-container #at-view-labels' do
- expect(find('.atwho-view-ul').text).to have_content('scoped::label')
+ expect(page).to have_selector('.tribute-container')
+
+ page.within '.tribute-container ul' do
+ expect(find('li').text).to have_content(user_xss.username)
end
end
- it 'allows spaces when autocompleting multi-word labels' do
- create(:label, project: project, title: 'Accepting merge requests')
+ it 'doesnt open autocomplete menu character is prefixed with text' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('testing')
+ find('#note-body').native.send_keys('@')
+ end
- note = find('#note-body')
- type(note, '~Accepting merge')
+ expect(page).not_to have_selector('.tribute-container')
+ end
+
+ it 'selects the first item for assignee dropdowns' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@')
+ end
+
+ expect(page).to have_selector('.tribute-container')
wait_for_requests
- page.within '.atwho-container #at-view-labels' do
- expect(find('.atwho-view-ul').text).to have_content('Accepting merge requests')
- end
+ expect(find('.tribute-container ul')).to have_selector('.highlight:first-of-type')
end
- it 'only autocompletes the latest label' do
- create(:label, project: project, title: 'Accepting merge requests')
- create(:label, project: project, title: 'Accepting job applicants')
+ it 'includes items for assignee dropdowns with non-ASCII characters in name' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('')
+ simulate_input('#note-body', "@#{user.name[0...8]}")
+ end
- note = find('#note-body')
- type(note, '~Accepting merge requests foo bar ~Accepting job')
+ expect(page).to have_selector('.tribute-container')
wait_for_requests
- page.within '.atwho-container #at-view-labels' do
- expect(find('.atwho-view-ul').text).to have_content('Accepting job applicants')
- end
+ expect(find('.tribute-container')).to have_content(user.name)
end
- it 'does not autocomplete labels if no tilde is typed' do
- create(:label, project: project, title: 'Accepting merge requests')
+ context 'if a selected value has special characters' do
+ it "shows dropdown after a new line" do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('test')
+ note.native.send_keys(:enter)
+ note.native.send_keys(:enter)
+ note.native.send_keys('@')
+ end
- note = find('#note-body')
- type(note, 'Accepting merge')
+ expect(page).to have_selector('.tribute-container')
+ end
- wait_for_requests
+ it "does not show dropdown when preceded with a special character" do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys("@")
+ end
- expect(page).not_to have_css('.atwho-container #at-view-labels')
- end
- end
+ expect(page).to have_selector('.tribute-container')
- shared_examples 'autocomplete suggestions' do
- it 'suggests objects correctly' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys(object.class.reference_prefix)
+ page.within '.timeline-content-form' do
+ note.native.send_keys("@")
+ end
+
+ expect(page).to have_selector('.tribute-container', visible: false)
end
- page.within '.atwho-container' do
- expect(page).to have_content(object.title)
+ it 'doesn\'t wrap for assignee values' do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys("@#{user.username[0]}")
+ end
- find('ul li').click
+ user_item = find('.tribute-container li', text: user.username)
+
+ expect_to_wrap(false, user_item, note, user.username)
end
- expect(find('.new-note #note-body').value).to include(expected_body)
- end
- end
+ it 'doesn\'t open autocomplete after non-word character' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys("@#{user.username[0..2]}!")
+ end
- context 'issues' do
- let(:object) { issue }
- let(:expected_body) { object.to_reference }
+ expect(page).not_to have_selector('.tribute-container')
+ end
- it_behaves_like 'autocomplete suggestions'
- end
+ it 'triggers autocomplete after selecting a quick action' do
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('/as')
+ end
- context 'merge requests' do
- let(:object) { create(:merge_request, source_project: project) }
- let(:expected_body) { object.to_reference }
+ find('.atwho-view li', text: '/assign')
+ note.native.send_keys(:tab)
+ note.native.send_keys(:right)
- it_behaves_like 'autocomplete suggestions'
- end
+ wait_for_requests
- context 'project snippets' do
- let!(:object) { create(:project_snippet, project: project, title: 'code snippet') }
- let(:expected_body) { object.to_reference }
+ user_item = find('.tribute-container li', text: user.username)
+ expect(user_item).to have_content(user.username)
+ end
+ end
- it_behaves_like 'autocomplete suggestions'
- end
+ context 'assignees' do
+ let(:issue_assignee) { create(:issue, project: project) }
+ let(:unassigned_user) { create(:user) }
- context 'label' do
- let!(:object) { label }
- let(:expected_body) { object.title }
+ before do
+ issue_assignee.update(assignees: [user])
- it_behaves_like 'autocomplete suggestions'
- end
+ project.add_maintainer(unassigned_user)
+ end
+
+ it 'lists users who are currently not assigned to the issue when using /assign' do
+ visit project_issue_path(project, issue_assignee)
+
+ note = find('#note-body')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('/as')
+ end
- context 'milestone' do
- let!(:object) { create(:milestone, project: project) }
- let(:expected_body) { object.to_reference }
+ find('.atwho-view li', text: '/assign')
+ note.native.send_keys(:tab)
+ note.native.send_keys(:right)
- it_behaves_like 'autocomplete suggestions'
+ wait_for_requests
+
+ expect(find('.tribute-container ul')).not_to have_content(user.username)
+ expect(find('.tribute-container ul')).to have_content(unassigned_user.username)
+ end
+ end
end
private
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index ab319daec71..9879703c8bf 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe 'Issue Detail', :js do
visit project_issue_path(project, issue)
end
- it 'encodes the description to prevent xss issues', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/207951' do
+ it 'encodes the description to prevent xss issues', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/207951' do
page.within('.issuable-details .detail-page-description') do
image = find('img.js-lazy-loaded')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 9e4362bf0e5..ecda80f2483 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -195,7 +195,7 @@ RSpec.describe 'Issue Sidebar' do
end
end
- context 'creating a project label', :js, :quarantine do
+ context 'creating a project label', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27992' do
before do
page.within('.block.labels') do
click_link 'Create project'
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index f3a6655f397..ee2fbf0865e 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -97,6 +97,45 @@ RSpec.describe 'issue move to another project' do
end
end
+ context 'service desk issue moved to a project with service desk disabled', :js do
+ let(:project_title) { 'service desk disabled project' }
+ let(:warning_selector) { '.js-alert-moved-from-service-desk-warning' }
+ let(:namespace) { create(:namespace) }
+ let(:regular_project) { create(:project, title: project_title, service_desk_enabled: false) }
+ let(:service_desk_project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) }
+ let(:service_desk_issue) { create(:issue, project: service_desk_project, author: ::User.support_bot) }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
+ allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
+
+ regular_project.add_reporter(user)
+ service_desk_project.add_reporter(user)
+
+ visit issue_path(service_desk_issue)
+
+ find('.js-move-issue').click
+ wait_for_requests
+ find('.js-move-issue-dropdown-item', text: project_title).click
+ find('.js-move-issue-confirmation-button').click
+ end
+
+ it 'shows an alert after being moved' do
+ expect(page).to have_content('This project does not have Service Desk enabled')
+ end
+
+ it 'does not show an alert after being dismissed' do
+ find("#{warning_selector} .js-close").click
+
+ expect(page).to have_no_selector(warning_selector)
+
+ page.refresh
+
+ expect(page).to have_no_selector(warning_selector)
+ end
+ end
+
def issue_path(issue)
project_issue_path(issue.project, issue)
end
diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb
new file mode 100644
index 00000000000..0995aa11654
--- /dev/null
+++ b/spec/features/issues/service_desk_spec.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Service Desk Issue Tracker', :js do
+ let(:project) { create(:project, :private, service_desk_enabled: true) }
+ let(:user) { create(:user) }
+
+ before do
+ stub_feature_flags(vue_issuables_list: false)
+
+ allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
+ allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
+
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe 'navigation to service desk' do
+ before do
+ visit project_path(project)
+ find('.sidebar-top-level-items .shortcuts-issues').click
+ find('.sidebar-sub-level-items a[title="Service Desk"]').click
+ end
+
+ it 'can navigate to the service desk from link in the sidebar' do
+ expect(page).to have_content('Use Service Desk to connect with your users')
+ end
+ end
+
+ describe 'issues list' do
+ context 'when service desk is misconfigured' do
+ before do
+ allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(false)
+ visit service_desk_project_issues_path(project)
+ end
+
+ it 'shows a message to say the configuration is incomplete' do
+ expect(page).to have_css('.empty-state')
+ expect(page).to have_text('Service Desk is enabled but not yet active')
+ expect(page).to have_text('You must set up incoming email before it becomes active')
+ expect(page).to have_link('More information', href: help_page_path('administration/incoming_email', anchor: 'set-it-up'))
+ end
+ end
+
+ context 'when service desk has not been activated' do
+ let(:project_without_service_desk) { create(:project, :private, service_desk_enabled: false) }
+
+ describe 'service desk info content' do
+ context 'when user has permissions to edit project settings' do
+ before do
+ project_without_service_desk.add_maintainer(user)
+ visit service_desk_project_issues_path(project_without_service_desk)
+ end
+
+ it 'displays the large info box, documentation, and a button to configure' do
+ aggregate_failures do
+ expect(page).to have_css('.empty-state')
+ expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk'))
+ expect(page).to have_link('Turn on Service Desk')
+ end
+ end
+ end
+
+ context 'when user does not have permission to edit project settings' do
+ before do
+ project_without_service_desk.add_guest(user)
+ visit service_desk_project_issues_path(project_without_service_desk)
+ end
+
+ it 'does not show a button configure service desk' do
+ expect(page).not_to have_link('Turn on Service Desk')
+ end
+ end
+ end
+ end
+
+ context 'when service desk has been activated' do
+ context 'when there are no issues' do
+ describe 'service desk info content' do
+ before do
+ visit service_desk_project_issues_path(project)
+ end
+
+ it 'displays the large info box, documentation, and the address' do
+ aggregate_failures do
+ expect(page).to have_css('.empty-state')
+ expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk'))
+ expect(page).not_to have_link('Turn on Service Desk')
+ expect(page).to have_content(project.service_desk_address)
+ end
+ end
+
+ context 'when user does not have permission to edit project settings' do
+ before do
+ user_2 = create(:user)
+
+ project.add_guest(user_2)
+ sign_in(user_2)
+ visit service_desk_project_issues_path(project)
+ end
+
+ it 'displays the large info box and the documentation link' do
+ aggregate_failures do
+ expect(page).to have_css('.empty-state')
+ expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk'))
+ expect(page).not_to have_link('Turn on Service Desk')
+ expect(page).not_to have_content(project.service_desk_address)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when there are issues' do
+ let(:support_bot) { User.support_bot }
+ let(:other_user) { create(:user) }
+ let!(:service_desk_issue) { create(:issue, project: project, author: support_bot) }
+ let!(:other_user_issue) { create(:issue, project: project, author: other_user) }
+
+ describe 'service desk info content' do
+ before do
+ visit service_desk_project_issues_path(project)
+ end
+
+ it 'displays the small info box, documentation, a button to configure service desk, and the address' do
+ aggregate_failures do
+ expect(page).to have_css('.non-empty-state')
+ expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk'))
+ expect(page).not_to have_link('Turn on Service Desk')
+ expect(page).to have_content(project.service_desk_address)
+ end
+ end
+ end
+
+ describe 'issues list' do
+ before do
+ visit service_desk_project_issues_path(project)
+ end
+
+ it 'only displays issues created by support bot' do
+ expect(page).to have_selector('.issues-list .issue', count: 1)
+ end
+ end
+
+ describe 'search box' do
+ before do
+ visit service_desk_project_issues_path(project)
+ end
+
+ it 'adds hidden support bot author token' do
+ expect(page).to have_selector('.filtered-search-token .value', text: 'Support Bot', visible: false)
+ end
+
+ it 'support bot author token cannot be deleted' do
+ find('.input-token .filtered-search').native.send_key(:backspace)
+ expect(page).to have_selector('.js-visual-token', count: 1)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index f8385f183d2..dfe3a1bf1b3 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
let!(:user) { create(:user)}
before do
+ stub_feature_flags(vue_issuables_list: false)
project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index a546fb3e85b..617eac88973 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
end
# In order to improve tests performance, all UI checks are placed in this test.
- it 'shows elements', :quarantine do
+ it 'shows elements', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27993' do
button_create_merge_request = find('.js-create-merge-request')
button_toggle_dropdown = find('.create-mr-dropdown-wrap .dropdown-toggle')
@@ -71,7 +71,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
perform_enqueued_jobs do
select_dropdown_option('create-mr')
- expect(page).to have_content('WIP: Resolve "Cherry-Coloured Funk"')
+ expect(page).to have_content('Draft: Resolve "Cherry-Coloured Funk"')
expect(current_path).to eq(project_merge_request_path(project, MergeRequest.first))
wait_for_requests
@@ -100,7 +100,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
perform_enqueued_jobs do
select_dropdown_option('create-mr', branch_name)
- expect(page).to have_content('WIP: Resolve "Cherry-Coloured Funk"')
+ expect(page).to have_content('Draft: Resolve "Cherry-Coloured Funk"')
expect(page).to have_content('Request to merge custom-branch-name into')
expect(current_path).to eq(project_merge_request_path(project, MergeRequest.first))
@@ -141,7 +141,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
visit project_issue_path(project, issue)
end
- it 'disables the create branch button', :quarantine do
+ it 'disables the create branch button', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27985' do
expect(page).to have_css('.create-mr-dropdown-wrap .unavailable:not(.hidden)')
expect(page).to have_css('.create-mr-dropdown-wrap .available.hidden', visible: false)
expect(page).to have_content /Related merge requests/
diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb
index 20ad47b111a..54a600910ef 100644
--- a/spec/features/issues/user_filters_issues_spec.rb
+++ b/spec/features/issues/user_filters_issues_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'User filters issues' do
let_it_be(:project) { create(:project_empty_repo, :public) }
before do
+ stub_feature_flags(vue_issuables_list: false)
+
%w[foobar barbaz].each do |title|
create(:issue,
author: user,
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index 7b7e087a6d6..35d9db68d32 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'User interacts with awards' do
visit(project_issue_path(project, issue))
end
- it 'toggles the thumbsup award emoji', :quarantine do
+ it 'toggles the thumbsup award emoji', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27959' do
page.within('.awards') do
thumbsup = page.first('.award-control')
thumbsup.click
@@ -77,7 +77,7 @@ RSpec.describe 'User interacts with awards' do
end
end
- it 'shows the list of award emoji categories', :quarantine do
+ it 'shows the list of award emoji categories', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27991' do
page.within('.awards') do
page.find('.js-add-award').click
end
diff --git a/spec/features/issues/user_sees_breadcrumb_links_spec.rb b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
index 2660101c330..f5793758a9b 100644
--- a/spec/features/issues/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'New issue breadcrumb' do
let(:user) { project.creator }
before do
+ stub_feature_flags(vue_issuables_list: false)
+
sign_in(user)
visit(new_project_issue_path(project))
end
diff --git a/spec/features/issues/user_sees_empty_state_spec.rb b/spec/features/issues/user_sees_empty_state_spec.rb
index 047c5ca2189..e39369b0150 100644
--- a/spec/features/issues/user_sees_empty_state_spec.rb
+++ b/spec/features/issues/user_sees_empty_state_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe 'Issues > User sees empty state' do
let_it_be(:project) { create(:project, :public) }
let_it_be(:user) { project.creator }
+ before do
+ stub_feature_flags(vue_issuables_list: false)
+ end
+
shared_examples_for 'empty state with filters' do
it 'user sees empty state with filters' do
create(:issue, author: user, project: project)
diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb
index ec38bf99035..91c6419b464 100644
--- a/spec/features/issues/user_sorts_issues_spec.rb
+++ b/spec/features/issues/user_sorts_issues_spec.rb
@@ -16,6 +16,8 @@ RSpec.describe "User sorts issues" do
let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') }
before do
+ stub_feature_flags(vue_issuables_list: false)
+
create_list(:award_emoji, 2, :upvote, awardable: issue1)
create_list(:award_emoji, 2, :downvote, awardable: issue2)
create(:award_emoji, :downvote, awardable: issue1)
diff --git a/spec/features/issues/user_views_issues_spec.rb b/spec/features/issues/user_views_issues_spec.rb
index 91de813e414..34cea7f3b0b 100644
--- a/spec/features/issues/user_views_issues_spec.rb
+++ b/spec/features/issues/user_views_issues_spec.rb
@@ -6,9 +6,14 @@ RSpec.describe "User views issues" do
let!(:closed_issue) { create(:closed_issue, project: project) }
let!(:open_issue1) { create(:issue, project: project) }
let!(:open_issue2) { create(:issue, project: project) }
+ let!(:moved_open_issue) { create(:issue, project: project, moved_to: create(:issue)) }
let_it_be(:user) { create(:user) }
+ before do
+ stub_feature_flags(vue_issuables_list: false)
+ end
+
shared_examples "opens issue from list" do
it "opens issue" do
click_link(issue.title)
@@ -32,6 +37,7 @@ RSpec.describe "User views issues" do
.and have_content(open_issue1.title)
.and have_content(open_issue2.title)
.and have_no_content(closed_issue.title)
+ .and have_content(moved_open_issue.title)
.and have_no_selector(".js-new-board-list")
end
@@ -62,6 +68,7 @@ RSpec.describe "User views issues" do
.and have_content(closed_issue.title)
.and have_no_content(open_issue1.title)
.and have_no_content(open_issue2.title)
+ .and have_no_content(moved_open_issue.title)
.and have_no_selector(".js-new-board-list")
end
@@ -82,6 +89,8 @@ RSpec.describe "User views issues" do
.and have_content(closed_issue.title)
.and have_content(open_issue1.title)
.and have_content(open_issue2.title)
+ .and have_content(moved_open_issue.title)
+ .and have_no_content('CLOSED (MOVED)')
.and have_no_selector(".js-new-board-list")
end
@@ -116,4 +125,24 @@ RSpec.describe "User views issues" do
context "when not signed in" do
include_examples "public project"
end
+
+ context 'when vue_issuables_list feature is enabled', :js do
+ before do
+ stub_feature_flags(vue_issuables_list: true)
+ end
+
+ context 'when signed in' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ include_examples "public project"
+ include_examples "internal project"
+ end
+
+ context 'when not signed in' do
+ include_examples "public project"
+ end
+ end
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 3ab7fbea198..1545cb36e9b 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Labels Hierarchy', :js do
end
shared_examples 'assigning labels from sidebar' do
- it 'can assign all ancestors labels', :quarantine do
+ it 'can assign all ancestors labels', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27952' do
[grandparent_group_label, parent_group_label, project_label_1].each do |label|
page.within('.block.labels') do
find('.edit-link').click
diff --git a/spec/features/markdown/metrics_spec.rb b/spec/features/markdown/metrics_spec.rb
index 092408c2be0..3e63ae67f19 100644
--- a/spec/features/markdown/metrics_spec.rb
+++ b/spec/features/markdown/metrics_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
+RSpec.describe 'Metrics rendering', :js, :kubeclient, :use_clean_rails_memory_store_caching, :sidekiq_inline do
include PrometheusHelpers
+ include KubernetesHelpers
include GrafanaApiHelpers
include MetricsDashboardUrlHelpers
@@ -166,6 +167,41 @@ RSpec.describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching,
end
end
+ context 'for GitLab embedded cluster health metrics' do
+ before do
+ project.add_maintainer(user)
+ import_common_metrics
+ stub_any_prometheus_request_with_response
+
+ allow(Prometheus::ProxyService).to receive(:new).and_call_original
+
+ create(:clusters_applications_prometheus, :installed, cluster: cluster)
+ stub_kubeclient_discover(cluster.platform.api_url)
+ stub_prometheus_request(/prometheus-prometheus-server/, body: prometheus_values_body)
+ stub_prometheus_request(/prometheus\/api\/v1/, body: prometheus_values_body)
+ end
+
+ let_it_be(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [project], user: user) }
+ let(:params) { [project.namespace.path, project.path, cluster.id] }
+ let(:query_params) { { group: 'Cluster Health', title: 'CPU Usage', y_label: 'CPU (cores)' } }
+ let(:metrics_url) { urls.namespace_project_cluster_url(*params, **query_params) }
+ let(:description) { "# Summary \n[](#{metrics_url})" }
+
+ it 'shows embedded metrics' do
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('div.prometheus-graph')
+ expect(page).to have_text(query_params[:title])
+ expect(page).to have_text(query_params[:y_label])
+ expect(page).not_to have_text(metrics_url)
+
+ expect(Prometheus::ProxyService)
+ .to have_received(:new)
+ .with(cluster, 'GET', 'query_range', hash_including('start', 'end', 'step'))
+ .at_least(:once)
+ end
+ end
+
def import_common_metrics
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 7573553b3fb..be12f774c29 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork
end
before do
- stub_feature_flags(web_ide_default: false, single_mr_diff_view: false)
+ stub_feature_flags(single_mr_diff_view: false)
target_project.add_maintainer(user)
sign_in(user)
@@ -37,7 +37,7 @@ RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork
end
it 'allows committing to the source branch' do
- find('.ace_text-input', visible: false).send_keys('Updated the readme')
+ execute_script("monaco.editor.getModels()[0].setValue('Updated the readme')")
click_button 'Commit changes'
wait_for_requests
diff --git a/spec/features/merge_request/user_approves_spec.rb b/spec/features/merge_request/user_approves_spec.rb
new file mode 100644
index 00000000000..d319fdcb87b
--- /dev/null
+++ b/spec/features/merge_request/user_approves_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge request > User approves', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_developer(user)
+
+ sign_in(user)
+
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'approves merge request' do
+ click_approval_button('Approve')
+ expect(page).to have_content('Merge request approved')
+
+ verify_approvals_count_on_index!
+
+ click_approval_button('Revoke approval')
+ expect(page).to have_content('No approval required; you can still approve')
+ end
+
+ def verify_approvals_count_on_index!
+ visit(project_merge_requests_path(project, state: :all))
+ expect(page.all('li').any? { |item| item["title"] == "1 approver (you've approved)"}).to be true
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ def click_approval_button(action)
+ page.within('.mr-state-widget') do
+ click_button(action)
+ end
+
+ wait_for_requests
+ end
+end
diff --git a/spec/features/merge_request/user_closes_merge_request_spec.rb b/spec/features/merge_request/user_closes_merge_request_spec.rb
index 669bd989f4f..e6b6778c76e 100644
--- a/spec/features/merge_request/user_closes_merge_request_spec.rb
+++ b/spec/features/merge_request/user_closes_merge_request_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'User closes a merge requests', :js do
end
it 'closes a merge request' do
- click_link('Close merge request', match: :first)
+ click_button('Close merge request', match: :first)
expect(page).to have_content(merge_request.title)
expect(page).to have_content('Closed by')
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
index 9cd3c7eaf76..30bf82e3665 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -117,9 +117,58 @@ RSpec.describe 'User comments on a diff', :js do
context 'when adding multiline comments' do
it 'saves a multiline comment' do
click_diff_line(find("[id='#{sample_commit.line_code}']"))
+ add_comment('-13', '+14')
+ end
+
+ context 'when in side-by-side view' do
+ before do
+ visit(diffs_project_merge_request_path(project, merge_request, view: 'parallel'))
+ end
+
+ # In `files/ruby/popen.rb`
+ it 'allows comments for changes involving both sides' do
+ # click +15, select -13 add and verify comment
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .new_line a[data-linenumber="15"]').find(:xpath, '../..'), 'right')
+ add_comment('-13', '+15')
+ end
+
+ it 'allows comments to start above hidden lines and end below' do
+ # click +28, select 21 add and verify comment
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .new_line a[data-linenumber="28"]').find(:xpath, '../..'), 'right')
+ add_comment('21', '+28')
+ end
+
+ it 'allows comments on previously hidden lines at the top of a file' do
+ # Click -9, expand up, select 1 add and verify comment
+ page.within('[data-path="files/ruby/popen.rb"]') do
+ all('.js-unfold-all')[0].click
+ end
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .old_line a[data-linenumber="9"]').find(:xpath, '../..'), 'left')
+ add_comment('1', '-9')
+ end
+
+ it 'allows comments on previously hidden lines the middle of a file' do
+ # Click 27, expand up, select 18, add and verify comment
+ page.within('[data-path="files/ruby/popen.rb"]') do
+ all('.js-unfold-all')[1].click
+ end
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .old_line a[data-linenumber="21"]').find(:xpath, '../..'), 'left')
+ add_comment('18', '21')
+ end
+
+ it 'allows comments on previously hidden lines at the bottom of a file' do
+ # Click +28, expand down, select 37 add and verify comment
+ page.within('[data-path="files/ruby/popen.rb"]') do
+ all('.js-unfold-down')[1].click
+ end
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .old_line a[data-linenumber="30"]').find(:xpath, '../..'), 'left')
+ add_comment('+28', '37')
+ end
+ end
+ def add_comment(start_line, end_line)
page.within('.discussion-form') do
- find('#comment-line-start option', text: '-13').select_option
+ find('#comment-line-start option', exact_text: start_line).select_option
end
page.within('.js-discussion-note-form') do
@@ -131,7 +180,7 @@ RSpec.describe 'User comments on a diff', :js do
page.within('.notes_holder') do
expect(page).to have_content('Line is wrong')
- expect(page).to have_content('Comment on lines -13 to +14')
+ expect(page).to have_content("Comment on lines #{start_line} to #{end_line}")
end
visit(merge_request_path(merge_request))
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index 34eaca24a01..3cd23764382 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -88,7 +88,7 @@ RSpec.describe 'Merge request > User creates image diff notes', :js do
create_image_diff_note
end
- it 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes', :quarantine do
+ it 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/27950' do
indicator = find('.js-image-badge', match: :first)
badge = find('.user-avatar-link .badge', match: :first)
diff --git a/spec/features/merge_request/user_edits_merge_request_spec.rb b/spec/features/merge_request/user_edits_merge_request_spec.rb
index 84ecd6537dc..6c5f508c8c6 100644
--- a/spec/features/merge_request/user_edits_merge_request_spec.rb
+++ b/spec/features/merge_request/user_edits_merge_request_spec.rb
@@ -16,6 +16,75 @@ RSpec.describe 'User edits a merge request', :js do
visit(edit_project_merge_request_path(project, merge_request))
end
+ describe 'Squash commits' do
+ it 'override MR setting if "Required" is saved' do
+ merge_request.update!(squash: false)
+
+ project.project_setting.update!(squash_option: 'always')
+ visit(edit_project_merge_request_path(project, merge_request))
+ click_button('Save changes')
+
+ project.project_setting.update!(squash_option: 'default_off')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ expect(find("#merge_request_squash").selected?).to be(true)
+ end
+
+ it 'recovers MR squash setting if "Required" is not saved' do
+ merge_request.update!(squash: false)
+
+ project.project_setting.update!(squash_option: 'always')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ project.project_setting.update!(squash_option: 'default_on')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ expect(find("#merge_request_squash").selected?).to be(false)
+ end
+
+ it 'does not override MR squash setting if "Do not allow" is saved' do
+ merge_request.update!(squash: true)
+
+ project.project_setting.update!(squash_option: 'never')
+ visit(edit_project_merge_request_path(project, merge_request))
+ click_button('Save changes')
+
+ expect(merge_request.squash).to be true
+ end
+
+ it 'displays "Required in this project" for "Required" project setting squash option' do
+ project.project_setting.update!(squash_option: 'always')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ expect(page).to have_content('Squash commits when merge request is accepted.')
+ expect(page).to have_content("Required in this project")
+ end
+
+ it 'does not display message for "Allow" project setting squash option' do
+ project.project_setting.update!(squash_option: 'default_off')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ expect(page).to have_content('Squash commits when merge request is accepted.')
+ expect(page).not_to have_content("Required in this project")
+ end
+
+ it 'does not display message for "Encourage" project setting squash option' do
+ project.project_setting.update!(squash_option: 'default_on')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ expect(page).to have_content('Squash commits when merge request is accepted.')
+ expect(page).not_to have_content("Required in this project")
+ end
+
+ it 'is hidden for "Do not allow" project setting squash option' do
+ project.project_setting.update!(squash_option: 'never')
+ visit(edit_project_merge_request_path(project, merge_request))
+
+ expect(page).not_to have_content('Squash commits when merge request is accepted.')
+ expect(page).not_to have_css('#merge_request_squash')
+ end
+ end
+
it 'changes the target branch' do
expect(page).to have_content('From master into feature')
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index dbad2f191a1..6ecffb05009 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
end
context 'with an old line on the left and a new line on the right' do
- it 'allows commenting on the left side', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/199050' do
+ it 'allows commenting on the left side', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/199050' do
should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'left')
end
@@ -56,7 +56,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
end
context 'with an unchanged line on the left and an unchanged line on the right' do
- it 'allows commenting on the left side', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/196826' do
+ it 'allows commenting on the left side', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/196826' do
should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'left')
end
diff --git a/spec/features/merge_request/user_reopens_merge_request_spec.rb b/spec/features/merge_request/user_reopens_merge_request_spec.rb
index 020929dc416..7866ece84ac 100644
--- a/spec/features/merge_request/user_reopens_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reopens_merge_request_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'User reopens a merge requests', :js do
end
it 'reopens a merge request' do
- click_link('Reopen merge request', match: :first)
+ click_button('Reopen merge request', match: :first)
page.within('.status-box') do
expect(page).to have_content('Open')
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 2229b242d5b..d067fc0ada4 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
end
context 'as user who needs to fork' do
- it 'shows fork/cancel confirmation', :sidekiq_might_not_need_inline, quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/196749' do
+ it 'shows fork/cancel confirmation', :sidekiq_might_not_need_inline, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/196749' do
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request)
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 bd140a0643d..ce49e9f4141 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -268,7 +268,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
end
end
- context 'view merge request where project has CI set up but no CI status' do
+ context 'view merge request where there is no pipeline yet' do
before do
pipeline = create(:ci_pipeline, project: project,
sha: merge_request.diff_head_sha,
@@ -278,11 +278,11 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
visit project_merge_request_path(project, merge_request)
end
- it 'has pipeline error text' do
+ it 'has pipeline loading state' do
# Wait for the `ci_status` and `merge_check` requests
wait_for_requests
- expect(page).to have_text("Could not retrieve the pipeline status. For troubleshooting steps, read the documentation.")
+ expect(page).to have_text("Checking pipeline status")
end
end
@@ -889,9 +889,9 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
visit project_merge_request_path(project, merge_request)
end
- it 'renders a CI pipeline error' do
+ it 'renders a CI pipeline loading state' do
within '.ci-widget' do
- expect(page).to have_content('Could not retrieve the pipeline status.')
+ expect(page).to have_content('Checking pipeline status')
end
end
end
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index 2d125087cb6..d693eec91c8 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -38,14 +38,6 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
expect(page).to have_selector('.stage-cell')
end
- it 'pipeline sha does not equal last commit sha' do
- pipeline.update_attribute(:sha, '19e2e9b4ef76b422ce1154af39a91323ccc57434')
- visit project_merge_request_path(project, merge_request)
- wait_for_requests
-
- expect(page.find('.ci-widget')).to have_text("Could not retrieve the pipeline status. For troubleshooting steps, read the documentation.")
- end
-
context 'with a detached merge request pipeline' do
let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
@@ -92,6 +84,111 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
end
end
+ describe 'fork MRs in parent project', :sidekiq_inline do
+ include ProjectForksHelper
+
+ let_it_be(:parent_project) { create(:project, :public, :repository) }
+ let_it_be(:forked_project) { fork_project(parent_project, developer_in_fork, repository: true, target_project: create(:project, :public, :repository)) }
+ let_it_be(:developer_in_parent) { create(:user) }
+ let_it_be(:developer_in_fork) { create(:user) }
+ let_it_be(:reporter_in_parent_and_developer_in_fork) { create(:user) }
+
+ let(:merge_request) do
+ create(:merge_request, :with_detached_merge_request_pipeline,
+ source_project: forked_project, source_branch: 'feature',
+ target_project: parent_project, target_branch: 'master')
+ end
+
+ let(:config) do
+ { test: { script: 'test', rules: [{ if: '$CI_MERGE_REQUEST_ID' }] } }
+ end
+
+ before_all do
+ parent_project.add_developer(developer_in_parent)
+ parent_project.add_reporter(reporter_in_parent_and_developer_in_fork)
+ forked_project.add_developer(developer_in_fork)
+ forked_project.add_developer(reporter_in_parent_and_developer_in_fork)
+ end
+
+ before do
+ stub_ci_pipeline_yaml_file(YAML.dump(config))
+ sign_in(actor)
+ end
+
+ after do
+ parent_project.all_pipelines.delete_all
+ forked_project.all_pipelines.delete_all
+ end
+
+ context 'when actor is a developer in parent project' do
+ let(:actor) { developer_in_parent }
+
+ it 'creates a pipeline in the parent project' do
+ visit project_merge_request_path(parent_project, merge_request)
+
+ create_merge_request_pipeline
+
+ check_pipeline(expected_project: parent_project)
+ check_head_pipeline(expected_project: parent_project)
+ end
+ end
+
+ context 'when actor is a developer in fork project' do
+ let(:actor) { developer_in_fork }
+
+ it 'creates a pipeline in the fork project' do
+ visit project_merge_request_path(parent_project, merge_request)
+
+ create_merge_request_pipeline
+
+ check_pipeline(expected_project: forked_project)
+ check_head_pipeline(expected_project: forked_project)
+ end
+ end
+
+ context 'when actor is a reporter in parent project and a developer in fork project' do
+ let(:actor) { reporter_in_parent_and_developer_in_fork }
+
+ it 'creates a pipeline in the fork project' do
+ visit project_merge_request_path(parent_project, merge_request)
+
+ create_merge_request_pipeline
+
+ check_pipeline(expected_project: forked_project)
+ check_head_pipeline(expected_project: forked_project)
+ end
+ end
+
+ def create_merge_request_pipeline
+ page.within('.merge-request-tabs') { click_link('Pipelines') }
+ click_button('Run Pipeline')
+ end
+
+ def check_pipeline(expected_project:)
+ page.within('.ci-table') do
+ expect(page).to have_selector('.commit', count: 2)
+
+ page.within(first('.commit')) do
+ page.within('.pipeline-tags') do
+ expect(page.find('.js-pipeline-url-link')[:href]).to include(expected_project.full_path)
+ expect(page).to have_content('detached')
+ end
+ page.within('.pipeline-triggerer') do
+ expect(page).to have_link(href: user_path(actor))
+ end
+ end
+ end
+ end
+
+ def check_head_pipeline(expected_project:)
+ page.within('.merge-request-tabs') { click_link('Overview') }
+
+ page.within('.ci-widget-content') do
+ expect(page.find('.pipeline-id')[:href]).to include(expected_project.full_path)
+ end
+ end
+ end
+
describe 'race condition' do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
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 b81c0e49538..0506d190487 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
@@ -155,7 +155,7 @@ RSpec.describe 'User comments on a diff', :js do
end
end
- it 'can apply multiple suggestions as a batch' do
+ it 'can apply multiple suggestions as a batch', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/224100' do
files.each_with_index do |file, index|
page.within("[id='#{file[:hash]}']") do
find("button[title='Show full file']").click
@@ -188,8 +188,7 @@ RSpec.describe 'User comments on a diff', :js do
end
context 'multiple suggestions in expanded lines' do
- # https://gitlab.com/gitlab-org/gitlab/issues/38277
- it 'suggestions are appliable', :quarantine do
+ it 'suggestions are appliable', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/38277' do
diff_file = merge_request.diffs(paths: ['files/ruby/popen.rb']).diff_files.first
hash = Digest::SHA1.hexdigest(diff_file.file_path)
diff --git a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
index fab500f47bf..05f4c16ef60 100644
--- a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
+++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe 'Merge request > User toggles whitespace changes', :js do
find('.js-show-diff-settings').click
- expect(find('#show-whitespace')).to be_checked
+ expect(find('#show-whitespace')).not_to be_checked
end
end
end
diff --git a/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb b/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
new file mode 100644
index 00000000000..c254a142349
--- /dev/null
+++ b/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User views diffs file-by-file', :js do
+ let(:merge_request) do
+ create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
+ end
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user, view_diffs_file_by_file: true) }
+
+ before do
+ project.add_developer(user)
+
+ sign_in(user)
+
+ visit(diffs_project_merge_request_path(project, merge_request))
+
+ wait_for_requests
+ end
+
+ it 'shows diffs file-by-file' do
+ page.within('#diffs') do
+ expect(page).to have_selector('.file-holder', count: 1)
+ expect(page).to have_selector('.diff-file .file-title', text: '.DS_Store')
+
+ click_button 'Next'
+
+ expect(page).to have_selector('.file-holder', count: 1)
+ expect(page).to have_selector('.diff-file .file-title', text: '.gitignore')
+ end
+ end
+end
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index 5c29ac870c0..d6f23b21d65 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -36,12 +36,35 @@ RSpec.describe 'Member autocomplete', :js do
let(:noteable) { create(:issue, author: author, project: project) }
before do
+ stub_feature_flags(tribute_autocomplete: false)
visit project_issue_path(project, noteable)
end
include_examples "open suggestions when typing @", 'issue'
end
+ describe 'when tribute_autocomplete feature flag is on' do
+ context 'adding a new note on a Issue' do
+ let(:noteable) { create(:issue, author: author, project: project) }
+
+ before do
+ stub_feature_flags(tribute_autocomplete: true)
+ visit project_issue_path(project, noteable)
+
+ page.within('.new-note') do
+ find('#note-body').send_keys('@')
+ end
+ end
+
+ it 'suggests noteable author and note author' do
+ page.within('.tribute-container', visible: true) do
+ expect(page).to have_content(author.username)
+ expect(page).to have_content(note.author.username)
+ end
+ end
+ end
+ end
+
context 'adding a new note on a Merge Request' do
let(:project) { create(:project, :public, :repository) }
let(:noteable) do
diff --git a/spec/features/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb
index e1117d2d420..817228edca7 100644
--- a/spec/features/profiles/user_edit_preferences_spec.rb
+++ b/spec/features/profiles/user_edit_preferences_spec.rb
@@ -56,4 +56,17 @@ RSpec.describe 'User edit preferences profile' do
expect(page).to have_content('Failed to save preferences')
end
end
+
+ describe 'User language' do
+ let(:user) { create(:user, preferred_language: :es) }
+
+ it 'shows the user preferred language by default' do
+ expect(page).to have_select(
+ 'user[preferred_language]',
+ selected: 'Spanish - español',
+ options: Gitlab::I18n::AVAILABLE_LANGUAGES.values,
+ visible: :all
+ )
+ end
+ end
end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 2659157d61d..697fccaca34 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'User edit profile' do
fill_in 'user_twitter', with: 'testtwitter'
fill_in 'user_website_url', with: 'testurl'
fill_in 'user_location', with: 'Ukraine'
- fill_in 'user_bio', with: 'I <3 GitLab'
+ fill_in 'user_bio', with: 'I <3 GitLab :tada:'
fill_in 'user_job_title', with: 'Frontend Engineer'
fill_in 'user_organization', with: 'GitLab'
submit_settings
@@ -36,7 +36,8 @@ RSpec.describe 'User edit profile' do
linkedin: 'testlinkedin',
twitter: 'testtwitter',
website_url: 'testurl',
- bio: 'I <3 GitLab',
+ bio: 'I <3 GitLab :tada:',
+ bio_html: '<p data-sourcepos="1:1-1:18" dir="auto">I &lt;3 GitLab <gl-emoji title="party popper" data-name="tada" data-unicode-version="6.0">🎉</gl-emoji></p>',
job_title: 'Frontend Engineer',
organization: 'GitLab'
)
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index d9421631b32..2747b5894dc 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe 'User visits the profile preferences page' do
end
describe 'User changes their language', :js do
- it 'creates a flash message', :quarantine do
+ it 'creates a flash message', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31404' do
select2('en', from: '#user_preferred_language')
click_button 'Save'
diff --git a/spec/features/projects/activity/user_sees_design_activity_spec.rb b/spec/features/projects/activity/user_sees_design_activity_spec.rb
new file mode 100644
index 00000000000..27a52b87178
--- /dev/null
+++ b/spec/features/projects/activity/user_sees_design_activity_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Projects > Activity > User sees design Activity', :js do
+ include DesignManagementTestHelpers
+
+ let_it_be(:uploader) { create(:user) }
+ let_it_be(:editor) { create(:user) }
+ let_it_be(:deleter) { create(:user) }
+ let_it_be(:archiver) { create(:user) }
+
+ def design_activity(user, action)
+ [user.name, user.to_reference, action, 'design'].join(' ')
+ end
+
+ shared_examples 'being able to see design activity' do
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:design) { create(:design, issue: issue) }
+
+ before_all do
+ project.add_developer(user) # implicitly adds a project join event.
+ common_attrs = { project: project, design: design }
+ create(:design_event, :created, author: uploader, **common_attrs)
+ create(:design_event, :updated, author: editor, **common_attrs)
+ create(:design_event, :destroyed, author: deleter, **common_attrs)
+ create(:design_event, :archived, author: archiver, **common_attrs)
+ end
+
+ before do
+ enable_design_management
+ sign_in(user)
+ end
+
+ it 'shows the design comment action in the activity page' do
+ visit activity_project_path(project)
+
+ expect(page).to have_content('joined project')
+ expect(page).to have_content(design_activity(uploader, 'uploaded'))
+ expect(page).to have_content(design_activity(editor, 'revised'))
+ expect(page).to have_content(design_activity(deleter, 'deleted'))
+ expect(page).to have_content(design_activity(archiver, 'archived'))
+ end
+
+ it 'allows filtering out the design events', :aggregate_failures do
+ visit activity_project_path(project, event_filter: EventFilter::ISSUE)
+
+ expect(page).not_to have_content(design_activity(uploader, 'uploaded'))
+ expect(page).not_to have_content(design_activity(editor, 'revised'))
+ expect(page).not_to have_content(design_activity(deleter, 'deleted'))
+ expect(page).not_to have_content(design_activity(archiver, 'archived'))
+ end
+
+ it 'allows filtering in the design events', :aggregate_failures do
+ visit activity_project_path(project, event_filter: EventFilter::DESIGNS)
+
+ expect(page).not_to have_content('joined project')
+ expect(page).to have_content(design_activity(uploader, 'uploaded'))
+ expect(page).to have_content(design_activity(editor, 'revised'))
+ expect(page).to have_content(design_activity(deleter, 'deleted'))
+ expect(page).to have_content(design_activity(archiver, 'archived'))
+ end
+ end
+
+ context 'the project is public' do
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'being able to see design activity'
+ end
+
+ context 'the project is private' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user, developer_projects: [project]) }
+
+ it_behaves_like 'being able to see design activity'
+ end
+end
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index cce656a1260..3032d115a00 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -589,7 +589,7 @@ RSpec.describe 'File blob', :js do
aggregate_failures do
# shows that dashboard yaml is invalid
expect(page).to have_content('Metrics Dashboard YAML definition is invalid:')
- expect(page).to have_content("panel_groups: can't be blank")
+ expect(page).to have_content("panel_groups: should be an array of panel_groups objects")
# shows a learn more link
expect(page).to have_link('Learn more')
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index a369bacc4dd..5aca994f53e 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -11,10 +11,6 @@ RSpec.describe 'Editing file blob', :js do
let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] }
let(:readme_file_path) { 'README.md' }
- before do
- stub_feature_flags(web_ide_default: false)
- end
-
context 'as a developer' do
let(:user) { create(:user) }
let(:role) { :developer }
@@ -36,8 +32,7 @@ RSpec.describe 'Editing file blob', :js do
def fill_editor(content: 'class NextFeature\\nend\\n')
wait_for_requests
- find('#editor')
- execute_script("ace.edit('editor').setValue('#{content}')")
+ execute_script("monaco.editor.getModels()[0].setValue('#{content}')")
end
context 'from MR diff' do
@@ -67,6 +62,15 @@ RSpec.describe 'Editing file blob', :js do
expect(find_by_id('file_path').value).to eq('ci/.gitlab-ci.yml')
end
+ it 'updating file path updates syntax highlighting' do
+ visit project_edit_blob_path(project, tree_join(branch, readme_file_path))
+ expect(find('#editor')['data-mode-id']).to eq('markdown')
+
+ find('#file_path').send_keys('foo.txt') do
+ expect(find('#editor')['data-mode-id']).to eq('plaintext')
+ end
+ end
+
context 'from blob file path' do
before do
visit project_blob_path(project, tree_join(branch, file_path))
diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
index d250a01c050..a271a4f43a8 100644
--- a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
+++ b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
@@ -15,8 +15,7 @@ RSpec.describe 'User creates blob in new project', :js do
it 'allows the user to add a new file' do
click_link 'New file'
- find('#editor')
- execute_script('ace.edit("editor").setValue("Hello world")')
+ execute_script("monaco.editor.getModels()[0].setValue('Hello world')")
fill_in(:file_name, with: 'dummy-file')
diff --git a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
index 5270774b541..8b43687c71c 100644
--- a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
+++ b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
@@ -32,6 +32,8 @@ RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled
end
it 'displays suggest_gitlab_ci_yml popover' do
+ page.find(:css, '.gitlab-ci-yml-selector').click
+
popover_selector = '.suggest-gitlab-ci-yml'
expect(page).to have_css(popover_selector, visible: true)
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 3e1006920e7..2e6a366f77a 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -139,6 +139,19 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
end
end
+ context 'when a user adds an existing cluster' do
+ before do
+ visit project_clusters_path(project)
+
+ click_link 'Add Kubernetes cluster'
+ click_link 'Add existing cluster'
+ end
+
+ it 'user sees the "Environment scope" field' do
+ expect(page).to have_css('#cluster_environment_scope')
+ end
+ end
+
context 'when user destroys the cluster' do
before do
click_link 'Advanced Settings'
@@ -155,19 +168,6 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
end
end
- context 'when a user cannot edit the environment scope' do
- before do
- visit project_clusters_path(project)
-
- click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
- end
-
- it 'user does not see the "Environment scope" field' do
- expect(page).not_to have_css('#cluster_environment_scope')
- end
- end
-
context 'when user has not dismissed GCP signup offer' do
before do
visit project_clusters_path(project)
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 1cf214a5c4e..c56a1ed1711 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -25,6 +25,168 @@ RSpec.describe 'Clusters', :js do
end
end
+ context 'when user has a cluster' do
+ before do
+ allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
+ end
+
+ context 'when user adds an existing cluster' do
+ before do
+ create(:cluster, :provided_by_user, name: 'default-cluster', environment_scope: '*', projects: [project])
+ visit project_clusters_path(project)
+ end
+
+ it 'user sees an add cluster button' do
+ expect(page).to have_selector('.js-add-cluster:not(.readonly)')
+ end
+
+ context 'when user filled form with environment scope' do
+ before do
+ click_link 'Add Kubernetes cluster'
+ click_link 'Add existing cluster'
+ fill_in 'cluster_name', with: 'staging-cluster'
+ fill_in 'cluster_environment_scope', with: 'staging/*'
+ click_button 'Add Kubernetes cluster'
+ end
+
+ it 'user sees a cluster details page' do
+ expect(page.find_field('cluster[name]').value).to eq('staging-cluster')
+ expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*')
+ end
+ end
+
+ context 'when user updates environment scope' do
+ before do
+ click_link 'default-cluster'
+ fill_in 'cluster_environment_scope', with: 'production/*'
+ within '.js-cluster-integration-form' do
+ click_button 'Save changes'
+ end
+ end
+
+ it 'updates the environment scope' do
+ expect(page.find_field('cluster[environment_scope]').value).to eq('production/*')
+ end
+ end
+
+ context 'when user updates duplicated environment scope' do
+ before do
+ click_link 'Add Kubernetes cluster'
+ click_link 'Add existing cluster'
+ fill_in 'cluster_name', with: 'staging-cluster'
+ fill_in 'cluster_environment_scope', with: '*'
+ fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'https://0.0.0.0'
+ fill_in 'cluster_platform_kubernetes_attributes_token', with: 'token'
+
+ click_button 'Add Kubernetes cluster'
+ end
+
+ it 'users sees an environment scope validation error' do
+ expect(page).to have_content('cannot add duplicated environment scope')
+ end
+ end
+ end
+
+ context 'when user adds a Google Kubernetes Engine cluster' do
+ before do
+ allow_any_instance_of(Projects::ClustersController)
+ .to receive(:token_in_session).and_return('token')
+ allow_any_instance_of(Projects::ClustersController)
+ .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
+
+ allow_any_instance_of(Projects::ClustersController).to receive(:authorize_google_project_billing)
+ allow_any_instance_of(Projects::ClustersController).to receive(:google_project_billing_status).and_return(true)
+
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_zones_clusters_create) do
+ OpenStruct.new(
+ self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
+ status: 'RUNNING'
+ )
+ end
+
+ allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
+
+ create(:cluster, :provided_by_gcp, name: 'default-cluster', environment_scope: '*', projects: [project])
+ visit project_clusters_path(project)
+ end
+
+ it 'user sees a add cluster button ' do
+ expect(page).to have_selector('.js-add-cluster:not(.readonly)')
+ end
+
+ context 'when user filled form with environment scope' do
+ before do
+ click_link 'Add Kubernetes cluster'
+ click_link 'Create new cluster'
+ click_link 'Google GKE'
+
+ sleep 2 # wait for ajax
+ execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+
+ fill_in 'cluster_name', with: 'staging-cluster'
+ fill_in 'cluster_environment_scope', with: 'staging/*'
+ fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
+ fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
+ fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
+ click_button 'Create Kubernetes cluster'
+
+ # The frontend won't show the details until the cluster is
+ # created, and we don't want to make calls out to GCP.
+ provider = Clusters::Cluster.last.provider
+ provider.make_created
+ end
+
+ it 'user sees a cluster details page' do
+ expect(page).to have_content('GitLab Integration')
+ expect(page.find_field('cluster[environment_scope]').value).to eq('staging/*')
+ end
+ end
+
+ context 'when user updates environment scope' do
+ before do
+ click_link 'default-cluster'
+ fill_in 'cluster_environment_scope', with: 'production/*'
+ within ".js-cluster-integration-form" do
+ click_button 'Save changes'
+ end
+ end
+
+ it 'updates the environment scope' do
+ expect(page.find_field('cluster[environment_scope]').value).to eq('production/*')
+ end
+ end
+
+ context 'when user updates duplicated environment scope' do
+ before do
+ click_link 'Add Kubernetes cluster'
+ click_link 'Create new cluster'
+ click_link 'Google GKE'
+
+ sleep 2 # wait for ajax
+ execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+
+ fill_in 'cluster_name', with: 'staging-cluster'
+ fill_in 'cluster_environment_scope', with: '*'
+ fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
+ fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
+ fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
+ click_button 'Create Kubernetes cluster'
+ end
+
+ it 'users sees an environment scope validation error' do
+ expect(page).to have_content('cannot add duplicated environment scope')
+ end
+ end
+ end
+ end
+
context 'when user has a cluster and visits cluster index page' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 13f73b8cf44..f97abc5bd8b 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -6,23 +6,32 @@ RSpec.describe 'project commit pipelines', :js do
let(:project) { create(:project, :repository) }
before do
+ create(:ci_pipeline, project: project,
+ sha: project.commit.sha,
+ ref: 'master')
+
user = create(:user)
project.add_maintainer(user)
sign_in(user)
+
+ visit pipelines_project_commit_path(project, project.commit.sha)
end
context 'when no builds triggered yet' do
- before do
- create(:ci_pipeline, project: project,
- sha: project.commit.sha,
- ref: 'master')
+ it 'shows the ID of the first pipeline' do
+ page.within('.table-holder') do
+ expect(page).to have_content project.ci_pipelines[0].id # pipeline ids
+ end
end
+ end
- it 'user views commit pipelines page' do
- visit pipelines_project_commit_path(project, project.commit.sha)
+ context 'with no related merge requests' do
+ it 'shows the correct text for no related MRs' do
+ wait_for_requests
- page.within('.table-holder') do
- expect(page).to have_content project.ci_pipelines[0].id # pipeline ids
+ page.within('.merge-request-info') do
+ expect(page).not_to have_selector '.spinner'
+ expect(page).to have_content 'No related merge requests found'
end
end
end
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 2796156bfbf..dee964005a4 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -137,6 +137,33 @@ RSpec.describe 'User browses commits' do
.and have_selector('entry summary', text: commit.description[0..10].delete("\r\n"))
end
+ context "when commit has a filename with pathspec characters" do
+ let(:path) { ':wq' }
+ let(:filename) { File.join(path, 'test.txt') }
+ let(:ref) { project.repository.root_ref }
+ let(:newrev) { project.repository.commit('master').sha }
+ let(:short_newrev) { project.repository.commit('master').short_id }
+ let(:message) { 'Glob characters'}
+
+ before do
+ create_file_in_repo(project, ref, ref, filename, 'Test file', commit_message: message)
+ visit project_commits_path(project, "#{ref}/#{path}", limit: 1)
+ wait_for_requests
+ end
+
+ it 'searches commit', :js do
+ expect(page).to have_content(message)
+
+ fill_in 'commits-search', with: 'bogus12345'
+
+ expect(page).to have_content "Your search didn't match any commits"
+
+ fill_in 'commits-search', with: 'Glob'
+
+ expect(page).to have_content message
+ end
+ end
+
context 'when a commit links to a confidential issue' do
let(:confidential_issue) { create(:issue, confidential: true, title: 'Secret issue!', project: project) }
diff --git a/spec/features/projects/confluence/user_views_confluence_page_spec.rb b/spec/features/projects/confluence/user_views_confluence_page_spec.rb
new file mode 100644
index 00000000000..d39c97291db
--- /dev/null
+++ b/spec/features/projects/confluence/user_views_confluence_page_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User views the Confluence page' do
+ let_it_be(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'shows the page when the Confluence integration is enabled' do
+ service = create(:confluence_service, project: project)
+
+ visit project_wikis_confluence_path(project)
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_link('Go to Confluence', href: service.confluence_url)
+ end
+
+ it 'does not show the page when the Confluence integration disabled' do
+ visit project_wikis_confluence_path(project)
+
+ expect(page).to have_gitlab_http_status(:not_found)
+ end
+end
diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb
index f561149d2ad..7514a26f020 100644
--- a/spec/features/projects/container_registry_spec.rb
+++ b/spec/features/projects/container_registry_spec.rb
@@ -84,7 +84,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="singleDeleteButton"]').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
end
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index a3b979d0f42..c72f88205b5 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -28,8 +28,9 @@ RSpec.describe 'Environment > Metrics' do
shared_examples 'has environment selector' do
it 'has a working environment selector', :js do
click_link('See metrics')
-
- expect(page).to have_metrics_path(environment)
+ # TODO: See metrics on the sidebar still points to the old metrics URL
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/229277
+ expect(page).to have_current_path(metrics_project_environment_path(project, id: environment.id))
expect(page).to have_css('[data-qa-selector="environments_dropdown"]')
within('[data-qa-selector="environments_dropdown"]') do
@@ -40,7 +41,7 @@ RSpec.describe 'Environment > Metrics' do
click_on(staging.name)
end
- expect(page).to have_metrics_path(staging)
+ expect(page).to have_current_path(project_metrics_dashboard_path(project, environment: staging.id))
wait_for_requests
end
@@ -67,8 +68,4 @@ RSpec.describe 'Environment > Metrics' do
def visit_environment(environment)
visit project_environment_path(environment.project, environment)
end
-
- def have_metrics_path(environment)
- have_current_path(metrics_project_environment_path(project, id: environment.id))
- end
end
diff --git a/spec/features/projects/environments_pod_logs_spec.rb b/spec/features/projects/environments_pod_logs_spec.rb
index 82dafbc6237..c491cd62d85 100644
--- a/spec/features/projects/environments_pod_logs_spec.rb
+++ b/spec/features/projects/environments_pod_logs_spec.rb
@@ -12,11 +12,12 @@ RSpec.describe 'Environment > Pod Logs', :js, :kubeclient do
let(:service) { create(:cluster_platform_kubernetes, :configured) }
before do
- create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project])
+ cluster = create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project])
create(:deployment, :success, environment: environment)
stub_kubeclient_pods(environment.deployment_namespace)
stub_kubeclient_logs(pod_name, environment.deployment_namespace, container: 'container-0')
+ stub_kubeclient_nodes_and_nodes_metrics(cluster.platform.api_url)
sign_in(project.owner)
end
@@ -37,7 +38,7 @@ RSpec.describe 'Environment > Pod Logs', :js, :kubeclient do
dropdown_items = find(".dropdown-menu").all(".dropdown-item")
expect(dropdown_items.first).to have_content(environment.name)
- expect(dropdown_items.size).to eq(2)
+ expect(dropdown_items.size).to eq(3)
end
end
diff --git a/spec/features/projects/files/edit_file_soft_wrap_spec.rb b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
index ede22204dbd..fda024e893d 100644
--- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb
+++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
@@ -8,8 +8,9 @@ RSpec.describe 'Projects > Files > User uses soft wrap while editing file', :js
user = project.owner
sign_in user
visit project_new_blob_path(project, 'master', file_name: 'test_file-name')
+
page.within('.file-editor.code') do
- find('.ace_text-input', visible: false).send_keys 'Touch water with paw then recoil in horror chase dog then
+ find('.inputarea', visible: false).send_keys 'Touch water with paw then recoil in horror chase dog then
run away chase the pig around the house eat owner\'s food, and knock
dish off table head butt cant eat out of my own dish. Cat is love, cat
is life rub face on everything poop on grasses so meow. Playing with
@@ -26,17 +27,20 @@ RSpec.describe 'Projects > Files > User uses soft wrap while editing file', :js
it 'user clicks the "Soft wrap" button and then "No wrap" button' do
wrapped_content_width = get_content_width
- toggle_button.click
- expect(toggle_button).to have_content 'No wrap'
- unwrapped_content_width = get_content_width
- expect(unwrapped_content_width).to be < wrapped_content_width
-
- toggle_button.click
- expect(toggle_button).to have_content 'Soft wrap'
- expect(get_content_width).to be > unwrapped_content_width
+
+ toggle_button.click do
+ expect(toggle_button).to have_content 'Soft wrap'
+ unwrapped_content_width = get_content_width
+ expect(unwrapped_content_width).to be > wrapped_content_width
+ end
+
+ toggle_button.click do
+ expect(toggle_button).to have_content 'No wrap'
+ expect(get_content_width).to be < unwrapped_content_width
+ end
end
def get_content_width
- find('.ace_content')[:style].slice!(/width: \d+/).slice!(/\d+/).to_i
+ find('.view-lines', visible: false)[:style].slice!(/width: \d+/).slice!(/\d+/).to_i
end
end
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index dae1164f7f2..5a39f2bcd98 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -25,6 +25,6 @@ RSpec.describe 'Projects > Files > User wants to add a .gitignore file' do
expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Apply a template')
expect(page).to have_content('/.bundle')
- expect(page).to have_content('# Gemfile.lock, .ruby-version, .ruby-gemset')
+ expect(page).to have_content('config/initializers/secret_token.rb')
end
end
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 44b5833a8c8..e5259bd88be 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -3,6 +3,8 @@
require "spec_helper"
RSpec.describe "User browses files" do
+ include RepoHelpers
+
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
@@ -339,6 +341,24 @@ RSpec.describe "User browses files" do
end
end
+ context "when browsing a file with pathspec characters" do
+ let(:filename) { ':wq' }
+ let(:newrev) { project.repository.commit('master').sha }
+
+ before do
+ create_file_in_repo(project, 'master', 'master', filename, 'Test file')
+ path = File.join('master', filename)
+
+ visit(project_blob_path(project, path))
+ end
+
+ it "shows a raw file content" do
+ click_link("Open raw")
+
+ expect(source).to eq("") # Body is filled in by gitlab-workhorse
+ end
+ end
+
context "when browsing a raw file" do
before do
path = File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path)
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index cc90f0cf294..5abc048c135 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -14,8 +14,6 @@ RSpec.describe 'Projects > Files > User creates files', :js do
let(:user) { create(:user) }
before do
- stub_feature_flags(web_ide_default: false)
-
project.add_maintainer(user)
sign_in(user)
end
@@ -67,7 +65,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
file_name = find('#file_name')
file_name.set options[:file_name] || 'README.md'
- find('.ace_text-input', visible: false).send_keys.native.send_keys options[:file_content] || 'Some content'
+ find('.monaco-editor textarea').send_keys.native.send_keys options[:file_content] || 'Some content'
click_button 'Commit changes'
end
@@ -89,7 +87,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
it 'creates and commit a new file' do
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:file_name, with: 'not_a_file.md')
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -105,7 +103,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
it 'creates and commit a new file with new lines at the end of file' do
find('#editor')
- execute_script('ace.edit("editor").setValue("Sample\n\n\n")')
+ execute_script('monaco.editor.getModels()[0].setValue("Sample\n\n\n")')
fill_in(:file_name, with: 'not_a_file.md')
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -117,7 +115,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
find('.js-edit-blob').click
find('#editor')
- expect(evaluate_script('ace.edit("editor").getValue()')).to eq("Sample\n\n\n")
+ expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq("Sample\n\n\n")
end
it 'creates and commit a new file with a directory name' do
@@ -126,7 +124,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
expect(page).to have_selector('.file-editor')
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -141,7 +139,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
expect(page).to have_selector('.file-editor')
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:file_name, with: 'not_a_file.md')
fill_in(:commit_message, with: 'New commit message', visible: true)
fill_in(:branch_name, with: 'new_branch_name', visible: true)
@@ -176,7 +174,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
expect(page).to have_selector('.file-editor')
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:file_name, with: 'not_a_file.md')
fill_in(:commit_message, with: 'New commit message', visible: true)
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index 1bb931e35ec..d3e075001c8 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -11,8 +11,6 @@ RSpec.describe 'Projects > Files > User edits files', :js do
let(:user) { create(:user) }
before do
- stub_feature_flags(web_ide_default: false)
-
sign_in(user)
end
@@ -46,9 +44,9 @@ RSpec.describe 'Projects > Files > User edits files', :js do
find('.file-editor', match: :first)
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
- expect(evaluate_script('ace.edit("editor").getValue()')).to eq('*.rbca')
+ expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq('*.rbca')
end
it 'does not show the edit link if a file is binary' do
@@ -67,7 +65,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do
find('.file-editor', match: :first)
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -85,7 +83,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do
find('.file-editor', match: :first)
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:commit_message, with: 'New commit message', visible: true)
fill_in(:branch_name, with: 'new_branch_name', visible: true)
click_button('Commit changes')
@@ -103,7 +101,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do
find('.file-editor', match: :first)
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
click_link('Preview changes')
expect(page).to have_css('.line_holder.new')
@@ -148,9 +146,9 @@ RSpec.describe 'Projects > Files > User edits files', :js do
find('.file-editor', match: :first)
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
- expect(evaluate_script('ace.edit("editor").getValue()')).to eq('*.rbca')
+ expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq('*.rbca')
end
it 'opens the Web IDE in a forked project', :sidekiq_might_not_need_inline do
@@ -178,7 +176,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do
find('.file-editor', match: :first)
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -207,7 +205,7 @@ RSpec.describe 'Projects > Files > User edits files', :js do
expect(page).not_to have_button('Cancel')
find('#editor')
- execute_script("ace.edit('editor').setValue('*.rbca')")
+ execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
fill_in(:commit_message, with: 'Another commit', visible: true)
click_button('Commit changes')
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index 730e586b278..b93da033aea 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb b/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb
index f871ca60596..aff8951d9de 100644
--- a/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_paginates_designs_spec.rb
@@ -8,33 +8,57 @@ RSpec.describe 'User paginates issue designs', :js do
let(:project) { create(:project_empty_repo, :public) }
let(:issue) { create(:issue, project: project) }
- before do
- enable_design_management
+ context 'design_management_moved flag disabled' do
+ before do
+ stub_feature_flags(design_management_moved: false)
+ enable_design_management
- create_list(:design, 2, :with_file, issue: issue)
+ create_list(:design, 2, :with_file, issue: issue)
+ visit project_issue_path(project, issue)
+ click_link 'Designs'
+ wait_for_requests
+ find('.js-design-list-item', match: :first).click
+ end
- visit project_issue_path(project, issue)
+ it 'paginates to next design' do
+ expect(find('.js-previous-design')[:disabled]).to eq('true')
- click_link 'Designs'
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content('1 of 2')
+ end
- wait_for_requests
+ find('.js-next-design').click
- find('.js-design-list-item', match: :first).click
- end
+ expect(find('.js-previous-design')[:disabled]).not_to eq('true')
- it 'paginates to next design' do
- expect(find('.js-previous-design')[:disabled]).to eq('true')
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content('2 of 2')
+ end
+ end
+ end
- page.within(find('.js-design-header')) do
- expect(page).to have_content('1 of 2')
+ context 'design_management_moved flag enabled' do
+ before do
+ enable_design_management
+ create_list(:design, 2, :with_file, issue: issue)
+ visit project_issue_path(project, issue)
+ find('.js-design-list-item', match: :first).click
end
- find('.js-next-design').click
+ it 'paginates to next design' do
+ expect(find('.js-previous-design')[:disabled]).to eq('true')
+
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content('1 of 2')
+ end
+
+ find('.js-next-design').click
- expect(find('.js-previous-design')[:disabled]).not_to eq('true')
+ expect(find('.js-previous-design')[:disabled]).not_to eq('true')
- page.within(find('.js-design-header')) do
- expect(page).to have_content('2 of 2')
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content('2 of 2')
+ end
end
end
end
diff --git a/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb b/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb
index 902a84afc83..4e45312eac3 100644
--- a/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb
+++ b/spec/features/projects/issues/design_management/user_permissions_upload_spec.rb
@@ -8,17 +8,32 @@ RSpec.describe 'User design permissions', :js do
let(:project) { create(:project_empty_repo, :public) }
let(:issue) { create(:issue, project: project) }
- before do
- enable_design_management
+ context 'design_management_moved flag disabled' do
+ before do
+ enable_design_management
+ stub_feature_flags(design_management_moved: false)
- visit project_issue_path(project, issue)
+ visit project_issue_path(project, issue)
- click_link 'Designs'
+ click_link 'Designs'
- wait_for_requests
+ wait_for_requests
+ end
+
+ it 'user does not have permissions to upload design' do
+ expect(page).not_to have_field('design_file')
+ end
end
- it 'user does not have permissions to upload design' do
- expect(page).not_to have_field('design_file')
+ context 'design_management_moved flag enabled' do
+ before do
+ enable_design_management
+
+ visit project_issue_path(project, issue)
+ end
+
+ it 'user does not have permissions to upload design' do
+ expect(page).not_to have_field('design_file')
+ end
end
end
diff --git a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
index 66b449a9de5..2381e00972f 100644
--- a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb
@@ -13,10 +13,10 @@ RSpec.describe 'User uploads new design', :js do
sign_in(user)
end
- context "when the feature is available" do
+ context 'design_management_moved flag disabled' do
before do
- enable_design_management
-
+ enable_design_management(feature_enabled)
+ stub_feature_flags(design_management_moved: false)
visit project_issue_path(project, issue)
click_link 'Designs'
@@ -24,32 +24,64 @@ RSpec.describe 'User uploads new design', :js do
wait_for_requests
end
- it 'uploads designs' do
- attach_file(:design_file, logo_fixture, make_visible: true)
+ context "when the feature is available" do
+ let(:feature_enabled) { true }
+
+ it 'uploads designs' do
+ attach_file(:design_file, logo_fixture, make_visible: true)
+
+ expect(page).to have_selector('.js-design-list-item', count: 1)
+
+ within first('#designs-tab .js-design-list-item') do
+ expect(page).to have_content('dk.png')
+ end
- expect(page).to have_selector('.js-design-list-item', count: 1)
+ attach_file(:design_file, gif_fixture, make_visible: true)
- within first('#designs-tab .js-design-list-item') do
- expect(page).to have_content('dk.png')
+ expect(page).to have_selector('.js-design-list-item', count: 2)
end
+ end
- attach_file(:design_file, gif_fixture, make_visible: true)
+ context 'when the feature is not available' do
+ let(:feature_enabled) { false }
- expect(page).to have_selector('.js-design-list-item', count: 2)
+ it 'shows the message about requirements' do
+ expect(page).to have_content("To enable design management, you'll need to meet the requirements.")
+ end
end
end
- context 'when the feature is not available' do
+ context 'design_management_moved flag enabled' do
before do
+ enable_design_management(feature_enabled)
+ stub_feature_flags(design_management_moved: true)
visit project_issue_path(project, issue)
+ end
- click_link 'Designs'
+ context "when the feature is available" do
+ let(:feature_enabled) { true }
- wait_for_requests
+ it 'uploads designs', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/225616' do
+ attach_file(:design_file, logo_fixture, make_visible: true)
+
+ expect(page).to have_selector('.js-design-list-item', count: 1)
+
+ within first('[data-testid="designs-root"] .js-design-list-item') do
+ expect(page).to have_content('dk.png')
+ end
+
+ attach_file(:design_file, gif_fixture, make_visible: true)
+
+ expect(page).to have_selector('.js-design-list-item', count: 2)
+ end
end
- it 'shows the message about requirements' do
- expect(page).to have_content("To enable design management, you'll need to meet the requirements.")
+ context 'when the feature is not available' do
+ let(:feature_enabled) { false }
+
+ it 'shows the message about requirements' do
+ expect(page).to have_content("To enable design management, you'll need to meet the requirements.")
+ end
end
end
diff --git a/spec/features/projects/issues/design_management/user_views_design_spec.rb b/spec/features/projects/issues/design_management/user_views_design_spec.rb
index 527442d5339..49245218e81 100644
--- a/spec/features/projects/issues/design_management/user_views_design_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_design_spec.rb
@@ -9,21 +9,42 @@ RSpec.describe 'User views issue designs', :js do
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:design) { create(:design, :with_file, issue: issue) }
- before do
- enable_design_management
+ context 'design_management_moved flag disabled' do
+ before do
+ enable_design_management
+ stub_feature_flags(design_management_moved: false)
- visit project_issue_path(project, issue)
+ visit project_issue_path(project, issue)
- click_link 'Designs'
+ click_link 'Designs'
+ end
+
+ it 'opens design detail' do
+ click_link design.filename
+
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content(design.filename)
+ end
+
+ expect(page).to have_selector('.js-design-image')
+ end
end
- it 'opens design detail' do
- click_link design.filename
+ context 'design_management_moved flag enabled' do
+ before do
+ enable_design_management
- page.within(find('.js-design-header')) do
- expect(page).to have_content(design.filename)
+ visit project_issue_path(project, issue)
end
- expect(page).to have_selector('.js-design-image')
+ it 'opens design detail' do
+ click_link design.filename
+
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content(design.filename)
+ end
+
+ expect(page).to have_selector('.js-design-image')
+ end
end
end
diff --git a/spec/features/projects/issues/design_management/user_views_designs_spec.rb b/spec/features/projects/issues/design_management/user_views_designs_spec.rb
index d371ae1aad7..772a9ffbe6f 100644
--- a/spec/features/projects/issues/design_management/user_views_designs_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_designs_spec.rb
@@ -9,39 +9,78 @@ RSpec.describe 'User views issue designs', :js do
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:design) { create(:design, :with_file, issue: issue) }
- before do
- enable_design_management
- end
-
- context 'navigates from the issue view' do
+ context 'design_management_moved flag disabled' do
before do
- visit project_issue_path(project, issue)
- click_link 'Designs'
- wait_for_requests
+ enable_design_management
+ stub_feature_flags(design_management_moved: false)
end
- it 'fetches list of designs' do
- expect(page).to have_selector('.js-design-list-item', count: 1)
+ context 'navigates from the issue view' do
+ before do
+ visit project_issue_path(project, issue)
+ click_link 'Designs'
+ wait_for_requests
+ end
+
+ it 'fetches list of designs' do
+ expect(page).to have_selector('.js-design-list-item', count: 1)
+ end
end
- end
- context 'navigates directly to the design collection view' do
- before do
- visit designs_project_issue_path(project, issue)
+ context 'navigates directly to the design collection view' do
+ before do
+ visit designs_project_issue_path(project, issue)
+ end
+
+ it 'expands the sidebar' do
+ expect(page).to have_selector('.layout-page.right-sidebar-expanded')
+ end
end
- it 'expands the sidebar' do
- expect(page).to have_selector('.layout-page.right-sidebar-expanded')
+ context 'navigates directly to the individual design view' do
+ before do
+ visit designs_project_issue_path(project, issue, vueroute: design.filename)
+ end
+
+ it 'sees the design' do
+ expect(page).to have_selector('.js-design-detail')
+ end
end
end
- context 'navigates directly to the individual design view' do
+ context 'design_management_moved flag enabled' do
before do
- visit designs_project_issue_path(project, issue, vueroute: design.filename)
+ enable_design_management
end
- it 'sees the design' do
- expect(page).to have_selector('.js-design-detail')
+ context 'navigates from the issue view' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'fetches list of designs' do
+ expect(page).to have_selector('.js-design-list-item', count: 1)
+ end
+ end
+
+ context 'navigates directly to the design collection view' do
+ before do
+ visit designs_project_issue_path(project, issue)
+ end
+
+ it 'expands the sidebar' do
+ expect(page).to have_selector('.layout-page.right-sidebar-expanded')
+ end
+ end
+
+ context 'navigates directly to the individual design view' do
+ before do
+ visit designs_project_issue_path(project, issue, vueroute: design.filename)
+ end
+
+ it 'sees the design' do
+ expect(page).to have_selector('.js-design-detail')
+ end
end
end
end
diff --git a/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb b/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb
index 5bc1271309c..0fe84ab47ed 100644
--- a/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb
@@ -29,6 +29,7 @@ RSpec.describe 'User views an SVG design that contains XSS', :js do
end
it 'displays the SVG' do
+ find("[data-testid='close-design']").click
expect(page).to have_selector("img.design-img[alt='xss.svg']", count: 1, visible: false)
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index e78e8989575..62e8997f6cb 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -837,7 +837,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
it 'renders empty state' do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
- expect(page).not_to have_selector('.js-build-trace')
+ expect(page).not_to have_selector('.job-log')
expect(page).to have_content('This job has been canceled')
end
end
@@ -852,7 +852,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
it 'renders empty state' do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
- expect(page).not_to have_selector('.js-build-trace')
+ expect(page).not_to have_selector('.job-log')
expect(page).to have_content('This job has been skipped')
end
end
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
index 66d61e629df..85a08c441ca 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'Issue prioritization' do
let(:label_5) { create(:label, title: 'label_5', project: project) } # no priority
# According to https://gitlab.com/gitlab-org/gitlab-foss/issues/14189#note_4360653
- context 'when issues have one label' do
+ context 'when issues have one label', :js do
it 'Are sorted properly' do
# Issues
issue_1 = create(:issue, title: 'issue_1', project: project)
@@ -44,7 +44,7 @@ RSpec.describe 'Issue prioritization' do
end
end
- context 'when issues have multiple labels' do
+ context 'when issues have multiple labels', :js do
it 'Are sorted properly' do
# Issues
issue_1 = create(:issue, title: 'issue_1', project: project)
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index f51ebde8f80..56b807e08d7 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -64,9 +64,12 @@ RSpec.describe 'Project members list' do
visit_members_page
- accept_confirm do
- find(:css, 'li.project_member', text: other_user.name).find(:css, 'a.btn-remove').click
- end
+ # Open modal
+ find(:css, 'li.project_member', text: other_user.name).find(:css, 'button.btn-remove').click
+
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
wait_for_requests
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
index aa7633c3b28..c4bd0b81dc0 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'Projects > Members > Member leaves project' do
expect(project.users.exists?(user.id)).to be_falsey
end
- it 'user leaves project by url param', :js, :quarantine do
+ it 'user leaves project by url param', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/35925' do
visit project_path(project, leave: 1)
page.accept_confirm
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 94d79d60aeb..22cd832ff06 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -12,8 +12,6 @@ RSpec.describe 'Project navbar' do
let_it_be(:project) { create(:project, :repository) }
before do
- stub_licensed_features(service_desk: false)
-
project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 921bbbfbe7d..1e2cd3c0a69 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -3,8 +3,6 @@
require 'spec_helper'
RSpec.describe 'Pipeline Schedules', :js do
- include PipelineSchedulesHelper
-
let!(:project) { create(:project, :repository) }
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
let!(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index c6a002ad18b..2ca584ab8f6 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe 'Pipeline', :js do
end
context 'when there is one related merge request' do
- before do
+ let!(:merge_request) do
create(:merge_request,
source_project: project,
source_branch: pipeline.ref)
@@ -123,7 +123,7 @@ RSpec.describe 'Pipeline', :js do
within '.related-merge-requests' do
expect(page).to have_content('1 related merge request: ')
expect(page).to have_selector('.js-truncated-mr-list')
- expect(page).to have_link('!1 My title 1')
+ expect(page).to have_link("#{merge_request.to_reference} #{merge_request.title}")
expect(page).not_to have_selector('.js-full-mr-list')
expect(page).not_to have_selector('.text-expander')
@@ -132,9 +132,16 @@ RSpec.describe 'Pipeline', :js do
end
context 'when there are two related merge requests' do
- before do
- create(:merge_request, source_project: project, source_branch: pipeline.ref)
- create(:merge_request, source_project: project, source_branch: pipeline.ref, target_branch: 'fix')
+ let!(:merge_request1) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: pipeline.ref)
+ end
+ let!(:merge_request2) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: pipeline.ref,
+ target_branch: 'fix')
end
it 'links to the most recent related merge request' do
@@ -142,7 +149,7 @@ RSpec.describe 'Pipeline', :js do
within '.related-merge-requests' do
expect(page).to have_content('2 related merge requests: ')
- expect(page).to have_link('!2 My title 3')
+ expect(page).to have_link("#{merge_request2.to_reference} #{merge_request2.title}")
expect(page).to have_selector('.text-expander')
expect(page).to have_selector('.js-full-mr-list', visible: false)
end
@@ -354,37 +361,68 @@ RSpec.describe 'Pipeline', :js do
end
describe 'test tabs' do
- let(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :with_test_reports, :with_report_results, project: project) }
- before do
- visit_pipeline
- wait_for_requests
- end
+ context 'with build_report_summary feature flag disabled' do
+ before do
+ stub_feature_flags(build_report_summary: false)
+ visit_pipeline
+ wait_for_requests
+ end
+
+ context 'with test reports' do
+ it 'shows badge counter in Tests tab' do
+ expect(pipeline.test_reports.total_count).to eq(4)
+ expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_reports.total_count.to_s)
+ end
- context 'with test reports' do
- it 'shows badge counter in Tests tab' do
- expect(pipeline.test_reports.total_count).to eq(4)
- expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_reports.total_count.to_s)
+ it 'does not call test_report.json endpoint by default', :js do
+ expect(page).to have_selector('.js-no-tests-to-show', visible: :all)
+ end
+
+ it 'does call test_report.json endpoint when tab is selected', :js do
+ find('.js-tests-tab-link').click
+ wait_for_requests
+
+ expect(page).to have_content('Jobs')
+ expect(page).to have_selector('.js-tests-detail', visible: :all)
+ end
end
- it 'does not call test_report.json endpoint by default', :js do
- expect(page).to have_selector('.js-no-tests-to-show', visible: :all)
+ context 'without test reports' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ it 'shows zero' do
+ expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("0")
+ end
end
+ end
- it 'does call test_report.json endpoint when tab is selected', :js do
- find('.js-tests-tab-link').click
+ context 'with build_report_summary feature flag enabled' do
+ before do
+ visit_pipeline
wait_for_requests
+ end
+
+ context 'with test reports' do
+ it 'shows badge counter in Tests tab' do
+ expect(page.find('.js-test-report-badge-counter').text).to eq(pipeline.test_report_summary.total_count.to_s)
+ end
- expect(page).to have_content('Test suites')
- expect(page).to have_selector('.js-tests-detail', visible: :all)
+ it 'calls summary.json endpoint', :js do
+ find('.js-tests-tab-link').click
+
+ expect(page).to have_content('Jobs')
+ expect(page).to have_selector('.js-tests-detail', visible: :all)
+ end
end
- end
- context 'without test reports' do
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ context 'without test reports' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
- it 'shows nothing' do
- expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("")
+ it 'shows zero' do
+ expect(page.find('.js-test-report-badge-counter', visible: :all).text).to eq("0")
+ end
end
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 0e33204f851..0eb92f3e679 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -65,19 +65,8 @@ RSpec.describe 'Pipelines', :js do
expect(page.find('.js-pipelines-tab-all .badge').text).to include('1')
end
- it 'shows a tab for Pending pipelines and count' do
- expect(page.find('.js-pipelines-tab-pending').text).to include('Pending')
- expect(page.find('.js-pipelines-tab-pending .badge').text).to include('0')
- end
-
- it 'shows a tab for Running pipelines and count' do
- expect(page.find('.js-pipelines-tab-running').text).to include('Running')
- expect(page.find('.js-pipelines-tab-running .badge').text).to include('1')
- end
-
it 'shows a tab for Finished pipelines and count' do
expect(page.find('.js-pipelines-tab-finished').text).to include('Finished')
- expect(page.find('.js-pipelines-tab-finished .badge').text).to include('0')
end
it 'shows a tab for Branches' do
@@ -89,9 +78,9 @@ RSpec.describe 'Pipelines', :js do
end
it 'updates content when tab is clicked' do
- page.find('.js-pipelines-tab-pending').click
+ page.find('.js-pipelines-tab-finished').click
wait_for_requests
- expect(page).to have_content('There are currently no pending pipelines.')
+ expect(page).to have_content('There are currently no finished pipelines.')
end
end
@@ -539,7 +528,7 @@ RSpec.describe 'Pipelines', :js do
end
it 'renders a mini pipeline graph' do
- expect(page).to have_selector('.js-mini-pipeline-graph')
+ expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]')
expect(page).to have_selector('.js-builds-dropdown-button')
end
diff --git a/spec/features/projects/services/disable_triggers_spec.rb b/spec/features/projects/services/disable_triggers_spec.rb
index 8f87d0e7ff1..b3a3d7f0622 100644
--- a/spec/features/projects/services/disable_triggers_spec.rb
+++ b/spec/features/projects/services/disable_triggers_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Disable individual triggers', :js do
include_context 'project service activation'
- let(:checkbox_selector) { 'input[type=checkbox][name$="_events]"]' }
+ let(:checkbox_selector) { 'input[name$="_events]"]' }
before do
visit_project_integration(service_name)
@@ -18,7 +18,7 @@ RSpec.describe 'Disable individual triggers', :js do
event_count = HipchatService.supported_events.count
expect(page).to have_content "Trigger"
- expect(page).to have_css(checkbox_selector, count: event_count)
+ expect(page).to have_css(checkbox_selector, visible: :all, count: event_count)
end
end
@@ -27,7 +27,7 @@ RSpec.describe 'Disable individual triggers', :js do
it "doesn't show unnecessary Trigger checkboxes" do
expect(page).not_to have_content "Trigger"
- expect(page).not_to have_css(checkbox_selector)
+ expect(page).not_to have_css(checkbox_selector, visible: :all)
end
end
end
diff --git a/spec/features/projects/services/user_activates_alerts_spec.rb b/spec/features/projects/services/user_activates_alerts_spec.rb
index 95642f49d61..8b0acdf3618 100644
--- a/spec/features/projects/services/user_activates_alerts_spec.rb
+++ b/spec/features/projects/services/user_activates_alerts_spec.rb
@@ -15,35 +15,32 @@ RSpec.describe 'User activates Alerts', :js do
end
context 'when service is deactivated' do
- it 'activates service' do
+ it 'user cannot activate service' do
visit_project_services
expect(page).to have_link(service_title)
click_link(service_title)
+ expect(page).to have_callout_message
expect(page).not_to have_active_service
-
- click_activate_service
- wait_for_requests
-
- expect(page).to have_active_service
+ expect(page).to have_toggle_active_disabled
end
end
context 'when service is activated' do
+ let_it_be(:activated_alerts_service) do
+ create(:alerts_service, :active, project: project)
+ end
+
before do
visit_alerts_service
- click_activate_service
end
- it 're-generates key' do
- expect(reset_key.value).to be_blank
-
- click_reset_key
- click_confirm_reset_key
- wait_for_requests
-
- expect(reset_key.value).to be_present
+ it 'user cannot change settings' do
+ expect(page).to have_callout_message
+ expect(page).to have_active_service
+ expect(page).to have_toggle_active_disabled
+ expect(page).to have_button_reset_key_disabled
end
end
@@ -57,25 +54,21 @@ RSpec.describe 'User activates Alerts', :js do
visit(edit_project_service_path(project, service_name))
end
- def click_activate_service
- find('#activated').click
- end
-
- def click_reset_key
- click_button('Reset key')
+ def have_callout_message
+ within('.gl-alert') do
+ have_content('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page.')
+ end
end
- def click_confirm_reset_key
- within '.modal-content' do
- click_reset_key
- end
+ def have_active_service
+ have_selector('.js-service-active-status[data-value="true"]')
end
- def reset_key
- find_field('Authorization key')
+ def have_toggle_active_disabled
+ have_selector('#activated .project-feature-toggle.is-disabled')
end
- def have_active_service
- have_selector('.js-service-active-status[data-value="true"]')
+ def have_button_reset_key_disabled
+ have_button('Reset key', disabled: true)
end
end
diff --git a/spec/features/projects/services/user_activates_issue_tracker_spec.rb b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
index a2a2604c610..4f25794d058 100644
--- a/spec/features/projects/services/user_activates_issue_tracker_spec.rb
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'User activates issue tracker', :js do
it 'activates the service' do
expect(page).to have_content("#{tracker} activated.")
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
it 'shows the link in the menu' do
@@ -50,7 +50,7 @@ RSpec.describe 'User activates issue tracker', :js do
click_test_then_save_integration
expect(page).to have_content("#{tracker} activated.")
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
end
end
@@ -65,7 +65,7 @@ RSpec.describe 'User activates issue tracker', :js do
it 'saves but does not activate the service' do
expect(page).to have_content("#{tracker} settings saved, but not activated.")
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
it 'does not show the external tracker link in the menu' do
diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb
index 1da8a49699b..483671c4b5b 100644
--- a/spec/features/projects/services/user_activates_jira_spec.rb
+++ b/spec/features/projects/services/user_activates_jira_spec.rb
@@ -4,18 +4,7 @@ require 'spec_helper'
RSpec.describe 'User activates Jira', :js do
include_context 'project service activation'
-
- let(:url) { 'http://jira.example.com' }
- let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' }
-
- def fill_form(disable: false)
- click_active_toggle if disable
-
- fill_in 'service_url', with: url
- fill_in 'service_username', with: 'username'
- fill_in 'service_password', with: 'password'
- fill_in 'service_jira_issue_transition_id', with: '25'
- end
+ include_context 'project service Jira context'
describe 'user sets and activates Jira Service' do
context 'when Jira connection test succeeds' do
@@ -30,12 +19,17 @@ RSpec.describe 'User activates Jira', :js do
it 'activates the Jira service' do
expect(page).to have_content('Jira activated.')
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :jira))
end
- it 'shows the Jira link in the menu' do
- page.within('.nav-sidebar') do
- expect(page).to have_link('Jira', href: url)
+ unless Gitlab.ee?
+ it 'adds Jira link to sidebar menu' do
+ page.within('.nav-sidebar') do
+ expect(page).not_to have_link('Jira Issues')
+ expect(page).not_to have_link('Issue List', visible: false)
+ expect(page).not_to have_link('Open Jira', href: url, visible: false)
+ expect(page).to have_link('Jira', href: url)
+ end
end
end
end
@@ -61,7 +55,7 @@ RSpec.describe 'User activates Jira', :js do
click_test_then_save_integration
expect(page).to have_content('Jira activated.')
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :jira))
end
end
end
@@ -75,7 +69,7 @@ RSpec.describe 'User activates Jira', :js do
it 'saves but does not activate the Jira service' do
expect(page).to have_content('Jira settings saved, but not activated.')
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :jira))
end
it 'does not show the Jira link in the menu' do
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index a6b4aaccfb5..6ddffb710a8 100644
--- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js do
click_active_toggle
click_on 'Save changes'
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :mattermost_slash_commands))
expect(page).to have_content('Mattermost slash commands settings saved, but not activated.')
end
@@ -41,7 +41,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js do
fill_in 'service_token', with: token
click_on 'Save changes'
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :mattermost_slash_commands))
expect(page).to have_content('Mattermost slash commands activated.')
end
diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
index 360e462b935..afe6855d6ad 100644
--- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe 'Slack slash commands', :js do
click_active_toggle
click_on 'Save'
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :slack_slash_commands))
expect(page).to have_content('Slack slash commands settings saved, but not activated.')
end
@@ -32,7 +32,7 @@ RSpec.describe 'Slack slash commands', :js do
fill_in 'Token', with: 'token'
click_on 'Save'
- expect(current_path).to eq(project_settings_integrations_path(project))
+ expect(current_path).to eq(edit_project_service_path(project, :slack_slash_commands))
expect(page).to have_content('Slack slash commands activated.')
end
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb
index dfbb6342173..878794bd897 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/operations_settings_spec.rb
@@ -45,15 +45,12 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
it 'updates form values' do
check(create_issue)
- template_select = find_field('Issue template')
- template_select.find(:xpath, 'option[2]').select_option
uncheck(send_email)
save_form
click_expand_incident_management_button
expect(find_field(create_issue)).to be_checked
- expect(page).to have_select('Issue template', selected: 'bug')
expect(find_field(send_email)).not_to be_checked
end
@@ -64,7 +61,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
end
def save_form
- page.within "#edit_project_#{project.id}" do
+ page.within ".qa-incident-management-settings" do
click_on 'Save changes'
end
end
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index 3dcb7ca54a1..8e2f97fd6a0 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -20,10 +20,10 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
it 'shows available section' do
settings_block = find('#js-registry-policies')
- expect(settings_block).to have_text 'Container Registry tag expiration policy'
+ expect(settings_block).to have_text 'Cleanup policy for tags'
end
- it 'saves expiration policy submit the form' do
+ it 'saves cleanup policy submit the form' do
within '#js-registry-policies' do
within '.card-body' do
select('7 days until tags are automatically removed', from: 'Expiration interval:')
@@ -36,10 +36,10 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
submit_button.click
end
toast = find('.gl-toast')
- expect(toast).to have_content('Expiration policy successfully saved.')
+ expect(toast).to have_content('Cleanup policy successfully saved.')
end
- it 'does not save expiration policy submit form with invalid regex' do
+ it 'does not save cleanup policy submit form with invalid regex' do
within '#js-registry-policies' do
within '.card-body' do
fill_in('Tags with names matching this regex pattern will expire:', with: '*-production')
@@ -49,7 +49,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
submit_button.click
end
toast = find('.gl-toast')
- expect(toast).to have_content('Something went wrong while updating the expiration policy.')
+ expect(toast).to have_content('Something went wrong while updating the cleanup policy.')
end
end
diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb
new file mode 100644
index 00000000000..7856ab1fb4e
--- /dev/null
+++ b/spec/features/projects/settings/service_desk_setting_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Service Desk Setting', :js do
+ let(:project) { create(:project_empty_repo, :private, service_desk_enabled: false) }
+ let(:presenter) { project.present(current_user: user) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ allow_any_instance_of(Project).to receive(:present).with(current_user: user).and_return(presenter)
+ allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
+ allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
+
+ visit edit_project_path(project)
+ end
+
+ it 'shows activation checkbox' do
+ expect(page).to have_selector("#service-desk-checkbox")
+ end
+
+ it 'shows incoming email after activating' do
+ find("#service-desk-checkbox").click
+ wait_for_requests
+ project.reload
+ expect(project.service_desk_enabled).to be_truthy
+ expect(project.service_desk_address).to be_present
+ expect(find('.incoming-email').value).to eq(project.service_desk_address)
+ end
+end
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index 3fc1f47d98a..e97e4a2030a 100644
--- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
@@ -28,6 +28,22 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
end
end
+ it 'shows Squash commit options', :aggregate_failures do
+ page.within '#js-merge-request-settings' do
+ expect(page).to have_content 'Do not allow'
+ expect(page).to have_content 'Squashing is never performed and the checkbox is hidden.'
+
+ expect(page).to have_content 'Allow'
+ expect(page).to have_content 'Checkbox is visible and unselected by default.'
+
+ expect(page).to have_content 'Encourage'
+ expect(page).to have_content 'Checkbox is visible and selected by default.'
+
+ expect(page).to have_content 'Require'
+ expect(page).to have_content 'Squashing is always performed. Checkbox is visible and selected, and users cannot change it.'
+ end
+ end
+
context 'when Merge Request and Pipelines are initially enabled', :js do
context 'when Pipelines are initially enabled' do
it 'shows the Merge Requests settings' do
@@ -130,4 +146,56 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(project.remove_source_branch_after_merge).to be(false)
end
end
+
+ describe 'Squash commits when merging', :js do
+ it 'initially has :squash_option set to :default_off' do
+ radio = find_field('project_project_setting_attributes_squash_option_default_off')
+ expect(radio).to be_checked
+ end
+
+ it 'allows :squash_option to be set to :default_on' do
+ choose('project_project_setting_attributes_squash_option_default_on')
+
+ within('.merge-request-settings-form') do
+ find('.qa-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ find('.flash-notice')
+ radio = find_field('project_project_setting_attributes_squash_option_default_on')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.squash_option).to eq('default_on')
+ end
+
+ it 'allows :squash_option to be set to :always' do
+ choose('project_project_setting_attributes_squash_option_always')
+
+ within('.merge-request-settings-form') do
+ find('.qa-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ find('.flash-notice')
+ radio = find_field('project_project_setting_attributes_squash_option_always')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.squash_option).to eq('always')
+ end
+
+ it 'allows :squash_option to be set to :never' do
+ choose('project_project_setting_attributes_squash_option_never')
+
+ within('.merge-request-settings-form') do
+ find('.qa-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ find('.flash-notice')
+ radio = find_field('project_project_setting_attributes_squash_option_never')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.squash_option).to eq('never')
+ end
+ end
end
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index d32f4cb8ec7..3836b95a28a 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -16,15 +16,20 @@ RSpec.describe 'Projects > Settings > User manages project members' do
sign_in(user)
end
- it 'cancels a team member' do
+ it 'cancels a team member', :js do
visit(project_project_members_path(project))
project_member = project.project_members.find_by(user_id: user_dmitriy.id)
page.within("#project_member_#{project_member.id}") do
- click_link('Remove user from project')
+ # Open modal
+ click_on('Remove user from project')
end
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
+
visit(project_project_members_path(project))
expect(page).not_to have_content(user_dmitriy.name)
diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb
index a35f6420bdc..3b77fd7eebf 100644
--- a/spec/features/projects/show/user_sees_git_instructions_spec.rb
+++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb
@@ -18,6 +18,8 @@ RSpec.describe 'Projects > Show > User sees Git instructions' do
page.within '.empty-wrapper' do
expect(page).to have_content('Command line instructions')
end
+
+ expect(page).to have_content("git push -u origin master")
end
end
@@ -59,6 +61,26 @@ RSpec.describe 'Projects > Show > User sees Git instructions' do
include_examples 'shows details of empty project with no repo'
end
+ context ":default_branch_name is specified" do
+ let_it_be(:project) { create(:project, :public) }
+
+ before do
+ expect(Gitlab::CurrentSettings)
+ .to receive(:default_branch_name)
+ .at_least(:once)
+ .and_return('example_branch')
+
+ sign_in(project.owner)
+ visit project_path(project)
+ end
+
+ it "recommends default_branch_name instead of master" do
+ click_link 'Create empty repository'
+
+ expect(page).to have_content("git push -u origin example_branch")
+ end
+ end
+
context 'when project is empty' do
let_it_be(:project) { create(:project_empty_repo, :public) }
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index 0f10b0a4010..afa9de5ce86 100644
--- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
+++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
@@ -63,6 +63,23 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
expect(page).to have_link('Add LICENSE', href: presenter.add_license_path)
end
end
+
+ context 'Gitlab::CurrentSettings.default_branch_name is available' do
+ before do
+ expect(Gitlab::CurrentSettings)
+ .to receive(:default_branch_name)
+ .at_least(:once)
+ .and_return('example_branch')
+
+ visit project_path(project)
+ end
+
+ it '"New file" button linked to new file page' do
+ page.within('.project-buttons') do
+ expect(page).to have_link('New file', href: project_new_blob_path(project, 'example_branch'))
+ end
+ end
+ end
end
end
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index 388fa39874d..bd2af66710a 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects tree', :js do
+ include RepoHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:gravatar_enabled) { true }
@@ -47,6 +49,30 @@ RSpec.describe 'Projects tree', :js do
expect(page).not_to have_selector('.flash-alert')
end
+ context "with a tree that contains pathspec characters" do
+ let(:path) { ':wq' }
+ let(:filename) { File.join(path, 'test.txt') }
+ let(:newrev) { project.repository.commit('master').sha }
+ let(:short_newrev) { project.repository.commit('master').short_id }
+ let(:message) { 'Glob characters'}
+
+ before do
+ create_file_in_repo(project, 'master', 'master', filename, 'Test file', commit_message: message)
+ visit project_tree_path(project, File.join('master', path))
+ wait_for_requests
+ end
+
+ it "renders tree table without errors" do
+ expect(page).to have_selector('.tree-item')
+ expect(page).to have_content('test.txt')
+ expect(page).to have_content(message)
+
+ # Check last commit
+ expect(find('.commit-content').text).to include(message)
+ expect(find('.commit-sha-group').text).to eq(short_newrev)
+ end
+ end
+
context 'gravatar disabled' do
let(:gravatar_enabled) { false }
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index cc2a9eacbad..1d443e0b339 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe 'Projects > User sees sidebar' do
let(:user) { create(:user) }
let(:project) { create(:project, :private, public_builds: false, namespace: user.namespace) }
+ before do
+ stub_feature_flags(vue_issuables_list: false)
+ end
+
# NOTE: See documented behaviour https://design.gitlab.com/regions/navigation#contextual-navigation
context 'on different viewports', :js do
include MobileHelpers
diff --git a/spec/features/projects/user_sees_user_popover_spec.rb b/spec/features/projects/user_sees_user_popover_spec.rb
index 851ce79e1c6..9cfc6234969 100644
--- a/spec/features/projects/user_sees_user_popover_spec.rb
+++ b/spec/features/projects/user_sees_user_popover_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'User sees user popover', :js do
subject { page }
describe 'hovering over a user link in a merge request' do
- let(:popover_selector) { '.user-popover' }
+ let(:popover_selector) { '[data-testid="user-popover"]' }
before do
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
index d9f79162c19..0af40a2d760 100644
--- a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
@@ -4,18 +4,19 @@ require 'spec_helper'
RSpec.describe 'User views empty wiki' do
let(:user) { create(:user) }
+ let(:confluence_link) { 'Enable the Confluence Wiki integration' }
+ let(:element) { page.find('.row.empty-state') }
shared_examples 'empty wiki and accessible issues' do
it 'show "issue tracker" message' do
visit(project_wikis_path(project))
- element = page.find('.row.empty-state')
-
expect(element).to have_content('This project has no wiki pages')
expect(element).to have_content('You must be a project member')
expect(element).to have_content('improve the wiki for this project')
expect(element).to have_link("issue tracker", href: project_issues_path(project))
expect(element).to have_link("Suggest wiki improvement", href: new_project_issue_path(project))
+ expect(element).to have_no_link(confluence_link)
end
end
@@ -23,11 +24,10 @@ RSpec.describe 'User views empty wiki' do
it 'does not show "issue tracker" message' do
visit(project_wikis_path(project))
- element = page.find('.row.empty-state')
-
expect(element).to have_content('This project has no wiki pages')
expect(element).to have_content('You must be a project member')
expect(element).to have_no_link('Suggest wiki improvement')
+ expect(element).to have_no_link(confluence_link)
end
end
@@ -60,16 +60,15 @@ RSpec.describe 'User views empty wiki' do
end
context 'when user is logged in and a member' do
- let(:project) { create(:project, :public, :wiki_repo) }
+ let(:project) { create(:project, :public) }
before do
sign_in(user)
project.add_developer(user)
end
- it 'show "create first page" message' do
+ it 'shows "create first page" message' do
visit(project_wikis_path(project))
- element = page.find('.row.empty-state')
expect(element).to have_content('your project', count: 2)
@@ -77,5 +76,34 @@ RSpec.describe 'User views empty wiki' do
expect(page).to have_button('Create page')
end
+
+ it 'does not show the "enable confluence" button' do
+ visit(project_wikis_path(project))
+
+ expect(element).to have_no_link(confluence_link)
+ end
+ end
+
+ context 'when user is logged in and an admin' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
+ end
+
+ it 'shows the "enable confluence" button' do
+ visit(project_wikis_path(project))
+
+ expect(element).to have_link(confluence_link)
+ end
+
+ it 'does not show "enable confluence" button if confluence is already enabled' do
+ create(:confluence_service, project: project)
+
+ visit(project_wikis_path(project))
+
+ expect(element).to have_no_link(confluence_link)
+ end
end
end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 59ccb83a9bb..e93689af0aa 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -8,9 +8,10 @@ RSpec.describe 'User views a wiki page' do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:path) { 'image.png' }
+ let(:wiki) { project.wiki }
let(:wiki_page) do
create(:wiki_page,
- wiki: project.wiki,
+ wiki: wiki,
title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})")
end
@@ -70,11 +71,13 @@ RSpec.describe 'User views a wiki page' do
click_on('Page history')
- page.within(:css, '.nav-text') do
+ within('.nav-text') do
expect(page).to have_content('History')
end
- find('a[href*="?version_id"]')
+ within('.wiki-history') do
+ expect(page).to have_css('a[href*="?version_id"]', count: 4)
+ end
end
end
@@ -92,8 +95,8 @@ RSpec.describe 'User views a wiki page' do
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
it do
- expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
- expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
+ expect(page).to have_xpath("//img[@data-src='#{wiki.wiki_base_path}/#{path}']")
+ expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}")
click_on('image')
@@ -103,7 +106,7 @@ RSpec.describe 'User views a wiki page' do
end
it 'shows the creation page if file does not exist' do
- expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
+ expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}")
click_on('image')
@@ -114,7 +117,7 @@ RSpec.describe 'User views a wiki page' do
context 'when a page has history' do
before do
- wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)')
+ wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') # rubocop:disable Rails/SaveBang
end
it 'shows the page history' do
@@ -134,13 +137,74 @@ RSpec.describe 'User views a wiki page' do
expect(page).not_to have_selector('a.btn', text: 'Edit')
end
+
+ context 'show the diff' do
+ def expect_diff_links(commit)
+ diff_path = wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
+
+ expect(page).to have_link('Hide whitespace changes', href: "#{diff_path}&w=1")
+ expect(page).to have_link('Inline', href: "#{diff_path}&view=inline")
+ expect(page).to have_link('Side-by-side', href: "#{diff_path}&view=parallel")
+ expect(page).to have_link("View page @ #{commit.short_id}", href: wiki_page_path(wiki, wiki_page, version_id: commit))
+ expect(page).to have_css('.diff-file[data-blob-diff-path="%s"]' % diff_path)
+ end
+
+ it 'links to the correct diffs' do
+ visit project_wiki_history_path(project, wiki_page)
+
+ commit1 = wiki.commit('HEAD^')
+ commit2 = wiki.commit
+
+ expect(page).to have_link('created page: home', href: wiki_page_path(wiki, wiki_page, version_id: commit1, action: :diff))
+ expect(page).to have_link('updated home', href: wiki_page_path(wiki, wiki_page, version_id: commit2, action: :diff))
+ end
+
+ it 'between the current and the previous version of a page' do
+ commit = wiki.commit
+ visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
+
+ expect(page).to have_content('by John Doe')
+ expect(page).to have_content('updated home')
+ expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions')
+ expect(page).to have_content('some link')
+
+ expect_diff_links(commit)
+ end
+
+ it 'between two old versions of a page' do
+ wiki_page.update(message: 'latest home change', content: 'updated [another link](other-page)') # rubocop:disable Rails/SaveBang:
+ commit = wiki.commit('HEAD^')
+ visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
+
+ expect(page).to have_content('by John Doe')
+ expect(page).to have_content('updated home')
+ expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions')
+ expect(page).to have_content('some link')
+ expect(page).not_to have_content('latest home change')
+ expect(page).not_to have_content('another link')
+
+ expect_diff_links(commit)
+ end
+
+ it 'for the oldest version of a page' do
+ commit = wiki.commit('HEAD^')
+ visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
+
+ expect(page).to have_content('by John Doe')
+ expect(page).to have_content('created page: home')
+ expect(page).to have_content('Showing 1 changed file with 4 additions and 0 deletions')
+ expect(page).to have_content('Look at this')
+
+ expect_diff_links(commit)
+ end
+ end
end
context 'when a page has special characters in its title' do
let(:title) { '<foo> !@#$%^&*()[]{}=_+\'"\\|<>? <bar>' }
before do
- wiki_page.update(title: title )
+ wiki_page.update(title: title ) # rubocop:disable Rails/SaveBang
end
it 'preserves the special characters' do
@@ -155,7 +219,7 @@ RSpec.describe 'User views a wiki page' do
let(:title) { '<script>alert("title")<script>' }
before do
- wiki_page.update(title: title, content: 'foo <script>alert("content")</script> bar')
+ wiki_page.update(title: title, content: 'foo <script>alert("content")</script> bar') # rubocop:disable Rails/SaveBang
end
it 'safely displays the page' do
@@ -168,7 +232,7 @@ RSpec.describe 'User views a wiki page' do
context 'when a page has XSS in its message' do
before do
- wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update')
+ wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update') # rubocop:disable Rails/SaveBang
end
it 'safely displays the message' do
diff --git a/spec/features/promotion_spec.rb b/spec/features/promotion_spec.rb
new file mode 100644
index 00000000000..9344f9b56b8
--- /dev/null
+++ b/spec/features/promotion_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Promotions', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project_empty_repo) }
+
+ describe 'for service desk', :js do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'when service desk is not supported' do
+ before do
+ allow(::Gitlab::ServiceDesk).to receive(:supported?).and_return(false)
+ end
+
+ it 'appears in project edit page' do
+ visit edit_project_path(project)
+
+ expect(find('#promote_service_desk')).to have_content 'Improve customer support with GitLab Service Desk.'
+ end
+
+ it 'does not show when cookie is set' do
+ visit edit_project_path(project)
+
+ within('#promote_service_desk') do
+ find('.close').click
+ end
+
+ wait_for_requests
+
+ visit edit_project_path(project)
+
+ expect(page).not_to have_selector('#promote_service_desk')
+ end
+ end
+
+ context 'when service desk is supported' do
+ before do
+ allow(::Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
+ end
+
+ it 'does not show promotion' do
+ visit edit_project_path(project)
+
+ expect(page).not_to have_selector('#promote_service_desk')
+ end
+ end
+ end
+end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 8806a363ca4..9b2373bf28b 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -270,7 +270,7 @@ RSpec.describe 'Runners' do
it 'there are no runners displayed' do
visit group_settings_ci_cd_path(group)
- expect(page).to have_content 'This group does not provide any group Runners yet'
+ expect(page).to have_content 'No runners found'
end
it 'user can see a link to install runners on kubernetes clusters' do
@@ -286,26 +286,26 @@ RSpec.describe 'Runners' do
it 'the runner is visible' do
visit group_settings_ci_cd_path(group)
- expect(page).not_to have_content 'This group does not provide any group Runners yet'
- expect(page).to have_content 'Available group Runners: 1'
+ expect(page).not_to have_content 'No runners found'
+ expect(page).to have_content 'Available Runners: 1'
expect(page).to have_content 'group-runner'
end
it 'user can pause and resume the group runner' do
visit group_settings_ci_cd_path(group)
- expect(page).to have_content('Pause')
- expect(page).not_to have_content('Resume')
+ expect(page).to have_link href: pause_group_runner_path(group, runner)
+ expect(page).not_to have_link href: resume_group_runner_path(group, runner)
- click_on 'Pause'
+ click_link href: pause_group_runner_path(group, runner)
- expect(page).not_to have_content('Pause')
- expect(page).to have_content('Resume')
+ expect(page).not_to have_link href: pause_group_runner_path(group, runner)
+ expect(page).to have_link href: resume_group_runner_path(group, runner)
- click_on 'Resume'
+ click_link href: resume_group_runner_path(group, runner)
- expect(page).to have_content('Pause')
- expect(page).not_to have_content('Resume')
+ expect(page).to have_link href: pause_group_runner_path(group, runner)
+ expect(page).not_to have_link href: resume_group_runner_path(group, runner)
end
it 'user can view runner details' do
@@ -321,7 +321,7 @@ RSpec.describe 'Runners' do
it 'user can remove a group runner' do
visit group_settings_ci_cd_path(group)
- click_on 'Remove Runner'
+ all(:link, href: group_runner_path(group, runner))[1].click
expect(page).not_to have_content(runner.display_name)
end
@@ -329,7 +329,7 @@ RSpec.describe 'Runners' do
it 'user edits the runner to be protected' do
visit group_settings_ci_cd_path(group)
- first('.edit-runner > a').click
+ click_link href: edit_group_runner_path(group, runner)
expect(page.find_field('runner[access_level]')).not_to be_checked
@@ -347,7 +347,87 @@ RSpec.describe 'Runners' do
it 'user edits runner not to run untagged jobs' do
visit group_settings_ci_cd_path(group)
- first('.edit-runner > a').click
+ click_link href: edit_group_runner_path(group, runner)
+
+ expect(page.find_field('runner[run_untagged]')).to be_checked
+
+ uncheck 'runner_run_untagged'
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Can run untagged jobs No'
+ end
+ end
+ end
+
+ context 'group with a project runner' do
+ let(:project) { create(:project, group: group) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project], description: 'project-runner') }
+
+ it 'the runner is visible' do
+ visit group_settings_ci_cd_path(group)
+
+ expect(page).not_to have_content 'No runners found'
+ expect(page).to have_content 'Available Runners: 1'
+ expect(page).to have_content 'project-runner'
+ end
+
+ it 'user can pause and resume the project runner' do
+ visit group_settings_ci_cd_path(group)
+
+ expect(page).to have_link href: pause_group_runner_path(group, runner)
+ expect(page).not_to have_link href: resume_group_runner_path(group, runner)
+
+ click_link href: pause_group_runner_path(group, runner)
+
+ expect(page).not_to have_link href: pause_group_runner_path(group, runner)
+ expect(page).to have_link href: resume_group_runner_path(group, runner)
+
+ click_link href: resume_group_runner_path(group, runner)
+
+ expect(page).to have_link href: pause_group_runner_path(group, runner)
+ expect(page).not_to have_link href: resume_group_runner_path(group, runner)
+ end
+
+ it 'user can view runner details' do
+ visit group_settings_ci_cd_path(group)
+
+ expect(page).to have_content(runner.display_name)
+
+ click_on runner.short_sha
+
+ expect(page).to have_content(runner.platform)
+ end
+
+ it 'user can remove a project runner' do
+ visit group_settings_ci_cd_path(group)
+
+ all(:link, href: group_runner_path(group, runner))[1].click
+
+ expect(page).not_to have_content(runner.display_name)
+ end
+
+ it 'user edits the runner to be protected' do
+ visit group_settings_ci_cd_path(group)
+
+ click_link href: edit_group_runner_path(group, runner)
+
+ expect(page.find_field('runner[access_level]')).not_to be_checked
+
+ check 'runner_access_level'
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Protected Yes'
+ end
+
+ context 'when a runner has a tag' do
+ before do
+ runner.update(tag_list: ['tag'])
+ end
+
+ it 'user edits runner not to run untagged jobs' do
+ visit group_settings_ci_cd_path(group)
+
+ click_link href: edit_group_runner_path(group, runner)
expect(page.find_field('runner[run_untagged]')).to be_checked
@@ -358,5 +438,17 @@ RSpec.describe 'Runners' do
end
end
end
+
+ context 'group with a multi-project runner' do
+ let(:project) { create(:project, group: group) }
+ let(:project_2) { create(:project, group: group) }
+ let!(:runner) { create(:ci_runner, :project, projects: [project, project_2], description: 'group-runner') }
+
+ it 'user cannot remove the project runner' do
+ visit group_settings_ci_cd_path(group)
+
+ expect(all(:link, href: group_runner_path(group, runner)).length).to eq(1)
+ end
+ end
end
end
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
index dfe78aa7ebc..20a271f9c0e 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -5,11 +5,10 @@ require 'spec_helper'
RSpec.describe "Public Project Snippets Access" do
include AccessMatchers
- let(:project) { create(:project, :public) }
-
- let(:public_snippet) { create(:project_snippet, :public, project: project, author: project.owner) }
- let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: project.owner) }
- let(:private_snippet) { create(:project_snippet, :private, project: project, author: project.owner) }
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:public_snippet) { create(:project_snippet, :public, project: project, author: project.owner) }
+ let_it_be(:internal_snippet) { create(:project_snippet, :internal, project: project, author: project.owner) }
+ let_it_be(:private_snippet) { create(:project_snippet, :private, project: project, author: project.owner) }
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 04ca8a09ca8..d679e4dbb99 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -72,6 +72,7 @@ RSpec.describe 'GPG signed commits' do
it 'unverified signature' do
visit project_commit_path(project, GpgHelpers::SIGNED_COMMIT_SHA)
+ wait_for_all_requests
page.find('.gpg-status-box', text: 'Unverified').click
@@ -85,6 +86,7 @@ RSpec.describe 'GPG signed commits' do
user_2_key
visit project_commit_path(project, GpgHelpers::DIFFERING_EMAIL_SHA)
+ wait_for_all_requests
page.find('.gpg-status-box', text: 'Unverified').click
@@ -100,6 +102,7 @@ RSpec.describe 'GPG signed commits' do
user_2_key
visit project_commit_path(project, GpgHelpers::SIGNED_COMMIT_SHA)
+ wait_for_all_requests
page.find('.gpg-status-box', text: 'Unverified').click
@@ -115,6 +118,7 @@ RSpec.describe 'GPG signed commits' do
user_1_key
visit project_commit_path(project, GpgHelpers::SIGNED_AND_AUTHORED_SHA)
+ wait_for_all_requests
page.find('.gpg-status-box', text: 'Verified').click
@@ -130,6 +134,7 @@ RSpec.describe 'GPG signed commits' do
user_1_key
visit project_commit_path(project, GpgHelpers::SIGNED_AND_AUTHORED_SHA)
+ wait_for_all_requests
# wait for the signature to get generated
expect(page).to have_selector('.gpg-status-box', text: 'Verified')
@@ -137,6 +142,7 @@ RSpec.describe 'GPG signed commits' do
user_1.destroy!
refresh
+ wait_for_all_requests
page.find('.gpg-status-box', text: 'Verified').click
@@ -153,6 +159,7 @@ RSpec.describe 'GPG signed commits' do
shared_examples 'a commit with a signature' do
before do
visit project_tree_path(project, 'signed-commits')
+ wait_for_all_requests
end
it 'displays commit signature' do
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index fa87c4bb1c4..fc3f8a94318 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -103,7 +103,7 @@ RSpec.describe 'Task Lists' do
wait_for_requests
expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox")
- expect(page).to have_selector('a.btn-close')
+ expect(page).to have_selector('.btn-close')
end
it 'is only editable by author' do
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 577134fe722..4be27673adf 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -85,7 +85,7 @@ RSpec.describe 'Triggers', :js do
end
expect(page.find('.flash-notice')).to have_content 'Trigger removed'
- expect(page).to have_selector('p.settings-message.text-center.append-bottom-default')
+ expect(page).to have_selector('p.settings-message.text-center.gl-mb-3')
end
end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 7ba663d08d4..2d0fcfe84e6 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
- expect(page).not_to have_content('You have to confirm your email address before continuing.')
+ expect(page).not_to have_content(I18n.t('devise.failure.unconfirmed'))
expect(page).not_to have_link('Resend confirmation email', href: new_user_confirmation_path)
end
end
@@ -124,7 +124,7 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
- expect(page).to have_content('You have to confirm your email address before continuing.')
+ expect(page).to have_content(I18n.t('devise.failure.unconfirmed'))
expect(page).to have_link('Resend confirmation email', href: new_user_confirmation_path)
end
end
@@ -820,7 +820,7 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
expect(current_path).to eq new_user_session_path
- expect(page).to have_content('You have to confirm your email address before continuing.')
+ expect(page).to have_content(I18n.t('devise.failure.unconfirmed'))
end
end
end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 66a26493339..af2ecfec498 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -70,6 +70,13 @@ RSpec.shared_examples 'Signup' do
expect(page).to have_content("Username is too long (maximum is 255 characters).")
end
+ it 'shows an error message if the username is less than 2 characters' do
+ fill_in 'new_user_username', with: 'u'
+ wait_for_requests
+
+ expect(page).to have_content("Username is too short (minimum is 2 characters).")
+ end
+
it 'shows an error message on submit if the username contains special characters' do
fill_in 'new_user_username', with: 'new$user!username'
wait_for_requests
@@ -91,7 +98,7 @@ RSpec.shared_examples 'Signup' do
expect(page).to have_content("Invalid input, please avoid emojis")
end
- it 'shows a pending message if the username availability is being fetched', :quarantine do
+ it 'shows a pending message if the username availability is being fetched', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31484' do
fill_in 'new_user_username', with: 'new-user'
expect(find('.username > .validation-pending')).not_to have_css '.hide'