Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb31
-rw-r--r--spec/features/admin/admin_dev_ops_report_spec.rb (renamed from spec/features/instance_statistics/dev_ops_score_spec.rb)24
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb81
-rw-r--r--spec/features/admin/admin_users_spec.rb7
-rw-r--r--spec/features/admin/services/admin_activates_prometheus_spec.rb2
-rw-r--r--spec/features/admin/services/admin_visits_service_templates_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb60
-rw-r--r--spec/features/boards/new_issue_spec.rb43
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/cycle_analytics_spec.rb13
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb35
-rw-r--r--spec/features/dashboard/instance_statistics_spec.rb64
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb5
-rw-r--r--spec/features/file_uploads/ci_artifact_spec.rb29
-rw-r--r--spec/features/file_uploads/git_lfs_spec.rb37
-rw-r--r--spec/features/file_uploads/graphql_add_design_spec.rb54
-rw-r--r--spec/features/file_uploads/group_import_spec.rb32
-rw-r--r--spec/features/file_uploads/maven_package_spec.rb29
-rw-r--r--spec/features/file_uploads/nuget_package_spec.rb35
-rw-r--r--spec/features/file_uploads/project_import_spec.rb31
-rw-r--r--spec/features/file_uploads/user_avatar_spec.rb33
-rw-r--r--spec/features/groups/board_sidebar_spec.rb2
-rw-r--r--spec/features/groups/clusters/user_spec.rb2
-rw-r--r--spec/features/groups/import_export/import_file_spec.rb2
-rw-r--r--spec/features/groups/members/filter_members_spec.rb2
-rw-r--r--spec/features/groups/members/leave_group_spec.rb2
-rw-r--r--spec/features/groups/members/list_members_spec.rb2
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb2
-rw-r--r--spec/features/groups/members/manage_members_spec.rb2
-rw-r--r--spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb4
-rw-r--r--spec/features/groups/members/search_members_spec.rb2
-rw-r--r--spec/features/groups/members/sort_members_spec.rb2
-rw-r--r--spec/features/groups/milestone_spec.rb2
-rw-r--r--spec/features/groups/navbar_spec.rb5
-rw-r--r--spec/features/import/manifest_import_spec.rb2
-rw-r--r--spec/features/instance_statistics/cohorts_spec.rb19
-rw-r--r--spec/features/instance_statistics/instance_statistics_spec.rb25
-rw-r--r--spec/features/invites_spec.rb15
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb16
-rw-r--r--spec/features/issuables/issuable_list_spec.rb4
-rw-r--r--spec/features/issuables/related_issues_spec.rb400
-rw-r--r--spec/features/issuables/sorting_list_spec.rb14
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb23
-rw-r--r--spec/features/issues/group_label_sidebar_spec.rb4
-rw-r--r--spec/features/issues/incident_issue_spec.rb23
-rw-r--r--spec/features/issues/issue_detail_spec.rb13
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb6
-rw-r--r--spec/features/issues/markdown_toolbar_spec.rb2
-rw-r--r--spec/features/issues/resource_label_events_spec.rb4
-rw-r--r--spec/features/issues/service_desk_spec.rb100
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb48
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb30
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb25
-rw-r--r--spec/features/issues/user_views_issue_spec.rb30
-rw-r--r--spec/features/jira_connect/subscriptions_spec.rb47
-rw-r--r--spec/features/jira_oauth_provider_authorize_spec.rb21
-rw-r--r--spec/features/labels_hierarchy_spec.rb9
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb56
-rw-r--r--spec/features/markdown/keyboard_shortcuts_spec.rb119
-rw-r--r--spec/features/markdown/markdown_spec.rb1
-rw-r--r--spec/features/markdown/mermaid_spec.rb2
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb2
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb2
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb34
-rw-r--r--spec/features/merge_request/user_assigns_themselves_spec.rb9
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb28
-rw-r--r--spec/features/merge_request/user_expands_diff_spec.rb11
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb3
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb1
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb10
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_auto_expanding_diff_spec.rb6
-rw-r--r--spec/features/merge_request/user_views_diffs_file_by_file_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb3
-rw-r--r--spec/features/merge_requests/user_views_diffs_commit_spec.rb1
-rw-r--r--spec/features/profiles/account_spec.rb33
-rw-r--r--spec/features/projects/artifacts/raw_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb79
-rw-r--r--spec/features/projects/branches_spec.rb2
-rw-r--r--spec/features/projects/ci/lint_spec.rb139
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb4
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/environments/environments_spec.rb4
-rw-r--r--spec/features/projects/issues/design_management/user_paginates_designs_spec.rb59
-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.rb79
-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.rb77
-rw-r--r--spec/features/projects/jobs_spec.rb49
-rw-r--r--spec/features/projects/members/invite_group_spec.rb2
-rw-r--r--spec/features/projects/navbar_spec.rb8
-rw-r--r--spec/features/projects/pages_spec.rb4
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb8
-rw-r--r--spec/features/projects/releases/user_creates_release_spec.rb147
-rw-r--r--spec/features/projects/releases/user_views_releases_spec.rb156
-rw-r--r--spec/features/projects/services/user_activates_asana_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_assembla_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_emails_on_push_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_flowdock_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_hipchat_spec.rb10
-rw-r--r--spec/features/projects/services/user_activates_irker_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb23
-rw-r--r--spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_jira_spec.rb15
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb10
-rw-r--r--spec/features/projects/services/user_activates_packagist_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_pivotaltracker_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_prometheus_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_pushover_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_slack_notifications_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb6
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb8
-rw-r--r--spec/features/projects/settings/user_renames_a_project_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_readme_spec.rb21
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb2
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb138
-rw-r--r--spec/features/projects/snippets/user_updates_snippet_spec.rb80
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb5
-rw-r--r--spec/features/projects/view_on_env_spec.rb2
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb1
-rw-r--r--spec/features/projects_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb85
-rw-r--r--spec/features/search/user_searches_for_projects_spec.rb45
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb4
-rw-r--r--spec/features/snippets/spam_snippets_spec.rb19
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb197
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb131
-rw-r--r--spec/features/static_site_editor_spec.rb8
-rw-r--r--spec/features/task_lists_spec.rb12
-rw-r--r--spec/features/u2f_spec.rb115
-rw-r--r--spec/features/users/login_spec.rb2
-rw-r--r--spec/features/users/signup_spec.rb105
-rw-r--r--spec/features/webauthn_spec.rb234
144 files changed, 2819 insertions, 1303 deletions
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
new file mode 100644
index 00000000000..f91446ed222
--- /dev/null
+++ b/spec/features/admin/admin_cohorts_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Cohorts page' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ context 'with usage ping enabled' do
+ it 'shows users count per month' do
+ stub_application_setting(usage_ping_enabled: true)
+
+ create_list(:user, 2)
+
+ visit admin_cohorts_path
+
+ expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
+ end
+ end
+
+ context 'with usage ping disabled' do
+ it 'shows empty state', :js do
+ stub_application_setting(usage_ping_enabled: false)
+
+ visit admin_cohorts_path
+
+ expect(page).to have_selector(".js-empty-state")
+ end
+ end
+end
diff --git a/spec/features/instance_statistics/dev_ops_score_spec.rb b/spec/features/admin/admin_dev_ops_report_spec.rb
index da87aedab5c..c201011cbea 100644
--- a/spec/features/instance_statistics/dev_ops_score_spec.rb
+++ b/spec/features/admin/admin_dev_ops_report_spec.rb
@@ -2,19 +2,19 @@
require 'spec_helper'
-RSpec.describe 'DevOps Score' do
+RSpec.describe 'DevOps Report page' do
before do
sign_in(create(:admin))
end
it 'has dismissable intro callout', :js do
- visit instance_statistics_dev_ops_score_index_path
+ visit admin_dev_ops_report_path
- expect(page).to have_content 'Introducing Your DevOps Score'
+ expect(page).to have_content 'Introducing Your DevOps Report'
find('.js-close-callout').click
- expect(page).not_to have_content 'Introducing Your DevOps Score'
+ expect(page).not_to have_content 'Introducing Your DevOps Report'
end
context 'when usage ping is disabled' do
@@ -22,16 +22,16 @@ RSpec.describe 'DevOps Score' do
stub_application_setting(usage_ping_enabled: false)
end
- it 'shows empty state' do
- visit instance_statistics_dev_ops_score_index_path
+ it 'shows empty state', :js do
+ visit admin_dev_ops_report_path
- expect(page).to have_content('Usage ping is not enabled')
+ expect(page).to have_selector(".js-empty-state")
end
it 'hides the intro callout' do
- visit instance_statistics_dev_ops_score_index_path
+ visit admin_dev_ops_report_path
- expect(page).not_to have_content 'Introducing Your DevOps Score'
+ expect(page).not_to have_content 'Introducing Your DevOps Report'
end
end
@@ -39,7 +39,7 @@ RSpec.describe 'DevOps Score' do
it 'shows empty state' do
stub_application_setting(usage_ping_enabled: true)
- visit instance_statistics_dev_ops_score_index_path
+ visit admin_dev_ops_report_path
expect(page).to have_content('Data is still calculating')
end
@@ -48,9 +48,9 @@ RSpec.describe 'DevOps Score' do
context 'when there is data to display' do
it 'shows numbers for each metric' do
stub_application_setting(usage_ping_enabled: true)
- create(:dev_ops_score_metric)
+ create(:dev_ops_report_metric)
- visit instance_statistics_dev_ops_score_index_path
+ visit admin_dev_ops_report_path
expect(page).to have_content(
'Issues created per active user 1.2 You 9.3 Lead 13.3%'
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 9cd335ffb8c..f5c5a73c042 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Admin Groups' do
let!(:current_user) { create(:admin) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(current_user)
stub_application_setting(default_group_visibility: internal)
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index f5b05c76e90..38f0b813183 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -17,7 +17,10 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
context 'General page' do
+ let(:gitpod_feature_enabled) { true }
+
before do
+ stub_feature_flags(gitpod: gitpod_feature_enabled)
visit general_admin_application_settings_path
end
@@ -205,6 +208,32 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
expect(current_settings.terminal_max_session_time).to eq(15)
end
+
+ context 'Configure Gitpod' do
+ context 'with feature disabled' do
+ let(:gitpod_feature_enabled) { false }
+
+ it 'do not show settings' do
+ expect(page).not_to have_selector('#js-gitpod-settings')
+ end
+ end
+
+ context 'with feature enabled' do
+ let(:gitpod_feature_enabled) { true }
+
+ it 'changes gitpod settings' do
+ page.within('#js-gitpod-settings') do
+ check 'Enable Gitpod integration'
+ fill_in 'Gitpod URL', with: 'https://gitpod.test/'
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content 'Application settings saved successfully'
+ expect(current_settings.gitpod_url).to eq('https://gitpod.test/')
+ expect(current_settings.gitpod_enabled).to be(true)
+ end
+ end
+ end
end
context 'Integrations page' do
@@ -232,7 +261,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
page.select 'All branches', from: 'Branches to be notified'
check_all_events
- click_on 'Save'
+ click_button 'Save changes'
expect(page).to have_content 'Application settings saved successfully'
@@ -285,6 +314,55 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(current_settings.auto_devops_domain).to eq('domain.com')
expect(page).to have_content "Application settings saved successfully"
end
+
+ context 'Container Registry' do
+ context 'delete tags service execution timeout' do
+ let(:feature_flag_enabled) { true }
+ let(:client_support) { true }
+
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_feature_flags(container_registry_expiration_policies_throttling: feature_flag_enabled)
+ allow(ContainerRegistry::Client).to receive(:supports_tag_delete?).and_return(client_support)
+ end
+
+ RSpec.shared_examples 'not having service timeout settings' do
+ it 'lacks the timeout settings' do
+ visit ci_cd_admin_application_settings_path
+
+ expect(page).not_to have_content "Container Registry delete tags service execution timeout"
+ end
+ end
+
+ context 'with feature flag enabled' do
+ context 'with client supporting tag delete' do
+ it 'changes the timeout' do
+ visit ci_cd_admin_application_settings_path
+
+ page.within('.as-registry') do
+ fill_in 'application_setting_container_registry_delete_tags_service_timeout', with: 400
+ click_button 'Save changes'
+ end
+
+ expect(current_settings.container_registry_delete_tags_service_timeout).to eq(400)
+ expect(page).to have_content "Application settings saved successfully"
+ end
+ end
+
+ context 'with client not supporting tag delete' do
+ let(:client_support) { false }
+
+ it_behaves_like 'not having service timeout settings'
+ end
+ end
+
+ context 'with feature flag disabled' do
+ let(:feature_flag_enabled) { false }
+
+ it_behaves_like 'not having service timeout settings'
+ end
+ end
+ end
end
context 'Repository page' do
@@ -525,6 +603,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
def check_all_events
+ page.check('Active')
page.check('Push')
page.check('Issue')
page.check('Confidential Issue')
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 6cd18f2755c..a37210d2acc 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -279,7 +279,8 @@ RSpec.describe "Admin::Users" do
expect(page).to have_content(user.email)
expect(page).to have_content(user.name)
- expect(page).to have_content(user.id)
+ expect(page).to have_content("ID: #{user.id}")
+ expect(page).to have_content("Namespace ID: #{user.namespace_id}")
expect(page).to have_button('Deactivate user')
expect(page).to have_button('Block user')
expect(page).to have_button('Delete user')
@@ -353,7 +354,7 @@ RSpec.describe "Admin::Users" do
it 'sees impersonation log out icon' do
subject
- icon = first('.fa.fa-user-secret')
+ icon = first('[data-testid="incognito-icon"]')
expect(icon).not_to be nil
end
@@ -536,7 +537,7 @@ RSpec.describe "Admin::Users" do
it 'allows group membership to be revoked', :js do
page.within(first('.group_member')) do
- accept_confirm { find('.btn-remove').click }
+ accept_confirm { find('.btn[data-testid="remove-user"]').click }
end
wait_for_requests
diff --git a/spec/features/admin/services/admin_activates_prometheus_spec.rb b/spec/features/admin/services/admin_activates_prometheus_spec.rb
index 35af9dd6c68..199eae59afc 100644
--- a/spec/features/admin/services/admin_activates_prometheus_spec.rb
+++ b/spec/features/admin/services/admin_activates_prometheus_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Admin activates Prometheus', :js do
it 'activates service' do
check('Active')
fill_in('API URL', with: 'http://prometheus.example.com')
- click_button('Save')
+ click_button('Save changes')
expect(page).to have_content('Application settings saved successfully')
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
index 8e02538ece0..a37e57304aa 100644
--- a/spec/features/admin/services/admin_visits_service_templates_spec.rb
+++ b/spec/features/admin/services/admin_visits_service_templates_spec.rb
@@ -4,7 +4,7 @@ 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' } }
+ let(:slack_service) { Service.for_template.find { |s| s.type == 'SlackService' } }
before do
sign_in(admin)
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 8e2a9381aa0..e36378bd34e 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -6,11 +6,11 @@ RSpec.describe 'Issue Boards', :js do
include DragTo
include MobileHelpers
- let(:group) { create(:group, :nested) }
- let(:project) { create(:project, :public, namespace: group) }
- let(:board) { create(:board, project: project) }
- let(:user) { create(:user) }
- let!(:user2) { create(:user) }
+ let_it_be(:group) { create(:group, :nested) }
+ let_it_be(:project) { create(:project, :public, namespace: group) }
+ let_it_be(:board) { create(:board, project: project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user2) { create(:user) }
before do
project.add_maintainer(user)
@@ -62,30 +62,30 @@ RSpec.describe 'Issue Boards', :js do
end
context 'with lists' do
- let(:milestone) { create(:milestone, project: project) }
-
- let(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') }
- let(:development) { create(:label, project: project, name: 'Development') }
- let(:testing) { create(:label, project: project, name: 'Testing') }
- let(:bug) { create(:label, project: project, name: 'Bug') }
- let!(:backlog) { create(:label, project: project, name: 'Backlog') }
- let!(:closed) { create(:label, project: project, name: 'Closed') }
- let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
- let!(:a_plus) { create(:label, project: project, name: 'A+') }
- let!(:list1) { create(:list, board: board, label: planning, position: 0) }
- let!(:list2) { create(:list, board: board, label: development, position: 1) }
-
- let!(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
- let!(:issue1) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
- let!(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
- let!(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
- let!(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
- let!(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
- let!(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
- let!(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
- let!(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
- let!(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
- let!(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
+ let_it_be(:milestone) { create(:milestone, project: project) }
+
+ let_it_be(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') }
+ let_it_be(:development) { create(:label, project: project, name: 'Development') }
+ let_it_be(:testing) { create(:label, project: project, name: 'Testing') }
+ let_it_be(:bug) { create(:label, project: project, name: 'Bug') }
+ let_it_be(:backlog) { create(:label, project: project, name: 'Backlog') }
+ let_it_be(:closed) { create(:label, project: project, name: 'Closed') }
+ let_it_be(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
+ let_it_be(:a_plus) { create(:label, project: project, name: 'A+') }
+ let_it_be(:list1) { create(:list, board: board, label: planning, position: 0) }
+ let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
+
+ let_it_be(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
+ let_it_be(:issue1) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
+ let_it_be(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
+ let_it_be(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
+ let_it_be(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
+ let_it_be(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
+ let_it_be(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
+ let_it_be(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
+ let_it_be(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
+ let_it_be(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
+ let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
before do
visit project_board_path(project, board)
@@ -636,7 +636,7 @@ RSpec.describe 'Issue Boards', :js do
end
context 'as guest user' do
- let(:user_guest) { create(:user) }
+ let_it_be(:user_guest) { create(:user) }
before do
project.add_guest(user_guest)
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index efa1f8cfc0d..f434ea0c66f 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -130,24 +130,43 @@ RSpec.describe 'Issue Boards new issue', :js do
context 'group boards' do
let_it_be(:group) { create(:group, :public) }
- let_it_be(:project) { create(:project, namespace: group) }
+ let_it_be(:project) { create(:project, :public, namespace: group) }
let_it_be(:group_board) { create(:board, group: group) }
- let_it_be(:list) { create(:list, board: group_board, position: 0) }
+ let_it_be(:project_label) { create(:label, project: project, name: 'label') }
+ let_it_be(:list) { create(:list, board: group_board, label: project_label, position: 0) }
context 'for unauthorized users' do
- before do
- sign_in(user)
- visit group_board_path(group, group_board)
- wait_for_requests
- end
+ context 'when backlog does not exist' do
+ before do
+ sign_in(user)
+ visit group_board_path(group, group_board)
+ wait_for_requests
+ end
- it 'displays new issue button in open list' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ it 'does not display new issue button in label list' do
+ page.within('.board.is-draggable') do
+ expect(page).not_to have_selector('.issue-count-badge-add-button')
+ end
+ end
end
- it 'does not display new issue button in label list' do
- page.within('.board.is-draggable') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ context 'when backlog list already exists' do
+ let!(:backlog_list) { create(:backlog_list, board: group_board) }
+
+ before do
+ sign_in(user)
+ visit group_board_path(group, group_board)
+ wait_for_requests
+ end
+
+ it 'displays new issue button in open list' do
+ expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ end
+
+ it 'does not display new issue button in label list' do
+ page.within('.board.is-draggable') do
+ expect(page).not_to have_selector('.issue-count-badge-add-button')
+ end
end
end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 65f2e5dfc0d..4b4cb444903 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Issue Boards', :js do
let(:application_settings) { {} }
around do |example|
- Timecop.freeze { example.run }
+ freeze_time { example.run }
end
before do
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index 0294ebbe13f..b63079777cb 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe 'Value Stream Analytics', :js do
- let(:user) { create(:user) }
- let(:guest) { create(:user) }
- let(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
let(:milestone) { create(:milestone, project: project) }
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
@@ -13,9 +13,11 @@ RSpec.describe 'Value Stream Analytics', :js do
context 'as an allowed user' do
context 'when project is new' do
- before do
+ before(:all) do
project.add_maintainer(user)
+ end
+ before do
sign_in(user)
visit project_cycle_analytics_path(project)
@@ -74,9 +76,6 @@ RSpec.describe 'Value Stream Analytics', :js do
click_stage('Staging')
expect_build_to_be_present
-
- click_stage('Total')
- expect_issue_to_be_present
end
context "when I change the time period observed" do
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index ed28ec6099d..a3eacd6147c 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -3,44 +3,53 @@
require 'spec_helper'
RSpec.describe 'Tooltips on .timeago dates', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, name: 'test', namespace: user.namespace) }
- let(:created_date) { Date.yesterday.to_time }
- let(:expected_format) { created_date.in_time_zone.strftime('%b %-d, %Y %l:%M%P') }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, name: 'test', namespace: user.namespace) }
+ let(:created_date) { 1.day.ago.beginning_of_minute - 1.hour }
+
+ before_all do
+ project.add_maintainer(user)
+ end
context 'on the activity tab' do
before do
- project.add_maintainer(user)
-
Event.create( project: project, author_id: user.id, action: :joined,
updated_at: created_date, created_at: created_date)
sign_in user
visit user_activity_path(user)
wait_for_requests
-
- page.find('.js-timeago').hover
end
it 'has the datetime formated correctly' do
- expect(page).to have_selector('.local-timeago', text: expected_format)
+ expect(page).to have_selector('.js-timeago', text: '1 day ago')
+
+ page.find('.js-timeago').hover
+
+ expect(datetime_in_tooltip).to eq(created_date)
end
end
context 'on the snippets tab' do
before do
- project.add_maintainer(user)
create(:snippet, author: user, updated_at: created_date, created_at: created_date)
sign_in user
visit user_snippets_path(user)
wait_for_requests
-
- page.find('.js-timeago.snippet-created-ago').hover
end
it 'has the datetime formated correctly' do
- expect(page).to have_selector('.local-timeago', text: expected_format)
+ expect(page).to have_selector('.js-timeago.snippet-created-ago', text: '1 day ago')
+
+ page.find('.js-timeago.snippet-created-ago').hover
+
+ expect(datetime_in_tooltip).to eq(created_date)
end
end
+
+ def datetime_in_tooltip
+ datetime_text = page.find('.local-timeago').text
+ DateTime.parse(datetime_text)
+ end
end
diff --git a/spec/features/dashboard/instance_statistics_spec.rb b/spec/features/dashboard/instance_statistics_spec.rb
deleted file mode 100644
index f85b8454113..00000000000
--- a/spec/features/dashboard/instance_statistics_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Showing analytics' do
- before do
- sign_in user if user
- end
-
- # Using a path that is publicly accessible
- subject { visit explore_projects_path }
-
- context 'for unauthenticated users' do
- let(:user) { nil }
-
- it 'does not show the Analytics link' do
- subject
-
- expect(page).not_to have_link('Analytics')
- end
- end
-
- context 'for regular users' do
- let(:user) { create(:user) }
-
- context 'when instance statistics are publicly available' do
- before do
- stub_application_setting(instance_statistics_visibility_private: false)
- end
-
- it 'shows the analytics link' do
- subject
-
- expect(page).to have_link('Analytics')
- end
- end
-
- context 'when instance statistics are not publicly available' do
- before do
- stub_application_setting(instance_statistics_visibility_private: true)
- end
-
- it 'does not show the analytics link' do
- subject
-
- # Skipping this test on EE as there is an EE specifc spec for this functionality
- # ee/spec/features/dashboards/analytics_spec.rb
- skip if Gitlab.ee?
-
- expect(page).not_to have_link('Analytics')
- end
- end
- end
-
- context 'for admins' do
- let(:user) { create(:admin) }
-
- it 'shows the analytics link' do
- subject
-
- expect(page).to have_link('Analytics')
- end
- end
-end
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index 6b8df8467e5..e705f2916da 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -7,11 +7,6 @@ RSpec.describe 'Expand and collapse diffs', :js do
let(:project) { create(:project, :repository) }
before do
- # Set the limits to those when these specs were written, to avoid having to
- # update the test repo every time we change them.
- allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
- allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
-
sign_in(create(:admin))
# Ensure that undiffable.md is in .gitattributes
diff --git a/spec/features/file_uploads/ci_artifact_spec.rb b/spec/features/file_uploads/ci_artifact_spec.rb
new file mode 100644
index 00000000000..4f3b6c90ad4
--- /dev/null
+++ b/spec/features/file_uploads/ci_artifact_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload ci artifact', :api, :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
+ let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project, pipeline: pipeline, runner_id: runner.id) }
+
+ let(:api_path) { "/jobs/#{job.id}/artifacts?token=#{job.token}" }
+ let(:url) { capybara_url(api(api_path)) }
+ let(:file) { fixture_file_upload('spec/fixtures/ci_build_artifacts.zip') }
+
+ subject do
+ HTTParty.post(url, body: { file: file })
+ end
+
+ RSpec.shared_examples 'for ci artifact' do
+ it { expect { subject }.to change { ::Ci::JobArtifact.count }.by(2) }
+
+ it { expect(subject.code).to eq(201) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for ci artifact'
+end
diff --git a/spec/features/file_uploads/git_lfs_spec.rb b/spec/features/file_uploads/git_lfs_spec.rb
new file mode 100644
index 00000000000..b902d7ab702
--- /dev/null
+++ b/spec/features/file_uploads/git_lfs_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a git lfs object', :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+
+ let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
+ let(:oid) { Digest::SHA256.hexdigest(File.read(file.path)) }
+ let(:size) { file.size }
+ let(:url) { capybara_url("/#{project.namespace.path}/#{project.path}.git/gitlab-lfs/objects/#{oid}/#{size}") }
+ let(:headers) { { 'Content-Type' => 'application/octet-stream' } }
+
+ subject do
+ HTTParty.put(
+ url,
+ headers: headers,
+ basic_auth: { user: user.username, password: personal_access_token.token },
+ body: file.read
+ )
+ end
+
+ before do
+ stub_lfs_setting(enabled: true)
+ end
+
+ RSpec.shared_examples 'for a git lfs object' do
+ it { expect { subject }.to change { LfsObject.count }.by(1) }
+ it { expect(subject.code).to eq(200) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for a git lfs object'
+end
diff --git a/spec/features/file_uploads/graphql_add_design_spec.rb b/spec/features/file_uploads/graphql_add_design_spec.rb
new file mode 100644
index 00000000000..f805ea86b4c
--- /dev/null
+++ b/spec/features/file_uploads/graphql_add_design_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a design through graphQL', :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:query) do
+ "
+ mutation uploadDesign($files: [Upload!]!, $projectPath: ID!, $iid: ID!) {
+ designManagementUpload(input: { projectPath: $projectPath, iid: $iid, files: $files}) {
+ clientMutationId,
+ errors
+ }
+ }
+ "
+ end
+
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+ let_it_be(:design) { create(:design) }
+ let_it_be(:operations) { { "operationName": "uploadDesign", "variables": { "files": [], "projectPath": design.project.full_path, "iid": design.issue.iid }, "query": query }.to_json }
+ let_it_be(:map) { { "1": ["variables.files.0"] }.to_json }
+
+ let(:url) { capybara_url("/api/graphql?private_token=#{personal_access_token.token}") }
+ let(:file) { fixture_file_upload('spec/fixtures/dk.png') }
+
+ subject do
+ HTTParty.post(
+ url,
+ body: {
+ operations: operations,
+ map: map,
+ "1": file
+ }
+ )
+ end
+
+ before do
+ stub_lfs_setting(enabled: true)
+ end
+
+ RSpec.shared_examples 'for a design upload through graphQL' do
+ it 'creates proper objects' do
+ expect { subject }
+ .to change { ::DesignManagement::Design.count }.by(1)
+ .and change { ::LfsObject.count }.by(1)
+ end
+
+ it { expect(subject.code).to eq(200) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for a design upload through graphQL'
+end
diff --git a/spec/features/file_uploads/group_import_spec.rb b/spec/features/file_uploads/group_import_spec.rb
new file mode 100644
index 00000000000..0f9d05c3975
--- /dev/null
+++ b/spec/features/file_uploads/group_import_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a group export archive', :api, :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+ let(:api_path) { '/groups/import' }
+ let(:url) { capybara_url(api(api_path, personal_access_token: personal_access_token)) }
+ let(:file) { fixture_file_upload('spec/fixtures/group_export.tar.gz') }
+
+ subject do
+ HTTParty.post(
+ url,
+ body: {
+ path: 'test-import-group',
+ name: 'test-import-group',
+ file: file
+ }
+ )
+ end
+
+ RSpec.shared_examples 'for a group export archive' do
+ it { expect { subject }.to change { Group.count }.by(1) }
+
+ it { expect(subject.code).to eq(202) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for a group export archive'
+end
diff --git a/spec/features/file_uploads/maven_package_spec.rb b/spec/features/file_uploads/maven_package_spec.rb
new file mode 100644
index 00000000000..c873a0e9a36
--- /dev/null
+++ b/spec/features/file_uploads/maven_package_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a maven package', :api, :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+
+ let(:api_path) { "/projects/#{project.id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar" }
+ let(:url) { capybara_url(api(api_path, personal_access_token: personal_access_token)) }
+ let(:file) { fixture_file_upload('spec/fixtures/dk.png') }
+
+ subject { HTTParty.put(url, body: file.read) }
+
+ RSpec.shared_examples 'for a maven package' do
+ it 'creates package files' do
+ expect { subject }
+ .to change { Packages::Package.maven.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
+ end
+
+ it { expect(subject.code).to eq(200) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for a maven package'
+end
diff --git a/spec/features/file_uploads/nuget_package_spec.rb b/spec/features/file_uploads/nuget_package_spec.rb
new file mode 100644
index 00000000000..fb1e0a54744
--- /dev/null
+++ b/spec/features/file_uploads/nuget_package_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a nuget package', :api, :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+
+ let(:api_path) { "/projects/#{project.id}/packages/nuget/" }
+ let(:url) { capybara_url(api(api_path)) }
+ let(:file) { fixture_file_upload('spec/fixtures/dk.png') }
+
+ subject do
+ HTTParty.put(
+ url,
+ basic_auth: { user: user.username, password: personal_access_token.token },
+ body: { package: file }
+ )
+ end
+
+ RSpec.shared_examples 'for a nuget package' do
+ it 'creates package files' do
+ expect { subject }
+ .to change { Packages::Package.nuget.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
+ end
+
+ it { expect(subject.code).to eq(201) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for a nuget package'
+end
diff --git a/spec/features/file_uploads/project_import_spec.rb b/spec/features/file_uploads/project_import_spec.rb
new file mode 100644
index 00000000000..1bf16f46c63
--- /dev/null
+++ b/spec/features/file_uploads/project_import_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a project export archive', :api, :js do
+ include_context 'file upload requests helpers'
+
+ let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+ let(:api_path) { '/projects/import' }
+ let(:url) { capybara_url(api(api_path, personal_access_token: personal_access_token)) }
+ let(:file) { fixture_file_upload('spec/features/projects/import_export/test_project_export.tar.gz') }
+
+ subject do
+ HTTParty.post(
+ url,
+ body: {
+ path: 'test-import',
+ file: file
+ }
+ )
+ end
+
+ RSpec.shared_examples 'for a project export archive' do
+ it { expect { subject }.to change { Project.count }.by(1) }
+
+ it { expect(subject.code).to eq(201) }
+ end
+
+ it_behaves_like 'handling file uploads', 'for a project export archive'
+end
diff --git a/spec/features/file_uploads/user_avatar_spec.rb b/spec/features/file_uploads/user_avatar_spec.rb
new file mode 100644
index 00000000000..043115be61a
--- /dev/null
+++ b/spec/features/file_uploads/user_avatar_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Upload a user avatar', :js do
+ let_it_be(:user, reload: true) { create(:user) }
+ let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
+
+ before do
+ sign_in(user)
+ visit(profile_path)
+ attach_file('user_avatar-trigger', file.path, make_visible: true)
+ click_button 'Set new profile picture'
+ end
+
+ subject do
+ click_button 'Update profile settings'
+ end
+
+ RSpec.shared_examples 'for a user avatar' do
+ it 'uploads successfully' do
+ expect(user.avatar.file).to eq nil
+ subject
+
+ expect(page).to have_content 'Profile was successfully updated'
+ expect(user.reload.avatar.file).to be_present
+ expect(user.avatar).to be_instance_of AvatarUploader
+ expect(current_path).to eq(profile_path)
+ end
+ end
+
+ it_behaves_like 'handling file uploads', 'for a user avatar'
+end
diff --git a/spec/features/groups/board_sidebar_spec.rb b/spec/features/groups/board_sidebar_spec.rb
index 3bbeed10948..690d661ba2f 100644
--- a/spec/features/groups/board_sidebar_spec.rb
+++ b/spec/features/groups/board_sidebar_spec.rb
@@ -19,6 +19,8 @@ RSpec.describe 'Group Issue Boards', :js do
let(:card) { find('.board:nth-child(1)').first('.board-card') }
before do
+ # stubbing until sidebar work is done: https://gitlab.com/gitlab-org/gitlab/-/issues/230711
+ stub_feature_flags(graphql_board_lists: false)
sign_in(user)
visit group_board_path(group, board)
diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb
index c6e5da92160..90253451d6b 100644
--- a/spec/features/groups/clusters/user_spec.rb
+++ b/spec/features/groups/clusters/user_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'User Cluster', :js do
visit group_clusters_path(group)
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
end
context 'when user filled form with valid parameters' do
diff --git a/spec/features/groups/import_export/import_file_spec.rb b/spec/features/groups/import_export/import_file_spec.rb
index ee4f2740f9f..f117b5d56e9 100644
--- a/spec/features/groups/import_export/import_file_spec.rb
+++ b/spec/features/groups/import_export/import_file_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe 'Import/Export - Group Import', :js do
fill_in :group_name, with: group_name
find('#import-group-tab').click
- expect(page).to have_content 'GitLab group export'
+ expect(page).to have_content 'Import a GitLab group export file'
attach_file(file) do
find('.js-filepicker-button').click
end
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index 643c8407578..d667690af29 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -10,6 +10,8 @@ RSpec.describe 'Groups > Members > Filter members' do
let(:nested_group) { create(:group, parent: group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
group.add_owner(user)
group.add_maintainer(user_with_2fa)
nested_group.add_maintainer(nested_group_user)
diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb
index fecc90f20c7..9eb5cc15c5e 100644
--- a/spec/features/groups/members/leave_group_spec.rb
+++ b/spec/features/groups/members/leave_group_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Leave group' do
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
gitlab_sign_in(user)
end
diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb
index 415c6927320..bcec2b50a24 100644
--- a/spec/features/groups/members/list_members_spec.rb
+++ b/spec/features/groups/members/list_members_spec.rb
@@ -12,6 +12,8 @@ RSpec.describe 'Groups > Members > List members' do
let(:nested_group) { create(:group, parent: group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(user1)
end
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index faf455e4ed9..e3bbbd4d73b 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
let(:shared_group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
shared_group.add_owner(user)
sign_in(user)
end
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 0267bea2f53..aedb7c170f8 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage members' do
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(user1)
end
diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
index f80925186ed..d94cc85f411 100644
--- a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
group.add_owner(user1)
sign_in(user1)
end
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
index 71c9b280ebe..44fd7380b79 100644
--- a/spec/features/groups/members/master_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe 'Groups > Members > Maintainer manages access requests' do
+ before do
+ stub_feature_flags(vue_group_members_list: false)
+ end
+
it_behaves_like 'Maintainer manages access requests' do
let(:has_tabs) { true }
let(:entity) { create(:group, :public) }
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index ad4f5c0b579..a95b59cece1 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -14,6 +14,8 @@ RSpec.describe 'Search group member' do
end
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(user)
visit group_group_members_path(guest_group)
end
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index cfc0e421aeb..d940550b18a 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Sort members' do
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago)
create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago)
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 2217bd9d6b5..3ae9a2b7555 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Group milestones' do
let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
around do |example|
- Timecop.freeze { example.run }
+ freeze_time { example.run }
end
before do
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index 6803b3a5785..60f1c404e78 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Group navbar' do
include NavbarStructureHelper
+ include WikiHelpers
include_context 'group navbar structure'
@@ -32,6 +33,7 @@ RSpec.describe 'Group navbar' do
nav_item: _('Merge Requests'),
nav_sub_items: []
},
+ (push_rules_nav_item if Gitlab.ee?),
{
nav_item: _('Kubernetes'),
nav_sub_items: []
@@ -47,9 +49,8 @@ RSpec.describe 'Group navbar' do
before do
insert_package_nav(_('Kubernetes'))
- stub_feature_flags(group_push_rules: false)
stub_feature_flags(group_iterations: false)
- stub_feature_flags(group_wiki: false)
+ stub_group_wikis(false)
group.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
index 9c359e932d5..cfd0c7e210f 100644
--- a/spec/features/import/manifest_import_spec.rb
+++ b/spec/features/import/manifest_import_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'Import multiple repositories by uploading a manifest file', :js
attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
click_on 'List available repositories'
- expect(page).to have_button('Import all repositories')
+ expect(page).to have_button('Import 660 repositories')
expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
end
diff --git a/spec/features/instance_statistics/cohorts_spec.rb b/spec/features/instance_statistics/cohorts_spec.rb
deleted file mode 100644
index 1f112e1831c..00000000000
--- a/spec/features/instance_statistics/cohorts_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Cohorts page' do
- before do
- sign_in(create(:admin))
-
- stub_application_setting(usage_ping_enabled: true)
- end
-
- it 'See users count per month' do
- create_list(:user, 2)
-
- visit instance_statistics_cohorts_path
-
- expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
- end
-end
diff --git a/spec/features/instance_statistics/instance_statistics_spec.rb b/spec/features/instance_statistics/instance_statistics_spec.rb
deleted file mode 100644
index 7695bf7874b..00000000000
--- a/spec/features/instance_statistics/instance_statistics_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Cohorts page', :js do
- before do
- sign_in(create(:admin))
- end
-
- it 'hides cohorts nav button when usage ping is disabled' do
- stub_application_setting(usage_ping_enabled: false)
-
- visit instance_statistics_root_path
-
- expect(find('.nav-sidebar')).not_to have_content('Cohorts')
- end
-
- it 'shows cohorts nav button when usage ping is enabled' do
- stub_application_setting(usage_ping_enabled: true)
-
- visit instance_statistics_root_path
-
- expect(find('.nav-sidebar')).to have_content('Cohorts')
- end
-end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index e7bcd7876ea..3954de56eea 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Invites', :aggregate_failures do
+RSpec.describe 'Group or Project invitations', :aggregate_failures do
let(:user) { create(:user, email: 'user@example.com') }
let(:owner) { create(:user, name: 'John Doe') }
let(:group) { create(:group, name: 'Owned') }
@@ -26,7 +26,6 @@ RSpec.describe 'Invites', :aggregate_failures do
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
@@ -38,6 +37,11 @@ RSpec.describe 'Invites', :aggregate_failures do
click_button 'Sign in'
end
+ def fill_in_welcome_form
+ select 'Software Developer', from: 'user_role'
+ click_button 'Get started!'
+ end
+
context 'when signed out' do
before do
visit invite_path(group_invite.raw_invite_token)
@@ -94,6 +98,7 @@ RSpec.describe 'Invites', :aggregate_failures 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)
+ fill_in_welcome_form
expect(current_path).to eq(dashboard_projects_path)
expect(page).to have_content(project.full_name)
@@ -108,6 +113,7 @@ RSpec.describe 'Invites', :aggregate_failures do
it 'signs up and redirects to the invitation page' do
fill_in_sign_up_form(new_user)
+ fill_in_welcome_form
expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
end
@@ -126,6 +132,7 @@ RSpec.describe 'Invites', :aggregate_failures do
fill_in_sign_up_form(new_user)
confirm_email(new_user)
fill_in_sign_in_form(new_user)
+ fill_in_welcome_form
expect(current_path).to eq(root_path)
expect(page).to have_content(project.full_name)
@@ -143,6 +150,7 @@ RSpec.describe 'Invites', :aggregate_failures 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)
+ fill_in_welcome_form
confirm_email(new_user)
expect(current_path).to eq(root_path)
@@ -156,6 +164,7 @@ RSpec.describe 'Invites', :aggregate_failures do
it "doesn't accept invitations until the user confirms their email" do
fill_in_sign_up_form(new_user)
+ fill_in_welcome_form
sign_in(owner)
visit project_project_members_path(project)
@@ -175,6 +184,7 @@ RSpec.describe 'Invites', :aggregate_failures do
fill_in_sign_up_form(new_user)
confirm_email(new_user)
fill_in_sign_in_form(new_user)
+ fill_in_welcome_form
expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
end
@@ -188,6 +198,7 @@ RSpec.describe 'Invites', :aggregate_failures do
it 'signs up and redirects to the invitation page' do
fill_in_sign_up_form(new_user)
+ fill_in_welcome_form
expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
end
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
index f442b25f593..5ea89a7984f 100644
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Issuables Close/Reopen/Report toggle' do
+ include IssuablesHelper
+
let(:user) { create(:user) }
shared_examples 'an issuable close/reopen/report toggle' do
@@ -27,19 +29,11 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
expect(container).not_to have_selector('.reopen-item')
end
- it 'changes the button when an item is selected' do
- button = container.find('.issuable-close-button')
-
- container.find('.dropdown-toggle').click
- container.find('.report-item').click
-
- expect(container).not_to have_selector('.dropdown-menu')
- expect(button).to have_content('Report abuse')
-
+ it 'links to Report Abuse' do
container.find('.dropdown-toggle').click
- container.find('.close-item').click
+ container.find('.report-abuse-link').click
- expect(button).to have_content("Close #{human_model_name}")
+ expect(page).to have_content('Report abuse to admin')
end
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 7790d8f1c4c..b1ffaaa7c7e 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -51,8 +51,8 @@ RSpec.describe 'issuable list', :js do
it "counts merge requests closing issues icons for each issue" do
visit_issuable_list(:issue)
- expect(page).to have_selector('.icon-merge-request-unmerged', count: 1)
- expect(first('.icon-merge-request-unmerged').find(:xpath, '..')).to have_content(1)
+ expect(page).to have_selector('[data-testid="merge-requests"]', count: 1)
+ expect(first('[data-testid="merge-requests"]').find(:xpath, '..')).to have_content(1)
end
def visit_issuable_list(issuable_type)
diff --git a/spec/features/issuables/related_issues_spec.rb b/spec/features/issuables/related_issues_spec.rb
new file mode 100644
index 00000000000..837859bbe26
--- /dev/null
+++ b/spec/features/issuables/related_issues_spec.rb
@@ -0,0 +1,400 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Related issues', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:project_b) { create(:project_empty_repo, :public) }
+ let(:project_unauthorized) { create(:project_empty_repo, :public) }
+ let(:issue_a) { create(:issue, project: project) }
+ let(:issue_b) { create(:issue, project: project) }
+ let(:issue_c) { create(:issue, project: project) }
+ let(:issue_d) { create(:issue, project: project) }
+ let(:issue_project_b_a) { create(:issue, project: project_b) }
+ let(:issue_project_unauthorized_a) { create(:issue, project: project_unauthorized) }
+
+ context 'widget visibility' do
+ context 'when not logged in' do
+ it 'does not show widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.related-issues-block')
+ end
+
+ it 'does not show widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.related-issues-block')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+
+ context 'when logged in but not a member' do
+ before do
+ gitlab_sign_in(user)
+ end
+
+ it 'shows widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'does not show widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.related-issues-block')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget on their own public issue' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project, author: user
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+
+ context 'when logged in and a guest' do
+ before do
+ gitlab_sign_in(user)
+ end
+
+ it 'shows widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+ project.add_guest(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+ project.add_guest(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+ project.add_guest(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+
+ context 'when logged in and a reporter' do
+ before do
+ gitlab_sign_in(user)
+ end
+
+ it 'shows widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget on their own public issue' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project, author: user
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+ end
+
+ context 'when user has no permission to manage related issues' do
+ let!(:issue_link_b) { create :issue_link, source: issue_a, target: issue_b }
+ let!(:issue_link_c) { create :issue_link, source: issue_a, target: issue_c }
+
+ before do
+ project.add_guest(user)
+ gitlab_sign_in(user)
+ end
+
+ context 'visiting some issue someone else created' do
+ before do
+ visit project_issue_path(project, issue_a)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+ end
+
+ context 'visiting issue_b which was targeted by issue_a' do
+ before do
+ visit project_issue_path(project, issue_b)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+ end
+ end
+
+ context 'when user has permission to manage related issues' do
+ before do
+ project.add_maintainer(user)
+ project_b.add_maintainer(user)
+ gitlab_sign_in(user)
+ end
+
+ context 'without existing related issues' do
+ before do
+ visit project_issue_path(project, issue_a)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('0')
+ end
+
+ it 'add related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_b.to_reference(project)} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ # Form gets hidden after submission
+ expect(page).not_to have_selector('.js-add-related-issues-form-area')
+ # Check if related issues are present
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+
+ it 'add cross-project related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq(issue_project_b_a.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+
+ it 'pressing enter should submit the form' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} "
+ find('.js-add-issuable-form-input').native.send_key(:enter)
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq(issue_project_b_a.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+
+ it 'disallows duplicate entries' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set 'duplicate duplicate duplicate'
+
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq('duplicate')
+
+ # Pending issues aren't counted towards the related issue count
+ expect(find('.js-related-issues-header-issue-count')).to have_content('0')
+ end
+
+ it 'allows us to remove pending issues' do
+ # Tests against https://gitlab.com/gitlab-org/gitlab/issues/11625
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set 'issue1 issue2 issue3 '
+
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(3)
+ expect(items[0].text).to eq('issue1')
+ expect(items[1].text).to eq('issue2')
+ expect(items[2].text).to eq('issue3')
+
+ # Remove pending issues left to right to make sure none get stuck
+ items[0].find('.js-issue-token-remove-button').click
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq('issue2')
+ expect(items[1].text).to eq('issue3')
+
+ items[0].find('.js-issue-token-remove-button').click
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq('issue3')
+
+ items[0].find('.js-issue-token-remove-button').click
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(0)
+ end
+ end
+
+ context 'with existing related issues' do
+ let!(:issue_link_b) { create :issue_link, source: issue_a, target: issue_b }
+ let!(:issue_link_c) { create :issue_link, source: issue_a, target: issue_c }
+
+ before do
+ visit project_issue_path(project, issue_a)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+
+ it 'shows related issues' do
+ items = all('.item-title a')
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ end
+
+ it 'allows us to remove a related issues' do
+ items_before = all('.item-title a')
+
+ expect(items_before.count).to eq(2)
+
+ first('.js-issue-item-remove-button').click
+
+ wait_for_requests
+
+ items_after = all('.item-title a')
+
+ expect(items_after.count).to eq(1)
+ end
+
+ it 'add related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "##{issue_d.iid} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(3)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ expect(items[2].text).to eq(issue_d.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('3')
+ end
+
+ it 'add invalid related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#9999999 "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+
+ it 'add unauthorized related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_project_unauthorized_a.to_reference(project)} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+ end
+ end
+end
diff --git a/spec/features/issuables/sorting_list_spec.rb b/spec/features/issuables/sorting_list_spec.rb
index ff92fe369d4..d065e96885c 100644
--- a/spec/features/issuables/sorting_list_spec.rb
+++ b/spec/features/issuables/sorting_list_spec.rb
@@ -10,10 +10,6 @@ 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
@@ -147,7 +143,7 @@ RSpec.describe 'Sort Issuable List' do
let(:issuable_type) { :issue }
it 'is "created date"' do
- visit_issues_with_state(project, 'open')
+ visit_issues_with_state(project, 'opened')
expect(find('.filter-dropdown-container')).to have_content('Created date')
expect(first_issue).to include(last_created_issuable.title)
@@ -179,11 +175,11 @@ RSpec.describe 'Sort Issuable List' do
end
end
- context 'when the sort in the URL is id_desc' do
+ context 'when the sort in the URL is created_date', :js do
let(:issuable_type) { :issue }
before do
- visit_issues(project, sort: 'id_desc')
+ visit_issues(project, sort: 'created_date')
end
it 'shows the sort order as created date' do
@@ -194,11 +190,11 @@ RSpec.describe 'Sort Issuable List' do
end
end
- context 'custom sorting' do
+ context 'custom sorting', :js do
let(:issuable_type) { :issue }
it 'supports sorting in asc and desc order' do
- visit_issues_with_state(project, 'open')
+ visit_issues_with_state(project, 'opened')
page.within('.filter-dropdown-container') do
click_button('Created date')
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 3757985f99c..0165fba9ace 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -763,6 +763,29 @@ RSpec.describe 'GFM autocomplete', :js do
end
end
+ shared_examples 'autocomplete suggestions' do
+ it 'suggests objects correctly' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys(object.class.reference_prefix)
+ end
+
+ page.within '.tribute-container' do
+ expect(page).to have_content(object.title)
+
+ find('ul li').click
+ end
+
+ expect(find('.new-note #note-body').value).to include(expected_body)
+ end
+ end
+
+ context 'merge requests' do
+ let(:object) { create(:merge_request, source_project: project) }
+ let(:expected_body) { object.to_reference }
+
+ it_behaves_like 'autocomplete suggestions'
+ end
+
context 'when other notes are destroyed' do
let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
diff --git a/spec/features/issues/group_label_sidebar_spec.rb b/spec/features/issues/group_label_sidebar_spec.rb
index e6a173f4589..8150f9c6faf 100644
--- a/spec/features/issues/group_label_sidebar_spec.rb
+++ b/spec/features/issues/group_label_sidebar_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Group label on issue' do
- it 'renders link to the project issues page' do
+ it 'renders link to the project issues page', :js do
group = create(:group)
project = create(:project, :public, namespace: group)
feature = create(:group_label, group: group, title: 'feature')
@@ -14,6 +14,6 @@ RSpec.describe 'Group label on issue' do
link = find('.issuable-show-labels a')
- expect(link[:href]).to eq(label_link)
+ expect(CGI.unescape(link[:href])).to include(CGI.unescape(label_link))
end
end
diff --git a/spec/features/issues/incident_issue_spec.rb b/spec/features/issues/incident_issue_spec.rb
new file mode 100644
index 00000000000..d004ee85dd8
--- /dev/null
+++ b/spec/features/issues/incident_issue_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Incident Detail', :js do
+ context 'when user displays the incident' do
+ it 'shows the incident tabs' do
+ project = create(:project, :public)
+ incident = create(:incident, project: project, description: 'hello')
+
+ visit project_issue_path(project, incident)
+ wait_for_requests
+
+ page.within('.issuable-details') do
+ incident_tabs = find('[data-testid="incident-tabs"]')
+
+ expect(find('h2')).to have_content(incident.title)
+ expect(incident_tabs).to have_content('Summary')
+ expect(incident_tabs).to have_content(incident.description)
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index 9879703c8bf..1c8da227412 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -20,6 +20,19 @@ RSpec.describe 'Issue Detail', :js do
end
end
+ context 'when user displays the issue as an incident' do
+ let(:issue) { create(:incident, project: project, author: user) }
+
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'does not show design management' do
+ expect(page).not_to have_selector('.js-design-management')
+ end
+ end
+
context 'when issue description has xss snippet' do
before do
issue.update!(description: '![xss" onload=alert(1);//](a)')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index ecda80f2483..38d11ee2560 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -168,7 +168,7 @@ RSpec.describe 'Issue Sidebar' do
it 'escapes XSS when viewing issue labels' do
page.within('.block.labels') do
- find('.edit-link').click
+ click_on 'Edit'
expect(page).to have_content '<script>alert("xss");</script>'
end
@@ -179,7 +179,7 @@ RSpec.describe 'Issue Sidebar' do
before do
issue.update(labels: [label])
page.within('.block.labels') do
- find('.edit-link').click
+ click_on 'Edit'
end
end
@@ -286,7 +286,7 @@ RSpec.describe 'Issue Sidebar' do
end
it 'does not have a option to edit labels' do
- expect(page).not_to have_selector('.block.labels .edit-link')
+ expect(page).not_to have_selector('.block.labels .js-sidebar-dropdown-toggle')
end
context 'interacting with collapsed sidebar', :js do
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index aab9d1026f9..6dc1cbfb2d7 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -36,6 +36,6 @@ RSpec.describe 'Issue markdown toolbar', :js do
all('.toolbar-btn')[1].click
- expect(find('#note-body')[:value]).to eq("test\n*underline*\n")
+ expect(find('#note-body')[:value]).to eq("test\n_underline_\n")
end
end
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
index f2c978c525e..8faec85f3df 100644
--- a/spec/features/issues/resource_label_events_spec.rb
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -35,12 +35,12 @@ RSpec.describe 'List issue resource label events', :js do
context 'when user adds label to the issue' do
def toggle_labels(labels)
page.within '.labels' do
- click_link 'Edit'
+ click_on 'Edit'
wait_for_requests
labels.each { |label| click_link label }
- click_link 'Edit'
+ click_on 'Edit'
wait_for_requests
end
end
diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb
index 2912ac33625..1512d539dec 100644
--- a/spec/features/issues/service_desk_spec.rb
+++ b/spec/features/issues/service_desk_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
let(:user) { create(:user) }
before do
+ # The following two conditions equate to Gitlab::ServiceDesk.supported == true
allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
@@ -27,53 +28,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
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 service desk is supported' do
context 'when there are no issues' do
describe 'service desk info content' do
it 'displays the large info box, documentation, and the address' do
@@ -81,6 +36,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
aggregate_failures do
expect(page).to have_css('.empty-state')
+ expect(page).to have_text('Use Service Desk to connect with your users')
expect(page).to have_link('Read more', href: help_page_path('user/project/service_desk'))
expect(page).not_to have_link('Turn on Service Desk')
expect(page).to have_content(project.service_desk_address)
@@ -99,6 +55,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
it 'displays the large info box and the documentation link' do
aggregate_failures do
expect(page).to have_css('.empty-state')
+ expect(page).to have_text('Use Service Desk to connect with your users')
expect(page).to have_link('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)
@@ -152,6 +109,55 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
find('.input-token .filtered-search').native.send_key(:backspace)
expect(page).to have_selector('.js-visual-token', count: 1)
end
+
+ it 'support bot author token has been properly added' do
+ within('.filtered-search-token') do
+ expect(page).to have_selector('.name', count: 1, visible: false)
+ expect(page).to have_selector('.operator', count: 1, visible: false)
+ expect(page).to have_selector('.value-container', count: 1, visible: false)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when service desk is not supported' do
+ let(:project_without_service_desk) { create(:project, :private, service_desk_enabled: false) }
+
+ before do
+ allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(false)
+ visit service_desk_project_issues_path(project)
+ end
+
+ 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 'informs user to setup incoming email to turn on support for Service Desk' do
+ aggregate_failures do
+ expect(page).to have_css('.empty-state')
+ expect(page).to have_text('Service Desk is not supported')
+ expect(page).to have_text('In order to enable Service Desk for your instance, you must first set up incoming email.')
+ expect(page).to have_link('More information', href: help_page_path('administration/incoming_email', anchor: 'set-it-up'))
+ end
+ end
+ end
+
+ context 'when user does not have permission to edit project settings' do
+ before do
+ project_without_service_desk.add_developer(user)
+ visit service_desk_project_issues_path(project_without_service_desk)
+ end
+
+ it 'informs user to contact an administrator to enable service desk' do
+ expect(page).to have_css('.empty-state')
+ # NOTE: here, "enabled" is not used in the sense of "ServiceDesk::Enabled?"
+ expect(page).to have_text('Service Desk is not enabled')
+ expect(page).to have_text('For help setting up the Service Desk for your instance, please contact an administrator.')
+ end
end
end
end
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index a2c868d0256..668b4265948 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -61,6 +61,10 @@ RSpec.describe "User creates issue" do
.and have_content(project.name)
expect(page).to have_selector('strong', text: 'Description')
end
+
+ it 'does not render the issue type dropdown' do
+ expect(page).not_to have_selector('.s-issuable-type-filter-dropdown-wrap')
+ end
end
context "when signed in as developer", :js do
@@ -188,6 +192,50 @@ RSpec.describe "User creates issue" do
end
end
+ context 'form create handles issue creation by default' do
+ let(:project) { create(:project) }
+
+ before do
+ visit new_project_issue_path(project)
+ end
+
+ it 'pre-fills the issue type dropdown with issue type' do
+ expect(find('.js-issuable-type-filter-dropdown-wrap .dropdown-toggle-text')).to have_content('Issue')
+ end
+
+ it 'does not hide the milestone select' do
+ expect(page).to have_selector('.qa-issuable-milestone-dropdown')
+ end
+ end
+
+ context 'form create handles incident creation' do
+ let(:project) { create(:project) }
+
+ before do
+ visit new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' } })
+ end
+
+ it 'pre-fills the issue type dropdown with incident type' do
+ expect(find('.js-issuable-type-filter-dropdown-wrap .dropdown-toggle-text')).to have_content('Incident')
+ end
+
+ it 'hides the epic select' do
+ expect(page).not_to have_selector('.epic-dropdown-container')
+ end
+
+ it 'hides the milestone select' do
+ expect(page).not_to have_selector('.qa-issuable-milestone-dropdown')
+ end
+
+ it 'hides the weight input' do
+ expect(page).not_to have_selector('.qa-issuable-weight-input')
+ end
+
+ it 'shows the incident help text' do
+ expect(page).to have_text('A modified issue to guide the resolution of incidents.')
+ end
+ end
+
context 'suggestions', :js do
it 'displays list of related issues' do
issue = create(:issue, project: project)
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 39bf535c715..88b8e9624e2 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -4,13 +4,17 @@ require "spec_helper"
RSpec.describe "Issues > User edits issue", :js do
let_it_be(:project) { create(:project_empty_repo, :public) }
+ let_it_be(:project_with_milestones) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
+ let_it_be(:issue_with_milestones) { create(:issue, project: project_with_milestones, author: user, assignees: [user]) }
let_it_be(:label) { create(:label, project: project) }
let_it_be(:milestone) { create(:milestone, project: project) }
+ let_it_be(:milestones) { create_list(:milestone, 25, project: project_with_milestones) }
before do
project.add_developer(user)
+ project_with_milestones.add_developer(user)
sign_in(user)
end
@@ -91,11 +95,12 @@ RSpec.describe "Issues > User edits issue", :js do
describe 'update labels' do
it 'will not send ajax request when no data is changed' do
page.within '.labels' do
- click_link 'Edit'
+ click_on 'Edit'
- find('.dropdown-menu-close', match: :first).click
+ find('.dropdown-title button').click
expect(page).not_to have_selector('.block-loading')
+ expect(page).not_to have_selector('.gl-spinner')
end
end
end
@@ -148,9 +153,7 @@ RSpec.describe "Issues > User edits issue", :js do
visit project_issue_path(project, issue2)
page.within '.assignee' do
- page.within '.value .author' do
- expect(page).to have_content user.name
- end
+ expect(page).to have_content user.name
click_link 'Edit'
click_link user.name
@@ -218,6 +221,23 @@ RSpec.describe "Issues > User edits issue", :js do
end
end
end
+
+ it 'allows user to search milestone' do
+ visit project_issue_path(project_with_milestones, issue_with_milestones)
+
+ page.within('.milestone') do
+ click_link 'Edit'
+ wait_for_requests
+ # We need to enclose search string in quotes for exact match as all the milestone titles
+ # within tests are prefixed with `My title`.
+ find('.dropdown-input-field', visible: true).send_keys "\"#{milestones[0].title}\""
+ wait_for_requests
+
+ page.within '.dropdown-content' do
+ expect(page).to have_content milestones[0].title
+ end
+ end
+ end
end
context 'by unauthorized user' do
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index 35d9db68d32..7db72f2cd05 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -184,6 +184,31 @@ RSpec.describe 'User interacts with awards' do
wait_for_requests
end
+ context 'when the issue is locked' do
+ before do
+ create(:award_emoji, awardable: issue, name: '100')
+ issue.update!(discussion_locked: true)
+
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'hides the add award button' do
+ page.within('.awards') do
+ expect(page).not_to have_css('.js-add-award')
+ end
+ end
+
+ it 'does not allow toggling existing emoji' do
+ page.within('.awards') do
+ find('gl-emoji[data-name="100"]').click
+ end
+ wait_for_requests
+
+ expect(issue.reload.award_emoji.size).to eq(1)
+ end
+ end
+
it 'adds award to issue' do
first('.js-emoji-btn').click
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
index 2b610bab9f0..3f18764aa58 100644
--- a/spec/features/issues/user_views_issue_spec.rb
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -3,12 +3,16 @@
require "spec_helper"
RSpec.describe "User views issue" do
- let(:project) { create(:project_empty_repo, :public) }
- let(:user) { create(:user) }
- let(:issue) { create(:issue, project: project, description: "# Description header", author: user) }
+ let_it_be(:project) { create(:project_empty_repo, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:issue) { create(:issue, project: project, description: "# Description header", author: user) }
+ let_it_be(:note) { create(:note, noteable: issue, project: project, author: user) }
- before do
+ before_all do
project.add_developer(user)
+ end
+
+ before do
sign_in(user)
visit(project_issue_path(project, issue))
@@ -37,30 +41,30 @@ RSpec.describe "User views issue" do
context 'when showing status of the author of the issue' do
it_behaves_like 'showing user status' do
- let(:user_with_status) { issue.author }
+ let(:user_with_status) { user }
end
end
context 'when showing status of a user who commented on an issue', :js do
- let!(:note) { create(:note, noteable: issue, project: project, author: user_with_status) }
-
it_behaves_like 'showing user status' do
- let(:user_with_status) { create(:user) }
+ let(:user_with_status) { user }
end
end
context 'when status message has an emoji', :js do
- let(:message) { 'My status with an emoji' }
- let(:message_emoji) { 'basketball' }
-
- let!(:note) { create(:note, noteable: issue, project: project, author: user) }
- let!(:status) { create(:user_status, user: user, emoji: 'smirk', message: "#{message} :#{message_emoji}:") }
+ let_it_be(:message) { 'My status with an emoji' }
+ let_it_be(:message_emoji) { 'basketball' }
+ let_it_be(:status) { create(:user_status, user: user, emoji: 'smirk', message: "#{message} :#{message_emoji}:") }
it 'correctly renders the emoji' do
+ wait_for_requests
+
tooltip_span = page.first(".user-status-emoji[title^='#{message}']")
tooltip_span.hover
+ wait_for_requests
+
tooltip = page.find('.tooltip .tooltip-inner')
page.within(tooltip) do
diff --git a/spec/features/jira_connect/subscriptions_spec.rb b/spec/features/jira_connect/subscriptions_spec.rb
new file mode 100644
index 00000000000..9be6b7c67ee
--- /dev/null
+++ b/spec/features/jira_connect/subscriptions_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Subscriptions Content Security Policy' do
+ let(:installation) { create(:jira_connect_installation) }
+ let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test') }
+ let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) }
+
+ subject { response_headers['Content-Security-Policy'] }
+
+ context 'when there is no global config' do
+ before do
+ expect_next_instance_of(JiraConnect::SubscriptionsController) do |controller|
+ expect(controller).to receive(:current_content_security_policy)
+ .and_return(ActionDispatch::ContentSecurityPolicy.new)
+ end
+ end
+
+ it 'does not add CSP directives' do
+ visit jira_connect_subscriptions_path(jwt: jwt)
+
+ is_expected.to be_blank
+ end
+ end
+
+ context 'when a global CSP config exists' do
+ before do
+ csp = ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.script_src :self, 'https://some-cdn.test'
+ p.style_src :self, 'https://some-cdn.test'
+ end
+
+ expect_next_instance_of(JiraConnect::SubscriptionsController) do |controller|
+ expect(controller).to receive(:current_content_security_policy).and_return(csp)
+ end
+ end
+
+ it 'appends to CSP directives' do
+ visit jira_connect_subscriptions_path(jwt: jwt)
+
+ is_expected.to include("frame-ancestors 'self' https://*.atlassian.net")
+ is_expected.to include("script-src 'self' https://some-cdn.test https://connect-cdn.atl-paas.net https://unpkg.com/jquery@3.3.1/")
+ is_expected.to include("style-src 'self' https://some-cdn.test 'unsafe-inline' https://unpkg.com/@atlaskit/")
+ end
+ end
+end
diff --git a/spec/features/jira_oauth_provider_authorize_spec.rb b/spec/features/jira_oauth_provider_authorize_spec.rb
new file mode 100644
index 00000000000..daecae56101
--- /dev/null
+++ b/spec/features/jira_oauth_provider_authorize_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'JIRA OAuth Provider' do
+ describe 'JIRA DVCS OAuth Authorization' do
+ let(:application) { create(:oauth_application, redirect_uri: oauth_jira_callback_url, scopes: 'read_user') }
+
+ before do
+ sign_in(user)
+
+ visit oauth_jira_authorize_path(client_id: application.uid,
+ redirect_uri: oauth_jira_callback_url,
+ response_type: 'code',
+ state: 'my_state',
+ scope: 'read_user')
+ end
+
+ it_behaves_like 'Secure OAuth Authorizations'
+ end
+end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 1545cb36e9b..eed9a6d1043 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -42,12 +42,12 @@ RSpec.describe 'Labels Hierarchy', :js do
it 'does not find child group labels on dropdown' do
page.within('.block.labels') do
- find('.edit-link').click
- end
+ click_on 'Edit'
- wait_for_requests
+ wait_for_requests
- expect(page).not_to have_selector('.badge', text: child_group_label.title)
+ expect(page).not_to have_text(child_group_label.title)
+ end
end
end
@@ -296,6 +296,7 @@ RSpec.describe 'Labels Hierarchy', :js do
let(:board) { create(:board, group: parent) }
before do
+ stub_feature_flags(graphql_board_lists: false)
parent.add_developer(user)
visit group_board_path(parent, board)
find('.js-new-board-list').click
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index 57362ed2d54..fbf4e531db1 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -30,13 +30,11 @@ RSpec.describe 'Copy as GFM', :js do
it 'works', :aggregate_failures do
verify(
'nesting',
-
'> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**'
)
verify(
'a real world example from the gitlab-ce README',
-
<<~GFM
# GitLab
@@ -103,19 +101,16 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'InlineDiffFilter',
-
'{-Deleted text-}',
'{+Added text+}'
)
verify(
'TaskListFilter',
-
<<~GFM,
* [ ] Unchecked task
* [x] Checked task
GFM
-
<<~GFM
1. [ ] Unchecked ordered task
1. [x] Checked ordered task
@@ -124,7 +119,6 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'ReferenceFilter',
-
# issue reference
@feat.issue.to_reference,
# full issue reference
@@ -141,13 +135,11 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'AutolinkFilter',
-
'https://example.com'
)
verify(
'TableOfContentsFilter',
-
<<~GFM,
[[_TOC_]]
@@ -155,64 +147,53 @@ RSpec.describe 'Copy as GFM', :js do
## Heading 2
GFM
-
pipeline: :wiki,
wiki: @project.wiki
)
verify(
'EmojiFilter',
-
':thumbsup:'
)
verify(
'ImageLinkFilter',
-
'![Image](https://example.com/image.png)'
)
verify_media_with_partial_path(
'[test.txt](/uploads/a123/image.txt)',
-
project_media_uri(@project, '/uploads/a123/image.txt')
)
verify_media_with_partial_path(
'![Image](/uploads/a123/image.png)',
-
project_media_uri(@project, '/uploads/a123/image.png')
)
verify(
'VideoLinkFilter',
-
'![Video](https://example.com/video.mp4)'
)
verify_media_with_partial_path(
'![Video](/uploads/a123/video.mp4)',
-
project_media_uri(@project, '/uploads/a123/video.mp4')
)
verify(
'AudioLinkFilter',
-
'![Audio](https://example.com/audio.wav)'
)
verify_media_with_partial_path(
'![Audio](/uploads/a123/audio.wav)',
-
project_media_uri(@project, '/uploads/a123/audio.wav')
)
verify(
'MathFilter: math as converted from GFM to HTML',
-
'$`c = \pm\sqrt{a^2 + b^2}`$',
-
# math block
<<~GFM
```math
@@ -334,7 +315,6 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'MermaidFilter: mermaid as converted from GFM to HTML',
-
<<~GFM
```mermaid
graph TD;
@@ -429,7 +409,6 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'SuggestionFilter: suggestion as converted from GFM to HTML',
-
<<~GFM
```suggestion
New
@@ -491,7 +470,6 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'SanitizationFilter',
-
<<~GFM
<sub>sub</sub>
@@ -527,13 +505,11 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'SanitizationFilter',
-
<<~GFM,
```
Plain text
```
GFM
-
<<~GFM,
```ruby
def foo
@@ -541,7 +517,6 @@ RSpec.describe 'Copy as GFM', :js do
end
```
GFM
-
<<~GFM
Foo
@@ -553,27 +528,19 @@ RSpec.describe 'Copy as GFM', :js do
verify(
'MarkdownFilter',
-
"Line with two spaces at the end \nto insert a linebreak",
-
'`code`',
'`` code with ` ticks ``',
-
'> Quote',
-
# multiline quote
<<~GFM,
> Multiline Quote
>
> With multiple paragraphs
GFM
-
'![Image](https://example.com/image.png)',
-
'# Heading with no anchor link',
-
'[Link](https://example.com)',
-
<<~GFM,
* List item
* List item 2
@@ -598,7 +565,6 @@ RSpec.describe 'Copy as GFM', :js do
> Blockquote
GFM
-
<<~GFM,
1. Ordered list item
1. Ordered list item 2
@@ -623,22 +589,16 @@ RSpec.describe 'Copy as GFM', :js do
---
GFM
-
'# Heading',
'## Heading',
'### Heading',
'#### Heading',
'##### Heading',
'###### Heading',
-
'**Bold**',
-
'*Italics*',
-
'~~Strikethrough~~',
-
'---',
-
# table
<<~GFM,
| Centered | Right | Left |
@@ -696,9 +656,7 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as inline code' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line .no',
-
'`RuntimeError`',
-
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'
)
end
@@ -708,9 +666,7 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as inline code' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]',
-
'`raise RuntimeError, "System commands must be given as an array of strings"`',
-
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'
)
end
@@ -720,14 +676,12 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as a code block' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]',
-
<<~GFM,
```ruby
raise RuntimeError, "System commands must be given as an array of strings"
end
```
GFM
-
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'
)
end
@@ -755,7 +709,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as a code block' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]',
-
<<~GFM,
```ruby
unless cmd.is_a?(Array)
@@ -763,7 +716,6 @@ RSpec.describe 'Copy as GFM', :js do
end
```
GFM
-
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].left-side'
)
end
@@ -773,7 +725,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as a code block' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]',
-
<<~GFM,
```ruby
unless cmd.is_a?(Array)
@@ -781,7 +732,6 @@ RSpec.describe 'Copy as GFM', :js do
end
```
GFM
-
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].right-side'
)
end
@@ -799,7 +749,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as inline code' do
verify(
'.line[id="LC9"] .no',
-
'`RuntimeError`'
)
end
@@ -809,7 +758,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as inline code' do
verify(
'.line[id="LC9"]',
-
'`raise RuntimeError, "System commands must be given as an array of strings"`'
)
end
@@ -819,7 +767,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as a code block' do
verify(
'.line[id="LC9"], .line[id="LC10"]',
-
<<~GFM
```ruby
raise RuntimeError, "System commands must be given as an array of strings"
@@ -841,7 +788,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as inline code' do
verify(
'.line[id="LC27"] .nl',
-
'`"bio"`'
)
end
@@ -851,7 +797,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as inline code' do
verify(
'.line[id="LC27"]',
-
'`"bio": null,`'
)
end
@@ -861,7 +806,6 @@ RSpec.describe 'Copy as GFM', :js do
it 'copies as a code block with the correct language' do
verify(
'.line[id="LC27"], .line[id="LC28"]',
-
<<~GFM
```json
"bio": null,
diff --git a/spec/features/markdown/keyboard_shortcuts_spec.rb b/spec/features/markdown/keyboard_shortcuts_spec.rb
new file mode 100644
index 00000000000..81b1928658c
--- /dev/null
+++ b/spec/features/markdown/keyboard_shortcuts_spec.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Markdown keyboard shortcuts', :js do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:is_mac) { page.evaluate_script('navigator.platform').include?('Mac') }
+ let(:modifier_key) { is_mac ? :command : :control }
+ let(:other_modifier_key) { is_mac ? :control : :command }
+
+ before do
+ project.add_developer(user)
+
+ gitlab_sign_in(user)
+
+ visit path_to_visit
+
+ wait_for_requests
+ end
+
+ shared_examples 'keyboard shortcuts' do
+ it 'bolds text when <modifier>+B is pressed' do
+ type_and_select('bold')
+
+ markdown_field.send_keys([modifier_key, 'b'])
+
+ expect(markdown_field.value).to eq('**bold**')
+ end
+
+ it 'italicizes text when <modifier>+I is pressed' do
+ type_and_select('italic')
+
+ markdown_field.send_keys([modifier_key, 'i'])
+
+ expect(markdown_field.value).to eq('_italic_')
+ end
+
+ it 'links text when <modifier>+K is pressed' do
+ type_and_select('link')
+
+ markdown_field.send_keys([modifier_key, 'k'])
+
+ expect(markdown_field.value).to eq('[link](url)')
+
+ # Type some more text to ensure the cursor
+ # and selection are set correctly
+ markdown_field.send_keys('https://example.com')
+
+ expect(markdown_field.value).to eq('[link](https://example.com)')
+ end
+
+ it 'does not affect non-markdown fields on the same page' do
+ non_markdown_field.send_keys('some text')
+
+ non_markdown_field.send_keys([modifier_key, 'b'])
+
+ expect(focused_element).to eq(non_markdown_field.native)
+ expect(markdown_field.value).to eq('')
+ end
+ end
+
+ shared_examples 'no side effects' do
+ it 'does not bold text when <other modifier>+B is pressed' do
+ type_and_select('bold')
+
+ markdown_field.send_keys([@other_modifier_key, 'b'])
+
+ expect(markdown_field.value).not_to eq('**bold**')
+ end
+
+ it 'does not italicize text when <other modifier>+I is pressed' do
+ type_and_select('italic')
+
+ markdown_field.send_keys([@other_modifier_key, 'i'])
+
+ expect(markdown_field.value).not_to eq('_italic_')
+ end
+
+ it 'does not link text when <other modifier>+K is pressed' do
+ type_and_select('link')
+
+ markdown_field.send_keys([@other_modifier_key, 'k'])
+
+ expect(markdown_field.value).not_to eq('[link](url)')
+ end
+ end
+
+ context 'Vue.js markdown editor' do
+ let(:path_to_visit) { new_project_release_path(project) }
+ let(:markdown_field) { find_field('Release notes') }
+ let(:non_markdown_field) { find_field('Release title') }
+
+ it_behaves_like 'keyboard shortcuts'
+ it_behaves_like 'no side effects'
+ end
+
+ context 'Haml markdown editor' do
+ let(:path_to_visit) { new_project_issue_path(project) }
+ let(:markdown_field) { find_field('Description') }
+ let(:non_markdown_field) { find_field('Title') }
+
+ it_behaves_like 'keyboard shortcuts'
+ it_behaves_like 'no side effects'
+ end
+
+ def type_and_select(text)
+ markdown_field.send_keys(text)
+
+ text.length.times do
+ markdown_field.send_keys([:shift, :arrow_left])
+ end
+ end
+
+ def focused_element
+ page.driver.browser.switch_to.active_element
+ end
+end
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index d9d3f566bce..151ef76e884 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -247,6 +247,7 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
expect(doc).to reference_commits
expect(doc).to reference_labels
expect(doc).to reference_milestones
+ expect(doc).to reference_alerts
end
aggregate_failures 'TaskListFilter' do
diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb
index 256dfdc26e9..bdb549326fa 100644
--- a/spec/features/markdown/mermaid_spec.rb
+++ b/spec/features/markdown/mermaid_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe 'Mermaid rendering', :js do
expect(page.html.scan(expected).count).to be(4)
end
- it 'renders only 2 Mermaid blocks and ', :js do
+ it 'renders only 2 Mermaid blocks and ', :js, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/234081' } do
description = <<~MERMAID
```mermaid
graph LR
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index 60671213d75..40f6482c948 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -20,8 +20,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
context 'Feature is enabled' do
before do
- stub_feature_flags(diffs_batch_load: false)
-
visit_diffs
end
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index be12f774c29..0e65cb358da 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -20,8 +20,6 @@ RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork
end
before do
- stub_feature_flags(single_mr_diff_view: false)
-
target_project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/merge_request/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index d7c9c8bddb1..3d18aef9327 100644
--- a/spec/features/merge_request/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
@@ -12,12 +12,38 @@ RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inli
sign_in(user)
end
- it 'presents merged merge request content' do
- visit(merge_request_path(merge_request))
+ context 'presents merged merge request content' do
+ it 'when merge method is set to merge commit' do
+ visit(merge_request_path(merge_request))
+
+ click_button('Merge')
+
+ expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
+ end
+
+ context 'when merge method is set to fast-forward merge' do
+ let(:project) { create(:project, :public, :repository, merge_requests_ff_only_enabled: true) }
+
+ it 'accepts a merge request with rebase and merge' do
+ merge_request = create(:merge_request, :rebased, source_project: project)
+
+ visit(merge_request_path(merge_request))
- click_button('Merge')
+ click_button('Merge')
- expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merge_commit_sha}")
+ expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
+ end
+
+ it 'accepts a merge request with squash and merge' do
+ merge_request = create(:merge_request, :rebased, source_project: project, squash: true)
+
+ visit(merge_request_path(merge_request))
+
+ click_button('Merge')
+
+ expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merged_commit_sha}")
+ end
+ end
end
context 'with removing the source branch' do
diff --git a/spec/features/merge_request/user_assigns_themselves_spec.rb b/spec/features/merge_request/user_assigns_themselves_spec.rb
index b6cd97dcc5a..04d401683bf 100644
--- a/spec/features/merge_request/user_assigns_themselves_spec.rb
+++ b/spec/features/merge_request/user_assigns_themselves_spec.rb
@@ -21,6 +21,15 @@ RSpec.describe 'Merge request > User assigns themselves' do
expect(page).to have_content '2 issues have been assigned to you'
end
+ it 'updates updated_by', :js do
+ expect do
+ click_button 'assign yourself'
+
+ expect(find('.assignee')).to have_content(user.name)
+ wait_for_all_requests
+ end.to change { merge_request.reload.updated_at }
+ end
+
it 'returns user to the merge request', :js do
click_link 'Assign yourself to these issues'
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
index 2c949ed84f4..397ca70f4a1 100644
--- a/spec/features/merge_request/user_edits_mr_spec.rb
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -20,4 +20,32 @@ RSpec.describe 'Merge request > User edits MR' do
include_context 'merge request edit context'
it_behaves_like 'an editable merge request'
end
+
+ context 'when merge_request_reviewers is turned on' do
+ before do
+ stub_feature_flags(merge_request_reviewers: true)
+ end
+
+ context 'non-fork merge request' do
+ include_context 'merge request edit context'
+ it_behaves_like 'an editable merge request with reviewers'
+ end
+
+ context 'for a forked project' do
+ let(:source_project) { fork_project(target_project, nil, repository: true) }
+
+ include_context 'merge request edit context'
+ it_behaves_like 'an editable merge request with reviewers'
+ end
+ end
+
+ context 'when merge_request_reviewers is turned off' do
+ before do
+ stub_feature_flags(merge_request_reviewers: false)
+ end
+
+ it 'does not render reviewers dropdown' do
+ expect(page).not_to have_selector('.js-reviewer-search')
+ end
+ end
end
diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb
index d3867a91846..0e39cce13a1 100644
--- a/spec/features/merge_request/user_expands_diff_spec.rb
+++ b/spec/features/merge_request/user_expands_diff_spec.rb
@@ -7,23 +7,18 @@ RSpec.describe 'User expands diff', :js do
let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) }
before do
- stub_feature_flags(diffs_batch_load: false)
-
- allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
- allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
-
visit(diffs_project_merge_request_path(project, merge_request))
wait_for_requests
end
it 'allows user to expand diff' do
- page.within find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9"]') do
- click_link 'Click to expand it.'
+ page.within find('[id="19763941ab80e8c09871c0a425f0560d9053bcb3"]') do
+ find('[data-testid="expandButton"]').click
wait_for_requests
- expect(page).not_to have_content('Click to expand it.')
+ expect(page).not_to have_content('Expand File')
expect(page).to have_selector('.code')
end
end
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index 1a7baff2fb1..782a7e3bfb6 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -10,9 +10,6 @@ RSpec.describe 'Batch diffs', :js do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') }
before do
- stub_feature_flags(single_mr_diff_view: project)
- stub_feature_flags(diffs_batch_load: true)
-
sign_in(project.owner)
visit diffs_project_merge_request_path(merge_request.project, merge_request)
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index 4c079b98c90..489582521b5 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe 'Merge request > User posts notes', :js do
it 'shows a reply button' do
reply_button = find('.js-reply-button', match: :first)
- expect(reply_button).to have_selector('.ic-comment')
+ expect(reply_button).to have_selector('[data-testid="comment-icon"]')
end
it 'shows reply placeholder when clicking reply button' do
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index f2adfd21e49..cd06886169d 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -15,10 +15,6 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
diff_refs: merge_request.diff_refs)
end
- before do
- stub_feature_flags(diffs_batch_load: false)
- end
-
context 'no threads' do
before do
project.add_maintainer(user)
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index 7fad805866b..d15d5b3bc73 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -20,7 +20,6 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) }
before do
- stub_feature_flags(diffs_batch_load: false)
project.add_maintainer(user)
sign_in user
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index d067fc0ada4..7a3a14e61e3 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -9,10 +9,6 @@ RSpec.describe 'Merge request > User sees diff', :js do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
- before do
- stub_feature_flags(diffs_batch_load: false)
- end
-
context 'when linking to note' do
describe 'with unresolved note' do
let(:note) { create :diff_note_on_merge_request, project: project, noteable: merge_request }
@@ -72,7 +68,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 do
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request)
@@ -97,7 +93,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
let c = 3;
let d = 3;
}
- CONTENT
+ CONTENT
new_file_content =
<<~CONTENT
@@ -107,7 +103,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
let c = 3;
let x = 3;
}
- CONTENT
+ CONTENT
file_name = 'xss_file.rs'
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 c7d26dfc814..93fea44707c 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -127,8 +127,10 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
end
end
- context 'when merge request is in the blocked pipeline state' do
+ context 'when merge request is in the blocked pipeline state and pipeline must succeed' do
before do
+ project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
+
create(
:ci_pipeline,
project: project,
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index 5d41e49c478..8e15ba6cf8d 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -123,6 +123,10 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
context 'when actor is a developer in parent project' do
let(:actor) { developer_in_parent }
+ before do
+ stub_feature_flags(ci_disallow_to_create_merge_request_pipelines_in_target_project: false)
+ end
+
it 'creates a pipeline in the parent project when user proceeds with the warning' do
visit project_merge_request_path(parent_project, merge_request)
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 60e054ddbee..fb616ceae9d 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -17,8 +17,6 @@ RSpec.describe 'Merge request > User sees versions', :js do
let!(:params) { {} }
before do
- stub_feature_flags(diffs_batch_load: false)
-
project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request, params)
diff --git a/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb b/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb
index 20a5910e66d..1748f66c934 100644
--- a/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb
+++ b/spec/features/merge_request/user_views_auto_expanding_diff_spec.rb
@@ -11,9 +11,6 @@ RSpec.describe 'User views diffs file-by-file', :js do
let(:user) { create(:user, view_diffs_file_by_file: true) }
before do
- allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
- allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
-
project.add_developer(user)
sign_in(user)
@@ -27,9 +24,10 @@ RSpec.describe 'User views diffs file-by-file', :js do
page.within('#diffs') do
expect(page).not_to have_content('This diff is collapsed')
- click_button 'Next'
+ find('.page-link.next-page-item').click
expect(page).not_to have_content('This diff is collapsed')
+ expect(page).to have_selector('.diff-file .file-title', text: 'large_diff_renamed.md')
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
index abb313cb529..bb4bf0864c9 100644
--- 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
@@ -25,7 +25,7 @@ RSpec.describe 'User views diffs file-by-file', :js 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'
+ find('.page-link.next-page-item').click
expect(page).to have_selector('.file-holder', count: 1)
expect(page).to have_selector('.diff-file .file-title', text: '.gitignore')
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index 537c0473fa4..928755bf5de 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -11,7 +11,6 @@ RSpec.describe 'User views diffs', :js do
let(:view) { 'inline' }
before do
- stub_feature_flags(diffs_batch_load: false)
visit(diffs_project_merge_request_path(project, merge_request, view: view))
wait_for_requests
@@ -62,7 +61,7 @@ RSpec.describe 'User views diffs', :js do
end
it 'expands all diffs' do
- first('#a5cc2925ca8258af241be7e5b0381edf30266302 .js-file-title').click
+ first('.js-file-title').click
expect(page).to have_button('Expand all')
diff --git a/spec/features/merge_requests/user_views_diffs_commit_spec.rb b/spec/features/merge_requests/user_views_diffs_commit_spec.rb
index fcaabf9b0e7..cf92603972e 100644
--- a/spec/features/merge_requests/user_views_diffs_commit_spec.rb
+++ b/spec/features/merge_requests/user_views_diffs_commit_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe 'User views diff by commit', :js do
let(:project) { create(:project, :public, :repository) }
before do
- stub_feature_flags(diffs_batch_load: false)
visit(diffs_project_merge_request_path(project, merge_request, commit_id: merge_request.diff_head_sha))
end
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index 620c2f60ba3..e8caa2159a4 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -9,6 +9,39 @@ RSpec.describe 'Profile > Account', :js do
sign_in(user)
end
+ describe 'Social sign-in' do
+ context 'when an identity does not exist' do
+ before do
+ allow(Devise).to receive_messages(omniauth_configs: { google_oauth2: {} })
+ end
+
+ it 'allows the user to connect' do
+ visit profile_account_path
+
+ expect(page).to have_link('Connect Google', href: '/users/auth/google_oauth2')
+ end
+ end
+
+ context 'when an identity already exists' do
+ before do
+ allow(Devise).to receive_messages(omniauth_configs: { twitter: {}, saml: {} })
+
+ create(:identity, user: user, provider: :twitter)
+ create(:identity, user: user, provider: :saml)
+
+ visit profile_account_path
+ end
+
+ it 'allows the user to disconnect when there is an existing identity' do
+ expect(page).to have_link('Disconnect Twitter', href: '/profile/account/unlink?provider=twitter')
+ end
+
+ it 'shows active for a provider that is not allowed to unlink' do
+ expect(page).to have_content('Saml Active')
+ end
+ end
+ end
+
describe 'Change username' do
let(:new_username) { 'bar' }
let(:new_user_path) { "/#{new_username}" }
diff --git a/spec/features/projects/artifacts/raw_spec.rb b/spec/features/projects/artifacts/raw_spec.rb
index d72a35fddf8..d580262d48b 100644
--- a/spec/features/projects/artifacts/raw_spec.rb
+++ b/spec/features/projects/artifacts/raw_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Raw artifact', :js do
+RSpec.describe 'Raw artifact' do
let(:project) { create(:project, :public) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 3032d115a00..7c564d76f70 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -564,35 +564,76 @@ RSpec.describe 'File blob', :js do
file_path: '.gitlab/dashboards/custom-dashboard.yml',
file_content: file_content
).execute
-
- visit_blob('.gitlab/dashboards/custom-dashboard.yml')
end
- context 'valid dashboard file' do
- let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
+ context 'with metrics_dashboard_exhaustive_validations feature flag off' do
+ before do
+ stub_feature_flags(metrics_dashboard_exhaustive_validations: false)
+ visit_blob('.gitlab/dashboards/custom-dashboard.yml')
+ end
- it 'displays an auxiliary viewer' do
- aggregate_failures do
- # shows that dashboard yaml is valid
- expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
+ context 'valid dashboard file' do
+ let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
+
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows that dashboard yaml is valid
+ expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
+ end
+ end
+ end
+
+ context 'invalid dashboard file' do
+ let(:file_content) { "dashboard: 'invalid'" }
- # shows a learn more link
- expect(page).to have_link('Learn more')
+ it 'displays an auxiliary viewer' 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: should be an array of panel_groups objects")
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
+ end
end
end
end
- context 'invalid dashboard file' do
- let(:file_content) { "dashboard: 'invalid'" }
+ context 'with metrics_dashboard_exhaustive_validations feature flag on' do
+ before do
+ stub_feature_flags(metrics_dashboard_exhaustive_validations: true)
+ visit_blob('.gitlab/dashboards/custom-dashboard.yml')
+ end
- it 'displays an auxiliary viewer' 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: should be an array of panel_groups objects")
+ context 'valid dashboard file' do
+ let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
- # shows a learn more link
- expect(page).to have_link('Learn more')
+ it 'displays an auxiliary viewer' do
+ aggregate_failures do
+ # shows that dashboard yaml is valid
+ expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
+ end
+ end
+ end
+
+ context 'invalid dashboard file' do
+ let(:file_content) { "dashboard: 'invalid'" }
+
+ it 'displays an auxiliary viewer' 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("root is missing required keys: panel_groups")
+
+ # shows a learn more link
+ expect(page).to have_link('Learn more')
+ end
end
end
end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index dbd1cebd515..0e2444c5434 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -97,7 +97,7 @@ RSpec.describe 'Branches' do
end
describe 'Delete unprotected branch on Overview' do
- it 'removes branch after confirmation', :js do
+ it 'removes branch after confirmation', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/239019' do
visit project_branches_filtered_path(project, state: 'all')
expect(all('.all-branches').last).to have_selector('li', count: 20)
diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb
index ba063acbe70..ce435151b84 100644
--- a/spec/features/projects/ci/lint_spec.rb
+++ b/spec/features/projects/ci/lint_spec.rb
@@ -3,89 +3,122 @@
require 'spec_helper'
RSpec.describe 'CI Lint', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
- before do
- project.add_developer(user)
- sign_in(user)
-
- visit project_ci_lint_path(project)
- find('#ci-editor')
- execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});")
+ shared_examples 'correct ci linting process' do
+ describe 'YAML parsing' do
+ shared_examples 'validates the YAML' do
+ before do
+ stub_feature_flags(ci_lint_vue: false)
+ click_on 'Validate'
+ end
- # Ace editor updates a hidden textarea and it happens asynchronously
- wait_for('YAML content') do
- find('.ace_content').text.present?
- end
- end
+ context 'YAML is correct' do
+ let(:yaml_content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
- describe 'YAML parsing' do
- shared_examples 'validates the YAML' do
- before do
- click_on 'Validate'
- end
+ it 'parses Yaml and displays the jobs' do
+ expect(page).to have_content('Status: syntax is correct')
- context 'YAML is correct' do
- let(:yaml_content) do
- File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ within "table" do
+ aggregate_failures do
+ expect(page).to have_content('Job - rspec')
+ expect(page).to have_content('Job - spinach')
+ expect(page).to have_content('Deploy Job - staging')
+ expect(page).to have_content('Deploy Job - production')
+ end
+ end
+ end
end
- it 'parses Yaml and displays the jobs' do
- expect(page).to have_content('Status: syntax is correct')
+ context 'YAML is incorrect' do
+ let(:yaml_content) { 'value: cannot have :' }
- within "table" do
- aggregate_failures do
- expect(page).to have_content('Job - rspec')
- expect(page).to have_content('Job - spinach')
- expect(page).to have_content('Deploy Job - staging')
- expect(page).to have_content('Deploy Job - production')
- end
+ it 'displays information about an error' do
+ expect(page).to have_content('Status: syntax is incorrect')
+ expect(page).to have_selector(content_selector, text: yaml_content)
end
end
end
- context 'YAML is incorrect' do
- let(:yaml_content) { 'value: cannot have :' }
+ it_behaves_like 'validates the YAML'
- it 'displays information about an error' do
- expect(page).to have_content('Status: syntax is incorrect')
- expect(page).to have_selector('.ace_content', text: yaml_content)
+ context 'when Dry Run is checked' do
+ before do
+ check 'Simulate a pipeline created for the default branch'
end
+
+ it_behaves_like 'validates the YAML'
end
- end
- it_behaves_like 'validates the YAML'
+ describe 'YAML revalidate' do
+ let(:yaml_content) { 'my yaml content' }
- context 'when Dry Run is checked' do
+ it 'loads previous YAML content after validation' do
+ expect(page).to have_field('content', with: 'my yaml content', visible: false, type: 'textarea')
+ end
+ end
+ end
+
+ describe 'YAML clearing' do
before do
- check 'Simulate a pipeline created for the default branch'
+ click_on 'Clear'
end
- it_behaves_like 'validates the YAML'
+ context 'YAML is present' do
+ let(:yaml_content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
+
+ it 'YAML content is cleared' do
+ expect(page).to have_field('content', with: '', visible: false, type: 'textarea')
+ end
+ end
end
+ end
- describe 'YAML revalidate' do
- let(:yaml_content) { 'my yaml content' }
+ context 'with ACE editor' do
+ it_behaves_like 'correct ci linting process' do
+ let(:content_selector) { '.ace_content' }
- it 'loads previous YAML content after validation' do
- expect(page).to have_field('content', with: 'my yaml content', visible: false, type: 'textarea')
+ before do
+ stub_feature_flags(monaco_ci: false)
+ stub_feature_flags(ci_lint_vue: false)
+ project.add_developer(user)
+ sign_in(user)
+
+ visit project_ci_lint_path(project)
+ find('#ci-editor')
+ execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});")
+
+ # Ace editor updates a hidden textarea and it happens asynchronously
+ wait_for('YAML content') do
+ find(content_selector).text.present?
+ end
end
end
end
- describe 'YAML clearing' do
- before do
- click_on 'Clear'
- end
+ context 'with Editor Lite' do
+ it_behaves_like 'correct ci linting process' do
+ let(:content_selector) { '.content .view-lines' }
- context 'YAML is present' do
- let(:yaml_content) do
- File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
- end
+ before do
+ stub_feature_flags(monaco_ci: true)
+ stub_feature_flags(ci_lint_vue: false)
+ project.add_developer(user)
+ sign_in(user)
- it 'YAML content is cleared' do
- expect(page).to have_field('content', with: '', visible: false, type: 'textarea')
+ visit project_ci_lint_path(project)
+ editor_set_value(yaml_content)
+
+ wait_for('YAML content') do
+ find(content_selector).text.present?
+ end
end
end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 63e5546b43c..04339d20d77 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -144,7 +144,7 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
end
it 'user sees the "Environment scope" field' do
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 450eaa7f004..9d0dc65093e 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'User Cluster', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
end
context 'when user filled form with valid parameters' do
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index c56a1ed1711..d674fbc457e 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe 'Clusters', :js do
context 'when user filled form with environment scope' do
before do
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: 'staging/*'
click_button 'Add Kubernetes cluster'
@@ -72,7 +72,7 @@ RSpec.describe 'Clusters', :js do
context 'when user updates duplicated environment scope' do
before do
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect 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'
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 7bd3bce85d5..cf9b86f16bb 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -26,8 +26,6 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
build.run
visit project_commit_path(project, project.commit.id)
- wait_for_all_requests
-
expect(page).to have_selector('.mr-widget-pipeline-graph')
first('.mini-pipeline-graph-dropdown-toggle').click
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 70d47516246..6218578cac6 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'Project deploy keys', :js do
page.within(find('.qa-deploy-keys-settings')) do
expect(page).to have_selector('.deploy-key', count: 1)
- accept_confirm { find('.ic-remove').click }
+ accept_confirm { find('[data-testid="remove-icon"]').click }
wait_for_requests
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index a05910cd892..7f2ef61bcbe 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -106,7 +106,7 @@ RSpec.describe 'Environments page', :js do
expect(page).to have_css('.environments-container')
expect(page.all('.environment-name').length).to eq(1)
- expect(page.all('.ic-stop').length).to eq(0)
+ expect(page.all('[data-testid="stop-icon"]').length).to eq(0)
end
end
end
@@ -301,7 +301,7 @@ RSpec.describe 'Environments page', :js do
end
it 'has a dropdown for actionable jobs' do
- expect(page).to have_selector('.dropdown-new.btn.btn-default .ic-play')
+ expect(page).to have_selector('.dropdown-new.btn.btn-default [data-testid="play-icon"]')
end
it "has link to the delayed job's action" do
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 aff8951d9de..908e30478b2 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,57 +8,26 @@ RSpec.describe 'User paginates issue designs', :js do
let(:project) { create(:project_empty_repo, :public) }
let(:issue) { create(:issue, project: project) }
- 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)
- visit project_issue_path(project, issue)
- click_link 'Designs'
- wait_for_requests
- find('.js-design-list-item', match: :first).click
- end
-
- 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')
-
- page.within(find('.js-design-header')) do
- expect(page).to have_content('2 of 2')
- end
- end
+ 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
- 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
+ it 'paginates to next design' do
+ expect(find('.js-previous-design')[:disabled]).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('1 of 2')
- end
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content('1 of 2')
+ end
- find('.js-next-design').click
+ 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')
- end
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content('2 of 2')
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 4e45312eac3..cfd8a4540ee 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,32 +8,13 @@ RSpec.describe 'User design permissions', :js do
let(:project) { create(:project_empty_repo, :public) }
let(:issue) { create(:issue, project: project) }
- context 'design_management_moved flag disabled' do
- before do
- enable_design_management
- stub_feature_flags(design_management_moved: false)
+ before do
+ enable_design_management
- visit project_issue_path(project, issue)
-
- click_link 'Designs'
-
- wait_for_requests
- end
-
- it 'user does not have permissions to upload design' do
- expect(page).not_to have_field('design_file')
- end
+ visit project_issue_path(project, issue)
end
- 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
+ it 'user does not have permissions to upload design' do
+ expect(page).not_to have_field('design_file')
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 29a27992a0d..de1fcc9d787 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
@@ -11,81 +11,34 @@ RSpec.describe 'User uploads new design', :js do
before do
sign_in(user)
+ enable_design_management(feature_enabled)
+ visit project_issue_path(project, issue)
end
- context 'design_management_moved flag disabled' do
- before do
- enable_design_management(feature_enabled)
- stub_feature_flags(design_management_moved: false)
- visit project_issue_path(project, issue)
+ context "when the feature is available" do
+ let(:feature_enabled) { true }
- click_link 'Designs'
+ it 'uploads designs' do
+ upload_design(logo_fixture, count: 1)
- wait_for_requests
- end
-
- context "when the feature is available" do
- let(:feature_enabled) { true }
-
- it 'uploads designs' do
- upload_design(logo_fixture, count: 1)
-
- 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)
- upload_design(gif_fixture, count: 2)
-
- # Known bug in the legacy implementation: new designs are inserted
- # in the beginning on the frontend.
- expect(page).to have_selector('.js-design-list-item', count: 2)
- expect(page.all('.js-design-list-item').map(&:text)).to eq(['banana_sample.gif', 'dk.png'])
+ within first('[data-testid="designs-root"] .js-design-list-item') do
+ expect(page).to have_content('dk.png')
end
- end
- context 'when the feature is not available' do
- let(:feature_enabled) { false }
+ upload_design(gif_fixture, count: 2)
- it 'shows the message about requirements' do
- expect(page).to have_content("To upload designs, you'll need to enable LFS.")
- end
+ expect(page).to have_selector('.js-design-list-item', count: 2)
+ expect(page.all('.js-design-list-item').map(&:text)).to eq(['dk.png', 'banana_sample.gif'])
end
end
- 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
-
- context "when the feature is available" do
- let(:feature_enabled) { true }
+ context 'when the feature is not available' do
+ let(:feature_enabled) { false }
- it 'uploads designs' do
- upload_design(logo_fixture, count: 1)
-
- 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
-
- upload_design(gif_fixture, count: 2)
-
- expect(page).to have_selector('.js-design-list-item', count: 2)
- expect(page.all('.js-design-list-item').map(&:text)).to eq(['dk.png', 'banana_sample.gif'])
- end
- end
-
- 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 upload designs, you'll need to enable LFS.")
- end
+ it 'shows the message about requirements' do
+ expect(page).to have_content("To upload designs, you'll need to enable LFS and have admin enable hashed storage.")
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 49245218e81..b513a4fe3fa 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,42 +9,19 @@ 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) }
- context 'design_management_moved flag disabled' do
- before do
- enable_design_management
- stub_feature_flags(design_management_moved: false)
+ before do
+ enable_design_management
- visit project_issue_path(project, issue)
-
- 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
+ visit project_issue_path(project, issue)
end
- context 'design_management_moved flag enabled' do
- before do
- enable_design_management
+ it 'opens design detail' do
+ click_link design.filename
- visit project_issue_path(project, issue)
+ page.within(find('.js-design-header')) do
+ expect(page).to have_content(design.filename)
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
+ expect(page).to have_selector('.js-design-image')
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 772a9ffbe6f..46c772027ad 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,78 +9,37 @@ 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) }
- context 'design_management_moved flag disabled' do
- before do
- enable_design_management
- stub_feature_flags(design_management_moved: false)
- end
-
- 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
-
- context 'navigates directly to the design collection view' do
- before do
- visit designs_project_issue_path(project, issue)
- end
+ before do
+ enable_design_management
+ end
- it 'expands the sidebar' do
- expect(page).to have_selector('.layout-page.right-sidebar-expanded')
- end
+ context 'navigates from the issue view' do
+ before do
+ visit project_issue_path(project, issue)
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
+ it 'fetches list of designs' do
+ expect(page).to have_selector('.js-design-list-item', count: 1)
end
end
- context 'design_management_moved flag enabled' do
+ context 'navigates directly to the design collection view' do
before do
- enable_design_management
+ visit designs_project_issue_path(project, issue)
end
- 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
+ it 'expands the sidebar' do
+ expect(page).to have_selector('.layout-page.right-sidebar-expanded')
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
+ context 'navigates directly to the individual design view' do
+ before do
+ visit designs_project_issue_path(project, issue, vueroute: design.filename)
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
+ it 'sees the design' do
+ expect(page).to have_selector('.js-design-detail')
end
end
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 0a6f204454e..404c3e93586 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -373,13 +373,29 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
let(:expire_at) { Time.now + 7.days }
context 'when user has ability to update job' do
- it 'keeps artifacts when keep button is clicked' do
- expect(page).to have_content 'The artifacts will be removed in'
+ context 'when artifacts are unlocked' do
+ before do
+ job.pipeline.unlocked!
+ end
- click_link 'Keep'
+ it 'keeps artifacts when keep button is clicked' do
+ expect(page).to have_content 'The artifacts will be removed in'
- expect(page).to have_no_link 'Keep'
- expect(page).to have_no_content 'The artifacts will be removed in'
+ click_link 'Keep'
+
+ expect(page).to have_no_link 'Keep'
+ expect(page).to have_no_content 'The artifacts will be removed in'
+ end
+ end
+
+ context 'when artifacts are locked' do
+ before do
+ job.pipeline.artifacts_locked!
+ end
+
+ it 'shows the keep button' do
+ expect(page).to have_link 'Keep'
+ end
end
end
@@ -395,9 +411,26 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
context 'when artifacts expired' do
let(:expire_at) { Time.now - 7.days }
- it 'does not have the Keep button' do
- expect(page).to have_content 'The artifacts were removed'
- expect(page).not_to have_link 'Keep'
+ context 'when artifacts are unlocked' do
+ before do
+ job.pipeline.unlocked!
+ end
+
+ it 'does not have the Keep button' do
+ expect(page).to have_content 'The artifacts were removed'
+ expect(page).not_to have_link 'Keep'
+ end
+ end
+
+ context 'when artifacts are locked' do
+ before do
+ job.pipeline.artifacts_locked!
+ end
+
+ it 'has the Keep button' do
+ expect(page).not_to have_content 'The artifacts were removed'
+ expect(page).to have_link 'Keep'
+ end
end
end
end
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
index 058cbfff662..30e32ad1366 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -112,7 +112,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
let!(:group) { create(:group) }
around do |example|
- Timecop.freeze { example.run }
+ freeze_time { example.run }
end
before do
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index dcb901bcf11..07f65fe62df 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -18,6 +18,14 @@ RSpec.describe 'Project navbar' do
project.add_maintainer(user)
sign_in(user)
+
+ if Gitlab.ee?
+ insert_after_sub_nav_item(
+ _('Kubernetes'),
+ within: _('Operations'),
+ new_sub_nav_item_name: _('Feature Flags')
+ )
+ end
end
it_behaves_like 'verified navigation bar' do
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index e1ace817c72..243579ee2f7 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -380,14 +380,14 @@ RSpec.shared_examples 'pages settings editing' do
expect(project).to be_pages_deployed
end
- it 'removes the pages' do
+ it 'removes the pages', :sidekiq_inline do
visit project_pages_path(project)
expect(page).to have_link('Remove pages')
accept_confirm { click_link 'Remove pages' }
- expect(page).to have_content('Pages were removed')
+ expect(page).to have_content('Pages were scheduled for removal')
expect(project.reload.pages_deployed?).to be_falsey
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 26c46190e7d..f59dc5dd074 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -378,7 +378,7 @@ RSpec.describe 'Pipeline', :js do
find('.js-tests-tab-link').click
expect(page).to have_content('Jobs')
- expect(page).to have_selector('.js-tests-detail', visible: :all)
+ expect(page).to have_selector('[data-testid="tests-detail"]', visible: :all)
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 8747b3ab54c..a9c196bb84b 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -311,7 +311,7 @@ RSpec.describe 'Pipelines', :js do
let!(:delayed_job) do
create(:ci_build, :scheduled,
pipeline: pipeline,
- name: 'delayed job',
+ name: 'delayed job 1',
stage: 'test')
end
@@ -327,7 +327,7 @@ RSpec.describe 'Pipelines', :js do
find('.js-pipeline-dropdown-manual-actions').click
time_diff = [0, delayed_job.scheduled_at - Time.now].max
- expect(page).to have_button('delayed job')
+ expect(page).to have_button('delayed job 1')
expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S"))
end
@@ -335,7 +335,7 @@ RSpec.describe 'Pipelines', :js do
let!(:delayed_job) do
create(:ci_build, :expired_scheduled,
pipeline: pipeline,
- name: 'delayed job',
+ name: 'delayed job 1',
stage: 'test')
end
@@ -349,7 +349,7 @@ RSpec.describe 'Pipelines', :js do
context 'when user played a delayed job immediately' do
before do
find('.js-pipeline-dropdown-manual-actions').click
- page.accept_confirm { click_button('delayed job') }
+ page.accept_confirm { click_button('delayed job 1') }
wait_for_requests
end
diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb
new file mode 100644
index 00000000000..5d05a7e4c91
--- /dev/null
+++ b/spec/features/projects/releases/user_creates_release_spec.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User creates release', :js do
+ include Spec::Support::Helpers::Features::ReleasesHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:milestone_1) { create(:milestone, project: project, title: '1.1') }
+ let_it_be(:milestone_2) { create(:milestone, project: project, title: '1.2') }
+ let_it_be(:user) { create(:user) }
+
+ let(:new_page_url) { new_project_release_path(project) }
+ let(:show_feature_flag) { true }
+
+ before do
+ stub_feature_flags(release_show_page: show_feature_flag)
+
+ project.add_developer(user)
+
+ gitlab_sign_in(user)
+
+ visit new_page_url
+
+ wait_for_requests
+ end
+
+ it 'renders the breadcrumbs', :aggregate_failures do
+ within('.breadcrumbs') do
+ expect(page).to have_content("#{project.creator.name} #{project.name} New Release")
+
+ expect(page).to have_link(project.creator.name, href: user_path(project.creator))
+ expect(page).to have_link(project.name, href: project_path(project))
+ expect(page).to have_link('New Release', href: new_project_release_path(project))
+ end
+ end
+
+ it 'defaults the "Create from" dropdown to the project\'s default branch' do
+ expect(page.find('.ref-selector button')).to have_content(project.default_branch)
+ end
+
+ context 'when the "Save release" button is clicked' do
+ let(:tag_name) { 'v1.0' }
+ let(:release_title) { 'A most magnificent release' }
+ let(:release_notes) { 'Best. Release. **Ever.** :rocket:' }
+ let(:link_1) { { url: 'https://gitlab.example.com/runbook', title: 'An example runbook', type: 'runbook' } }
+ let(:link_2) { { url: 'https://gitlab.example.com/other', title: 'An example link', type: 'other' } }
+
+ before do
+ fill_out_form_and_submit
+ end
+
+ it 'creates a new release when "Create release" is clicked', :aggregate_failures do
+ release = project.releases.last
+
+ expect(release.tag).to eq(tag_name)
+ expect(release.sha).to eq(commit.id)
+ expect(release.name).to eq(release_title)
+ expect(release.milestones.first.title).to eq(milestone_1.title)
+ expect(release.milestones.second.title).to eq(milestone_2.title)
+ expect(release.description).to eq(release_notes)
+ expect(release.links.length).to eq(2)
+
+ link = release.links.find { |l| l.link_type == link_1[:type] }
+ expect(link.url).to eq(link_1[:url])
+ expect(link.name).to eq(link_1[:title])
+
+ link = release.links.find { |l| l.link_type == link_2[:type] }
+ expect(link.url).to eq(link_2[:url])
+ expect(link.name).to eq(link_2[:title])
+ end
+
+ it 'redirects to the dedicated page for the newly created release' do
+ release = project.releases.last
+
+ expect(page).to have_current_path(project_release_path(project, release))
+ end
+
+ context 'when the release_show_page feature flag is disabled' do
+ let(:show_feature_flag) { false }
+
+ it 'redirects to the main "Releases" page' do
+ expect(page).to have_current_path(project_releases_path(project))
+ end
+ end
+ end
+
+ context 'when the "Cancel" button is clicked' do
+ before do
+ click_link_or_button 'Cancel'
+
+ wait_for_all_requests
+ end
+
+ it 'redirects to the main "Releases" page' do
+ expect(page).to have_current_path(project_releases_path(project))
+ end
+
+ context 'when the URL includes a back_url query parameter' do
+ let(:back_path) { project_releases_path(project, params: { page: 2 }) }
+ let(:new_page_url) do
+ new_project_release_path(project, params: { back_url: back_path })
+ end
+
+ it 'redirects to the page specified with back_url' do
+ expect(page).to have_current_path(back_path)
+ end
+ end
+ end
+
+ def fill_out_form_and_submit
+ fill_tag_name(tag_name)
+
+ select_create_from(branch.name)
+
+ fill_release_title(release_title)
+
+ select_milestone(milestone_1.title, and_tab: false)
+ select_milestone(milestone_2.title)
+
+ # Focus the "Release notes" field by clicking instead of tabbing
+ # because tabbing to the field requires too many tabs
+ # (see https://gitlab.com/gitlab-org/gitlab/-/issues/238619)
+ find_field('Release notes').click
+ fill_release_notes(release_notes)
+
+ # Tab past the "assets" documentation link
+ focused_element.send_keys(:tab)
+
+ fill_asset_link(link_1)
+ add_another_asset_link
+ fill_asset_link(link_2)
+
+ # Submit using the Control+Enter shortcut
+ focused_element.send_keys([:control, :enter])
+
+ wait_for_all_requests
+ end
+
+ def branch
+ project.repository.branches.find { |b| b.name == 'feature' }
+ end
+
+ def commit
+ branch.dereferenced_target
+ end
+end
diff --git a/spec/features/projects/releases/user_views_releases_spec.rb b/spec/features/projects/releases/user_views_releases_spec.rb
index 962d5551631..993d3371904 100644
--- a/spec/features/projects/releases/user_views_releases_spec.rb
+++ b/spec/features/projects/releases/user_views_releases_spec.rb
@@ -13,37 +13,25 @@ RSpec.describe 'User views releases', :js do
project.add_guest(guest)
end
- context('when the user is a maintainer') do
- before do
- gitlab_sign_in(maintainer)
- end
-
- it 'sees the release' do
- visit project_releases_path(project)
-
- expect(page).to have_content(release.name)
- expect(page).to have_content(release.tag)
- expect(page).not_to have_content('Upcoming Release')
- end
-
- shared_examples 'asset link tests' do
- context 'when there is a link as an asset' do
- let!(:release_link) { create(:release_link, release: release, url: url ) }
- let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
- let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release) << release_link.filepath }
+ shared_examples 'releases page' do
+ context('when the user is a maintainer') do
+ before do
+ gitlab_sign_in(maintainer)
+ end
- it 'sees the link' do
- visit project_releases_path(project)
+ it 'sees the release' do
+ visit project_releases_path(project)
- page.within('.js-assets-list') do
- expect(page).to have_link release_link.name, href: direct_asset_link
- expect(page).not_to have_css('[data-testid="external-link-indicator"]')
- end
- end
+ expect(page).to have_content(release.name)
+ expect(page).to have_content(release.tag)
+ expect(page).not_to have_content('Upcoming Release')
+ end
- context 'when there is a link redirect' do
- let!(:release_link) { create(:release_link, release: release, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
+ shared_examples 'asset link tests' do
+ context 'when there is a link as an asset' do
+ let!(:release_link) { create(:release_link, release: release, url: url ) }
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
+ let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release) << release_link.filepath }
it 'sees the link' do
visit project_releases_path(project)
@@ -53,77 +41,103 @@ RSpec.describe 'User views releases', :js do
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
end
end
- end
- context 'when url points to external resource' do
- let(:url) { 'http://google.com/download' }
+ context 'when there is a link redirect' do
+ let!(:release_link) { create(:release_link, release: release, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
+ let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
- it 'sees that the link is external resource' do
- visit project_releases_path(project)
+ it 'sees the link' do
+ visit project_releases_path(project)
- page.within('.js-assets-list') do
- expect(page).to have_css('[data-testid="external-link-indicator"]')
+ page.within('.js-assets-list') do
+ expect(page).to have_link release_link.name, href: direct_asset_link
+ expect(page).not_to have_css('[data-testid="external-link-indicator"]')
+ end
+ end
+ end
+
+ context 'when url points to external resource' do
+ let(:url) { 'http://google.com/download' }
+
+ it 'sees that the link is external resource' do
+ visit project_releases_path(project)
+
+ page.within('.js-assets-list') do
+ expect(page).to have_css('[data-testid="external-link-indicator"]')
+ end
end
end
end
end
- end
- context 'when the release_asset_link_type feature flag is enabled' do
- before do
- stub_feature_flags(release_asset_link_type: true)
+ context 'when the release_asset_link_type feature flag is enabled' do
+ before do
+ stub_feature_flags(release_asset_link_type: true)
+ end
+
+ it_behaves_like 'asset link tests'
end
- it_behaves_like 'asset link tests'
- end
+ context 'when the release_asset_link_type feature flag is disabled' do
+ before do
+ stub_feature_flags(release_asset_link_type: false)
+ end
- context 'when the release_asset_link_type feature flag is disabled' do
- before do
- stub_feature_flags(release_asset_link_type: false)
+ it_behaves_like 'asset link tests'
end
- it_behaves_like 'asset link tests'
- end
+ context 'with an upcoming release' do
+ let(:tomorrow) { Time.zone.now + 1.day }
+ let!(:release) { create(:release, project: project, released_at: tomorrow ) }
- context 'with an upcoming release' do
- let(:tomorrow) { Time.zone.now + 1.day }
- let!(:release) { create(:release, project: project, released_at: tomorrow ) }
+ it 'sees the upcoming tag' do
+ visit project_releases_path(project)
- it 'sees the upcoming tag' do
- visit project_releases_path(project)
+ expect(page).to have_content('Upcoming Release')
+ end
+ end
+
+ context 'with a tag containing a slash' do
+ it 'sees the release' do
+ release = create :release, project: project, tag: 'debian/2.4.0-1'
+ visit project_releases_path(project)
- expect(page).to have_content('Upcoming Release')
+ expect(page).to have_content(release.name)
+ expect(page).to have_content(release.tag)
+ end
end
end
- context 'with a tag containing a slash' do
- it 'sees the release' do
- release = create :release, project: project, tag: 'debian/2.4.0-1'
+ context('when the user is a guest') do
+ before do
+ gitlab_sign_in(guest)
+ end
+
+ it 'renders release info except for Git-related data' do
visit project_releases_path(project)
- expect(page).to have_content(release.name)
- expect(page).to have_content(release.tag)
+ within('.release-block') do
+ expect(page).to have_content(release.description)
+
+ # The following properties (sometimes) include Git info,
+ # so they are not rendered for Guest users
+ expect(page).not_to have_content(release.name)
+ expect(page).not_to have_content(release.tag)
+ expect(page).not_to have_content(release.commit.short_id)
+ end
end
end
end
- context('when the user is a guest') do
+ context 'when the graphql_releases_page feature flag is enabled' do
+ it_behaves_like 'releases page'
+ end
+
+ context 'when the graphql_releases_page feature flag is disabled' do
before do
- gitlab_sign_in(guest)
+ stub_feature_flags(graphql_releases_page: false)
end
- it 'renders release info except for Git-related data' do
- visit project_releases_path(project)
-
- within('.release-block') do
- expect(page).to have_content(release.description)
-
- # The following properties (sometimes) include Git info,
- # so they are not rendered for Guest users
- expect(page).not_to have_content(release.name)
- expect(page).not_to have_content(release.tag)
- expect(page).not_to have_content(release.commit.short_id)
- end
- end
+ it_behaves_like 'releases page'
end
end
diff --git a/spec/features/projects/services/user_activates_asana_spec.rb b/spec/features/projects/services/user_activates_asana_spec.rb
index 3e24d106be0..e95e7e89fc2 100644
--- a/spec/features/projects/services/user_activates_asana_spec.rb
+++ b/spec/features/projects/services/user_activates_asana_spec.rb
@@ -12,6 +12,6 @@ RSpec.describe 'User activates Asana' do
click_test_then_save_integration
- expect(page).to have_content('Asana activated.')
+ expect(page).to have_content('Asana settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_assembla_spec.rb b/spec/features/projects/services/user_activates_assembla_spec.rb
index 2e49f4caa82..63cc424a641 100644
--- a/spec/features/projects/services/user_activates_assembla_spec.rb
+++ b/spec/features/projects/services/user_activates_assembla_spec.rb
@@ -13,8 +13,8 @@ RSpec.describe 'User activates Assembla' do
visit_project_integration('Assembla')
fill_in('Token', with: 'verySecret')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('Assembla activated.')
+ expect(page).to have_content('Assembla settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
index 7b89b9ac4a7..a9d91454670 100644
--- a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
+++ b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
@@ -16,9 +16,9 @@ RSpec.describe 'User activates Atlassian Bamboo CI' do
fill_in('Username', with: 'user')
fill_in('Password', with: 'verySecret')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('Atlassian Bamboo CI activated.')
+ expect(page).to have_content('Atlassian Bamboo CI settings saved and active.')
# Password field should not be filled in.
click_link('Atlassian Bamboo CI')
diff --git a/spec/features/projects/services/user_activates_emails_on_push_spec.rb b/spec/features/projects/services/user_activates_emails_on_push_spec.rb
index 40947027146..5a075fc61e8 100644
--- a/spec/features/projects/services/user_activates_emails_on_push_spec.rb
+++ b/spec/features/projects/services/user_activates_emails_on_push_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe 'User activates Emails on push' do
visit_project_integration('Emails on push')
fill_in('Recipients', with: 'qa@company.name')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('Emails on push activated.')
+ expect(page).to have_content('Emails on push settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_flowdock_spec.rb b/spec/features/projects/services/user_activates_flowdock_spec.rb
index 9581d718400..4a4d7bbecfd 100644
--- a/spec/features/projects/services/user_activates_flowdock_spec.rb
+++ b/spec/features/projects/services/user_activates_flowdock_spec.rb
@@ -15,8 +15,8 @@ RSpec.describe 'User activates Flowdock' do
visit_project_integration('Flowdock')
fill_in('Token', with: 'verySecret')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('Flowdock activated.')
+ expect(page).to have_content('Flowdock settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_hipchat_spec.rb b/spec/features/projects/services/user_activates_hipchat_spec.rb
index a2820c4bb0f..cffb780e05d 100644
--- a/spec/features/projects/services/user_activates_hipchat_spec.rb
+++ b/spec/features/projects/services/user_activates_hipchat_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'User activates HipChat', :js do
include_context 'project service activation'
- context 'with standart settings' do
+ context 'with standard settings' do
before do
stub_request(:post, /.*api.hipchat.com.*/)
end
@@ -15,9 +15,9 @@ RSpec.describe 'User activates HipChat', :js do
fill_in('Room', with: 'gitlab')
fill_in('Token', with: 'verySecret')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('HipChat activated.')
+ expect(page).to have_content('HipChat settings saved and active.')
end
end
@@ -32,9 +32,9 @@ RSpec.describe 'User activates HipChat', :js do
fill_in('Token', with: 'secretCustom')
fill_in('Server', with: 'https://chat.example.com')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('HipChat activated.')
+ expect(page).to have_content('HipChat settings saved and active.')
end
end
end
diff --git a/spec/features/projects/services/user_activates_irker_spec.rb b/spec/features/projects/services/user_activates_irker_spec.rb
index fad40fa6085..e4d92dc30ff 100644
--- a/spec/features/projects/services/user_activates_irker_spec.rb
+++ b/spec/features/projects/services/user_activates_irker_spec.rb
@@ -10,8 +10,8 @@ RSpec.describe 'User activates Irker (IRC gateway)' do
check('Colorize messages')
fill_in('Recipients', with: 'irc://chat.freenode.net/#commits')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('Irker (IRC gateway) activated.')
+ expect(page).to have_content('Irker (IRC gateway) settings saved and active.')
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 4f25794d058..1aec8883395 100644
--- a/spec/features/projects/services/user_activates_issue_tracker_spec.rb
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'User activates issue tracker', :js do
let(:url) { 'http://tracker.example.com' }
def fill_form(disable: false, skip_new_issue_url: false)
- click_active_toggle if disable
+ click_active_checkbox if disable
fill_in 'service_project_url', with: url
fill_in 'service_issues_url', with: "#{url}/:id"
@@ -16,7 +16,7 @@ RSpec.describe 'User activates issue tracker', :js do
fill_in 'service_new_issue_url', with: url unless skip_new_issue_url
end
- shared_examples 'external issue tracker activation' do |tracker:, skip_new_issue_url: false|
+ shared_examples 'external issue tracker activation' do |tracker:, skip_new_issue_url: false, skip_test: false|
describe 'user sets and activates the Service' do
context 'when the connection test succeeds' do
before do
@@ -25,11 +25,15 @@ RSpec.describe 'User activates issue tracker', :js do
visit_project_integration(tracker)
fill_form(skip_new_issue_url: skip_new_issue_url)
- click_test_integration
+ if skip_test
+ click_save_integration
+ else
+ click_test_then_save_integration(expect_test_to_fail: false)
+ end
end
it 'activates the service' do
- expect(page).to have_content("#{tracker} activated.")
+ expect(page).to have_content("#{tracker} settings saved and active.")
expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
@@ -47,9 +51,13 @@ RSpec.describe 'User activates issue tracker', :js do
visit_project_integration(tracker)
fill_form(skip_new_issue_url: skip_new_issue_url)
- click_test_then_save_integration
+ if skip_test
+ click_button('Save changes')
+ else
+ click_test_then_save_integration
+ end
- expect(page).to have_content("#{tracker} activated.")
+ expect(page).to have_content("#{tracker} settings saved and active.")
expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
end
@@ -64,7 +72,7 @@ RSpec.describe 'User activates issue tracker', :js do
end
it 'saves but does not activate the service' do
- expect(page).to have_content("#{tracker} settings saved, but not activated.")
+ expect(page).to have_content("#{tracker} settings saved, but not active.")
expect(current_path).to eq(edit_project_service_path(project, tracker.parameterize(separator: '_')))
end
@@ -80,4 +88,5 @@ RSpec.describe 'User activates issue tracker', :js do
it_behaves_like 'external issue tracker activation', tracker: 'YouTrack', skip_new_issue_url: true
it_behaves_like 'external issue tracker activation', tracker: 'Bugzilla'
it_behaves_like 'external issue tracker activation', tracker: 'Custom Issue Tracker'
+ it_behaves_like 'external issue tracker activation', tracker: 'EWM', skip_test: true
end
diff --git a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
index 8ee369eb6ec..72881054c6c 100644
--- a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
+++ b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe 'User activates JetBrains TeamCity CI' do
fill_in('Username', with: 'user')
fill_in('Password', with: 'verySecret')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('JetBrains TeamCity CI activated.')
+ expect(page).to have_content('JetBrains TeamCity CI settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb
index 483671c4b5b..85afc54be48 100644
--- a/spec/features/projects/services/user_activates_jira_spec.rb
+++ b/spec/features/projects/services/user_activates_jira_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'User activates Jira', :js do
include_context 'project service activation'
include_context 'project service Jira context'
- describe 'user sets and activates Jira Service' do
+ describe 'user tests Jira Service' do
context 'when Jira connection test succeeds' do
before do
server_info = { key: 'value' }.to_json
@@ -14,11 +14,11 @@ RSpec.describe 'User activates Jira', :js do
visit_project_integration('Jira')
fill_form
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
end
it 'activates the Jira service' do
- expect(page).to have_content('Jira activated.')
+ expect(page).to have_content('Jira settings saved and active.')
expect(current_path).to eq(edit_project_service_path(project, :jira))
end
@@ -54,21 +54,24 @@ RSpec.describe 'User activates Jira', :js do
fill_form
click_test_then_save_integration
- expect(page).to have_content('Jira activated.')
+ expect(page).to have_content('Jira settings saved and active.')
expect(current_path).to eq(edit_project_service_path(project, :jira))
end
end
end
describe 'user disables the Jira Service' do
+ include JiraServiceHelper
+
before do
+ stub_jira_service_test
visit_project_integration('Jira')
fill_form(disable: true)
- click_button('Save changes')
+ click_save_integration
end
it 'saves but does not activate the Jira service' do
- expect(page).to have_content('Jira settings saved, but not activated.')
+ expect(page).to have_content('Jira settings saved, but not active.')
expect(current_path).to eq(edit_project_service_path(project, :jira))
end
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index 6ddffb710a8..32519b14d4e 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
@@ -28,21 +28,21 @@ RSpec.describe 'Set up Mattermost slash commands', :js do
token = ('a'..'z').to_a.join
fill_in 'service_token', with: token
- click_active_toggle
- click_on 'Save changes'
+ click_active_checkbox
+ click_save_integration
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.')
+ expect(page).to have_content('Mattermost slash commands settings saved, but not active.')
end
it 'redirects to the integrations page after activating' do
token = ('a'..'z').to_a.join
fill_in 'service_token', with: token
- click_on 'Save changes'
+ click_save_integration
expect(current_path).to eq(edit_project_service_path(project, :mattermost_slash_commands))
- expect(page).to have_content('Mattermost slash commands activated.')
+ expect(page).to have_content('Mattermost slash commands settings saved and active.')
end
it 'shows the add to mattermost button' do
diff --git a/spec/features/projects/services/user_activates_packagist_spec.rb b/spec/features/projects/services/user_activates_packagist_spec.rb
index 70cf612bb2a..87303cf8fb4 100644
--- a/spec/features/projects/services/user_activates_packagist_spec.rb
+++ b/spec/features/projects/services/user_activates_packagist_spec.rb
@@ -16,6 +16,6 @@ RSpec.describe 'User activates Packagist' do
click_test_then_save_integration
- expect(page).to have_content('Packagist activated.')
+ expect(page).to have_content('Packagist settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb b/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
index 8e99c6e303b..83f66d4fa7b 100644
--- a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
+++ b/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
@@ -13,8 +13,8 @@ RSpec.describe 'User activates PivotalTracker' do
visit_project_integration('PivotalTracker')
fill_in('Token', with: 'verySecret')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('PivotalTracker activated.')
+ expect(page).to have_content('PivotalTracker settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_prometheus_spec.rb b/spec/features/projects/services/user_activates_prometheus_spec.rb
index 89b1f447c32..b89e89d250f 100644
--- a/spec/features/projects/services/user_activates_prometheus_spec.rb
+++ b/spec/features/projects/services/user_activates_prometheus_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'User activates Prometheus' do
click_button('Save changes')
- expect(page).not_to have_content('Prometheus activated.')
+ expect(page).not_to have_content('Prometheus settings saved and active.')
expect(page).to have_content('Fields on this page has been deprecated.')
end
end
diff --git a/spec/features/projects/services/user_activates_pushover_spec.rb b/spec/features/projects/services/user_activates_pushover_spec.rb
index 789cc30a42e..3cfd069032a 100644
--- a/spec/features/projects/services/user_activates_pushover_spec.rb
+++ b/spec/features/projects/services/user_activates_pushover_spec.rb
@@ -17,8 +17,8 @@ RSpec.describe 'User activates Pushover' do
select('High Priority', from: 'Priority')
select('Bike', from: 'Sound')
- click_test_integration
+ click_test_then_save_integration(expect_test_to_fail: false)
- expect(page).to have_content('Pushover activated.')
+ expect(page).to have_content('Pushover settings saved and active.')
end
end
diff --git a/spec/features/projects/services/user_activates_slack_notifications_spec.rb b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
index 20e2bd3f085..2a880e05e0f 100644
--- a/spec/features/projects/services/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'User activates Slack notifications', :js do
click_test_then_save_integration
- expect(page).to have_content('Slack notifications activated.')
+ expect(page).to have_content('Slack notifications settings saved and active.')
end
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 afe6855d6ad..3994f55caee 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
@@ -21,11 +21,11 @@ RSpec.describe 'Slack slash commands', :js do
it 'redirects to the integrations page after saving but not activating' do
fill_in 'Token', with: 'token'
- click_active_toggle
+ click_active_checkbox
click_on 'Save'
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.')
+ expect(page).to have_content('Slack slash commands settings saved, but not active.')
end
it 'redirects to the integrations page after activating' do
@@ -33,7 +33,7 @@ RSpec.describe 'Slack slash commands', :js do
click_on 'Save'
expect(current_path).to eq(edit_project_service_path(project, :slack_slash_commands))
- expect(page).to have_content('Slack slash commands activated.')
+ expect(page).to have_content('Slack slash commands settings saved and active.')
end
it 'shows the correct trigger url' do
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 8beecedf85f..8c7b7bc70a2 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << private_deploy_key
visit project_settings_repository_path(project)
- find('.deploy-key', text: private_deploy_key.title).find('.ic-pencil').click
+ find('.deploy-key', text: private_deploy_key.title).find('[data-testid="pencil-icon"]').click
fill_in 'deploy_key_title', with: 'updated_deploy_key'
check 'deploy_key_deploy_keys_projects_attributes_0_can_push'
@@ -84,7 +84,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << public_deploy_key
visit project_settings_repository_path(project)
- find('.deploy-key', text: public_deploy_key.title).find('.ic-pencil').click
+ find('.deploy-key', text: public_deploy_key.title).find('[data-testid="pencil-icon"]').click
check 'deploy_key_deploy_keys_projects_attributes_0_can_push'
click_button 'Save changes'
@@ -102,7 +102,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
find('.js-deployKeys-tab-available_project_keys').click
- find('.deploy-key', text: private_deploy_key.title).find('.ic-pencil').click
+ find('.deploy-key', text: private_deploy_key.title).find('[data-testid="pencil-icon"]').click
fill_in 'deploy_key_title', with: 'updated_deploy_key'
click_button 'Save changes'
@@ -116,7 +116,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << private_deploy_key
visit project_settings_repository_path(project)
- accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('.ic-remove').click }
+ accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('[data-testid="remove-icon"]').click }
expect(page).not_to have_content(private_deploy_key.title)
end
diff --git a/spec/features/projects/settings/user_renames_a_project_spec.rb b/spec/features/projects/settings/user_renames_a_project_spec.rb
index 6088ea31661..1ff976eb800 100644
--- a/spec/features/projects/settings/user_renames_a_project_spec.rb
+++ b/spec/features/projects/settings/user_renames_a_project_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe 'Projects > Settings > User renames a project' do
it 'shows errors for invalid project path' do
change_path(project, 'foo&bar')
- expect(page).to have_field 'Path', with: 'foo&bar'
+ expect(page).to have_field 'Path', with: 'gitlab'
expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"
end
end
diff --git a/spec/features/projects/show/user_sees_readme_spec.rb b/spec/features/projects/show/user_sees_readme_spec.rb
index 250f707948e..6a5b9472be8 100644
--- a/spec/features/projects/show/user_sees_readme_spec.rb
+++ b/spec/features/projects/show/user_sees_readme_spec.rb
@@ -14,4 +14,25 @@ RSpec.describe 'Projects > Show > User sees README' do
expect(page).to have_content 'testme'
end
end
+
+ context 'obeying robots.txt' do
+ before do
+ Gitlab::Testing::RobotsBlockerMiddleware.block_requests!
+ end
+
+ after do
+ Gitlab::Testing::RobotsBlockerMiddleware.allow_requests!
+ end
+
+ # For example, see this regression we had in
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39520
+ it 'does not block the requests necessary to load the project README', :js do
+ visit project_path(project)
+ wait_for_requests
+
+ page.within('.readme-holder') do
+ expect(page).to have_content 'testme'
+ end
+ end
+ end
end
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 afa9de5ce86..81736fefae9 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
@@ -226,7 +226,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
expect(project.repository.gitlab_ci_yml).to be_nil
page.within('.project-buttons') do
- expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
+ expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_ide_path)
end
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 3db870f229a..503246bbdcf 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Snippets > Create Snippet', :js do
include DropzoneHelper
+ include Spec::Support::Helpers::Features::SnippetSpecHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) do
@@ -16,96 +17,115 @@ RSpec.describe 'Projects > Snippets > Create Snippet', :js do
let(:file_content) { 'Hello World!' }
let(:md_description) { 'My Snippet **Description**' }
let(:description) { 'My Snippet Description' }
+ let(:snippet_title_field) { 'project_snippet_title' }
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
+ shared_examples 'snippet creation' do
+ def fill_form
+ snippet_fill_in_form(title: title, content: file_content, description: md_description)
+ end
- sign_in(user)
+ it 'shows collapsible description input' do
+ collapsed = description_field
- visit new_project_snippet_path(project)
- end
+ expect(page).not_to have_field(snippet_description_field)
+ expect(collapsed).to be_visible
- def description_field
- find('.js-description-input').find('input,textarea')
- end
+ collapsed.click
- def fill_form
- fill_in 'project_snippet_title', with: title
+ expect(page).to have_field(snippet_description_field)
+ expect(collapsed).not_to be_visible
+ end
- # Click placeholder first to expand full description field
- description_field.click
- fill_in 'project_snippet_description', with: md_description
+ it 'creates a new snippet' do
+ fill_form
+ click_button('Create snippet')
+ wait_for_requests
- page.within('.file-editor') do
- el = find('.inputarea')
- el.send_keys file_content
+ expect(page).to have_content(title)
+ expect(page).to have_content(file_content)
+ page.within(snippet_description_view_selector) do
+ expect(page).to have_content(description)
+ expect(page).to have_selector('strong')
+ end
end
- end
- it 'shows collapsible description input' do
- collapsed = description_field
+ it 'uploads a file when dragging into textarea' do
+ fill_form
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- expect(page).not_to have_field('project_snippet_description')
- expect(collapsed).to be_visible
+ expect(snippet_description_value).to have_content('banana_sample')
- collapsed.click
+ click_button('Create snippet')
+ wait_for_requests
- expect(page).to have_field('project_snippet_description')
- expect(collapsed).not_to be_visible
- end
+ link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
+ expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
+ end
+
+ context 'when the git operation fails' do
+ let(:error) { 'Error creating the snippet' }
- it 'creates a new snippet' do
- fill_form
- click_button('Create snippet')
- wait_for_requests
+ before do
+ allow_next_instance_of(Snippets::CreateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, error)
+ end
- expect(page).to have_content(title)
- expect(page).to have_content(file_content)
- page.within('.snippet-header .description') do
- expect(page).to have_content(description)
- expect(page).to have_selector('strong')
+ fill_form
+
+ click_button('Create snippet')
+ wait_for_requests
+ end
+
+ it 'renders the new page and displays the error' do
+ expect(page).to have_content(error)
+ expect(page).to have_content('New Snippet')
+ end
end
end
- it 'uploads a file when dragging into textarea' do
- fill_form
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ context 'Vue application' do
+ let(:snippet_description_field) { 'snippet-description' }
+ let(:snippet_description_view_selector) { '.snippet-header .snippet-description' }
- expect(page.find_field('project_snippet_description').value).to have_content('banana_sample')
+ before do
+ sign_in(user)
+
+ visit new_project_snippet_path(project)
+ end
- click_button('Create snippet')
- wait_for_requests
+ it_behaves_like 'snippet creation'
- link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
- end
+ it 'does not allow submitting the form without title and content' do
+ fill_in snippet_title_field, with: title
- it 'displays validation errors' do
- fill_in 'project_snippet_title', with: title
- click_button('Create snippet')
- wait_for_requests
+ expect(page).not_to have_button('Create snippet')
- expect(page).to have_selector('#error_explanation')
+ snippet_fill_in_form(title: title, content: file_content)
+ expect(page).to have_button('Create snippet')
+ end
end
- context 'when the git operation fails' do
- let(:error) { 'Error creating the snippet' }
+ context 'non-Vue application' do
+ let(:snippet_description_field) { 'project_snippet_description' }
+ let(:snippet_description_view_selector) { '.snippet-header .description' }
before do
- allow_next_instance_of(Snippets::CreateService) do |instance|
- allow(instance).to receive(:create_commit).and_raise(StandardError, error)
- end
+ stub_feature_flags(snippets_vue: false)
+ stub_feature_flags(snippets_edit_vue: false)
- fill_form
+ sign_in(user)
+ visit new_project_snippet_path(project)
+ end
+
+ it_behaves_like 'snippet creation'
+
+ it 'displays validation errors' do
+ fill_in snippet_title_field, with: title
click_button('Create snippet')
wait_for_requests
- end
- it 'renders the new page and displays the error' do
- expect(page).to have_content(error)
- expect(page).to have_content('New Snippet')
+ expect(page).to have_selector('#error_explanation')
end
end
end
diff --git a/spec/features/projects/snippets/user_updates_snippet_spec.rb b/spec/features/projects/snippets/user_updates_snippet_spec.rb
index a40113bd93e..193eaa9576a 100644
--- a/spec/features/projects/snippets/user_updates_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_updates_snippet_spec.rb
@@ -3,57 +3,81 @@
require 'spec_helper'
RSpec.describe 'Projects > Snippets > User updates a snippet', :js do
+ include Spec::Support::Helpers::Features::SnippetSpecHelpers
+
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:snippet, reload: true) { create(:project_snippet, :repository, project: project, author: user) }
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
+ let(:snippet_title_field) { 'project_snippet_title' }
+ def bootstrap_snippet
project.add_maintainer(user)
sign_in(user)
- visit(project_snippet_path(project, snippet))
+ page.visit(edit_project_snippet_path(project, snippet))
- page.within('.detail-page-header') do
- first(:link, 'Edit').click
- end
wait_for_all_requests
end
- it 'displays the snippet blob path and content' do
- blob = snippet.blobs.first
+ shared_examples 'snippet update' do
+ it 'displays the snippet blob path and content' do
+ blob = snippet.blobs.first
- aggregate_failures do
- expect(page.find_field('project_snippet_file_name').value).to eq blob.path
- expect(page.find('.file-content')).to have_content(blob.data.strip)
- expect(page.find('.snippet-file-content', visible: false).value).to eq blob.data
+ aggregate_failures do
+ expect(snippet_get_first_blob_path).to eq blob.path
+ expect(snippet_get_first_blob_value).to have_content(blob.data.strip)
+ end
end
- end
- it 'updates a snippet' do
- fill_in('project_snippet_title', with: 'Snippet new title')
- click_button('Save')
+ it 'updates a snippet' do
+ fill_in('project_snippet_title', with: 'Snippet new title')
+ click_button('Save')
+
+ expect(page).to have_content('Snippet new title')
+ end
+
+ context 'when the git operation fails' do
+ before do
+ allow_next_instance_of(Snippets::UpdateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
+ end
+
+ fill_in(snippet_title_field, with: 'Snippet new title')
+ fill_in(snippet_blob_path_field, match: :first, with: 'new_file_name')
+
+ click_button('Save')
+ end
- expect(page).to have_content('Snippet new title')
+ it 'renders edit page and displays the error' do
+ expect(page.find('.flash-container')).to have_content('Error updating the snippet - Error Message')
+ expect(page).to have_content('Edit Snippet')
+ end
+ end
end
- context 'when the git operation fails' do
+ context 'Vue application' do
before do
- allow_next_instance_of(Snippets::UpdateService) do |instance|
- allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
- end
+ bootstrap_snippet
+ end
- fill_in('project_snippet_title', with: 'Snippet new title')
- fill_in('project_snippet_file_name', with: 'new_file_name')
+ it_behaves_like 'snippet update' do
+ let(:snippet_blob_path_field) { 'snippet_file_name' }
+ let(:snippet_blob_content_selector) { '.file-content' }
+ end
+ end
- click_button('Save')
+ context 'non-Vue application' do
+ before do
+ stub_feature_flags(snippets_vue: false)
+ stub_feature_flags(snippets_edit_vue: false)
+
+ bootstrap_snippet
end
- it 'renders edit page and displays the error' do
- expect(page.find('.flash-container span').text).to eq('Error updating the snippet - Error Message')
- expect(page).to have_content('Edit Snippet')
+ it_behaves_like 'snippet update' do
+ let(:snippet_blob_path_field) { 'project_snippet_file_name' }
+ let(:snippet_blob_content_selector) { '.file-content' }
end
end
end
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index 1d443e0b339..50d7b353c46 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -6,10 +6,6 @@ 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
@@ -134,6 +130,7 @@ RSpec.describe 'Projects > User sees sidebar' do
context 'as guest' do
let(:guest) { create(:user) }
+ let!(:issue) { create(:issue, :opened, project: project, author: guest) }
before do
project.add_guest(guest)
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 6f78f888c12..d220db01c24 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -9,8 +9,6 @@ RSpec.describe 'View on environment', :js do
let(:user) { project.creator }
before do
- stub_feature_flags(diffs_batch_load: false)
-
project.add_maintainer(user)
end
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 8eba2c98595..8f2fb9e827c 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Projects > Wiki > User previews markdown changes', :js do
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, title: 'home', content: '[some link](other-page)') }
let(:wiki_content) do
<<-HEREDOC
+Some text so key event for [ does not trigger an incorrect replacement.
[regular link](regular)
[relative link 1](../relative)
[relative link 2](./relative)
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 3577498c3b4..970500985ae 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -159,7 +159,7 @@ RSpec.describe 'Project' do
describe 'remove forked relationship', :js do
let(:user) { create(:user) }
- let(:project) { fork_project(create(:project, :public), user, namespace_id: user.namespace) }
+ let(:project) { fork_project(create(:project, :public), user, namespace: user.namespace) }
before do
sign_in user
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index e9943347522..900ed35adea 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -6,7 +6,13 @@ RSpec.describe 'User searches for issues', :js do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let!(:issue1) { create(:issue, title: 'Foo', project: project) }
- let!(:issue2) { create(:issue, title: 'Bar', project: project) }
+ let!(:issue2) { create(:issue, :closed, :confidential, title: 'Bar', project: project) }
+
+ def search_for_issue(search)
+ fill_in('dashboard_search', with: search)
+ find('.btn-search').click
+ select_search_scope('Issues')
+ end
context 'when signed in' do
before do
@@ -19,9 +25,7 @@ RSpec.describe 'User searches for issues', :js do
include_examples 'top right search form'
it 'finds an issue' do
- fill_in('dashboard_search', with: issue1.title)
- find('.btn-search').click
- select_search_scope('Issues')
+ search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_link(issue1.title)
@@ -29,6 +33,40 @@ RSpec.describe 'User searches for issues', :js do
end
end
+ it 'hides confidential icon for non-confidential issues' do
+ search_for_issue(issue1.title)
+
+ page.within('.results') do
+ expect(page).not_to have_css('[data-testid="eye-slash-icon"]')
+ end
+ end
+
+ it 'shows confidential icon for confidential issues' do
+ search_for_issue(issue2.title)
+
+ page.within('.results') do
+ expect(page).to have_css('[data-testid="eye-slash-icon"]')
+ end
+ end
+
+ it 'shows correct badge for open issues' do
+ search_for_issue(issue1.title)
+
+ page.within('.results') do
+ expect(page).to have_css('.badge-success')
+ expect(page).not_to have_css('.badge-info')
+ end
+ end
+
+ it 'shows correct badge for closed issues' do
+ search_for_issue(issue2.title)
+
+ page.within('.results') do
+ expect(page).not_to have_css('.badge-success')
+ expect(page).to have_css('.badge-info')
+ end
+ end
+
context 'when on a project page' do
it 'finds an issue' do
find('.js-search-project-dropdown').click
@@ -37,9 +75,7 @@ RSpec.describe 'User searches for issues', :js do
click_link(project.full_name)
end
- fill_in('dashboard_search', with: issue1.title)
- find('.btn-search').click
- select_search_scope('Issues')
+ search_for_issue(issue1.title)
page.within('.results') do
expect(page).to have_link(issue1.title)
@@ -50,22 +86,33 @@ RSpec.describe 'User searches for issues', :js do
end
context 'when signed out' do
- let(:project) { create(:project, :public) }
+ context 'when block_anonymous_global_searches is disabled' do
+ let(:project) { create(:project, :public) }
- before do
- visit(search_path)
- end
+ before do
+ stub_feature_flags(block_anonymous_global_searches: false)
+ visit(search_path)
+ end
- include_examples 'top right search form'
+ include_examples 'top right search form'
- it 'finds an issue' do
- fill_in('dashboard_search', with: issue1.title)
- find('.btn-search').click
- select_search_scope('Issues')
+ it 'finds an issue' do
+ search_for_issue(issue1.title)
- page.within('.results') do
- expect(page).to have_link(issue1.title)
- expect(page).not_to have_link(issue2.title)
+ page.within('.results') do
+ expect(page).to have_link(issue1.title)
+ expect(page).not_to have_link(issue2.title)
+ end
+ end
+ end
+
+ context 'when block_anonymous_global_searches is enabled' do
+ before do
+ visit(search_path)
+ end
+
+ it 'is redirected to login page' do
+ expect(page).to have_content('You must be logged in to search across all of GitLab')
end
end
end
diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb
index 7bb5a4da7d0..b64909dd42f 100644
--- a/spec/features/search/user_searches_for_projects_spec.rb
+++ b/spec/features/search/user_searches_for_projects_spec.rb
@@ -6,31 +6,44 @@ RSpec.describe 'User searches for projects' do
let!(:project) { create(:project, :public, name: 'Shop') }
context 'when signed out' do
- include_examples 'top right search form'
+ context 'when block_anonymous_global_searches is disabled' do
+ before do
+ stub_feature_flags(block_anonymous_global_searches: false)
+ end
- it 'finds a project' do
- visit(search_path)
+ include_examples 'top right search form'
- fill_in('dashboard_search', with: project.name[0..3])
- click_button('Search')
+ it 'finds a project' do
+ visit(search_path)
- expect(page).to have_link(project.name)
- end
+ fill_in('dashboard_search', with: project.name[0..3])
+ click_button('Search')
- it 'preserves the group being searched in' do
- visit(search_path(group_id: project.namespace.id))
+ expect(page).to have_link(project.name)
+ end
- submit_search('foo')
+ it 'preserves the group being searched in' do
+ visit(search_path(group_id: project.namespace.id))
- expect(find('#group_id', visible: false).value).to eq(project.namespace.id.to_s)
- end
+ submit_search('foo')
+
+ expect(find('#group_id', visible: false).value).to eq(project.namespace.id.to_s)
+ end
- it 'preserves the project being searched in' do
- visit(search_path(project_id: project.id))
+ it 'preserves the project being searched in' do
+ visit(search_path(project_id: project.id))
- submit_search('foo')
+ submit_search('foo')
+
+ expect(find('#project_id', visible: false).value).to eq(project.id.to_s)
+ end
+ end
- expect(find('#project_id', visible: false).value).to eq(project.id.to_s)
+ context 'when block_anonymous_global_searches is enabled' do
+ it 'is redirected to login page' do
+ visit(search_path)
+ expect(page).to have_content('You must be logged in to search across all of GitLab')
+ end
end
end
end
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index 37e83d1e888..cfda25b9ab4 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe 'User uses header search field', :js do
context 'when using the keyboard shortcut' do
before do
- find('#search.js-autocomplete-disabled')
+ find('#search')
find('body').native.send_keys('s')
end
@@ -39,7 +39,7 @@ RSpec.describe 'User uses header search field', :js do
context 'when clicking the search field' do
before do
- page.find('#search.js-autocomplete-disabled').click
+ page.find('#search').click
end
it 'shows category search dropdown' do
diff --git a/spec/features/snippets/spam_snippets_spec.rb b/spec/features/snippets/spam_snippets_spec.rb
index e6a9467a3d7..1483ba4bf8f 100644
--- a/spec/features/snippets/spam_snippets_spec.rb
+++ b/spec/features/snippets/spam_snippets_spec.rb
@@ -11,8 +11,6 @@ RSpec.shared_examples_for 'snippet editor' do
before do
stub_feature_flags(allow_possible_spam: false)
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
Gitlab::CurrentSettings.update!(
@@ -125,5 +123,20 @@ end
RSpec.describe 'User creates snippet', :js do
let_it_be(:user) { create(:user) }
- it_behaves_like "snippet editor"
+ context 'Vue application' do
+ before do
+ stub_feature_flags(snippets_edit_vue: false)
+ end
+
+ it_behaves_like "snippet editor"
+ end
+
+ context 'non-Vue application' do
+ before do
+ stub_feature_flags(snippets_vue: false)
+ stub_feature_flags(snippets_edit_vue: false)
+ end
+
+ it_behaves_like "snippet editor"
+ end
end
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index f4c6536d6d3..eabca028b8c 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'User creates snippet', :js do
include DropzoneHelper
+ include Spec::Support::Helpers::Features::SnippetSpecHelpers
let_it_be(:user) { create(:user) }
@@ -12,149 +13,163 @@ RSpec.describe 'User creates snippet', :js do
let(:md_description) { 'My Snippet **Description**' }
let(:description) { 'My Snippet Description' }
let(:created_snippet) { Snippet.last }
-
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
- sign_in(user)
- end
+ let(:snippet_title_field) { 'personal_snippet_title' }
def description_field
find('.js-description-input').find('input,textarea')
end
- def fill_form
- fill_in 'personal_snippet_title', with: title
-
- # Click placeholder first to expand full description field
- description_field.click
- fill_in 'personal_snippet_description', with: md_description
-
- page.within('.file-editor') do
- el = find('.inputarea')
- el.send_keys file_content
+ shared_examples 'snippet creation' do
+ def fill_form
+ snippet_fill_in_form(title: title, content: file_content, description: md_description)
end
- end
-
- it 'Authenticated user creates a snippet' do
- visit new_snippet_path
- fill_form
+ it 'Authenticated user creates a snippet' do
+ fill_form
- click_button('Create snippet')
- wait_for_requests
+ click_button('Create snippet')
+ wait_for_requests
- expect(page).to have_content(title)
- page.within('.snippet-header .description') do
- expect(page).to have_content(description)
- expect(page).to have_selector('strong')
+ expect(page).to have_content(title)
+ page.within(snippet_description_view_selector) do
+ expect(page).to have_content(description)
+ expect(page).to have_selector('strong')
+ end
+ expect(page).to have_content(file_content)
end
- expect(page).to have_content(file_content)
- end
- it 'previews a snippet with file' do
- visit new_snippet_path
+ it 'uploads a file when dragging into textarea' do
+ fill_form
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- # Click placeholder first to expand full description field
- description_field.click
- fill_in 'personal_snippet_description', with: 'My Snippet'
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- find('.js-md-preview-button').click
+ expect(snippet_description_value).to have_content('banana_sample')
- page.within('#new_personal_snippet .md-preview-holder') do
- expect(page).to have_content('My Snippet')
+ click_button('Create snippet')
+ wait_for_requests
link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
- # Adds a cache buster for checking if the image exists as Selenium is now handling the cached requests
- # not anymore as requests when they come straight from memory cache.
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
expect(reqs.first.status_code).to eq(200)
end
- end
- it 'uploads a file when dragging into textarea' do
- visit new_snippet_path
+ context 'when the git operation fails' do
+ let(:error) { 'Error creating the snippet' }
- fill_form
+ before do
+ allow_next_instance_of(Snippets::CreateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, error)
+ end
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ fill_form
+ click_button('Create snippet')
+ wait_for_requests
+ end
+
+ it 'renders the new page and displays the error' do
+ expect(page).to have_content(error)
+ expect(page).to have_content('New Snippet')
- expect(page.find_field("personal_snippet_description").value).to have_content('banana_sample')
+ action = find('form.snippet-form')['action']
+ expect(action).to include("/snippets")
+ end
+ end
- click_button('Create snippet')
- wait_for_requests
+ context 'when snippets default visibility level is restricted' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE],
+ default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
+ end
- link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
+ it 'creates a snippet using the lowest available visibility level as default' do
+ visit new_snippet_path
- reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
- expect(reqs.first.status_code).to eq(200)
- end
+ fill_form
- context 'when the git operation fails' do
- let(:error) { 'Error creating the snippet' }
+ click_button('Create snippet')
+ wait_for_requests
- before do
- allow_next_instance_of(Snippets::CreateService) do |instance|
- allow(instance).to receive(:create_commit).and_raise(StandardError, error)
+ expect(find('.blob-content')).to have_content(file_content)
+ expect(Snippet.last.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
+ end
- visit new_snippet_path
+ it_behaves_like 'personal snippet with references' do
+ let(:container) { snippet_description_view_selector }
+ let(:md_description) { references }
- fill_form
+ subject do
+ fill_form
+ click_button('Create snippet')
- click_button('Create snippet')
- wait_for_requests
+ wait_for_requests
+ end
end
+ end
+
+ context 'Vue application' do
+ let(:snippet_description_field) { 'snippet-description' }
+ let(:snippet_description_view_selector) { '.snippet-header .snippet-description' }
- it 'renders the new page and displays the error' do
- expect(page).to have_content(error)
- expect(page).to have_content('New Snippet')
+ before do
+ sign_in(user)
- action = find('form.snippet-form')['action']
- expect(action).to match(%r{/snippets\z})
+ visit new_snippet_path
end
- end
- it 'validation fails for the first time' do
- visit new_snippet_path
+ it_behaves_like 'snippet creation'
- fill_in 'personal_snippet_title', with: title
- click_button('Create snippet')
+ it 'validation fails for the first time' do
+ fill_in snippet_title_field, with: title
- expect(page).to have_selector('#error_explanation')
+ expect(page).not_to have_button('Create snippet')
+
+ snippet_fill_in_form(title: title, content: file_content)
+ expect(page).to have_button('Create snippet')
+ end
end
- context 'when snippets default visibility level is restricted' do
+ context 'non-Vue application' do
+ let(:snippet_description_field) { 'personal_snippet_description' }
+ let(:snippet_description_view_selector) { '.snippet-header .description' }
+
before do
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE],
- default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
- end
+ stub_feature_flags(snippets_vue: false)
+ stub_feature_flags(snippets_edit_vue: false)
+
+ sign_in(user)
- it 'creates a snippet using the lowest available visibility level as default' do
visit new_snippet_path
+ end
- fill_form
+ it_behaves_like 'snippet creation'
+ it 'validation fails for the first time' do
+ fill_in snippet_title_field, with: title
click_button('Create snippet')
- wait_for_requests
- expect(created_snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ expect(page).to have_selector('#error_explanation')
end
- end
- it_behaves_like 'personal snippet with references' do
- let(:container) { '.snippet-header .description' }
- let(:md_description) { references }
+ it 'previews a snippet with file' do
+ # Click placeholder first to expand full description field
+ description_field.click
+ fill_in snippet_description_field, with: 'My Snippet'
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ find('.js-md-preview-button').click
- subject do
- visit new_snippet_path
- fill_form
- click_button('Create snippet')
+ page.within('.md-preview-holder') do
+ expect(page).to have_content('My Snippet')
- wait_for_requests
+ link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
+ expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z})
+
+ # Adds a cache buster for checking if the image exists as Selenium is now handling the cached requests
+ # not anymore as requests when they come straight from memory cache.
+ reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
+ expect(reqs.first.status_code).to eq(200)
+ end
end
end
end
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index 5773904dedf..9a83eb58b63 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -4,87 +4,114 @@ require 'spec_helper'
RSpec.describe 'User edits snippet', :js do
include DropzoneHelper
+ include Spec::Support::Helpers::Features::SnippetSpecHelpers
let_it_be(:file_name) { 'test.rb' }
let_it_be(:content) { 'puts "test"' }
let_it_be(:user) { create(:user) }
let_it_be(:snippet, reload: true) { create(:personal_snippet, :repository, :public, file_name: file_name, content: content, author: user) }
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
+ let(:snippet_title_field) { 'personal_snippet_title' }
- sign_in(user)
+ shared_examples 'snippet editing' do
+ it 'displays the snippet blob path and content' do
+ blob = snippet.blobs.first
- visit edit_snippet_path(snippet)
- wait_for_all_requests
- end
+ aggregate_failures do
+ expect(snippet_get_first_blob_path).to eq blob.path
+ expect(snippet_get_first_blob_value).to have_content(blob.data.strip)
+ end
+ end
- it 'displays the snippet blob path and content' do
- blob = snippet.blobs.first
+ it 'updates the snippet' do
+ fill_in snippet_title_field, with: 'New Snippet Title'
- aggregate_failures do
- expect(page.find_field('personal_snippet_file_name').value).to eq blob.path
- expect(page.find('.file-content')).to have_content(blob.data.strip)
- expect(page.find('.snippet-file-content', visible: false).value).to eq blob.data
+ click_button('Save changes')
+ wait_for_requests
+
+ expect(page).to have_content('New Snippet Title')
end
- end
- it 'updates the snippet' do
- fill_in 'personal_snippet_title', with: 'New Snippet Title'
+ it 'updates the snippet with files attached' do
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ expect(snippet_description_value).to have_content('banana_sample')
- click_button('Save changes')
- wait_for_requests
+ click_button('Save changes')
+ wait_for_requests
- expect(page).to have_content('New Snippet Title')
- end
+ link = find('a.no-attachment-icon img:not(.lazy)[alt="banana_sample"]')['src']
+ expect(link).to match(%r{/uploads/-/system/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
+ end
- it 'updates the snippet with files attached' do
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- expect(page.find_field('personal_snippet_description').value).to have_content('banana_sample')
+ it 'updates the snippet to make it internal' do
+ choose 'Internal'
- click_button('Save changes')
- wait_for_requests
+ click_button 'Save changes'
+ wait_for_requests
- link = find('a.no-attachment-icon img:not(.lazy)[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/-/system/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
- end
+ expect(page).to have_no_selector('[data-testid="lock-icon"]')
+ expect(page).to have_selector('[data-testid="shield-icon"]')
+ end
- it 'updates the snippet to make it internal' do
- choose 'Internal'
+ it 'updates the snippet to make it public' do
+ choose 'Public'
- click_button 'Save changes'
- wait_for_requests
+ click_button 'Save changes'
+ wait_for_requests
- expect(page).to have_no_selector('[data-testid="lock-icon"]')
- expect(page).to have_selector('[data-testid="shield-icon"]')
- end
+ expect(page).to have_no_selector('[data-testid="lock-icon"]')
+ expect(page).to have_selector('[data-testid="earth-icon"]')
+ end
- it 'updates the snippet to make it public' do
- choose 'Public'
+ context 'when the git operation fails' do
+ before do
+ allow_next_instance_of(Snippets::UpdateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
+ end
- click_button 'Save changes'
- wait_for_requests
+ fill_in snippet_title_field, with: 'New Snippet Title'
+ fill_in snippet_blob_path_field, with: 'new_file_name', match: :first
- expect(page).to have_no_selector('[data-testid="lock-icon"]')
- expect(page).to have_selector('[data-testid="earth-icon"]')
- end
+ click_button('Save changes')
+ end
- context 'when the git operation fails' do
- before do
- allow_next_instance_of(Snippets::UpdateService) do |instance|
- allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
+ it 'renders edit page and displays the error' do
+ expect(page.find('.flash-container')).to have_content('Error updating the snippet - Error Message')
+ expect(page).to have_content('Edit Snippet')
end
+ end
+ end
- fill_in 'personal_snippet_title', with: 'New Snippet Title'
- fill_in 'personal_snippet_file_name', with: 'new_file_name'
+ context 'Vue application' do
+ it_behaves_like 'snippet editing' do
+ let(:snippet_blob_path_field) { 'snippet_file_name' }
+ let(:snippet_blob_content_selector) { '.file-content' }
+ let(:snippet_description_field) { 'snippet-description' }
- click_button('Save changes')
+ before do
+ sign_in(user)
+
+ visit edit_snippet_path(snippet)
+ wait_for_all_requests
+ end
end
+ end
+
+ context 'non-Vue application' do
+ it_behaves_like 'snippet editing' do
+ let(:snippet_blob_path_field) { 'personal_snippet_file_name' }
+ let(:snippet_blob_content_selector) { '.file-content' }
+ let(:snippet_description_field) { 'personal_snippet_description' }
- it 'renders edit page and displays the error' do
- expect(page.find('.flash-container span').text).to eq('Error updating the snippet - Error Message')
- expect(page).to have_content('Edit Snippet')
+ before do
+ stub_feature_flags(snippets_vue: false)
+ stub_feature_flags(snippets_edit_vue: false)
+
+ sign_in(user)
+
+ visit edit_snippet_path(snippet)
+ wait_for_all_requests
+ end
end
end
end
diff --git a/spec/features/static_site_editor_spec.rb b/spec/features/static_site_editor_spec.rb
index 9ae23b4bdec..b67e47b6ac4 100644
--- a/spec/features/static_site_editor_spec.rb
+++ b/spec/features/static_site_editor_spec.rb
@@ -13,7 +13,11 @@ RSpec.describe 'Static Site Editor' do
visit project_show_sse_path(project, 'master/README.md')
end
- it 'renders Static Site Editor page' do
- expect(page).to have_selector('#static-site-editor')
+ it 'renders Static Site Editor page with generated and file attributes' do
+ # assert generated config value is present
+ expect(page).to have_css('#static-site-editor[data-branch="master"]')
+
+ # assert file config value is present
+ expect(page).to have_css('#static-site-editor[data-static-site-generator="middleman"]')
end
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index fc3f8a94318..a9cfe794177 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe 'Task Lists' do
include Warden::Test::Helpers
- let(:project) { create(:project, :public, :repository) }
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user2) { create(:user) }
let(:markdown) do
<<-MARKDOWN.strip_heredoc
@@ -72,12 +72,12 @@ RSpec.describe 'Task Lists' do
EOT
end
- before do
- Warden.test_mode!
-
+ before(:all) do
project.add_maintainer(user)
project.add_guest(user2)
+ end
+ before do
login_as(user)
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 8dbedc0a7ee..5762a54a717 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -3,22 +3,14 @@
require 'spec_helper'
RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
- def manage_two_factor_authentication
- click_on 'Manage two-factor authentication'
- expect(page).to have_content("Set up new U2F device")
- wait_for_requests
- end
+ include Spec::Support::Helpers::Features::TwoFactorHelpers
- def register_u2f_device(u2f_device = nil, name: 'My device')
- u2f_device ||= FakeU2fDevice.new(page, name)
- u2f_device.respond_to_u2f_registration
- click_on 'Set up new U2F device'
- expect(page).to have_content('Your device was successfully set up')
- fill_in "Pick a name", with: name
- click_on 'Register U2F device'
- u2f_device
+ before do
+ stub_feature_flags(webauthn: false)
end
+ it_behaves_like 'hardware device for 2fa', 'U2F'
+
describe "registration" do
let(:user) { create(:user) }
@@ -27,31 +19,7 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
user.update_attribute(:otp_required_for_login, true)
end
- describe 'when 2FA via OTP is disabled' do
- before do
- user.update_attribute(:otp_required_for_login, false)
- end
-
- it 'does not allow registering a new device' do
- visit profile_account_path
- click_on 'Enable two-factor authentication'
-
- expect(page).to have_button('Set up new U2F device', disabled: true)
- end
- end
-
describe 'when 2FA via OTP is enabled' do
- it 'allows registering a new device with a name' do
- visit profile_account_path
- manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
-
- u2f_device = register_u2f_device
-
- expect(page).to have_content(u2f_device.name)
- expect(page).to have_content('Your U2F device was registered')
- end
-
it 'allows registering more than one device' do
visit profile_account_path
@@ -68,21 +36,6 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
expect(page).to have_content(second_device.name)
expect(U2fRegistration.count).to eq(2)
end
-
- it 'allows deleting a device' do
- visit profile_account_path
- manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
-
- first_u2f_device = register_u2f_device
- second_u2f_device = register_u2f_device(name: 'My other device')
-
- accept_confirm { click_on "Delete", match: :first }
-
- expect(page).to have_content('Successfully deleted')
- expect(page.body).not_to match(first_u2f_device.name)
- expect(page).to have_content(second_u2f_device.name)
- end
end
it 'allows the same device to be registered for multiple users' do
@@ -111,9 +64,9 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
# Have the "u2f device" respond with bad data
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Set up new U2F device'
+ click_on 'Set up new device'
expect(page).to have_content('Your device was successfully set up')
- click_on 'Register U2F device'
+ click_on 'Register device'
expect(U2fRegistration.count).to eq(0)
expect(page).to have_content("The form contains the following error")
@@ -126,9 +79,9 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
# Failed registration
page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
- click_on 'Set up new U2F device'
+ click_on 'Set up new device'
expect(page).to have_content('Your device was successfully set up')
- click_on 'Register U2F device'
+ click_on 'Register device'
expect(page).to have_content("The form contains the following error")
# Successful registration
@@ -228,12 +181,12 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
user = gitlab_sign_in(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_two_factor_auth_path
- expect(page).to have_content("Your U2F device needs to be set up.")
+ expect(page).to have_content("Your device needs to be set up.")
first_device = register_u2f_device
# Register second device
visit profile_two_factor_auth_path
- expect(page).to have_content("Your U2F device needs to be set up.")
+ expect(page).to have_content("Your device needs to be set up.")
second_device = register_u2f_device(name: 'My other device')
gitlab_sign_out
@@ -249,50 +202,4 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
end
end
end
-
- describe 'fallback code authentication' do
- let(:user) { create(:user) }
-
- def assert_fallback_ui(page)
- expect(page).to have_button('Verify code')
- expect(page).to have_css('#user_otp_attempt')
- expect(page).not_to have_link('Sign in via 2FA code')
- expect(page).not_to have_css('#js-authenticate-token-2fa')
- end
-
- before do
- # Register and logout
- gitlab_sign_in(user)
- user.update_attribute(:otp_required_for_login, true)
- visit profile_account_path
- end
-
- describe 'when no u2f device is registered' do
- before do
- gitlab_sign_out
- gitlab_sign_in(user)
- end
-
- it 'shows the fallback otp code UI' do
- assert_fallback_ui(page)
- end
- end
-
- describe 'when a u2f device is registered' do
- before do
- manage_two_factor_authentication
- @u2f_device = register_u2f_device
- gitlab_sign_out
- gitlab_sign_in(user)
- end
-
- it 'provides a button that shows the fallback otp code UI' do
- expect(page).to have_link('Sign in via 2FA code')
-
- click_link('Sign in via 2FA code')
-
- assert_fallback_ui(page)
- end
- end
- end
end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 6f6ebe34c03..853c381fe6b 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -511,7 +511,7 @@ RSpec.describe 'Login' do
context 'within the grace period' do
it 'redirects to two-factor configuration page' do
- Timecop.freeze do
+ freeze_time do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 332be055027..5fd0e677cd0 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -152,7 +152,6 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
@@ -180,19 +179,13 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
expect { click_button 'Register' }.to change { User.count }.by(1)
- if Gitlab::Experimentation.enabled?(:signup_flow)
- expect(current_path).to eq users_sign_up_welcome_path
- else
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Please check your email (#{new_user.email}) to verify that you own this address and unlock the power of CI/CD.")
- end
+ expect(current_path).to eq users_sign_up_welcome_path
end
end
end
@@ -209,18 +202,12 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email.capitalize
end
fill_in 'new_user_password', with: new_user.password
click_button "Register"
- if Gitlab::Experimentation.enabled?(:signup_flow)
- expect(current_path).to eq users_sign_up_welcome_path
- else
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Welcome! You have signed up successfully.")
- end
+ expect(current_path).to eq users_sign_up_welcome_path
end
end
@@ -240,18 +227,12 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
click_button "Register"
- if Gitlab::Experimentation.enabled?(:signup_flow)
- expect(current_path).to eq users_sign_up_welcome_path
- else
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Welcome! You have signed up successfully.")
- end
+ expect(current_path).to eq users_sign_up_welcome_path
end
end
end
@@ -275,14 +256,7 @@ RSpec.shared_examples 'Signup' do
click_button "Register"
expect(current_path).to eq user_registration_path
-
- if Gitlab::Experimentation.enabled?(:signup_flow)
- expect(page).to have_content("error prohibited this user from being saved")
- else
- expect(page).to have_content("errors prohibited this user from being saved")
- expect(page).to have_content("Email confirmation doesn't match")
- end
-
+ expect(page).to have_content("error prohibited this user from being saved")
expect(page).to have_content("Email has already been taken")
end
@@ -324,7 +298,6 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
@@ -346,7 +319,6 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
@@ -354,11 +326,7 @@ RSpec.shared_examples 'Signup' do
click_button "Register"
- if Gitlab::Experimentation.enabled?(:signup_flow)
- expect(current_path).to eq users_sign_up_welcome_path
- else
- expect(current_path).to eq dashboard_projects_path
- end
+ expect(current_path).to eq users_sign_up_welcome_path
end
end
@@ -393,7 +361,6 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
@@ -415,7 +382,6 @@ RSpec.shared_examples 'Signup' do
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
end
fill_in 'new_user_password', with: new_user.password
@@ -425,6 +391,35 @@ RSpec.shared_examples 'Signup' do
end
end
end
+
+ it 'redirects to step 2 of the signup process, sets the role and redirects back' do
+ new_user = build_stubbed(:user)
+ visit new_user_registration_path
+
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+
+ if Gitlab::Experimentation.enabled?(:signup_flow)
+ fill_in 'new_user_first_name', with: new_user.first_name
+ fill_in 'new_user_last_name', with: new_user.last_name
+ else
+ fill_in 'new_user_name', with: new_user.name
+ end
+
+ fill_in 'new_user_password', with: new_user.password
+ click_button 'Register'
+ visit new_project_path
+
+ expect(page).to have_current_path(users_sign_up_welcome_path)
+
+ select 'Software Developer', from: 'user_role'
+ click_button 'Get started!'
+ new_user = User.find_by_username(new_user.username)
+
+ expect(new_user.software_developer_role?).to be_truthy
+ expect(new_user.setup_for_company).to be_nil
+ expect(page).to have_current_path(new_project_path)
+ end
end
RSpec.shared_examples 'Signup name validation' do |field, max_length|
@@ -485,31 +480,6 @@ RSpec.describe 'With experimental flow' do
it_behaves_like 'Signup name validation', 'new_user_first_name', 127
it_behaves_like 'Signup name validation', 'new_user_last_name', 127
- describe 'when role is required' do
- it 'after registering, it redirects to step 2 of the signup process, sets the name and role and then redirects to the original requested url' do
- new_user = build_stubbed(:user)
- visit new_user_registration_path
- fill_in 'new_user_first_name', with: new_user.first_name
- fill_in 'new_user_last_name', with: new_user.last_name
- fill_in 'new_user_username', with: new_user.username
- fill_in 'new_user_email', with: new_user.email
- fill_in 'new_user_password', with: new_user.password
- click_button 'Register'
- visit new_project_path
-
- expect(page).to have_current_path(users_sign_up_welcome_path)
-
- select 'Software Developer', from: 'user_role'
- choose 'user_setup_for_company_true'
- click_button 'Get started!'
- new_user = User.find_by_username(new_user.username)
-
- expect(new_user.software_developer_role?).to be_truthy
- expect(new_user.setup_for_company).to be_truthy
- expect(page).to have_current_path(new_project_path)
- end
- end
-
context 'when terms_opt_in experimental is enabled' do
include TermsHelper
@@ -521,14 +491,13 @@ RSpec.describe 'With experimental flow' do
it 'terms are checked by default' do
new_user = build_stubbed(:user)
- visit new_user_registration_path
- fill_in 'new_user_username', with: new_user.username
- fill_in 'new_user_email', with: new_user.email
+ visit new_user_registration_path
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
fill_in 'new_user_password', with: new_user.password
-
click_button 'Register'
expect(current_path).to eq users_sign_up_welcome_path
diff --git a/spec/features/webauthn_spec.rb b/spec/features/webauthn_spec.rb
new file mode 100644
index 00000000000..2ffb6bb3477
--- /dev/null
+++ b/spec/features/webauthn_spec.rb
@@ -0,0 +1,234 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Using WebAuthn Devices for Authentication', :js do
+ include Spec::Support::Helpers::Features::TwoFactorHelpers
+ let(:app_id) { "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}" }
+
+ before do
+ WebAuthn.configuration.origin = app_id
+ end
+
+ it_behaves_like 'hardware device for 2fa', 'WebAuthn'
+
+ describe 'registration' do
+ let(:user) { create(:user) }
+
+ before do
+ gitlab_sign_in(user)
+ user.update_attribute(:otp_required_for_login, true)
+ end
+
+ describe 'when 2FA via OTP is enabled' do
+ it 'allows registering more than one device' do
+ visit profile_account_path
+
+ # First device
+ manage_two_factor_authentication
+ first_device = register_webauthn_device
+ expect(page).to have_content('Your WebAuthn device was registered')
+
+ # Second device
+ second_device = register_webauthn_device(name: 'My other device')
+ expect(page).to have_content('Your WebAuthn device was registered')
+
+ expect(page).to have_content(first_device.name)
+ expect(page).to have_content(second_device.name)
+ expect(WebauthnRegistration.count).to eq(2)
+ end
+ end
+
+ it 'allows the same device to be registered for multiple users' do
+ # First user
+ visit profile_account_path
+ manage_two_factor_authentication
+ webauthn_device = register_webauthn_device
+ expect(page).to have_content('Your WebAuthn device was registered')
+ gitlab_sign_out
+
+ # Second user
+ user = gitlab_sign_in(:user)
+ user.update_attribute(:otp_required_for_login, true)
+ visit profile_account_path
+ manage_two_factor_authentication
+ register_webauthn_device(webauthn_device, name: 'My other device')
+ expect(page).to have_content('Your WebAuthn device was registered')
+
+ expect(WebauthnRegistration.count).to eq(2)
+ end
+
+ context 'when there are form errors' do
+ let(:mock_register_js) do
+ <<~JS
+ const mockResponse = {
+ type: 'public-key',
+ id: '',
+ rawId: '',
+ response: {
+ clientDataJSON: '',
+ attestationObject: '',
+ },
+ getClientExtensionResults: () => {},
+ };
+ navigator.credentials.create = function(_) {return Promise.resolve(mockResponse);}
+ JS
+ end
+
+ it "doesn't register the device if there are errors" do
+ visit profile_account_path
+ manage_two_factor_authentication
+
+ # Have the "webauthn device" respond with bad data
+ page.execute_script(mock_register_js)
+ click_on 'Set up new device'
+ expect(page).to have_content('Your device was successfully set up')
+ click_on 'Register device'
+
+ expect(WebauthnRegistration.count).to eq(0)
+ expect(page).to have_content('The form contains the following error')
+ expect(page).to have_content('did not send a valid JSON response')
+ end
+
+ it 'allows retrying registration' do
+ visit profile_account_path
+ manage_two_factor_authentication
+
+ # Failed registration
+ page.execute_script(mock_register_js)
+ click_on 'Set up new device'
+ expect(page).to have_content('Your device was successfully set up')
+ click_on 'Register device'
+ expect(page).to have_content('The form contains the following error')
+
+ # Successful registration
+ register_webauthn_device
+
+ expect(page).to have_content('Your WebAuthn device was registered')
+ expect(WebauthnRegistration.count).to eq(1)
+ end
+ end
+ end
+
+ describe 'authentication' do
+ let(:otp_required_for_login) { true }
+ let(:user) { create(:user, webauthn_xid: WebAuthn.generate_user_id, otp_required_for_login: otp_required_for_login) }
+
+ describe 'when there is only an U2F device' do
+ let!(:u2f_device) do
+ fake_device = U2F::FakeU2F.new(app_id) # "Client"
+ u2f = U2F::U2F.new(app_id) # "Server"
+
+ challenges = u2f.registration_requests.map(&:challenge)
+ device_response = fake_device.register_response(challenges[0])
+ device_registration_params = { device_response: device_response,
+ name: 'My device' }
+
+ U2fRegistration.register(user, app_id, device_registration_params, challenges)
+ FakeU2fDevice.new(page, 'My device', fake_device)
+ end
+
+ it 'falls back to U2F' do
+ gitlab_sign_in(user)
+
+ u2f_device.respond_to_u2f_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+
+ describe 'when there is a WebAuthn device' do
+ let!(:webauthn_device) do
+ add_webauthn_device(app_id, user)
+ end
+
+ describe 'when 2FA via OTP is disabled' do
+ let(:otp_required_for_login) { false }
+
+ it 'allows logging in with the WebAuthn device' do
+ gitlab_sign_in(user)
+
+ webauthn_device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+
+ describe 'when 2FA via OTP is enabled' do
+ it 'allows logging in with the WebAuthn device' do
+ gitlab_sign_in(user)
+
+ webauthn_device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+
+ describe 'when a given WebAuthn device has already been registered by another user' do
+ describe 'but not the current user' do
+ let(:other_user) { create(:user, webauthn_xid: WebAuthn.generate_user_id, otp_required_for_login: otp_required_for_login) }
+
+ it 'does not allow logging in with that particular device' do
+ # Register other user with a different WebAuthn device
+ other_device = add_webauthn_device(app_id, other_user)
+
+ # Try authenticating user with the old WebAuthn device
+ gitlab_sign_in(user)
+ other_device.respond_to_webauthn_authentication
+ expect(page).to have_content('Authentication via WebAuthn device failed')
+ end
+ end
+
+ describe "and also the current user" do
+ # TODO Uncomment once WebAuthn::FakeClient supports passing credential options
+ # (especially allow_credentials, as this is needed to specify which credential the
+ # fake client should use. Currently, the first credential is always used).
+ # There is an issue open for this: https://github.com/cedarcode/webauthn-ruby/issues/259
+ it "allows logging in with that particular device" do
+ pending("support for passing credential options in FakeClient")
+ # Register current user with the same WebAuthn device
+ current_user = gitlab_sign_in(:user)
+ visit profile_account_path
+ manage_two_factor_authentication
+ register_webauthn_device(webauthn_device)
+ gitlab_sign_out
+
+ # Try authenticating user with the same WebAuthn device
+ gitlab_sign_in(current_user)
+ webauthn_device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+ end
+ end
+ end
+
+ describe 'when a given WebAuthn device has not been registered' do
+ it 'does not allow logging in with that particular device' do
+ unregistered_device = FakeWebauthnDevice.new(page, 'My device')
+ gitlab_sign_in(user)
+ unregistered_device.respond_to_webauthn_authentication
+
+ expect(page).to have_content('Authentication via WebAuthn device failed')
+ end
+ end
+
+ describe 'when more than one device has been registered by the same user' do
+ it 'allows logging in with either device' do
+ first_device = add_webauthn_device(app_id, user)
+ second_device = add_webauthn_device(app_id, user)
+
+ # Authenticate as both devices
+ [first_device, second_device].each do |device|
+ gitlab_sign_in(user)
+ # register_webauthn_device(device)
+ device.respond_to_webauthn_authentication
+
+ expect(page).to have_css('.sign-out-link', visible: false)
+
+ gitlab_sign_out
+ end
+ end
+ end
+ end
+ end
+end