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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-21 10:08:36 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-21 10:08:36 +0300
commit48aff82709769b098321c738f3444b9bdaa694c6 (patch)
treee00c7c43e2d9b603a5a6af576b1685e400410dee /spec/features
parent879f5329ee916a948223f8f43d77fba4da6cd028 (diff)
Add latest changes from gitlab-org/gitlab@13-5-stable-eev13.5.0-rc42
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/login_spec.rb10
-rw-r--r--spec/features/admin/admin_settings_spec.rb37
-rw-r--r--spec/features/admin/admin_users_spec.rb97
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/admin/clusters/eks_spec.rb2
-rw-r--r--spec/features/admin/dashboard_spec.rb6
-rw-r--r--spec/features/alert_management/alert_details_spec.rb86
-rw-r--r--spec/features/alert_management/alert_management_list_spec.rb58
-rw-r--r--spec/features/alert_management/user_filters_alerts_by_status_spec.rb56
-rw-r--r--spec/features/alert_management/user_searches_alerts_spec.rb31
-rw-r--r--spec/features/alert_management/user_updates_alert_status_spec.rb36
-rw-r--r--spec/features/alert_management_spec.rb62
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb12
-rw-r--r--spec/features/boards/boards_spec.rb40
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/calendar_spec.rb2
-rw-r--r--spec/features/clusters/cluster_detail_page_spec.rb12
-rw-r--r--spec/features/commits_spec.rb1
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb4
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb6
-rw-r--r--spec/features/dashboard/todos/todos_filtering_spec.rb8
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb19
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb1
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb1
-rw-r--r--spec/features/file_uploads/maven_package_spec.rb26
-rw-r--r--spec/features/groups/clusters/eks_spec.rb2
-rw-r--r--spec/features/groups/clusters/user_spec.rb8
-rw-r--r--spec/features/groups/members/leave_group_spec.rb2
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb113
-rw-r--r--spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb75
-rw-r--r--spec/features/groups/navbar_spec.rb8
-rw-r--r--spec/features/groups/packages_spec.rb2
-rw-r--r--spec/features/groups/show_spec.rb13
-rw-r--r--spec/features/incidents/incident_details_spec.rb52
-rw-r--r--spec/features/incidents/incidents_list_spec.rb38
-rw-r--r--spec/features/incidents/user_creates_new_incident_spec.rb55
-rw-r--r--spec/features/incidents/user_filters_incidents_by_status_spec.rb59
-rw-r--r--spec/features/incidents/user_searches_incidents_spec.rb30
-rw-r--r--spec/features/invites_spec.rb48
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb12
-rw-r--r--spec/features/issuables/issuable_list_spec.rb8
-rw-r--r--spec/features/issuables/markdown_references/internal_references_spec.rb8
-rw-r--r--spec/features/issuables/merge_request_discussion_lock_spec.rb79
-rw-r--r--spec/features/issues/csv_spec.rb4
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb24
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb136
-rw-r--r--spec/features/issues/todo_spec.rb4
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb436
-rw-r--r--spec/features/issues/user_sees_live_update_spec.rb2
-rw-r--r--spec/features/issues/user_views_issue_spec.rb4
-rw-r--r--spec/features/labels_hierarchy_spec.rb1
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb15
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb7
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb9
-rw-r--r--spec/features/merge_request/user_edits_assignees_sidebar_spec.rb47
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb18
-rw-r--r--spec/features/merge_request/user_expands_diff_spec.rb1
-rw-r--r--spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb29
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_reopens_merge_request_spec.rb6
-rw-r--r--spec/features/merge_request/user_resolves_wip_mr_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb3
-rw-r--r--spec/features/merge_request/user_sees_page_metadata_spec.rb17
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_suggest_pipeline_spec.rb35
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb7
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb19
-rw-r--r--spec/features/merge_requests/user_filters_by_approvals_spec.rb82
-rw-r--r--spec/features/merge_requests/user_filters_by_deployments_spec.rb93
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb37
-rw-r--r--spec/features/milestone_spec.rb4
-rw-r--r--spec/features/milestones/user_views_milestone_spec.rb76
-rw-r--r--spec/features/milestones/user_views_milestones_spec.rb3
-rw-r--r--spec/features/operations_sidebar_link_spec.rb81
-rw-r--r--spec/features/profiles/keys_spec.rb36
-rw-r--r--spec/features/projects/activity/user_sees_design_comment_spec.rb2
-rw-r--r--spec/features/projects/badges/list_spec.rb8
-rw-r--r--spec/features/projects/blobs/edit_spec.rb16
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb17
-rw-r--r--spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb2
-rw-r--r--spec/features/projects/branches/user_deletes_branch_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb8
-rw-r--r--spec/features/projects/ci/lint_spec.rb137
-rw-r--r--spec/features/projects/clusters/eks_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb12
-rw-r--r--spec/features/projects/clusters/user_spec.rb8
-rw-r--r--spec/features/projects/clusters_spec.rb16
-rw-r--r--spec/features/projects/commit/builds_spec.rb2
-rw-r--r--spec/features/projects/commit/user_comments_on_commit_spec.rb48
-rw-r--r--spec/features/projects/compare_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_spec.rb2
-rw-r--r--spec/features/projects/environments/environments_spec.rb4
-rw-r--r--spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb63
-rw-r--r--spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb21
-rw-r--r--spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb21
-rw-r--r--spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb200
-rw-r--r--spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb31
-rw-r--r--spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb147
-rw-r--r--spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb195
-rw-r--r--spec/features/projects/features_visibility_spec.rb2
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb41
-rw-r--r--spec/features/projects/files/user_browses_lfs_files_spec.rb24
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb6
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb173
-rw-r--r--spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb29
-rw-r--r--spec/features/projects/issues/viewing_relocated_issues_spec.rb40
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb4
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb43
-rw-r--r--spec/features/projects/members/list_spec.rb2
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb75
-rw-r--r--spec/features/projects/navbar_spec.rb10
-rw-r--r--spec/features/projects/pages_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb17
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb4
-rw-r--r--spec/features/projects/releases/user_creates_release_spec.rb31
-rw-r--r--spec/features/projects/releases/user_views_edit_release_spec.rb49
-rw-r--r--spec/features/projects/releases/user_views_release_spec.rb55
-rw-r--r--spec/features/projects/releases/user_views_releases_spec.rb68
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb70
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb8
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb22
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb134
-rw-r--r--spec/features/projects/snippets/show_spec.rb10
-rw-r--r--spec/features/projects/snippets/user_comments_on_snippet_spec.rb1
-rw-r--r--spec/features/projects/snippets/user_deletes_snippet_spec.rb11
-rw-r--r--spec/features/projects/snippets/user_updates_snippet_spec.rb70
-rw-r--r--spec/features/projects/tracings_spec.rb63
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb2
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb61
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb168
-rw-r--r--spec/features/projects/wiki/shortcuts_spec.rb20
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb360
-rw-r--r--spec/features/projects/wiki/user_deletes_wiki_page_spec.rb22
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb263
-rw-r--r--spec/features/projects/wiki/user_views_wiki_empty_spec.rb138
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb276
-rw-r--r--spec/features/projects/wiki/user_views_wiki_pages_spec.rb91
-rw-r--r--spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb79
-rw-r--r--spec/features/projects/wikis_spec.rb20
-rw-r--r--spec/features/projects_spec.rb37
-rw-r--r--spec/features/protected_branches_spec.rb16
-rw-r--r--spec/features/reportable_note/snippets_spec.rb1
-rw-r--r--spec/features/runners_spec.rb4
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb1
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb26
-rw-r--r--spec/features/search/user_uses_search_filters_spec.rb38
-rw-r--r--spec/features/sentry_js_spec.rb2
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb11
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb1
-rw-r--r--spec/features/snippets/private_snippets_spec.rb7
-rw-r--r--spec/features/snippets/public_snippets_spec.rb19
-rw-r--r--spec/features/snippets/show_spec.rb12
-rw-r--r--spec/features/snippets/spam_snippets_spec.rb25
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb196
-rw-r--r--spec/features/snippets/user_deletes_snippet_spec.rb12
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb128
-rw-r--r--spec/features/snippets_spec.rb12
-rw-r--r--spec/features/static_site_editor_spec.rb67
-rw-r--r--spec/features/tags/developer_deletes_tag_spec.rb27
-rw-r--r--spec/features/tags/developer_views_tags_spec.rb13
-rw-r--r--spec/features/task_lists_spec.rb115
-rw-r--r--spec/features/triggers_spec.rb186
-rw-r--r--spec/features/users/overview_spec.rb70
-rw-r--r--spec/features/users/show_spec.rb52
-rw-r--r--spec/features/users/signup_spec.rb214
-rw-r--r--spec/features/users/terms_spec.rb15
168 files changed, 4206 insertions, 3112 deletions
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index f5c5a73c042..653a45a4bb8 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -194,7 +194,7 @@ RSpec.describe 'Admin Groups' do
expect(page).to have_content('Developer')
end
- accept_confirm { find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click }
+ accept_confirm { find(:css, 'li', text: current_user.name).find(:css, 'a.btn-danger').click }
visit group_group_members_path(group)
diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb
index 12046518aac..7cbba9ec674 100644
--- a/spec/features/admin/admin_mode/login_spec.rb
+++ b/spec/features/admin/admin_mode/login_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_moc
it 'allows login with valid code' do
# Cannot reuse the TOTP
- Timecop.travel(30.seconds.from_now) do
+ travel_to(30.seconds.from_now) do
enter_code(user.current_otp)
expect(current_path).to eq admin_root_path
@@ -58,7 +58,7 @@ RSpec.describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_moc
it 'blocks login with invalid code' do
# Cannot reuse the TOTP
- Timecop.travel(30.seconds.from_now) do
+ travel_to(30.seconds.from_now) do
enter_code('foo')
expect(page).to have_content('Invalid two-factor code')
@@ -67,7 +67,7 @@ RSpec.describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_moc
it 'allows login with invalid code, then valid code' do
# Cannot reuse the TOTP
- Timecop.travel(30.seconds.from_now) do
+ travel_to(30.seconds.from_now) do
enter_code('foo')
expect(page).to have_content('Invalid two-factor code')
@@ -163,7 +163,7 @@ RSpec.describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_moc
expect(page).to have_content('Two-Factor Authentication')
# Cannot reuse the TOTP
- Timecop.travel(30.seconds.from_now) do
+ travel_to(30.seconds.from_now) do
enter_code(user.current_otp)
expect(current_path).to eq admin_root_path
@@ -215,7 +215,7 @@ RSpec.describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_moc
expect(page).to have_content('Two-Factor Authentication')
# Cannot reuse the TOTP
- Timecop.travel(30.seconds.from_now) do
+ travel_to(30.seconds.from_now) do
enter_code(user.current_otp)
expect(current_path).to eq admin_root_path
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 38f0b813183..528dfad606e 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -130,6 +130,38 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(user_internal_regex['placeholder']).to eq 'Regex pattern'
end
+ context 'Change Sign-up restrictions' do
+ context 'Require Admin approval for new signup setting' do
+ context 'when feature is enabled' do
+ before do
+ stub_feature_flags(admin_approval_for_new_user_signups: true)
+ end
+
+ it 'changes the setting' do
+ page.within('.as-signup') do
+ check 'Require admin approval for new sign-ups'
+ click_button 'Save changes'
+ end
+
+ expect(current_settings.require_admin_approval_after_user_signup).to be_truthy
+ expect(page).to have_content "Application settings saved successfully"
+ end
+ end
+
+ context 'when feature is disabled' do
+ before do
+ stub_feature_flags(admin_approval_for_new_user_signups: false)
+ end
+
+ it 'does not show the the setting' do
+ page.within('.as-signup') do
+ expect(page).not_to have_selector('.application_setting_require_admin_approval_after_user_signup')
+ end
+ end
+ end
+ end
+ end
+
it 'Change Sign-in restrictions' do
page.within('.as-signin') do
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
@@ -497,18 +529,23 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
it 'Change Help page' do
+ stub_feature_flags(help_page_documentation_redirect: true)
+
new_support_url = 'http://example.com/help'
+ new_documentation_url = 'https://docs.gitlab.com'
page.within('.as-help-page') do
fill_in 'Help page text', with: 'Example text'
check 'Hide marketing-related entries from help'
fill_in 'Support page URL', with: new_support_url
+ fill_in 'Documentation pages URL', with: new_documentation_url
click_button 'Save changes'
end
expect(current_settings.help_page_text).to eq "Example text"
expect(current_settings.help_page_hide_commercial_content).to be_truthy
expect(current_settings.help_page_support_url).to eq new_support_url
+ expect(current_settings.help_page_documentation_base_url).to eq new_documentation_url
expect(page).to have_content "Application settings saved successfully"
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index a37210d2acc..e06e2d14f3c 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe "Admin::Users" do
expect(page).to have_content(current_user.last_activity_on.strftime("%e %b, %Y"))
expect(page).to have_content(user.email)
expect(page).to have_content(user.name)
+ expect(page).to have_content('Projects')
expect(page).to have_button('Block')
expect(page).to have_button('Deactivate')
expect(page).to have_button('Delete user')
@@ -48,6 +49,56 @@ RSpec.describe "Admin::Users" do
end
end
+ context 'user project count' do
+ before do
+ project = create(:project)
+ project.add_maintainer(current_user)
+ end
+
+ it 'displays count of users projects' do
+ visit admin_users_path
+
+ expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
+ end
+ end
+
+ describe 'tabs' do
+ it 'has multiple tabs to filter users' do
+ expect(page).to have_link('Active', href: admin_users_path)
+ expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
+ expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
+ expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
+ expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
+ expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
+ expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
+ expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
+ end
+
+ context '`Pending approval` tab' do
+ context 'feature is enabled' do
+ before do
+ stub_feature_flags(admin_approval_for_new_user_signups: true)
+ visit admin_users_path
+ end
+
+ it 'shows the `Pending approval` tab' do
+ expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ end
+ end
+
+ context 'feature is disabled' do
+ before do
+ stub_feature_flags(admin_approval_for_new_user_signups: false)
+ visit admin_users_path
+ end
+
+ it 'does not show the `Pending approval` tab' do
+ expect(page).not_to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ end
+ end
+ end
+ end
+
describe 'search and sort' do
before do
create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
@@ -146,6 +197,27 @@ RSpec.describe "Admin::Users" do
expect(page).to have_content(user.email)
end
end
+
+ describe 'Pending approval filter' do
+ it 'counts users who are pending approval' do
+ create_list(:user, 2, :blocked_pending_approval)
+
+ visit admin_users_path
+
+ page.within('.filter-blocked-pending-approval small') do
+ expect(page).to have_content('2')
+ end
+ end
+
+ it 'filters by users who are pending approval' do
+ user = create(:user, :blocked_pending_approval)
+
+ visit admin_users_path
+ click_link 'Pending approval'
+
+ expect(page).to have_content(user.email)
+ end
+ end
end
describe "GET /admin/users/new" do
@@ -287,6 +359,23 @@ RSpec.describe "Admin::Users" do
expect(page).to have_button('Delete user and contributions')
end
+ context 'user pending approval' do
+ it 'shows user info' do
+ user = create(:user, :blocked_pending_approval)
+
+ visit admin_users_path
+ click_link 'Pending approval'
+ click_link user.name
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Pending approval')
+ expect(page).to have_link('Approve user')
+ expect(page).to have_button('Block user')
+ expect(page).to have_button('Delete user')
+ expect(page).to have_button('Delete user and contributions')
+ end
+ end
+
describe 'Impersonation' do
let(:another_user) { create(:user) }
@@ -606,7 +695,7 @@ RSpec.describe "Admin::Users" do
end
end
- describe 'show user keys' do
+ describe 'show user keys', :js do
let!(:key1) do
create(:key, user: user, title: "ssh-rsa Key1", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1")
end
@@ -629,7 +718,11 @@ RSpec.describe "Admin::Users" do
expect(page).to have_content(key2.title)
expect(page).to have_content(key2.key)
- click_link 'Remove'
+ click_button 'Delete'
+
+ page.within('.modal') do
+ page.click_button('Delete')
+ end
expect(page).not_to have_content(key2.title)
end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index b8851c28531..44642983a36 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe 'Admin uses repository checks', :request_store, :clean_gitlab_red
)
visit_admin_project_page(project)
- page.within('.alert') do
+ page.within('.gl-alert') do
expect(page.text).to match(/Last repository check \(just now\) failed/)
end
end
diff --git a/spec/features/admin/clusters/eks_spec.rb b/spec/features/admin/clusters/eks_spec.rb
index ef49aebc7c5..ad7122bf182 100644
--- a/spec/features/admin/clusters/eks_spec.rb
+++ b/spec/features/admin/clusters/eks_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do
before do
visit admin_clusters_path
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
end
context 'when user creates a cluster on AWS EKS' do
diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb
index 4ffa5e3be0b..acb8fb54e11 100644
--- a/spec/features/admin/dashboard_spec.rb
+++ b/spec/features/admin/dashboard_spec.rb
@@ -28,11 +28,9 @@ RSpec.describe 'admin visits dashboard' do
describe 'Users statistic' do
let_it_be(:users_statistics) { create(:users_statistics) }
+ let_it_be(:users_count_label) { Gitlab.ee? ? 'Billable users 71' : 'Active users 71' }
it 'shows correct amounts of users', :aggregate_failures do
- expected_active_users_text = Gitlab.ee? ? 'Active users (Billable users) 71' : 'Active users 71'
-
- sign_in(create(:admin))
visit admin_dashboard_stats_path
expect(page).to have_content('Users without a Group and Project 23')
@@ -42,9 +40,9 @@ RSpec.describe 'admin visits dashboard' do
expect(page).to have_content('Users with highest role Maintainer 6')
expect(page).to have_content('Users with highest role Owner 5')
expect(page).to have_content('Bots 2')
- expect(page).to have_content(expected_active_users_text)
expect(page).to have_content('Blocked users 7')
expect(page).to have_content('Total users 78')
+ expect(page).to have_content(users_count_label)
end
end
end
diff --git a/spec/features/alert_management/alert_details_spec.rb b/spec/features/alert_management/alert_details_spec.rb
new file mode 100644
index 00000000000..d190e4b6939
--- /dev/null
+++ b/spec/features/alert_management/alert_details_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Alert details', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered', title: 'Alert') }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit details_project_alert_management_path(project, alert)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the alert' do
+ it 'shows the alert' do
+ page.within('.alert-management-details') do
+ expect(find('h2')).to have_content(alert.title)
+ end
+ end
+
+ it 'shows the alert tabs' do
+ page.within('.alert-management-details') do
+ alert_tabs = find('[data-testid="alertDetailsTabs"]')
+
+ expect(alert_tabs).to have_content('Alert details')
+ end
+ end
+
+ it 'shows the right sidebar mounted with correct widgets' do
+ page.within('.layout-page') do
+ sidebar = find('.right-sidebar')
+
+ expect(sidebar).to have_selector('.alert-status')
+ expect(sidebar).to have_selector('.alert-assignees')
+ expect(sidebar).to have_content('Triggered')
+ end
+ end
+
+ it 'updates the alert todo button from the right sidebar' do
+ expect(page).to have_selector('[data-testid="alert-todo-button"]')
+ todo_button = find('[data-testid="alert-todo-button"]')
+
+ expect(todo_button).to have_content('Add a To-Do')
+ find('[data-testid="alert-todo-button"]').click
+ wait_for_requests
+
+ expect(todo_button).to have_content('Mark as done')
+ end
+
+ it 'updates the alert status from the right sidebar' do
+ page.within('.alert-status') do
+ alert_status = find('[data-testid="status"]')
+
+ expect(alert_status).to have_content('Triggered')
+
+ find('.btn-link').click
+ find('.gl-new-dropdown-item', text: 'Acknowledged').click
+
+ wait_for_requests
+
+ expect(alert_status).to have_content('Acknowledged')
+ end
+ end
+
+ it 'updates the alert assignee from the right sidebar' do
+ page.within('.right-sidebar') do
+ alert_assignee = find('.alert-assignees')
+
+ expect(alert_assignee).to have_content('None - assign yourself')
+
+ find('[data-testid="unassigned-users"]').click
+
+ wait_for_requests
+
+ expect(alert_assignee).to have_content('Assignee Edit John Doe')
+ end
+ end
+ end
+end
diff --git a/spec/features/alert_management/alert_management_list_spec.rb b/spec/features/alert_management/alert_management_list_spec.rb
new file mode 100644
index 00000000000..c2514d80474
--- /dev/null
+++ b/spec/features/alert_management/alert_management_list_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Alert Management index', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_alert_management_index_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the alert list and the alert service is not enabled' do
+ it 'shows the alert page title' do
+ expect(page).to have_content('Alerts')
+ end
+
+ it 'shows the empty state by default' do
+ expect(page).to have_content('Surface alerts in GitLab')
+ end
+
+ it 'does not show the filtered search' do
+ page.within('.layout-page') do
+ expect(page).not_to have_css('[data-testid="search-icon"]')
+ end
+ end
+
+ it 'does not show the alert table' do
+ expect(page).not_to have_selector('.gl-table')
+ end
+ end
+
+ context 'when a developer displays the alert list and the alert service is enabled' do
+ let_it_be(:alerts_service) { create(:alerts_service, project: project) }
+
+ it 'shows the alert page title' do
+ expect(page).to have_content('Alerts')
+ end
+
+ it 'shows the filtered search' do
+ page.within('.layout-page') do
+ expect(page).to have_css('[data-testid="search-icon"]')
+ end
+ end
+
+ it 'shows the alert table' do
+ expect(page).to have_selector('.gl-table')
+ end
+ end
+end
diff --git a/spec/features/alert_management/user_filters_alerts_by_status_spec.rb b/spec/features/alert_management/user_filters_alerts_by_status_spec.rb
new file mode 100644
index 00000000000..ee516418cd6
--- /dev/null
+++ b/spec/features/alert_management/user_filters_alerts_by_status_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User filters Alert Management table by status', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:alerts_service) { create(:alerts_service, project: project) }
+ let_it_be(:alert1, reload: true) { create(:alert_management_alert, :triggered, project: project) }
+ let_it_be(:alert2, reload: true) { create(:alert_management_alert, :acknowledged, project: project) }
+ let_it_be(:alert3, reload: true) { create(:alert_management_alert, :acknowledged, project: project) }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_alert_management_index_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the alert list and the alert service is enabled they can filter the table by an alert status' do
+ it 'shows the alert table items with alert status of Open by default' do
+ expect(page).to have_selector('.gl-table')
+ expect(page).to have_content('Open 3')
+ end
+
+ it 'shows the alert table items with alert status of Acknowledged' do
+ find('.gl-tab-nav-item', text: 'Acknowledged').click
+
+ expect(page).to have_selector('.gl-tab-nav-item-active')
+ expect(find('.gl-tab-nav-item-active')).to have_content('Acknowledged 2')
+ expect(all('.dropdown-menu-selectable').count).to be(2)
+ end
+
+ it 'shows the alert table items with alert status of Triggered' do
+ find('.gl-tab-nav-item', text: 'Triggered').click
+ wait_for_requests
+
+ expect(page).to have_selector('.gl-tab-nav-item-active')
+ expect(find('.gl-tab-nav-item-active')).to have_content('Triggered 1')
+ expect(all('.dropdown-menu-selectable').count).to be(1)
+ end
+
+ it 'shows the an empty table for a status with no alerts' do
+ find('.gl-tab-nav-item', text: 'Resolved').click
+ wait_for_requests
+
+ expect(page).to have_selector('.gl-tab-nav-item-active')
+ expect(find('.gl-tab-nav-item-active')).to have_content('Resolved 0')
+ expect(page).to have_content('No alerts to display.')
+ end
+ end
+end
diff --git a/spec/features/alert_management/user_searches_alerts_spec.rb b/spec/features/alert_management/user_searches_alerts_spec.rb
new file mode 100644
index 00000000000..568321de025
--- /dev/null
+++ b/spec/features/alert_management/user_searches_alerts_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User searches Alert Management alerts', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:alerts_service) { create(:alerts_service, project: project) }
+ let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_alert_management_index_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the alert list and the alert service is enabled they can search an alert' do
+ it 'shows the incident table with an incident for a valid search filter bar' do
+ expect(page).to have_selector('.filtered-search-wrapper')
+ expect(page).to have_selector('.gl-table')
+ expect(page).to have_css('[data-testid="severityField"]')
+ expect(all('tbody tr').count).to be(1)
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+end
diff --git a/spec/features/alert_management/user_updates_alert_status_spec.rb b/spec/features/alert_management/user_updates_alert_status_spec.rb
new file mode 100644
index 00000000000..8974796662c
--- /dev/null
+++ b/spec/features/alert_management/user_updates_alert_status_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User updates Alert Management status', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:alerts_service) { create(:alerts_service, project: project) }
+ let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_alert_management_index_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer+ displays the alerts list and the alert service is enabled they can update an alert status' do
+ it 'shows the alert table with an alert status dropdown' do
+ expect(page).to have_selector('.gl-table')
+ expect(find('.dropdown-menu-selectable')).to have_content('Triggered')
+ end
+
+ it 'updates the alert status' do
+ find('.dropdown-menu-selectable').click
+ find('.dropdown-item', text: 'Acknowledged').click
+ wait_for_requests
+
+ expect(find('.dropdown-menu-selectable')).to have_content('Acknowledged')
+ end
+ end
+end
diff --git a/spec/features/alert_management_spec.rb b/spec/features/alert_management_spec.rb
new file mode 100644
index 00000000000..2989f72e356
--- /dev/null
+++ b/spec/features/alert_management_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Alert management', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ context 'when visiting the alert details page' do
+ let!(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, title: 'dos-test', project: project, **options) }
+ let(:options) { {} }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when actor has permission to see the alert' do
+ let(:user) { developer }
+
+ it 'shows the alert details' do
+ visit(details_project_alert_management_path(project, alert))
+
+ within('.alert-management-details-table') do
+ expect(page).to have_content(alert.title)
+ end
+ end
+
+ context 'when alert belongs to an environment' do
+ let(:options) { { environment: environment } }
+ let!(:environment) { create(:environment, name: 'production', project: project) }
+
+ it 'shows the environment name' do
+ visit(details_project_alert_management_path(project, alert))
+
+ expect(page).to have_link(environment.name, href: project_environment_path(project, environment))
+ within('.alert-management-details-table') do
+ expect(page).to have_content(environment.name)
+ end
+ end
+
+ context 'when expose_environment_path_in_alert_details feature flag is disabled' do
+ before do
+ stub_feature_flags(expose_environment_path_in_alert_details: false)
+ end
+
+ it 'does not show the environment name' do
+ visit(details_project_alert_management_path(project, alert))
+
+ within('.alert-management-details-table') do
+ expect(page).to have_content(alert.title)
+ expect(page).not_to have_content(environment.name)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index d432825e113..00efca5d3a8 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -79,7 +79,7 @@ RSpec.describe 'Issue Boards add issue modal', :js do
it 'loads issues' do
page.within('.add-issues-modal') do
- page.within('.nav-links') do
+ page.within('.gl-tabs') do
expect(page).to have_content('2')
end
@@ -103,7 +103,13 @@ RSpec.describe 'Issue Boards add issue modal', :js do
click_button 'Cancel'
end
- accept_confirm { first('.board-delete').click }
+ page.within(find('.board:nth-child(2)')) do
+ find('button[title="List settings"]').click
+ end
+
+ page.within(find('.js-board-settings-sidebar')) do
+ accept_confirm { find('[data-testid="remove-list"]').click }
+ end
click_button('Add issues')
@@ -146,7 +152,7 @@ RSpec.describe 'Issue Boards add issue modal', :js do
page.within('.add-issues-modal') do
first('.board-card .board-card-number').click
- page.within('.nav-links') do
+ page.within('.gl-tabs') do
expect(page).to have_content('Selected issues 1')
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index e36378bd34e..06ec4e05828 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -24,33 +24,11 @@ RSpec.describe 'Issue Boards', :js do
context 'no lists' do
before do
visit project_board_path(project, board)
- wait_for_requests
- expect(page).to have_selector('.board', count: 3)
- end
-
- it 'shows blank state' do
- expect(page).to have_content('Welcome to your Issue Board!')
- end
-
- it 'shows tooltip on add issues button' do
- button = page.find('.filter-dropdown-container button', text: 'Add issues')
-
- expect(button[:"data-original-title"]).to eq("Please add a list to your board first")
- end
-
- it 'hides the blank state when clicking nevermind button' do
- page.within(find('.board-blank-state')) do
- click_button("Nevermind, I'll use my own")
- end
- expect(page).to have_selector('.board', count: 2)
end
it 'creates default lists' do
lists = ['Open', 'To Do', 'Doing', 'Closed']
- page.within(find('.board-blank-state')) do
- click_button('Add default lists')
- end
wait_for_requests
expect(page).to have_selector('.board', count: 4)
@@ -181,9 +159,7 @@ RSpec.describe 'Issue Boards', :js do
end
it 'allows user to delete board' do
- page.within(find('.board:nth-child(2)')) do
- accept_confirm { find('.board-delete').click }
- end
+ remove_list
wait_for_requests
@@ -196,9 +172,7 @@ RSpec.describe 'Issue Boards', :js do
find('.js-new-board-list').click
- page.within(find('.board:nth-child(2)')) do
- accept_confirm { find('.board-delete').click }
- end
+ remove_list
wait_for_requests
@@ -692,4 +666,14 @@ RSpec.describe 'Issue Boards', :js do
click_button(link_text)
end
end
+
+ def remove_list
+ page.within(find('.board:nth-child(2)')) do
+ find('button[title="List settings"]').click
+ end
+
+ page.within(find('.js-board-settings-sidebar')) do
+ accept_confirm { find('[data-testid="remove-list"]').click }
+ end
+ end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 4b4cb444903..332c90df6d7 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -229,7 +229,7 @@ RSpec.describe 'Issue Boards', :js do
end
context 'time tracking' do
- let(:compare_meter_tooltip) { find('.time-tracking .time-tracking-content .compare-meter')['data-original-title'] }
+ let(:compare_meter_tooltip) { find('.time-tracking .time-tracking-content .compare-meter')['title'] }
before do
issue2.timelogs.create(time_spent: 14400, user: user)
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 346f305f0d0..5f58fa420fb 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -180,7 +180,7 @@ RSpec.describe 'Contributions Calendar', :js do
before do
push_code_contribution
- Timecop.freeze(Date.yesterday) do
+ travel_to(Date.yesterday) do
Issues::CreateService.new(contributed_project, user, issue_params).execute
end
end
diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb
index 4f7f62d00a5..31d6bcda9e8 100644
--- a/spec/features/clusters/cluster_detail_page_spec.rb
+++ b/spec/features/clusters/cluster_detail_page_spec.rb
@@ -87,6 +87,7 @@ RSpec.describe 'Clusterable > Show page' do
within('#advanced-settings-section') do
expect(page).to have_content('Google Kubernetes Engine')
expect(page).to have_content('Manage your Kubernetes cluster by visiting')
+ expect_common_advanced_options
end
end
end
@@ -117,6 +118,7 @@ RSpec.describe 'Clusterable > Show page' do
within('#advanced-settings-section') do
expect(page).not_to have_content('Google Kubernetes Engine')
expect(page).not_to have_content('Manage your Kubernetes cluster by visiting')
+ expect_common_advanced_options
end
end
end
@@ -176,4 +178,14 @@ RSpec.describe 'Clusterable > Show page' do
let(:cluster) { create(:cluster, :provided_by_user, :instance) }
end
end
+
+ private
+
+ def expect_common_advanced_options
+ aggregate_failures do
+ expect(page).to have_content('Cluster management project')
+ expect(page).to have_content('Clear cluster cache')
+ expect(page).to have_content('Remove Kubernetes cluster integration')
+ end
+ end
end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index e66a40720da..97ee891dbb8 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -140,6 +140,7 @@ RSpec.describe 'Commits' do
context 'when accessing internal project with disallowed access', :js do
before do
+ stub_feature_flags(graphql_pipeline_header: false)
project.update(
visibility_level: Gitlab::VisibilityLevel::INTERNAL,
public_builds: false)
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index 7526a55a3c1..3cb7140d253 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
user.invalidate_cache_counts
- Timecop.travel(3.minutes.from_now) do
+ travel_to(3.minutes.from_now) do
visit issues_path
expect_counters('issues', '0')
@@ -39,7 +39,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
user.invalidate_cache_counts
- Timecop.travel(3.minutes.from_now) do
+ travel_to(3.minutes.from_now) do
visit merge_requests_path
expect_counters('merge_requests', '0')
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 5331b5559d8..952a78ec79a 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -19,6 +19,12 @@ RSpec.describe 'Dashboard Merge Requests' do
sign_in(current_user)
end
+ it 'disables target branch filter' do
+ visit merge_requests_dashboard_path
+
+ expect(page).not_to have_selector('#js-dropdown-target-branch', visible: false)
+ end
+
context 'new merge request dropdown' do
let(:project_with_disabled_merge_requests) { create(:project, :merge_requests_disabled) }
diff --git a/spec/features/dashboard/todos/todos_filtering_spec.rb b/spec/features/dashboard/todos/todos_filtering_spec.rb
index f60b07c976e..b1464af4194 100644
--- a/spec/features/dashboard/todos/todos_filtering_spec.rb
+++ b/spec/features/dashboard/todos/todos_filtering_spec.rb
@@ -130,6 +130,7 @@ RSpec.describe 'Dashboard > User filters todos', :js do
before do
create(:todo, :build_failed, user: user_1, author: user_2, project: project_1)
create(:todo, :marked, user: user_1, author: user_2, project: project_1, target: issue1)
+ create(:todo, :review_requested, user: user_1, author: user_2, project: project_1, target: issue1)
end
it 'filters by Assigned' do
@@ -138,6 +139,12 @@ RSpec.describe 'Dashboard > User filters todos', :js do
expect_to_see_action(:assigned)
end
+ it 'filters by Review Requested' do
+ filter_action('Review requested')
+
+ expect_to_see_action(:review_requested)
+ end
+
it 'filters by Mentioned' do
filter_action('Mentioned')
@@ -168,6 +175,7 @@ RSpec.describe 'Dashboard > User filters todos', :js do
def expect_to_see_action(action_name)
action_names = {
assigned: ' assigned you ',
+ review_requested: ' requested a review of ',
mentioned: ' mentioned ',
marked: ' added a todo for ',
build_failed: ' build failed for '
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index cf773d2caed..0b4fed55f11 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -197,6 +197,21 @@ RSpec.describe 'Dashboard Todos' do
end
end
end
+
+ context 'review request todo' do
+ let(:merge_request) { create(:merge_request, title: "Fixes issue") }
+
+ before do
+ create(:todo, :review_requested, user: user, project: project, target: merge_request, author: user)
+ visit dashboard_todos_path
+ end
+
+ it 'shows you set yourself as an reviewer message' do
+ page.within('.js-todos-all') do
+ expect(page).to have_content("You requested a review of merge request #{merge_request.to_reference} \"Fixes issue\" at #{project.namespace.owner_name} / #{project.name} from yourself")
+ end
+ end
+ end
end
context 'User has done todos', :js do
@@ -213,7 +228,7 @@ RSpec.describe 'Dashboard Todos' do
describe 'restoring the todo' do
before do
within first('.todo') do
- click_link 'Add a To Do'
+ click_link 'Add a to do'
end
end
@@ -228,7 +243,7 @@ RSpec.describe 'Dashboard Todos' do
end
end
- context 'User has Todos with labels spanning multiple projects' do
+ context 'User has to dos with labels spanning multiple projects' do
before do
label1 = create(:label, project: project)
note1 = create(:note_on_issue, note: "Hello #{label1.to_reference(format: :name)}", noteable_id: issue.id, noteable_type: 'Issue', project: issue.project)
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index 50201bbdb21..b2d3fbf4b5d 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe 'Thread Comments Snippet', :js do
let_it_be(:snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
before do
- stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index e705f2916da..49343cc7a57 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Expand and collapse diffs', :js do
let(:project) { create(:project, :repository) }
before do
+ stub_feature_flags(increased_diff_limits: false)
sign_in(create(:admin))
# Ensure that undiffable.md is in .gitattributes
diff --git a/spec/features/file_uploads/maven_package_spec.rb b/spec/features/file_uploads/maven_package_spec.rb
index c873a0e9a36..e87eec58618 100644
--- a/spec/features/file_uploads/maven_package_spec.rb
+++ b/spec/features/file_uploads/maven_package_spec.rb
@@ -25,5 +25,31 @@ RSpec.describe 'Upload a maven package', :api, :js do
it { expect(subject.code).to eq(200) }
end
+ RSpec.shared_examples 'for a maven sha1' do
+ let(:dummy_package) { double(Packages::Package) }
+ let(:api_path) { "/projects/#{project.id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar.sha1" }
+
+ before do
+ # The sha verification done by the maven api is between:
+ # - the sha256 set by workhorse
+ # - the sha256 of the sha1 of the uploaded package file
+ # We're going to send `file` for the sha1 and stub the sha1 of the package file so that
+ # both sha256 being the same
+ expect(::Packages::PackageFileFinder).to receive(:new).and_return(double(execute!: dummy_package))
+ expect(dummy_package).to receive(:file_sha1).and_return(File.read(file.path))
+ end
+
+ it { expect(subject.code).to eq(204) }
+ end
+
+ RSpec.shared_examples 'for a maven md5' do
+ let(:api_path) { "/projects/#{project.id}/packages/maven/com/example/my-app/1.0/my-app-1.0-20180724.124855-1.jar.md5" }
+ let(:file) { StringIO.new('dummy_package') }
+
+ it { expect(subject.code).to eq(200) }
+ end
+
it_behaves_like 'handling file uploads', 'for a maven package'
+ it_behaves_like 'handling file uploads', 'for a maven sha1'
+ it_behaves_like 'handling file uploads', 'for a maven md5'
end
diff --git a/spec/features/groups/clusters/eks_spec.rb b/spec/features/groups/clusters/eks_spec.rb
index 5a62741250a..c361c502cbb 100644
--- a/spec/features/groups/clusters/eks_spec.rb
+++ b/spec/features/groups/clusters/eks_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'Group AWS EKS Cluster', :js do
before do
visit group_clusters_path(group)
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
end
context 'when user creates a cluster on AWS EKS' do
diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb
index 90253451d6b..97f8864aab2 100644
--- a/spec/features/groups/clusters/user_spec.rb
+++ b/spec/features/groups/clusters/user_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe 'User Cluster', :js do
before do
visit group_clusters_path(group)
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
click_link 'Connect existing cluster'
end
@@ -66,6 +66,10 @@ RSpec.describe 'User Cluster', :js do
expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked
end
end
+
+ it 'user sees namespace per environment is enabled by default' do
+ expect(page).to have_checked_field('Namespace per environment')
+ end
end
context 'when user filled form with invalid parameters' do
@@ -125,7 +129,7 @@ RSpec.describe 'User Cluster', :js do
it 'user sees creation form with the successful message' do
expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
- expect(page).to have_link('Add Kubernetes cluster')
+ expect(page).to have_link('Integrate with a cluster certificate')
end
end
end
diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb
index 9eb5cc15c5e..32acf7edd2a 100644
--- a/spec/features/groups/members/leave_group_spec.rb
+++ b/spec/features/groups/members/leave_group_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe 'Groups > Members > Leave group' do
visit group_group_members_path(group)
- expect(find(:css, '.project-members-page li', text: user.name)).not_to have_selector(:css, 'a.btn-remove')
+ expect(find(:css, '.project-members-page li', text: user.name)).to have_no_selector(:css, 'a.btn-danger')
end
it 'owner can not leave the group by url param if they are the last owner', :js do
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index e3bbbd4d73b..33caa3af36d 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -6,62 +6,115 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
include Select2Helper
include Spec::Support::Helpers::Features::ListRowsHelpers
- let(:user) { create(:user) }
- let(:shared_with_group) { create(:group) }
- let(:shared_group) { create(:group) }
+ let_it_be(:user) { create(:user) }
before do
stub_feature_flags(vue_group_members_list: false)
- shared_group.add_owner(user)
sign_in(user)
end
- it 'add group to group' do
- visit group_group_members_path(shared_group)
+ context 'when group link does not exist' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_to_add) { create(:group) }
- add_group(shared_with_group.id, 'Reporter')
+ before do
+ group.add_owner(user)
+ visit group_group_members_path(group)
+ end
- click_groups_tab
+ it 'add group to group' do
+ add_group(group_to_add.id, 'Reporter')
- page.within(first_row) do
- expect(page).to have_content(shared_with_group.name)
- expect(page).to have_content('Reporter')
+ click_groups_tab
+
+ page.within(first_row) do
+ expect(page).to have_content(group_to_add.name)
+ expect(page).to have_content('Reporter')
+ end
end
end
- it 'remove group from group' do
- create(:group_group_link, shared_group: shared_group,
- shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER)
+ context 'when group link exists' do
+ let_it_be(:shared_with_group) { create(:group) }
+ let_it_be(:shared_group) { create(:group) }
- visit group_group_members_path(shared_group)
+ let(:additional_link_attrs) { {} }
- click_groups_tab
+ let_it_be(:group_link, refind: true) do
+ create(
+ :group_group_link,
+ shared_group: shared_group,
+ shared_with_group: shared_with_group,
+ group_access: ::Gitlab::Access::DEVELOPER
+ )
+ end
- expect(page).to have_content(shared_with_group.name)
+ before do
+ travel_to Time.now.utc.beginning_of_day
+ group_link.update!(additional_link_attrs)
- accept_confirm do
- find(:css, '#tab-groups li', text: shared_with_group.name).find(:css, 'a.btn-remove').click
+ shared_group.add_owner(user)
+ visit group_group_members_path(shared_group)
end
- expect(page).not_to have_content(shared_with_group.name)
- end
+ it 'remove group from group' do
+ click_groups_tab
+
+ expect(page).to have_content(shared_with_group.name)
+
+ accept_confirm do
+ find(:css, '#tab-groups li', text: shared_with_group.name).find(:css, 'a.btn-danger').click
+ end
+
+ expect(page).not_to have_content(shared_with_group.name)
+ end
- it 'update group to owner level' do
- create(:group_group_link, shared_group: shared_group,
- shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER)
+ it 'update group to owner level' do
+ click_groups_tab
- visit group_group_members_path(shared_group)
+ page.within(first_row) do
+ click_button('Developer')
+ click_link('Maintainer')
- click_groups_tab
+ wait_for_requests
- page.within(first_row) do
- click_button('Developer')
- click_link('Maintainer')
+ expect(page).to have_button('Maintainer')
+ end
+ end
+
+ it 'updates expiry date' do
+ click_groups_tab
+
+ expires_at_field = "member_expires_at_#{shared_with_group.id}"
+ fill_in "member_expires_at_#{shared_with_group.id}", with: 3.days.from_now.to_date
+ find_field(expires_at_field).native.send_keys :enter
wait_for_requests
- expect(page).to have_button('Maintainer')
+ page.within(find('li.group_member')) do
+ expect(page).to have_content('Expires in 3 days')
+ end
+ end
+
+ context 'when expiry date is set' do
+ let(:additional_link_attrs) { { expires_at: 3.days.from_now.to_date } }
+
+ it 'clears expiry date' do
+ click_groups_tab
+
+ page.within(find('li.group_member')) do
+ expect(page).to have_content('Expires in 3 days')
+
+ page.within(find('.js-edit-member-form')) do
+ find('.js-clear-input').click
+ end
+
+ wait_for_requests
+
+ expect(page).not_to have_content('Expires in')
+ end
+ end
end
end
diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
index d94cc85f411..dd708c243a8 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
@@ -6,65 +6,66 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
- let(:user1) { create(:user, name: 'John Doe') }
- let!(:new_member) { create(:user, name: 'Mary Jane') }
- let(:group) { create(:group) }
+ let_it_be(:user1) { create(:user, name: 'John Doe') }
+ let_it_be(:group) { create(:group) }
+ let(:new_member) { create(:user, name: 'Mary Jane') }
before do
stub_feature_flags(vue_group_members_list: false)
+ travel_to Time.now.utc.beginning_of_day
+
group.add_owner(user1)
sign_in(user1)
end
it 'expiration date is displayed in the members list' do
- travel_to Time.zone.parse('2016-08-06 08:00') do
- date = 4.days.from_now
- visit group_group_members_path(group)
-
- page.within '.invite-users-form' do
- select2(new_member.id, from: '#user_ids', multiple: true)
- fill_in 'expires_at', with: date.to_s(:medium) + "\n"
- click_on 'Invite'
- end
-
- page.within "#group_member_#{group_member_id(new_member)}" do
- expect(page).to have_content('Expires in 4 days')
- end
+ visit group_group_members_path(group)
+
+ page.within '.invite-users-form' do
+ select2(new_member.id, from: '#user_ids', multiple: true)
+
+ fill_in 'expires_at', with: 3.days.from_now.to_date
+ find_field('expires_at').native.send_keys :enter
+
+ click_on 'Invite'
+ end
+
+ page.within "#group_member_#{group_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
end
end
- it 'change expiration date' do
- travel_to Time.zone.parse('2016-08-06 08:00') do
- date = 3.days.from_now
- group.add_developer(new_member)
+ it 'changes expiration date' do
+ group.add_developer(new_member)
+ visit group_group_members_path(group)
+
+ page.within "#group_member_#{group_member_id}" do
+ fill_in 'Expiration date', with: 3.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
- visit group_group_members_path(group)
+ wait_for_requests
- page.within "#group_member_#{group_member_id(new_member)}" do
- find('.js-access-expiration-date').set date.to_s(:medium) + "\n"
- wait_for_requests
- expect(page).to have_content('Expires in 3 days')
- end
+ expect(page).to have_content('Expires in 3 days')
end
end
- it 'remove expiration date' do
- travel_to Time.zone.parse('2016-08-06 08:00') do
- date = 3.days.from_now
- group_member = create(:group_member, :developer, user: new_member, group: group, expires_at: date.to_s(:medium))
+ it 'clears expiration date' do
+ create(:group_member, :developer, user: new_member, group: group, expires_at: 3.days.from_now.to_date)
+ visit group_group_members_path(group)
+
+ page.within "#group_member_#{group_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
+
+ find('.js-clear-input').click
- visit group_group_members_path(group)
+ wait_for_requests
- page.within "#group_member_#{group_member.id}" do
- find('.js-clear-input').click
- wait_for_requests
- expect(page).not_to have_content('Expires in 3 days')
- end
+ expect(page).not_to have_content('Expires in')
end
end
- def group_member_id(user)
+ def group_member_id
group.members.find_by(user_id: new_member).id
end
end
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index 60f1c404e78..e81f2370d10 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -72,4 +72,12 @@ RSpec.describe 'Group navbar' do
it_behaves_like 'verified navigation bar'
end
+
+ context 'when invite team members is not available' do
+ it 'does not display the js-invite-members-trigger' do
+ visit group_path(group)
+
+ expect(page).not_to have_selector('.js-invite-members-trigger')
+ end
+ end
end
diff --git a/spec/features/groups/packages_spec.rb b/spec/features/groups/packages_spec.rb
index d81e4aa70cf..60e0c08b3d4 100644
--- a/spec/features/groups/packages_spec.rb
+++ b/spec/features/groups/packages_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe 'Group Packages' do
it 'allows you to navigate to the project page' do
page.within('[data-qa-selector="packages-table"]') do
- click_link project.name
+ find('[data-qa-selector="package-path"]', text: project.name).click
end
expect(page).to have_current_path(project_path(project))
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index ec30f34199d..304573ecd6e 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -184,4 +184,17 @@ RSpec.describe 'Group show page' do
expect(page).to have_selector('.notifications-btn.disabled', visible: true)
end
end
+
+ context 'page og:description' do
+ let(:group) { create(:group, description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') }
+ let(:maintainer) { create(:user) }
+
+ before do
+ group.add_maintainer(maintainer)
+ sign_in(maintainer)
+ visit path
+ end
+
+ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
+ end
end
diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb
new file mode 100644
index 00000000000..3ec7717b649
--- /dev/null
+++ b/spec/features/incidents/incident_details_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Incident details', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:incident) { create(:incident, project: project, author: developer, description: 'description') }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_issues_incident_path(project, incident)
+ wait_for_requests
+ end
+
+ context 'when a developer+ displays the incident' do
+ it 'shows the incident' do
+ page.within('.issuable-details') do
+ expect(find('h2')).to have_content(incident.title)
+ end
+ end
+
+ it 'does not show design management' do
+ expect(page).not_to have_selector('.js-design-management')
+ end
+
+ it 'shows the incident tabs' do
+ 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
+
+ it 'shows the right sidebar mounted with type issue' do
+ page.within('.layout-page') do
+ sidebar = find('.right-sidebar')
+
+ expect(page).to have_selector('.right-sidebar[data-issuable-type="issue"]')
+ expect(sidebar).to have_selector('.incident-severity')
+ expect(sidebar).not_to have_selector('.milestone')
+ end
+ end
+ end
+end
diff --git a/spec/features/incidents/incidents_list_spec.rb b/spec/features/incidents/incidents_list_spec.rb
new file mode 100644
index 00000000000..c65c83b2804
--- /dev/null
+++ b/spec/features/incidents/incidents_list_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Incident Management index', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:incident) { create(:incident, project: project) }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_incidents_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the incident list' do
+ it 'shows the status tabs' do
+ expect(page).to have_selector('.gl-tabs')
+ end
+
+ it 'shows the filtered search' do
+ expect(page).to have_selector('.filtered-search-wrapper')
+ end
+
+ it 'shows the alert table' do
+ expect(page).to have_selector('.gl-table')
+ end
+
+ it 'alert page title' do
+ expect(page).to have_content('Incidents')
+ end
+ end
+end
diff --git a/spec/features/incidents/user_creates_new_incident_spec.rb b/spec/features/incidents/user_creates_new_incident_spec.rb
new file mode 100644
index 00000000000..99a137b5852
--- /dev/null
+++ b/spec/features/incidents/user_creates_new_incident_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Incident Management index', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:incident) { create(:incident, project: project) }
+
+ before_all do
+ project.add_developer(developer)
+ project.add_guest(guest)
+ end
+
+ shared_examples 'create incident form' do
+ it 'shows the create new issue button' do
+ expect(page).to have_selector('.create-incident-button')
+ end
+
+ it 'when clicked shows the create issue page with the Incident type pre-selected' do
+ find('.create-incident-button').click
+ wait_for_all_requests
+
+ expect(page).to have_selector('.dropdown-menu-toggle')
+ expect(page).to have_selector('.js-issuable-type-filter-dropdown-wrap')
+
+ page.within('.js-issuable-type-filter-dropdown-wrap') do
+ expect(page).to have_content('Incident')
+ end
+ end
+ end
+
+ context 'when a developer displays the incident list' do
+ before do
+ sign_in(developer)
+
+ visit project_incidents_path(project)
+ wait_for_all_requests
+ end
+
+ it_behaves_like 'create incident form'
+ end
+
+ context 'when a guest displays the incident list' do
+ before do
+ sign_in(guest)
+
+ visit project_incidents_path(project)
+ wait_for_all_requests
+ end
+
+ it_behaves_like 'create incident form'
+ end
+end
diff --git a/spec/features/incidents/user_filters_incidents_by_status_spec.rb b/spec/features/incidents/user_filters_incidents_by_status_spec.rb
new file mode 100644
index 00000000000..661c737141b
--- /dev/null
+++ b/spec/features/incidents/user_filters_incidents_by_status_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User filters Incident Management table by status', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+
+ before_all do
+ project.add_developer(developer)
+
+ create_list(:incident, 2, project: project, state: 'opened')
+ create(:incident, project: project, state: 'closed')
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_incidents_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the incident list they can filter the table by an incident status' do
+ def the_page_shows_the_nav_text_with_correct_count
+ expect(page).to have_selector('.gl-table')
+ expect(page).to have_content('All 3')
+ expect(page).to have_content('Open 2')
+ expect(page).to have_content('Closed 1')
+ end
+
+ it 'shows the incident table items with incident status of Open by default' do
+ expect(find('.gl-tab-nav-item-active')).to have_content('Open 2')
+ expect(all('tbody tr').count).to be(2)
+
+ the_page_shows_the_nav_text_with_correct_count
+ end
+
+ it 'shows the incident table items with incident status of Closed' do
+ find('.gl-tab-nav-item', text: 'Closed').click
+ wait_for_requests
+
+ expect(find('.gl-tab-nav-item-active')).to have_content('Closed 1')
+ expect(all('tbody tr').count).to be(1)
+
+ the_page_shows_the_nav_text_with_correct_count
+ end
+
+ it 'shows the incident table items with all status' do
+ find('.gl-tab-nav-item', text: 'All').click
+ wait_for_requests
+
+ expect(find('.gl-tab-nav-item-active')).to have_content('All 3')
+ expect(all('[data-testid="incident-assignees"]').count).to be(3)
+ expect(all('tbody tr').count).to be(3)
+
+ the_page_shows_the_nav_text_with_correct_count
+ end
+ end
+end
diff --git a/spec/features/incidents/user_searches_incidents_spec.rb b/spec/features/incidents/user_searches_incidents_spec.rb
new file mode 100644
index 00000000000..b8e3ff534c3
--- /dev/null
+++ b/spec/features/incidents/user_searches_incidents_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User searches Incident Management incidents', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:incident) { create(:incident, project: project) }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(developer)
+
+ visit project_incidents_path(project)
+ wait_for_requests
+ end
+
+ context 'when a developer displays the incident list they can search for an incident' do
+ it 'shows the incident table with an incident for a valid search filter bar' do
+ expect(page).to have_selector('.filtered-search-wrapper')
+ expect(page).to have_selector('.gl-table')
+ expect(page).to have_selector('.incident-severity')
+ expect(all('tbody tr').count).to be(1)
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index 3954de56eea..8ccaf82536a 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -23,7 +23,8 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
end
def fill_in_sign_up_form(new_user)
- fill_in 'new_user_name', with: new_user.name
+ 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
@@ -81,10 +82,10 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
end
end
- context 'when inviting a user using their email address' do
+ context 'when inviting a user' do
let(:new_user) { build_stubbed(:user) }
let(:invite_email) { new_user.email }
- let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email) }
+ let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email, created_by: owner) }
let!(:project_invite) { create(:project_member, :invited, project: project, invite_email: invite_email) }
context 'when user has not signed in yet' do
@@ -210,30 +211,43 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
context 'when declining the invitation' do
let(:send_email_confirmation) { true }
- context 'when signed in' do
- before do
- sign_in(user)
- visit invite_path(group_invite.raw_invite_token)
+ context 'as an existing user' do
+ let(:group_invite) { create(:group_member, user: user, group: group, created_by: owner) }
+
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ visit decline_invite_path(group_invite.raw_invite_token)
+ end
+
+ it 'declines application and redirects to dashboard' do
+ expect(current_path).to eq(dashboard_projects_path)
+ expect(page).to have_content('You have declined the invitation to join group Owned.')
+ expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
end
- it 'declines application and redirects to dashboard' do
- page.click_link 'Decline'
+ context 'when signed out' do
+ before do
+ visit decline_invite_path(group_invite.raw_invite_token)
+ end
- expect(current_path).to eq(dashboard_projects_path)
- expect(page).to have_content('You have declined the invitation to join group Owned.')
- expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
+ it 'declines application and redirects to sign in page' do
+ expect(current_path).to eq(new_user_session_path)
+ expect(page).to have_content('You have declined the invitation to join group Owned.')
+ expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
end
end
- context 'when signed out' do
+ context 'as a non-existing user' do
before do
visit decline_invite_path(group_invite.raw_invite_token)
end
- it 'declines application and redirects to sign in page' do
- expect(current_path).to eq(new_user_session_path)
-
- expect(page).to have_content('You have declined the invitation to join group Owned.')
+ it 'declines application and shows a decline page' do
+ expect(current_path).to eq(decline_invite_path(group_invite.raw_invite_token))
+ expect(page).to have_content('You successfully declined the invitation')
expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
index 5ea89a7984f..6e99cfb3293 100644
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb
@@ -23,7 +23,15 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
expect(container).to have_content("Close #{human_model_name}")
expect(container).to have_content('Report abuse')
expect(container).to have_content("Report #{human_model_name.pluralize} that are abusive, inappropriate or spam.")
- expect(container).to have_selector('.close-item.droplab-item-selected')
+
+ if issuable.is_a?(MergeRequest)
+ page.within('.js-issuable-close-dropdown') do
+ expect(page).to have_link('Close merge request')
+ end
+ else
+ expect(container).to have_selector('.close-item.droplab-item-selected')
+ end
+
expect(container).to have_selector('.report-item')
expect(container).not_to have_selector('.report-item.droplab-item-selected')
expect(container).not_to have_selector('.reopen-item')
@@ -123,7 +131,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'shows only the `Edit` button' do
expect(page).to have_link('Edit')
- expect(page).not_to have_link('Report abuse')
+ expect(page).to have_link('Report abuse')
expect(page).not_to have_button('Close merge request')
expect(page).not_to have_button('Reopen merge request')
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index b1ffaaa7c7e..3f00bdc478d 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -48,6 +48,14 @@ RSpec.describe 'issuable list', :js do
end
end
+ it 'displays a warning if counting the number of issues times out' do
+ allow_any_instance_of(IssuesFinder).to receive(:count_by_state).and_raise(ActiveRecord::QueryCanceled)
+
+ visit_issuable_list(:issue)
+
+ expect(page).to have_text('Open ? Closed ? All ?')
+ end
+
it "counts merge requests closing issues icons for each issue" do
visit_issuable_list(:issue)
diff --git a/spec/features/issuables/markdown_references/internal_references_spec.rb b/spec/features/issuables/markdown_references/internal_references_spec.rb
index aceaea8d2ed..07d4271eed7 100644
--- a/spec/features/issuables/markdown_references/internal_references_spec.rb
+++ b/spec/features/issuables/markdown_references/internal_references_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe "Internal references", :js do
add_note("##{public_project_issue.to_reference(private_project)}")
end
- context "when user doesn't have access to private project" do
+ context "when user doesn't have access to private project", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/257832' do
before do
sign_in(public_project_user)
@@ -52,7 +52,7 @@ RSpec.describe "Internal references", :js do
visit(project_issue_path(public_project, public_project_issue))
end
- it "doesn't show any references" do
+ it "doesn't show any references", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/257832' do
page.within(".issue-details") do
expect(page).not_to have_content("#merge-requests .merge-requests-title")
end
@@ -94,7 +94,7 @@ RSpec.describe "Internal references", :js do
add_note("##{public_project_merge_request.to_reference(private_project)}")
end
- context "when user doesn't have access to private project" do
+ context "when user doesn't have access to private project", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/257832' do
before do
sign_in(public_project_user)
@@ -121,7 +121,7 @@ RSpec.describe "Internal references", :js do
visit(project_merge_request_path(public_project, public_project_merge_request))
end
- it "doesn't show any references" do
+ it "doesn't show any references", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/257832' do
page.within(".merge-request-details") do
expect(page).not_to have_content("#merge-requests .merge-requests-title")
end
diff --git a/spec/features/issuables/merge_request_discussion_lock_spec.rb b/spec/features/issuables/merge_request_discussion_lock_spec.rb
new file mode 100644
index 00000000000..4e0265839f6
--- /dev/null
+++ b/spec/features/issuables/merge_request_discussion_lock_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+# TODO use shared examples to merge this spec with discussion_lock_spec.rb
+# https://gitlab.com/gitlab-org/gitlab/-/issues/255910
+
+require 'spec_helper'
+
+RSpec.describe 'Merge Request Discussion Lock', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project, author: user) }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when a user is a team member' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when the discussion is unlocked' do
+ it 'the user can lock the merge_request' do
+ visit project_merge_request_path(merge_request.project, merge_request)
+
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Lock')
+ end
+
+ expect(find('[data-testid="lock-status"]')).to have_content('Locked')
+ end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ merge_request.update_attribute(:discussion_locked, true)
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can unlock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Unlock')
+ end
+
+ expect(find('[data-testid="lock-status"]')).to have_content('Unlocked')
+ end
+ end
+ end
+
+ context 'when a user is not a team member' do
+ context 'when the discussion is unlocked' do
+ before do
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can not lock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ merge_request.update_attribute(:discussion_locked, true)
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can not unlock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/csv_spec.rb b/spec/features/issues/csv_spec.rb
index 8d06bf24f8b..c93693ec40a 100644
--- a/spec/features/issues/csv_spec.rb
+++ b/spec/features/issues/csv_spec.rb
@@ -31,13 +31,13 @@ RSpec.describe 'Issues csv' do
end
it 'triggers an email export' do
- expect(ExportCsvWorker).to receive(:perform_async).with(user.id, project.id, hash_including("project_id" => project.id))
+ expect(IssuableExportCsvWorker).to receive(:perform_async).with(:issue, user.id, project.id, hash_including("project_id" => project.id))
request_csv
end
it "doesn't send request params to ExportCsvWorker" do
- expect(ExportCsvWorker).to receive(:perform_async).with(anything, anything, hash_excluding("controller" => anything, "action" => anything))
+ expect(IssuableExportCsvWorker).to receive(:perform_async).with(:issue, anything, anything, hash_excluding("controller" => anything, "action" => anything))
request_csv
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 0165fba9ace..ff78b9e608f 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -6,7 +6,9 @@ RSpec.describe 'GFM autocomplete', :js do
let_it_be(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
let_it_be(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
- let_it_be(:project) { create(:project) }
+ let_it_be(:group) { create(:group, name: 'Ancestor') }
+ let_it_be(:child_group) { create(:group, parent: group, name: 'My group') }
+ let_it_be(:project) { create(:project, group: child_group) }
let_it_be(:label) { create(:label, project: project, title: 'special+') }
let(:issue) { create(:issue, project: project) }
@@ -530,7 +532,7 @@ RSpec.describe 'GFM autocomplete', :js do
expect(page).to have_selector('.tribute-container', visible: true)
- expect(find('.tribute-container ul', visible: true).text).to have_content(user_xss.username)
+ expect(find('.tribute-container ul', visible: true)).to have_text(user_xss.username)
end
it 'selects the first item for assignee dropdowns' do
@@ -558,6 +560,24 @@ RSpec.describe 'GFM autocomplete', :js do
expect(find('.tribute-container ul', visible: true)).to have_content(user.name)
end
+ context 'when autocompleting for groups' do
+ it 'shows the group when searching for the name of the group' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@mygroup')
+ end
+
+ expect(find('.tribute-container ul', visible: true)).to have_text('My group')
+ end
+
+ it 'does not show the group when searching for the name of the parent of the group' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@ancestor')
+ end
+
+ expect(find('.tribute-container ul', visible: true)).not_to have_text('My group')
+ end
+ end
+
context 'if a selected value has special characters' do
it 'wraps the result in double quotes' do
note = find('#note-body')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 38d11ee2560..94a1de06488 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -16,129 +16,83 @@ RSpec.describe 'Issue Sidebar' do
sign_in(user)
end
- context 'assignee', :js do
+ context 'when concerning the assignee', :js do
let(:user2) { create(:user) }
let(:issue2) { create(:issue, project: project, author: user2) }
- context 'when invite_members_version_a experiment is enabled' do
- before do
- stub_experiment_for_user(invite_members_version_a: true)
- end
+ include_examples 'issuable invite members experiments' do
+ let(:issuable_path) { project_issue_path(project, issue2) }
+ end
- context 'when user can not see invite members' do
- before do
- project.add_developer(user)
- visit_issue(project, issue2)
+ context 'when user is a developer' do
+ before do
+ project.add_developer(user)
+ visit_issue(project, issue2)
- find('.block.assignee .edit-link').click
+ find('.block.assignee .edit-link').click
- wait_for_requests
- end
+ wait_for_requests
+ end
- it 'does not see link to invite members' do
- page.within '.dropdown-menu-user' do
- expect(page).not_to have_link('Invite Members')
- end
+ it 'shows author in assignee dropdown' do
+ page.within '.dropdown-menu-user' do
+ expect(page).to have_content(user2.name)
end
end
- context 'when user can see invite members' do
- before do
- project.add_maintainer(user)
- visit_issue(project, issue2)
-
- find('.block.assignee .edit-link').click
+ it 'shows author when filtering assignee dropdown' do
+ page.within '.dropdown-menu-user' do
+ find('.dropdown-input-field').set(user2.name)
wait_for_requests
- end
- it 'sees link to invite members' do
- page.within '.dropdown-menu-user' do
- expect(page).to have_link('Invite Members', href: project_project_members_path(project))
- expect(page).to have_selector('[data-track-event="click_invite_members"]')
- expect(page).to have_selector("[data-track-label='edit_assignee']")
- end
+ expect(page).to have_content(user2.name)
end
end
- end
-
- context 'when invite_members_version_a experiment is not enabled' do
- context 'when user is a developer' do
- before do
- project.add_developer(user)
- visit_issue(project, issue2)
- find('.block.assignee .edit-link').click
+ it 'assigns yourself' do
+ find('.block.assignee .dropdown-menu-toggle').click
- wait_for_requests
- end
-
- it 'shows author in assignee dropdown' do
- page.within '.dropdown-menu-user' do
- expect(page).to have_content(user2.name)
- end
- end
+ click_button 'assign yourself'
- it 'shows author when filtering assignee dropdown' do
- page.within '.dropdown-menu-user' do
- find('.dropdown-input-field').native.send_keys user2.name
- sleep 1 # Required to wait for end of input delay
+ wait_for_requests
- wait_for_requests
+ find('.block.assignee .edit-link').click
- expect(page).to have_content(user2.name)
- end
+ page.within '.dropdown-menu-user' do
+ expect(page.find('.dropdown-header')).to be_visible
+ expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name)
end
+ end
- it 'assigns yourself' do
- find('.block.assignee .dropdown-menu-toggle').click
-
- click_button 'assign yourself'
-
- wait_for_requests
+ it 'keeps your filtered term after filtering and dismissing the dropdown' do
+ find('.dropdown-input-field').set(user2.name)
- find('.block.assignee .edit-link').click
+ wait_for_requests
- page.within '.dropdown-menu-user' do
- expect(page.find('.dropdown-header')).to be_visible
- expect(page.find('.dropdown-menu-user-link.is-active')).to have_content(user.name)
- end
+ page.within '.dropdown-menu-user' do
+ expect(page).not_to have_content 'Unassigned'
+ click_link user2.name
end
- it 'keeps your filtered term after filtering and dismissing the dropdown' do
- find('.dropdown-input-field').native.send_keys user2.name
-
- wait_for_requests
-
- page.within '.dropdown-menu-user' do
- expect(page).not_to have_content 'Unassigned'
- click_link user2.name
- end
-
- find('.js-right-sidebar').click
- find('.block.assignee .edit-link').click
+ find('.js-right-sidebar').click
+ find('.block.assignee .edit-link').click
- expect(page.all('.dropdown-menu-user li').length).to eq(1)
- expect(find('.dropdown-input-field').value).to eq(user2.name)
- end
+ expect(page.all('.dropdown-menu-user li').length).to eq(1)
+ expect(find('.dropdown-input-field').value).to eq(user2.name)
end
+ end
- context 'when user is a maintainer' do
- before do
- project.add_maintainer(user)
- visit_issue(project, issue2)
+ it 'shows label text as "Apply" when assignees are changed' do
+ project.add_developer(user)
+ visit_issue(project, issue2)
- find('.block.assignee .edit-link').click
+ find('.block.assignee .edit-link').click
+ wait_for_requests
- wait_for_requests
- end
+ click_on 'Unassigned'
- it 'shows author in assignee dropdown and no invite link' do
- page.within '.dropdown-menu-user' do
- expect(page).not_to have_link('Invite Members')
- end
- end
- end
+ expect(page).to have_link('Apply')
end
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 3de33049db0..315d1c911a2 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'Manually create a todo item from issue', :js do
it 'creates todo when clicking button' do
page.within '.issuable-sidebar' do
- click_button 'Add a To Do'
+ click_button 'Add a to do'
expect(page).to have_content 'Mark as done'
end
@@ -32,7 +32,7 @@ RSpec.describe 'Manually create a todo item from issue', :js do
it 'marks a todo as done' do
page.within '.issuable-sidebar' do
- click_button 'Add a To Do'
+ click_button 'Add a to do'
click_button 'Mark as done'
end
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 88b8e9624e2..de746415205 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -6,292 +6,356 @@ 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(:label_assigned) { create(:label, project: project, title: 'verisimilitude') }
+ let_it_be(:label_unassigned) { create(:label, project: project, title: 'syzygy') }
+ let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user], labels: [label_assigned]) }
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
-
- context "from edit page" do
+ context 'with authorized user' do
before do
- visit edit_project_issue_path(project, issue)
+ project.add_developer(user)
+ project_with_milestones.add_developer(user)
+ sign_in(user)
end
- it "previews content" do
- form = first(".gfm-form")
-
- page.within(form) do
- fill_in("Description", with: "Bug fixed :smile:")
- click_button("Preview")
+ context "from edit page" do
+ before do
+ visit edit_project_issue_path(project, issue)
end
- expect(form).to have_button("Write")
- end
-
- it 'allows user to select unassigned' do
- visit edit_project_issue_path(project, issue)
-
- expect(page).to have_content "Assignee #{user.name}"
+ it "previews content" do
+ form = first(".gfm-form")
- first('.js-user-search').click
- click_link 'Unassigned'
-
- click_button 'Save changes'
+ page.within(form) do
+ fill_in("Description", with: "Bug fixed :smile:")
+ click_button("Preview")
+ end
- page.within('.assignee') do
- expect(page).to have_content 'None - assign yourself'
+ expect(form).to have_button("Write")
end
- end
- context 'with due date' do
- before do
+ it 'allows user to select unassigned' do
visit edit_project_issue_path(project, issue)
- end
-
- it 'saves with due date' do
- date = Date.today.at_beginning_of_month.tomorrow
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
- find('#issuable-due-date').click
-
- page.within '.pika-single' do
- click_button date.day
- end
+ expect(page).to have_content "Assignee #{user.name}"
- expect(find('#issuable-due-date').value).to eq date.to_s
+ first('.js-user-search').click
+ click_link 'Unassigned'
click_button 'Save changes'
- page.within '.issuable-sidebar' do
- expect(page).to have_content date.to_s(:medium)
+ page.within('.assignee') do
+ expect(page).to have_content 'None - assign yourself'
end
end
- it 'warns about version conflict' do
- issue.update(title: "New title")
+ context 'with due date' do
+ before do
+ visit edit_project_issue_path(project, issue)
+ end
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month.tomorrow
- click_button 'Save changes'
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
- expect(page).to have_content 'Someone edited the issue the same time you did'
- end
- end
- end
+ page.within '.pika-single' do
+ click_button date.day
+ end
- context "from issue#show" do
- before do
- visit project_issue_path(project, issue)
- end
+ expect(find('#issuable-due-date').value).to eq date.to_s
+
+ click_button 'Save changes'
+
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
- describe 'update labels' do
- it 'will not send ajax request when no data is changed' do
- page.within '.labels' do
- click_on 'Edit'
+ it 'warns about version conflict' do
+ issue.update(title: "New title")
- find('.dropdown-title button').click
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
- expect(page).not_to have_selector('.block-loading')
- expect(page).not_to have_selector('.gl-spinner')
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Someone edited the issue the same time you did'
end
end
end
- describe 'update assignee' do
- context 'by authorized user' do
- def close_dropdown_menu_if_visible
- find('.dropdown-menu-toggle', visible: :all).tap do |toggle|
- toggle.click if toggle.visible?
+ context "from issue#show" do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ describe 'update labels' do
+ it 'will not send ajax request when no data is changed' do
+ page.within '.labels' do
+ click_on 'Edit'
+
+ find('.dropdown-title button').click
+
+ expect(page).not_to have_selector('.block-loading')
+ expect(page).not_to have_selector('.gl-spinner')
end
end
- it 'allows user to select unassigned' do
- visit project_issue_path(project, issue)
+ it 'can add label to issue' do
+ page.within '.block.labels' do
+ expect(page).to have_text('verisimilitude')
+ expect(page).not_to have_text('syzygy')
- page.within('.assignee') do
- expect(page).to have_content "#{user.name}"
+ click_on 'Edit'
- click_link 'Edit'
- click_link 'Unassigned'
- first('.title').click
- expect(page).to have_content 'None - assign yourself'
+ wait_for_requests
+
+ click_on 'syzygy'
+ find('.dropdown-header-button').click
+
+ wait_for_requests
+
+ expect(page).to have_text('verisimilitude')
+ expect(page).to have_text('syzygy')
end
end
- it 'allows user to select an assignee' do
- issue2 = create(:issue, project: project, author: user)
- visit project_issue_path(project, issue2)
+ it 'can remove label from issue by clicking on the label `x` button' do
+ page.within '.block.labels' do
+ expect(page).to have_text('verisimilitude')
+
+ within '.gl-label' do
+ click_button
+ end
+
+ wait_for_requests
- page.within('.assignee') do
- expect(page).to have_content "None"
+ expect(page).not_to have_text('verisimilitude')
end
+ end
+ end
- page.within '.assignee' do
- click_link 'Edit'
+ describe 'update assignee' do
+ context 'by authorized user' do
+ def close_dropdown_menu_if_visible
+ find('.dropdown-menu-toggle', visible: :all).tap do |toggle|
+ toggle.click if toggle.visible?
+ end
end
- page.within '.dropdown-menu-user' do
- click_link user.name
+ it 'allows user to select unassigned' do
+ visit project_issue_path(project, issue)
+
+ page.within('.assignee') do
+ expect(page).to have_content "#{user.name}"
+
+ click_link 'Edit'
+ click_link 'Unassigned'
+ first('.title').click
+ expect(page).to have_content 'None - assign yourself'
+ end
end
- page.within('.assignee') do
- expect(page).to have_content user.name
+ it 'allows user to select an assignee' do
+ issue2 = create(:issue, project: project, author: user)
+ visit project_issue_path(project, issue2)
+
+ page.within('.assignee') do
+ expect(page).to have_content "None"
+ end
+
+ page.within '.assignee' do
+ click_link 'Edit'
+ end
+
+ page.within '.dropdown-menu-user' do
+ click_link user.name
+ end
+
+ page.within('.assignee') do
+ expect(page).to have_content user.name
+ end
end
- end
- it 'allows user to unselect themselves' do
- issue2 = create(:issue, project: project, author: user, assignees: [user])
+ it 'allows user to unselect themselves' do
+ issue2 = create(:issue, project: project, author: user, assignees: [user])
- visit project_issue_path(project, issue2)
+ visit project_issue_path(project, issue2)
- page.within '.assignee' do
- expect(page).to have_content user.name
+ page.within '.assignee' do
+ expect(page).to have_content user.name
- click_link 'Edit'
- click_link user.name
+ click_link 'Edit'
+ click_link user.name
- close_dropdown_menu_if_visible
+ close_dropdown_menu_if_visible
- page.within '.value .assign-yourself' do
- expect(page).to have_content "None"
+ page.within '.value .assign-yourself' do
+ expect(page).to have_content "None"
+ end
end
end
end
- end
- context 'by unauthorized user' do
- let(:guest) { create(:user) }
+ context 'by unauthorized user' do
+ let(:guest) { create(:user) }
- before do
- project.add_guest(guest)
- end
+ before do
+ project.add_guest(guest)
+ end
- it 'shows assignee text' do
- sign_out(:user)
- sign_in(guest)
+ it 'shows assignee text' do
+ sign_out(:user)
+ sign_in(guest)
- visit project_issue_path(project, issue)
- expect(page).to have_content issue.assignees.first.name
+ visit project_issue_path(project, issue)
+ expect(page).to have_content issue.assignees.first.name
+ end
end
end
- end
- describe 'update milestone' do
- context 'by authorized user' do
- it 'allows user to select unassigned' do
- visit project_issue_path(project, issue)
+ describe 'update milestone' do
+ context 'by authorized user' do
+ it 'allows user to select unassigned' do
+ visit project_issue_path(project, issue)
- page.within('.milestone') do
- expect(page).to have_content "None"
- end
+ page.within('.milestone') do
+ expect(page).to have_content "None"
+ end
- find('.block.milestone .edit-link').click
- sleep 2 # wait for ajax stuff to complete
- first('.dropdown-content li').click
- sleep 2
- page.within('.milestone') do
- expect(page).to have_content 'None'
+ find('.block.milestone .edit-link').click
+ sleep 2 # wait for ajax stuff to complete
+ first('.dropdown-content li').click
+ sleep 2
+ page.within('.milestone') do
+ expect(page).to have_content 'None'
+ end
end
- end
- it 'allows user to de-select milestone' do
- visit project_issue_path(project, issue)
+ it 'allows user to de-select milestone' do
+ visit project_issue_path(project, issue)
- page.within('.milestone') do
- click_link 'Edit'
- click_link milestone.title
+ page.within('.milestone') do
+ click_link 'Edit'
+ click_link milestone.title
- page.within '.value' do
- expect(page).to have_content milestone.title
- end
+ page.within '.value' do
+ expect(page).to have_content milestone.title
+ end
- click_link 'Edit'
- click_link milestone.title
+ click_link 'Edit'
+ click_link milestone.title
- page.within '.value' do
- expect(page).to have_content 'None'
+ page.within '.value' do
+ expect(page).to have_content 'None'
+ end
end
end
- end
- it 'allows user to search milestone' do
- visit project_issue_path(project_with_milestones, issue_with_milestones)
+ 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('.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
+ page.within '.dropdown-content' do
+ expect(page).to have_content milestones[0].title
+ end
end
end
end
- end
- context 'by unauthorized user' do
- let(:guest) { create(:user) }
+ context 'by unauthorized user' do
+ let(:guest) { create(:user) }
- before do
- project.add_guest(guest)
- issue.milestone = milestone
- issue.save
- end
+ before do
+ project.add_guest(guest)
+ issue.milestone = milestone
+ issue.save
+ end
- it 'shows milestone text' do
- sign_out(:user)
- sign_in(guest)
+ it 'shows milestone text' do
+ sign_out(:user)
+ sign_in(guest)
- visit project_issue_path(project, issue)
- expect(page).to have_content milestone.title
+ visit project_issue_path(project, issue)
+ expect(page).to have_content milestone.title
+ end
end
end
- end
- context 'update due date' do
- it 'adds due date to issue' do
- date = Date.today.at_beginning_of_month + 2.days
+ context 'update due date' do
+ it 'adds due date to issue' do
+ date = Date.today.at_beginning_of_month + 2.days
- page.within '.due_date' do
- click_link 'Edit'
+ page.within '.due_date' do
+ click_link 'Edit'
- page.within '.pika-single' do
- click_button date.day
- end
+ page.within '.pika-single' do
+ click_button date.day
+ end
- wait_for_requests
+ wait_for_requests
- expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
+ expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
+ end
end
- end
- it 'removes due date from issue' do
- date = Date.today.at_beginning_of_month + 2.days
+ it 'removes due date from issue' do
+ date = Date.today.at_beginning_of_month + 2.days
- page.within '.due_date' do
- click_link 'Edit'
+ page.within '.due_date' do
+ click_link 'Edit'
- page.within '.pika-single' do
- click_button date.day
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ wait_for_requests
+
+ expect(page).to have_no_content 'None'
+
+ click_link 'remove due date'
+ expect(page).to have_content 'None'
end
+ end
+ end
+ end
+ end
- wait_for_requests
+ context 'with unauthorized user' do
+ before do
+ sign_in(user)
+ end
- expect(page).to have_no_content 'None'
+ context "from issue#show" do
+ before do
+ visit project_issue_path(project, issue)
+ end
- click_link 'remove due date'
- expect(page).to have_content 'None'
+ describe 'updating labels' do
+ it 'cannot edit labels' do
+ page.within '.block.labels' do
+ expect(page).not_to have_button('Edit')
+ end
+ end
+
+ it 'cannot remove label with a click as it has no `x` button' do
+ page.within '.block.labels' do
+ within '.gl-label' do
+ expect(page).not_to have_button
+ end
+ end
end
end
end
diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb
index c9b751715bc..d27cdb774a5 100644
--- a/spec/features/issues/user_sees_live_update_spec.rb
+++ b/spec/features/issues/user_sees_live_update_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe 'Issues > User sees live update', :js do
expect(page).to have_css('.sidebar-item-warning-message')
within('.sidebar-item-warning-message') do
- find('.btn-close').click
+ find('[data-testid="confidential-toggle"]').click
end
wait_for_requests
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
index 3f18764aa58..9b1c8be1513 100644
--- a/spec/features/issues/user_views_issue_spec.rb
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -5,7 +5,7 @@ require "spec_helper"
RSpec.describe "User views issue" do
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(:issue) { create(:issue, project: project, description: "# Description header\n\n**Lorem** _ipsum_ dolor sit [amet](https://example.com)", author: user) }
let_it_be(:note) { create(:note, noteable: issue, project: project, author: user) }
before_all do
@@ -20,6 +20,8 @@ RSpec.describe "User views issue" do
it { expect(page).to have_header_with_correct_id_and_link(1, "Description header", "description-header") }
+ it_behaves_like 'page meta description', ' Description header Lorem ipsum dolor sit amet'
+
it 'shows the merge request and issue actions', :aggregate_failures do
expect(page).to have_link('New issue')
expect(page).to have_button('Create merge request')
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index eed9a6d1043..5d141580874 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe 'Labels Hierarchy', :js do
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
before do
+ stub_feature_flags(graphql_board_lists: false)
grandparent.add_owner(user)
sign_in(user)
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index 40f6482c948..c8fc23bebf9 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -41,7 +41,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
write_comment
page.within('.review-bar-content') do
- click_button 'Finish review'
click_button 'Submit review'
end
@@ -64,18 +63,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(page).to have_selector('.note:not(.draft-note)', text: 'Line is wrong')
end
- it 'discards review' do
- write_comment
-
- click_button 'Discard review'
-
- click_button 'Delete all pending comments'
-
- wait_for_requests
-
- expect(page).not_to have_selector('.draft-note-component')
- end
-
it 'deletes draft note' do
write_comment
@@ -149,7 +136,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
write_reply_to_discussion(resolve: true)
page.within('.review-bar-content') do
- click_button 'Finish review'
click_button 'Submit review'
end
@@ -192,7 +178,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
write_reply_to_discussion(button_text: 'Start a review', unresolve: true)
page.within('.review-bar-content') do
- click_button 'Finish review'
click_button 'Submit review'
end
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 0e65cb358da..a98bfd1c8a4 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -26,7 +26,12 @@ RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork
visit project_merge_request_path(target_project, merge_request)
click_link 'Changes'
wait_for_requests
- first('.js-file-title').find('.js-edit-blob').click
+
+ page.within(first('.js-file-title')) do
+ find('.js-diff-more-actions').click
+ find('.js-edit-blob').click
+ end
+
wait_for_requests
end
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
index 3a199951b56..ad1ad067935 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -34,7 +34,8 @@ RSpec.describe 'User comments on a diff', :js do
page.within('.diff-files-holder > div:nth-child(3)') do
expect(page).to have_content('Line is wrong')
- find('.js-btn-vue-toggle-comments').click
+ find('.js-diff-more-actions').click
+ click_button 'Hide comments on this file'
expect(page).not_to have_content('Line is wrong')
end
@@ -67,7 +68,8 @@ RSpec.describe 'User comments on a diff', :js do
# Hide the comment.
page.within('.diff-files-holder > div:nth-child(3)') do
- find('.js-btn-vue-toggle-comments').click
+ find('.js-diff-more-actions').click
+ click_button 'Hide comments on this file'
expect(page).not_to have_content('Line is wrong')
end
@@ -80,7 +82,8 @@ RSpec.describe 'User comments on a diff', :js do
# Show the comment.
page.within('.diff-files-holder > div:nth-child(3)') do
- find('.js-btn-vue-toggle-comments').click
+ find('.js-diff-more-actions').click
+ click_button 'Show comments on this file'
end
# Now both the comments should be shown.
diff --git a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
index affd6f6b7b5..7d55a72c2b1 100644
--- a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
+++ b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'Merge request > User edits assignees sidebar', :js do
let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-menu li[data-user-id=\"#{assignee.id}\"]") }
let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item.find('a')['data-title'] || '' }
- context 'when invite_members_version_a experiment is not enabled' do
+ context 'when user is an owner' do
before do
stub_const('Autocomplete::UsersFinder::LIMIT', users_find_limit)
@@ -52,12 +52,6 @@ RSpec.describe 'Merge request > User edits assignees sidebar', :js do
it "shows assignee tooltip '#{expected_tooltip}" do
expect(sidebar_assignee_dropdown_tooltip).to eql(expected_tooltip)
end
-
- it 'does not show invite link' do
- page.within '.dropdown-menu-user' do
- expect(page).not_to have_link('Invite Members')
- end
- end
end
end
@@ -74,48 +68,15 @@ RSpec.describe 'Merge request > User edits assignees sidebar', :js do
end
end
- context 'when invite_members_version_a experiment is enabled' do
+ context 'with invite members experiment considerations' do
let_it_be(:user) { create(:user) }
before do
- stub_experiment_for_user(invite_members_version_a: true)
sign_in(user)
end
- context 'when user can not see invite members' do
- before do
- project.add_developer(user)
- visit project_merge_request_path(project, merge_request)
-
- find('.block.assignee .edit-link').click
-
- wait_for_requests
- end
-
- it 'does not see link to invite members' do
- page.within '.dropdown-menu-user' do
- expect(page).not_to have_link('Invite Members')
- end
- end
- end
-
- context 'when user can see invite members' do
- before do
- project.add_maintainer(user)
- visit project_merge_request_path(project, merge_request)
-
- find('.block.assignee .edit-link').click
-
- wait_for_requests
- end
-
- it 'sees link to invite members' do
- page.within '.dropdown-menu-user' do
- expect(page).to have_link('Invite Members', href: project_project_members_path(project))
- expect(page).to have_selector('[data-track-event="click_invite_members"]')
- expect(page).to have_selector("[data-track-label='edit_assignee']")
- end
- end
+ include_examples 'issuable invite members experiments' do
+ let(:issuable_path) { project_merge_request_path(project, merge_request) }
end
end
end
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
index 397ca70f4a1..817b4e0b48e 100644
--- a/spec/features/merge_request/user_edits_mr_spec.rb
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -21,24 +21,6 @@ RSpec.describe 'Merge request > User edits MR' do
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)
diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb
index 0340d9ccc3d..0cdc87de761 100644
--- a/spec/features/merge_request/user_expands_diff_spec.rb
+++ b/spec/features/merge_request/user_expands_diff_spec.rb
@@ -7,6 +7,7 @@ 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(increased_diff_limits: false)
visit(diffs_project_merge_request_path(project, merge_request))
wait_for_requests
diff --git a/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb b/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
new file mode 100644
index 00000000000..f5bca7cf015
--- /dev/null
+++ b/spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge request > User marks merge request as draft', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_maintainer(user)
+
+ sign_in(user)
+
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'toggles draft status' do
+ click_link 'Mark as draft'
+
+ expect(page).to have_content("Draft: #{merge_request.title}")
+
+ page.within('.detail-page-header-actions') do
+ click_link 'Mark as ready'
+ end
+
+ expect(page).to have_content(merge_request.title)
+ end
+end
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index 3dc49fb4dea..444d5371e7a 100644
--- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
it_behaves_like 'Merge when pipeline succeeds activator'
end
- context 'when enabled after pipeline status changed' do
+ context 'when enabled after pipeline status changed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/258667' do
before do
pipeline.run!
diff --git a/spec/features/merge_request/user_reopens_merge_request_spec.rb b/spec/features/merge_request/user_reopens_merge_request_spec.rb
index 7866ece84ac..4a05a3be59a 100644
--- a/spec/features/merge_request/user_reopens_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reopens_merge_request_spec.rb
@@ -15,7 +15,11 @@ RSpec.describe 'User reopens a merge requests', :js do
end
it 'reopens a merge request' do
- click_button('Reopen merge request', match: :first)
+ find('.js-issuable-close-dropdown .dropdown-toggle').click
+
+ click_link('Reopen merge request', match: :first)
+
+ wait_for_requests
page.within('.status-box') do
expect(page).to have_content('Open')
diff --git a/spec/features/merge_request/user_resolves_wip_mr_spec.rb b/spec/features/merge_request/user_resolves_wip_mr_spec.rb
index a9d4c4df507..b67167252e1 100644
--- a/spec/features/merge_request/user_resolves_wip_mr_spec.rb
+++ b/spec/features/merge_request/user_resolves_wip_mr_spec.rb
@@ -35,7 +35,9 @@ RSpec.describe 'Merge request > User resolves Work in Progress', :js do
expect(page.find('.ci-widget-content')).to have_content("Pipeline ##{pipeline.id}")
expect(page).to have_content "This merge request is still a work in progress."
- click_button('Mark as ready')
+ page.within('.mr-state-widget') do
+ click_button('Mark as ready')
+ end
wait_for_requests
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 7a3a14e61e3..a7713ed9964 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
- expect(page).to have_selector("[id=\"#{changelog_id}\"] a.js-edit-blob")
+ expect(page).to have_selector("[id=\"#{changelog_id}\"] .js-edit-blob", visible: false)
end
end
@@ -73,6 +73,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
+ find("[id=\"#{changelog_id}\"] .js-diff-more-actions").click
find("[id=\"#{changelog_id}\"] .js-edit-blob").click
expect(page).to have_selector('.js-fork-suggestion-button', count: 1)
diff --git a/spec/features/merge_request/user_sees_page_metadata_spec.rb b/spec/features/merge_request/user_sees_page_metadata_spec.rb
new file mode 100644
index 00000000000..7b3e07152a0
--- /dev/null
+++ b/spec/features/merge_request/user_sees_page_metadata_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge request > User sees page metadata' do
+ let(:merge_request) { create(:merge_request, description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') }
+ let(:project) { merge_request.target_project }
+ let(:user) { project.creator }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
+end
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index 8e15ba6cf8d..107fc002ebd 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
wait_for_requests
- expect(page.find('.js-run-mr-pipeline')).to have_text('Run Pipeline')
+ expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run Pipeline')
end
end
@@ -66,7 +66,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
wait_for_requests
- expect(page.find('.js-run-mr-pipeline')).to have_text('Run Pipeline')
+ expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run Pipeline')
end
end
end
diff --git a/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb b/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb
new file mode 100644
index 00000000000..93807512d9c
--- /dev/null
+++ b/spec/features/merge_request/user_sees_suggest_pipeline_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge request > User sees suggest pipeline', :js do
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.source_project }
+ let(:user) { project.creator }
+
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ stub_experiment(suggest_pipeline: true)
+ stub_experiment_for_user(suggest_pipeline: true)
+ project.add_maintainer(user)
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows the suggest pipeline widget and then allows dismissal correctly' do
+ expect(page).to have_content('Are you adding technical debt or code vulnerabilities?')
+
+ page.within '.mr-pipeline-suggest' do
+ find('[data-testid="close"]').click
+ end
+
+ wait_for_requests
+
+ expect(page).not_to have_content('Are you adding technical debt or code vulnerabilities?')
+
+ # Reload so we know the user callout was registered
+ visit page.current_url
+
+ expect(page).not_to have_content('Are you adding technical debt or code vulnerabilities?')
+ end
+end
diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
index 39495832547..9268190c7e0 100644
--- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
+++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
@@ -119,7 +119,8 @@ RSpec.describe 'User comments on a diff', :js do
it 'can add and remove suggestions from a batch' do
files.each_with_index do |file, index|
page.within("[id='#{file[:hash]}']") do
- find("button[title='Show full file']").click
+ find('.js-diff-more-actions').click
+ click_button 'Show full file'
wait_for_requests
click_diff_line(find("[id='#{file[:line_code]}']"))
@@ -130,7 +131,9 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests
end
end
+ end
+ files.each_with_index do |file, index|
page.within("[id='#{file[:hash]}']") do
expect(page).not_to have_content('Applied')
@@ -247,7 +250,7 @@ RSpec.describe 'User comments on a diff', :js do
end
context 'multiple suggestions in a single note' do
- it 'suggestions are presented' do
+ it 'suggestions are presented', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/258989' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
page.within('.js-discussion-note-form') do
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
index 448844ae57d..e8998f9457a 100644
--- a/spec/features/merge_request/user_views_open_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -22,7 +22,24 @@ RSpec.describe 'User views an open merge request' do
# returns the whole document, not the node's actual parent element
expect(find(:xpath, "#{node.path}/..").text).to eq(merge_request.description[2..-1])
- expect(page).to have_content(merge_request.title).and have_content(merge_request.description)
+ expect(page).to have_content(merge_request.title)
+ end
+
+ it 'has reviewers in sidebar' do
+ expect(page).to have_css('.reviewer')
+ end
+ end
+
+ context 'when merge_request_reviewers is turned off' do
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ stub_feature_flags(merge_request_reviewers: false)
+ visit(merge_request_path(merge_request))
+ end
+
+ it 'has reviewers in sidebar' do
+ expect(page).not_to have_css('.reviewer')
end
end
diff --git a/spec/features/merge_requests/user_filters_by_approvals_spec.rb b/spec/features/merge_requests/user_filters_by_approvals_spec.rb
new file mode 100644
index 00000000000..6dda9ca7952
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_approvals_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge Requests > User filters', :js do
+ include FilteredSearchHelpers
+
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { project.creator }
+ let_it_be(:group_user) { create(:user) }
+ let_it_be(:first_user) { create(:user) }
+
+ before do
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ end
+
+ context 'by "approved by"' do
+ let_it_be(:merge_request) { create(:merge_request, title: 'Bugfix3', source_project: project, source_branch: 'bugfix3') }
+
+ let_it_be(:merge_request_with_first_user_approval) do
+ create(:merge_request, source_project: project, title: 'Bugfix5').tap do |mr|
+ create(:approval, merge_request: mr, user: first_user)
+ end
+ end
+
+ let_it_be(:merge_request_with_group_user_approved) do
+ group = create(:group)
+ group.add_developer(group_user)
+
+ create(:merge_request, source_project: project, title: 'Bugfix6', source_branch: 'bugfix6').tap do |mr|
+ create(:approval, merge_request: mr, user: group_user)
+ end
+ end
+
+ context 'filtering by approved-by:none' do
+ it 'applies the filter' do
+ input_filtered_search('approved-by:=none')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+
+ expect(page).not_to have_content 'Bugfix5'
+ expect(page).not_to have_content 'Bugfix6'
+ expect(page).to have_content 'Bugfix3'
+ end
+ end
+
+ context 'filtering by approved-by:any' do
+ it 'applies the filter' do
+ input_filtered_search('approved-by:=any')
+
+ expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
+
+ expect(page).to have_content 'Bugfix5'
+ expect(page).not_to have_content 'Bugfix3'
+ end
+ end
+
+ context 'filtering by approved-by:@username' do
+ it 'applies the filter' do
+ input_filtered_search("approved-by:=@#{first_user.username}")
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+
+ expect(page).to have_content 'Bugfix5'
+ expect(page).not_to have_content 'Bugfix3'
+ end
+ end
+
+ context 'filtering by an approver from a group' do
+ it 'applies the filter' do
+ input_filtered_search("approved-by:=@#{group_user.username}")
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+
+ expect(page).to have_content 'Bugfix6'
+ expect(page).not_to have_content 'Bugfix5'
+ expect(page).not_to have_content 'Bugfix3'
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_filters_by_deployments_spec.rb b/spec/features/merge_requests/user_filters_by_deployments_spec.rb
new file mode 100644
index 00000000000..157454d4e10
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_deployments_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge Requests > User filters by deployments', :js do
+ include FilteredSearchHelpers
+
+ let!(:project) { create(:project, :public, :repository) }
+ let!(:user) { project.creator }
+ let!(:gstg) { create(:environment, project: project, name: 'gstg') }
+ let!(:gprd) { create(:environment, project: project, name: 'gprd') }
+
+ let(:mr1) do
+ create(
+ :merge_request,
+ :simple,
+ :merged,
+ author: user,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ let(:mr2) do
+ create(
+ :merge_request,
+ :simple,
+ :merged,
+ author: user,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ let(:deploy1) do
+ create(
+ :deployment,
+ :success,
+ deployable: nil,
+ environment: gstg,
+ project: project,
+ sha: mr1.diff_head_sha,
+ finished_at: Time.utc(2020, 10, 1, 0, 0)
+ )
+ end
+
+ let(:deploy2) do
+ create(
+ :deployment,
+ :success,
+ deployable: nil,
+ environment: gprd,
+ project: project,
+ sha: mr2.diff_head_sha,
+ finished_at: Time.utc(2020, 10, 2, 0, 0)
+ )
+ end
+
+ before do
+ deploy1.link_merge_requests(MergeRequest.where(id: mr1.id))
+ deploy2.link_merge_requests(MergeRequest.where(id: mr2.id))
+
+ sign_in(user)
+ visit(project_merge_requests_path(project, state: :merged))
+ end
+
+ describe 'filtering by deployed-before' do
+ it 'applies the filter' do
+ input_filtered_search('deployed-before:=2020-10-02')
+
+ expect(page).to have_issuable_counts(open: 0, merged: 1, all: 1)
+ expect(page).to have_content mr1.title
+ end
+ end
+
+ describe 'filtering by deployed-after' do
+ it 'applies the filter' do
+ input_filtered_search('deployed-after:=2020-10-01')
+
+ expect(page).to have_issuable_counts(open: 0, merged: 1, all: 1)
+ expect(page).to have_content mr2.title
+ end
+ end
+
+ describe 'filtering by environment' do
+ it 'applies the filter' do
+ input_filtered_search('environment:=gstg')
+
+ expect(page).to have_issuable_counts(open: 0, merged: 1, all: 1)
+ expect(page).to have_content mr1.title
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index 4531ef40901..36d28ae2822 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -8,6 +8,10 @@ RSpec.describe 'Merge requests > User lists merge requests' do
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+ let(:user4) { create(:user) }
+ let(:user5) { create(:user) }
before do
@fix = create(:merge_request,
@@ -15,6 +19,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do
source_project: project,
source_branch: 'fix',
assignees: [user],
+ reviewers: [user, user2, user3, user4, user5],
milestone: create(:milestone, project: project, due_date: '2013-12-11'),
created_at: 1.minute.ago,
updated_at: 1.minute.ago)
@@ -23,6 +28,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do
source_project: project,
source_branch: 'markdown',
assignees: [user],
+ reviewers: [user, user2, user3, user4],
milestone: create(:milestone, project: project, due_date: '2013-12-12'),
created_at: 2.minutes.ago,
updated_at: 2.minutes.ago)
@@ -34,6 +40,37 @@ RSpec.describe 'Merge requests > User lists merge requests' do
updated_at: 10.seconds.ago)
end
+ context 'when merge_request_reviewers is turned on' do
+ before do
+ stub_feature_flags(merge_request_reviewers: true)
+ visit_merge_requests(project, reviewer_id: user.id)
+ end
+
+ it 'has reviewers in MR list' do
+ expect(page).to have_css('.issuable-reviewers')
+ end
+
+ it 'shows reviewers avatar count badge if more_reviewers_count > 4' do
+ first_issuable_reviewers = first('.issuable-reviewers')
+
+ expect(first_issuable_reviewers).to have_content('2')
+ expect(first_issuable_reviewers).to have_css('.avatar-counter')
+ end
+
+ it 'does not show reviewers avatar count badge if more_reviewers_count <= 4' do
+ expect(page.all('.issuable-reviewers')[1]).not_to have_css('.avatar-counter')
+ end
+ end
+
+ context 'when merge_request_reviewers is turned false' do
+ it 'has no reviewers in MR list' do
+ stub_feature_flags(merge_request_reviewers: false)
+ visit_merge_requests(project, reviewer_id: user.id)
+
+ expect(page).not_to have_css('.issuable-reviewers')
+ end
+ end
+
it 'filters on no assignee' do
visit_merge_requests(project, assignee_id: IssuableFinder::Params::FILTER_NONE)
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index 4a7f14d5a1b..fefa2916c30 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe 'Milestone' do
find('input[name="commit"]').click
- expect(find('.alert-success')).to have_content('Assign some issues to this milestone.')
+ expect(find('[data-testid="no-issues-alert"]')).to have_content('Assign some issues to this milestone.')
expect(page).to have_content('Nov 16, 2016–Dec 16, 2016')
end
end
@@ -37,7 +37,7 @@ RSpec.describe 'Milestone' do
create(:issue, title: "Bugfix1", project: project, milestone: milestone, state: "closed")
visit project_milestone_path(project, milestone)
- expect(find('.alert-success')).to have_content('All issues for this milestone are closed. You may close this milestone now.')
+ expect(find('[data-testid="all-issues-closed-alert"]')).to have_content('All issues for this milestone are closed. You may close this milestone now.')
end
end
diff --git a/spec/features/milestones/user_views_milestone_spec.rb b/spec/features/milestones/user_views_milestone_spec.rb
index 420f8d49483..9c19f842427 100644
--- a/spec/features/milestones/user_views_milestone_spec.rb
+++ b/spec/features/milestones/user_views_milestone_spec.rb
@@ -4,15 +4,27 @@ require 'spec_helper'
RSpec.describe "User views milestone" do
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
- let_it_be(:milestone) { create(:milestone, project: project) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:milestone) { create(:milestone, project: project, description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') }
let_it_be(:labels) { create_list(:label, 2, project: project) }
- before do
+ before_all do
project.add_developer(user)
+ end
+
+ before do
sign_in(user)
end
+ context 'page description' do
+ before do
+ visit(project_milestone_path(project, milestone))
+ end
+
+ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
+ end
+
it "avoids N+1 database queries" do
issue_params = { project: project, assignees: [user], author: user, milestone: milestone, labels: labels }.freeze
@@ -25,7 +37,7 @@ RSpec.describe "User views milestone" do
expect { visit_milestone }.not_to exceed_query_limit(control)
end
- context 'limiting milestone issues' do
+ context 'issues list', :js do
before_all do
2.times do
create(:issue, milestone: milestone, project: project)
@@ -34,6 +46,28 @@ RSpec.describe "User views milestone" do
end
end
+ context 'for a project milestone' do
+ it 'does not show the project name' do
+ visit(project_milestone_path(project, milestone))
+
+ wait_for_requests
+
+ expect(page.find('#tab-issues')).not_to have_text(project.name)
+ end
+ end
+
+ context 'for a group milestone' do
+ let(:group_milestone) { create(:milestone, group: group) }
+
+ it 'shows the project name' do
+ create(:issue, project: project, milestone: group_milestone)
+
+ visit(group_milestone_path(group, group_milestone))
+
+ expect(page.find('#tab-issues')).to have_text(project.name)
+ end
+ end
+
context 'when issues on milestone are over DISPLAY_ISSUES_LIMIT' do
it "limits issues to display and shows warning" do
stub_const('Milestoneish::DISPLAY_ISSUES_LIMIT', 3)
@@ -56,6 +90,40 @@ RSpec.describe "User views milestone" do
end
end
+ context 'merge requests list', :js do
+ context 'for a project milestone' do
+ it 'does not show the project name' do
+ create(:merge_request, source_project: project, milestone: milestone)
+
+ visit(project_milestone_path(project, milestone))
+
+ within('.js-milestone-tabs') do
+ click_link('Merge Requests')
+ end
+
+ wait_for_requests
+
+ expect(page.find('#tab-merge-requests')).not_to have_text(project.name)
+ end
+ end
+
+ context 'for a group milestone' do
+ let(:group_milestone) { create(:milestone, group: group) }
+
+ it 'shows the project name' do
+ create(:merge_request, source_project: project, milestone: group_milestone)
+
+ visit(group_milestone_path(group, group_milestone))
+
+ within('.js-milestone-tabs') do
+ click_link('Merge Requests')
+ end
+
+ expect(page.find('#tab-merge-requests')).to have_text(project.name)
+ end
+ end
+ end
+
private
def visit_milestone
diff --git a/spec/features/milestones/user_views_milestones_spec.rb b/spec/features/milestones/user_views_milestones_spec.rb
index 3f606577121..f8b4b802a60 100644
--- a/spec/features/milestones/user_views_milestones_spec.rb
+++ b/spec/features/milestones/user_views_milestones_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe "User views milestones" do
.and have_content("Merge Requests")
end
- context "with issues" do
+ context "with issues", :js do
let_it_be(:issue) { create(:issue, project: project, milestone: milestone) }
let_it_be(:closed_issue) { create(:closed_issue, project: project, milestone: milestone) }
@@ -33,7 +33,6 @@ RSpec.describe "User views milestones" do
.and have_selector("#tab-issues li.issuable-row", count: 2)
.and have_content(issue.title)
.and have_content(closed_issue.title)
- .and have_selector("#tab-merge-requests")
end
end
diff --git a/spec/features/operations_sidebar_link_spec.rb b/spec/features/operations_sidebar_link_spec.rb
new file mode 100644
index 00000000000..32e2833dafb
--- /dev/null
+++ b/spec/features/operations_sidebar_link_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Operations dropdown sidebar' do
+ let_it_be(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_role(user, role)
+ sign_in(user)
+ visit project_issues_path(project)
+ end
+
+ context 'user has guest role' do
+ let(:role) { :guest }
+
+ it 'has the correct `Operations` menu items' do
+ expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
+
+ expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
+ expect(page).not_to have_link(title: 'Environments', href: project_environments_path(project))
+ expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
+ expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
+ expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
+ end
+ end
+
+ context 'user has reporter role' do
+ let(:role) { :reporter }
+
+ it 'has the correct `Operations` menu items' do
+ expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
+ expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
+ expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
+
+ expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
+ expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
+ expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
+ end
+ end
+
+ context 'user has developer role' do
+ let(:role) { :developer }
+
+ it 'has the correct `Operations` menu items' do
+ expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
+ expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
+ expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
+ expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
+ expect(page).to have_link(title: 'Logs', href: project_logs_path(project))
+
+ expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
+ end
+ end
+
+ context 'user has maintainer role' do
+ let(:role) { :maintainer }
+
+ it 'has the correct `Operations` menu items' do
+ expect(page).to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
+ expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
+ expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
+ expect(page).to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
+ expect(page).to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
+ expect(page).to have_link(title: 'Logs', href: project_logs_path(project))
+ expect(page).to have_link(title: 'Kubernetes', href: project_clusters_path(project))
+ end
+ end
+end
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index b5e784a749f..23bbe9c1587 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -71,21 +71,35 @@ RSpec.describe 'Profile > SSH Keys' do
expect(page).to have_content(key.title)
end
- it 'User removes a key via the key index' do
- create(:key, user: user)
- visit profile_keys_path
+ describe 'User removes a key', :js do
+ shared_examples 'removes key' do
+ it 'removes key' do
+ visit path
+ click_button('Delete')
- click_link('Remove')
+ page.within('.modal') do
+ page.click_button('Delete')
+ end
- expect(page).to have_content('Your SSH keys (0)')
- end
+ expect(page).to have_content('Your SSH keys (0)')
+ end
+ end
- it 'User removes a key via its details page' do
- key = create(:key, user: user)
- visit profile_key_path(key)
+ context 'via the key index' do
+ before do
+ create(:key, user: user)
+ end
+
+ let(:path) { profile_keys_path }
- click_link('Remove')
+ it_behaves_like 'removes key'
+ end
- expect(page).to have_content('Your SSH keys (0)')
+ context 'via its details page' do
+ let(:key) { create(:key, user: user) }
+ let(:path) { profile_keys_path(key) }
+
+ it_behaves_like 'removes key'
+ end
end
end
diff --git a/spec/features/projects/activity/user_sees_design_comment_spec.rb b/spec/features/projects/activity/user_sees_design_comment_spec.rb
index e60deba65f0..3a8e2790858 100644
--- a/spec/features/projects/activity/user_sees_design_comment_spec.rb
+++ b/spec/features/projects/activity/user_sees_design_comment_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Projects > Activity > User sees design comment', :js do
let_it_be(:design) { create(:design, issue: issue) }
let(:design_activity) do
- "#{commenter.name} #{commenter.to_reference} commented on design"
+ "#{commenter.name} #{commenter.to_reference} commented on design #{design.to_reference}"
end
let(:issue_activity) do
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 3382bdcd65f..d1e635f11c0 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -17,10 +17,10 @@ RSpec.describe 'list of badges' do
expect(page).to have_content 'Markdown'
expect(page).to have_content 'HTML'
expect(page).to have_content 'AsciiDoc'
- expect(page).to have_css('.highlight', count: 3)
+ expect(page).to have_css('.js-syntax-highlight', count: 3)
expect(page).to have_xpath("//img[@alt='pipeline status']")
- page.within('.highlight', match: :first) do
+ page.within('.js-syntax-highlight', match: :first) do
expect(page).to have_content 'badges/master/pipeline.svg'
end
end
@@ -32,10 +32,10 @@ RSpec.describe 'list of badges' do
expect(page).to have_content 'Markdown'
expect(page).to have_content 'HTML'
expect(page).to have_content 'AsciiDoc'
- expect(page).to have_css('.highlight', count: 3)
+ expect(page).to have_css('.js-syntax-highlight', count: 3)
expect(page).to have_xpath("//img[@alt='coverage report']")
- page.within('.highlight', match: :first) do
+ page.within('.js-syntax-highlight', match: :first) do
expect(page).to have_content 'badges/master/coverage.svg'
end
end
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 5aca994f53e..c30c8dda852 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Editing file blob', :js do
include TreeHelper
+ include BlobSpecHelpers
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
@@ -20,9 +21,18 @@ RSpec.describe 'Editing file blob', :js do
sign_in(user)
end
- def edit_and_commit(commit_changes: true)
+ def edit_and_commit(commit_changes: true, is_diff: false)
+ set_default_button('edit')
+ refresh
wait_for_requests
- find('.js-edit-blob').click
+
+ if is_diff
+ first('.js-diff-more-actions').click
+ click_link('Edit in single-file editor')
+ else
+ click_link('Edit')
+ end
+
fill_editor(content: 'class NextFeature\\nend\\n')
if commit_changes
@@ -38,7 +48,7 @@ RSpec.describe 'Editing file blob', :js do
context 'from MR diff' do
before do
visit diffs_project_merge_request_path(project, merge_request)
- edit_and_commit
+ edit_and_commit(is_diff: true)
end
it 'returns me to the mr' do
diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
index a271a4f43a8..fda2992af8d 100644
--- a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
+++ b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-RSpec.describe 'User creates blob in new project', :js do
+RSpec.describe 'User creates new blob', :js do
+ include WebIdeSpecHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project, :empty_repo) }
@@ -12,16 +14,19 @@ RSpec.describe 'User creates blob in new project', :js do
visit project_path(project)
end
- it 'allows the user to add a new file' do
+ it 'allows the user to add a new file in Web IDE' do
click_link 'New file'
- execute_script("monaco.editor.getModels()[0].setValue('Hello world')")
+ wait_for_requests
+
+ ide_create_new_file('dummy-file', content: "Hello world\n")
- fill_in(:file_name, with: 'dummy-file')
+ ide_commit
- click_button('Commit changes')
+ click_button('Commit')
- expect(page).to have_content('The file has been successfully created')
+ expect(page).to have_content('All changes are committed')
+ expect(project.repository.blob_at('master', 'dummy-file').data).to eql("Hello world\n")
end
end
diff --git a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
index 8b43687c71c..023e00a3e02 100644
--- a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
+++ b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled
describe 'viewing the new blob page' do
before do
- stub_feature_flags(suggest_pipeline: true)
+ stub_experiment_for_user(suggest_pipeline: true)
sign_in(user)
end
diff --git a/spec/features/projects/branches/user_deletes_branch_spec.rb b/spec/features/projects/branches/user_deletes_branch_spec.rb
index 21a1d31bad4..c480c41709c 100644
--- a/spec/features/projects/branches/user_deletes_branch_spec.rb
+++ b/spec/features/projects/branches/user_deletes_branch_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "User deletes branch", :js do
fill_in("branch-search", with: "improve/awesome").native.send_keys(:enter)
page.within(".js-branch-improve\\/awesome") do
- accept_alert { find(".btn-remove").click }
+ accept_alert { find(".btn-danger").click }
end
wait_for_requests
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 0e2444c5434..dcad7ee66a3 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -21,11 +21,11 @@ RSpec.describe 'Branches' do
before do
# Add 4 stale branches
(1..4).reverse_each do |i|
- Timecop.freeze((threshold + i).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
+ travel_to((threshold + i).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
end
# Add 6 active branches
(1..6).each do |i|
- Timecop.freeze((threshold - i).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
+ travel_to((threshold - i).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
end
end
@@ -101,7 +101,7 @@ RSpec.describe 'Branches' do
visit project_branches_filtered_path(project, state: 'all')
expect(all('.all-branches').last).to have_selector('li', count: 20)
- accept_confirm { first('.js-branch-item .btn-remove').click }
+ accept_confirm { first('.js-branch-item .btn-danger').click }
expect(all('.all-branches').last).to have_selector('li', count: 19)
end
@@ -163,7 +163,7 @@ RSpec.describe 'Branches' do
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
- accept_confirm { find('.js-branch-fix .btn-remove').click }
+ accept_confirm { find('.js-branch-fix .btn-danger').click }
expect(page).not_to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 0)
diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb
index ce435151b84..eb2efb4357d 100644
--- a/spec/features/projects/ci/lint_spec.rb
+++ b/spec/features/projects/ci/lint_spec.rb
@@ -8,117 +8,88 @@ RSpec.describe 'CI Lint', :js do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
- 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
+ let(:content_selector) { '.content .view-lines' }
- context 'YAML is correct' do
- let(:yaml_content) do
- File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
- end
+ before do
+ stub_feature_flags(ci_lint_vue: false)
+ project.add_developer(user)
+ sign_in(user)
- it 'parses Yaml and displays the jobs' do
- expect(page).to have_content('Status: syntax is correct')
+ visit project_ci_lint_path(project)
+ editor_set_value(yaml_content)
- 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
+ wait_for('YAML content') do
+ find(content_selector).text.present?
+ end
+ end
- context 'YAML is incorrect' do
- let(:yaml_content) { 'value: cannot have :' }
+ describe 'YAML parsing' do
+ shared_examples 'validates the YAML' do
+ before do
+ stub_feature_flags(ci_lint_vue: false)
+ click_on 'Validate'
+ 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
+ context 'YAML is correct' do
+ let(:yaml_content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
end
- end
- it_behaves_like 'validates the YAML'
+ it 'parses Yaml and displays the jobs' do
+ expect(page).to have_content('Status: syntax is correct')
- context 'when Dry Run is checked' do
- before do
- check 'Simulate a pipeline created for the default branch'
+ 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
-
- it_behaves_like 'validates the YAML'
end
- describe 'YAML revalidate' do
- let(:yaml_content) { 'my yaml content' }
+ context 'YAML is incorrect' do
+ let(:yaml_content) { 'value: cannot have :' }
- it 'loads previous YAML content after validation' do
- expect(page).to have_field('content', with: 'my yaml content', visible: false, type: 'textarea')
+ 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
- describe 'YAML clearing' do
+ it_behaves_like 'validates the YAML'
+
+ context 'when Dry Run is checked' do
before do
- click_on 'Clear'
+ check 'Simulate a pipeline created for the default branch'
end
- 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
+ it_behaves_like 'validates the YAML'
end
- end
- context 'with ACE editor' do
- it_behaves_like 'correct ci linting process' do
- let(:content_selector) { '.ace_content' }
+ describe 'YAML revalidate' do
+ let(:yaml_content) { 'my yaml content' }
- 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
+ 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
- context 'with Editor Lite' do
- it_behaves_like 'correct ci linting process' do
- let(:content_selector) { '.content .view-lines' }
-
- before do
- stub_feature_flags(monaco_ci: true)
- stub_feature_flags(ci_lint_vue: false)
- project.add_developer(user)
- sign_in(user)
+ describe 'YAML clearing' do
+ before do
+ click_on 'Clear'
+ end
- visit project_ci_lint_path(project)
- editor_set_value(yaml_content)
+ context 'YAML is present' do
+ let(:yaml_content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
- wait_for('YAML content') do
- find(content_selector).text.present?
- end
+ it 'YAML content is cleared' do
+ expect(page).to have_field('content', with: '', visible: false, type: 'textarea')
end
end
end
diff --git a/spec/features/projects/clusters/eks_spec.rb b/spec/features/projects/clusters/eks_spec.rb
index c5feef6c6f3..9f3f331cfab 100644
--- a/spec/features/projects/clusters/eks_spec.rb
+++ b/spec/features/projects/clusters/eks_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'AWS EKS Cluster', :js do
before do
visit project_clusters_path(project)
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
end
context 'when user creates a cluster on AWS EKS' do
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 04339d20d77..a0519d88532 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
before do
visit project_clusters_path(project)
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
click_link 'Create new cluster'
click_link 'Google GKE'
end
@@ -143,7 +143,7 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
before do
visit project_clusters_path(project)
- click_link 'Add Kubernetes cluster'
+ click_link 'Connect cluster with certificate'
click_link 'Connect existing cluster'
end
@@ -162,7 +162,7 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
it 'user sees creation form with the successful message' do
expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
- expect(page).to have_link('Add Kubernetes cluster')
+ expect(page).to have_link('Integrate with a cluster certificate')
end
end
end
@@ -178,7 +178,7 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
end
it 'user sees offer on cluster create page' do
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
expect(page).to have_css('.gcp-signup-offer')
end
@@ -192,10 +192,10 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
it 'user does not see offer after dismissing' do
expect(page).to have_css('.gcp-signup-offer')
- find('.gcp-signup-offer .close').click
+ find('.gcp-signup-offer .js-close').click
wait_for_requests
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
expect(page).not_to have_css('.gcp-signup-offer')
end
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 9d0dc65093e..748eba558aa 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe 'User Cluster', :js do
before do
visit project_clusters_path(project)
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
click_link 'Connect existing cluster'
end
@@ -52,6 +52,10 @@ RSpec.describe 'User Cluster', :js do
it 'user sees RBAC is enabled by default' do
expect(page).to have_checked_field('RBAC-enabled cluster')
end
+
+ it 'user sees namespace per environment is enabled by default' do
+ expect(page).to have_checked_field('Namespace per environment')
+ end
end
context 'when user filled form with invalid parameters' do
@@ -112,7 +116,7 @@ RSpec.describe 'User Cluster', :js do
it 'user sees creation form with the successful message' do
expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
- expect(page).to have_link('Add Kubernetes cluster')
+ expect(page).to have_link('Integrate with a cluster certificate')
end
end
end
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index d674fbc457e..6c6e65005f6 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -11,7 +11,6 @@ RSpec.describe 'Clusters', :js do
before do
project.add_maintainer(user)
gitlab_sign_in(user)
- stub_feature_flags(clusters_list_redesign: false)
end
context 'when user does not have a cluster and visits cluster index page' do
@@ -20,7 +19,7 @@ RSpec.describe 'Clusters', :js do
end
it 'sees empty state' do
- expect(page).to have_link('Add Kubernetes cluster')
+ expect(page).to have_link('Integrate with a cluster certificate')
expect(page).to have_selector('.empty-state')
end
end
@@ -42,7 +41,7 @@ RSpec.describe 'Clusters', :js do
context 'when user filled form with environment scope' do
before do
- click_link 'Add Kubernetes cluster'
+ click_link 'Connect cluster with certificate'
click_link 'Connect existing cluster'
fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: 'staging/*'
@@ -71,7 +70,7 @@ RSpec.describe 'Clusters', :js do
context 'when user updates duplicated environment scope' do
before do
- click_link 'Add Kubernetes cluster'
+ click_link 'Connect cluster with certificate'
click_link 'Connect existing cluster'
fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: '*'
@@ -117,7 +116,7 @@ RSpec.describe 'Clusters', :js do
context 'when user filled form with environment scope' do
before do
- click_link 'Add Kubernetes cluster'
+ click_link 'Connect cluster with certificate'
click_link 'Create new cluster'
click_link 'Google GKE'
@@ -162,7 +161,7 @@ RSpec.describe 'Clusters', :js do
context 'when user updates duplicated environment scope' do
before do
- click_link 'Add Kubernetes cluster'
+ click_link 'Connect cluster with certificate'
click_link 'Create new cluster'
click_link 'Google GKE'
@@ -196,8 +195,7 @@ RSpec.describe 'Clusters', :js do
end
it 'user sees a table with one cluster' do
- # One is the header row, the other the cluster row
- expect(page).to have_selector('.gl-responsive-table-row', count: 2)
+ expect(page).to have_selector('[data-testid="cluster_list_table"] tbody tr', count: 1)
end
context 'when user clicks on a cluster' do
@@ -216,7 +214,7 @@ RSpec.describe 'Clusters', :js do
before do
visit project_clusters_path(project)
- click_link 'Add Kubernetes cluster'
+ click_link 'Integrate with a cluster certificate'
click_link 'Create new cluster'
end
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index f97abc5bd8b..00ec9d49a10 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'project commit pipelines', :js do
context 'when no builds triggered yet' do
it 'shows the ID of the first pipeline' do
- page.within('.table-holder') do
+ page.within('.pipelines .ci-table') do
expect(page).to have_content project.ci_pipelines[0].id # pipeline ids
end
end
diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb
index 87a022d74a3..0fa4975bb25 100644
--- a/spec/features/projects/commit/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb
@@ -6,19 +6,22 @@ RSpec.describe "User comments on commit", :js do
include Spec::Support::Helpers::Features::NotesHelpers
include RepoHelpers
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
let(:comment_text) { "XML attached" }
- before do
- sign_in(user)
+ before_all do
project.add_developer(user)
+ end
- visit(project_commit_path(project, sample_commit.id))
+ before do
+ sign_in(user)
end
context "when adding new comment" do
it "adds comment" do
+ visit(project_commit_path(project, sample_commit.id))
+
emoji_code = ":+1:"
page.within(".js-main-target-form") do
@@ -57,6 +60,8 @@ RSpec.describe "User comments on commit", :js do
context "when editing comment" do
before do
+ visit(project_commit_path(project, sample_commit.id))
+
add_note(comment_text)
end
@@ -87,6 +92,8 @@ RSpec.describe "User comments on commit", :js do
context "when deleting comment" do
before do
+ visit(project_commit_path(project, sample_commit.id))
+
add_note(comment_text)
end
@@ -108,4 +115,35 @@ RSpec.describe "User comments on commit", :js do
expect(page).not_to have_css(".note")
end
end
+
+ context 'when checking task lists' do
+ let(:note_with_task) do
+ <<-EOT.strip_heredoc
+
+ - [ ] Task 1
+ EOT
+ end
+
+ before do
+ create(:note_on_commit, project: project, commit_id: sample_commit.id, note: note_with_task, author: user)
+ create(:note_on_commit, project: project, commit_id: sample_commit.id, note: note_with_task, author: user)
+
+ visit(project_commit_path(project, sample_commit.id))
+ end
+
+ it 'allows the tasks to be checked' do
+ expect(page).to have_selector('li.task-list-item', count: 2)
+ expect(page).to have_selector('li.task-list-item input[checked]', count: 0)
+
+ all('.task-list-item-checkbox').each do |checkbox|
+ checkbox.click
+ end
+ wait_for_requests
+
+ visit(project_commit_path(project, sample_commit.id))
+
+ expect(page).to have_selector('li.task-list-item', count: 2)
+ expect(page).to have_selector('li.task-list-item input[checked]', count: 2)
+ end
+ end
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 865ae3ad8cb..e387ea4d473 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -113,7 +113,7 @@ RSpec.describe "Compare", :js do
click_button('Compare')
- page.within('.alert') do
+ page.within('.gl-alert') do
expect(page).to have_text("Too many changes to show. To preserve performance only 3 of 3+ files are displayed.")
end
end
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index fa10e429af2..1d7be7fa7a3 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -333,7 +333,7 @@ RSpec.describe 'Environment' do
visit project_branches_filtered_path(project, state: 'all', search: 'feature')
remove_branch_with_hooks(project, user, 'feature') do
- page.within('.js-branch-feature') { find('a.btn-remove').click }
+ page.within('.js-branch-feature') { find('a.btn-danger').click }
end
visit_environment(environment)
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 7f2ef61bcbe..8c032660726 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -372,7 +372,7 @@ RSpec.describe 'Environments page', :js do
let(:role) { :developer }
it 'developer creates a new environment with a valid name' do
- within(".top-area") { click_link 'New environment' }
+ within(".environments-section") { click_link 'New environment' }
fill_in('Name', with: 'production')
click_on 'Save'
@@ -380,7 +380,7 @@ RSpec.describe 'Environments page', :js do
end
it 'developer creates a new environmetn with invalid name' do
- within(".top-area") { click_link 'New environment' }
+ within(".environments-section") { click_link 'New environment' }
fill_in('Name', with: 'name,with,commas')
click_on 'Save'
diff --git a/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb b/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb
new file mode 100644
index 00000000000..2a81c706525
--- /dev/null
+++ b/spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User deletes feature flag user list', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+
+ before do
+ project.add_developer(developer)
+ sign_in(developer)
+ end
+
+ context 'with a list' do
+ before do
+ create(:operations_feature_flag_user_list, project: project, name: 'My List')
+ end
+
+ it 'deletes the list' do
+ visit(project_feature_flags_path(project, scope: 'userLists'))
+
+ delete_user_list_button.click
+ delete_user_list_modal_confirmation_button.click
+
+ expect(page).to have_text('Lists 0')
+ end
+ end
+
+ context 'with a list that is in use' do
+ before do
+ list = create(:operations_feature_flag_user_list, project: project, name: 'My List')
+ feature_flag = create(:operations_feature_flag, :new_version_flag, project: project)
+ create(:operations_strategy, feature_flag: feature_flag, name: 'gitlabUserList', user_list: list)
+ end
+
+ it 'does not delete the list' do
+ visit(project_feature_flags_path(project, scope: 'userLists'))
+
+ delete_user_list_button.click
+ delete_user_list_modal_confirmation_button.click
+
+ expect(page).to have_text('User list is associated with a strategy')
+ expect(page).to have_text('Lists 1')
+ expect(page).to have_text('My List')
+
+ alert_dismiss_button.click
+
+ expect(page).not_to have_text('User list is associated with a strategy')
+ end
+ end
+
+ def delete_user_list_button
+ find("button[data-testid='delete-user-list']")
+ end
+
+ def delete_user_list_modal_confirmation_button
+ find("button[data-testid='modal-confirm']")
+ end
+
+ def alert_dismiss_button
+ find("div[data-testid='serverErrors'] button")
+ end
+end
diff --git a/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb b/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb
new file mode 100644
index 00000000000..b37c2780827
--- /dev/null
+++ b/spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User edits feature flag user list', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+
+ before do
+ project.add_developer(developer)
+ sign_in(developer)
+ end
+
+ it 'prefills the edit form with the list name' do
+ list = create(:operations_feature_flag_user_list, project: project, name: 'My List Name')
+
+ visit(edit_project_feature_flags_user_list_path(project, list))
+
+ expect(page).to have_field 'Name', with: 'My List Name'
+ end
+end
diff --git a/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb b/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb
new file mode 100644
index 00000000000..dfebe6408bd
--- /dev/null
+++ b/spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User sees feature flag user list details', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+
+ before do
+ project.add_developer(developer)
+ sign_in(developer)
+ end
+
+ it 'displays the list name' do
+ list = create(:operations_feature_flag_user_list, project: project, name: 'My List')
+
+ visit(project_feature_flags_user_list_path(project, list))
+
+ expect(page).to have_text('My List')
+ end
+end
diff --git a/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb
new file mode 100644
index 00000000000..830dda737b0
--- /dev/null
+++ b/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User creates feature flag', :js do
+ include FeatureFlagHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+
+ before do
+ project.add_developer(user)
+ stub_feature_flags(feature_flag_permissions: false)
+ sign_in(user)
+ end
+
+ it 'user creates a flag enabled for user ids' do
+ visit(new_project_feature_flag_path(project))
+ set_feature_flag_info('test_feature', 'Test feature')
+ within_strategy_row(1) do
+ select 'User IDs', from: 'Type'
+ fill_in 'User IDs', with: 'user1, user2'
+ environment_plus_button.click
+ environment_search_input.set('production')
+ environment_search_results.first.click
+ end
+ click_button 'Create feature flag'
+
+ expect_user_to_see_feature_flags_index_page
+ expect(page).to have_text('test_feature')
+ end
+
+ it 'user creates a flag with default environment scopes' do
+ visit(new_project_feature_flag_path(project))
+ set_feature_flag_info('test_flag', 'Test flag')
+ within_strategy_row(1) do
+ select 'All users', from: 'Type'
+ end
+ click_button 'Create feature flag'
+
+ expect_user_to_see_feature_flags_index_page
+ expect(page).to have_text('test_flag')
+
+ edit_feature_flag_button.click
+
+ within_strategy_row(1) do
+ expect(page).to have_text('All users')
+ expect(page).to have_text('All environments')
+ end
+ end
+
+ it 'removes the correct strategy when a strategy is deleted' do
+ visit(new_project_feature_flag_path(project))
+ click_button 'Add strategy'
+ within_strategy_row(1) do
+ select 'All users', from: 'Type'
+ end
+ within_strategy_row(2) do
+ select 'Percent of users', from: 'Type'
+ end
+ within_strategy_row(1) do
+ delete_strategy_button.click
+ end
+
+ within_strategy_row(1) do
+ expect(page).to have_select('Type', selected: 'Percent of users')
+ end
+ end
+
+ context 'with new version flags disabled' do
+ before do
+ stub_feature_flags(feature_flags_new_version: false)
+ end
+
+ context 'when creates without changing scopes' do
+ before do
+ visit(new_project_feature_flag_path(project))
+ set_feature_flag_info('ci_live_trace', 'For live trace')
+ click_button 'Create feature flag'
+ expect(page).to have_current_path(project_feature_flags_path(project))
+ end
+
+ it 'shows the created feature flag' do
+ within_feature_flag_row(1) do
+ expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
+ expect_status_toggle_button_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
+ end
+ end
+ end
+ end
+
+ context 'when creates with disabling the default scope' do
+ before do
+ visit(new_project_feature_flag_path(project))
+ set_feature_flag_info('ci_live_trace', 'For live trace')
+
+ within_scope_row(1) do
+ within_status { find('.project-feature-toggle').click }
+ end
+
+ click_button 'Create feature flag'
+ end
+
+ it 'shows the created feature flag' do
+ within_feature_flag_row(1) do
+ expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
+ expect_status_toggle_button_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(1)')).to have_content('*')
+ end
+ end
+ end
+ end
+
+ context 'when creates with an additional scope' do
+ before do
+ visit(new_project_feature_flag_path(project))
+ set_feature_flag_info('mr_train', '')
+
+ within_scope_row(2) do
+ within_environment_spec do
+ find('.js-env-search > input').set("review/*")
+ find('.js-create-button').click
+ end
+ end
+
+ within_scope_row(2) do
+ within_status { find('.project-feature-toggle').click }
+ end
+
+ click_button 'Create feature flag'
+ end
+
+ it 'shows the created feature flag' do
+ within_feature_flag_row(1) do
+ expect(page.find('.feature-flag-name')).to have_content('mr_train')
+ expect_status_toggle_button_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
+ expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(2)')).to have_content('review/*')
+ end
+ end
+ end
+ end
+
+ context 'when searches an environment name for scope creation' do
+ let!(:environment) { create(:environment, name: 'production', project: project) }
+
+ before do
+ visit(new_project_feature_flag_path(project))
+ set_feature_flag_info('mr_train', '')
+
+ within_scope_row(2) do
+ within_environment_spec do
+ find('.js-env-search > input').set('prod')
+ click_button 'production'
+ end
+ end
+
+ click_button 'Create feature flag'
+ end
+
+ it 'shows the created feature flag' do
+ within_feature_flag_row(1) do
+ expect(page.find('.feature-flag-name')).to have_content('mr_train')
+ expect_status_toggle_button_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
+ expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(2)')).to have_content('production')
+ end
+ end
+ end
+ end
+ end
+
+ private
+
+ def set_feature_flag_info(name, description)
+ fill_in 'Name', with: name
+ fill_in 'Description', with: description
+ end
+
+ def environment_plus_button
+ find('.js-new-environments-dropdown')
+ end
+
+ def environment_search_input
+ find('.js-new-environments-dropdown input')
+ end
+
+ def environment_search_results
+ all('.js-new-environments-dropdown button.dropdown-item')
+ end
+end
diff --git a/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb
new file mode 100644
index 00000000000..581709aacee
--- /dev/null
+++ b/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User deletes feature flag', :js do
+ include FeatureFlagHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+
+ let!(:feature_flag) do
+ create_flag(project, 'ci_live_trace', false,
+ description: 'For live trace feature')
+ end
+
+ before do
+ project.add_developer(user)
+ stub_feature_flags(feature_flag_permissions: false)
+ sign_in(user)
+
+ visit(project_feature_flags_path(project))
+
+ find('.js-feature-flag-delete-button').click
+ click_button('Delete feature flag')
+ expect(page).to have_current_path(project_feature_flags_path(project))
+ end
+
+ it 'user does not see feature flag' do
+ expect(page).to have_no_content('ci_live_trace')
+ end
+end
diff --git a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
new file mode 100644
index 00000000000..750f4dc5ef4
--- /dev/null
+++ b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User sees feature flag list', :js do
+ include FeatureFlagHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, namespace: user.namespace) }
+
+ before_all do
+ project.add_developer(user)
+ end
+
+ before do
+ sign_in(user)
+ end
+
+ context 'with legacy feature flags' do
+ before do
+ create_flag(project, 'ci_live_trace', false).tap do |feature_flag|
+ create_scope(feature_flag, 'review/*', true)
+ end
+ create_flag(project, 'drop_legacy_artifacts', false)
+ create_flag(project, 'mr_train', true).tap do |feature_flag|
+ create_scope(feature_flag, 'production', false)
+ end
+ stub_feature_flags(feature_flags_legacy_read_only_override: false)
+ end
+
+ it 'user sees the first flag' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(1) do
+ expect(page.find('.js-feature-flag-id')).to have_content('^1')
+ expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
+ expect_status_toggle_button_not_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(1)')).to have_content('*')
+ expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(2)')).to have_content('review/*')
+ end
+ end
+ end
+
+ it 'user sees the second flag' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(2) do
+ expect(page.find('.js-feature-flag-id')).to have_content('^2')
+ expect(page.find('.feature-flag-name')).to have_content('drop_legacy_artifacts')
+ expect_status_toggle_button_not_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(1)')).to have_content('*')
+ end
+ end
+ end
+
+ it 'user sees the third flag' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(3) do
+ expect(page.find('.js-feature-flag-id')).to have_content('^3')
+ expect(page.find('.feature-flag-name')).to have_content('mr_train')
+ expect_status_toggle_button_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
+ expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(2)')).to have_content('production')
+ end
+ end
+ end
+
+ it 'user sees the status toggle disabled' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(1) do
+ expect_status_toggle_button_to_be_disabled
+ end
+ end
+
+ context 'when legacy feature flags are not read-only' do
+ before do
+ stub_feature_flags(feature_flags_legacy_read_only: false)
+ end
+
+ it 'user updates the status toggle' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(1) do
+ status_toggle_button.click
+
+ expect_status_toggle_button_to_be_checked
+ end
+ end
+ end
+
+ context 'when legacy feature flags are read-only but the override is active for a project' do
+ before do
+ stub_feature_flags(
+ feature_flags_legacy_read_only: true,
+ feature_flags_legacy_read_only_override: project
+ )
+ end
+
+ it 'user updates the status toggle' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(1) do
+ status_toggle_button.click
+
+ expect_status_toggle_button_to_be_checked
+ end
+ end
+ end
+ end
+
+ context 'with new version flags' do
+ before do
+ create(:operations_feature_flag, :new_version_flag, project: project,
+ name: 'my_flag', active: false)
+ end
+
+ it 'user updates the status toggle' do
+ visit(project_feature_flags_path(project))
+
+ within_feature_flag_row(1) do
+ status_toggle_button.click
+
+ expect_status_toggle_button_to_be_checked
+ end
+ end
+ end
+
+ context 'when there are no feature flags' do
+ before do
+ visit(project_feature_flags_path(project))
+ end
+
+ it 'shows empty page' do
+ expect(page).to have_text 'Get started with feature flags'
+ expect(page).to have_selector('.btn-success', text: 'New feature flag')
+ expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure')
+ end
+ end
+end
diff --git a/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb
new file mode 100644
index 00000000000..bc2d63e1953
--- /dev/null
+++ b/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User updates feature flag', :js do
+ include FeatureFlagHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, namespace: user.namespace) }
+
+ before_all do
+ project.add_developer(user)
+ end
+
+ before do
+ stub_feature_flags(
+ feature_flag_permissions: false,
+ feature_flags_legacy_read_only_override: false
+ )
+ sign_in(user)
+ end
+
+ context 'with a new version feature flag' do
+ let!(:feature_flag) do
+ create_flag(project, 'test_flag', false, version: Operations::FeatureFlag.versions['new_version_flag'],
+ description: 'For testing')
+ end
+
+ let!(:strategy) do
+ create(:operations_strategy, feature_flag: feature_flag,
+ name: 'default', parameters: {})
+ end
+
+ let!(:scope) do
+ create(:operations_scope, strategy: strategy, environment_scope: '*')
+ end
+
+ it 'user adds a second strategy' do
+ visit(edit_project_feature_flag_path(project, feature_flag))
+
+ wait_for_requests
+
+ click_button 'Add strategy'
+ within_strategy_row(2) do
+ select 'Percent of users', from: 'Type'
+ fill_in 'Percentage', with: '15'
+ end
+ click_button 'Save changes'
+
+ edit_feature_flag_button.click
+
+ within_strategy_row(1) do
+ expect(page).to have_text 'All users'
+ expect(page).to have_text 'All environments'
+ end
+ within_strategy_row(2) do
+ expect(page).to have_text 'Percent of users'
+ expect(page).to have_field 'Percentage', with: '15'
+ expect(page).to have_text 'All environments'
+ end
+ end
+
+ it 'user toggles the flag on' do
+ visit(edit_project_feature_flag_path(project, feature_flag))
+ status_toggle_button.click
+ click_button 'Save changes'
+
+ within_feature_flag_row(1) do
+ expect_status_toggle_button_to_be_checked
+ end
+ end
+ end
+
+ context 'with a legacy feature flag' do
+ let!(:feature_flag) do
+ create_flag(project, 'ci_live_trace', true,
+ description: 'For live trace feature')
+ end
+
+ let!(:scope) { create_scope(feature_flag, 'review/*', true) }
+
+ context 'when legacy flags are editable' do
+ before do
+ stub_feature_flags(feature_flags_legacy_read_only: false)
+
+ visit(edit_project_feature_flag_path(project, feature_flag))
+ end
+
+ it 'user sees persisted default scope' do
+ within_scope_row(1) do
+ within_environment_spec do
+ expect(page).to have_content('* (All Environments)')
+ end
+
+ within_status do
+ expect(find('.project-feature-toggle')['aria-label'])
+ .to eq('Toggle Status: ON')
+ end
+ end
+ end
+
+ context 'when user updates the status of a scope' do
+ before do
+ within_scope_row(2) do
+ within_status { find('.project-feature-toggle').click }
+ end
+
+ click_button 'Save changes'
+ expect(page).to have_current_path(project_feature_flags_path(project))
+ end
+
+ it 'shows the updated feature flag' do
+ within_feature_flag_row(1) do
+ expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
+ expect_status_toggle_button_to_be_checked
+
+ within_feature_flag_scopes do
+ expect(page.find('.badge:nth-child(1)')).to have_content('*')
+ expect(page.find('.badge:nth-child(1)')['class']).to include('badge-info')
+ expect(page.find('.badge:nth-child(2)')).to have_content('review/*')
+ expect(page.find('.badge:nth-child(2)')['class']).to include('badge-muted')
+ end
+ end
+ end
+ end
+
+ context 'when user adds a new scope' do
+ before do
+ within_scope_row(3) do
+ within_environment_spec do
+ find('.js-env-search > input').set('production')
+ find('.js-create-button').click
+ end
+ end
+
+ click_button 'Save changes'
+ expect(page).to have_current_path(project_feature_flags_path(project))
+ end
+
+ it 'shows the newly created scope' do
+ within_feature_flag_row(1) do
+ within_feature_flag_scopes do
+ expect(page.find('.badge:nth-child(3)')).to have_content('production')
+ expect(page.find('.badge:nth-child(3)')['class']).to include('badge-muted')
+ end
+ end
+ end
+ end
+
+ context 'when user deletes a scope' do
+ before do
+ within_scope_row(2) do
+ within_delete { find('.js-delete-scope').click }
+ end
+
+ click_button 'Save changes'
+ expect(page).to have_current_path(project_feature_flags_path(project))
+ end
+
+ it 'shows the updated feature flag' do
+ within_feature_flag_row(1) do
+ within_feature_flag_scopes do
+ expect(page).to have_css('.badge:nth-child(1)')
+ expect(page).not_to have_css('.badge:nth-child(2)')
+ end
+ end
+ end
+ end
+ end
+
+ context 'when legacy flags are read-only' do
+ it 'the user cannot edit the flag' do
+ visit(edit_project_feature_flag_path(project, feature_flag))
+
+ expect(page).to have_text 'This feature flag is read-only, and it will be removed in 14.0.'
+ expect(page).to have_css('button.js-ff-submit.disabled')
+ end
+ end
+
+ context 'when legacy flags are read-only, but the override is active for one project' do
+ it 'the user can edit the flag' do
+ stub_feature_flags(feature_flags_legacy_read_only_override: project)
+
+ visit(edit_project_feature_flag_path(project, feature_flag))
+ status_toggle_button.click
+ click_button 'Save changes'
+
+ expect(page).to have_current_path(project_feature_flags_path(project))
+ within_feature_flag_row(1) do
+ expect_status_toggle_button_not_to_be_checked
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 8d3ca9d9fd1..467adb25a17 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -201,7 +201,7 @@ RSpec.describe 'Edit Project Settings' do
visit project_path(project)
- expect(page).to have_content "Customize your workflow!"
+ expect(page).to have_content "joined project"
end
it "hides project activity tabs" do
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index eed1e7aaf1b..d28e31c08dc 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js do
+ include WebIdeSpecHelpers
+
let(:project) { create(:project_empty_repo) }
let(:project_maintainer) { project.owner }
@@ -10,36 +12,35 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license
sign_in(project_maintainer)
end
- it 'project maintainer creates a license file from a template' do
+ it 'allows project maintainer creates a license file from a template in Web IDE' do
visit project_path(project)
click_on 'Add LICENSE'
- expect(page).to have_content('New file')
- expect(current_path).to eq(
- project_new_blob_path(project, 'master'))
- expect(find('#file_name').value).to eq('LICENSE')
- expect(page).to have_selector('.license-selector')
+ expect(current_path).to eq("/-/ide/project/#{project.full_path}/edit/master/-/LICENSE")
+
+ expect(page).to have_selector('.qa-file-templates-bar')
select_template('MIT License')
- file_content = first('.file-editor')
- expect(file_content).to have_content('MIT License')
- expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
+ expect(ide_editor_value).to have_content('MIT License')
+ expect(ide_editor_value).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
+
+ ide_commit
+
+ click_button('Commit')
+
+ expect(current_path).to eq("/-/ide/project/#{project.full_path}/tree/master/-/")
- fill_in :commit_message, with: 'Add a LICENSE file', visible: true
- click_button 'Commit changes'
+ expect(page).to have_content('All changes are committed')
- expect(current_path).to eq(
- project_blob_path(project, 'master/LICENSE'))
- expect(page).to have_content('MIT License')
- expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
+ license_file = project.repository.blob_at('master', 'LICENSE').data
+ expect(license_file).to have_content('MIT License')
+ expect(license_file).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
def select_template(template)
- page.within('.js-license-selector-wrap') do
- click_button 'Apply a template'
- click_link template
- wait_for_requests
- end
+ click_button 'Choose a template...'
+ click_button template
+ wait_for_requests
end
end
diff --git a/spec/features/projects/files/user_browses_lfs_files_spec.rb b/spec/features/projects/files/user_browses_lfs_files_spec.rb
index ecc56b794b2..3be5ab64834 100644
--- a/spec/features/projects/files/user_browses_lfs_files_spec.rb
+++ b/spec/features/projects/files/user_browses_lfs_files_spec.rb
@@ -66,10 +66,30 @@ RSpec.describe 'Projects > Files > User browses LFS files' do
expect(page).to have_content('History')
expect(page).to have_content('Permalink')
expect(page).to have_content('Replace')
+ expect(page).to have_link('Download')
+
expect(page).not_to have_content('Annotate')
expect(page).not_to have_content('Blame')
- expect(page).not_to have_content('Edit')
- expect(page).to have_link('Download')
+
+ expect(page).not_to have_selector(:link_or_button, text: /^Edit$/)
+ expect(page).to have_selector(:link_or_button, 'Edit in Web IDE')
+ end
+ end
+
+ context 'when feature flag :consolidated_edit_button is off' do
+ before do
+ stub_feature_flags(consolidated_edit_button: false)
+
+ click_link('files')
+ click_link('lfs')
+ click_link('lfs_object.iso')
+ end
+
+ it 'does not show single file edit link' do
+ page.within('.content') do
+ expect(page).to have_selector(:link_or_button, 'Web IDE')
+ expect(page).not_to have_selector(:link_or_button, 'Edit')
+ end
end
end
end
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index 39bc139656b..fd83547d064 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Files > User creates files', :js do
+ include BlobSpecHelpers
+
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
@@ -103,6 +105,8 @@ RSpec.describe 'Projects > Files > User creates files', :js do
end
it 'creates and commit a new file with new lines at the end of file' do
+ set_default_button('edit')
+
find('#editor')
execute_script('monaco.editor.getModels()[0].setValue("Sample\n\n\n")')
fill_in(:file_name, with: 'not_a_file.md')
@@ -113,7 +117,7 @@ RSpec.describe 'Projects > Files > User creates files', :js do
expect(current_path).to eq(new_file_path)
- find('.js-edit-blob').click
+ click_link('Edit')
find('#editor')
expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq("Sample\n\n\n")
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index d3e075001c8..c18ff9ddbbc 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -4,6 +4,8 @@ require 'spec_helper'
RSpec.describe 'Projects > Files > User edits files', :js do
include ProjectForksHelper
+ include BlobSpecHelpers
+
let(:project) { create(:project, :repository, name: 'Shop') }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) }
@@ -14,6 +16,10 @@ RSpec.describe 'Projects > Files > User edits files', :js do
sign_in(user)
end
+ after do
+ unset_default_button
+ end
+
shared_examples 'unavailable for an archived project' do
it 'does not show the edit link for an archived project', :js do
project.update!(archived: true)
@@ -39,14 +45,15 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'inserts a content of a file' do
+ set_default_button('edit')
click_link('.gitignore')
- find('.js-edit-blob').click
+ click_link_or_button('Edit')
find('.file-editor', match: :first)
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
- expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq('*.rbca')
+ expect(editor_value).to eq('*.rbca')
end
it 'does not show the edit link if a file is binary' do
@@ -60,12 +67,13 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'commits an edited file' do
+ set_default_button('edit')
click_link('.gitignore')
- find('.js-edit-blob').click
+ click_link_or_button('Edit')
find('.file-editor', match: :first)
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -77,13 +85,14 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'commits an edited file to a new branch' do
+ set_default_button('edit')
click_link('.gitignore')
- find('.js-edit-blob').click
+ click_link_or_button('Edit')
find('.file-editor', match: :first)
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
fill_in(:commit_message, with: 'New commit message', visible: true)
fill_in(:branch_name, with: 'new_branch_name', visible: true)
click_button('Commit changes')
@@ -96,12 +105,13 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'shows the diff of an edited file' do
+ set_default_button('edit')
click_link('.gitignore')
- find('.js-edit-blob').click
+ click_link_or_button('Edit')
find('.file-editor', match: :first)
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
click_link('Preview changes')
expect(page).to have_css('.line_holder.new')
@@ -118,8 +128,8 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
def expect_fork_prompt
- expect(page).to have_link('Fork')
- expect(page).to have_button('Cancel')
+ expect(page).to have_selector(:link_or_button, 'Fork')
+ expect(page).to have_selector(:link_or_button, 'Cancel')
expect(page).to have_content(
"You're not allowed to edit files in this project directly. "\
"Please fork this project, make your changes there, and submit a merge request."
@@ -134,30 +144,32 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'inserts a content of a file in a forked project', :sidekiq_might_not_need_inline do
+ set_default_button('edit')
click_link('.gitignore')
- click_button('Edit')
+ click_link_or_button('Edit')
expect_fork_prompt
- click_link('Fork')
+ click_link_or_button('Fork project')
expect_fork_status
find('.file-editor', match: :first)
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
- expect(evaluate_script('monaco.editor.getModels()[0].getValue()')).to eq('*.rbca')
+ expect(editor_value).to eq('*.rbca')
end
it 'opens the Web IDE in a forked project', :sidekiq_might_not_need_inline do
+ set_default_button('webide')
click_link('.gitignore')
- click_button('Web IDE')
+ click_link_or_button('Web IDE')
expect_fork_prompt
- click_link('Fork')
+ click_link_or_button('Fork project')
expect_fork_status
@@ -166,17 +178,17 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'commits an edited file in a forked project', :sidekiq_might_not_need_inline do
+ set_default_button('edit')
click_link('.gitignore')
- find('.js-edit-blob').click
+ click_link_or_button('Edit')
expect_fork_prompt
-
- click_link('Fork')
+ click_link_or_button('Fork project')
find('.file-editor', match: :first)
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Commit changes')
@@ -198,14 +210,14 @@ RSpec.describe 'Projects > Files > User edits files', :js do
end
it 'links to the forked project for editing', :sidekiq_might_not_need_inline do
+ set_default_button('edit')
click_link('.gitignore')
- find('.js-edit-blob').click
+ click_link_or_button('Edit')
- expect(page).not_to have_link('Fork')
- expect(page).not_to have_button('Cancel')
+ expect(page).not_to have_link('Fork project')
find('#editor')
- execute_script("monaco.editor.getModels()[0].setValue('*.rbca')")
+ set_editor_value('*.rbca')
fill_in(:commit_message, with: 'Another commit', visible: true)
click_button('Commit changes')
@@ -224,5 +236,116 @@ RSpec.describe 'Projects > Files > User edits files', :js do
let(:project) { project2 }
end
end
+
+ context 'when feature flag :consolidated_edit_button is off' do
+ before do
+ stub_feature_flags(consolidated_edit_button: false)
+ end
+
+ context 'when an user does not have write access', :js do
+ before do
+ project2.add_reporter(user)
+ visit(project2_tree_path_root_ref)
+ wait_for_requests
+ end
+
+ it 'inserts a content of a file in a forked project', :sidekiq_might_not_need_inline do
+ set_default_button('edit')
+ click_link('.gitignore')
+ click_link_or_button('Edit')
+
+ expect_fork_prompt
+
+ click_link_or_button('Fork')
+
+ expect_fork_status
+
+ find('.file-editor', match: :first)
+
+ find('#editor')
+ set_editor_value('*.rbca')
+
+ expect(editor_value).to eq('*.rbca')
+ end
+
+ it 'opens the Web IDE in a forked project', :sidekiq_might_not_need_inline do
+ set_default_button('webide')
+ click_link('.gitignore')
+ click_link_or_button('Web IDE')
+
+ expect_fork_prompt
+
+ click_link_or_button('Fork')
+
+ expect_fork_status
+
+ expect(page).to have_css('.ide-sidebar-project-title', text: "#{project2.name} #{user.namespace.full_path}/#{project2.path}")
+ expect(page).to have_css('.ide .multi-file-tab', text: '.gitignore')
+ end
+
+ it 'commits an edited file in a forked project', :sidekiq_might_not_need_inline do
+ set_default_button('edit')
+ click_link('.gitignore')
+ click_link_or_button('Edit')
+
+ expect_fork_prompt
+
+ click_link_or_button('Fork')
+
+ expect_fork_status
+
+ find('.file-editor', match: :first)
+
+ find('#editor')
+ set_editor_value('*.rbca')
+ fill_in(:commit_message, with: 'New commit message', visible: true)
+ click_button('Commit changes')
+
+ fork = user.fork_of(project2.reload)
+
+ expect(current_path).to eq(project_new_merge_request_path(fork))
+
+ wait_for_requests
+
+ expect(page).to have_content('New commit message')
+ end
+
+ context 'when the user already had a fork of the project', :js do
+ let!(:forked_project) { fork_project(project2, user, namespace: user.namespace, repository: true) }
+
+ before do
+ visit(project2_tree_path_root_ref)
+ wait_for_requests
+ end
+
+ it 'links to the forked project for editing', :sidekiq_might_not_need_inline do
+ set_default_button('edit')
+ click_link('.gitignore')
+ click_link_or_button('Edit')
+
+ expect(page).not_to have_link('Fork')
+
+ find('#editor')
+ set_editor_value('*.rbca')
+ fill_in(:commit_message, with: 'Another commit', visible: true)
+ click_button('Commit changes')
+
+ fork = user.fork_of(project2)
+
+ expect(current_path).to eq(project_new_merge_request_path(fork))
+
+ wait_for_requests
+
+ expect(page).to have_content('Another commit')
+ expect(page).to have_content("From #{forked_project.full_path}")
+ expect(page).to have_content("into #{project2.full_path}")
+ end
+
+ it_behaves_like 'unavailable for an archived project' do
+ let(:project) { project2 }
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb b/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb
index 8d5e99d7e2b..78fb470d4ea 100644
--- a/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb
+++ b/spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb
@@ -90,34 +90,5 @@ RSpec.describe 'viewing issues with design references' do
expect(page).not_to have_link(design_ref_b)
end
end
-
- context 'design management is enabled, but the filter is disabled globally' do
- before do
- enable_design_management
- stub_feature_flags(
- Banzai::Filter::DesignReferenceFilter::FEATURE_FLAG => false
- )
- end
-
- it 'processes design tab links successfully, and design references as issue references', :aggregate_failures do
- visit_page_with_design_references
-
- expect(page).to have_text('The designs I mentioned')
- expect(page).to have_link(design_tab_ref)
- expect(page).to have_link(issue_ref)
- expect(page).not_to have_link(design_ref_a)
- expect(page).not_to have_link(design_ref_b)
- end
- end
-
- context 'design management is enabled, and the filter is enabled for the current project' do
- before do
- stub_feature_flags(
- Banzai::Filter::DesignReferenceFilter::FEATURE_FLAG => public_project
- )
- end
-
- it_behaves_like 'successful use of design link references'
- end
end
end
diff --git a/spec/features/projects/issues/viewing_relocated_issues_spec.rb b/spec/features/projects/issues/viewing_relocated_issues_spec.rb
new file mode 100644
index 00000000000..10d5ad1747c
--- /dev/null
+++ b/spec/features/projects/issues/viewing_relocated_issues_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'issues canonical link' do
+ include Spec::Support::Helpers::Features::CanonicalLinkHelpers
+
+ let_it_be(:original_project) { create(:project, :public) }
+ let_it_be(:original_issue) { create(:issue, project: original_project) }
+ let_it_be(:canonical_issue) { create(:issue) }
+ let_it_be(:canonical_url) { issue_url(canonical_issue, Gitlab::Application.routes.default_url_options) }
+
+ it "doesn't show the canonical URL" do
+ visit(issue_path(original_issue))
+
+ expect(page).not_to have_any_canonical_links
+ end
+
+ context 'when the issue was moved' do
+ it 'shows the canonical URL' do
+ original_issue.moved_to = canonical_issue
+ original_issue.save!
+
+ visit(issue_path(original_issue))
+
+ expect(page).to have_canonical_link(canonical_url)
+ end
+ end
+
+ context 'when the issue was duplicated' do
+ it 'shows the canonical URL' do
+ original_issue.duplicated_to = canonical_issue
+ original_issue.save!
+
+ visit(issue_path(original_issue))
+
+ expect(page).to have_canonical_link(canonical_url)
+ end
+ end
+end
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index b935b99642b..9b199157d79 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe 'User browses a job', :js do
wait_for_all_requests
within('.builds-container') do
expect(page).to have_selector(
- ".build-job > a[data-original-title='test - failed - (unknown failure)']")
+ ".build-job > a[title='test - failed - (unknown failure)']")
end
end
end
@@ -55,7 +55,7 @@ RSpec.describe 'User browses a job', :js do
wait_for_all_requests
within('.builds-container') do
expect(page).to have_selector(
- ".build-job > a[data-original-title='test - failed - (unknown failure) (retried)']")
+ ".build-job > a[title='test - failed - (unknown failure) (retried)']")
end
end
end
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index 2ee6bc103e9..d59f8eb4b1d 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -3,20 +3,23 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Groups with access list', :js do
- let(:user) { create(:user) }
- let(:group) { create(:group, :public) }
- let(:project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :public) }
+
+ let(:additional_link_attrs) { {} }
+ let!(:group_link) { create(:project_group_link, project: project, group: group, **additional_link_attrs) }
before do
- project.add_maintainer(user)
- @group_link = create(:project_group_link, project: project, group: group)
+ travel_to Time.now.utc.beginning_of_day
+ project.add_maintainer(user)
sign_in(user)
visit project_project_members_path(project)
end
it 'updates group access level' do
- click_button @group_link.human_access
+ click_button group_link.human_access
page.within '.dropdown-menu' do
click_link 'Guest'
@@ -30,20 +33,38 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
end
it 'updates expiry date' do
- tomorrow = Date.today + 3
+ expires_at_field = "member_expires_at_#{group.id}"
+ fill_in expires_at_field, with: 3.days.from_now.to_date
- fill_in "member_expires_at_#{group.id}", with: tomorrow.strftime("%F")
- find('body').click
+ find_field(expires_at_field).native.send_keys :enter
wait_for_requests
page.within(find('li.group_member')) do
- expect(page).to have_content('Expires in')
+ expect(page).to have_content('Expires in 3 days')
+ end
+ end
+
+ context 'when link has expiry date set' do
+ let(:additional_link_attrs) { { expires_at: 3.days.from_now.to_date } }
+
+ it 'clears expiry date' do
+ page.within(find('li.group_member')) do
+ expect(page).to have_content('Expires in 3 days')
+
+ page.within(find('.js-edit-member-form')) do
+ find('.js-clear-input').click
+ end
+
+ wait_for_requests
+
+ expect(page).not_to have_content('Expires in')
+ end
end
end
it 'deletes group link' do
page.within(first('.group_member')) do
- accept_confirm { find('.btn-remove').click }
+ accept_confirm { find('.btn-danger').click }
end
wait_for_requests
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index b32ccb0ccef..36ff461aac2 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -102,7 +102,7 @@ RSpec.describe 'Project members list' do
visit_members_page
expect(page).not_to have_selector("#edit_project_member_#{project_member.id}")
- expect(page).not_to have_selector("#project_member_#{project_member.id} .btn-remove")
+ expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger")
end
end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 979bbd57aa3..d69c3f2652c 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -6,43 +6,64 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
- let(:maintainer) { create(:user) }
- let(:project) { create(:project) }
- let!(:new_member) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let(:new_member) { create(:user) }
before do
+ travel_to Time.now.utc.beginning_of_day
+
project.add_maintainer(maintainer)
sign_in(maintainer)
end
it 'expiration date is displayed in the members list' do
- travel_to Time.zone.parse('2016-08-06 08:00') do
- date = 4.days.from_now
- visit project_project_members_path(project)
-
- page.within '.invite-users-form' do
- select2(new_member.id, from: '#user_ids', multiple: true)
- fill_in 'expires_at', with: date.to_s(:medium) + "\n"
- click_on 'Invite'
- end
-
- page.within "#project_member_#{new_member.project_members.first.id}" do
- expect(page).to have_content('Expires in 4 days')
- end
+ visit project_project_members_path(project)
+
+ page.within '.invite-users-form' do
+ select2(new_member.id, from: '#user_ids', multiple: true)
+
+ fill_in 'expires_at', with: 3.days.from_now.to_date
+ find_field('expires_at').native.send_keys :enter
+
+ click_on 'Invite'
+ end
+
+ page.within "#project_member_#{project_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
+ end
+ end
+
+ it 'changes expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: Date.today.to_date)
+ visit project_project_members_path(project)
+
+ page.within "#project_member_#{project_member_id}" do
+ fill_in 'Expiration date', with: 3.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
+
+ wait_for_requests
+
+ expect(page).to have_content('Expires in 3 days')
end
end
- it 'change expiration date' do
- travel_to Time.zone.parse('2016-08-06 08:00') do
- date = 3.days.from_now
- project.team.add_users([new_member.id], :developer, expires_at: Date.today.to_s(:medium))
- visit project_project_members_path(project)
-
- page.within "#project_member_#{new_member.project_members.first.id}" do
- find('.js-access-expiration-date').set date.to_s(:medium) + "\n"
- wait_for_requests
- expect(page).to have_content('Expires in 3 days')
- end
+ it 'clears expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date)
+ visit project_project_members_path(project)
+
+ page.within "#project_member_#{project_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
+
+ find('.js-clear-input').click
+
+ wait_for_requests
+
+ expect(page).not_to have_content('Expires in')
end
end
+
+ def project_member_id
+ project.members.find_by(user_id: new_member).id
+ end
end
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 07f65fe62df..4ff3827b240 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -12,20 +12,10 @@ RSpec.describe 'Project navbar' do
let_it_be(:project) { create(:project, :repository) }
before do
- stub_feature_flags(project_iterations: false)
-
insert_package_nav(_('Operations'))
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 243579ee2f7..c3eea0195a6 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -336,7 +336,7 @@ RSpec.shared_examples 'pages settings editing' do
expect(page).not_to have_field(:project_pages_https_only)
expect(page).not_to have_content('Force HTTPS (requires valid certificates)')
- expect(page).not_to have_button('Save')
+ expect(page).to have_button('Save')
end
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index f59dc5dd074..51826d867cd 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -172,10 +172,17 @@ RSpec.describe 'Pipeline', :js do
end
end
- it_behaves_like 'showing user status' do
- let(:user_with_status) { pipeline.user }
+ describe 'pipelines details view' do
+ let!(:status) { create(:user_status, user: pipeline.user, emoji: 'smirk', message: 'Authoring this object') }
- subject { visit project_pipeline_path(project, pipeline) }
+ it 'pipeline header shows the user status and emoji' do
+ visit project_pipeline_path(project, pipeline)
+
+ within '[data-testid="ci-header-content"]' do
+ expect(page).to have_selector("[data-testid='#{status.message}']")
+ expect(page).to have_selector("[data-name='#{status.emoji}']")
+ end
+ end
end
describe 'pipeline graph' do
@@ -400,7 +407,7 @@ RSpec.describe 'Pipeline', :js do
context 'when retrying' do
before do
- find('[data-testid="retryButton"]').click
+ find('[data-testid="retryPipeline"]').click
end
it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do
@@ -902,7 +909,7 @@ RSpec.describe 'Pipeline', :js do
context 'when retrying' do
before do
- find('[data-testid="retryButton"]').click
+ find('[data-testid="retryPipeline"]').click
end
it 'does not show a "Retry" button', :sidekiq_might_not_need_inline do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index a9c196bb84b..3e78dfc3bc7 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -118,7 +118,7 @@ RSpec.describe 'Pipelines', :js do
context 'when canceling' do
before do
find('.js-pipelines-cancel-button').click
- find('.js-modal-primary-action').click
+ click_button 'Stop pipeline'
wait_for_requests
end
@@ -407,7 +407,7 @@ RSpec.describe 'Pipelines', :js do
context 'when canceling' do
before do
find('.js-pipelines-cancel-button').click
- find('.js-modal-primary-action').click
+ click_button 'Stop pipeline'
end
it 'indicates that pipeline was canceled', :sidekiq_might_not_need_inline do
diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb
index 5d05a7e4c91..0a5f7cc7edd 100644
--- a/spec/features/projects/releases/user_creates_release_spec.rb
+++ b/spec/features/projects/releases/user_creates_release_spec.rb
@@ -11,14 +11,11 @@ RSpec.describe 'User creates release', :js do
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)
+ sign_in(user)
visit new_page_url
@@ -75,14 +72,6 @@ RSpec.describe 'User creates release', :js do
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
@@ -108,6 +97,24 @@ RSpec.describe 'User creates release', :js do
end
end
+ context 'when the release notes "Preview" tab is clicked' do
+ before do
+ find_field('Release notes').click
+
+ fill_release_notes('**some** _markdown_ [content](https://example.com)')
+
+ click_on 'Preview'
+
+ wait_for_all_requests
+ end
+
+ it 'renders a preview of the release notes markdown' do
+ within('[data-testid="release-notes"]') do
+ expect(page).to have_text('some markdown content')
+ end
+ end
+ end
+
def fill_out_form_and_submit
fill_tag_name(tag_name)
diff --git a/spec/features/projects/releases/user_views_edit_release_spec.rb b/spec/features/projects/releases/user_views_edit_release_spec.rb
index 4ed1be6db6b..9115a135aeb 100644
--- a/spec/features/projects/releases/user_views_edit_release_spec.rb
+++ b/spec/features/projects/releases/user_views_edit_release_spec.rb
@@ -6,14 +6,11 @@ RSpec.describe 'User edits Release', :js do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:release) { create(:release, project: project, name: 'The first release' ) }
let_it_be(:user) { create(:user) }
- let(:show_feature_flag) { true }
before do
- stub_feature_flags(release_show_page: show_feature_flag)
-
project.add_developer(user)
- gitlab_sign_in(user)
+ sign_in(user)
visit edit_project_release_path(project, release)
@@ -42,7 +39,7 @@ RSpec.describe 'User edits Release', :js do
it 'renders the edit Release form' do
expect(page).to have_content('Releases are based on Git tags. We recommend tags that use semantic versioning, for example v1.0, v2.0-pre.')
- expect(find_field('Tag name', { disabled: true }).value).to eq(release.tag)
+ expect(find_field('Tag name', disabled: true).value).to eq(release.tag)
expect(find_field('Release title').value).to eq(release.name)
expect(find_field('Release notes').value).to eq(release.description)
@@ -71,42 +68,24 @@ RSpec.describe 'User edits Release', :js do
expect(release.description).to eq('Updated Release notes')
end
- context 'when the release_show_page feature flag is disabled' do
- let(:show_feature_flag) { false }
-
- it 'redirects to the main Releases page when "Cancel" is clicked' do
- fill_out_form_and_click 'Cancel'
-
- expect(page).to have_current_path(project_releases_path(project))
- end
+ it 'redirects to the previous page when "Cancel" is clicked when the url includes a back_url query parameter' do
+ back_path = project_releases_path(project, params: { page: 2 })
+ visit edit_project_release_path(project, release, params: { back_url: back_path })
- it 'redirects to the main Releases page when "Save changes" is clicked' do
- fill_out_form_and_click 'Save changes'
+ fill_out_form_and_click 'Cancel'
- expect(page).to have_current_path(project_releases_path(project))
- end
+ expect(page).to have_current_path(back_path)
end
- context 'when the release_show_page feature flag is enabled' do
- it 'redirects to the previous page when "Cancel" is clicked when the url includes a back_url query parameter' do
- back_path = project_releases_path(project, params: { page: 2 })
- visit edit_project_release_path(project, release, params: { back_url: back_path })
-
- fill_out_form_and_click 'Cancel'
-
- expect(page).to have_current_path(back_path)
- end
-
- it 'redirects to the main Releases page when "Cancel" is clicked when the url does not include a back_url query parameter' do
- fill_out_form_and_click 'Cancel'
+ it 'redirects to the main Releases page when "Cancel" is clicked when the url does not include a back_url query parameter' do
+ fill_out_form_and_click 'Cancel'
- expect(page).to have_current_path(project_releases_path(project))
- end
+ expect(page).to have_current_path(project_releases_path(project))
+ end
- it 'redirects to the dedicated Release page when "Save changes" is clicked' do
- fill_out_form_and_click 'Save changes'
+ it 'redirects to the dedicated Release page when "Save changes" is clicked' do
+ fill_out_form_and_click 'Save changes'
- expect(page).to have_current_path(project_release_path(project, release))
- end
+ expect(page).to have_current_path(project_release_path(project, release))
end
end
diff --git a/spec/features/projects/releases/user_views_release_spec.rb b/spec/features/projects/releases/user_views_release_spec.rb
index c82588746a8..186122536ce 100644
--- a/spec/features/projects/releases/user_views_release_spec.rb
+++ b/spec/features/projects/releases/user_views_release_spec.rb
@@ -4,34 +4,57 @@ require 'spec_helper'
RSpec.describe 'User views Release', :js do
let(:project) { create(:project, :repository) }
- let(:release) { create(:release, project: project, name: 'The first release' ) }
let(:user) { create(:user) }
+ let(:graphql_feature_flag) { true }
+
+ let(:release) do
+ create(:release,
+ project: project,
+ name: 'The first release',
+ description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)')
+ end
before do
+ stub_feature_flags(graphql_individual_release_page: graphql_feature_flag)
+
project.add_developer(user)
- gitlab_sign_in(user)
+ sign_in(user)
visit project_release_path(project, release)
end
- it 'renders the breadcrumbs' do
- within('.breadcrumbs') do
- expect(page).to have_content("#{project.creator.name} #{project.name} Releases #{release.name}")
+ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
- 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('Releases', href: project_releases_path(project))
- expect(page).to have_link(release.name, href: project_release_path(project, release))
+ shared_examples 'release page' do
+ it 'renders the breadcrumbs' do
+ within('.breadcrumbs') do
+ expect(page).to have_content("#{project.creator.name} #{project.name} Releases #{release.name}")
+
+ 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('Releases', href: project_releases_path(project))
+ expect(page).to have_link(release.name, href: project_release_path(project, release))
+ end
end
- end
- it 'renders the release details' do
- within('.release-block') do
- expect(page).to have_content(release.name)
- expect(page).to have_content(release.tag)
- expect(page).to have_content(release.commit.short_id)
- expect(page).to have_content(release.description)
+ it 'renders the release details' do
+ within('.release-block') do
+ expect(page).to have_content(release.name)
+ expect(page).to have_content(release.tag)
+ expect(page).to have_content(release.commit.short_id)
+ expect(page).to have_content('Lorem ipsum dolor sit amet')
+ end
end
end
+
+ describe 'when the graphql_individual_release_page feature flag is enabled' do
+ it_behaves_like 'release page'
+ end
+
+ describe 'when the graphql_individual_release_page feature flag is disabled' do
+ let(:graphql_feature_flag) { false }
+
+ it_behaves_like 'release page'
+ 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 993d3371904..323c57570c3 100644
--- a/spec/features/projects/releases/user_views_releases_spec.rb
+++ b/spec/features/projects/releases/user_views_releases_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'User views releases', :js do
shared_examples 'releases page' do
context('when the user is a maintainer') do
before do
- gitlab_sign_in(maintainer)
+ sign_in(maintainer)
end
it 'sees the release' do
@@ -27,11 +27,23 @@ RSpec.describe 'User views releases', :js do
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 ) }
+ 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)
+
+ 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
+
+ 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" }
- 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)
@@ -41,51 +53,21 @@ RSpec.describe 'User views releases', :js do
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
end
end
+ 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) }
- let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
-
- it 'sees the link' 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
- end
-
- context 'when url points to external resource' do
- let(:url) { 'http://google.com/download' }
+ 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)
+ 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
+ page.within('.js-assets-list') do
+ expect(page).to have_css('[data-testid="external-link-indicator"]')
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)
- 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
-
- 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 ) }
@@ -110,7 +92,7 @@ RSpec.describe 'User views releases', :js do
context('when the user is a guest') do
before do
- gitlab_sign_in(guest)
+ sign_in(guest)
end
it 'renders release info except for Git-related data' do
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 0358acc8dcc..ffc0ecc4966 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -64,7 +64,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
it 'updates forward_deployment_enabled' do
visit project_settings_ci_cd_path(project)
- checkbox = find_field('project_forward_deployment_enabled')
+ checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_enabled')
expect(checkbox).to be_checked
checkbox.set(false)
@@ -79,7 +79,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
expect(page).to have_button('Save changes', disabled: false)
end
- checkbox = find_field('project_forward_deployment_enabled')
+ checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_enabled')
expect(checkbox).not_to be_checked
end
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index 8e2f97fd6a0..4e1b53ffc87 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -3,27 +3,35 @@
require 'spec_helper'
RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration policy', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace, container_registry_enabled: container_registry_enabled) }
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
+
let(:container_registry_enabled) { true }
+ let(:container_registry_enabled_on_project) { true }
+
+ subject { visit project_settings_ci_cd_path(project) }
before do
+ project.update!(container_registry_enabled: container_registry_enabled_on_project)
+
sign_in(user)
- stub_container_registry_config(enabled: true)
+ stub_container_registry_config(enabled: container_registry_enabled)
stub_feature_flags(new_variables_ui: false)
end
context 'as owner' do
- before do
- visit project_settings_ci_cd_path(project)
- end
-
it 'shows available section' do
+ subject
+
settings_block = find('#js-registry-policies')
expect(settings_block).to have_text 'Cleanup policy for tags'
end
it 'saves cleanup policy submit the form' do
+ subject
+
within '#js-registry-policies' do
within '.card-body' do
select('7 days until tags are automatically removed', from: 'Expiration interval:')
@@ -40,6 +48,8 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
end
it 'does not save cleanup policy submit form with invalid regex' do
+ subject
+
within '#js-registry-policies' do
within '.card-body' do
fill_in('Tags with names matching this regex pattern will expire:', with: '*-production')
@@ -53,25 +63,53 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
end
end
- context 'when registry is disabled' do
- before do
- stub_container_registry_config(enabled: false)
- visit project_settings_ci_cd_path(project)
+ context 'with a project without expiration policy' do
+ where(:application_setting, :feature_flag, :result) do
+ true | true | :available_section
+ true | false | :available_section
+ false | true | :available_section
+ false | false | :disabled_message
end
- it 'does not exists' do
- expect(page).not_to have_selector('#js-registry-policies')
+ with_them do
+ before do
+ project.container_expiration_policy.destroy!
+ stub_feature_flags(container_expiration_policies_historic_entry: false)
+ stub_application_setting(container_expiration_policies_enable_historic_entries: application_setting)
+ stub_feature_flags(container_expiration_policies_historic_entry: project) if feature_flag
+ end
+
+ it 'displays the expected result' do
+ subject
+
+ within '#js-registry-policies' do
+ case result
+ when :available_section
+ expect(find('.card-header')).to have_content('Tag expiration policy')
+ when :disabled_message
+ expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
+ end
+ end
+ end
end
end
- context 'when container registry is disabled on project' do
+ context 'when registry is disabled' do
let(:container_registry_enabled) { false }
- before do
- visit project_settings_ci_cd_path(project)
+ it 'does not exists' do
+ subject
+
+ expect(page).not_to have_selector('#js-registry-policies')
end
+ end
+
+ context 'when container registry is disabled on project' do
+ let(:container_registry_enabled_on_project) { false }
it 'does not exists' do
+ subject
+
expect(page).not_to have_selector('#js-registry-policies')
end
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 9d9a75c22be..d444ea27d35 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -18,7 +18,9 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button
click_link 'On mention'
- wait_for_requests
+ page.within('.notification-dropdown') do
+ expect(page).not_to have_css('.gl-spinner')
+ end
click_notifications_button
expect(find('.update-notification.is-active')).to have_content('On mention')
@@ -30,7 +32,9 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button
click_link 'Disabled'
- wait_for_requests
+ page.within('.notification-dropdown') do
+ expect(page).not_to have_css('.gl-spinner')
+ end
expect(page).to have_css('.notifications-icon[data-testid="notifications-off-icon"]')
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 81736fefae9..189aa45ff75 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
@@ -46,21 +46,21 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
- it '"New file" button linked to new file page' do
+ it '"New file" button linked to IDE new file page' do
page.within('.project-buttons') do
- expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master'))
+ expect(page).to have_link('New file', href: presenter.ide_edit_path(project, project.default_branch || 'master'))
end
end
- it '"Add README" button linked to new file populated for a README' do
+ it '"Add README" button linked to IDE new file populated for a README' do
page.within('.project-buttons') do
- expect(page).to have_link('Add README', href: presenter.add_readme_path)
+ expect(page).to have_link('Add README', href: presenter.add_readme_ide_path)
end
end
- it '"Add license" button linked to new file populated for a license' do
+ it '"Add license" button linked to IDE new file populated for a license' do
page.within('.project-buttons') do
- expect(page).to have_link('Add LICENSE', href: presenter.add_license_path)
+ expect(page).to have_link('Add LICENSE', href: presenter.add_license_ide_path)
end
end
@@ -74,9 +74,9 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
- it '"New file" button linked to new file page' do
+ it '"New file" button linked to IDE new file page' do
page.within('.project-buttons') do
- expect(page).to have_link('New file', href: project_new_blob_path(project, 'example_branch'))
+ expect(page).to have_link('New file', href: presenter.ide_edit_path(project, 'example_branch'))
end
end
end
@@ -144,7 +144,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
expect(project.repository.readme).not_to be_nil
page.within('.project-buttons') do
- expect(page).not_to have_link('Add README', href: presenter.add_readme_path)
+ expect(page).not_to have_link('Add README', href: presenter.add_readme_ide_path)
expect(page).to have_link('README', href: presenter.readme_path)
end
end
@@ -164,7 +164,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
end
context 'when the project does not have a README' do
- it 'shows the "Add README" button' do
+ it 'shows the single file editor "Add README" button' do
allow(project.repository).to receive(:readme).and_return(nil)
visit project_path(project)
@@ -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_ide_path)
+ expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
end
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 503246bbdcf..28fe0a0b7e1 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -17,115 +17,81 @@ 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' }
- shared_examples 'snippet creation' do
- def fill_form
- snippet_fill_in_form(title: title, content: file_content, description: md_description)
- end
-
- it 'shows collapsible description input' do
- collapsed = description_field
+ def fill_form
+ snippet_fill_in_form(title: title, content: file_content, description: md_description)
+ end
- expect(page).not_to have_field(snippet_description_field)
- expect(collapsed).to be_visible
+ before do
+ sign_in(user)
- collapsed.click
+ visit new_project_snippet_path(project)
+ end
- expect(page).to have_field(snippet_description_field)
- expect(collapsed).not_to be_visible
- end
+ it 'shows collapsible description input' do
+ collapsed = snippet_description_field_collapsed
- it 'creates a new snippet' do
- fill_form
- click_button('Create snippet')
- wait_for_requests
+ expect(page).not_to have_field(snippet_description_locator)
+ expect(collapsed).to be_visible
- 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
+ collapsed.click
- it 'uploads a file when dragging into textarea' do
- fill_form
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
-
- expect(snippet_description_value).to have_content('banana_sample')
+ expect(page).to have_field(snippet_description_locator)
+ expect(collapsed).not_to be_visible
+ end
- click_button('Create snippet')
- wait_for_requests
+ it 'creates a new snippet' do
+ fill_form
+ click_button('Create snippet')
+ wait_for_requests
- 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})
+ expect(page).to have_content(title)
+ expect(page).to have_content(file_content)
+ page.within('.snippet-header .snippet-description') do
+ expect(page).to have_content(description)
+ expect(page).to have_selector('strong')
end
+ end
- context 'when the git operation fails' do
- let(:error) { 'Error creating the snippet' }
-
- before do
- allow_next_instance_of(Snippets::CreateService) do |instance|
- allow(instance).to receive(:create_commit).and_raise(StandardError, error)
- end
+ it 'uploads a file when dragging into textarea' do
+ fill_form
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- fill_form
+ expect(snippet_description_value).to have_content('banana_sample')
- click_button('Create snippet')
- wait_for_requests
- end
+ click_button('Create snippet')
+ wait_for_requests
- 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
+ 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 'Vue application' do
- let(:snippet_description_field) { 'snippet-description' }
- let(:snippet_description_view_selector) { '.snippet-header .snippet-description' }
+ context 'when the git operation fails' do
+ let(:error) { 'Error creating the snippet' }
before do
- sign_in(user)
-
- visit new_project_snippet_path(project)
- end
-
- it_behaves_like 'snippet creation'
-
- it 'does not allow submitting the form without title and content' do
- fill_in snippet_title_field, with: title
+ allow_next_instance_of(Snippets::CreateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, error)
+ end
- expect(page).not_to have_button('Create snippet')
+ fill_form
- snippet_fill_in_form(title: title, content: file_content)
- expect(page).to have_button('Create snippet')
+ click_button('Create snippet')
+ wait_for_requests
end
- end
-
- context 'non-Vue application' do
- let(:snippet_description_field) { 'project_snippet_description' }
- let(:snippet_description_view_selector) { '.snippet-header .description' }
-
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
-
- sign_in(user)
- visit new_project_snippet_path(project)
+ 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
- it_behaves_like 'snippet creation'
+ it 'does not allow submitting the form without title and content' do
+ snippet_fill_in_title(title)
- it 'displays validation errors' do
- fill_in snippet_title_field, with: title
- click_button('Create snippet')
- wait_for_requests
+ expect(page).not_to have_button('Create snippet')
- expect(page).to have_selector('#error_explanation')
- end
+ snippet_fill_in_form(title: title, content: file_content)
+ expect(page).to have_button('Create snippet')
end
end
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index 8fded3cde80..5937ff75457 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -13,8 +13,6 @@ RSpec.describe 'Projects > Snippets > Project snippet', :js do
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
before do
- stub_feature_flags(snippets_vue: false)
-
sign_in(user)
end
@@ -28,12 +26,8 @@ RSpec.describe 'Projects > Snippets > Project snippet', :js do
end
end
- it_behaves_like 'showing user status' do
- let(:file_path) { 'files/ruby/popen.rb' }
- let(:user_with_status) { snippet.author }
-
- subject { visit project_snippet_path(project, snippet) }
- end
+ # it_behaves_like 'showing user status' do
+ # This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394
it_behaves_like 'does not show New Snippet button' do
let(:file_path) { 'files/ruby/popen.rb' }
diff --git a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
index 2784fec3dc1..b37d40c0eed 100644
--- a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe 'Projects > Snippets > User comments on a snippet', :js do
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
before do
- stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/projects/snippets/user_deletes_snippet_spec.rb b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
index 44fe9834484..6d526e60512 100644
--- a/spec/features/projects/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
@@ -2,13 +2,12 @@
require 'spec_helper'
-RSpec.describe 'Projects > Snippets > User deletes a snippet' do
+RSpec.describe 'Projects > Snippets > User deletes a snippet', :js do
let(:project) { create(:project) }
- let!(:snippet) { create(:project_snippet, project: project, author: user) }
+ let!(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
let(:user) { create(:user) }
before do
- stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)
@@ -16,7 +15,11 @@ RSpec.describe 'Projects > Snippets > User deletes a snippet' do
end
it 'deletes a snippet' do
- first(:link, 'Delete').click
+ expect(page).to have_content(snippet.title)
+
+ click_button('Delete')
+ click_button('Delete snippet')
+ wait_for_requests
expect(page).not_to have_content(snippet.title)
end
diff --git a/spec/features/projects/snippets/user_updates_snippet_spec.rb b/spec/features/projects/snippets/user_updates_snippet_spec.rb
index 193eaa9576a..aa498163f52 100644
--- a/spec/features/projects/snippets/user_updates_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_updates_snippet_spec.rb
@@ -9,9 +9,7 @@ RSpec.describe 'Projects > Snippets > User updates a snippet', :js do
let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:snippet, reload: true) { create(:project_snippet, :repository, project: project, author: user) }
- let(:snippet_title_field) { 'project_snippet_title' }
-
- def bootstrap_snippet
+ before do
project.add_maintainer(user)
sign_in(user)
@@ -20,64 +18,36 @@ RSpec.describe 'Projects > Snippets > User updates a snippet', :js do
wait_for_all_requests
end
- shared_examples 'snippet update' do
- it 'displays the snippet blob path and content' do
- blob = snippet.blobs.first
-
- 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 'updates a snippet' do
- fill_in('project_snippet_title', with: 'Snippet new title')
- click_button('Save')
+ it 'displays the snippet blob path and content' do
+ blob = snippet.blobs.first
- 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
-
- 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
+ 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
- context 'Vue application' do
- before do
- bootstrap_snippet
- end
+ it 'updates a snippet' do
+ fill_in('snippet-title', with: 'Snippet new title')
+ click_button('Save')
- it_behaves_like 'snippet update' do
- let(:snippet_blob_path_field) { 'snippet_file_name' }
- let(:snippet_blob_content_selector) { '.file-content' }
- end
+ expect(page).to have_content('Snippet new title')
end
- context 'non-Vue application' do
+ context 'when the git operation fails' do
before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
+ allow_next_instance_of(Snippets::UpdateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
+ end
- bootstrap_snippet
+ snippet_fill_in_form(title: 'Snippet new title', file_name: 'new_file_name')
+
+ click_button('Save')
end
- it_behaves_like 'snippet update' do
- let(:snippet_blob_path_field) { 'project_snippet_file_name' }
- let(:snippet_blob_content_selector) { '.file-content' }
+ 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
diff --git a/spec/features/projects/tracings_spec.rb b/spec/features/projects/tracings_spec.rb
new file mode 100644
index 00000000000..c4a4f1382ed
--- /dev/null
+++ b/spec/features/projects/tracings_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Tracings Content Security Policy' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ subject { response_headers['Content-Security-Policy'] }
+
+ before_all do
+ project.add_maintainer(user)
+ end
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when there is no global config' do
+ before do
+ expect_next_instance_of(Projects::TracingsController) 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 project_tracing_path(project)
+
+ is_expected.to be_blank
+ end
+ end
+
+ context 'when a global CSP config exists' do
+ before do
+ csp = ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.frame_src 'https://global-policy.com'
+ end
+
+ expect_next_instance_of(Projects::TracingsController) do |controller|
+ expect(controller).to receive(:current_content_security_policy).and_return(csp)
+ end
+ end
+
+ context 'when external_url is set' do
+ let!(:project_tracing_setting) { create(:project_tracing_setting, project: project) }
+
+ it 'overwrites frame-src' do
+ visit project_tracing_path(project)
+
+ is_expected.to eq("frame-src https://example.com")
+ end
+ end
+
+ context 'when external_url is not set' do
+ it 'uses global policy' do
+ visit project_tracing_path(project)
+
+ is_expected.to eq("frame-src https://global-policy.com")
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index bd2af66710a..ca9e0a23888 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe 'Projects tree', :js do
# Check last commit
expect(find('.commit-content').text).to include(message)
- expect(find('.commit-sha-group').text).to eq(short_newrev)
+ expect(find('.js-commit-sha-group').text).to eq(short_newrev)
end
end
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index 50d7b353c46..616c5065c07 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -128,6 +128,59 @@ RSpec.describe 'Projects > User sees sidebar' do
end
end
+ context 'as anonymous' do
+ let(:project) { create(:project, :public) }
+ let!(:issue) { create(:issue, :opened, project: project, author: user) }
+
+ describe 'project landing page' do
+ before do
+ project.project_feature.update!(
+ builds_access_level: ProjectFeature::DISABLED,
+ merge_requests_access_level: ProjectFeature::DISABLED,
+ repository_access_level: ProjectFeature::DISABLED,
+ issues_access_level: ProjectFeature::DISABLED,
+ wiki_access_level: ProjectFeature::DISABLED
+ )
+ end
+
+ it 'does not show the project file list landing page, but the activity' do
+ visit project_path(project)
+
+ expect(page).not_to have_selector '.project-stats'
+ expect(page).not_to have_selector '.project-last-commit'
+ expect(page).not_to have_selector '.project-show-files'
+ expect(page).to have_selector '.project-show-activity'
+ end
+
+ it 'shows the wiki when enabled' do
+ project.project_feature.update!(wiki_access_level: ProjectFeature::ENABLED)
+
+ visit project_path(project)
+
+ expect(page).to have_selector '.project-show-wiki'
+ end
+
+ it 'shows the issues when enabled' do
+ project.project_feature.update!(issues_access_level: ProjectFeature::ENABLED)
+
+ visit project_path(project)
+
+ expect(page).to have_selector '.issues-list'
+ end
+
+ it 'shows the wiki when wiki and issues are enabled' do
+ project.project_feature.update!(
+ issues_access_level: ProjectFeature::ENABLED,
+ wiki_access_level: ProjectFeature::ENABLED
+ )
+
+ visit project_path(project)
+
+ expect(page).to have_selector '.project-show-wiki'
+ end
+ end
+ end
+
context 'as guest' do
let(:guest) { create(:user) }
let!(:issue) { create(:issue, :opened, project: project, author: guest) }
@@ -145,11 +198,11 @@ RSpec.describe 'Projects > User sees sidebar' do
expect(page).to have_content 'Project'
expect(page).to have_content 'Issues'
expect(page).to have_content 'Wiki'
+ expect(page).to have_content 'Operations'
expect(page).not_to have_content 'Repository'
expect(page).not_to have_content 'CI / CD'
expect(page).not_to have_content 'Merge Requests'
- expect(page).not_to have_content 'Operations'
end
end
@@ -194,13 +247,13 @@ RSpec.describe 'Projects > User sees sidebar' do
expect(page).not_to have_selector '.project-stats'
expect(page).not_to have_selector '.project-last-commit'
expect(page).not_to have_selector '.project-show-files'
- expect(page).to have_selector '.project-show-customize_workflow'
+ expect(page).to have_selector '.project-show-activity'
end
- it 'shows the customize workflow when issues and wiki are disabled' do
+ it 'shows the project activity when issues and wiki are disabled' do
visit project_path(project)
- expect(page).to have_selector '.project-show-customize_workflow'
+ expect(page).to have_selector '.project-show-activity'
end
it 'shows the wiki when enabled' do
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
deleted file mode 100644
index 8f2fb9e827c..00000000000
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ /dev/null
@@ -1,168 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Projects > Wiki > User previews markdown changes', :js do
- let_it_be(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
- 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)
-[relative link 3](./e/f/relative)
-[spaced link](title with spaces)
- HEREDOC
- end
-
- before do
- project.add_maintainer(user)
-
- sign_in(user)
- end
-
- context "while creating a new wiki page" do
- context "when there are no spaces or hyphens in the page name" do
- it "rewrites relative links as expected" do
- create_wiki_page('a/b/c/d', content: wiki_content)
-
- expect(page).to have_content("regular link")
-
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a/b/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a/b/c/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a/b/c/e/f/relative\">relative link 3</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/title%20with%20spaces\">spaced link</a>")
- end
- end
-
- context "when there are spaces in the page name" do
- it "rewrites relative links as expected" do
- create_wiki_page('a page/b page/c page/d page', content: wiki_content)
-
- expect(page).to have_content("regular link")
-
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/title%20with%20spaces\">spaced link</a>")
- end
- end
-
- context "when there are hyphens in the page name" do
- it "rewrites relative links as expected" do
- create_wiki_page('a-page/b-page/c-page/d-page', content: wiki_content)
-
- expect(page).to have_content("regular link")
-
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/title%20with%20spaces\">spaced link</a>")
- end
- end
- end
-
- context "while editing a wiki page" do
- context "when there are no spaces or hyphens in the page name" do
- it "rewrites relative links as expected" do
- create_wiki_page('a/b/c/d')
- click_link 'Edit'
-
- fill_in :wiki_content, with: wiki_content
- click_on "Preview"
-
- expect(page).to have_content("regular link")
-
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a/b/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a/b/c/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a/b/c/e/f/relative\">relative link 3</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/title%20with%20spaces\">spaced link</a>")
- end
- end
-
- context "when there are spaces in the page name" do
- it "rewrites relative links as expected" do
- create_wiki_page('a page/b page/c page/d page')
- click_link 'Edit'
-
- fill_in :wiki_content, with: wiki_content
- click_on "Preview"
-
- expect(page).to have_content("regular link")
-
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/title%20with%20spaces\">spaced link</a>")
- end
- end
-
- context "when there are hyphens in the page name" do
- it "rewrites relative links as expected" do
- create_wiki_page('a-page/b-page/c-page/d-page')
- click_link 'Edit'
-
- fill_in :wiki_content, with: wiki_content
- click_on "Preview"
-
- expect(page).to have_content("regular link")
-
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
- expect(page.html).to include("<a href=\"/#{project.full_path}/-/wikis/title%20with%20spaces\">spaced link</a>")
- end
- end
-
- context 'when rendering the preview' do
- it 'renders content with CommonMark' do
- create_wiki_page('a-page/b-page/c-page/common-mark')
- click_link 'Edit'
-
- fill_in :wiki_content, with: "1. one\n - sublist\n"
- click_on "Preview"
-
- # the above generates two separate lists (not embedded) in CommonMark
- expect(page).to have_content("sublist")
- expect(page).not_to have_xpath("//ol//li//ul")
- end
- end
- end
-
- it "does not linkify double brackets inside code blocks as expected" do
- wiki_content = <<-HEREDOC
- `[[do_not_linkify]]`
- ```
- [[also_do_not_linkify]]
- ```
- HEREDOC
-
- create_wiki_page('linkify_test', wiki_content)
-
- expect(page).to have_content("do_not_linkify")
-
- expect(page.html).to include('[[do_not_linkify]]')
- expect(page.html).to include('[[also_do_not_linkify]]')
- end
-
- private
-
- def create_wiki_page(path, content = 'content')
- visit project_wiki_path(project, wiki_page)
-
- click_link 'New page'
-
- fill_in :wiki_title, with: path
- fill_in :wiki_content, with: content
-
- click_button 'Create page'
- end
-end
diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb
deleted file mode 100644
index 170e7afb51f..00000000000
--- a/spec/features/projects/wiki/shortcuts_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Wiki shortcuts', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
- let(:wiki_page) { create(:wiki_page, wiki: project.wiki, title: 'home', content: 'Home page') }
-
- before do
- sign_in(user)
- visit project_wiki_path(project, wiki_page)
- end
-
- it 'Visit edit wiki page using "e" keyboard shortcut' do
- find('body').native.send_key('e')
-
- expect(find('.wiki-page-title')).to have_content('Edit Page')
- end
-end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
deleted file mode 100644
index eba1b63765a..00000000000
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ /dev/null
@@ -1,360 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe "User creates wiki page" do
- include WikiHelpers
-
- let(:user) { create(:user) }
- let(:wiki) { ProjectWiki.new(project, user) }
- let(:project) { create(:project) }
-
- before do
- project.add_maintainer(user)
-
- sign_in(user)
- end
-
- context "when wiki is empty" do
- before do |example|
- visit(project_wikis_path(project))
-
- wait_for_svg_to_be_loaded(example)
-
- click_link "Create your first page"
- end
-
- context "in a user namespace" do
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
-
- it "shows validation error message" do
- page.within(".wiki-form") do
- fill_in(:wiki_content, with: "")
-
- click_on("Create page")
- end
-
- expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
-
- page.within(".wiki-form") do
- fill_in(:wiki_content, with: "[link test](test)")
-
- click_on("Create page")
- end
-
- expect(page).to have_content("Home").and have_content("link test")
-
- click_link("link test")
-
- expect(page).to have_content("Create New Page")
- end
-
- it "shows non-escaped link in the pages list" do
- fill_in(:wiki_title, with: "one/two/three-test")
-
- page.within(".wiki-form") do
- fill_in(:wiki_content, with: "wiki content")
-
- click_on("Create page")
- end
-
- expect(current_path).to include("one/two/three-test")
- expect(page).to have_xpath("//a[@href='/#{project.full_path}/-/wikis/one/two/three-test']")
- end
-
- it "has `Create home` as a commit message", :js do
- wait_for_requests
-
- expect(page).to have_field("wiki[message]", with: "Create home")
- end
-
- it "creates a page from the home page" do
- fill_in(:wiki_content, with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n# Wiki header\n")
- fill_in(:wiki_message, with: "Adding links to wiki")
-
- page.within(".wiki-form") do
- click_button("Create page")
- end
-
- expect(current_path).to eq(project_wiki_path(project, "home"))
- expect(page).to have_content("test GitLab API doc Rake tasks Wiki header")
- .and have_content("Home")
- .and have_content("Last edited by #{user.name}")
- .and have_header_with_correct_id_and_link(1, "Wiki header", "wiki-header")
-
- click_link("test")
-
- expect(current_path).to eq(project_wiki_path(project, "test"))
-
- page.within(:css, ".nav-text") do
- expect(page).to have_content("Create New Page")
- end
-
- click_link("Home")
-
- expect(current_path).to eq(project_wiki_path(project, "home"))
-
- click_link("GitLab API")
-
- expect(current_path).to eq(project_wiki_path(project, "api"))
-
- page.within(:css, ".nav-text") do
- expect(page).to have_content("Create")
- end
-
- click_link("Home")
-
- expect(current_path).to eq(project_wiki_path(project, "home"))
-
- click_link("Rake tasks")
-
- expect(current_path).to eq(project_wiki_path(project, "raketasks"))
-
- page.within(:css, ".nav-text") do
- expect(page).to have_content("Create")
- end
- end
-
- it "creates ASCII wiki with LaTeX blocks", :js do
- stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true)
-
- ascii_content = <<~MD
- :stem: latexmath
-
- [stem]
- ++++
- \\sqrt{4} = 2
- ++++
-
- another part
-
- [latexmath]
- ++++
- \\beta_x \\gamma
- ++++
-
- stem:[2+2] is 4
- MD
-
- find("#wiki_format option[value=asciidoc]").select_option
-
- fill_in(:wiki_content, with: ascii_content)
-
- page.within(".wiki-form") do
- click_button("Create page")
- end
-
- page.within ".md" do
- expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
- end
- end
-
- it 'creates a wiki page with Org markup', :aggregate_failures do
- org_content = <<~ORG
- * Heading
- ** Subheading
- [[home][Link to Home]]
- ORG
-
- page.within('.wiki-form') do
- find('#wiki_format option[value=org]').select_option
- fill_in(:wiki_content, with: org_content)
- click_button('Create page')
- end
-
- expect(page).to have_selector('h1', text: 'Heading')
- expect(page).to have_selector('h2', text: 'Subheading')
- expect(page).to have_link('Link to Home', href: "/#{project.full_path}/-/wikis/home")
- end
-
- it_behaves_like 'wiki file attachments'
- end
-
- context "in a group namespace", :js do
- let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
-
- it "has `Create home` as a commit message" do
- wait_for_requests
-
- expect(page).to have_field("wiki[message]", with: "Create home")
- end
-
- it "creates a page from the home page" do
- page.within(".wiki-form") do
- fill_in(:wiki_content, with: "My awesome wiki!")
-
- click_button("Create page")
- end
-
- expect(page).to have_content("Home")
- .and have_content("Last edited by #{user.name}")
- .and have_content("My awesome wiki!")
- end
- end
- end
-
- context "when wiki is not empty", :js do
- before do
- create(:wiki_page, wiki: wiki, title: 'home', content: 'Home page')
-
- visit(project_wikis_path(project))
- end
-
- context "in a user namespace" do
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
-
- context "via the `new wiki page` page" do
- it "creates a page with a single word" do
- click_link("New page")
-
- page.within(".wiki-form") do
- fill_in(:wiki_title, with: "foo")
- fill_in(:wiki_content, with: "My awesome wiki!")
- end
-
- # Commit message field should have correct value.
- expect(page).to have_field("wiki[message]", with: "Create foo")
-
- click_button("Create page")
-
- expect(page).to have_content("foo")
- .and have_content("Last edited by #{user.name}")
- .and have_content("My awesome wiki!")
- end
-
- it "creates a page with spaces in the name" do
- click_link("New page")
-
- page.within(".wiki-form") do
- fill_in(:wiki_title, with: "Spaces in the name")
- fill_in(:wiki_content, with: "My awesome wiki!")
- end
-
- # Commit message field should have correct value.
- expect(page).to have_field("wiki[message]", with: "Create Spaces in the name")
-
- click_button("Create page")
-
- expect(page).to have_content("Spaces in the name")
- .and have_content("Last edited by #{user.name}")
- .and have_content("My awesome wiki!")
- end
-
- it "creates a page with hyphens in the name" do
- click_link("New page")
-
- page.within(".wiki-form") do
- fill_in(:wiki_title, with: "hyphens-in-the-name")
- fill_in(:wiki_content, with: "My awesome wiki!")
- end
-
- # Commit message field should have correct value.
- expect(page).to have_field("wiki[message]", with: "Create hyphens in the name")
-
- page.within(".wiki-form") do
- fill_in(:wiki_content, with: "My awesome wiki!")
-
- click_button("Create page")
- end
-
- expect(page).to have_content("hyphens in the name")
- .and have_content("Last edited by #{user.name}")
- .and have_content("My awesome wiki!")
- end
- end
-
- it "shows the emoji autocompletion dropdown" do
- click_link("New page")
-
- page.within(".wiki-form") do
- find("#wiki_content").native.send_keys("")
-
- fill_in(:wiki_content, with: ":")
- end
-
- expect(page).to have_selector(".atwho-view")
- end
- end
-
- context "in a group namespace" do
- let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
-
- context "via the `new wiki page` page" do
- it "creates a page" do
- click_link("New page")
-
- page.within(".wiki-form") do
- fill_in(:wiki_title, with: "foo")
- fill_in(:wiki_content, with: "My awesome wiki!")
- end
-
- # Commit message field should have correct value.
- expect(page).to have_field("wiki[message]", with: "Create foo")
-
- click_button("Create page")
-
- expect(page).to have_content("foo")
- .and have_content("Last edited by #{user.name}")
- .and have_content("My awesome wiki!")
- end
- end
- end
- end
-
- describe 'sidebar feature' do
- context 'when there are some existing pages' do
- before do
- create(:wiki_page, wiki: wiki, title: 'home', content: 'home')
- create(:wiki_page, wiki: wiki, title: 'another', content: 'another')
- end
-
- it 'renders a default sidebar when there is no customized sidebar' do
- visit(project_wikis_path(project))
-
- expect(page).to have_content('another')
- expect(page).not_to have_link('View All Pages')
- end
-
- context 'when there is a customized sidebar' do
- before do
- create(:wiki_page, wiki: wiki, title: '_sidebar', content: 'My customized sidebar')
- end
-
- it 'renders my customized sidebar instead of the default one' do
- visit(project_wikis_path(project))
-
- expect(page).to have_content('My customized sidebar')
- expect(page).not_to have_content('Another')
- end
- end
- end
-
- context 'when there are 15 existing pages' do
- before do
- (1..5).each { |i| create(:wiki_page, wiki: wiki, title: "my page #{i}") }
- (6..10).each { |i| create(:wiki_page, wiki: wiki, title: "parent/my page #{i}") }
- (11..15).each { |i| create(:wiki_page, wiki: wiki, title: "grandparent/parent/my page #{i}") }
- end
-
- it 'shows all pages in the sidebar' do
- visit(project_wikis_path(project))
-
- (1..15).each { |i| expect(page).to have_content("my page #{i}") }
- expect(page).not_to have_link('View All Pages')
- end
-
- context 'when there are more than 15 existing pages' do
- before do
- create(:wiki_page, wiki: wiki, title: 'my page 16')
- end
-
- it 'shows the first 15 pages in the sidebar' do
- visit(project_wikis_path(project))
-
- expect(page).to have_text('my page', count: 15)
- expect(page).to have_link('View All Pages')
- end
- end
- end
- end
-end
diff --git a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
deleted file mode 100644
index a5d865d581b..00000000000
--- a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User deletes wiki page', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
- let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
-
- before do
- sign_in(user)
- visit(project_wiki_path(project, wiki_page))
- end
-
- it 'deletes a page' do
- click_on('Edit')
- click_on('Delete')
- find('.modal-footer .btn-danger').click
-
- expect(page).to have_content('Page was successfully deleted')
- end
-end
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
deleted file mode 100644
index fdab63a56b8..00000000000
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ /dev/null
@@ -1,263 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User updates wiki page' do
- include WikiHelpers
-
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- context 'when wiki is empty' do
- before do |example|
- visit(project_wikis_path(project))
-
- wait_for_svg_to_be_loaded(example)
-
- click_link "Create your first page"
- end
-
- context 'in a user namespace' do
- let(:project) { create(:project, :wiki_repo) }
-
- it 'redirects back to the home edit page' do
- page.within(:css, '.wiki-form .form-actions') do
- click_on('Cancel')
- end
-
- expect(current_path).to eq wiki_path(project.wiki)
- end
-
- it 'updates a page that has a path', :js do
- fill_in(:wiki_title, with: 'one/two/three-test')
-
- page.within '.wiki-form' do
- fill_in(:wiki_content, with: 'wiki content')
- click_on('Create page')
- end
-
- expect(current_path).to include('one/two/three-test')
- expect(find('.wiki-pages')).to have_content('three')
-
- first(:link, text: 'three').click
-
- expect(find('.nav-text')).to have_content('three')
-
- click_on('Edit')
-
- expect(current_path).to include('one/two/three-test')
- expect(page).to have_content('Edit Page')
-
- fill_in('Content', with: 'Updated Wiki Content')
- click_on('Save changes')
-
- expect(page).to have_content('Updated Wiki Content')
- end
-
- it_behaves_like 'wiki file attachments'
- end
- end
-
- context 'when wiki is not empty' do
- let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
- let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, title: 'home', content: 'Home page') }
-
- before do
- visit(project_wikis_path(project))
-
- click_link('Edit')
- end
-
- context 'in a user namespace' do
- let(:project) { create(:project, :wiki_repo) }
-
- it 'updates a page', :js do
- # Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Update home')
-
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Save changes')
-
- expect(page).to have_content('Home')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
- end
-
- it 'updates the commit message as the title is changed', :js do
- fill_in(:wiki_title, with: '& < > \ \ { } &')
-
- expect(page).to have_field('wiki[message]', with: 'Update & < > \ \ { } &')
- end
-
- it 'correctly escapes the commit message entities', :js do
- fill_in(:wiki_title, with: 'Wiki title')
-
- expect(page).to have_field('wiki[message]', with: 'Update Wiki title')
- end
-
- it 'shows a validation error message' do
- fill_in(:wiki_content, with: '')
- click_button('Save changes')
-
- expect(page).to have_selector('.wiki-form')
- expect(page).to have_content('Edit Page')
- expect(page).to have_content('The form contains the following error:')
- expect(page).to have_content("Content can't be blank")
- expect(find('textarea#wiki_content').value).to eq('')
- end
-
- it 'shows the emoji autocompletion dropdown', :js do
- find('#wiki_content').native.send_keys('')
- fill_in(:wiki_content, with: ':')
-
- expect(page).to have_selector('.atwho-view')
- end
-
- it 'shows the error message' do
- wiki_page.update(content: 'Update')
-
- click_button('Save changes')
-
- expect(page).to have_content('Someone edited the page the same time you did.')
- end
-
- it 'updates a page' do
- fill_in('Content', with: 'Updated Wiki Content')
- click_on('Save changes')
-
- expect(page).to have_content('Updated Wiki Content')
- end
-
- it 'cancels editing of a page' do
- page.within(:css, '.wiki-form .form-actions') do
- click_on('Cancel')
- end
-
- expect(current_path).to eq(project_wiki_path(project, wiki_page))
- end
-
- it_behaves_like 'wiki file attachments'
- end
-
- context 'in a group namespace' do
- let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
-
- it 'updates a page', :js do
- # Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Update home')
-
- fill_in(:wiki_content, with: 'My awesome wiki!')
-
- click_button('Save changes')
-
- expect(page).to have_content('Home')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
- end
-
- it_behaves_like 'wiki file attachments'
- end
- end
-
- context 'when the page is in a subdir' do
- let!(:project) { create(:project, :wiki_repo) }
- let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
- let(:page_name) { 'page_name' }
- let(:page_dir) { "foo/bar/#{page_name}" }
- let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, title: page_dir, content: 'Home page') }
-
- before do
- visit(project_wiki_edit_path(project, wiki_page))
- end
-
- it 'moves the page to the root folder' do
- fill_in(:wiki_title, with: "/#{page_name}")
-
- click_button('Save changes')
-
- expect(current_path).to eq(project_wiki_path(project, page_name))
- end
-
- it 'moves the page to other dir' do
- new_page_dir = "foo1/bar1/#{page_name}"
-
- fill_in(:wiki_title, with: new_page_dir)
-
- click_button('Save changes')
-
- expect(current_path).to eq(project_wiki_path(project, new_page_dir))
- end
-
- it 'remains in the same place if title has not changed' do
- original_path = project_wiki_path(project, wiki_page)
-
- fill_in(:wiki_title, with: page_name)
-
- click_button('Save changes')
-
- expect(current_path).to eq(original_path)
- end
-
- it 'can be moved to a different dir with a different name' do
- new_page_dir = "foo1/bar1/new_page_name"
-
- fill_in(:wiki_title, with: new_page_dir)
-
- click_button('Save changes')
-
- expect(current_path).to eq(project_wiki_path(project, new_page_dir))
- end
-
- it 'can be renamed and moved to the root folder' do
- new_name = 'new_page_name'
-
- fill_in(:wiki_title, with: "/#{new_name}")
-
- click_button('Save changes')
-
- expect(current_path).to eq(project_wiki_path(project, new_name))
- end
-
- it 'squishes the title before creating the page' do
- new_page_dir = " foo1 / bar1 / #{page_name} "
-
- fill_in(:wiki_title, with: new_page_dir)
-
- click_button('Save changes')
-
- expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
- end
-
- it_behaves_like 'wiki file attachments'
- end
-
- context 'when an existing page exceeds the content size limit' do
- let_it_be(:project) { create(:project, :wiki_repo) }
- let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, content: "one\ntwo\nthree") }
-
- before do
- stub_application_setting(wiki_page_max_content_bytes: 10)
-
- visit wiki_page_path(wiki_page.wiki, wiki_page, action: :edit)
- end
-
- it 'allows changing the title if the content does not change' do
- fill_in 'Title', with: 'new title'
- click_on 'Save changes'
-
- expect(page).to have_content('Wiki was successfully updated.')
- end
-
- it 'shows a validation error when trying to change the content' do
- fill_in 'Content', with: 'new content'
- click_on 'Save changes'
-
- expect(page).to have_content('The form contains the following error:')
- expect(page).to have_content('Content is too long (11 Bytes). The maximum size is 10 Bytes.')
- end
- end
-end
diff --git a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
index 0af40a2d760..1f460f39267 100644
--- a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
@@ -2,108 +2,86 @@
require 'spec_helper'
-RSpec.describe 'User views empty wiki' do
- let(:user) { create(:user) }
- let(:confluence_link) { 'Enable the Confluence Wiki integration' }
- let(:element) { page.find('.row.empty-state') }
-
- shared_examples 'empty wiki and accessible issues' do
- it 'show "issue tracker" message' do
- visit(project_wikis_path(project))
-
- expect(element).to have_content('This project has no wiki pages')
- expect(element).to have_content('You must be a project member')
- expect(element).to have_content('improve the wiki for this project')
- expect(element).to have_link("issue tracker", href: project_issues_path(project))
- expect(element).to have_link("Suggest wiki improvement", href: new_project_issue_path(project))
- expect(element).to have_no_link(confluence_link)
- end
- end
-
- shared_examples 'empty wiki and non-accessible issues' do
- it 'does not show "issue tracker" message' do
- visit(project_wikis_path(project))
+RSpec.describe 'Project > User views empty wiki' do
+ let_it_be(:user) { create(:user) }
- expect(element).to have_content('This project has no wiki pages')
- expect(element).to have_content('You must be a project member')
- expect(element).to have_no_link('Suggest wiki improvement')
- expect(element).to have_no_link(confluence_link)
- end
- end
+ let(:wiki) { create(:project_wiki, project: project) }
- context 'when user is logged out and issue tracker is public' do
- let(:project) { create(:project, :public, :wiki_repo) }
+ it_behaves_like 'User views empty wiki' do
+ context 'when project is public' do
+ let(:project) { create(:project, :public) }
- it_behaves_like 'empty wiki and accessible issues'
- end
+ it_behaves_like 'empty wiki message', issuable: true
- context 'when user is logged in and not a member' do
- let(:project) { create(:project, :public, :wiki_repo) }
+ context 'when issue tracker is private' do
+ let(:project) { create(:project, :public, :issues_private) }
- before do
- sign_in(user)
- end
+ it_behaves_like 'empty wiki message', issuable: false
+ end
- it_behaves_like 'empty wiki and accessible issues'
- end
+ context 'when issue tracker is disabled' do
+ let(:project) { create(:project, :public, :issues_disabled) }
- context 'when issue tracker is private' do
- let(:project) { create(:project, :public, :wiki_repo, :issues_private) }
+ it_behaves_like 'empty wiki message', issuable: false
+ end
- it_behaves_like 'empty wiki and non-accessible issues'
- end
+ context 'and user is logged in' do
+ before do
+ sign_in(user)
+ end
- context 'when issue tracker is disabled' do
- let(:project) { create(:project, :public, :wiki_repo, :issues_disabled) }
+ context 'and user is not a member' do
+ it_behaves_like 'empty wiki message', issuable: true
+ end
- it_behaves_like 'empty wiki and non-accessible issues'
- end
+ context 'and user is a member' do
+ before do
+ project.add_developer(user)
+ end
- context 'when user is logged in and a member' do
- let(:project) { create(:project, :public) }
-
- before do
- sign_in(user)
- project.add_developer(user)
+ it_behaves_like 'empty wiki message', writable: true, issuable: true
+ end
+ end
end
- it 'shows "create first page" message' do
- visit(project_wikis_path(project))
-
- expect(element).to have_content('your project', count: 2)
+ context 'when project is private' do
+ let(:project) { create(:project, :private) }
- element.click_link 'Create your first page'
+ it_behaves_like 'wiki is not found'
- expect(page).to have_button('Create page')
- end
+ context 'and user is logged in' do
+ before do
+ sign_in(user)
+ end
- it 'does not show the "enable confluence" button' do
- visit(project_wikis_path(project))
+ context 'and user is not a member' do
+ it_behaves_like 'wiki is not found'
+ end
- expect(element).to have_no_link(confluence_link)
- end
- end
+ context 'and user is a member' do
+ before do
+ project.add_developer(user)
+ end
- context 'when user is logged in and an admin' do
- let(:project) { create(:project, :public, :wiki_repo) }
+ it_behaves_like 'empty wiki message', writable: true, issuable: true
+ end
- before do
- sign_in(user)
- project.add_maintainer(user)
- end
-
- it 'shows the "enable confluence" button' do
- visit(project_wikis_path(project))
-
- expect(element).to have_link(confluence_link)
- end
+ context 'and user is a maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
- it 'does not show "enable confluence" button if confluence is already enabled' do
- create(:confluence_service, project: project)
+ it_behaves_like 'empty wiki message', writable: true, issuable: true, confluence: true
- visit(project_wikis_path(project))
+ context 'and Confluence is already enabled' do
+ before do
+ create(:confluence_service, project: project)
+ end
- expect(element).to have_no_link(confluence_link)
+ it_behaves_like 'empty wiki message', writable: true, issuable: true, confluence: false
+ end
+ end
+ end
end
end
end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
deleted file mode 100644
index e93689af0aa..00000000000
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User views a wiki page' do
- include WikiHelpers
-
- let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
- let(:path) { 'image.png' }
- let(:wiki) { project.wiki }
- let(:wiki_page) do
- create(:wiki_page,
- wiki: wiki,
- title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})")
- end
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- context 'when wiki is empty', :js do
- before do
- visit project_wikis_path(project)
-
- wait_for_svg_to_be_loaded
-
- click_link "Create your first page"
-
- fill_in(:wiki_title, with: 'one/two/three-test')
-
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'wiki content')
- click_on('Create page')
- end
-
- expect(page).to have_content('Wiki was successfully updated.')
- end
-
- it 'shows the history of a page that has a path' do
- expect(current_path).to include('one/two/three-test')
-
- first(:link, text: 'three').click
- click_on('Page history')
-
- expect(current_path).to include('one/two/three-test')
-
- page.within(:css, '.nav-text') do
- expect(page).to have_content('History')
- end
- end
-
- it 'shows an old version of a page' do
- expect(current_path).to include('one/two/three-test')
- expect(find('.wiki-pages')).to have_content('three')
-
- first(:link, text: 'three').click
-
- expect(find('.nav-text')).to have_content('three')
-
- click_on('Edit')
-
- expect(current_path).to include('one/two/three-test')
- expect(page).to have_content('Edit Page')
-
- fill_in('Content', with: 'Updated Wiki Content')
- click_on('Save changes')
-
- expect(page).to have_content('Wiki was successfully updated.')
-
- click_on('Page history')
-
- within('.nav-text') do
- expect(page).to have_content('History')
- end
-
- within('.wiki-history') do
- expect(page).to have_css('a[href*="?version_id"]', count: 4)
- end
- end
- end
-
- context 'when a page does not have history' do
- before do
- visit(project_wiki_path(project, wiki_page))
- end
-
- it 'shows all the pages' do
- expect(page).to have_content(user.name)
- expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
- end
-
- context 'shows a file stored in a page' do
- let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
-
- it do
- expect(page).to have_xpath("//img[@data-src='#{wiki.wiki_base_path}/#{path}']")
- expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}")
-
- click_on('image')
-
- expect(current_path).to match("wikis/#{path}")
- expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
- end
- end
-
- it 'shows the creation page if file does not exist' do
- expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}")
-
- click_on('image')
-
- expect(current_path).to match("wikis/#{path}")
- expect(page).to have_content('Create New Page')
- end
- end
-
- context 'when a page has history' do
- before do
- wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') # rubocop:disable Rails/SaveBang
- end
-
- it 'shows the page history' do
- visit(project_wiki_path(project, wiki_page))
-
- expect(page).to have_selector('a.btn', text: 'Edit')
-
- click_on('Page history')
-
- expect(page).to have_content(user.name)
- expect(page).to have_content("#{user.username} created page: home")
- expect(page).to have_content('updated home')
- end
-
- it 'does not show the "Edit" button' do
- visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id))
-
- expect(page).not_to have_selector('a.btn', text: 'Edit')
- end
-
- context 'show the diff' do
- def expect_diff_links(commit)
- diff_path = wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
-
- expect(page).to have_link('Hide whitespace changes', href: "#{diff_path}&w=1")
- expect(page).to have_link('Inline', href: "#{diff_path}&view=inline")
- expect(page).to have_link('Side-by-side', href: "#{diff_path}&view=parallel")
- expect(page).to have_link("View page @ #{commit.short_id}", href: wiki_page_path(wiki, wiki_page, version_id: commit))
- expect(page).to have_css('.diff-file[data-blob-diff-path="%s"]' % diff_path)
- end
-
- it 'links to the correct diffs' do
- visit project_wiki_history_path(project, wiki_page)
-
- commit1 = wiki.commit('HEAD^')
- commit2 = wiki.commit
-
- expect(page).to have_link('created page: home', href: wiki_page_path(wiki, wiki_page, version_id: commit1, action: :diff))
- expect(page).to have_link('updated home', href: wiki_page_path(wiki, wiki_page, version_id: commit2, action: :diff))
- end
-
- it 'between the current and the previous version of a page' do
- commit = wiki.commit
- visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
-
- expect(page).to have_content('by John Doe')
- expect(page).to have_content('updated home')
- expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions')
- expect(page).to have_content('some link')
-
- expect_diff_links(commit)
- end
-
- it 'between two old versions of a page' do
- wiki_page.update(message: 'latest home change', content: 'updated [another link](other-page)') # rubocop:disable Rails/SaveBang:
- commit = wiki.commit('HEAD^')
- visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
-
- expect(page).to have_content('by John Doe')
- expect(page).to have_content('updated home')
- expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions')
- expect(page).to have_content('some link')
- expect(page).not_to have_content('latest home change')
- expect(page).not_to have_content('another link')
-
- expect_diff_links(commit)
- end
-
- it 'for the oldest version of a page' do
- commit = wiki.commit('HEAD^')
- visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff)
-
- expect(page).to have_content('by John Doe')
- expect(page).to have_content('created page: home')
- expect(page).to have_content('Showing 1 changed file with 4 additions and 0 deletions')
- expect(page).to have_content('Look at this')
-
- expect_diff_links(commit)
- end
- end
- end
-
- context 'when a page has special characters in its title' do
- let(:title) { '<foo> !@#$%^&*()[]{}=_+\'"\\|<>? <bar>' }
-
- before do
- wiki_page.update(title: title ) # rubocop:disable Rails/SaveBang
- end
-
- it 'preserves the special characters' do
- visit(project_wiki_path(project, wiki_page))
-
- expect(page).to have_css('.wiki-page-title', text: title)
- expect(page).to have_css('.wiki-pages li', text: title)
- end
- end
-
- context 'when a page has XSS in its title or content' do
- let(:title) { '<script>alert("title")<script>' }
-
- before do
- wiki_page.update(title: title, content: 'foo <script>alert("content")</script> bar') # rubocop:disable Rails/SaveBang
- end
-
- it 'safely displays the page' do
- visit(project_wiki_path(project, wiki_page))
-
- expect(page).to have_css('.wiki-page-title', text: title)
- expect(page).to have_content('foo bar')
- end
- end
-
- context 'when a page has XSS in its message' do
- before do
- wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update') # rubocop:disable Rails/SaveBang
- end
-
- it 'safely displays the message' do
- visit(project_wiki_history_path(project, wiki_page))
-
- expect(page).to have_content('<script>alert(true)<script>')
- end
- end
-
- context 'when page has invalid content encoding' do
- let(:content) { (+'whatever').force_encoding('ISO-8859-1') }
-
- before do
- allow(Gitlab::EncodingHelper).to receive(:encode!).and_return(content)
-
- visit(project_wiki_path(project, wiki_page))
- end
-
- it 'does not show "Edit" button' do
- expect(page).not_to have_selector('a.btn', text: 'Edit')
- end
-
- it 'shows error' do
- page.within(:css, '.flash-notice') do
- expect(page).to have_content('The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.')
- end
- end
- end
-
- it 'opens a default wiki page', :js do
- visit project_path(project)
-
- find('.shortcuts-wiki').click
-
- wait_for_svg_to_be_loaded
-
- click_link "Create your first page"
-
- expect(page).to have_content('Create New Page')
- end
-end
diff --git a/spec/features/projects/wiki/user_views_wiki_pages_spec.rb b/spec/features/projects/wiki/user_views_wiki_pages_spec.rb
deleted file mode 100644
index 4f29ae0cc8a..00000000000
--- a/spec/features/projects/wiki/user_views_wiki_pages_spec.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User views wiki pages' do
- include WikiHelpers
-
- let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
-
- let!(:wiki_page1) do
- create(:wiki_page, wiki: project.wiki, title: '3 home', content: '3')
- end
-
- let!(:wiki_page2) do
- create(:wiki_page, wiki: project.wiki, title: '1 home', content: '1')
- end
-
- let!(:wiki_page3) do
- create(:wiki_page, wiki: project.wiki, title: '2 home', content: '2')
- end
-
- let(:pages) do
- page.find('.wiki-pages-list').all('li').map { |li| li.find('a') }
- end
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- visit(project_wikis_pages_path(project))
- end
-
- context 'ordered by title' do
- let(:pages_ordered_by_title) { [wiki_page2, wiki_page3, wiki_page1] }
-
- context 'asc' do
- it 'pages are displayed in direct order' do
- pages.each.with_index do |page_title, index|
- expect(page_title.text).to eq(pages_ordered_by_title[index].title)
- end
- end
- end
-
- context 'desc' do
- before do
- page.within('.wiki-sort-dropdown') do
- page.find('.rspec-reverse-sort').click
- end
- end
-
- it 'pages are displayed in reversed order' do
- pages.reverse_each.with_index do |page_title, index|
- expect(page_title.text).to eq(pages_ordered_by_title[index].title)
- end
- end
- end
- end
-
- context 'ordered by created_at' do
- let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3] }
-
- before do
- page.within('.wiki-sort-dropdown') do
- click_button('Title')
- click_link('Created date')
- end
- end
-
- context 'asc' do
- it 'pages are displayed in direct order' do
- pages.each.with_index do |page_title, index|
- expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
- end
- end
- end
-
- context 'desc' do
- before do
- page.within('.wiki-sort-dropdown') do
- page.find('.rspec-reverse-sort').click
- end
- end
-
- it 'pages are displayed in reversed order' do
- pages.reverse_each.with_index do |page_title, index|
- expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
- end
- end
- end
- end
-end
diff --git a/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb b/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb
deleted file mode 100644
index 5c45e34595f..00000000000
--- a/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User views AsciiDoc page with includes', :js do
- let_it_be(:user) { create(:user) }
- let_it_be(:wiki_content_selector) { '[data-qa-selector=wiki_page_content]' }
- let(:project) { create(:project, :public, :wiki_repo) }
- let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page')}
- let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") }
-
- def create_wiki_page(title, content:)
- attrs = {
- title: title,
- content: content,
- format: :asciidoc
- }
-
- create(:wiki_page, wiki: project.wiki, **attrs)
- end
-
- before do
- sign_in(user)
- end
-
- context 'when the file being included exists' do
- it 'includes the file contents' do
- visit(project_wiki_path(project, wiki_page))
-
- page.within(:css, wiki_content_selector) do
- expect(page).to have_content('Content from the main page. Content from the included page')
- end
- end
-
- context 'when there are multiple versions of the wiki pages' do
- before do
- included_wiki_page.update(message: 'updated included file', content: 'Updated content from the included page')
- wiki_page.update(message: 'updated wiki page', content: "Updated content from the main page.\ninclude::included_page.asciidoc[]")
- end
-
- let(:latest_version_id) { wiki_page.versions.first.id }
- let(:oldest_version_id) { wiki_page.versions.last.id }
-
- context 'viewing the latest version' do
- it 'includes the latest content' do
- visit(project_wiki_path(project, wiki_page, version_id: latest_version_id))
-
- page.within(:css, wiki_content_selector) do
- expect(page).to have_content('Updated content from the main page. Updated content from the included page')
- end
- end
- end
-
- context 'viewing the original version' do
- it 'includes the content from the original version' do
- visit(project_wiki_path(project, wiki_page, version_id: oldest_version_id))
-
- page.within(:css, wiki_content_selector) do
- expect(page).to have_content('Content from the main page. Content from the included page')
- end
- end
- end
- end
- end
-
- context 'when the file being included does not exist' do
- before do
- included_wiki_page.delete
- end
-
- it 'outputs an error' do
- visit(project_wiki_path(project, wiki_page))
-
- page.within(:css, wiki_content_selector) do
- expect(page).to have_content('Content from the main page. [ERROR: include::included_page.asciidoc[] - unresolved directive]')
- end
- end
- end
-end
diff --git a/spec/features/projects/wikis_spec.rb b/spec/features/projects/wikis_spec.rb
new file mode 100644
index 00000000000..1c66ad81145
--- /dev/null
+++ b/spec/features/projects/wikis_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe 'Project wikis' do
+ let_it_be(:user) { create(:user) }
+
+ let(:wiki) { create(:project_wiki, user: user, project: project) }
+ let(:project) { create(:project, namespace: user.namespace, creator: user) }
+
+ it_behaves_like 'User creates wiki page'
+ it_behaves_like 'User deletes wiki page'
+ it_behaves_like 'User previews wiki changes'
+ it_behaves_like 'User updates wiki page'
+ it_behaves_like 'User uses wiki shortcuts'
+ it_behaves_like 'User views AsciiDoc page with includes'
+ it_behaves_like 'User views a wiki page'
+ it_behaves_like 'User views wiki pages'
+ it_behaves_like 'User views wiki sidebar'
+end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 970500985ae..6baeb4ce368 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -6,25 +6,35 @@ RSpec.describe 'Project' do
include ProjectForksHelper
include MobileHelpers
- describe 'creating from template' do
+ describe 'template' do
let(:user) { create(:user) }
- let(:template) { Gitlab::ProjectTemplate.find(:rails) }
before do
sign_in user
visit new_project_path
end
- it "allows creation from templates", :js do
- find('#create-from-template-tab').click
- find("label[for=#{template.name}]").click
- fill_in("project_name", with: template.name)
+ shared_examples 'creates from template' do |template, sub_template_tab = nil|
+ it "is created from template", :js do
+ find('#create-from-template-tab').click
+ find(".project-template #{sub_template_tab}").click if sub_template_tab
+ find("label[for=#{template.name}]").click
+ fill_in("project_name", with: template.name)
- page.within '#content-body' do
- click_button "Create project"
+ page.within '#content-body' do
+ click_button "Create project"
+ end
+
+ expect(page).to have_content template.name
end
+ end
- expect(page).to have_content template.name
+ context 'create with project template' do
+ it_behaves_like 'creates from template', Gitlab::ProjectTemplate.find(:rails)
+ end
+
+ context 'create with sample data template' do
+ it_behaves_like 'creates from template', Gitlab::SampleDataTemplate.find(:basic), '.sample-data-templates-tab'
end
end
@@ -99,6 +109,15 @@ RSpec.describe 'Project' do
expect(page).to have_css('.home-panel-description .is-expanded')
end
end
+
+ context 'page description' do
+ before do
+ project.update_attribute(:description, '**Lorem** _ipsum_ dolor sit [amet](https://example.com)')
+ visit path
+ end
+
+ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
+ end
end
describe 'project topics' do
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index f0707610c3f..3be01595502 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -9,6 +9,10 @@ RSpec.describe 'Protected Branches', :js do
let(:admin) { create(:admin) }
let(:project) { create(:project, :repository) }
+ before do
+ stub_feature_flags(deploy_keys_on_protected_branches: false)
+ end
+
context 'logged in as developer' do
before do
project.add_developer(user)
@@ -27,7 +31,7 @@ RSpec.describe 'Protected Branches', :js do
fill_in 'branch-search', with: 'fix'
find('#branch-search').native.send_keys(:enter)
- expect(page).to have_css('.btn-remove.disabled')
+ expect(page).to have_css('.btn-danger.disabled')
end
end
end
@@ -163,4 +167,14 @@ RSpec.describe 'Protected Branches', :js do
include_examples "protected branches > access control > CE"
end
end
+
+ context 'when the users for protected branches feature is off' do
+ before do
+ stub_licensed_features(protected_refs_for_users: false)
+ end
+
+ include_examples 'when the deploy_keys_on_protected_branches FF is turned on' do
+ let(:all_dropdown_sections) { %w(Roles Deploy\ Keys) }
+ end
+ end
end
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index 4d61e5d8285..92bf304ac86 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe 'Reportable note on snippets', :js do
let_it_be(:project) { create(:project) }
before do
- stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 0dff4c28270..6e18de3be7b 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -173,9 +173,9 @@ RSpec.describe 'Runners' do
it 'user enables shared runners' do
visit project_runners_path(project)
- click_on 'Enable shared Runners'
+ click_on 'Enable shared runners'
- expect(page.find('.shared-runners-description')).to have_content('Disable shared Runners')
+ expect(page.find('.shared-runners-description')).to have_content('Disable shared runners')
end
end
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index 227e75088d2..a88043c98ac 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe 'User searches for code' do
expect(page).to have_selector('.results', text: 'application.js')
expect(page).to have_selector('.file-content .code')
expect(page).to have_selector("span.line[lang='javascript']")
+ expect(page).to have_link('application.js', href: /master\/files\/js\/application.js/)
end
context 'when on a project page', :js do
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 cfda25b9ab4..5cbfacf4e48 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -30,6 +30,8 @@ RSpec.describe 'User uses header search field', :js do
before do
find('#search')
find('body').native.send_keys('s')
+
+ wait_for_all_requests
end
it 'shows the category search dropdown' do
@@ -89,9 +91,7 @@ RSpec.describe 'User uses header search field', :js do
context 'when entering text into the search field' do
it 'does not display the category search dropdown' do
- page.within('.search-input-wrap') do
- fill_in('search', with: scope_name.first(4))
- end
+ fill_in_search(scope_name.first(4))
expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i)
end
@@ -105,9 +105,7 @@ RSpec.describe 'User uses header search field', :js do
end
it 'displays search options' do
- page.within('.search-input-wrap') do
- fill_in('search', with: 'test')
- end
+ fill_in_search('test')
expect(page).to have_selector(scoped_search_link('test'))
end
@@ -140,9 +138,7 @@ RSpec.describe 'User uses header search field', :js do
end
it 'displays search options' do
- page.within('.search-input-wrap') do
- fill_in('search', with: 'test')
- end
+ fill_in_search('test')
expect(page).to have_selector(scoped_search_link('test'))
expect(page).to have_selector(scoped_search_link('test', group_id: group.id))
@@ -157,9 +153,7 @@ RSpec.describe 'User uses header search field', :js do
end
it 'displays search options' do
- page.within('.search-input-wrap') do
- fill_in('search', with: 'test')
- end
+ fill_in_search('test')
expect(page).to have_selector(scoped_search_link('test'))
expect(page).not_to have_selector(scoped_search_link('test', group_id: project.namespace_id))
@@ -182,9 +176,7 @@ RSpec.describe 'User uses header search field', :js do
end
it 'displays search options' do
- page.within('.search-input-wrap') do
- fill_in('search', with: 'test')
- end
+ fill_in_search('test')
expect(page).to have_selector(scoped_search_link('test'))
expect(page).to have_selector(scoped_search_link('test', group_id: group.id))
@@ -208,9 +200,7 @@ RSpec.describe 'User uses header search field', :js do
end
it 'displays search options' do
- page.within('.search-input-wrap') do
- fill_in('search', with: 'test')
- end
+ fill_in_search('test')
expect(page).to have_selector(scoped_search_link('test'))
expect(page).to have_selector(scoped_search_link('test', group_id: subgroup.id))
diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb
index f39a1f8fe37..080cced21c3 100644
--- a/spec/features/search/user_uses_search_filters_spec.rb
+++ b/spec/features/search/user_uses_search_filters_spec.rb
@@ -12,12 +12,12 @@ RSpec.describe 'User uses search filters', :js do
project.add_reporter(user)
group.add_owner(user)
sign_in(user)
-
- visit(search_path)
end
context 'when filtering by group' do
it 'shows group projects' do
+ visit search_path
+
find('.js-search-group-dropdown').click
wait_for_requests
@@ -36,10 +36,27 @@ RSpec.describe 'User uses search filters', :js do
expect(page).to have_link(group_project.full_name)
end
end
+
+ context 'when the group filter is set' do
+ before do
+ visit search_path(search: "test", group_id: group.id, project_id: project.id)
+ end
+
+ describe 'clear filter button' do
+ it 'removes Group and Project filters' do
+ link = find('[data-testid="group-filter"] .js-search-clear')
+ params = CGI.parse(URI.parse(link[:href]).query)
+
+ expect(params).not_to include(:group_id, :project_id)
+ end
+ end
+ end
end
context 'when filtering by project' do
it 'shows a project' do
+ visit search_path
+
page.within('.project-filter') do
find('.js-search-project-dropdown').click
@@ -50,5 +67,22 @@ RSpec.describe 'User uses search filters', :js do
expect(find('.js-search-project-dropdown')).to have_content(project.full_name)
end
+
+ context 'when the project filter is set' do
+ before do
+ visit search_path(search: "test", project_id: project.id)
+ end
+
+ let(:query) { { project_id: project.id } }
+
+ describe 'clear filter button' do
+ it 'removes Project filters' do
+ link = find('.project-filter .js-search-clear')
+ params = CGI.parse(URI.parse(link[:href]).query)
+
+ expect(params).not_to include(:project_id)
+ end
+ end
+ end
end
end
diff --git a/spec/features/sentry_js_spec.rb b/spec/features/sentry_js_spec.rb
index 1d277ba7b3c..aa0ad17340a 100644
--- a/spec/features/sentry_js_spec.rb
+++ b/spec/features/sentry_js_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Sentry' do
expect(has_requested_sentry).to eq(false)
end
- it 'loads sentry if sentry is enabled' do
+ xit 'loads sentry if sentry is enabled' do
stub_sentry_settings
visit new_user_session_path
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
index 3ce297ab22d..2fcd11c2a47 100644
--- a/spec/features/snippets/internal_snippet_spec.rb
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -3,11 +3,8 @@
require 'spec_helper'
RSpec.describe 'Internal Snippets', :js do
- let(:internal_snippet) { create(:personal_snippet, :internal) }
-
- before do
- stub_feature_flags(snippets_vue: false)
- end
+ let(:internal_snippet) { create(:personal_snippet, :internal, :repository) }
+ let(:content) { internal_snippet.blobs.first.data.strip! }
describe 'normal user' do
before do
@@ -17,13 +14,13 @@ RSpec.describe 'Internal Snippets', :js do
it 'sees internal snippets' do
visit snippet_path(internal_snippet)
- expect(page).to have_content(internal_snippet.content)
+ expect(page).to have_content(content)
end
it 'sees raw internal snippets' do
visit raw_snippet_path(internal_snippet)
- expect(page).to have_content(internal_snippet.content)
+ expect(page).to have_content(content)
end
end
end
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index e98bb22d3ea..ce9a2d1461e 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -18,7 +18,6 @@ RSpec.describe 'Comments on personal snippets', :js do
end
before do
- stub_feature_flags(snippets_vue: false)
sign_in user
visit snippet_path(snippet)
diff --git a/spec/features/snippets/private_snippets_spec.rb b/spec/features/snippets/private_snippets_spec.rb
index 6b45f3485e7..03745c1025a 100644
--- a/spec/features/snippets/private_snippets_spec.rb
+++ b/spec/features/snippets/private_snippets_spec.rb
@@ -4,19 +4,18 @@ require 'spec_helper'
RSpec.describe 'Private Snippets', :js do
let(:user) { create(:user) }
+ let(:private_snippet) { create(:personal_snippet, :repository, :private, author: user) }
+ let(:content) { private_snippet.blobs.first.data.strip! }
before do
- stub_feature_flags(snippets_vue: false)
sign_in(user)
end
it 'Private Snippet renders for creator' do
- private_snippet = create(:personal_snippet, :private, author: user)
-
visit snippet_path(private_snippet)
wait_for_requests
- expect(page).to have_content(private_snippet.content)
+ expect(page).to have_content(content)
expect(page).not_to have_css('.js-embed-btn')
expect(page).not_to have_css('.js-share-btn')
end
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index 4b72b33245d..d2dc85a9614 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -3,27 +3,24 @@
require 'spec_helper'
RSpec.describe 'Public Snippets', :js do
- before do
- stub_feature_flags(snippets_vue: false)
- end
+ let(:public_snippet) { create(:personal_snippet, :public, :repository) }
+ let(:content) { public_snippet.blobs.first.data.strip! }
it 'Unauthenticated user should see public snippets' do
- public_snippet = create(:personal_snippet, :public)
+ url = Gitlab::UrlBuilder.build(public_snippet)
visit snippet_path(public_snippet)
wait_for_requests
- expect(page).to have_content(public_snippet.content)
- expect(page).to have_css('.js-embed-btn', visible: false)
- expect(page).to have_css('.js-share-btn', visible: false)
- expect(page.find('.js-snippet-url-area')).to be_readonly
+ expect(page).to have_content(content)
+ click_button('Embed')
+ expect(page).to have_field('Embed', readonly: true, with: "<script src=\"#{url}.js\"></script>")
+ expect(page).to have_field('Share', readonly: true, with: url)
end
it 'Unauthenticated user should see raw public snippets' do
- public_snippet = create(:personal_snippet, :public)
-
visit raw_snippet_path(public_snippet)
- expect(page).to have_content(public_snippet.content)
+ expect(page).to have_content(content)
end
end
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index 981ed12d540..2103d362f94 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -6,10 +6,6 @@ RSpec.describe 'Snippet', :js do
let_it_be(:user) { create(:user) }
let_it_be(:snippet) { create(:personal_snippet, :public, :repository, author: user) }
- before do
- stub_feature_flags(snippets_vue: false)
- end
-
it_behaves_like 'show and render proper snippet blob' do
let(:anchor) { nil }
@@ -20,12 +16,8 @@ RSpec.describe 'Snippet', :js do
end
end
- it_behaves_like 'showing user status' do
- let(:file_path) { 'files/ruby/popen.rb' }
- let(:user_with_status) { snippet.author }
-
- subject { visit snippet_path(snippet) }
- end
+ # it_behaves_like 'showing user status' do
+ # This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394
it_behaves_like 'does not show New Snippet button' do
let(:file_path) { 'files/ruby/popen.rb' }
diff --git a/spec/features/snippets/spam_snippets_spec.rb b/spec/features/snippets/spam_snippets_spec.rb
index 1483ba4bf8f..54a56ac962c 100644
--- a/spec/features/snippets/spam_snippets_spec.rb
+++ b/spec/features/snippets/spam_snippets_spec.rb
@@ -2,9 +2,11 @@
require 'spec_helper'
-RSpec.shared_examples_for 'snippet editor' do
+RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/217722" do
include_context 'includes Spam constants'
+ let_it_be(:user) { create(:user) }
+
def description_field
find('.js-description-input').find('input,textarea')
end
@@ -119,24 +121,3 @@ RSpec.shared_examples_for 'snippet editor' do
end
end
end
-
-RSpec.describe 'User creates snippet', :js do
- let_it_be(:user) { create(:user) }
-
- 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 eabca028b8c..1e51210c2b8 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -13,163 +13,127 @@ RSpec.describe 'User creates snippet', :js do
let(:md_description) { 'My Snippet **Description**' }
let(:description) { 'My Snippet Description' }
let(:created_snippet) { Snippet.last }
- let(:snippet_title_field) { 'personal_snippet_title' }
+ let(:snippet_title_field) { 'snippet-title' }
- def description_field
- find('.js-description-input').find('input,textarea')
+ before do
+ sign_in(user)
+
+ visit new_snippet_path
end
- shared_examples 'snippet creation' do
- def fill_form
- snippet_fill_in_form(title: title, content: file_content, description: md_description)
- end
+ def fill_form
+ snippet_fill_in_form(title: title, content: file_content, description: md_description)
+ end
- it 'Authenticated user creates a snippet' do
- 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_description_view_selector) do
- expect(page).to have_content(description)
- expect(page).to have_selector('strong')
- end
- expect(page).to have_content(file_content)
+ 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
- it 'uploads a file when dragging into textarea' do
- fill_form
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
-
- expect(snippet_description_value).to have_content('banana_sample')
-
- 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/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
+ it 'uploads a file when dragging into textarea' do
+ fill_form
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
- expect(reqs.first.status_code).to eq(200)
- end
+ expect(snippet_description_value).to have_content('banana_sample')
- 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)
- 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})
- fill_form
- click_button('Create snippet')
- wait_for_requests
- end
+ reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
+ expect(reqs.first.status_code).to eq(200)
+ end
- it 'renders the new page and displays the error' do
- expect(page).to have_content(error)
- expect(page).to have_content('New Snippet')
+ context 'when the git operation fails' do
+ let(:error) { 'Error creating the snippet' }
- action = find('form.snippet-form')['action']
- expect(action).to include("/snippets")
- end
- end
-
- 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)
+ before do
+ allow_next_instance_of(Snippets::CreateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError, error)
end
- it 'creates a snippet using the lowest available visibility level as default' do
- visit new_snippet_path
-
- fill_form
-
- click_button('Create snippet')
- wait_for_requests
-
- expect(find('.blob-content')).to have_content(file_content)
- expect(Snippet.last.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
- end
+ fill_form
+ click_button('Create snippet')
+ wait_for_requests
end
- it_behaves_like 'personal snippet with references' do
- let(:container) { snippet_description_view_selector }
- let(:md_description) { references }
+ it 'renders the new page and displays the error' do
+ expect(page).to have_content(error)
+ expect(page).to have_content('New Snippet')
- subject do
- fill_form
- click_button('Create snippet')
-
- wait_for_requests
- end
+ action = find('form.snippet-form')['action']
+ expect(action).to include("/snippets")
end
end
- context 'Vue application' do
- let(:snippet_description_field) { 'snippet-description' }
- let(:snippet_description_view_selector) { '.snippet-header .snippet-description' }
-
+ context 'when snippets default visibility level is restricted' do
before do
- sign_in(user)
-
- visit new_snippet_path
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE],
+ default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
end
- it_behaves_like 'snippet creation'
+ it 'creates a snippet using the lowest available visibility level as default' do
+ visit new_snippet_path
- it 'validation fails for the first time' do
- fill_in snippet_title_field, with: title
+ fill_form
- expect(page).not_to have_button('Create snippet')
+ click_button('Create snippet')
+ wait_for_requests
- snippet_fill_in_form(title: title, content: file_content)
- expect(page).to have_button('Create snippet')
+ expect(find('.blob-content')).to have_content(file_content)
+ expect(Snippet.last.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
end
- context 'non-Vue application' do
- let(:snippet_description_field) { 'personal_snippet_description' }
- let(:snippet_description_view_selector) { '.snippet-header .description' }
+ it_behaves_like 'personal snippet with references' do
+ let(:container) { snippet_description_view_selector }
+ let(:md_description) { references }
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
-
- sign_in(user)
+ subject do
+ fill_form
+ click_button('Create snippet')
- visit new_snippet_path
+ wait_for_requests
end
+ end
- it_behaves_like 'snippet creation'
+ it 'validation fails for the first time' do
+ fill_in snippet_title_field, with: title
- it 'validation fails for the first time' do
- fill_in snippet_title_field, with: title
- click_button('Create snippet')
+ expect(page).not_to have_button('Create snippet')
- expect(page).to have_selector('#error_explanation')
- end
+ snippet_fill_in_form(title: title, content: file_content)
+ expect(page).to have_button('Create snippet')
+ end
- 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
+ it 'previews a snippet with file' do
+ # Click placeholder first to expand full description field
+ snippet_fill_in_description('My Snippet')
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ find('.js-md-preview-button').click
- page.within('.md-preview-holder') do
- expect(page).to have_content('My Snippet')
+ page.within('.md-preview-holder') do
+ expect(page).to have_content('My Snippet')
- 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})
+ 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
+ # 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.
+ # accept_confirm is needed because of https://gitlab.com/gitlab-org/gitlab/-/issues/262102
+ reqs = inspect_requests { accept_confirm { visit("#{link}?ran=#{SecureRandom.base64(20)}") } }
+ expect(reqs.first.status_code).to eq(200)
end
end
end
diff --git a/spec/features/snippets/user_deletes_snippet_spec.rb b/spec/features/snippets/user_deletes_snippet_spec.rb
index d7cfc67df13..e896f7eb25b 100644
--- a/spec/features/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/snippets/user_deletes_snippet_spec.rb
@@ -2,21 +2,23 @@
require 'spec_helper'
-RSpec.describe 'User deletes snippet' do
+RSpec.describe 'User deletes snippet', :js do
let(:user) { create(:user) }
let(:content) { 'puts "test"' }
- let(:snippet) { create(:personal_snippet, :public, content: content, author: user) }
+ let(:snippet) { create(:personal_snippet, :repository, :public, content: content, author: user) }
before do
sign_in(user)
- stub_feature_flags(snippets_vue: false)
-
visit snippet_path(snippet)
end
it 'deletes the snippet' do
- first(:link, 'Delete').click
+ expect(page).to have_content(snippet.title)
+
+ click_button('Delete')
+ click_button('Delete snippet')
+ wait_for_requests
expect(page).not_to have_content(snippet.title)
end
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index 9a83eb58b63..a04c59b53d2 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -11,107 +11,77 @@ RSpec.describe 'User edits snippet', :js do
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) }
- let(:snippet_title_field) { 'personal_snippet_title' }
+ before do
+ sign_in(user)
- shared_examples 'snippet editing' do
- it 'displays the snippet blob path and content' do
- blob = snippet.blobs.first
-
- 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 'updates the snippet' do
- fill_in snippet_title_field, with: 'New Snippet Title'
-
- click_button('Save changes')
- wait_for_requests
-
- expect(page).to have_content('New Snippet Title')
- end
-
- 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')
+ visit edit_snippet_path(snippet)
+ wait_for_all_requests
+ end
- click_button('Save changes')
- wait_for_requests
+ it 'displays the snippet blob path and content' do
+ blob = snippet.blobs.first
- 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})
+ 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 'updates the snippet to make it internal' do
- choose 'Internal'
-
- click_button 'Save changes'
- wait_for_requests
+ it 'updates the snippet' do
+ snippet_fill_in_title('New Snippet Title')
+ expect(page).not_to have_selector('.gl-spinner')
- expect(page).to have_no_selector('[data-testid="lock-icon"]')
- expect(page).to have_selector('[data-testid="shield-icon"]')
- end
+ click_button('Save changes')
+ wait_for_requests
- it 'updates the snippet to make it public' do
- choose 'Public'
+ expect(page).to have_content('New Snippet Title')
+ end
- click_button 'Save changes'
- wait_for_requests
+ 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')
- expect(page).to have_no_selector('[data-testid="lock-icon"]')
- expect(page).to have_selector('[data-testid="earth-icon"]')
- end
+ click_button('Save changes')
+ wait_for_requests
- 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
+ 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
- fill_in snippet_title_field, with: 'New Snippet Title'
- fill_in snippet_blob_path_field, with: 'new_file_name', match: :first
+ it 'updates the snippet to make it internal' do
+ snippet_fill_in_visibility('Internal')
- click_button('Save changes')
- end
+ click_button 'Save changes'
+ wait_for_requests
- 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
+ expect(page).to have_no_selector('[data-testid="lock-icon"]')
+ expect(page).to have_selector('[data-testid="shield-icon"]')
end
- 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' }
+ it 'updates the snippet to make it public' do
+ snippet_fill_in_visibility('Public')
- before do
- sign_in(user)
+ click_button 'Save changes'
+ wait_for_requests
- visit edit_snippet_path(snippet)
- wait_for_all_requests
- end
- end
+ expect(page).to have_no_selector('[data-testid="lock-icon"]')
+ expect(page).to have_selector('[data-testid="earth-icon"]')
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' }
+ 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
- before do
- stub_feature_flags(snippets_vue: false)
- stub_feature_flags(snippets_edit_vue: false)
+ snippet_fill_in_form(title: 'New Snippet Title', file_name: 'new_file_name')
- sign_in(user)
+ click_button('Save changes')
+ end
- visit edit_snippet_path(snippet)
- wait_for_all_requests
- end
+ 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
diff --git a/spec/features/snippets_spec.rb b/spec/features/snippets_spec.rb
index 75309ca3e7c..8cdb4bc3344 100644
--- a/spec/features/snippets_spec.rb
+++ b/spec/features/snippets_spec.rb
@@ -18,11 +18,8 @@ RSpec.describe 'Snippets' do
describe 'rendering engine' do
let_it_be(:snippet) { create(:personal_snippet, :public) }
- let(:snippets_vue_feature_flag_enabled) { true }
before do
- stub_feature_flags(snippets_vue: snippets_vue_feature_flag_enabled)
-
visit snippet_path(snippet)
end
@@ -30,14 +27,5 @@ RSpec.describe 'Snippets' do
expect(page).to have_selector('#js-snippet-view')
expect(page).not_to have_selector('.personal-snippets')
end
-
- context 'when feature flag is disabled' do
- let(:snippets_vue_feature_flag_enabled) { false }
-
- it 'renders HAML application and not Vue' do
- expect(page).not_to have_selector('#js-snippet-view')
- expect(page).to have_selector('.personal-snippets')
- end
- end
end
end
diff --git a/spec/features/static_site_editor_spec.rb b/spec/features/static_site_editor_spec.rb
index b67e47b6ac4..03085917d67 100644
--- a/spec/features/static_site_editor_spec.rb
+++ b/spec/features/static_site_editor_spec.rb
@@ -6,18 +6,71 @@ RSpec.describe 'Static Site Editor' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
+ let(:sse_path) { project_show_sse_path(project, 'master/README.md') }
+
+ before_all do
+ project.add_developer(user)
+ end
+
before do
- project.add_maintainer(user)
sign_in(user)
+ end
+
+ context "when no config file is present" do
+ before do
+ visit sse_path
+ end
- visit project_show_sse_path(project, 'master/README.md')
+ it 'renders SSE page with all generated config values and default config file values' do
+ node = page.find('#static-site-editor')
+
+ # assert generated config values are present
+ expect(node['data-base-url']).to eq("/#{project.full_path}/-/sse/master%2FREADME.md")
+ expect(node['data-branch']).to eq('master')
+ expect(node['data-commit-id']).to match(/\A[0-9a-f]{40}\z/)
+ expect(node['data-is-supported-content']).to eq('true')
+ expect(node['data-merge-requests-illustration-path'])
+ .to match(%r{/assets/illustrations/merge_requests-.*\.svg})
+ expect(node['data-namespace']).to eq(project.namespace.full_path)
+ expect(node['data-project']).to eq(project.path)
+ expect(node['data-project-id']).to eq(project.id.to_s)
+
+ # assert default config file values are present
+ expect(node['data-image-upload-path']).to eq('source/images')
+ expect(node['data-mounts']).to eq('[{"source":"source","target":""}]')
+ expect(node['data-static-site-generator']).to eq('middleman')
+ end
end
- 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"]')
+ context "when a config file is present" do
+ let(:config_file_yml) do
+ <<~YAML
+ image_upload_path: custom-image-upload-path
+ mounts:
+ - source: source1
+ target: ""
+ - source: source2
+ target: target2
+ static_site_generator: middleman
+ YAML
+ end
+
+ before do
+ allow_next_instance_of(Repository) do |repository|
+ allow(repository).to receive(:blob_data_at).and_return(config_file_yml)
+ end
+
+ visit sse_path
+ end
+
+ it 'renders Static Site Editor page values read from config file' do
+ node = page.find('#static-site-editor')
- # assert file config value is present
- expect(page).to have_css('#static-site-editor[data-static-site-generator="middleman"]')
+ # assert user-specified config file values are present
+ expected_mounts = '[{"source":"source1","target":""},{"source":"source2","target":"target2"}]'
+ expect(node['data-image-upload-path']).to eq('custom-image-upload-path')
+ expect(node['data-mounts']).to eq(expected_mounts)
+ expect(node['data-static-site-generator']).to eq('middleman')
+ end
end
end
diff --git a/spec/features/tags/developer_deletes_tag_spec.rb b/spec/features/tags/developer_deletes_tag_spec.rb
index de9296bc08e..7c4c6f54685 100644
--- a/spec/features/tags/developer_deletes_tag_spec.rb
+++ b/spec/features/tags/developer_deletes_tag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Developer deletes tag' do
+RSpec.describe 'Developer deletes tag', :js do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
@@ -13,11 +13,12 @@ RSpec.describe 'Developer deletes tag' do
visit project_tags_path(project)
end
- context 'from the tags list page', :js do
+ context 'from the tags list page' do
it 'deletes the tag' do
expect(page).to have_content 'v1.1.0'
- delete_tag 'v1.1.0'
+ container = page.find('.content .flex-row', text: 'v1.1.0')
+ delete_tag container
expect(page).not_to have_content 'v1.1.0'
end
@@ -29,15 +30,15 @@ RSpec.describe 'Developer deletes tag' do
expect(current_path).to eq(
project_tag_path(project, 'v1.0.0'))
- click_on 'Delete tag'
+ container = page.find('.nav-controls')
+ delete_tag container
- expect(current_path).to eq(
- project_tags_path(project))
+ expect(current_path).to eq("#{project_tags_path(project)}/")
expect(page).not_to have_content 'v1.0.0'
end
end
- context 'when pre-receive hook fails', :js do
+ context 'when pre-receive hook fails' do
before do
allow_next_instance_of(Gitlab::GitalyClient::OperationService) do |instance|
allow(instance).to receive(:rm_tag)
@@ -46,15 +47,17 @@ RSpec.describe 'Developer deletes tag' do
end
it 'shows the error message' do
- delete_tag 'v1.1.0'
+ container = page.find('.content .flex-row', text: 'v1.1.0')
+ delete_tag container
expect(page).to have_content('Do not delete tags')
end
end
- def delete_tag(tag)
- page.within('.content') do
- accept_confirm { find("li > .row-fixed-content.controls a.btn-remove[href='/#{project.full_path}/-/tags/#{tag}']").click }
- end
+ def delete_tag(container)
+ container.find('.js-remove-tag').click
+
+ page.within('.modal') { click_button('Delete tag') }
+ wait_for_requests
end
end
diff --git a/spec/features/tags/developer_views_tags_spec.rb b/spec/features/tags/developer_views_tags_spec.rb
index 4888611472c..6bae53afe6f 100644
--- a/spec/features/tags/developer_views_tags_spec.rb
+++ b/spec/features/tags/developer_views_tags_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Developer views tags' do
+ include RepoHelpers
+
let(:user) { create(:user) }
let(:group) { create(:group) }
@@ -15,10 +17,13 @@ RSpec.describe 'Developer views tags' do
let(:project) { create(:project_empty_repo, namespace: group) }
before do
- visit project_path(project)
- click_on 'Add README'
- fill_in :commit_message, with: 'Add a README file', visible: true
- click_button 'Commit changes'
+ project.repository.create_file(
+ user,
+ 'README.md',
+ 'Example readme',
+ message: 'Add README',
+ branch_name: 'master')
+
visit project_tags_path(project)
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index a9cfe794177..0f8daaf8e15 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Task Lists' do
+RSpec.describe 'Task Lists', :js do
include Warden::Test::Helpers
let_it_be(:project) { create(:project, :public, :repository) }
@@ -38,41 +38,7 @@ RSpec.describe 'Task Lists' do
MARKDOWN
end
- let(:nested_tasks_markdown) do
- <<-EOT.strip_heredoc
- - [ ] Task a
- - [x] Task a.1
- - [ ] Task a.2
- - [ ] Task b
-
- 1. [ ] Task 1
- 1. [ ] Task 1.1
- 1. [x] Task 1.2
- EOT
- end
-
- let(:commented_tasks_markdown) do
- <<-EOT.strip_heredoc
- <!--
- - [ ] a
- -->
-
- - [ ] b
- EOT
- end
-
- let(:summary_no_blank_line_markdown) do
- <<-EOT.strip_heredoc
- <details>
- <summary>No blank line after summary element breaks task list</summary>
- 1. [ ] People Ops: do such and such
- </details>
-
- * [ ] Task 1
- EOT
- end
-
- before(:all) do
+ before_all do
project.add_maintainer(user)
project.add_guest(user2)
end
@@ -86,7 +52,7 @@ RSpec.describe 'Task Lists' do
end
describe 'for Issues' do
- describe 'multiple tasks', :js do
+ describe 'multiple tasks' do
let!(:issue) { create(:issue, description: markdown, author: user, project: project) }
it 'renders' do
@@ -127,7 +93,7 @@ RSpec.describe 'Task Lists' do
end
end
- describe 'single incomplete task', :js do
+ describe 'single incomplete task' do
let!(:issue) { create(:issue, description: singleIncompleteMarkdown, author: user, project: project) }
it 'renders' do
@@ -146,7 +112,7 @@ RSpec.describe 'Task Lists' do
end
end
- describe 'single complete task', :js do
+ describe 'single complete task' do
let!(:issue) { create(:issue, description: singleCompleteMarkdown, author: user, project: project) }
it 'renders' do
@@ -175,7 +141,7 @@ RSpec.describe 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body', :js do
+ it 'renders for note body' do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -183,14 +149,14 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('.note ul input[checked]', count: 2)
end
- it 'contains the required selectors', :js do
+ it 'contains the required selectors' do
visit_issue(project, issue)
expect(page).to have_selector('.note .js-task-list-container')
expect(page).to have_selector('.note .js-task-list-container .task-list .task-list-item .task-list-item-checkbox')
end
- it 'is only editable by author', :js do
+ it 'is only editable by author' do
visit_issue(project, issue)
expect(page).to have_selector('.js-task-list-container')
@@ -209,7 +175,7 @@ RSpec.describe 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body', :js do
+ it 'renders for note body' do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -224,7 +190,7 @@ RSpec.describe 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body', :js do
+ it 'renders for note body' do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -240,7 +206,7 @@ RSpec.describe 'Task Lists' do
end
shared_examples 'multiple tasks' do
- it 'renders for description', :js do
+ it 'renders for description' do
visit_merge_request(project, merge)
wait_for_requests
@@ -249,7 +215,7 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('ul input[checked]', count: 2)
end
- it 'contains the required selectors', :js do
+ it 'contains the required selectors' do
visit_merge_request(project, merge)
wait_for_requests
@@ -261,7 +227,7 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('form.js-issuable-update')
end
- it 'is only editable by author', :js do
+ it 'is only editable by author' do
visit_merge_request(project, merge)
wait_for_requests
@@ -300,7 +266,7 @@ RSpec.describe 'Task Lists' do
describe 'single incomplete task' do
let!(:merge) { create(:merge_request, :simple, description: singleIncompleteMarkdown, author: user, source_project: project) }
- it 'renders for description', :js do
+ it 'renders for description' do
visit_merge_request(project, merge)
wait_for_requests
@@ -319,7 +285,7 @@ RSpec.describe 'Task Lists' do
describe 'single complete task' do
let!(:merge) { create(:merge_request, :simple, description: singleCompleteMarkdown, author: user, source_project: project) }
- it 'renders for description', :js do
+ it 'renders for description' do
visit_merge_request(project, merge)
wait_for_requests
@@ -337,7 +303,17 @@ RSpec.describe 'Task Lists' do
end
describe 'markdown task edge cases' do
- describe 'commented tasks', :js do
+ describe 'commented tasks' do
+ let(:commented_tasks_markdown) do
+ <<-EOT.strip_heredoc
+ <!--
+ - [ ] a
+ -->
+
+ - [ ] b
+ EOT
+ end
+
let!(:issue) { create(:issue, description: commented_tasks_markdown, author: user, project: project) }
it 'renders' do
@@ -360,7 +336,18 @@ RSpec.describe 'Task Lists' do
end
end
- describe 'summary with no blank line', :js do
+ describe 'summary with no blank line' do
+ let(:summary_no_blank_line_markdown) do
+ <<-EOT.strip_heredoc
+ <details>
+ <summary>No blank line after summary element breaks task list</summary>
+ 1. [ ] People Ops: do such and such
+ </details>
+
+ * [ ] Task 1
+ EOT
+ end
+
let!(:issue) { create(:issue, description: summary_no_blank_line_markdown, author: user, project: project) }
it 'renders' do
@@ -382,5 +369,31 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('ul input[checked]', count: 1)
end
end
+
+ describe 'markdown starting with new line character' do
+ let(:markdown_starting_with_new_line) do
+ <<-EOT.strip_heredoc
+
+ - [ ] Task 1
+ EOT
+ end
+
+ let(:merge_request) { create(:merge_request, description: markdown_starting_with_new_line, author: user, source_project: project) }
+
+ it 'allows the task to be checked' do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_selector('ul input[checked]', count: 0)
+
+ find('.task-list-item-checkbox').click
+ wait_for_requests
+
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_selector('ul input[checked]', count: 1)
+ end
+ end
end
end
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 4be27673adf..6fa805d8c74 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -19,114 +19,132 @@ RSpec.describe 'Triggers', :js do
visit project_settings_ci_cd_path(@project)
end
- describe 'create trigger workflow' do
- it 'prevents adding new trigger with no description' do
- fill_in 'trigger_description', with: ''
- click_button 'Add trigger'
-
- # See if input has error due to empty value
- expect(page.find('form.gl-show-field-errors .gl-field-error')).to be_visible
- end
+ shared_examples 'triggers page' do
+ describe 'create trigger workflow' do
+ it 'prevents adding new trigger with no description' do
+ fill_in 'trigger_description', with: ''
+ click_button 'Add trigger'
+
+ # See if input has error due to empty value
+ expect(page.find('form.gl-show-field-errors .gl-field-error')).to be_visible
+ end
- it 'adds new trigger with description' do
- fill_in 'trigger_description', with: 'trigger desc'
- click_button 'Add trigger'
+ it 'adds new trigger with description' do
+ fill_in 'trigger_description', with: 'trigger desc'
+ click_button 'Add trigger'
- # See if "trigger creation successful" message displayed and description and owner are correct
- expect(page.find('.flash-notice')).to have_content 'Trigger was created successfully.'
- expect(page.find('.triggers-list')).to have_content 'trigger desc'
- expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
+ aggregate_failures 'display creation notice and trigger is created' do
+ expect(page.find('.flash-notice')).to have_content 'Trigger was created successfully.'
+ expect(page.find('.triggers-list')).to have_content 'trigger desc'
+ expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
+ end
+ end
end
- end
-
- describe 'edit trigger workflow' do
- let(:new_trigger_title) { 'new trigger' }
- it 'click on edit trigger opens edit trigger page' do
- create(:ci_trigger, owner: user, project: @project, description: trigger_title)
- visit project_settings_ci_cd_path(@project)
+ describe 'edit trigger workflow' do
+ let(:new_trigger_title) { 'new trigger' }
- # See if edit page has correct descrption
- find('a[title="Edit"]').send_keys(:return)
- expect(page.find('#trigger_description').value).to have_content 'trigger desc'
- end
+ it 'click on edit trigger opens edit trigger page' do
+ create(:ci_trigger, owner: user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
- it 'edit trigger and save' do
- create(:ci_trigger, owner: user, project: @project, description: trigger_title)
- visit project_settings_ci_cd_path(@project)
+ # See if edit page has correct descrption
+ find('a[title="Edit"]').send_keys(:return)
+ expect(page.find('#trigger_description').value).to have_content 'trigger desc'
+ end
- # See if edit page opens, then fill in new description and save
- find('a[title="Edit"]').send_keys(:return)
- fill_in 'trigger_description', with: new_trigger_title
- click_button 'Save trigger'
+ it 'edit trigger and save' do
+ create(:ci_trigger, owner: user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
- # See if "trigger updated successfully" message displayed and description and owner are correct
- expect(page.find('.flash-notice')).to have_content 'Trigger was successfully updated.'
- expect(page.find('.triggers-list')).to have_content new_trigger_title
- expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
- end
- end
+ # See if edit page opens, then fill in new description and save
+ find('a[title="Edit"]').send_keys(:return)
+ fill_in 'trigger_description', with: new_trigger_title
+ click_button 'Save trigger'
- describe 'trigger "Revoke" workflow' do
- before do
- create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
- visit project_settings_ci_cd_path(@project)
+ aggregate_failures 'display update notice and trigger is updated' do
+ expect(page.find('.flash-notice')).to have_content 'Trigger was successfully updated.'
+ expect(page.find('.triggers-list')).to have_content new_trigger_title
+ expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
+ end
+ end
end
- it 'button "Revoke" has correct alert' do
- expected_alert = 'By revoking a trigger you will break any processes making use of it. Are you sure?'
- expect(page.find('a.btn-trigger-revoke')['data-confirm']).to eq expected_alert
- end
+ describe 'trigger "Revoke" workflow' do
+ before do
+ create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+ end
- it 'revoke trigger' do
- # See if "Revoke" on trigger works post trigger creation
- page.accept_confirm do
- find('a.btn-trigger-revoke').send_keys(:return)
+ it 'button "Revoke" has correct alert' do
+ expected_alert = 'By revoking a trigger you will break any processes making use of it. Are you sure?'
+ expect(page.find('[data-testid="trigger_revoke_button"]')['data-confirm']).to eq expected_alert
end
- expect(page.find('.flash-notice')).to have_content 'Trigger removed'
- expect(page).to have_selector('p.settings-message.text-center.gl-mb-3')
- end
- end
+ it 'revoke trigger' do
+ # See if "Revoke" on trigger works post trigger creation
+ page.accept_confirm do
+ find('[data-testid="trigger_revoke_button"]').send_keys(:return)
+ end
- describe 'show triggers workflow' do
- it 'contains trigger description placeholder' do
- expect(page.find('#trigger_description')['placeholder']).to eq 'Trigger description'
+ aggregate_failures 'trigger is removed' do
+ expect(page.find('.flash-notice')).to have_content 'Trigger removed'
+ expect(page).to have_css('[data-testid="no_triggers_content"]')
+ end
+ end
end
- it 'show "invalid" badge for trigger with owner having insufficient permissions' do
- create(:ci_trigger, owner: guest_user, project: @project, description: trigger_title)
- visit project_settings_ci_cd_path(@project)
+ describe 'show triggers workflow' do
+ it 'contains trigger description placeholder' do
+ expect(page.find('#trigger_description')['placeholder']).to eq 'Trigger description'
+ end
- expect(page.find('.triggers-list')).to have_content 'invalid'
- expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
- end
+ it 'show "invalid" badge for trigger with owner having insufficient permissions' do
+ create(:ci_trigger, owner: guest_user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+
+ aggregate_failures 'has invalid badge and no edit link' do
+ expect(page.find('.triggers-list')).to have_content 'invalid'
+ expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
+ end
+ end
- it 'do not show "Edit" or full token for not owned trigger' do
- # Create trigger with user different from current_user
- create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
- visit project_settings_ci_cd_path(@project)
+ it 'do not show "Edit" or full token for not owned trigger' do
+ # Create trigger with user different from current_user
+ create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+
+ aggregate_failures 'shows truncated token, no clipboard button and no edit link' do
+ expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3])
+ expect(page.find('.triggers-list')).not_to have_selector('[data-testid="clipboard-btn"]')
+ expect(page.find('.triggers-list .trigger-owner')).not_to have_content user.name
+ expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
+ end
+ end
- # See if trigger not owned by current_user shows only first few token chars and doesn't have copy-to-clipboard button
- expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3])
- expect(page.find('.triggers-list')).not_to have_selector('button.btn-clipboard')
+ it 'show "Edit" and full token for owned trigger' do
+ create(:ci_trigger, owner: user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
- # See if trigger owner name doesn't match with current_user and trigger is non-editable
- expect(page.find('.triggers-list .trigger-owner')).not_to have_content user.name
- expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
+ aggregate_failures 'shows full token, clipboard button and edit link' do
+ expect(page.find('.triggers-list')).to have_content @project.triggers.first.token
+ expect(page.find('.triggers-list')).to have_selector('[data-testid="clipboard-btn"]')
+ expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
+ expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
+ end
+ end
end
+ end
- it 'show "Edit" and full token for owned trigger' do
- create(:ci_trigger, owner: user, project: @project, description: trigger_title)
- visit project_settings_ci_cd_path(@project)
-
- # See if trigger shows full token and has copy-to-clipboard button
- expect(page.find('.triggers-list')).to have_content @project.triggers.first.token
- expect(page.find('.triggers-list')).to have_selector('button.btn-clipboard')
+ context 'when ci_pipeline_triggers_settings_vue_ui is enabled' do
+ it_behaves_like 'triggers page'
+ end
- # See if trigger owner name matches with current_user and is editable
- expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
- expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
+ context 'when ci_pipeline_triggers_settings_vue_ui is disabled' do
+ before do
+ stub_feature_flags(ci_pipeline_triggers_settings_vue_ui: false)
end
+
+ it_behaves_like 'triggers page'
end
end
diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb
index 549087e5950..67216b04504 100644
--- a/spec/features/users/overview_spec.rb
+++ b/spec/features/users/overview_spec.rb
@@ -21,15 +21,15 @@ RSpec.describe 'Overview tab on a user profile', :js do
sign_in user
end
- describe 'activities section' do
- shared_context 'visit overview tab' do
- before do
- visit user.username
- page.find('.js-overview-tab a').click
- wait_for_requests
- end
+ shared_context 'visit overview tab' do
+ before do
+ visit user.username
+ page.find('.js-overview-tab a').click
+ wait_for_requests
end
+ end
+ describe 'activities section' do
describe 'user has no activities' do
include_context 'visit overview tab'
@@ -84,14 +84,6 @@ RSpec.describe 'Overview tab on a user profile', :js do
end
describe 'projects section' do
- shared_context 'visit overview tab' do
- before do
- visit user.username
- page.find('.js-overview-tab a').click
- wait_for_requests
- end
- end
-
describe 'user has no personal projects' do
include_context 'visit overview tab'
@@ -158,4 +150,52 @@ RSpec.describe 'Overview tab on a user profile', :js do
end
end
end
+
+ describe 'bot user' do
+ let(:bot_user) { create(:user, user_type: :security_bot) }
+
+ shared_context "visit bot's overview tab" do
+ before do
+ visit bot_user.username
+ page.find('.js-overview-tab a').click
+ wait_for_requests
+ end
+ end
+
+ describe 'feature flag enabled' do
+ before do
+ stub_feature_flags(security_auto_fix: true)
+ end
+
+ include_context "visit bot's overview tab"
+
+ it "activity panel's title is 'Bot activity'" do
+ page.within('.activities-block') do
+ expect(page).to have_text('Bot activity')
+ end
+ end
+
+ it 'does not show projects panel' do
+ expect(page).not_to have_selector('.projects-block')
+ end
+ end
+
+ describe 'feature flag disabled' do
+ before do
+ stub_feature_flags(security_auto_fix: false)
+ end
+
+ include_context "visit bot's overview tab"
+
+ it "activity panel's title is not 'Bot activity'" do
+ page.within('.activities-block') do
+ expect(page).not_to have_text('Bot activity')
+ end
+ end
+
+ it 'shows projects panel' do
+ expect(page).to have_selector('.projects-block')
+ end
+ end
+ end
end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index dd5c2442d00..466b7361da9 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'User page' do
include ExternalAuthorizationServiceHelpers
- let(:user) { create(:user) }
+ let(:user) { create(:user, bio: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') }
context 'with public profile' do
it 'shows all the tabs' do
@@ -174,4 +174,54 @@ RSpec.describe 'User page' do
end
end
end
+
+ context 'page description' do
+ before do
+ visit(user_path(user))
+ end
+
+ it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
+ end
+
+ context 'with a bot user' do
+ let(:user) { create(:user, user_type: :security_bot) }
+
+ describe 'feature flag enabled' do
+ before do
+ stub_feature_flags(security_auto_fix: true)
+ end
+
+ it 'only shows Overview and Activity tabs' do
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Overview')
+ expect(page).to have_link('Activity')
+ expect(page).not_to have_link('Groups')
+ expect(page).not_to have_link('Contributed projects')
+ expect(page).not_to have_link('Personal projects')
+ expect(page).not_to have_link('Snippets')
+ end
+ end
+ end
+
+ describe 'feature flag disabled' do
+ before do
+ stub_feature_flags(security_auto_fix: false)
+ end
+
+ it 'only shows Overview and Activity tabs' do
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Overview')
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 5fd0e677cd0..c59121626f0 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -7,6 +7,14 @@ RSpec.shared_examples 'Signup' do
let(:new_user) { build_stubbed(:user) }
+ def fill_in_signup_form
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ 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_password', with: new_user.password
+ end
+
describe 'username validation', :js do
before do
visit new_user_registration_path
@@ -144,20 +152,9 @@ RSpec.shared_examples 'Signup' do
it 'creates the user account and sends a confirmation email' do
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
+ fill_in_signup_form
expect { click_button 'Register' }.to change { User.count }.by(1)
-
expect(current_path).to eq users_almost_there_path
expect(page).to have_content('Please check your email to confirm your account')
end
@@ -171,46 +168,14 @@ RSpec.shared_examples 'Signup' do
it 'creates the user account and sends a confirmation email' do
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
+ fill_in_signup_form
expect { click_button 'Register' }.to change { User.count }.by(1)
-
expect(current_path).to eq users_sign_up_welcome_path
end
end
end
- context "when sigining up with different cased emails" do
- it "creates the user successfully" do
- 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"
-
- expect(current_path).to eq users_sign_up_welcome_path
- end
- end
-
context "when not sending confirmation email" do
before do
stub_application_setting(send_user_confirmation_email: false)
@@ -219,17 +184,7 @@ RSpec.shared_examples 'Signup' do
it 'creates the user account and goes to dashboard' do
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
+ fill_in_signup_form
click_button "Register"
expect(current_path).to eq users_sign_up_welcome_path
@@ -239,20 +194,10 @@ RSpec.shared_examples 'Signup' do
context 'with errors' do
it "displays the errors" do
- existing_user = create(:user)
-
+ create(:user, email: new_user.email)
visit new_user_registration_path
- 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_username', with: new_user.username
- fill_in 'new_user_email', with: existing_user.email
- fill_in 'new_user_password', with: new_user.password
+ fill_in_signup_form
click_button "Register"
expect(current_path).to eq user_registration_path
@@ -261,20 +206,10 @@ RSpec.shared_examples 'Signup' do
end
it 'does not redisplay the password' do
- existing_user = create(:user)
-
+ create(:user, email: new_user.email)
visit new_user_registration_path
- 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_username', with: new_user.username
- fill_in 'new_user_email', with: existing_user.email
- fill_in 'new_user_password', with: new_user.password
+ fill_in_signup_form
click_button "Register"
expect(current_path).to eq user_registration_path
@@ -287,45 +222,14 @@ RSpec.shared_examples 'Signup' do
enforce_terms
end
- it 'requires the user to check the checkbox' do
+ it 'renders text that the user confirms terms by clicking register' do
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
+ expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)
+ fill_in_signup_form
click_button 'Register'
- expect(current_path).to eq new_user_session_path
- expect(page).to have_content(/you must accept our terms of service/i)
- end
-
- it 'asks the user to accept terms before going to the dashboard' do
- 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
- check :terms_opt_in
-
- click_button "Register"
-
expect(current_path).to eq users_sign_up_welcome_path
end
end
@@ -353,17 +257,7 @@ RSpec.shared_examples 'Signup' do
it 'prevents from signing up' do
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
+ fill_in_signup_form
expect { click_button 'Register' }.not_to change { User.count }
expect(page).to have_content('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')
@@ -374,17 +268,7 @@ RSpec.shared_examples 'Signup' do
it 'prevents from signing up' do
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
+ fill_in_signup_form
expect { click_button 'Register' }.not_to change { User.count }
expect(page).to have_content('That was a bit too quick! Please resubmit.')
@@ -393,36 +277,27 @@ RSpec.shared_examples 'Signup' do
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
+ fill_in_signup_form
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
+ created_user = User.find_by_username(new_user.username)
+
+ expect(created_user.software_developer_role?).to be_truthy
+ expect(created_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|
+RSpec.shared_examples 'Signup name validation' do |field, max_length, label|
before do
visit new_user_registration_path
end
@@ -446,10 +321,10 @@ RSpec.shared_examples 'Signup name validation' do |field, max_length|
expect(find('.name')).to have_css '.gl-field-error-outline'
end
- it "shows an error message if the user\'s fullname is longer than #{max_length} characters" do
+ it "shows an error message if the user\'s #{label} is longer than #{max_length} characters" do
fill_in field, with: 'n' * (max_length + 1)
- expect(page).to have_content("Name is too long (maximum is #{max_length} characters).")
+ expect(page).to have_content("#{label} is too long (maximum is #{max_length} characters).")
end
it 'shows an error message if the username contains emojis' do
@@ -467,7 +342,8 @@ RSpec.describe 'With original flow' do
end
it_behaves_like 'Signup'
- it_behaves_like 'Signup name validation', 'new_user_name', 255
+ it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
+ it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
end
RSpec.describe 'With experimental flow' do
@@ -477,30 +353,6 @@ RSpec.describe 'With experimental flow' do
end
it_behaves_like 'Signup'
- it_behaves_like 'Signup name validation', 'new_user_first_name', 127
- it_behaves_like 'Signup name validation', 'new_user_last_name', 127
-
- context 'when terms_opt_in experimental is enabled' do
- include TermsHelper
-
- before do
- enforce_terms
- stub_experiment(signup_flow: true, terms_opt_in: true)
- stub_experiment_for_user(signup_flow: true, terms_opt_in: true)
- end
-
- it 'terms are checked by default' 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'
-
- expect(current_path).to eq users_sign_up_welcome_path
- end
- end
+ it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
+ it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
end
diff --git a/spec/features/users/terms_spec.rb b/spec/features/users/terms_spec.rb
index 5275845fe5b..7500f2fe59a 100644
--- a/spec/features/users/terms_spec.rb
+++ b/spec/features/users/terms_spec.rb
@@ -26,6 +26,21 @@ RSpec.describe 'Users > Terms' do
expect(page).not_to have_content('Continue')
end
+ context 'when user is a project bot' do
+ let(:project_bot) { create(:user, :project_bot) }
+
+ before do
+ enforce_terms
+ end
+
+ it 'auto accepts the terms' do
+ visit terms_path
+
+ expect(page).not_to have_content('Accept terms')
+ expect(project_bot.terms_accepted?).to be(true)
+ end
+ end
+
context 'when signed in' do
let(:user) { create(:user) }