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>2021-02-18 13:34:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 13:34:06 +0300
commit859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch)
treed7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /spec/features
parent446d496a6d000c73a304be52587cd9bbc7493136 (diff)
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'spec/features')
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb33
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb15
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb99
-rw-r--r--spec/features/admin/admin_search_settings_spec.rb27
-rw-r--r--spec/features/admin/admin_settings_spec.rb75
-rw-r--r--spec/features/admin/admin_users_spec.rb70
-rw-r--r--spec/features/admin/users/user_spec.rb4
-rw-r--r--spec/features/alert_management/alert_details_spec.rb2
-rw-r--r--spec/features/alerts_settings/user_views_alerts_settings_spec.rb3
-rw-r--r--spec/features/boards/boards_spec.rb8
-rw-r--r--spec/features/boards/sidebar_spec.rb44
-rw-r--r--spec/features/calendar_spec.rb2
-rw-r--r--spec/features/commit_spec.rb78
-rw-r--r--spec/features/commits_spec.rb3
-rw-r--r--spec/features/dashboard/activity_spec.rb20
-rw-r--r--spec/features/discussion_comments/issue_spec.rb2
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb1
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb29
-rw-r--r--spec/features/groups/import_export/connect_instance_spec.rb16
-rw-r--r--spec/features/groups/navbar_spec.rb8
-rw-r--r--spec/features/groups/settings/packages_and_registries_spec.rb68
-rw-r--r--spec/features/groups/show_spec.rb1
-rw-r--r--spec/features/help_pages_spec.rb2
-rw-r--r--spec/features/ide/user_opens_merge_request_spec.rb2
-rw-r--r--spec/features/import/manifest_import_spec.rb2
-rw-r--r--spec/features/issuables/issuable_list_spec.rb2
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb10
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb35
-rw-r--r--spec/features/issues/issue_state_spec.rb26
-rw-r--r--spec/features/issues/user_comments_on_issue_spec.rb4
-rw-r--r--spec/features/issues/user_creates_issue_by_email_spec.rb4
-rw-r--r--spec/features/issues/user_resets_their_incoming_email_token_spec.rb24
-rw-r--r--spec/features/issues/user_toggles_subscription_spec.rb6
-rw-r--r--spec/features/labels_hierarchy_spec.rb5
-rw-r--r--spec/features/markdown/markdown_spec.rb27
-rw-r--r--spec/features/markdown/mermaid_spec.rb2
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb37
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb10
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb4
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb8
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_reverts_merge_request_spec.rb43
-rw-r--r--spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb14
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb25
-rw-r--r--spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb234
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb17
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb29
-rw-r--r--spec/features/merge_requests/user_filters_by_milestones_spec.rb2
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb12
-rw-r--r--spec/features/profiles/user_edit_preferences_spec.rb11
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb32
-rw-r--r--spec/features/profiles/user_search_settings_spec.rb25
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb1
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb12
-rw-r--r--spec/features/profiles/user_visits_profile_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb10
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb160
-rw-r--r--spec/features/projects/commit/user_reverts_commit_spec.rb91
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb9
-rw-r--r--spec/features/projects/compare_spec.rb23
-rw-r--r--spec/features/projects/features_visibility_spec.rb6
-rw-r--r--spec/features/projects/files/dockerfile_dropdown_spec.rb8
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb10
-rw-r--r--spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb7
-rw-r--r--spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb10
-rw-r--r--spec/features/projects/fork_spec.rb39
-rw-r--r--spec/features/projects/graph_spec.rb6
-rw-r--r--spec/features/projects/issuable_templates_spec.rb4
-rw-r--r--spec/features/projects/jobs_spec.rb52
-rw-r--r--spec/features/projects/members/anonymous_user_sees_members_spec.rb26
-rw-r--r--spec/features/projects/members/group_members_spec.rb243
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb180
-rw-r--r--spec/features/projects/members/invite_group_spec.rb86
-rw-r--r--spec/features/projects/members/list_spec.rb222
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb111
-rw-r--r--spec/features/projects/members/sorting_spec.rb204
-rw-r--r--spec/features/projects/members/tabs_spec.rb99
-rw-r--r--spec/features/projects/navbar_spec.rb19
-rw-r--r--spec/features/projects/pages/user_adds_domain_spec.rb185
-rw-r--r--spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb (renamed from spec/features/projects/pages_lets_encrypt_spec.rb)2
-rw-r--r--spec/features/projects/pages/user_edits_settings_spec.rb201
-rw-r--r--spec/features/projects/pages_spec.rb411
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb221
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb113
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb4
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/project_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb134
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb4
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb1
-rw-r--r--spec/features/projects/show/user_sees_git_instructions_spec.rb7
-rw-r--r--spec/features/projects/terraform_spec.rb2
-rw-r--r--spec/features/projects/user_creates_project_spec.rb6
-rw-r--r--spec/features/projects/user_sees_user_popover_spec.rb2
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb7
-rw-r--r--spec/features/protected_branches_spec.rb6
-rw-r--r--spec/features/registrations/experience_level_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_commits_spec.rb4
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_merge_requests_spec.rb34
-rw-r--r--spec/features/search/user_searches_for_projects_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb8
-rw-r--r--spec/features/u2f_spec.rb10
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb27
-rw-r--r--spec/features/users/login_spec.rb2
-rw-r--r--spec/features/users/overview_spec.rb126
-rw-r--r--spec/features/users/show_spec.rb52
-rw-r--r--spec/features/webauthn_spec.rb4
-rw-r--r--spec/features/whats_new_spec.rb35
116 files changed, 3065 insertions, 1484 deletions
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
deleted file mode 100644
index 982a9333275..00000000000
--- a/spec/features/admin/admin_cohorts_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Cohorts page' do
- before do
- admin = create(:admin)
- sign_in(admin)
- gitlab_enable_admin_mode_sign_in(admin)
- end
-
- context 'with usage ping enabled' do
- it 'shows users count per month' do
- stub_application_setting(usage_ping_enabled: true)
-
- create_list(:user, 2)
-
- visit admin_cohorts_path
-
- expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
- end
- end
-
- context 'with usage ping disabled' do
- it 'shows empty state', :js do
- stub_application_setting(usage_ping_enabled: false)
-
- visit admin_cohorts_path
-
- expect(page).to have_selector(".js-empty-state")
- end
- end
-end
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index f7f0592a315..b370b779afe 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -37,7 +37,10 @@ RSpec.describe 'Admin disables Git access protocol', :js do
it 'shows only the SSH clone information' do
resize_screen_xs
visit_project
- find('.dropdown-toggle').click
+
+ within('.js-mobile-git-clone') do
+ find('.dropdown-toggle').click
+ end
expect(page).to have_content('Copy SSH clone URL')
expect(page).not_to have_content('Copy HTTP clone URL')
@@ -66,7 +69,10 @@ RSpec.describe 'Admin disables Git access protocol', :js do
it 'shows only the HTTP clone information' do
resize_screen_xs
visit_project
- find('.dropdown-toggle').click
+
+ within('.js-mobile-git-clone') do
+ find('.dropdown-toggle').click
+ end
expect(page).to have_content('Copy HTTP clone URL')
expect(page).not_to have_content('Copy SSH clone URL')
@@ -97,7 +103,10 @@ RSpec.describe 'Admin disables Git access protocol', :js do
it 'shows both SSH and HTTP clone information' do
resize_screen_xs
visit_project
- find('.dropdown-toggle').click
+
+ within('.js-mobile-git-clone') do
+ find('.dropdown-toggle').click
+ end
expect(page).to have_content('Copy HTTP clone URL')
expect(page).to have_content('Copy SSH clone URL')
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index a8e18385bd2..bbdf2f7f4a9 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe 'Admin Groups' do
click_button "Create group"
expect(current_path).to eq admin_group_path(Group.find_by(path: path_component))
- content = page.find('div#content-body')
+ content = page.find('#content-body')
h3_texts = content.all('h3').collect(&:text).join("\n")
expect(h3_texts).to match group_name
li_texts = content.all('li').collect(&:text).join("\n")
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index ff4e592234b..aab2e6d7cef 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe "Admin::Projects" do
+ include Spec::Support::Helpers::Features::MembersHelpers
include Select2Helper
let(:user) { create :user }
@@ -91,45 +92,97 @@ RSpec.describe "Admin::Projects" do
end
end
- describe 'add admin himself to a project' do
- before do
- project.add_maintainer(user)
- end
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ describe 'admin adds themselves to the project' do
+ before do
+ project.add_maintainer(user)
+ stub_feature_flags(invite_members_group_modal: false)
+ end
- it 'adds admin a to a project as developer', :js do
- visit project_project_members_path(project)
+ it 'adds admin to the project as developer', :js do
+ visit project_project_members_path(project)
- page.within '.invite-users-form' do
- select2(current_user.id, from: '#user_ids', multiple: true)
- select 'Developer', from: 'access_level'
+ page.within '.invite-users-form' do
+ select2(current_user.id, from: '#user_ids', multiple: true)
+ select 'Developer', from: 'access_level'
+ end
+
+ click_button 'Invite'
+
+ expect(find_member_row(current_user)).to have_content('Developer')
end
+ end
+
+ describe 'admin removes themselves from the project' do
+ before do
+ project.add_maintainer(user)
+ project.add_developer(current_user)
+ end
+
+ it 'removes admin from the project' do
+ visit project_project_members_path(project)
+
+ expect(find_member_row(current_user)).to have_content('Developer')
+
+ page.within find_member_row(current_user) do
+ click_button 'Leave'
+ end
- click_button 'Invite'
+ page.within('[role="dialog"]') do
+ click_button('Leave')
+ end
- page.within '.content-list' do
- expect(page).to have_content(current_user.name)
- expect(page).to have_content('Developer')
+ expect(current_path).to match dashboard_projects_path
end
end
end
- describe 'admin remove himself from a project' do
+ context 'when `vue_project_members_list` feature flag is disabled' do
before do
- project.add_maintainer(user)
- project.add_developer(current_user)
+ stub_feature_flags(vue_project_members_list: false)
end
- it 'removes admin from the project' do
- visit project_project_members_path(project)
+ describe 'admin adds themselves to the project' do
+ before do
+ project.add_maintainer(user)
+ stub_feature_flags(invite_members_group_modal: false)
+ end
+
+ it 'adds admin to the project as developer', :js do
+ visit project_project_members_path(project)
+
+ page.within '.invite-users-form' do
+ select2(current_user.id, from: '#user_ids', multiple: true)
+ select 'Developer', from: 'access_level'
+ end
+
+ click_button 'Invite'
+
+ page.within '.content-list' do
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content('Developer')
+ end
+ end
+ end
- page.within '.content-list' do
- expect(page).to have_content(current_user.name)
- expect(page).to have_content('Developer')
+ describe 'admin removes themselves from the project' do
+ before do
+ project.add_maintainer(user)
+ project.add_developer(current_user)
end
- find(:css, '.content-list li', text: current_user.name).find(:css, 'a.btn-danger').click
+ it 'removes admin from the project' do
+ visit project_project_members_path(project)
+
+ page.within '.content-list' do
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content('Developer')
+ end
- expect(page).not_to have_selector(:css, '.content-list')
+ find(:css, '.content-list li', text: current_user.name).find(:css, 'a.btn-danger').click
+
+ expect(page).not_to have_selector(:css, '.content-list')
+ end
end
end
end
diff --git a/spec/features/admin/admin_search_settings_spec.rb b/spec/features/admin/admin_search_settings_spec.rb
new file mode 100644
index 00000000000..a78d17a6651
--- /dev/null
+++ b/spec/features/admin/admin_search_settings_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Admin searches application settings', :js do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:application_settings) { create(:application_setting) }
+
+ before do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
+ end
+
+ context 'in appearances page' do
+ before do
+ visit(admin_appearances_path)
+ end
+
+ it_behaves_like 'cannot search settings'
+ end
+
+ context 'in ci/cd settings page' do
+ let(:visit_path) { ci_cd_admin_application_settings_path }
+
+ it_behaves_like 'can search settings with feature flag check', 'Variables', 'Package Registry'
+ end
+end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 0c66775c323..52f39f65bd0 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -306,59 +306,70 @@ RSpec.describe 'Admin updates settings' do
page.within('.as-ci-cd') do
check 'Default to Auto DevOps pipeline for all projects'
fill_in 'application_setting_auto_devops_domain', with: 'domain.com'
+ uncheck 'Keep the latest artifacts for all jobs in the latest successful pipelines'
click_button 'Save changes'
end
expect(current_settings.auto_devops_enabled?).to be true
expect(current_settings.auto_devops_domain).to eq('domain.com')
+ expect(current_settings.keep_latest_artifact).to be false
expect(page).to have_content "Application settings saved successfully"
end
context 'Container Registry' do
- context 'delete tags service execution timeout' do
- let(:feature_flag_enabled) { true }
- let(:client_support) { true }
-
- before do
- stub_container_registry_config(enabled: true)
- stub_feature_flags(container_registry_expiration_policies_throttling: feature_flag_enabled)
- allow(ContainerRegistry::Client).to receive(:supports_tag_delete?).and_return(client_support)
- end
+ let(:feature_flag_enabled) { true }
+ let(:client_support) { true }
+ let(:settings_titles) do
+ {
+ container_registry_delete_tags_service_timeout: 'Container Registry delete tags service execution timeout',
+ container_registry_expiration_policies_worker_capacity: 'Cleanup policy maximum workers running concurrently',
+ container_registry_cleanup_tags_service_max_list_size: 'Cleanup policy maximum number of tags to be deleted'
+ }
+ end
+
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_feature_flags(container_registry_expiration_policies_throttling: feature_flag_enabled)
+ allow(ContainerRegistry::Client).to receive(:supports_tag_delete?).and_return(client_support)
+ end
- RSpec.shared_examples 'not having service timeout settings' do
- it 'lacks the timeout settings' do
- visit ci_cd_admin_application_settings_path
+ shared_examples 'not having container registry setting' do |registry_setting|
+ it "lacks the container setting #{registry_setting}" do
+ visit ci_cd_admin_application_settings_path
- expect(page).not_to have_content "Container Registry delete tags service execution timeout"
- end
+ expect(page).not_to have_content(settings_titles[registry_setting])
end
+ end
- context 'with feature flag enabled' do
- context 'with client supporting tag delete' do
- it 'changes the timeout' do
- visit ci_cd_admin_application_settings_path
+ %i[container_registry_delete_tags_service_timeout container_registry_expiration_policies_worker_capacity container_registry_cleanup_tags_service_max_list_size].each do |setting|
+ context "for container registry setting #{setting}" do
+ context 'with feature flag enabled' do
+ context 'with client supporting tag delete' do
+ it 'changes the setting' do
+ visit ci_cd_admin_application_settings_path
- page.within('.as-registry') do
- fill_in 'application_setting_container_registry_delete_tags_service_timeout', with: 400
- click_button 'Save changes'
- end
+ page.within('.as-registry') do
+ fill_in "application_setting_#{setting}", with: 400
+ click_button 'Save changes'
+ end
- expect(current_settings.container_registry_delete_tags_service_timeout).to eq(400)
- expect(page).to have_content "Application settings saved successfully"
+ expect(current_settings.public_send(setting)).to eq(400)
+ expect(page).to have_content "Application settings saved successfully"
+ end
end
- end
- context 'with client not supporting tag delete' do
- let(:client_support) { false }
+ context 'with client not supporting tag delete' do
+ let(:client_support) { false }
- it_behaves_like 'not having service timeout settings'
+ it_behaves_like 'not having container registry setting', setting
+ end
end
- end
- context 'with feature flag disabled' do
- let(:feature_flag_enabled) { false }
+ context 'with feature flag disabled' do
+ let(:feature_flag_enabled) { false }
- it_behaves_like 'not having service timeout settings'
+ it_behaves_like 'not having container registry setting', setting
+ end
end
end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
new file mode 100644
index 00000000000..4fc60d17886
--- /dev/null
+++ b/spec/features/admin/admin_users_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "Admin::Users" do
+ let(:current_user) { create(:admin) }
+
+ before do
+ sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
+ end
+
+ describe 'Tabs', :js do
+ let(:tabs_selector) { '.js-users-tabs' }
+ let(:active_tab_selector) { '.nav-link.active' }
+
+ it 'does not add the tab param when the Users tab is selected' do
+ visit admin_users_path
+
+ within tabs_selector do
+ click_link 'Users'
+ end
+
+ expect(page).to have_current_path(admin_users_path)
+ end
+
+ it 'adds the ?tab=cohorts param when the Cohorts tab is selected' do
+ visit admin_users_path
+
+ within tabs_selector do
+ click_link 'Cohorts'
+ end
+
+ expect(page).to have_current_path(admin_users_path(tab: 'cohorts'))
+ end
+
+ it 'shows the cohorts tab when the tab param is set' do
+ visit admin_users_path(tab: 'cohorts')
+
+ within tabs_selector do
+ expect(page).to have_selector active_tab_selector, text: 'Cohorts'
+ end
+ end
+ end
+
+ describe 'Cohorts tab content' do
+ context 'with usage ping enabled' do
+ it 'shows users count per month' do
+ stub_application_setting(usage_ping_enabled: true)
+
+ create_list(:user, 2)
+
+ visit admin_users_path(tab: 'cohorts')
+
+ expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
+ end
+ end
+
+ context 'with usage ping disabled' do
+ it 'shows empty state', :js do
+ stub_application_setting(usage_ping_enabled: false)
+
+ visit admin_users_path(tab: 'cohorts')
+
+ expect(page).to have_selector(".js-empty-state")
+ expect(page).to have_content("Activate user activity analysis")
+ end
+ end
+ end
+end
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index e7dd50ed514..befa7bd338b 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -171,7 +171,7 @@ RSpec.describe 'Admin::Users::User' do
it 'logs in as the user when impersonate is clicked' do
subject
- expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
+ expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username)
end
it 'sees impersonation log out icon' do
@@ -205,7 +205,7 @@ RSpec.describe 'Admin::Users::User' do
it 'logs out of impersonated user back to original user' do
subject
- expect(page.find(:css, '.header-user .profile-link')['data-user']).to eq(current_user.username)
+ expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username)
end
it 'is redirected back to the impersonated users page in the admin after stopping' do
diff --git a/spec/features/alert_management/alert_details_spec.rb b/spec/features/alert_management/alert_details_spec.rb
index d190e4b6939..ce82b5adf8d 100644
--- a/spec/features/alert_management/alert_details_spec.rb
+++ b/spec/features/alert_management/alert_details_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe 'Alert details', :js 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')
+ expect(todo_button).to have_content('Add a to do')
find('[data-testid="alert-todo-button"]').click
wait_for_requests
diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
index 698a36d3f76..07c87f98eb6 100644
--- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
+++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
@@ -19,13 +19,14 @@ RSpec.describe 'Alert integrations settings form', :js do
describe 'when viewing alert integrations as a maintainer' do
context 'with the default page permissions' do
before do
+ stub_feature_flags(multiple_http_integrations_custom_mapping: false)
visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
wait_for_requests
end
it 'shows the alerts setting form title' do
page.within('#js-alert-management-settings') do
- expect(find('h3')).to have_content('Alerts')
+ expect(find('h4')).to have_content('Alerts')
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index b3cc2eb418d..2d6b669f28b 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -13,12 +13,14 @@ RSpec.describe 'Issue Boards', :js do
let_it_be(:user2) { create(:user) }
before do
+ stub_feature_flags(board_new_list: false)
+
project.add_maintainer(user)
project.add_maintainer(user2)
- set_cookie('sidebar_collapsed', 'true')
-
sign_in(user)
+
+ set_cookie('sidebar_collapsed', 'true')
end
context 'no lists' do
@@ -573,7 +575,7 @@ RSpec.describe 'Issue Boards', :js do
end
it 'shows the button' do
- expect(page).to have_link('Toggle focus mode')
+ expect(page).to have_button('Toggle focus mode')
end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 2af5b787a78..08bc70d7116 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -107,17 +107,20 @@ RSpec.describe 'Issue Boards', :js do
click_card(card)
page.within('.assignee') do
- click_link 'Edit'
+ click_button('Edit')
wait_for_requests
- page.within('.dropdown-menu-user') do
- click_link user.name
+ assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text
- wait_for_requests
+ page.within('.dropdown-menu-user') do
+ first('.gl-avatar-labeled').click
end
- expect(page).to have_content(user.name)
+ click_button('Edit')
+ wait_for_requests
+
+ expect(page).to have_content(assignee)
end
expect(card).to have_selector('.avatar')
@@ -128,15 +131,15 @@ RSpec.describe 'Issue Boards', :js do
click_card(card_two)
page.within('.assignee') do
- click_link 'Edit'
+ click_button('Edit')
wait_for_requests
page.within('.dropdown-menu-user') do
- click_link 'Unassigned'
+ find('[data-testid="unassign"]').click
end
- close_dropdown_menu_if_visible
+ click_button('Edit')
wait_for_requests
expect(page).to have_content('None')
@@ -165,17 +168,20 @@ RSpec.describe 'Issue Boards', :js do
click_card(card)
page.within('.assignee') do
- click_link 'Edit'
+ click_button('Edit')
wait_for_requests
- page.within('.dropdown-menu-user') do
- click_link user.name
+ assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text
- wait_for_requests
+ page.within('.dropdown-menu-user') do
+ first('.gl-avatar-labeled').click
end
- expect(page).to have_content(user.name)
+ click_button('Edit')
+ wait_for_requests
+
+ expect(page).to have_content(assignee)
end
page.within(find('.board:nth-child(2)')) do
@@ -183,9 +189,9 @@ RSpec.describe 'Issue Boards', :js do
end
page.within('.assignee') do
- click_link 'Edit'
+ click_button('Edit')
- expect(find('.dropdown-menu')).to have_selector('.is-active')
+ expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon')
end
end
end
@@ -411,10 +417,10 @@ RSpec.describe 'Issue Boards', :js do
wait_for_requests
page.within('.subscriptions') do
- find('.js-issuable-subscribe-button button:not(.is-checked)').click
+ find('[data-testid="subscription-toggle"] button:not(.is-checked)').click
wait_for_requests
- expect(page).to have_css('.js-issuable-subscribe-button button.is-checked')
+ expect(page).to have_css('[data-testid="subscription-toggle"] button.is-checked')
end
end
@@ -427,10 +433,10 @@ RSpec.describe 'Issue Boards', :js do
wait_for_requests
page.within('.subscriptions') do
- find('.js-issuable-subscribe-button button.is-checked').click
+ find('[data-testid="subscription-toggle"] button.is-checked').click
wait_for_requests
- expect(page).to have_css('.js-issuable-subscribe-button button:not(.is-checked)')
+ expect(page).to have_css('[data-testid="subscription-toggle"] button:not(.is-checked)')
end
end
end
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 60d485d4558..ee156bdcab4 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -113,8 +113,8 @@ RSpec.describe 'Contributions Calendar', :js do
describe 'deselect calendar day' do
before do
cells[0].click
- page.find('.js-overview-tab a').click
wait_for_requests
+ cells[0].click
end
it 'hides calendar day activities' do
diff --git a/spec/features/commit_spec.rb b/spec/features/commit_spec.rb
new file mode 100644
index 00000000000..02754cc803e
--- /dev/null
+++ b/spec/features/commit_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Commit' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ describe "single commit view" do
+ let(:commit) do
+ project.repository.commits(nil, limit: 100).find do |commit|
+ commit.diffs.size > 1
+ end
+ end
+
+ let(:files) { commit.diffs.diff_files.to_a }
+
+ before do
+ stub_feature_flags(async_commit_diff_files: false)
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe "commit details" do
+ before do
+ visit project_commit_path(project, commit)
+ end
+
+ it "shows the short commit message" do
+ expect(page).to have_content(commit.title)
+ end
+
+ it "reports the correct number of total changes" do
+ expect(page).to have_content("Changes #{commit.diffs.size}")
+ end
+ end
+
+ context "pagination enabled" do
+ before do
+ stub_feature_flags(paginate_commit_view: true)
+ stub_const("Projects::CommitController::COMMIT_DIFFS_PER_PAGE", 1)
+
+ visit project_commit_path(project, commit)
+ end
+
+ it "shows an adjusted count for changed files on this page" do
+ expect(page).to have_content("Showing 1 changed file")
+ end
+
+ it "shows only the first diff on the first page" do
+ expect(page).to have_selector(".files ##{files[0].file_hash}")
+ expect(page).not_to have_selector(".files ##{files[1].file_hash}")
+ end
+
+ it "can navigate to the second page" do
+ within(".files .gl-pagination") do
+ click_on("2")
+ end
+
+ expect(page).not_to have_selector(".files ##{files[0].file_hash}")
+ expect(page).to have_selector(".files ##{files[1].file_hash}")
+ end
+ end
+
+ context "pagination disabled" do
+ before do
+ stub_feature_flags(paginate_commit_view: false)
+
+ visit project_commit_path(project, commit)
+ end
+
+ it "shows both diffs on the page" do
+ expect(page).to have_selector(".files ##{files[0].file_hash}")
+ expect(page).to have_selector(".files ##{files[1].file_hash}")
+ end
+ end
+ end
+end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index f8e84043c1b..1622979812d 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -138,9 +138,8 @@ RSpec.describe 'Commits' do
end
end
- context 'when accessing internal project with disallowed access', :js do
+ context 'when accessing internal project with disallowed access', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/299575' 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/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index b419a063858..e75e661b513 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -9,6 +9,26 @@ RSpec.describe 'Dashboard > Activity' do
sign_in(user)
end
+ context 'tabs' do
+ it 'shows Your Projects' do
+ visit activity_dashboard_path
+
+ expect(find('.top-area .nav-tabs li.active')).to have_content('Your projects')
+ end
+
+ it 'shows Starred Projects' do
+ visit activity_dashboard_path(filter: 'starred')
+
+ expect(find('.top-area .nav-tabs li.active')).to have_content('Starred projects')
+ end
+
+ it 'shows Followed Projects' do
+ visit activity_dashboard_path(filter: 'followed')
+
+ expect(find('.top-area .nav-tabs li.active')).to have_content('Followed users')
+ end
+ end
+
context 'rss' do
before do
visit activity_dashboard_path
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index 2ad77a2884c..86743e31fbd 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'Thread Comments Issue', :js do
let(:issue) { create(:issue, project: project) }
before do
+ stub_feature_flags(remove_comment_close_reopen: false)
+
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index 761cc7ae796..82dcdf9f918 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe 'Thread Comments Merge Request', :js do
before do
stub_feature_flags(remove_resolve_note: false)
+ stub_feature_flags(remove_comment_close_reopen: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index b2d3fbf4b5d..42053e571e9 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -4,15 +4,34 @@ require 'spec_helper'
RSpec.describe 'Thread Comments Snippet', :js do
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
- let_it_be(:snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
before do
- project.add_maintainer(user)
sign_in(user)
+ end
+
+ context 'with project snippets' do
+ let_it_be(:project) do
+ create(:project).tap do |p|
+ p.add_maintainer(user)
+ end
+ end
+
+ let_it_be(:snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
+
+ before do
+ visit project_snippet_path(project, snippet)
+ end
- visit project_snippet_path(project, snippet)
+ it_behaves_like 'thread comments', 'snippet'
end
- it_behaves_like 'thread comments', 'snippet'
+ context 'with personal snippets' do
+ let_it_be(:snippet) { create(:personal_snippet, :private, :repository, author: user) }
+
+ before do
+ visit snippet_path(snippet)
+ end
+
+ it_behaves_like 'thread comments', 'snippet'
+ end
end
diff --git a/spec/features/groups/import_export/connect_instance_spec.rb b/spec/features/groups/import_export/connect_instance_spec.rb
index 2e1bf27ba8b..73de49101ea 100644
--- a/spec/features/groups/import_export/connect_instance_spec.rb
+++ b/spec/features/groups/import_export/connect_instance_spec.rb
@@ -23,8 +23,8 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do
source_url = 'https://gitlab.com'
pat = 'demo-pat'
stub_path = 'stub-group'
-
- stub_request(:get, "%{url}/api/v4/groups?page=1&per_page=30&top_level_only=true&min_access_level=40" % { url: source_url }).to_return(
+ total = 37
+ stub_request(:get, "%{url}/api/v4/groups?page=1&per_page=20&top_level_only=true&min_access_level=40&search=" % { url: source_url }).to_return(
body: [{
id: 2595438,
web_url: 'https://gitlab.com/groups/auto-breakfast',
@@ -33,17 +33,25 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do
full_name: 'Stub',
full_path: stub_path
}].to_json,
- headers: { 'Content-Type' => 'application/json' }
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'X-Next-Page' => 2,
+ 'X-Page' => 1,
+ 'X-Per-Page' => 20,
+ 'X-Total' => total,
+ 'X-Total-Pages' => 2
+ }
)
expect(page).to have_content 'Import groups from another instance of GitLab'
+ expect(page).to have_content 'Not all related objects are migrated'
fill_in :bulk_import_gitlab_url, with: source_url
fill_in :bulk_import_gitlab_access_token, with: pat
click_on 'Connect instance'
- expect(page).to have_content 'Importing groups from %{url}' % { url: source_url }
+ expect(page).to have_content 'Showing 1-1 of %{total} groups from %{url}' % { url: source_url, total: total }
expect(page).to have_content stub_path
end
end
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index a4c450c9a2c..7025874a4ff 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -87,12 +87,4 @@ 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/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb
index b8ffd73335d..45ea77e3868 100644
--- a/spec/features/groups/settings/packages_and_registries_spec.rb
+++ b/spec/features/groups/settings/packages_and_registries_spec.rb
@@ -7,15 +7,17 @@ RSpec.describe 'Group Packages & Registries settings' do
let(:user) { create(:user) }
let(:group) { create(:group) }
+ let(:sub_group) { create(:group, parent: group) }
before do
group.add_owner(user)
+ sub_group.add_owner(user)
sign_in(user)
end
- context 'when the feature flag is off' do
+ context 'when packges feature is disabled on the group' do
before do
- stub_feature_flags(packages_and_registries_group_settings: false)
+ stub_packages_setting(enabled: false)
end
it 'the menu item is not visible' do
@@ -25,9 +27,15 @@ RSpec.describe 'Group Packages & Registries settings' do
expect(settings_menu).not_to have_content 'Packages & Registries'
end
+
+ it 'renders 404 when navigating to page' do
+ visit_settings_page
+
+ expect(page).to have_content('Not Found')
+ end
end
- context 'when the feature flag is on' do
+ context 'when packages feature is enabled on the group' do
it 'the menu item is visible' do
visit group_path(group)
@@ -47,6 +55,56 @@ RSpec.describe 'Group Packages & Registries settings' do
sidebar = find('.nav-sidebar')
expect(sidebar).to have_link _('Packages & Registries')
end
+
+ it 'has a Package Registry section', :js do
+ visit_settings_page
+
+ expect(page).to have_content('Package Registry')
+ expect(page).to have_button('Collapse')
+ end
+
+ it 'automatically saves changes to the server', :js do
+ visit_settings_page
+
+ expect(page).to have_content('Allow duplicates')
+
+ find('.gl-toggle').click
+
+ expect(page).to have_content('Do not allow duplicates')
+
+ visit_settings_page
+
+ expect(page).to have_content('Do not allow duplicates')
+ end
+
+ it 'shows an error on wrong regex', :js do
+ visit_settings_page
+
+ expect(page).to have_content('Allow duplicates')
+
+ find('.gl-toggle').click
+
+ expect(page).to have_content('Do not allow duplicates')
+
+ fill_in 'Exceptions', with: ')'
+
+ # simulate blur event
+ find('body').click
+
+ expect(page).to have_content('is an invalid regexp')
+ end
+
+ context 'in a sub group' do
+ it 'works correctly', :js do
+ visit_sub_group_settings_page
+
+ expect(page).to have_content('Allow duplicates')
+
+ find('.gl-toggle').click
+
+ expect(page).to have_content('Do not allow duplicates')
+ end
+ end
end
def find_settings_menu
@@ -56,4 +114,8 @@ RSpec.describe 'Group Packages & Registries settings' do
def visit_settings_page
visit group_settings_packages_and_registries_path(group)
end
+
+ def visit_sub_group_settings_page
+ visit group_settings_packages_and_registries_path(sub_group)
+ end
end
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index 3a42fd508b4..5067f11be67 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -163,6 +163,7 @@ RSpec.describe 'Group show page' do
let!(:project) { create(:project, namespace: group) }
before do
+ stub_feature_flags(vue_notification_dropdown: false)
group.add_maintainer(maintainer)
sign_in(maintainer)
end
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 1f8397e45f7..90647305281 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Help Pages' do
it 'opens shortcuts help dialog' do
find('.js-trigger-shortcut').click
- expect(page).to have_selector('#modal-shortcuts')
+ expect(page).to have_selector('[data-testid="modal-shortcuts"]')
end
end
end
diff --git a/spec/features/ide/user_opens_merge_request_spec.rb b/spec/features/ide/user_opens_merge_request_spec.rb
index e6101e90a83..7ae43f35901 100644
--- a/spec/features/ide/user_opens_merge_request_spec.rb
+++ b/spec/features/ide/user_opens_merge_request_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'IDE merge request', :js do
- let(:merge_request) { create(:merge_request, :with_diffs, :simple, source_project: project) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { project.owner }
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
index cfd0c7e210f..dfd6211a683 100644
--- a/spec/features/import/manifest_import_spec.rb
+++ b/spec/features/import/manifest_import_spec.rb
@@ -52,6 +52,6 @@ RSpec.describe 'Import multiple repositories by uploading a manifest file', :js
end
def second_row
- page.all('table.import-table tbody tr')[1]
+ page.all('table tbody tr')[1]
end
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 3f00bdc478d..a0786d36fdf 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe 'issuable list', :js do
visit_issuable_list(:issue)
- expect(page).to have_text('Open ? Closed ? All ?')
+ expect(page).to have_text('Open Closed All')
end
it "counts merge requests closing issues icons for each issue" do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 9b2a11c4b0e..e2087868035 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -183,6 +183,16 @@ RSpec.describe 'GFM autocomplete', :js do
expect(find('#at-view-users')).to have_content(user.name)
end
+ it 'searches across full name for assignees' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('@speciÄ…lsome')
+ end
+
+ wait_for_requests
+
+ expect(find('.atwho-view li', visible: true)).to have_content(user.name)
+ end
+
it 'selects the first item for non-assignee dropdowns if a query is entered' do
page.within '.timeline-content-form' do
find('#note-body').native.send_keys(':1')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 59fba5f65e0..ca44978d223 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -11,6 +11,11 @@ RSpec.describe 'Issue Sidebar' do
let!(:label) { create(:label, project: project, title: 'bug') }
let(:issue) { create(:labeled_issue, project: project, labels: [label]) }
let!(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
+ let!(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) }
+ let!(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') }
+ let!(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) }
+ let!(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) }
+ let!(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) }
before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
@@ -134,6 +139,36 @@ RSpec.describe 'Issue Sidebar' do
end
end
+ context 'editing issue milestone', :js do
+ before do
+ page.within('.block.milestone > .title') do
+ click_on 'Edit'
+ end
+ end
+
+ it 'shows milestons list in the dropdown' do
+ page.within('.block.milestone .dropdown-content') do
+ # 5 milestones + "No milestone" = 6 items
+ expect(page.find('ul')).to have_selector('li[data-milestone-id]', count: 6)
+ end
+ end
+
+ it 'shows expired milestone at the bottom of the list' do
+ page.within('.block.milestone .dropdown-content ul') do
+ expect(page.find('li:last-child')).to have_content milestone_expired.title
+ end
+ end
+
+ it 'shows milestone due earliest at the top of the list' do
+ page.within('.block.milestone .dropdown-content ul') do
+ expect(page.all('li[data-milestone-id]')[1]).to have_content milestone3.title
+ expect(page.all('li[data-milestone-id]')[2]).to have_content milestone2.title
+ expect(page.all('li[data-milestone-id]')[3]).to have_content milestone1.title
+ expect(page.all('li[data-milestone-id]')[4]).to have_content milestone_no_duedate.title
+ end
+ end
+ end
+
context 'editing issue labels', :js do
before do
issue.update(labels: [label])
diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb
index d5a115433aa..409f498798b 100644
--- a/spec/features/issues/issue_state_spec.rb
+++ b/spec/features/issues/issue_state_spec.rb
@@ -42,9 +42,15 @@ RSpec.describe 'issue state', :js do
end
describe 'when open', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297348' do
- context 'when clicking the top `Close issue` button', :aggregate_failures do
- let(:open_issue) { create(:issue, project: project) }
+ let(:open_issue) { create(:issue, project: project) }
+ it_behaves_like 'page with comment and close button', 'Close issue' do
+ def setup
+ visit project_issue_path(project, open_issue)
+ end
+ end
+
+ context 'when clicking the top `Close issue` button', :aggregate_failures do
before do
visit project_issue_path(project, open_issue)
end
@@ -53,9 +59,8 @@ RSpec.describe 'issue state', :js do
end
context 'when clicking the bottom `Close issue` button', :aggregate_failures do
- let(:open_issue) { create(:issue, project: project) }
-
before do
+ stub_feature_flags(remove_comment_close_reopen: false)
visit project_issue_path(project, open_issue)
end
@@ -64,9 +69,15 @@ RSpec.describe 'issue state', :js do
end
describe 'when closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297201' do
- context 'when clicking the top `Reopen issue` button', :aggregate_failures do
- let(:closed_issue) { create(:issue, project: project, state: 'closed') }
+ let(:closed_issue) { create(:issue, project: project, state: 'closed') }
+ it_behaves_like 'page with comment and close button', 'Reopen issue' do
+ def setup
+ visit project_issue_path(project, closed_issue)
+ end
+ end
+
+ context 'when clicking the top `Reopen issue` button', :aggregate_failures do
before do
visit project_issue_path(project, closed_issue)
end
@@ -75,9 +86,8 @@ RSpec.describe 'issue state', :js do
end
context 'when clicking the bottom `Reopen issue` button', :aggregate_failures do
- let(:closed_issue) { create(:issue, project: project, state: 'closed') }
-
before do
+ stub_feature_flags(remove_comment_close_reopen: false)
visit project_issue_path(project, closed_issue)
end
diff --git a/spec/features/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb
index 24a261f592b..004488f2f64 100644
--- a/spec/features/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/issues/user_comments_on_issue_spec.rb
@@ -39,8 +39,6 @@ RSpec.describe "User comments on issue", :js do
add_note(comment)
- wait_for_requests
-
expect(page.find('pre code').text).to eq code_block_content
end
@@ -51,8 +49,6 @@ RSpec.describe "User comments on issue", :js do
add_note(comment)
- wait_for_requests
-
expect(page.find('svg.mermaid')).to have_content html_content
within('svg.mermaid') { expect(page).not_to have_selector('img') }
end
diff --git a/spec/features/issues/user_creates_issue_by_email_spec.rb b/spec/features/issues/user_creates_issue_by_email_spec.rb
index 5a0036170ab..c47f24ab836 100644
--- a/spec/features/issues/user_creates_issue_by_email_spec.rb
+++ b/spec/features/issues/user_creates_issue_by_email_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Issues > User creates issue by email' do
project.add_developer(user)
end
- describe 'new issue by email' do
+ describe 'new issue by email', :js do
shared_examples 'show the email in the modal' do
let(:issue) { create(:issue, project: project) }
@@ -28,7 +28,7 @@ RSpec.describe 'Issues > User creates issue by email' do
page.within '#issuable-email-modal' do
email = project.new_issuable_address(user, 'issue')
- expect(page).to have_selector("input[value='#{email}']")
+ expect(page.find('input[type="text"]').value).to eq email
end
end
end
diff --git a/spec/features/issues/user_resets_their_incoming_email_token_spec.rb b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
index a20f65abebf..2b1c25174c2 100644
--- a/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
+++ b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
@@ -16,17 +16,17 @@ RSpec.describe 'Issues > User resets their incoming email token' do
end
it 'changes incoming email address token', :js do
- find('.issuable-email-modal-btn').click
- previous_token = find('input#issuable_email').value
- find('.incoming-email-token-reset').click
-
- wait_for_requests
-
- expect(page).to have_no_field('issuable_email', with: previous_token)
- new_token = project.new_issuable_address(user.reload, 'issue')
- expect(page).to have_field(
- 'issuable_email',
- with: new_token
- )
+ page.find('[data-testid="issuable-email-modal-btn"]').click
+
+ page.within '#issuable-email-modal' do
+ previous_token = page.find('input[type="text"]').value
+ page.find('[data-testid="incoming-email-token-reset"]').click
+
+ wait_for_requests
+
+ expect(page.find('input[type="text"]').value).not_to eq previous_token
+ new_token = project.new_issuable_address(user.reload, 'issue')
+ expect(page.find('input[type="text"]').value).to eq new_token
+ end
end
end
diff --git a/spec/features/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb
index 971c8a3b431..d91c187c840 100644
--- a/spec/features/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/issues/user_toggles_subscription_spec.rb
@@ -15,13 +15,13 @@ RSpec.describe "User toggles subscription", :js do
end
it "unsubscribes from issue" do
- subscription_button = find(".js-issuable-subscribe-button")
+ subscription_button = find('[data-testid="subscription-toggle"]')
# Check we're subscribed.
expect(subscription_button).to have_css("button.is-checked")
# Toggle subscription.
- find(".js-issuable-subscribe-button button").click
+ find('[data-testid="subscription-toggle"]').click
wait_for_requests
# Check we're unsubscribed.
@@ -33,7 +33,7 @@ RSpec.describe "User toggles subscription", :js do
it 'is disabled' do
expect(page).to have_content('Notifications have been disabled by the project or group owner')
- expect(page).not_to have_selector('.js-issuable-subscribe-button')
+ expect(page).not_to have_selector('[data-testid="subscription-toggle"]')
end
end
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 5d141580874..aeb42cc2edb 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe 'Labels Hierarchy', :js do
before do
stub_feature_flags(graphql_board_lists: false)
+ stub_feature_flags(board_new_list: false)
grandparent.add_owner(user)
sign_in(user)
@@ -270,6 +271,10 @@ RSpec.describe 'Labels Hierarchy', :js do
end
context 'creating boards lists' do
+ before do
+ stub_feature_flags(board_new_list: false)
+ end
+
context 'on project boards' do
let(:board) { create(:board, project: project_1) }
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index 151ef76e884..8e28f89f49e 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -206,6 +206,9 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
# `markdown` helper expects a `@project` and `@group` variable
@project = @feat.project
@group = @feat.group
+
+ stub_application_setting(plantuml_enabled: true, plantuml_url: 'http://localhost:8080')
+ stub_application_setting(kroki_enabled: true, kroki_url: 'http://localhost:8000')
end
let(:project) { @feat.project } # Shadow this so matchers can use it
@@ -265,6 +268,18 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'ColorFilter' do
expect(doc).to parse_colors
end
+
+ aggregate_failures 'MermaidFilter' do
+ expect(doc).to parse_mermaid
+ end
+
+ aggregate_failures 'PlantumlFilter' do
+ expect(doc).to parse_plantuml
+ end
+
+ aggregate_failures 'KrokiFilter' do
+ expect(doc).to parse_kroki
+ end
end
end
@@ -338,6 +353,18 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'ColorFilter' do
expect(doc).to parse_colors
end
+
+ aggregate_failures 'MermaidFilter' do
+ expect(doc).to parse_mermaid
+ end
+
+ aggregate_failures 'PlantumlFilter' do
+ expect(doc).to parse_plantuml
+ end
+
+ aggregate_failures 'KrokiFilter' do
+ expect(doc).to parse_kroki
+ end
end
end
diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb
index 23cdd9d2ce5..207678e07c3 100644
--- a/spec/features/markdown/mermaid_spec.rb
+++ b/spec/features/markdown/mermaid_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe 'Mermaid rendering', :js do
expect(svg[:style]).to match(/max-width/)
expect(svg[:width].to_i).to eq(100)
- expect(svg[:height].to_i).to eq(0)
+ expect(svg[:height].to_i).to be_within(5).of(220)
end
end
diff --git a/spec/features/merge_request/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index 3d18aef9327..d4b185a82e9 100644
--- a/spec/features/merge_request/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'User accepts a merge request', :js, :sidekiq_might_not_need_inline do
- let(:merge_request) { create(:merge_request, :with_diffs, :simple, source_project: project) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
index 2b94c072c8b..ab3ef7c1ac0 100644
--- a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
+++ b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User closes/reopens a merge request', :js do
+RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297500' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -12,9 +12,15 @@ RSpec.describe 'User closes/reopens a merge request', :js do
end
describe 'when open' do
- context 'when clicking the top `Close merge request` link', :aggregate_failures do
- let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'page with comment and close button', 'Close merge request' do
+ def setup
+ visit merge_request_path(open_merge_request)
+ end
+ end
+ context 'when clicking the top `Close merge request` link', :aggregate_failures do
before do
visit merge_request_path(open_merge_request)
end
@@ -34,9 +40,8 @@ RSpec.describe 'User closes/reopens a merge request', :js do
end
context 'when clicking the bottom `Close merge request` button', :aggregate_failures do
- let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
-
before do
+ stub_feature_flags(remove_comment_close_reopen: false)
visit merge_request_path(open_merge_request)
end
@@ -55,10 +60,23 @@ RSpec.describe 'User closes/reopens a merge request', :js do
end
end
- describe 'when closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297500' do
- context 'when clicking the top `Reopen merge request` link', :aggregate_failures do
- let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
+ describe 'when closed' do
+ let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
+ it_behaves_like 'page with comment and close button', 'Close merge request' do
+ def setup
+ visit merge_request_path(closed_merge_request)
+
+ within '.detail-page-header' do
+ click_button 'Toggle dropdown'
+ click_link 'Reopen merge request'
+ end
+
+ wait_for_requests
+ end
+ end
+
+ context 'when clicking the top `Reopen merge request` link', :aggregate_failures do
before do
visit merge_request_path(closed_merge_request)
end
@@ -78,9 +96,8 @@ RSpec.describe 'User closes/reopens a merge request', :js do
end
context 'when clicking the bottom `Reopen merge request` button', :aggregate_failures do
- let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
-
before do
+ stub_feature_flags(remove_comment_close_reopen: false)
visit merge_request_path(closed_merge_request)
end
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
index 817b4e0b48e..2c949ed84f4 100644
--- a/spec/features/merge_request/user_edits_mr_spec.rb
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -20,14 +20,4 @@ RSpec.describe 'Merge request > User edits MR' do
include_context 'merge request edit context'
it_behaves_like 'an editable merge request'
end
-
- context 'when merge_request_reviewers is turned off' do
- before do
- stub_feature_flags(merge_request_reviewers: false)
- end
-
- it 'does not render reviewers dropdown' do
- expect(page).not_to have_selector('.js-reviewer-search')
- end
- end
end
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index 9ed5b67fa0e..3cdb22000f6 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'User manages subscription', :js do
end
it 'toggles subscription' do
- page.within('.js-issuable-subscribe-button') do
+ page.within('[data-testid="subscription-toggle"]') do
wait_for_requests
expect(page).to have_css 'button:not(.is-checked)'
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index ea3e90a4508..8438c0af553 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js do
wait_for_requests
expect(page).to have_css('button[disabled="disabled"]', text: 'Merge')
- expect(page).to have_content('Please retry the job or push a new commit to fix the failure')
+ expect(page).to have_content('The pipeline for this merge request did not complete. Push a new commit to fix the failure, or check the troubleshooting documentation to see other possible actions.')
end
end
@@ -70,7 +70,7 @@ RSpec.describe 'Merge request > User merges only if pipeline succeeds', :js do
wait_for_requests
expect(page).not_to have_button 'Merge'
- expect(page).to have_content('Please retry the job or push a new commit to fix the failure')
+ expect(page).to have_content('The pipeline for this merge request did not complete. Push a new commit to fix the failure, or check the troubleshooting documentation to see other possible actions.')
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 5e99383e4a1..63b463a2c5f 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
@@ -68,7 +68,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
wait_for_requests
- expect(page).to have_content 'Merge when pipeline succeeds', wait: 0
+ expect(page).to have_content 'Merge when pipeline succeeds'
end
it_behaves_like 'Merge when pipeline succeeds activator'
@@ -145,7 +145,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
before do
merge_request.update!(
merge_user: merge_request.author,
- merge_error: 'Something went wrong.'
+ merge_error: 'Something went wrong'
)
refresh
end
@@ -155,7 +155,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
wait_for_requests
page.within('.mr-section-container') do
- expect(page).to have_content('Merge failed: Something went wrong. Please try again.')
+ expect(page).to have_content('Something went wrong. Try again.')
end
end
end
@@ -174,7 +174,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js do
wait_for_requests
page.within('.mr-section-container') do
- expect(page).to have_content('Merge failed: Something went wrong. Please try again.')
+ expect(page).to have_content('Something went wrong. Try again.')
end
end
end
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index 489582521b5..e629bc0dc53 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -161,7 +161,7 @@ RSpec.describe 'Merge request > User posts notes', :js do
fill_in 'note[note]', with: 'Some new content'
accept_confirm do
- find('.btn-cancel').click
+ find('[data-testid="cancel"]').click
end
end
expect(find('.js-note-text').text).to eq ''
diff --git a/spec/features/merge_request/user_reverts_merge_request_spec.rb b/spec/features/merge_request/user_reverts_merge_request_spec.rb
index 5e9611de460..9cbba6c470f 100644
--- a/spec/features/merge_request/user_reverts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_reverts_merge_request_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'User reverts a merge request', :js do
- let(:merge_request) { create(:merge_request, :with_diffs, :simple, source_project: project) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
@@ -17,46 +17,28 @@ RSpec.describe 'User reverts a merge request', :js do
wait_for_requests
- visit(merge_request_path(merge_request))
+ # do not reload the page by visiting, let javascript update the page as it will validate we have loaded the modal
+ # code correctly on page update that adds the `revert` button
end
it 'reverts a merge request', :sidekiq_might_not_need_inline do
- find("a[href='#modal-revert-commit']").click
+ revert_commit
- page.within('#modal-revert-commit') do
- uncheck('create_merge_request')
- click_button('Revert')
- end
+ wait_for_requests
expect(page).to have_content('The merge request has been successfully reverted.')
-
- wait_for_requests
end
it 'does not revert a merge request that was previously reverted', :sidekiq_might_not_need_inline do
- find("a[href='#modal-revert-commit']").click
-
- page.within('#modal-revert-commit') do
- uncheck('create_merge_request')
- click_button('Revert')
- end
-
- find("a[href='#modal-revert-commit']").click
+ revert_commit
- page.within('#modal-revert-commit') do
- uncheck('create_merge_request')
- click_button('Revert')
- end
+ revert_commit
expect(page).to have_content('Sorry, we cannot revert this merge request automatically.')
end
it 'reverts a merge request in a new merge request', :sidekiq_might_not_need_inline do
- find("a[href='#modal-revert-commit']").click
-
- page.within('#modal-revert-commit') do
- click_button('Revert')
- end
+ revert_commit(create_merge_request: true)
expect(page).to have_content('The merge request has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
end
@@ -68,4 +50,13 @@ RSpec.describe 'User reverts a merge request', :js do
expect(page).not_to have_link('Revert')
end
+
+ def revert_commit(create_merge_request: false)
+ click_button('Revert')
+
+ page.within('[data-testid="modal-commit"]') do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Revert')
+ end
+ end
end
diff --git a/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
index 7f4249336fe..78c1b2a718e 100644
--- a/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe 'Merge request > User cherry-picks', :js do
it 'does not show a Cherry-pick button' do
visit project_merge_request_path(project, merge_request)
- expect(page).not_to have_link 'Cherry-pick'
+ expect(page).not_to have_button 'Cherry-pick'
end
end
@@ -40,7 +40,7 @@ RSpec.describe 'Merge request > User cherry-picks', :js do
it 'shows a Cherry-pick button' do
visit project_merge_request_path(project, merge_request)
- expect(page).to have_link 'Cherry-pick'
+ expect(page).to have_button 'Cherry-pick'
end
it 'hides the cherry pick button for an archived project' do
@@ -48,7 +48,7 @@ RSpec.describe 'Merge request > User cherry-picks', :js do
visit project_merge_request_path(project, merge_request)
- expect(page).not_to have_link 'Cherry-pick'
+ expect(page).not_to have_button 'Cherry-pick'
end
end
@@ -56,18 +56,12 @@ RSpec.describe 'Merge request > User cherry-picks', :js do
before do
visit project_merge_request_path(project, merge_request)
- click_link('Cherry-pick')
+ click_button('Cherry-pick')
end
it 'shows the cherry-pick modal' do
expect(page).to have_content('Cherry-pick this merge request')
end
-
- it 'closes the cherry-pick modal with escape keypress' do
- find('#modal-cherry-pick-commit').send_keys(:escape)
-
- expect(page).not_to have_content('Start a new merge request with these changes')
- end
end
end
end
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index d9743f6f330..708ce53b4fe 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -160,7 +160,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'merges the merge request' do
expect(page).to have_content('Merged by')
- expect(page).to have_link('Revert')
+ expect(page).to have_button('Revert')
end
end
@@ -357,7 +357,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'merges the merge request' do
expect(page).to have_content('Merged by')
- expect(page).to have_link('Revert')
+ expect(page).to have_button('Revert')
end
end
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index c2b2ada47be..0854a8b9fb7 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -77,15 +77,26 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
end
it 'allows me to merge, see cherry-pick modal and load branches list', :sidekiq_might_not_need_inline do
+ modal_selector = '[data-testid="modal-commit"]'
+
wait_for_requests
click_button 'Merge'
wait_for_requests
- click_link 'Cherry-pick'
- page.find('.js-project-refs-dropdown').click
- wait_for_requests
- expect(page.all('.js-cherry-pick-form .dropdown-content li').size).to be > 1
+ click_button 'Cherry-pick'
+
+ page.within(modal_selector) do
+ click_button 'master'
+ end
+
+ page.within("#{modal_selector} .dropdown-menu") do
+ find('[data-testid="dropdown-search-box"]').set('')
+
+ wait_for_requests
+
+ expect(page.all('[data-testid="dropdown-item"]').size).to be > 1
+ end
end
end
@@ -319,7 +330,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
wait_for_requests
page.within('.mr-section-container') do
- expect(page).to have_content('Merge failed: Something went wrong')
+ expect(page).to have_content('Something went wrong.')
end
end
end
@@ -340,7 +351,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
wait_for_requests
page.within('.mr-section-container') do
- expect(page).to have_content('Merge failed: Something went wrong')
+ expect(page).to have_content('Something went wrong.')
end
end
end
@@ -377,7 +388,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
click_button 'Merge'
page.within('.mr-widget-body') do
- expect(page).to have_content('Conflicts detected during merge')
+ expect(page).to have_content('An error occurred while merging')
end
end
end
diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
index 04d8c52df61..1ef6d2a1068 100644
--- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
@@ -9,152 +9,166 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running', sha: project.commit.id) }
let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test') }
- before do
- build.run
- build.trace.set('hello')
- sign_in(user)
- visit_merge_request
- end
-
- def visit_merge_request(format: :html, serializer: nil)
- visit project_merge_request_path(project, merge_request, format: format, serializer: serializer)
- end
-
- it 'displays a mini pipeline graph' do
- expect(page).to have_selector('.mr-widget-pipeline-graph')
- end
-
- context 'as json' do
- let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') }
- let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') }
-
+ shared_examples 'mini pipeline renders' do |ci_mini_pipeline_gl_dropdown_enabled|
before do
- job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
- create(:ci_job_artifact, :archive, file: artifacts_file1, job: job)
- create(:ci_build, :manual, pipeline: pipeline, when: 'manual')
+ build.run
+ build.trace.set('hello')
+ sign_in(user)
+ stub_feature_flags(ci_mini_pipeline_gl_dropdown: ci_mini_pipeline_gl_dropdown_enabled)
+ visit_merge_request
end
- # TODO: https://gitlab.com/gitlab-org/gitlab-foss/issues/48034
- xit 'avoids repeated database queries' do
- before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') }
-
- job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
- create(:ci_job_artifact, :archive, file: artifacts_file2, job: job)
- create(:ci_build, :manual, pipeline: pipeline, when: 'manual')
-
- after = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') }
+ let_it_be(:dropdown_toggle_selector) do
+ if ci_mini_pipeline_gl_dropdown_enabled
+ '[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle'
+ else
+ '[data-testid="mini-pipeline-graph-dropdown-toggle"]'
+ end
+ end
- expect(before.count).to eq(after.count)
- expect(before.cached_count).to eq(after.cached_count)
+ def visit_merge_request(format: :html, serializer: nil)
+ visit project_merge_request_path(project, merge_request, format: format, serializer: serializer)
end
- end
- describe 'build list toggle' do
- let(:toggle) do
- find('.mini-pipeline-graph-dropdown-toggle')
- first('.mini-pipeline-graph-dropdown-toggle')
+ it 'displays a mini pipeline graph' do
+ expect(page).to have_selector('.mr-widget-pipeline-graph')
end
- # Status icon button styles should update as described in
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/42769
- it 'has unique styles for default, :hover, :active, and :focus states' do
- find('.mini-pipeline-graph-dropdown-toggle')
- default_background_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('background-color');")
- default_foreground_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible svg').css('fill');")
- default_box_shadow = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('box-shadow');")
+ context 'as json' do
+ let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') }
+ let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') }
- toggle.hover
+ before do
+ job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
+ create(:ci_job_artifact, :archive, file: artifacts_file1, job: job)
+ create(:ci_build, :manual, pipeline: pipeline, when: 'manual')
+ end
- find('.mini-pipeline-graph-dropdown-toggle')
- hover_background_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('background-color');")
- hover_foreground_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible svg').css('fill');")
- hover_box_shadow = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('box-shadow');")
+ # TODO: https://gitlab.com/gitlab-org/gitlab-foss/issues/48034
+ xit 'avoids repeated database queries' do
+ before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') }
- page.driver.browser.action.click_and_hold(toggle.native).perform
+ job = create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
+ create(:ci_job_artifact, :archive, file: artifacts_file2, job: job)
+ create(:ci_build, :manual, pipeline: pipeline, when: 'manual')
- find('.mini-pipeline-graph-dropdown-toggle')
- active_background_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('background-color');")
- active_foreground_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible svg').css('fill');")
- active_box_shadow = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('box-shadow');")
+ after = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') }
- page.driver.browser.action.release(toggle.native)
- .move_by(100, 100)
- .perform
+ expect(before.count).to eq(after.count)
+ expect(before.cached_count).to eq(after.cached_count)
+ end
+ end
- find('.mini-pipeline-graph-dropdown-toggle')
- focus_background_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('background-color');")
- focus_foreground_color = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible svg').css('fill');")
- focus_box_shadow = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').css('box-shadow');")
+ describe 'build list toggle' do
+ let(:toggle) do
+ find(dropdown_toggle_selector)
+ first(dropdown_toggle_selector)
+ end
- expect(default_background_color).not_to eq(hover_background_color)
- expect(hover_background_color).not_to eq(active_background_color)
- expect(default_background_color).not_to eq(active_background_color)
+ # Status icon button styles should update as described in
+ # https://gitlab.com/gitlab-org/gitlab-foss/issues/42769
+ it 'has unique styles for default, :hover, :active, and :focus states' do
+ default_background_color, default_foreground_color, default_box_shadow = get_toggle_colors(dropdown_toggle_selector)
- expect(default_foreground_color).not_to eq(hover_foreground_color)
- expect(hover_foreground_color).not_to eq(active_foreground_color)
- expect(default_foreground_color).not_to eq(active_foreground_color)
+ toggle.hover
+ hover_background_color, hover_foreground_color, hover_box_shadow = get_toggle_colors(dropdown_toggle_selector)
- expect(focus_background_color).to eq(hover_background_color)
- expect(focus_foreground_color).to eq(hover_foreground_color)
+ page.driver.browser.action.click_and_hold(toggle.native).perform
+ active_background_color, active_foreground_color, active_box_shadow = get_toggle_colors(dropdown_toggle_selector)
+ page.driver.browser.action.release(toggle.native).perform
- expect(default_box_shadow).to eq('none')
- expect(hover_box_shadow).to eq('none')
- expect(active_box_shadow).not_to eq('none')
- expect(focus_box_shadow).not_to eq('none')
- end
+ page.driver.browser.action.click(toggle.native).move_by(100, 100).perform
+ focus_background_color, focus_foreground_color, focus_box_shadow = get_toggle_colors(dropdown_toggle_selector)
- it 'shows tooltip when hovered' do
- toggle.hover
+ expect(default_background_color).not_to eq(hover_background_color)
+ expect(hover_background_color).not_to eq(active_background_color)
+ expect(default_background_color).not_to eq(active_background_color)
- expect(page).to have_selector('.tooltip')
- end
- end
+ expect(default_foreground_color).not_to eq(hover_foreground_color)
+ expect(hover_foreground_color).not_to eq(active_foreground_color)
+ expect(default_foreground_color).not_to eq(active_foreground_color)
- describe 'builds list menu' do
- let(:toggle) do
- find('.mini-pipeline-graph-dropdown-toggle')
- first('.mini-pipeline-graph-dropdown-toggle')
- end
+ expect(focus_background_color).to eq(hover_background_color)
+ expect(focus_foreground_color).to eq(hover_foreground_color)
- before do
- toggle.click
- wait_for_requests
- end
+ expect(default_box_shadow).to eq('none')
+ expect(hover_box_shadow).to eq('none')
+ expect(active_box_shadow).not_to eq('none')
+ expect(focus_box_shadow).not_to eq('none')
+ end
+
+ it 'shows tooltip when hovered' do
+ toggle.hover
- it 'pens when toggle is clicked' do
- expect(toggle.find(:xpath, '..')).to have_selector('.mini-pipeline-graph-dropdown-menu')
+ expect(page).to have_selector('.tooltip')
+ end
end
- it 'closes when toggle is clicked again' do
- toggle.click
+ describe 'builds list menu' do
+ let(:toggle) do
+ find(dropdown_toggle_selector)
+ first(dropdown_toggle_selector)
+ end
- expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu')
- end
+ before do
+ toggle.click
+ wait_for_requests
+ end
- it 'closes when clicking somewhere else' do
- find('body').click
+ it 'pens when toggle is clicked' do
+ expect(toggle.find(:xpath, '..')).to have_selector('.mini-pipeline-graph-dropdown-menu')
+ end
- expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu')
- end
+ it 'closes when toggle is clicked again' do
+ toggle.click
- describe 'build list build item' do
- let(:build_item) do
- find('.mini-pipeline-graph-dropdown-item')
- first('.mini-pipeline-graph-dropdown-item')
+ expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu')
end
- it 'visits the build page when clicked' do
- build_item.click
- find('.build-page')
+ it 'closes when clicking somewhere else' do
+ find('body').click
- expect(current_path).to eql(project_job_path(project, build))
+ expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu')
end
- it 'shows tooltip when hovered' do
- build_item.hover
+ describe 'build list build item' do
+ let(:build_item) do
+ find('.mini-pipeline-graph-dropdown-item')
+ first('.mini-pipeline-graph-dropdown-item')
+ end
- expect(page).to have_selector('.tooltip')
+ it 'visits the build page when clicked' do
+ build_item.click
+ find('.build-page')
+
+ expect(current_path).to eql(project_job_path(project, build))
+ end
+
+ it 'shows tooltip when hovered' do
+ build_item.hover
+
+ expect(page).to have_selector('.tooltip')
+ end
end
end
end
+
+ context 'with ci_mini_pipeline_gl_dropdown disabled' do
+ it_behaves_like "mini pipeline renders", false
+ end
+
+ context 'with ci_mini_pipeline_gl_dropdown enabled' do
+ it_behaves_like "mini pipeline renders", true
+ end
+
+ private
+
+ def get_toggle_colors(selector)
+ find(selector)
+ [
+ evaluate_script("$('#{selector}:visible').css('background-color');"),
+ evaluate_script("$('#{selector}:visible svg').css('fill');"),
+ evaluate_script("$('#{selector}:visible').css('box-shadow');")
+ ]
+ 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 a2ec34335ec..bbeb91bbd19 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
@@ -73,6 +73,23 @@ RSpec.describe 'User comments on a diff', :js do
end
end
+ it 'allows suggestions in replies' do
+ click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
+ click_button('Add comment now')
+ end
+
+ wait_for_requests
+
+ click_button 'Reply...'
+
+ find('.js-suggestion-btn').click
+
+ expect(find('.js-vue-issue-note-form').value).to include("url = https://github.com/gitlabhq/gitlab-shell.git")
+ end
+
it 'suggestion is appliable' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
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 e8998f9457a..9bda48a3ec5 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
@@ -30,19 +30,6 @@ RSpec.describe 'User views an open merge request' do
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
-
context 'when a merge request has repository', :js do
let(:project) { create(:project, :public, :repository) }
@@ -107,5 +94,21 @@ RSpec.describe 'User views an open merge request' do
end
end
end
+
+ context 'when the assignee\'s availability set' do
+ before do
+ merge_request.author.create_status(availability: 'busy')
+ merge_request.assignees << merge_request.author
+
+ visit(merge_request_path(merge_request))
+ end
+
+ it 'exposes the availability in the data-availability attribute' do
+ assignees_data = find_all("input[name='merge_request[assignee_ids][]']", visible: false)
+
+ expect(assignees_data.size).to eq(1)
+ expect(assignees_data.first['data-availability']).to eq('busy')
+ end
+ end
end
end
diff --git a/spec/features/merge_requests/user_filters_by_milestones_spec.rb b/spec/features/merge_requests/user_filters_by_milestones_spec.rb
index 41a0b0012d1..877d5e6a4ee 100644
--- a/spec/features/merge_requests/user_filters_by_milestones_spec.rb
+++ b/spec/features/merge_requests/user_filters_by_milestones_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe 'Merge Requests > User filters by milestones', :js do
let(:milestone) { create(:milestone, project: project) }
before do
- create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, source_project: project)
create(:merge_request, :simple, source_project: project, milestone: milestone)
sign_in(user)
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 36d28ae2822..6b8dcd7dbb6 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -40,9 +40,8 @@ RSpec.describe 'Merge requests > User lists merge requests' do
updated_at: 10.seconds.ago)
end
- context 'when merge_request_reviewers is turned on' do
+ context 'merge request reviewers' do
before do
- stub_feature_flags(merge_request_reviewers: true)
visit_merge_requests(project, reviewer_id: user.id)
end
@@ -62,15 +61,6 @@ RSpec.describe 'Merge requests > User lists merge requests' do
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/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb
index d489d92c524..3129e4bd952 100644
--- a/spec/features/profiles/user_edit_preferences_spec.rb
+++ b/spec/features/profiles/user_edit_preferences_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'User edit preferences profile' do
+RSpec.describe 'User edit preferences profile', :js do
let(:user) { create(:user) }
before do
@@ -53,7 +53,14 @@ RSpec.describe 'User edit preferences profile' do
fill_in 'Tab width', with: -1
click_button 'Save changes'
- expect(page).to have_content('Failed to save preferences')
+ field = page.find_field('user[tab_width]')
+ message = field.native.attribute("validationMessage")
+ expect(message).to eq "Value must be greater than or equal to 1."
+
+ # User trying to hack an invalid value
+ page.execute_script("document.querySelector('#user_tab_width').setAttribute('min', '-1')")
+ click_button 'Save changes'
+ expect(page).to have_content('Failed to save preferences.')
end
end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 239bc04a9cb..bd4917824d1 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -199,6 +199,38 @@ RSpec.describe 'User edit profile' do
expect(busy_status.checked?).to eq(true)
end
+ context 'with user status set to busy' do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project, author: user) }
+
+ before do
+ toggle_busy_status
+ submit_settings
+
+ project.add_developer(user)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'shows author as busy in the assignee dropdown' do
+ find('.block.assignee .edit-link').click
+ wait_for_requests
+
+ page.within '.dropdown-menu-user' do
+ expect(page).to have_content("#{user.name} (Busy)")
+ end
+ end
+
+ it 'displays the assignee busy status' do
+ click_button 'assign yourself'
+ wait_for_requests
+
+ visit project_issue_path(project, issue)
+ wait_for_requests
+
+ expect(page.find('[data-testid="expanded-assignee"]')).to have_text("#{user.name} (Busy)")
+ end
+ end
+
context 'with set_user_availability_status feature flag disabled' do
before do
stub_feature_flags(set_user_availability_status: false)
diff --git a/spec/features/profiles/user_search_settings_spec.rb b/spec/features/profiles/user_search_settings_spec.rb
new file mode 100644
index 00000000000..60df0d7532b
--- /dev/null
+++ b/spec/features/profiles/user_search_settings_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User searches their settings', :js do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'in profile page' do
+ let(:visit_path) { profile_path }
+
+ it_behaves_like 'can search settings with feature flag check', 'Public Avatar', 'Main settings'
+ end
+
+ context 'in preferences page' do
+ before do
+ visit profile_preferences_path
+ end
+
+ it_behaves_like 'can search settings', 'Syntax highlighting theme', 'Behavior'
+ end
+end
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 997cc8e3c4b..289fbff0404 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'User visits the notifications tab', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(vue_notification_dropdown: false)
project.add_maintainer(user)
sign_in(user)
visit(profile_notifications_path)
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index 56db7efff51..da63f7c0f41 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User visits the profile preferences page' do
+RSpec.describe 'User visits the profile preferences page', :js do
include Select2Helper
let(:user) { create(:user) }
@@ -39,7 +39,7 @@ RSpec.describe 'User visits the profile preferences page' do
describe 'User changes their default dashboard', :js do
it 'creates a flash message' do
select2('stars', from: '#user_dashboard')
- click_button 'Save'
+ click_button 'Save changes'
wait_for_requests
@@ -48,7 +48,7 @@ RSpec.describe 'User visits the profile preferences page' do
it 'updates their preference' do
select2('stars', from: '#user_dashboard')
- click_button 'Save'
+ click_button 'Save changes'
wait_for_requests
@@ -67,7 +67,7 @@ RSpec.describe 'User visits the profile preferences page' do
describe 'User changes their language', :js do
it 'creates a flash message', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31404' do
select2('en', from: '#user_preferred_language')
- click_button 'Save'
+ click_button 'Save changes'
wait_for_requests
@@ -77,7 +77,7 @@ RSpec.describe 'User visits the profile preferences page' do
it 'updates their preference' do
wait_for_requests
select2('pt_BR', from: '#user_preferred_language')
- click_button 'Save'
+ click_button 'Save changes'
wait_for_requests
refresh
@@ -94,6 +94,8 @@ RSpec.describe 'User visits the profile preferences page' do
click_button 'Save changes'
+ wait_for_requests
+
expect(user.reload.render_whitespace_in_code).to be(true)
expect(render_whitespace_field).to be_checked
end
diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb
index 77da1f138c7..475fda5e7a1 100644
--- a/spec/features/profiles/user_visits_profile_spec.rb
+++ b/spec/features/profiles/user_visits_profile_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe 'User visits their profile' do
find(:css, '.header-user-dropdown-toggle').click
page.within ".header-user" do
- click_link "Profile"
+ click_link user.username
end
end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index dcad7ee66a3..4bfe8852291 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|
- travel_to((threshold + i).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
+ travel_to((threshold + i.hours).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
end
# Add 6 active branches
(1..6).each do |i|
- travel_to((threshold - i).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
+ travel_to((threshold - i.hours).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
end
end
@@ -34,7 +34,7 @@ RSpec.describe 'Branches' do
visit project_branches_path(project)
expect(page).to have_content(sorted_branches(repository, count: 5, sort_by: :updated_desc, state: 'active'))
- expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_desc, state: 'stale'))
+ expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_asc, state: 'stale'))
expect(page).to have_link('Show more active branches', href: project_branches_filtered_path(project, state: 'active'))
expect(page).not_to have_content('Show more stale branches')
@@ -50,10 +50,10 @@ RSpec.describe 'Branches' do
end
describe 'Stale branches page' do
- it 'shows 4 active branches sorted by last updated' do
+ it 'shows 4 stale branches sorted by last updated' do
visit project_branches_filtered_path(project, state: 'stale')
- expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_desc, state: 'stale'))
+ expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_asc, state: 'stale'))
end
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 9fe3f4cd63e..489a90cc8fc 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -2,108 +2,126 @@
require 'spec_helper'
-RSpec.describe 'Cherry-pick Commits' do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
- let(:project) { create(:project, :repository, namespace: group) }
- let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
- let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
+RSpec.describe 'Cherry-pick Commits', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
+ let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
+ let(:master_pickable_commit) { project.commit(sha) }
before do
sign_in(user)
- project.add_maintainer(user)
- visit project_commit_path(project, master_pickable_commit.id)
end
- context "I cherry-pick a commit" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- expect(page).not_to have_content('v1.0.0') # Only branches, not tags
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
- expect(page).to have_content('The commit has been successfully cherry-picked into master.')
- end
- end
+ context 'when clicking cherry-pick from the dropdown for a commit on pipelines tab' do
+ it 'launches the modal form' do
+ create(:ci_empty_pipeline, sha: sha)
+ visit project_commit_path(project, master_pickable_commit.id)
+ click_link 'Pipelines'
- context "I cherry-pick a merge commit" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
+ open_modal
+
+ page.within(modal_selector) do
+ expect(page).to have_content('Cherry-pick this commit')
end
- expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
end
- context "I cherry-pick a commit that was previously cherry-picked" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
+ context 'when starting from the commit tab' do
+ before do
visit project_commit_path(project, master_pickable_commit.id)
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
- expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
end
- end
- context "I cherry-pick a commit in a new merge request", :js do
- it do
- find('.header-action-buttons a.dropdown-toggle').click
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- click_button 'Cherry-pick'
+ context 'when cherry-picking a commit' do
+ specify do
+ cherry_pick_commit
+
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
+ end
- wait_for_requests
+ context 'when cherry-picking a merge commit' do
+ specify do
+ cherry_pick_commit
- expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
- expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
+ end
end
- end
- context "I cherry-pick a commit from a different branch", :js do
- it do
- find('.header-action-buttons a.dropdown-toggle').click
- find(:css, "a[href='#modal-cherry-pick-commit']").click
+ context 'when cherry-picking a commit that was previously cherry-picked' do
+ specify do
+ cherry_pick_commit
- page.within('#modal-cherry-pick-commit') do
- click_button 'master'
+ visit project_commit_path(project, master_pickable_commit.id)
+
+ cherry_pick_commit
+
+ expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
end
+ end
- wait_for_requests
+ context 'when cherry-picking a commit in a new merge request' do
+ specify do
+ cherry_pick_commit(create_merge_request: true)
- page.within('#modal-cherry-pick-commit .dropdown-menu') do
- find('.dropdown-input input').set('feature')
- wait_for_requests
- click_link "feature"
+ expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
+ expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
end
+ end
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
+ context 'when I cherry-picking a commit from a different branch' do
+ specify do
+ open_modal
+
+ page.within(modal_selector) do
+ click_button 'master'
+ end
+
+ page.within("#{modal_selector} .dropdown-menu") do
+ find('[data-testid="dropdown-search-box"]').set('feature')
+ wait_for_requests
+ click_button 'feature'
+ end
+
+ submit_cherry_pick
+
+ expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
end
+ end
+
+ context 'when the project is archived' do
+ let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
+ it 'does not show the cherry-pick link' do
+ open_dropdown
+
+ expect(page).not_to have_text("Cherry-pick")
+ end
end
end
- context 'when the project is archived' do
- let(:project) { create(:project, :repository, :archived, namespace: group) }
+ def cherry_pick_commit(create_merge_request: false)
+ open_modal
- it 'does not show the cherry-pick link' do
- find('.header-action-buttons a.dropdown-toggle').click
+ submit_cherry_pick(create_merge_request: create_merge_request)
+ end
+
+ def open_dropdown
+ find('.header-action-buttons .dropdown').click
+ end
- expect(page).not_to have_text("Cherry-pick")
- expect(page).not_to have_css("a[href='#modal-cherry-pick-commit']")
+ def open_modal
+ open_dropdown
+ find('[data-testid="cherry-pick-commit-link"]').click
+ end
+
+ def submit_cherry_pick(create_merge_request: false)
+ page.within(modal_selector) do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Cherry-pick')
end
end
+
+ def modal_selector
+ '[data-testid="modal-commit"]'
+ end
end
diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb
index f3c364dab97..72c639a027e 100644
--- a/spec/features/projects/commit/user_reverts_commit_spec.rb
+++ b/spec/features/projects/commit/user_reverts_commit_spec.rb
@@ -6,58 +6,89 @@ RSpec.describe 'User reverts a commit', :js do
include RepoHelpers
let_it_be(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
before do
sign_in(user)
-
- visit(project_commit_path(project, sample_commit.id))
end
- def revert_commit(create_merge_request: false)
- find('.header-action-buttons .dropdown').click
- find('[data-testid="revert-commit-link"]').click
+ context 'when clicking revert from the dropdown for a commit on pipelines tab' do
+ it 'launches the modal and is able to submit the revert' do
+ sha = '7d3b0f7cff5f37573aea97cebfd5692ea1689924'
+ create(:ci_empty_pipeline, sha: sha)
+ visit project_commit_path(project, project.commit(sha).id)
+ click_link 'Pipelines'
- page.within('[data-testid="modal-commit"]') do
- uncheck('create_merge_request') unless create_merge_request
- click_button('Revert')
+ open_modal
+
+ page.within(modal_selector) do
+ expect(page).to have_content('Revert this commit')
+ end
end
end
- context 'without creating a new merge request' do
- it 'reverts a commit' do
- revert_commit
+ context 'when starting from the commit tab' do
+ before do
+ visit project_commit_path(project, sample_commit.id)
+ end
+
+ context 'without creating a new merge request' do
+ it 'reverts a commit' do
+ revert_commit
+
+ expect(page).to have_content('The commit has been successfully reverted.')
+ end
+
+ it 'does not revert a previously reverted commit' do
+ revert_commit
+ # Visit the comment again once it was reverted.
+ visit project_commit_path(project, sample_commit.id)
+
+ revert_commit
- expect(page).to have_content('The commit has been successfully reverted.')
+ expect(page).to have_content('Sorry, we cannot revert this commit automatically.')
+ end
end
- it 'does not revert a previously reverted commit' do
- revert_commit
- # Visit the comment again once it was reverted.
- visit project_commit_path(project, sample_commit.id)
+ context 'with creating a new merge request' do
+ it 'reverts a commit' do
+ revert_commit(create_merge_request: true)
+
+ expect(page).to have_content('The commit has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
+ expect(page).to have_content("From revert-#{Commit.truncate_sha(sample_commit.id)} into master")
+ end
+ end
- revert_commit
+ context 'when the project is archived' do
+ let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- expect(page).to have_content('Sorry, we cannot revert this commit automatically.')
+ it 'does not show the revert link' do
+ open_dropdown
+
+ expect(page).not_to have_link('Revert')
+ end
end
end
- context 'with creating a new merge request' do
- it 'reverts a commit' do
- revert_commit(create_merge_request: true)
+ def revert_commit(create_merge_request: false)
+ open_modal
- expect(page).to have_content('The commit has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
- expect(page).to have_content("From revert-#{Commit.truncate_sha(sample_commit.id)} into master")
+ page.within(modal_selector) do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Revert')
end
end
- context 'when the project is archived' do
- let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
+ def open_dropdown
+ find('.header-action-buttons .dropdown').click
+ end
- it 'does not show the revert link' do
- find('.header-action-buttons .dropdown').click
+ def open_modal
+ open_dropdown
+ find('[data-testid="revert-commit-link"]').click
+ end
- expect(page).not_to have_link('Revert')
- end
+ def modal_selector
+ '[data-testid="modal-commit"]'
end
end
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 596b4773716..4894e2b7f3e 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -203,10 +203,11 @@ RSpec.describe 'User browses commits' do
context 'when click the compare tab' do
before do
+ wait_for_requests
click_link('Compare')
end
- it 'does not render create merge request button' do
+ it 'does not render create merge request button', :js do
expect(page).not_to have_link 'Create merge request'
end
end
@@ -236,10 +237,11 @@ RSpec.describe 'User browses commits' do
context 'when click the compare tab' do
before do
+ wait_for_requests
click_link('Compare')
end
- it 'renders create merge request button' do
+ it 'renders create merge request button', :js do
expect(page).to have_link 'Create merge request'
end
end
@@ -276,10 +278,11 @@ RSpec.describe 'User browses commits' do
context 'when click the compare tab' do
before do
+ wait_for_requests
click_link('Compare')
end
- it 'renders button to the merge request' do
+ it 'renders button to the merge request', :js do
expect(page).not_to have_link 'Create merge request'
expect(page).to have_link 'View open merge request', href: project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index e387ea4d473..64e9968061c 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -17,10 +17,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: 'master', to: 'master')
select_using_dropdown 'from', 'feature'
- expect(find('.js-compare-from-dropdown .dropdown-toggle-text')).to have_content('feature')
+ expect(find('.js-compare-from-dropdown .gl-new-dropdown-button-text')).to have_content('feature')
select_using_dropdown 'to', 'binary-encoding'
- expect(find('.js-compare-to-dropdown .dropdown-toggle-text')).to have_content('binary-encoding')
+ expect(find('.js-compare-to-dropdown .gl-new-dropdown-button-text')).to have_content('binary-encoding')
click_button 'Compare'
@@ -32,8 +32,8 @@ RSpec.describe "Compare", :js do
it "pre-populates fields" do
visit project_compare_index_path(project, from: "master", to: "master")
- expect(find(".js-compare-from-dropdown .dropdown-toggle-text")).to have_content("master")
- expect(find(".js-compare-to-dropdown .dropdown-toggle-text")).to have_content("master")
+ expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("master")
+ expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("master")
end
it_behaves_like 'compares branches'
@@ -99,7 +99,7 @@ RSpec.describe "Compare", :js do
find(".js-compare-from-dropdown .compare-dropdown-toggle").click
- expect(find(".js-compare-from-dropdown .dropdown-content")).to have_selector("li", count: 3)
+ expect(find(".js-compare-from-dropdown .gl-new-dropdown-contents")).to have_selector('li.gl-new-dropdown-item', count: 1)
end
context 'when commit has overflow', :js do
@@ -125,10 +125,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: "master", to: "master")
select_using_dropdown "from", "v1.0.0"
- expect(find(".js-compare-from-dropdown .dropdown-toggle-text")).to have_content("v1.0.0")
+ expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("v1.0.0")
select_using_dropdown "to", "v1.1.0"
- expect(find(".js-compare-to-dropdown .dropdown-toggle-text")).to have_content("v1.1.0")
+ expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("v1.1.0")
click_button "Compare"
expect(page).to have_content "Commits"
@@ -136,19 +136,22 @@ RSpec.describe "Compare", :js do
end
def select_using_dropdown(dropdown_type, selection, commit: false)
+ wait_for_requests
+
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
# find input before using to wait for the inputs visibility
dropdown.find('.dropdown-menu')
dropdown.fill_in("Filter by Git revision", with: selection)
+
wait_for_requests
if commit
- dropdown.find('input[type="search"]').send_keys(:return)
+ dropdown.find('.gl-search-box-by-type-input').send_keys(:return)
else
# find before all to wait for the items visibility
- dropdown.find("a[data-ref=\"#{selection}\"]", match: :first)
- dropdown.all("a[data-ref=\"#{selection}\"]").last.click
+ dropdown.find(".js-compare-#{dropdown_type}-dropdown .dropdown-item", text: selection, match: :first)
+ dropdown.all(".js-compare-#{dropdown_type}-dropdown .dropdown-item", text: selection).first.click
end
end
end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 2f0fbd29cb5..c94247f65d2 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -187,7 +187,7 @@ RSpec.describe 'Edit Project Settings' do
click_button "Save changes"
end
- expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 4)
+ expect(find(".sharing-permissions")).to have_selector(".gl-toggle.is-disabled", minimum: 4)
end
it "shows empty features project homepage" do
@@ -282,10 +282,10 @@ RSpec.describe 'Edit Project Settings' do
end
def toggle_feature_off(feature_name)
- find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle.is-checked").click
+ find(".project-feature-controls[data-for=\"#{feature_name}\"] .gl-toggle.is-checked").click
end
def toggle_feature_on(feature_name)
- find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle:not(.is-checked)").click
+ find(".project-feature-controls[data-for=\"#{feature_name}\"] .gl-toggle:not(.is-checked)").click
end
end
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index 17258f7042f..40d19a94b42 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297400' do
+RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
visit project_new_blob_path(project, 'master', file_name: 'Dockerfile')
end
- it 'user can pick a Dockerfile file from the dropdown', :js do
+ it 'user can pick a Dockerfile file from the dropdown' do
expect(page).to have_css('.dockerfile-selector')
find('.js-dockerfile-selector').click
@@ -24,6 +26,6 @@ RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', quarant
wait_for_requests
expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'Apply a template')
- expect(page).to have_content('COPY ./ /usr/local/apache2/htdocs/')
+ expect(editor_get_value).to have_content('COPY ./ /usr/local/apache2/htdocs/')
end
end
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index 5a39f2bcd98..a9f2463ecf6 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a .gitignore file' do
+RSpec.describe 'Projects > Files > User wants to add a .gitignore file', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
visit project_new_blob_path(project, 'master', file_name: '.gitignore')
end
- it 'user can pick a .gitignore file from the dropdown', :js do
+ it 'user can pick a .gitignore file from the dropdown' do
expect(page).to have_css('.gitignore-selector')
find('.js-gitignore-selector').click
@@ -24,7 +26,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitignore file' do
wait_for_requests
expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Apply a template')
- expect(page).to have_content('/.bundle')
- expect(page).to have_content('config/initializers/secret_token.rb')
+ expect(editor_get_value).to have_content('/.bundle')
+ expect(editor_get_value).to have_content('config/initializers/secret_token.rb')
end
end
diff --git a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
index 6308acb41f5..ca6f03472dd 100644
--- a/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_syntax_yml_dropdown_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
@@ -34,8 +36,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
let(:experiment_active) { true }
let(:in_experiment_group) { true }
- it 'allows the user to pick a "Learn CI/CD syntax" template from the dropdown', :js,
- { quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297347' } } do
+ it 'allows the user to pick a "Learn CI/CD syntax" template from the dropdown', :js do
expect(page).to have_css('.gitlab-ci-syntax-yml-selector')
find('.js-gitlab-ci-syntax-yml-selector').click
@@ -50,7 +51,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
wait_for_requests
expect(page).to have_css('.gitlab-ci-syntax-yml-selector .dropdown-toggle-text', text: 'Learn CI/CD syntax')
- expect(page).to have_content('You can use artifacts to pass data to jobs in later stages.')
+ expect(editor_get_value).to have_content('You can use artifacts to pass data to jobs in later stages.')
end
end
end
diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
index 879cb6a65c8..55b9f38d8e7 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -2,14 +2,16 @@
require 'spec_helper'
-RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
+RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js do
+ include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
+
before do
project = create(:project, :repository)
sign_in project.owner
visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml')
end
- it 'user can pick a template from the dropdown', :js do
+ it 'user can pick a template from the dropdown' do
expect(page).to have_css('.gitlab-ci-yml-selector')
find('.js-gitlab-ci-yml-selector').click
@@ -24,7 +26,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file' do
wait_for_requests
expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template')
- expect(page).to have_content('This file is a template, and might need editing before it works on your project')
- expect(page).to have_content('jekyll build -d test')
+ expect(editor_get_value).to have_content('This file is a template, and might need editing before it works on your project')
+ expect(editor_get_value).to have_content('jekyll build -d test')
end
end
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index 1e84d1552a1..8d0500f5e13 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -9,22 +9,45 @@ RSpec.describe 'Project fork' do
let(:project) { create(:project, :public, :repository) }
before do
- sign_in user
+ sign_in(user)
end
- it 'allows user to fork project' do
+ it 'allows user to fork project from the project page' do
visit project_path(project)
- expect(page).not_to have_css('a.disabled', text: 'Select')
+ expect(page).not_to have_css('a.disabled', text: 'Fork')
end
- it 'disables fork button when user has exceeded project limit' do
- user.projects_limit = 0
- user.save!
+ context 'user has exceeded personal project limit' do
+ before do
+ user.update!(projects_limit: 0)
+ end
- visit project_path(project)
+ it 'disables fork button on project page' do
+ visit project_path(project)
+
+ expect(page).to have_css('a.disabled', text: 'Fork')
+ end
+
+ context 'with a group to fork to' do
+ let!(:group) { create(:group).tap { |group| group.add_owner(user) } }
+
+ it 'enables fork button on project page' do
+ visit project_path(project)
+
+ expect(page).not_to have_css('a.disabled', text: 'Fork')
+ end
+
+ it 'allows user to fork only to the group on fork page', :js do
+ visit new_project_fork_path(project)
+
+ to_personal_namespace = find('[data-qa-selector=fork_namespace_button].disabled')
+ to_group = find(".fork-groups button[data-qa-name=#{group.name}]")
- expect(page).to have_css('a.disabled', text: 'Fork')
+ expect(to_personal_namespace).not_to be_nil
+ expect(to_group).not_to be_disabled
+ end
+ end
end
context 'forking enabled / disabled in project settings' do
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 7b9f79c9f7f..72df84bf905 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -72,9 +72,9 @@ RSpec.describe 'Project Graph', :js do
it 'renders CI graphs' do
expect(page).to have_content 'Overall'
- expect(page).to have_content 'Pipelines for last week'
- expect(page).to have_content 'Pipelines for last month'
- expect(page).to have_content 'Pipelines for last year'
+ expect(page).to have_content 'Last week'
+ expect(page).to have_content 'Last month'
+ expect(page).to have_content 'Last year'
expect(page).to have_content 'Duration for the last 30 commits'
end
end
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 8f1c31f229f..12c5820a69d 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe 'issuable templates', :js do
let(:bug_template_content) { 'this is merge request bug template' }
let(:template_override_warning) { 'Applying a template will replace the existing issue description.' }
let(:updated_description) { 'updated merge request description' }
- let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
before do
project.repository.create_file(
@@ -154,7 +154,7 @@ RSpec.describe 'issuable templates', :js do
let(:template_content) { 'this is a test "feature-proposal" template' }
let(:fork_user) { create(:user) }
let(:forked_project) { fork_project(project, fork_user, repository: true) }
- let(:merge_request) { create(:merge_request, :with_diffs, source_project: forked_project, target_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: project) }
before do
sign_out(:user)
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 1557a8a2d72..7811394b541 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -27,40 +27,12 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
describe "GET /:project/jobs" do
context 'with no jobs' do
before do
- stub_experiment(jobs_empty_state: experiment_active)
- stub_experiment_for_subject(jobs_empty_state: in_experiment_group)
-
visit project_jobs_path(project)
end
- context 'when experiment not active' do
- let(:experiment_active) { false }
- let(:in_experiment_group) { false }
-
- it 'shows the empty state control page' do
- expect(page).to have_content('No jobs to show')
- expect(page).to have_link('Get started with Pipelines')
- end
- end
-
- context 'when experiment active and user in control group' do
- let(:experiment_active) { true }
- let(:in_experiment_group) { false }
-
- it 'shows the empty state control page' do
- expect(page).to have_content('No jobs to show')
- expect(page).to have_link('Get started with Pipelines')
- end
- end
-
- context 'when experiment active and user in experimental group' do
- let(:experiment_active) { true }
- let(:in_experiment_group) { true }
-
- it 'shows the empty state experiment page' do
- expect(page).to have_content('Use jobs to automate your tasks')
- expect(page).to have_link('Create CI/CD configuration file')
- end
+ it 'shows the empty state page' do
+ expect(page).to have_content('Use jobs to automate your tasks')
+ expect(page).to have_link('Create CI/CD configuration file', href: project.present(current_user: user).add_ci_yml_path)
end
end
@@ -102,7 +74,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
it "shows Finished tab jobs" do
expect(page).to have_selector('.nav-links li.active', text: 'Finished')
- expect(page).to have_content 'No jobs to show'
+ expect(page).to have_content('Use jobs to automate your tasks')
end
end
@@ -533,10 +505,10 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
expect(page).to have_content('Trigger token')
expect(page).to have_content('Trigger variables')
- expect(page).not_to have_css('.js-reveal-variables')
+ expect(page).not_to have_selector('[data-testid="trigger-reveal-values-button"]')
- expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
- expect(page).to have_selector('.js-build-value', text: '••••••')
+ expect(page).to have_selector('[data-testid="trigger-build-key"]', text: 'TRIGGER_KEY_1')
+ expect(page).to have_selector('[data-testid="trigger-build-value"]', text: '••••••')
end
end
@@ -571,17 +543,17 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
expect(page).to have_content('Trigger token')
expect(page).to have_content('Trigger variables')
- expect(page).to have_css('.js-reveal-variables')
+ expect(page).to have_selector('[data-testid="trigger-reveal-values-button"]')
- expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
- expect(page).to have_selector('.js-build-value', text: '••••••')
+ expect(page).to have_selector('[data-testid="trigger-build-key"]', text: 'TRIGGER_KEY_1')
+ expect(page).to have_selector('[data-testid="trigger-build-value"]', text: '••••••')
end
it 'reveals values on button click', :js do
click_button 'Reveal values'
- expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
- expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
+ expect(page).to have_selector('[data-testid="trigger-build-key"]', text: 'TRIGGER_KEY_1')
+ expect(page).to have_selector('[data-testid="trigger-build-value"]', text: 'TRIGGER_VALUE_1')
end
end
diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
index 3b0f00c5494..d710ecf6c88 100644
--- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb
+++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Anonymous user sees members' do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public) }
@@ -12,11 +14,25 @@ RSpec.describe 'Projects > Members > Anonymous user sees members' do
create(:project_group_link, project: project, group: group)
end
- it "anonymous user visits the project's members page and sees the list of members" do
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ it "anonymous user visits the project's members page and sees the list of members" do
+ visit project_project_members_path(project)
+
+ expect(find_member_row(user)).to have_content(user.name)
+ end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it "anonymous user visits the project's members page and sees the list of members" do
+ visit project_project_members_path(project)
- expect(current_path).to eq(
- project_project_members_path(project))
- expect(page).to have_content(user.name)
+ expect(current_path).to eq(
+ project_project_members_path(project))
+ expect(page).to have_content(user.name)
+ end
end
end
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index aa15f04bf24..1abd00421ec 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects members', :js do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let(:user) { create(:user) }
let(:developer) { create(:user) }
let(:group) { create(:group, :public) }
@@ -18,117 +20,218 @@ RSpec.describe 'Projects members', :js do
sign_in(user)
end
- context 'with a group invitee' do
- before do
- group_invitee
- visit project_project_members_path(project)
- end
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ context 'with a group invitee' do
+ before do
+ group_invitee
+ visit project_project_members_path(project)
+ end
- it 'does not appear in the project members page' do
- page.within first('.content-list') do
- expect(page).not_to have_content('test2@abc.com')
+ it 'does not appear in the project members page' do
+ expect(members_table).not_to have_content('test2@abc.com')
end
end
- end
- context 'with a group' do
- it 'shows group and project members by default' do
- visit project_project_members_path(project)
+ context 'with a group' do
+ it 'shows group and project members by default' do
+ visit project_project_members_path(project)
+
+ expect(members_table).to have_content(developer.name)
+ expect(members_table).to have_content(user.name)
+ expect(members_table).to have_content(group.name)
+ end
+
+ it 'shows project members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'exclude')
+
+ expect(members_table).to have_content(developer.name)
+ expect(members_table).not_to have_content(user.name)
+ expect(members_table).not_to have_content(group.name)
+ end
- page.within first('.content-list') do
- expect(page).to have_content(developer.name)
+ it 'shows group members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'only')
- expect(page).to have_content(user.name)
- expect(page).to have_content(group.name)
+ expect(members_table).not_to have_content(developer.name)
+ expect(members_table).to have_content(user.name)
+ expect(members_table).to have_content(group.name)
end
end
- it 'shows project members only if requested' do
- visit project_project_members_path(project, with_inherited_permissions: 'exclude')
+ context 'with a group, a project invitee, and a project requester' do
+ before do
+ group.request_access(group_requester)
+ project.request_access(project_requester)
+ group_invitee
+ project_invitee
+ visit project_project_members_path(project)
+ end
+
+ it 'shows the group owner' do
+ expect(members_table).to have_content(user.name)
+ expect(members_table).to have_content(group.name)
+ end
+
+ it 'shows the project developer' do
+ expect(members_table).to have_content(developer.name)
+ end
+
+ it 'shows the project invitee' do
+ click_link 'Invited'
+
+ expect(members_table).to have_content('test1@abc.com')
+ expect(members_table).not_to have_content('test2@abc.com')
+ end
- page.within first('.content-list') do
- expect(page).to have_content(developer.name)
+ it 'shows the project requester' do
+ click_link 'Access requests'
- expect(page).not_to have_content(user.name)
- expect(page).not_to have_content(group.name)
+ expect(members_table).to have_content(project_requester.name)
+ expect(members_table).not_to have_content(group_requester.name)
end
end
- it 'shows group members only if requested' do
- visit project_project_members_path(project, with_inherited_permissions: 'only')
+ context 'with a group requester' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ group.request_access(group_requester)
+ visit project_project_members_path(project)
+ end
+
+ it 'does not appear in the project members page' do
+ expect(page).not_to have_link('Access requests')
+ expect(members_table).not_to have_content(group_requester.name)
+ end
+ end
+
+ context 'showing status of members' do
+ it 'shows the status' do
+ create(:user_status, user: user, emoji: 'smirk', message: 'Authoring this object')
- page.within first('.content-list') do
- expect(page).not_to have_content(developer.name)
+ visit project_project_members_path(project)
- expect(page).to have_content(user.name)
- expect(page).to have_content(group.name)
+ expect(first_row).to have_selector('gl-emoji[data-name="smirk"]')
end
end
end
- context 'with a group, a project invitee, and a project requester' do
+ context 'when `vue_project_members_list` feature flag is disabled' do
before do
- group.request_access(group_requester)
- project.request_access(project_requester)
- group_invitee
- project_invitee
- visit project_project_members_path(project)
+ stub_feature_flags(vue_project_members_list: false)
end
- it 'shows the group owner' do
- page.within first('.content-list') do
- # Group owner
- expect(page).to have_content(user.name)
- expect(page).to have_content(group.name)
+ context 'with a group invitee' do
+ before do
+ group_invitee
+ visit project_project_members_path(project)
end
- end
- it 'shows the project developer' do
- page.within first('.content-list') do
- # Project developer
- expect(page).to have_content(developer.name)
+ it 'does not appear in the project members page' do
+ page.within first('.content-list') do
+ expect(page).not_to have_content('test2@abc.com')
+ end
end
end
- it 'shows the project invitee' do
- click_link 'Invited'
+ context 'with a group' do
+ it 'shows group and project members by default' do
+ visit project_project_members_path(project)
- page.within first('.content-list') do
- expect(page).to have_content('test1@abc.com')
- expect(page).not_to have_content('test2@abc.com')
+ page.within first('.content-list') do
+ expect(page).to have_content(developer.name)
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
+ end
+
+ it 'shows project members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'exclude')
+
+ page.within first('.content-list') do
+ expect(page).to have_content(developer.name)
+
+ expect(page).not_to have_content(user.name)
+ expect(page).not_to have_content(group.name)
+ end
end
- end
- it 'shows the project requester' do
- click_link 'Access requests'
+ it 'shows group members only if requested' do
+ visit project_project_members_path(project, with_inherited_permissions: 'only')
- page.within first('.content-list') do
- expect(page).to have_content(project_requester.name)
- expect(page).not_to have_content(group_requester.name)
+ page.within first('.content-list') do
+ expect(page).not_to have_content(developer.name)
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
end
end
- end
- context 'with a group requester' do
- before do
- stub_feature_flags(invite_members_group_modal: false)
- group.request_access(group_requester)
- visit project_project_members_path(project)
+ context 'with a group, a project invitee, and a project requester' do
+ before do
+ group.request_access(group_requester)
+ project.request_access(project_requester)
+ group_invitee
+ project_invitee
+ visit project_project_members_path(project)
+ end
+
+ it 'shows the group owner' do
+ page.within first('.content-list') do
+ # Group owner
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
+ end
+
+ it 'shows the project developer' do
+ page.within first('.content-list') do
+ # Project developer
+ expect(page).to have_content(developer.name)
+ end
+ end
+
+ it 'shows the project invitee' do
+ click_link 'Invited'
+
+ page.within first('.content-list') do
+ expect(page).to have_content('test1@abc.com')
+ expect(page).not_to have_content('test2@abc.com')
+ end
+ end
+
+ it 'shows the project requester' do
+ click_link 'Access requests'
+
+ page.within first('.content-list') do
+ expect(page).to have_content(project_requester.name)
+ expect(page).not_to have_content(group_requester.name)
+ end
+ end
end
- it 'does not appear in the project members page' do
- expect(page).not_to have_link('Access requests')
- page.within first('.content-list') do
- expect(page).not_to have_content(group_requester.name)
+ context 'with a group requester' do
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ group.request_access(group_requester)
+ visit project_project_members_path(project)
+ end
+
+ it 'does not appear in the project members page' do
+ expect(page).not_to have_link('Access requests')
+ page.within first('.content-list') do
+ expect(page).not_to have_content(group_requester.name)
+ end
end
end
- end
- describe 'showing status of members' do
- it_behaves_like 'showing user status' do
- let(:user_with_status) { developer }
+ context 'showing status of members' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { developer }
- subject { visit project_project_members_path(project) }
+ subject { visit project_project_members_path(project) }
+ end
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 686d86b1783..9d087dfd5f6 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Groups with access list', :js do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public) }
@@ -15,86 +17,172 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
project.add_maintainer(user)
sign_in(user)
- visit project_project_members_path(project)
- click_groups_tab
end
- it 'updates group access level' do
- click_button group_link.human_access
-
- page.within '.dropdown-menu' do
- click_link 'Guest'
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ before do
+ visit project_project_members_path(project)
+ click_groups_tab
end
- wait_for_requests
+ it 'updates group access level' do
+ click_button group_link.human_access
+ click_button 'Guest'
- visit project_project_members_path(project)
+ wait_for_requests
- click_groups_tab
+ visit project_project_members_path(project)
- expect(first('.group_member')).to have_content('Guest')
- end
+ click_groups_tab
+
+ expect(find_group_row(group)).to have_content('Guest')
+ end
- it 'updates expiry date' do
- expires_at_field = "member_expires_at_#{group.id}"
- fill_in expires_at_field, with: 3.days.from_now.to_date
+ it 'updates expiry date' do
+ page.within find_group_row(group) do
+ fill_in 'Expiration date', with: 5.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
- find_field(expires_at_field).native.send_keys :enter
- wait_for_requests
+ wait_for_requests
- page.within(find('li.group_member')) do
- expect(page).to have_content('Expires in 3 days')
+ expect(page).to have_content(/in \d days/)
+ end
end
- end
- context 'when link has expiry date set' do
- let(:additional_link_attrs) { { expires_at: 3.days.from_now.to_date } }
+ context 'when link has expiry date set' do
+ let(:additional_link_attrs) { { expires_at: 5.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')
+ it 'clears expiry date' do
+ page.within find_group_row(group) do
+ expect(page).to have_content(/in \d days/)
- page.within(find('.js-edit-member-form')) do
- find('.js-clear-input').click
+ find('[data-testid="clear-button"]').click
+
+ wait_for_requests
+
+ expect(page).to have_content('No expiration set')
end
+ end
+ end
- wait_for_requests
+ it 'deletes group link' do
+ expect(page).to have_content(group.full_name)
+
+ page.within find_group_row(group) do
+ click_button 'Remove group'
+ end
- expect(page).not_to have_content('Expires in')
+ page.within('[role="dialog"]') do
+ click_button('Remove group')
+ end
+
+ expect(page).not_to have_content(group.full_name)
+ end
+
+ context 'search in existing members' do
+ it 'finds no results' do
+ fill_in_filtered_search 'Search groups', with: 'testing 123'
+
+ click_groups_tab
+
+ expect(page).not_to have_content(group.full_name)
+ end
+
+ it 'finds results' do
+ fill_in_filtered_search 'Search groups', with: group.full_name
+
+ click_groups_tab
+
+ expect(members_table).to have_content(group.full_name)
end
end
end
- it 'deletes group link' do
- page.within(first('.group_member')) do
- accept_confirm { find('.btn-danger').click }
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+
+ visit project_project_members_path(project)
+ click_groups_tab
end
- wait_for_requests
- expect(page).not_to have_selector('.group_member')
- end
+ it 'updates group access level' do
+ click_button group_link.human_access
- context 'search in existing members' do
- it 'finds no results' do
- page.within '.user-search-form' do
- fill_in 'search_groups', with: 'testing 123'
- find('.user-search-btn').click
+ page.within '.dropdown-menu' do
+ click_link 'Guest'
end
+ wait_for_requests
+
+ visit project_project_members_path(project)
+
click_groups_tab
+ expect(first('.group_member')).to have_content('Guest')
+ end
+
+ it 'updates expiry date' do
+ expires_at_field = "member_expires_at_#{group.id}"
+ fill_in expires_at_field, with: 3.days.from_now.to_date
+
+ 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 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-danger').click }
+ end
+ wait_for_requests
+
expect(page).not_to have_selector('.group_member')
end
- it 'finds results' do
- page.within '.user-search-form' do
- fill_in 'search_groups', with: group.name
- find('.user-search-btn').click
+ context 'search in existing members' do
+ it 'finds no results' do
+ page.within '.user-search-form' do
+ fill_in 'search_groups', with: 'testing 123'
+ find('.user-search-btn').click
+ end
+
+ click_groups_tab
+
+ expect(page).not_to have_selector('.group_member')
end
- click_groups_tab
+ it 'finds results' do
+ page.within '.user-search-form' do
+ fill_in 'search_groups', with: group.name
+ find('.user-search-btn').click
+ end
+
+ click_groups_tab
- expect(page).to have_selector('.group_member', count: 1)
+ expect(page).to have_selector('.group_member', count: 1)
+ end
end
end
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
index bb56ae348fb..f0d115fef1d 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -5,9 +5,14 @@ require 'spec_helper'
RSpec.describe 'Project > Members > Invite group', :js do
include Select2Helper
include ActionView::Helpers::DateHelper
+ include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user) }
+ before do
+ stub_feature_flags(invite_members_group_modal: false)
+ end
+
describe 'Share with group lock' do
shared_examples 'the project can be shared with groups' do
it 'the "Invite group" tab exists' do
@@ -36,21 +41,45 @@ RSpec.describe 'Project > Members > Invite group', :js do
context 'when the group has "Share with group lock" disabled' do
it_behaves_like 'the project can be shared with groups'
- it 'the project can be shared with another group' do
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'the project can be shared with another group' do
+ visit project_project_members_path(project)
- expect(page).not_to have_link 'Groups'
+ expect(page).not_to have_link 'Groups'
- click_on 'invite-group-tab'
+ click_on 'invite-group-tab'
- select2 group_to_share_with.id, from: '#link_group_id'
- page.find('body').click
- find('.btn-success').click
+ select2 group_to_share_with.id, from: '#link_group_id'
+ page.find('body').click
+ find('.btn-success').click
- click_link 'Groups'
+ click_link 'Groups'
- page.within('[data-testid="project-member-groups"]') do
- expect(page).to have_content(group_to_share_with.name)
+ expect(members_table).to have_content(group_to_share_with.name)
+ end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'the project can be shared with another group' do
+ visit project_project_members_path(project)
+
+ expect(page).not_to have_link 'Groups'
+
+ click_on 'invite-group-tab'
+
+ select2 group_to_share_with.id, from: '#link_group_id'
+ page.find('body').click
+ find('.btn-success').click
+
+ click_link 'Groups'
+
+ page.within('[data-testid="project-member-groups"]') do
+ expect(page).to have_content(group_to_share_with.name)
+ end
end
end
end
@@ -117,7 +146,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
freeze_time { example.run }
end
- before do
+ def setup
project.add_maintainer(maintainer)
group.add_guest(maintainer)
sign_in(maintainer)
@@ -128,20 +157,37 @@ RSpec.describe 'Project > Members > Invite group', :js do
select2 group.id, from: '#link_group_id'
- fill_in 'expires_at_groups', with: (Time.now + 4.5.days).strftime('%Y-%m-%d')
+ fill_in 'expires_at_groups', with: 5.days.from_now.strftime('%Y-%m-%d')
click_on 'invite-group-tab'
find('.btn-success').click
end
- it 'the group link shows the expiration time with a warning class' do
- click_link 'Groups'
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'the group link shows the expiration time with a warning class' do
+ setup
+ click_link 'Groups'
+
+ expect(find_group_row(group)).to have_content(/in \d days/)
+ expect(find_group_row(group)).to have_selector('.gl-text-orange-500')
+ end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
- page.within('[data-testid="project-member-groups"]') do
- # Using distance_of_time_in_words_to_now because it is not the same as
- # subtraction, and this way avoids time zone issues as well
- expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at)
- expect(page).to have_content(expires_in_text)
- expect(page).to have_selector('.text-warning')
+ it 'the group link shows the expiration time with a warning class' do
+ setup
+ click_link 'Groups'
+
+ page.within('[data-testid="project-member-groups"]') do
+ # Using distance_of_time_in_words_to_now because it is not the same as
+ # subtraction, and this way avoids time zone issues as well
+ expires_in_text = distance_of_time_in_words_to_now(project.project_group_links.first.expires_at)
+ expect(page).to have_content(expires_in_text)
+ expect(page).to have_selector('.text-warning')
+ end
end
end
end
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index 62115f2dce6..b0fe5b9c48a 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe 'Project members list' do
include Select2Helper
- include Spec::Support::Helpers::Features::ListRowsHelpers
let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') }
@@ -13,102 +12,215 @@ RSpec.describe 'Project members list' do
before do
stub_feature_flags(invite_members_group_modal: false)
+
sign_in(user1)
group.add_owner(user1)
end
- it 'show members from project and group' do
- project.add_developer(user2)
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ include Spec::Support::Helpers::Features::MembersHelpers
- visit_members_page
+ it 'pushes `vue_project_members_list` feature flag to the frontend' do
+ visit_members_page
- expect(first_row.text).to include(user1.name)
- expect(second_row.text).to include(user2.name)
- end
+ expect(page).to have_pushed_frontend_feature_flags(vueProjectMembersList: true)
+ end
- it 'show user once if member of both group and project' do
- project.add_developer(user1)
+ it 'show members from project and group' do
+ project.add_developer(user2)
- visit_members_page
+ visit_members_page
- expect(first_row.text).to include(user1.name)
- expect(second_row).to be_blank
- end
+ expect(first_row).to have_content(user1.name)
+ expect(second_row).to have_content(user2.name)
+ end
+
+ it 'show user once if member of both group and project' do
+ project.add_developer(user1)
- it 'update user access level', :js do
- project.add_developer(user2)
+ visit_members_page
+
+ expect(first_row).to have_content(user1.name)
+ expect(second_row).to be_blank
+ end
+
+ it 'update user access level', :js do
+ project.add_developer(user2)
- visit_members_page
+ visit_members_page
- page.within(second_row) do
- click_button('Developer')
- click_link('Reporter')
+ page.within find_member_row(user2) do
+ click_button('Developer')
+ click_button('Reporter')
- expect(page).to have_button('Reporter')
+ expect(page).to have_button('Reporter')
+ end
end
- end
- it 'add user to project', :js do
- visit_members_page
+ it 'add user to project', :js do
+ visit_members_page
- add_user(user2.id, 'Reporter')
+ add_user(user2.id, 'Reporter')
- page.within(second_row) do
- expect(page).to have_content(user2.name)
- expect(page).to have_button('Reporter')
+ page.within find_member_row(user2) do
+ expect(page).to have_button('Reporter')
+ end
end
- end
- it 'remove user from project', :js do
- other_user = create(:user)
- project.add_developer(other_user)
+ it 'remove user from project', :js do
+ other_user = create(:user)
+ project.add_developer(other_user)
- visit_members_page
+ visit_members_page
- # Open modal
- find(:css, 'li.project_member', text: other_user.name).find(:css, 'button.btn-danger').click
+ # Open modal
+ page.within find_member_row(other_user) do
+ click_button 'Remove member'
+ end
- expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ page.within('[role="dialog"]') do
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ click_button('Remove member')
+ end
- click_on('Remove member')
+ wait_for_requests
- wait_for_requests
+ expect(members_table).not_to have_content(other_user.name)
+ end
- expect(page).not_to have_content(other_user.name)
- expect(project.users).not_to include(other_user)
- end
+ it 'invite user to project', :js do
+ visit_members_page
+
+ add_user('test@example.com', 'Reporter')
+
+ click_link 'Invited'
+
+ page.within find_invited_member_row('test@example.com') do
+ expect(page).to have_button('Reporter')
+ end
+ end
- it 'invite user to project', :js do
- visit_members_page
+ context 'project bots' do
+ let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
- add_user('test@example.com', 'Reporter')
+ before do
+ project.add_maintainer(project_bot)
+ end
- click_link 'Invited'
+ it 'does not show form used to change roles and "Expiration date" or the remove user button' do
+ visit_members_page
- page.within(first_row) do
- expect(page).to have_content('test@example.com')
- expect(page).to have_content('Invited')
- expect(page).to have_button('Reporter')
+ page.within find_member_row(project_bot) do
+ expect(page).not_to have_button('Maintainer')
+ expect(page).to have_field('Expiration date', disabled: true)
+ expect(page).not_to have_button('Remove member')
+ end
+ end
end
end
- context 'project bots' do
- let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ include Spec::Support::Helpers::Features::ListRowsHelpers
before do
- project.add_maintainer(project_bot)
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'show members from project and group' do
+ project.add_developer(user2)
+
+ visit_members_page
+
+ expect(first_row.text).to include(user1.name)
+ expect(second_row.text).to include(user2.name)
+ end
+
+ it 'show user once if member of both group and project' do
+ project.add_developer(user1)
+
+ visit_members_page
+
+ expect(first_row.text).to include(user1.name)
+ expect(second_row).to be_blank
end
- it 'does not show form used to change roles and "Expiration date" or the remove user button' do
- project_member = project.project_members.find_by(user_id: project_bot.id)
+ it 'update user access level', :js do
+ project.add_developer(user2)
+
+ visit_members_page
+
+ page.within(second_row) do
+ click_button('Developer')
+ click_link('Reporter')
+
+ expect(page).to have_button('Reporter')
+ end
+ end
+ it 'add user to project', :js do
visit_members_page
- expect(page).not_to have_selector("#edit_project_member_#{project_member.id}")
- expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger")
+ add_user(user2.id, 'Reporter')
+
+ page.within(second_row) do
+ expect(page).to have_content(user2.name)
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ it 'remove user from project', :js do
+ other_user = create(:user)
+ project.add_developer(other_user)
+
+ visit_members_page
+
+ # Open modal
+ find(:css, 'li.project_member', text: other_user.name).find(:css, 'button.btn-danger').click
+
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
+
+ wait_for_requests
+
+ expect(page).not_to have_content(other_user.name)
+ expect(project.users).not_to include(other_user)
+ end
+
+ it 'invite user to project', :js do
+ visit_members_page
+
+ add_user('test@example.com', 'Reporter')
+
+ click_link 'Invited'
+
+ page.within(first_row) do
+ expect(page).to have_content('test@example.com')
+ expect(page).to have_content('Invited')
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ context 'project bots' do
+ let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
+
+ before do
+ project.add_maintainer(project_bot)
+ end
+
+ it 'does not show form used to change roles and "Expiration date" or the remove user button' do
+ project_member = project.project_members.find_by(user_id: project_bot.id)
+
+ visit_members_page
+
+ expect(page).not_to have_selector("#edit_project_member_#{project_member.id}")
+ expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger")
+ end
end
end
+ private
+
def add_user(id, role)
page.within ".invite-users-form" do
select2(id, from: "#user_ids", multiple: true)
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 d69c3f2652c..1127c64e0c7 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
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Members > Maintainer adds member with expiration date', :js do
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
+ include Spec::Support::Helpers::Features::MembersHelpers
let_it_be(:maintainer) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -17,49 +18,107 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
sign_in(maintainer)
end
- it 'expiration date is displayed in the members list' do
- visit project_project_members_path(project)
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'expiration date is displayed in the members list' do
+ stub_feature_flags(invite_members_group_modal: false)
- page.within '.invite-users-form' do
- select2(new_member.id, from: '#user_ids', multiple: true)
+ visit project_project_members_path(project)
- fill_in 'expires_at', with: 3.days.from_now.to_date
- find_field('expires_at').native.send_keys :enter
+ page.within '.invite-users-form' do
+ select2(new_member.id, from: '#user_ids', multiple: true)
- click_on 'Invite'
+ fill_in 'expires_at', with: 5.days.from_now.to_date
+ find_field('expires_at').native.send_keys :enter
+
+ click_on 'Invite'
+ end
+
+ page.within find_member_row(new_member) do
+ expect(page).to have_content(/in \d days/)
+ end
end
- page.within "#project_member_#{project_member_id}" do
- expect(page).to have_content('Expires in 3 days')
+ it 'changes 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 find_member_row(new_member) do
+ fill_in 'Expiration date', with: 5.days.from_now.to_date
+ find_field('Expiration date').native.send_keys :enter
+
+ wait_for_requests
+
+ expect(page).to have_content(/in \d days/)
+ end
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)
+ it 'clears expiration date' do
+ project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.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
+ page.within find_member_row(new_member) do
+ expect(page).to have_content(/in \d days/)
- wait_for_requests
+ find('[data-testid="clear-button"]').click
- expect(page).to have_content('Expires in 3 days')
+ wait_for_requests
+
+ expect(page).to have_content('No expiration set')
+ end
end
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)
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'expiration date is displayed in the members list' do
+ stub_feature_flags(invite_members_group_modal: false)
+
+ 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: 1.day.from_now.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 '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')
+ page.within "#project_member_#{project_member_id}" do
+ expect(page).to have_content('Expires in 3 days')
- find('.js-clear-input').click
+ find('.js-clear-input').click
- wait_for_requests
+ wait_for_requests
- expect(page).not_to have_content('Expires in')
+ expect(page).not_to have_content('Expires in')
+ end
end
end
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index be27cbc0d66..3c132747bc4 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Sorting' do
+ include Spec::Support::Helpers::Features::MembersHelpers
+
let(:maintainer) { create(:user, name: 'John Doe') }
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) }
@@ -13,78 +15,169 @@ RSpec.describe 'Projects > Members > Sorting' do
sign_in(maintainer)
end
- it 'sorts alphabetically by default' do
- visit_members_list(sort: nil)
+ context 'when `vue_project_members_list` feature flag is enabled', :js do
+ it 'sorts by account by default' do
+ visit_members_list(sort: nil)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
- end
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- it 'sorts by access level ascending' do
- visit_members_list(sort: :access_level_asc)
+ expect_sort_by('Account', :asc)
+ end
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
- end
+ it 'sorts by max role ascending' do
+ visit_members_list(sort: :access_level_asc)
- it 'sorts by access level descending' do
- visit_members_list(sort: :access_level_desc)
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
- end
+ expect_sort_by('Max role', :asc)
+ end
- it 'sorts by last joined' do
- visit_members_list(sort: :last_joined)
+ it 'sorts by max role descending' do
+ visit_members_list(sort: :access_level_desc)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
- end
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- it 'sorts by oldest joined' do
- visit_members_list(sort: :oldest_joined)
+ expect_sort_by('Max role', :desc)
+ end
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
- end
+ it 'sorts by access granted ascending' do
+ visit_members_list(sort: :last_joined)
- it 'sorts by name ascending' do
- visit_members_list(sort: :name_asc)
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
- end
+ expect_sort_by('Access granted', :asc)
+ end
- it 'sorts by name descending' do
- visit_members_list(sort: :name_desc)
+ it 'sorts by access granted descending' do
+ visit_members_list(sort: :oldest_joined)
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
- end
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
+
+ expect_sort_by('Access granted', :desc)
+ end
+
+ it 'sorts by account ascending' do
+ visit_members_list(sort: :name_asc)
- it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
- visit_members_list(sort: :recent_sign_in)
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
- expect(first_member).to include(maintainer.name)
- expect(second_member).to include(developer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ expect_sort_by('Account', :asc)
+ end
+
+ it 'sorts by account descending' do
+ visit_members_list(sort: :name_desc)
+
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
+
+ expect_sort_by('Account', :desc)
+ end
+
+ it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :recent_sign_in)
+
+ expect(first_row).to have_content(maintainer.name)
+ expect(second_row).to have_content(developer.name)
+
+ expect_sort_by('Last sign-in', :asc)
+ end
+
+ it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :oldest_sign_in)
+
+ expect(first_row).to have_content(developer.name)
+ expect(second_row).to have_content(maintainer.name)
+
+ expect_sort_by('Last sign-in', :desc)
+ end
end
- it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
- visit_members_list(sort: :oldest_sign_in)
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'sorts alphabetically by default' do
+ visit_members_list(sort: nil)
- expect(first_member).to include(developer.name)
- expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ end
+
+ it 'sorts by access level ascending' do
+ visit_members_list(sort: :access_level_asc)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
+ end
+
+ it 'sorts by access level descending' do
+ visit_members_list(sort: :access_level_desc)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
+ end
+
+ it 'sorts by last joined' do
+ visit_members_list(sort: :last_joined)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
+ end
+
+ it 'sorts by oldest joined' do
+ visit_members_list(sort: :oldest_joined)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
+ end
+
+ it 'sorts by name ascending' do
+ visit_members_list(sort: :name_asc)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ end
+
+ it 'sorts by name descending' do
+ visit_members_list(sort: :name_desc)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
+ end
+
+ it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :recent_sign_in)
+
+ expect(first_member).to include(maintainer.name)
+ expect(second_member).to include(developer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ end
+
+ it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :oldest_sign_in)
+
+ expect(first_member).to include(developer.name)
+ expect(second_member).to include(maintainer.name)
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ end
end
+ private
+
def visit_members_list(sort:)
visit project_project_members_path(project, sort: sort)
end
@@ -96,4 +189,11 @@ RSpec.describe 'Projects > Members > Sorting' do
def second_member
page.all('ul.content-list > li').last.text
end
+
+ def expect_sort_by(text, sort_direction)
+ within('[data-testid="members-sort-dropdown"]') do
+ expect(page).to have_css('button[aria-haspopup="true"]', text: text)
+ expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
+ end
+ end
end
diff --git a/spec/features/projects/members/tabs_spec.rb b/spec/features/projects/members/tabs_spec.rb
index bdcf02c82a4..eef3395de91 100644
--- a/spec/features/projects/members/tabs_spec.rb
+++ b/spec/features/projects/members/tabs_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > Tabs' do
+ include Spec::Support::Helpers::Features::MembersHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
@@ -19,55 +20,93 @@ RSpec.describe 'Projects > Members > Tabs' do
end
end
- before do
- allow(Kaminari.config).to receive(:default_per_page).and_return(1)
-
- sign_in(user)
- visit project_project_members_path(project)
- end
+ context 'tabs' do
+ before do
+ sign_in(user)
+ visit project_project_members_path(project)
+ end
- where(:tab, :count) do
- 'Members' | 3
- 'Invited' | 2
- 'Groups' | 2
- 'Access requests' | 2
- end
+ where(:tab, :count) do
+ 'Members' | 3
+ 'Invited' | 2
+ 'Groups' | 2
+ 'Access requests' | 2
+ end
- with_them do
- it "renders #{params[:tab]} tab" do
- expect(page).to have_selector('.nav-link', text: "#{tab} #{count}")
+ with_them do
+ it "renders #{params[:tab]} tab" do
+ expect(page).to have_selector('.nav-link', text: "#{tab} #{count}")
+ end
end
- end
- context 'displays "Members" tab by default' do
- it_behaves_like 'active "Members" tab'
+ context 'displays "Members" tab by default' do
+ it_behaves_like 'active "Members" tab'
+ end
end
- context 'when searching "Groups"', :js do
+ context 'when `vue_project_members_list` feature flag is enabled' do
before do
- click_link 'Groups'
+ sign_in(user)
+ visit project_project_members_path(project)
+ end
+
+ context 'when searching "Groups"', :js do
+ before do
+ click_link 'Groups'
+
+ fill_in_filtered_search 'Search groups', with: 'group'
+ end
- page.within '[data-testid="group-link-search-form"]' do
- fill_in 'search_groups', with: 'group'
- find('button[type="submit"]').click
+ it 'displays "Groups" tab' do
+ expect(page).to have_selector('.nav-link.active', text: 'Groups')
+ end
+
+ context 'and then searching "Members"' do
+ before do
+ click_link 'Members 3'
+
+ fill_in_filtered_search 'Filter members', with: 'user'
+ end
+
+ it_behaves_like 'active "Members" tab'
end
end
+ end
+
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
- it 'displays "Groups" tab' do
- expect(page).to have_selector('.nav-link.active', text: 'Groups')
+ sign_in(user)
+ visit project_project_members_path(project)
end
- context 'and then searching "Members"' do
+ context 'when searching "Groups"', :js do
before do
- click_link 'Members 3'
+ click_link 'Groups'
- page.within '[data-testid="user-search-form"]' do
- fill_in 'search', with: 'user'
+ page.within '[data-testid="group-link-search-form"]' do
+ fill_in 'search_groups', with: 'group'
find('button[type="submit"]').click
end
end
- it_behaves_like 'active "Members" tab'
+ it 'displays "Groups" tab' do
+ expect(page).to have_selector('.nav-link.active', text: 'Groups')
+ end
+
+ context 'and then searching "Members"' do
+ before do
+ click_link 'Members 3'
+
+ page.within '[data-testid="user-search-form"]' do
+ fill_in 'search', with: 'user'
+ find('button[type="submit"]').click
+ end
+ end
+
+ it_behaves_like 'active "Members" tab'
+ end
end
end
end
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 25791b393bc..4ff3827b240 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -67,23 +67,4 @@ RSpec.describe 'Project 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 project_path(project)
-
- expect(page).not_to have_selector('.js-invite-members-trigger')
- end
- end
-
- context 'when invite team members is available' do
- it 'includes the div for js-invite-members-trigger' do
- stub_feature_flags(invite_members_group_modal: true)
- allow_any_instance_of(InviteMembersHelper).to receive(:invite_members_allowed?).and_return(true)
-
- visit project_path(project)
-
- expect(page).to have_selector('.js-invite-members-trigger')
- end
- end
end
diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb
new file mode 100644
index 00000000000..24c9edb79e5
--- /dev/null
+++ b/spec/features/projects/pages/user_adds_domain_spec.rb
@@ -0,0 +1,185 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'User adds pages domain', :js do
+ include LetsEncryptHelpers
+
+ let_it_be(:project) { create(:project, pages_https_only: false) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
+
+ project.add_maintainer(user)
+
+ sign_in(user)
+ end
+
+ context 'when pages are exposed on external HTTP address', :http_pages_enabled do
+ let(:project) { create(:project, pages_https_only: false) }
+
+ shared_examples 'adds new domain' do
+ it 'adds new domain' do
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+ end
+
+ it 'allows to add new domain' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('New Domain')
+ end
+
+ it_behaves_like 'adds new domain'
+
+ context 'when project in group namespace' do
+ it_behaves_like 'adds new domain' do
+ let(:group) { create :group }
+ let(:project) { create(:project, namespace: group, pages_https_only: false) }
+ end
+ end
+
+ context 'when pages domain is added' do
+ before do
+ create(:pages_domain, project: project, domain: 'my.test.domain.com')
+
+ visit new_project_pages_domain_path(project)
+ end
+
+ it 'renders certificates is disabled' do
+ expect(page).to have_content('Support for custom certificates is disabled')
+ end
+
+ it 'does not adds new domain and renders error message' do
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('Domain has already been taken')
+ end
+ end
+ end
+
+ context 'when pages are exposed on external HTTPS address', :https_pages_enabled, :js do
+ let(:certificate_pem) do
+ attributes_for(:pages_domain)[:certificate]
+ end
+
+ let(:certificate_key) do
+ attributes_for(:pages_domain)[:key]
+ end
+
+ it 'adds new domain with certificate' do
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+
+ fill_in 'Certificate (PEM)', with: certificate_pem
+ fill_in 'Key (PEM)', with: certificate_key
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+
+ it "adds new domain with certificate if Let's Encrypt is enabled" do
+ stub_lets_encrypt_settings
+
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+
+ find('.js-auto-ssl-toggle-container .project-feature-toggle').click
+
+ fill_in 'Certificate (PEM)', with: certificate_pem
+ fill_in 'Key (PEM)', with: certificate_key
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+
+ it 'shows validation error if domain is duplicated' do
+ project.pages_domains.create!(domain: 'my.test.domain.com')
+
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('Domain has already been taken')
+ end
+
+ describe 'with dns verification enabled' do
+ before do
+ stub_application_setting(pages_domain_verification_enabled: true)
+ end
+
+ it 'shows the DNS verification record' do
+ domain = create(:pages_domain, project: project)
+
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ expect(page).to have_field :domain_verification, with: "#{domain.verification_domain} TXT #{domain.keyed_verification_code}"
+ end
+ end
+
+ describe 'updating the certificate for an existing domain' do
+ let!(:domain) do
+ create(:pages_domain, project: project, auto_ssl_enabled: false)
+ end
+
+ it 'allows the certificate to be updated' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ click_button 'Save Changes'
+
+ expect(page).to have_content('Domain was updated')
+ end
+
+ context 'when the certificate is invalid' do
+ let!(:domain) do
+ create(:pages_domain, :without_certificate, :without_key, project: project)
+ end
+
+ it 'tells the user what the problem is' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+
+ fill_in 'Certificate (PEM)', with: 'invalid data'
+ click_button 'Save Changes'
+
+ expect(page).to have_content('Certificate must be a valid PEM certificate')
+ expect(page).to have_content('Certificate misses intermediates')
+ expect(page).to have_content("Key doesn't match the certificate")
+ end
+ end
+
+ it 'allows the certificate to be removed', :js do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+
+ accept_confirm { click_link 'Remove' }
+
+ expect(page).to have_field('Certificate (PEM)', with: '')
+ expect(page).to have_field('Key (PEM)', with: '')
+ domain.reload
+ expect(domain.certificate).to be_nil
+ expect(domain.key).to be_nil
+ end
+
+ it 'shows the DNS CNAME record' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ expect(page).to have_field :domain_dns, with: "#{domain.domain} CNAME #{domain.project.pages_subdomain}.#{Settings.pages.host}."
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pages_lets_encrypt_spec.rb b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
index 302e9f5e533..cf8438d5e6f 100644
--- a/spec/features/projects/pages_lets_encrypt_spec.rb
+++ b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
project.add_role(user, role)
sign_in(user)
- project.namespace.update(owner: user)
+ project.namespace.update!(owner: user)
allow_next_instance_of(Project) do |instance|
allow(instance).to receive(:pages_deployed?) { true }
end
diff --git a/spec/features/projects/pages/user_edits_settings_spec.rb b/spec/features/projects/pages/user_edits_settings_spec.rb
new file mode 100644
index 00000000000..3649fae17ce
--- /dev/null
+++ b/spec/features/projects/pages/user_edits_settings_spec.rb
@@ -0,0 +1,201 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'Pages edits pages settings', :js do
+ let(:project) { create(:project, pages_https_only: false) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
+
+ project.add_maintainer(user)
+
+ sign_in(user)
+ end
+
+ context 'when user is the owner' do
+ before do
+ project.namespace.update!(owner: user)
+ end
+
+ context 'when pages deployed' do
+ before do
+ project.mark_pages_as_deployed
+ end
+
+ it 'renders Access pages' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Access pages')
+ end
+
+ context 'when pages are disabled in the project settings' do
+ it 'renders disabled warning' do
+ project.project_feature.update!(pages_access_level: ProjectFeature::DISABLED)
+
+ visit project_pages_path(project)
+
+ expect(page).to have_content('GitLab Pages are disabled for this project')
+ end
+ end
+
+ it 'renders first deployment warning' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('It may take up to 30 minutes before the site is available after the first deployment.')
+ end
+
+ shared_examples 'does not render access control warning' do
+ it 'does not render access control warning' do
+ visit project_pages_path(project)
+
+ expect(page).not_to have_content('Access Control is enabled for this Pages website')
+ end
+ end
+
+ include_examples 'does not render access control warning'
+
+ context 'when access control is enabled in gitlab settings' do
+ before do
+ stub_pages_setting(access_control: true)
+ end
+
+ it 'renders access control warning' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Access Control is enabled for this Pages website')
+ end
+
+ context 'when pages are public' do
+ before do
+ project.project_feature.update!(pages_access_level: ProjectFeature::PUBLIC)
+ end
+
+ include_examples 'does not render access control warning'
+ end
+ end
+
+ context 'when support for external domains is disabled' do
+ it 'renders message that support is disabled' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Support for domains and certificates is disabled')
+ end
+ end
+ end
+
+ it 'does not see anything to destroy' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Configure pages')
+ expect(page).not_to have_link('Remove pages')
+ end
+
+ describe 'project settings page' do
+ it 'renders "Pages" tab' do
+ visit edit_project_path(project)
+
+ page.within '.nav-sidebar' do
+ expect(page).to have_link('Pages')
+ end
+ end
+
+ context 'when pages are disabled' do
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
+ end
+
+ it 'does not render "Pages" tab' do
+ visit edit_project_path(project)
+
+ page.within '.nav-sidebar' do
+ expect(page).not_to have_link('Pages')
+ end
+ end
+ end
+ end
+ end
+
+ describe 'HTTPS settings', :https_pages_enabled do
+ before do
+ project.namespace.update!(owner: user)
+
+ project.mark_pages_as_deployed
+ end
+
+ it 'tries to change the setting' do
+ visit project_pages_path(project)
+ expect(page).to have_content("Force HTTPS (requires valid certificates)")
+
+ uncheck :project_pages_https_only
+
+ click_button 'Save'
+
+ expect(page).to have_text('Your changes have been saved')
+ expect(page).not_to have_checked_field('project_pages_https_only')
+ end
+
+ context 'setting could not be updated' do
+ let(:service) { instance_double('Projects::UpdateService') }
+
+ before do
+ allow(Projects::UpdateService).to receive(:new).and_return(service)
+ allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured')
+ end
+
+ it 'tries to change the setting' do
+ visit project_pages_path(project)
+
+ uncheck :project_pages_https_only
+
+ click_button 'Save'
+
+ expect(page).to have_text('Some error has occured')
+ end
+ end
+
+ context 'non-HTTPS domain exists' do
+ let(:project) { create(:project, pages_https_only: false) }
+
+ before do
+ create(:pages_domain, :without_key, :without_certificate, project: project)
+ end
+
+ it 'the setting is disabled' do
+ visit project_pages_path(project)
+
+ expect(page).to have_field(:project_pages_https_only, disabled: true)
+ expect(page).to have_button('Save')
+ end
+ end
+
+ context 'HTTPS pages are disabled', :https_pages_disabled do
+ it 'the setting is unavailable' do
+ visit project_pages_path(project)
+
+ expect(page).not_to have_field(:project_pages_https_only)
+ expect(page).not_to have_content('Force HTTPS (requires valid certificates)')
+ expect(page).to have_button('Save')
+ end
+ end
+ end
+
+ describe 'Remove page' do
+ context 'when pages are deployed' do
+ before do
+ project.mark_pages_as_deployed
+ end
+
+ it 'removes the pages', :sidekiq_inline do
+ visit project_pages_path(project)
+
+ expect(page).to have_link('Remove pages')
+
+ accept_confirm { click_link 'Remove pages' }
+
+ expect(page).to have_content('Pages were scheduled for removal')
+ expect(project.reload.pages_deployed?).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
deleted file mode 100644
index 11f712fde81..00000000000
--- a/spec/features/projects/pages_spec.rb
+++ /dev/null
@@ -1,411 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.shared_examples 'pages settings editing' do
- let_it_be(:project) { create(:project, pages_https_only: false) }
- let(:user) { create(:user) }
- let(:role) { :maintainer }
-
- before do
- allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
-
- project.add_role(user, role)
-
- sign_in(user)
- end
-
- context 'when user is the owner' do
- before do
- project.namespace.update(owner: user)
- end
-
- context 'when pages deployed' do
- before do
- allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
- end
-
- it 'renders Access pages' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Access pages')
- end
-
- context 'when pages are disabled in the project settings' do
- it 'renders disabled warning' do
- project.project_feature.update!(pages_access_level: ProjectFeature::DISABLED)
-
- visit project_pages_path(project)
-
- expect(page).to have_content('GitLab Pages are disabled for this project')
- end
- end
-
- it 'renders first deployment warning' do
- visit project_pages_path(project)
-
- expect(page).to have_content('It may take up to 30 minutes before the site is available after the first deployment.')
- end
-
- shared_examples 'does not render access control warning' do
- it 'does not render access control warning' do
- visit project_pages_path(project)
-
- expect(page).not_to have_content('Access Control is enabled for this Pages website')
- end
- end
-
- include_examples 'does not render access control warning'
-
- context 'when access control is enabled in gitlab settings' do
- before do
- stub_pages_setting(access_control: true)
- end
-
- it 'renders access control warning' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Access Control is enabled for this Pages website')
- end
-
- context 'when pages are public' do
- before do
- project.project_feature.update!(pages_access_level: ProjectFeature::PUBLIC)
- end
-
- include_examples 'does not render access control warning'
- end
- end
-
- context 'when support for external domains is disabled' do
- it 'renders message that support is disabled' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Support for domains and certificates is disabled')
- end
- end
-
- context 'when pages are exposed on external HTTP address', :http_pages_enabled do
- let(:project) { create(:project, pages_https_only: false) }
-
- shared_examples 'adds new domain' do
- it 'adds new domain' do
- visit new_project_pages_domain_path(project)
-
- fill_in 'Domain', with: 'my.test.domain.com'
- click_button 'Create New Domain'
-
- expect(page).to have_content('my.test.domain.com')
- end
- end
-
- it 'allows to add new domain' do
- visit project_pages_path(project)
-
- expect(page).to have_content('New Domain')
- end
-
- it_behaves_like 'adds new domain'
-
- context 'when project in group namespace' do
- it_behaves_like 'adds new domain' do
- let(:group) { create :group }
- let(:project) { create(:project, namespace: group, pages_https_only: false) }
- end
- end
-
- context 'when pages domain is added' do
- before do
- create(:pages_domain, project: project, domain: 'my.test.domain.com')
-
- visit new_project_pages_domain_path(project)
- end
-
- it 'renders certificates is disabled' do
- expect(page).to have_content('Support for custom certificates is disabled')
- end
-
- it 'does not adds new domain and renders error message' do
- fill_in 'Domain', with: 'my.test.domain.com'
- click_button 'Create New Domain'
-
- expect(page).to have_content('Domain has already been taken')
- end
- end
- end
-
- context 'when pages are exposed on external HTTPS address', :https_pages_enabled, :js do
- let(:certificate_pem) do
- attributes_for(:pages_domain)[:certificate]
- end
-
- let(:certificate_key) do
- attributes_for(:pages_domain)[:key]
- end
-
- it 'adds new domain with certificate' do
- visit new_project_pages_domain_path(project)
-
- fill_in 'Domain', with: 'my.test.domain.com'
-
- if ::Gitlab::LetsEncrypt.enabled?
- find('.js-auto-ssl-toggle-container .project-feature-toggle').click
- end
-
- fill_in 'Certificate (PEM)', with: certificate_pem
- fill_in 'Key (PEM)', with: certificate_key
- click_button 'Create New Domain'
-
- expect(page).to have_content('my.test.domain.com')
- end
-
- it 'shows validation error if domain is duplicated' do
- project.pages_domains.create!(domain: 'my.test.domain.com')
-
- visit new_project_pages_domain_path(project)
-
- fill_in 'Domain', with: 'my.test.domain.com'
- click_button 'Create New Domain'
-
- expect(page).to have_content('Domain has already been taken')
- end
-
- describe 'with dns verification enabled' do
- before do
- stub_application_setting(pages_domain_verification_enabled: true)
- end
-
- it 'shows the DNS verification record' do
- domain = create(:pages_domain, project: project)
-
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
- expect(page).to have_field :domain_verification, with: "#{domain.verification_domain} TXT #{domain.keyed_verification_code}"
- end
- end
-
- describe 'updating the certificate for an existing domain' do
- let!(:domain) do
- create(:pages_domain, project: project, auto_ssl_enabled: false)
- end
-
- it 'allows the certificate to be updated' do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
- click_button 'Save Changes'
-
- expect(page).to have_content('Domain was updated')
- end
-
- context 'when the certificate is invalid' do
- let!(:domain) do
- create(:pages_domain, :without_certificate, :without_key, project: project)
- end
-
- it 'tells the user what the problem is' do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
-
- if ::Gitlab::LetsEncrypt.enabled?
- find('.js-auto-ssl-toggle-container .project-feature-toggle').click
- end
-
- fill_in 'Certificate (PEM)', with: 'invalid data'
- click_button 'Save Changes'
-
- expect(page).to have_content('Certificate must be a valid PEM certificate')
- expect(page).to have_content('Certificate misses intermediates')
- expect(page).to have_content("Key doesn't match the certificate")
- end
- end
-
- it 'allows the certificate to be removed', :js do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
-
- accept_confirm { click_link 'Remove' }
-
- expect(page).to have_field('Certificate (PEM)', with: '')
- expect(page).to have_field('Key (PEM)', with: '')
- domain.reload
- expect(domain.certificate).to be_nil
- expect(domain.key).to be_nil
- end
-
- it 'shows the DNS CNAME record' do
- visit project_pages_path(project)
-
- within('#content-body') { click_link 'Edit' }
- expect(page).to have_field :domain_dns, with: "#{domain.domain} CNAME #{domain.project.pages_subdomain}.#{Settings.pages.host}."
- end
- end
- end
- end
-
- it 'does not see anything to destroy' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Configure pages')
- expect(page).not_to have_link('Remove pages')
- end
-
- describe 'project settings page' do
- it 'renders "Pages" tab' do
- visit edit_project_path(project)
-
- page.within '.nav-sidebar' do
- expect(page).to have_link('Pages')
- end
- end
-
- context 'when pages are disabled' do
- before do
- allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
- end
-
- it 'does not render "Pages" tab' do
- visit edit_project_path(project)
-
- page.within '.nav-sidebar' do
- expect(page).not_to have_link('Pages')
- end
- end
- end
- end
- end
-
- describe 'HTTPS settings', :https_pages_enabled do
- before do
- project.namespace.update(owner: user)
-
- allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
- end
-
- it 'tries to change the setting' do
- visit project_pages_path(project)
- expect(page).to have_content("Force HTTPS (requires valid certificates)")
-
- uncheck :project_pages_https_only
-
- click_button 'Save'
-
- expect(page).to have_text('Your changes have been saved')
- expect(page).not_to have_checked_field('project_pages_https_only')
- end
-
- context 'setting could not be updated' do
- let(:service) { instance_double('Projects::UpdateService') }
-
- before do
- allow(Projects::UpdateService).to receive(:new).and_return(service)
- allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured')
- end
-
- it 'tries to change the setting' do
- visit project_pages_path(project)
-
- uncheck :project_pages_https_only
-
- click_button 'Save'
-
- expect(page).to have_text('Some error has occured')
- end
- end
-
- context 'non-HTTPS domain exists' do
- let(:project) { create(:project, pages_https_only: false) }
-
- before do
- create(:pages_domain, :without_key, :without_certificate, project: project)
- end
-
- it 'the setting is disabled' do
- visit project_pages_path(project)
-
- expect(page).to have_field(:project_pages_https_only, disabled: true)
- expect(page).to have_button('Save')
- end
- end
-
- context 'HTTPS pages are disabled', :https_pages_disabled do
- it 'the setting is unavailable' do
- visit project_pages_path(project)
-
- expect(page).not_to have_field(:project_pages_https_only)
- expect(page).not_to have_content('Force HTTPS (requires valid certificates)')
- expect(page).to have_button('Save')
- end
- end
- end
-
- describe 'Remove page' do
- let(:project) { create :project, :repository }
-
- context 'when pages are deployed' do
- let(:pipeline) do
- commit_sha = project.commit('HEAD').sha
-
- project.ci_pipelines.create(
- ref: 'HEAD',
- sha: commit_sha,
- source: :push,
- protected: false
- )
- end
-
- let(:ci_build) do
- create(
- :ci_build,
- project: project,
- pipeline: pipeline,
- ref: 'HEAD')
- end
-
- let!(:artifact) do
- create(:ci_job_artifact, :archive, :correct_checksum,
- file: fixture_file_upload(File.join('spec/fixtures/pages.zip')), job: ci_build)
- end
-
- let!(:metadata) do
- create(:ci_job_artifact, :metadata,
- file: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta')), job: ci_build)
- end
-
- before do
- result = Projects::UpdatePagesService.new(project, ci_build).execute
- expect(result[:status]).to eq(:success)
- expect(project).to be_pages_deployed
- end
-
- it 'removes the pages', :sidekiq_inline do
- visit project_pages_path(project)
-
- expect(page).to have_link('Remove pages')
-
- accept_confirm { click_link 'Remove pages' }
-
- expect(page).to have_content('Pages were scheduled for removal')
- expect(project.reload.pages_deployed?).to be_falsey
- end
- end
- end
-end
-
-RSpec.describe 'Pages', :js do
- include LetsEncryptHelpers
-
- context 'when editing normally' do
- include_examples 'pages settings editing'
- end
-
- context 'when letsencrypt support is enabled' do
- before do
- stub_lets_encrypt_settings
- end
-
- include_examples 'pages settings editing'
- end
-end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index ac3566fbbdd..94800717677 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -14,7 +14,6 @@ RSpec.describe 'Pipeline', :js do
before do
sign_in(user)
project.add_role(user, role)
- stub_feature_flags(graphql_pipeline_details: false)
end
shared_context 'pipeline builds' do
@@ -57,7 +56,7 @@ RSpec.describe 'Pipeline', :js do
end
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
include_context 'pipeline builds'
let(:group) { create(:group) }
@@ -69,7 +68,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows the pipeline graph' do
visit_pipeline
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
@@ -625,20 +624,6 @@ RSpec.describe 'Pipeline', :js do
end
end
end
-
- context 'when FF dag_pipeline_tab is disabled' do
- before do
- stub_feature_flags(dag_pipeline_tab: false)
- visit_pipeline
- end
-
- it 'does not show DAG link' do
- expect(page).to have_link('Pipeline')
- expect(page).to have_link('Jobs')
- expect(page).not_to have_link('DAG')
- expect(page).to have_link('Failed Jobs')
- end
- end
end
context 'when user does not have access to read jobs' do
@@ -646,7 +631,7 @@ RSpec.describe 'Pipeline', :js do
project.update(public_builds: false)
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
@@ -657,7 +642,7 @@ RSpec.describe 'Pipeline', :js do
end
it 'shows the pipeline graph' do
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
@@ -691,13 +676,13 @@ RSpec.describe 'Pipeline', :js do
downstream: downstream)
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
before do
visit project_pipeline_path(project, pipeline)
end
it 'shows the pipeline with a bridge job' do
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('cross-build')
end
@@ -740,7 +725,7 @@ RSpec.describe 'Pipeline', :js do
end
end
- describe 'GET /:project/pipelines/:id/builds' do
+ describe 'GET /:project/-/pipelines/:id/builds' do
before do
visit builds_project_pipeline_path(project, pipeline)
end
@@ -767,9 +752,64 @@ RSpec.describe 'Pipeline', :js do
stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
end
- describe 'GET /:project/pipelines/:id' do
+ describe 'GET /:project/-/pipelines/:id' do
subject { visit project_pipeline_path(project, pipeline) }
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+
+ it 'shows deploy job as created' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('pending')
+ end
+
+ within('.js-pipeline-graph') do
+ within '.stage-column:nth-child(1)' do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-created')
+ end
+ end
+ end
+
+ context 'when test job succeeded' do
+ before do
+ test_job.success!
+ end
+
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(1)' do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-success')
+ end
+
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+ end
+
it 'shows deploy job as created' do
subject
@@ -777,13 +817,13 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('pending')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(1)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
expect(page).to have_content('test')
expect(page).to have_css('.ci-status-icon-pending')
end
- within '.stage-column:nth-child(2)' do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-created')
end
@@ -802,13 +842,13 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('running')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(1)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
expect(page).to have_content('test')
expect(page).to have_css('.ci-status-icon-success')
end
- within '.stage-column:nth-child(2)' do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-pending')
end
@@ -831,14 +871,37 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('waiting')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-waiting-for-resource')
end
end
end
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
context 'when resource is released from another job' do
before do
another_job.success!
@@ -851,19 +914,86 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('running')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-pending')
end
end
end
+
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
end
end
end
end
- describe 'GET /:project/pipelines/:id/builds' do
+ describe 'GET /:project/-/pipelines/:id/builds' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
@@ -965,7 +1095,7 @@ RSpec.describe 'Pipeline', :js do
end
end
- describe 'GET /:project/pipelines/:id/failures' do
+ describe 'GET /:project/-/pipelines/:id/failures' do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: '1234') }
let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
@@ -1078,12 +1208,29 @@ RSpec.describe 'Pipeline', :js do
expect(current_path).to eq(pipeline_path(pipeline))
expect(page).not_to have_content('Failed Jobs')
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
+ end
+
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+
+ it 'displays the pipeline graph' do
+ subject
+
+ expect(current_path).to eq(pipeline_path(pipeline))
+ expect(page).not_to have_content('Failed Jobs')
+ expect(page).to have_selector('.pipeline-visualization')
+ end
end
end
end
- describe 'GET /:project/pipelines/:id/dag' do
+ describe 'GET /:project/-/pipelines/:id/dag' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 450524b8d70..6421d3db2cd 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -13,11 +13,13 @@ RSpec.describe 'Pipelines', :js do
before do
sign_in(user)
stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+
project.add_developer(user)
project.update!(auto_devops_attributes: { enabled: false })
end
- describe 'GET /:project/pipelines' do
+ describe 'GET /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
let!(:pipeline) do
@@ -287,23 +289,23 @@ RSpec.describe 'Pipelines', :js do
end
it 'has a dropdown with play button' do
- expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
end
it 'has link to the manual action' do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
expect(page).to have_button('manual build')
end
context 'when manual action was played' do
before do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
click_button('manual build')
end
it 'enqueues manual action job' do
- expect(page).to have_selector('.js-pipeline-dropdown-manual-actions:disabled')
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] .gl-dropdown-toggle:disabled')
end
end
end
@@ -321,11 +323,11 @@ RSpec.describe 'Pipelines', :js do
end
it 'has a dropdown for actionable jobs' do
- expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
+ expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
end
it "has link to the delayed job's action" do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
time_diff = [0, delayed_job.scheduled_at - Time.now].max
expect(page).to have_button('delayed job 1')
@@ -341,7 +343,7 @@ RSpec.describe 'Pipelines', :js do
end
it "shows 00:00:00 as the remaining time" do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
expect(page).to have_content("00:00:00")
end
@@ -349,7 +351,7 @@ RSpec.describe 'Pipelines', :js do
context 'when user played a delayed job immediately' do
before do
- find('.js-pipeline-dropdown-manual-actions').click
+ find('[data-testid="pipelines-manual-actions-dropdown"]').click
page.accept_confirm { click_button('delayed job 1') }
wait_for_requests
end
@@ -517,56 +519,75 @@ RSpec.describe 'Pipelines', :js do
end
end
- context 'mini pipeline graph' do
- let!(:build) do
- create(:ci_build, :pending, pipeline: pipeline,
- stage: 'build',
- name: 'build')
- end
-
- before do
- visit_project_pipelines
- end
+ shared_examples 'mini pipeline renders' do |ci_mini_pipeline_gl_dropdown_enabled|
+ context 'mini pipeline graph' do
+ let!(:build) do
+ create(:ci_build, :pending, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
- it 'renders a mini pipeline graph' do
- expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]')
- expect(page).to have_selector('.js-builds-dropdown-button')
- end
+ before do
+ stub_feature_flags(ci_mini_pipeline_gl_dropdown: ci_mini_pipeline_gl_dropdown_enabled)
+ visit_project_pipelines
+ end
- context 'when clicking a stage badge' do
- it 'opens a dropdown' do
- find('.js-builds-dropdown-button').click
+ let_it_be(:dropdown_toggle_selector) do
+ if ci_mini_pipeline_gl_dropdown_enabled
+ '[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle'
+ else
+ '[data-testid="mini-pipeline-graph-dropdown-toggle"]'
+ end
+ end
- expect(page).to have_link build.name
+ it 'renders a mini pipeline graph' do
+ expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]')
+ expect(page).to have_selector(dropdown_toggle_selector)
end
- it 'is possible to cancel pending build' do
- find('.js-builds-dropdown-button').click
- find('.js-ci-action').click
- wait_for_requests
+ context 'when clicking a stage badge' do
+ it 'opens a dropdown' do
+ find(dropdown_toggle_selector).click
- expect(build.reload).to be_canceled
- end
- end
+ expect(page).to have_link build.name
+ end
- context 'for a failed pipeline' do
- let!(:build) do
- create(:ci_build, :failed, pipeline: pipeline,
- stage: 'build',
- name: 'build')
+ it 'is possible to cancel pending build' do
+ find(dropdown_toggle_selector).click
+ find('.js-ci-action').click
+ wait_for_requests
+
+ expect(build.reload).to be_canceled
+ end
end
- it 'displays the failure reason' do
- find('.js-builds-dropdown-button').click
+ context 'for a failed pipeline' do
+ let!(:build) do
+ create(:ci_build, :failed, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
+
+ it 'displays the failure reason' do
+ find(dropdown_toggle_selector).click
- within('.js-builds-dropdown-list') do
- build_element = page.find('.mini-pipeline-graph-dropdown-item')
- expect(build_element['title']).to eq('build - failed - (unknown failure)')
+ within('.js-builds-dropdown-list') do
+ build_element = page.find('.mini-pipeline-graph-dropdown-item')
+ expect(build_element['title']).to eq('build - failed - (unknown failure)')
+ end
end
end
end
end
+ context 'with ci_mini_pipeline_gl_dropdown disabled' do
+ it_behaves_like "mini pipeline renders", false
+ end
+
+ context 'with ci_mini_pipeline_gl_dropdown enabled' do
+ it_behaves_like "mini pipeline renders", true
+ end
+
context 'with pagination' do
before do
allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
@@ -597,7 +618,7 @@ RSpec.describe 'Pipelines', :js do
end
end
- describe 'GET /:project/pipelines/show' do
+ describe 'GET /:project/-/pipelines/show' do
let(:project) { create(:project, :repository) }
let(:pipeline) do
@@ -649,7 +670,7 @@ RSpec.describe 'Pipelines', :js do
end
end
- describe 'POST /:project/pipelines' do
+ describe 'POST /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
before do
diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
index 3994f55caee..4dfd4416eeb 100644
--- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
@@ -40,4 +40,8 @@ RSpec.describe 'Slack slash commands', :js do
value = find_field('url').value
expect(value).to match("api/v4/projects/#{project.id}/services/slack_slash_commands/trigger")
end
+
+ it 'shows help content' do
+ expect(page).to have_content('This service allows users to perform common operations on this project by entering slash commands in Slack.')
+ end
end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index c087237fd7c..39c4315bf0f 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
it 'updates auto_cancel_pending_pipelines' do
visit project_settings_ci_cd_path(project)
- page.check('Auto-cancel redundant, pending pipelines')
+ page.check('Auto-cancel redundant pipelines')
page.within '#js-general-pipeline-settings' do
click_on 'Save changes'
end
diff --git a/spec/features/projects/settings/project_settings_spec.rb b/spec/features/projects/settings/project_settings_spec.rb
index 7b2b5594c22..cd1c9ecde9c 100644
--- a/spec/features/projects/settings/project_settings_spec.rb
+++ b/spec/features/projects/settings/project_settings_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Projects settings' do
let_it_be(:project) { create(:project) }
let(:user) { project.owner }
let(:panel) { find('.general-settings', match: :first) }
- let(:button) { panel.find('.btn.js-settings-toggle') }
+ let(:button) { panel.find('.btn.gl-button.js-settings-toggle') }
let(:title) { panel.find('.settings-title') }
before do
@@ -39,7 +39,7 @@ RSpec.describe 'Projects settings' do
visit edit_project_path(project)
forking_enabled_input = find('input[name="project[project_feature_attributes][forking_access_level]"]', visible: :hidden)
- forking_enabled_button = find('input[name="project[project_feature_attributes][forking_access_level]"] + label > button')
+ forking_enabled_button = find('[data-for="project[project_feature_attributes][forking_access_level]"] .gl-toggle')
expect(forking_enabled_input.value).to eq('20')
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index 2b03ecf5af1..6e4082d1391 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
select('7 days', from: 'Remove tags older than:')
fill_in('Remove tags matching:', with: '.*-production')
- submit_button = find('.btn.btn-success')
+ submit_button = find('.btn.gl-button.btn-success')
expect(submit_button).not_to be_disabled
submit_button.click
end
@@ -53,7 +53,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
within '#js-registry-policies' do
fill_in('Remove tags matching:', with: '*-production')
- submit_button = find('.btn.btn-success')
+ submit_button = find('.btn.gl-button.btn-success')
expect(submit_button).not_to be_disabled
submit_button.click
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 3e520142117..2f257d299d8 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Add key'
expect(page).to have_content('new_deploy_key')
- expect(page).to have_content('Write access allowed')
+ expect(page).to have_content('Grant write permissions to this key')
end
it 'edit an existing deploy key' do
@@ -77,7 +77,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Save changes'
expect(page).to have_content('updated_deploy_key')
- expect(page).to have_content('Write access allowed')
+ expect(page).to have_content('Grant write permissions to this key')
end
it 'edit an existing public deploy key to be writable' do
@@ -90,7 +90,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
click_button 'Save changes'
expect(page).to have_content('public_deploy_key')
- expect(page).to have_content('Write access allowed')
+ expect(page).to have_content('Grant write permissions to this key')
end
it 'edit a deploy key from projects user has access to' do
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index e97e4a2030a..e8e32d93f7b 100644
--- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
@@ -71,7 +71,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
@@ -92,7 +92,7 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).not_to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index 726b8fb6840..0d22da34b91 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe 'Projects > Settings > User manages project members' do
+ include Spec::Support::Helpers::Features::MembersHelpers
+ include Select2Helper
+
let(:group) { create(:group, name: 'OpenSource') }
let(:project) { create(:project) }
let(:project2) { create(:project) }
@@ -16,62 +19,123 @@ RSpec.describe 'Projects > Settings > User manages project members' do
sign_in(user)
end
- it 'cancels a team member', :js do
- visit(project_project_members_path(project))
+ context 'when `vue_project_members_list` feature flag is enabled' do
+ it 'cancels a team member', :js do
+ visit(project_project_members_path(project))
+
+ page.within find_member_row(user_dmitriy) do
+ click_button 'Remove member'
+ end
+
+ page.within('[role="dialog"]') do
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ click_button('Remove member')
+ end
+
+ visit(project_project_members_path(project))
+
+ expect(members_table).not_to have_content(user_dmitriy.name)
+ expect(members_table).not_to have_content(user_dmitriy.username)
+ end
+
+ it 'imports a team from another project', :js do
+ stub_feature_flags(invite_members_group_modal: false)
+
+ project2.add_maintainer(user)
+ project2.add_reporter(user_mike)
+
+ visit(project_project_members_path(project))
- project_member = project.project_members.find_by(user_id: user_dmitriy.id)
+ page.within('.invite-users-form') do
+ click_link('Import')
+ end
- page.within("#project_member_#{project_member.id}") do
- # Open modal
- click_on('Remove user from project')
+ select2(project2.id, from: '#source_project_id')
+ click_button('Import project members')
+
+ expect(find_member_row(user_mike)).to have_content('Reporter')
end
- expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+ it 'shows all members of project shared group', :js do
+ group.add_owner(user)
+ group.add_developer(user_dmitriy)
+
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
+ share_link.group_id = group.id
+ share_link.save!
- click_on('Remove member')
+ visit(project_project_members_path(project))
- visit(project_project_members_path(project))
+ click_link 'Groups'
- expect(page).not_to have_content(user_dmitriy.name)
- expect(page).not_to have_content(user_dmitriy.username)
+ expect(find_group_row(group)).to have_content('Maintainer')
+ end
end
- it 'imports a team from another project' do
- project2.add_maintainer(user)
- project2.add_reporter(user_mike)
+ context 'when `vue_project_members_list` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_project_members_list: false)
+ end
+
+ it 'cancels a team member', :js do
+ visit(project_project_members_path(project))
+
+ project_member = project.project_members.find_by(user_id: user_dmitriy.id)
+
+ page.within("#project_member_#{project_member.id}") do
+ # Open modal
+ click_on('Remove user from project')
+ end
+
+ expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
+
+ click_on('Remove member')
- visit(project_project_members_path(project))
+ visit(project_project_members_path(project))
- page.within('.invite-users-form') do
- click_link('Import')
+ expect(page).not_to have_content(user_dmitriy.name)
+ expect(page).not_to have_content(user_dmitriy.username)
end
- select(project2.full_name, from: 'source_project_id')
- click_button('Import')
+ it 'imports a team from another project' do
+ stub_feature_flags(invite_members_group_modal: false)
- project_member = project.project_members.find_by(user_id: user_mike.id)
+ project2.add_maintainer(user)
+ project2.add_reporter(user_mike)
- page.within("#project_member_#{project_member.id}") do
- expect(page).to have_content('Mike')
- expect(page).to have_content('Reporter')
+ visit(project_project_members_path(project))
+
+ page.within('.invite-users-form') do
+ click_link('Import')
+ end
+
+ select(project2.full_name, from: 'source_project_id')
+ click_button('Import')
+
+ project_member = project.project_members.find_by(user_id: user_mike.id)
+
+ page.within("#project_member_#{project_member.id}") do
+ expect(page).to have_content('Mike')
+ expect(page).to have_content('Reporter')
+ end
end
- end
- it 'shows all members of project shared group', :js do
- group.add_owner(user)
- group.add_developer(user_dmitriy)
+ it 'shows all members of project shared group', :js do
+ group.add_owner(user)
+ group.add_developer(user_dmitriy)
- share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
- share_link.group_id = group.id
- share_link.save!
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
+ share_link.group_id = group.id
+ share_link.save!
- visit(project_project_members_path(project))
+ visit(project_project_members_path(project))
- click_link 'Groups'
+ click_link 'Groups'
- page.within('[data-testid="project-member-groups"]') do
- expect(page).to have_content('OpenSource')
- expect(first('.group_member')).to have_content('Maintainer')
+ page.within('[data-testid="project-member-groups"]') do
+ expect(page).to have_content('OpenSource')
+ expect(first('.group_member')).to have_content('Maintainer')
+ end
end
end
end
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 6cecbbdb3d0..becb30c02b7 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
context 'merge requests select' do
it 'hides merge requests section' do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
expect(page).to have_selector('.merge-requests-feature', visible: false)
end
@@ -46,7 +46,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do
context 'builds select' do
it 'hides builds select section' do
- find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click
+ find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
expect(page).to have_selector('.builds-feature', visible: false)
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index d444ea27d35..5f7d9b0963b 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
let(:project) { create(:project, :public, :repository) }
before do
+ stub_feature_flags(vue_notification_dropdown: false)
sign_in(project.owner)
end
diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb
index febdb70de86..e6157887c12 100644
--- a/spec/features/projects/show/user_sees_git_instructions_spec.rb
+++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb
@@ -5,6 +5,13 @@ require 'spec_helper'
RSpec.describe 'Projects > Show > User sees Git instructions' do
let_it_be(:user) { create(:user) }
+ before do
+ # Reset user notification settings between examples to prevent
+ # validation failure on NotificationSetting.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/299822#note_492817174
+ user.notification_settings.reset
+ end
+
shared_examples_for 'redirects to the sign in page' do
it 'redirects to the sign in page' do
expect(current_path).to eq(new_user_session_path)
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index dfa4dad8490..55b906c2bc5 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Terraform', :js do
fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
click_button 'Remove'
- expect(page).not_to have_content(additional_state.name)
+ expect(page).to have_content("#{additional_state.name} successfully removed")
expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index feb5f348256..aff3022bd4e 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'User creates a project', :js do
before do
sign_in(user)
create(:personal_key, user: user)
+
+ stub_experiments(new_project_readme: :candidate)
end
it 'creates a new project' do
@@ -16,6 +18,10 @@ RSpec.describe 'User creates a project', :js do
find('[data-qa-selector="blank_project_link"]').click
fill_in(:project_name, with: 'Empty')
+ # part of the new_project_readme experiment
+ expect(page).to have_checked_field 'Initialize repository with a README'
+ uncheck 'Initialize repository with a README'
+
page.within('#content-body') do
click_button('Create project')
end
diff --git a/spec/features/projects/user_sees_user_popover_spec.rb b/spec/features/projects/user_sees_user_popover_spec.rb
index 9cfc6234969..52e65deae3b 100644
--- a/spec/features/projects/user_sees_user_popover_spec.rb
+++ b/spec/features/projects/user_sees_user_popover_spec.rb
@@ -38,8 +38,6 @@ RSpec.describe 'User sees user popover', :js do
it "displays user popover in system note" do
add_note("/assign @#{user.username}")
- wait_for_requests
-
find('.system-note-message .js-user-link').hover
page.within(popover_selector) do
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index 8fa5f741a95..13ae035e8ef 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -27,14 +27,13 @@ RSpec.describe 'User uses shortcuts', :js do
open_modal_shortcut_keys
- # modal-shortcuts still in the DOM, but hidden
- expect(find('#modal-shortcuts', visible: false)).not_to be_visible
+ expect(page).not_to have_selector('[data-testid="modal-shortcuts"]')
page.refresh
open_modal_shortcut_keys
# after reload, shortcuts modal doesn't exist at all until we add it
- expect(page).not_to have_selector('#modal-shortcuts')
+ expect(page).not_to have_selector('[data-testid="modal-shortcuts"]')
end
it 're-enables shortcuts' do
@@ -47,7 +46,7 @@ RSpec.describe 'User uses shortcuts', :js do
close_modal
open_modal_shortcut_keys
- expect(find('#modal-shortcuts')).to be_visible
+ expect(find('[data-testid="modal-shortcuts"]')).to be_visible
end
def open_modal_shortcut_keys
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 95d268ab1be..eb099359df9 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -9,10 +9,6 @@ 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)
@@ -174,7 +170,7 @@ RSpec.describe 'Protected Branches', :js do
stub_licensed_features(protected_refs_for_users: false)
end
- include_examples 'when the deploy_keys_on_protected_branches FF is turned on' do
+ include_examples 'Deploy keys with protected branches' do
let(:all_dropdown_sections) { %w(Roles Deploy\ Keys) }
end
end
diff --git a/spec/features/registrations/experience_level_spec.rb b/spec/features/registrations/experience_level_spec.rb
index 25496e2fef1..f432215d4a8 100644
--- a/spec/features/registrations/experience_level_spec.rb
+++ b/spec/features/registrations/experience_level_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe 'Experience level screen' do
before do
group.add_owner(user)
gitlab_sign_in(user)
- stub_experiment_for_subject(onboarding_issues: true)
visit users_sign_up_experience_level_path(namespace_path: group.to_param)
end
diff --git a/spec/features/search/user_searches_for_commits_spec.rb b/spec/features/search/user_searches_for_commits_spec.rb
index b860cd08e64..1a882050126 100644
--- a/spec/features/search/user_searches_for_commits_spec.rb
+++ b/spec/features/search/user_searches_for_commits_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for commits' do
+RSpec.describe 'User searches for commits', :js do
let(:project) { create(:project, :repository) }
let(:sha) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
let(:user) { create(:user) }
@@ -41,7 +41,7 @@ RSpec.describe 'User searches for commits' do
submit_search('See merge request')
select_search_scope('Commits')
- expect(page).to have_selector('.commit-row-description', count: 9)
+ expect(page).to have_selector('.commit-row-description', visible: false, count: 9)
end
end
end
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index e253b9f2f7a..828e478d701 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -75,7 +75,7 @@ RSpec.describe 'User searches for issues', :js do
expect(page.all('.search-result-row').last).to have_link(issue1.title)
end
- find('.reverse-sort-btn').click
+ find('[data-testid="sort-highest-icon"]').click
page.within('.results') do
expect(page.all('.search-result-row').first).to have_link(issue1.title)
diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb
index 21e8075739f..7271716644b 100644
--- a/spec/features/search/user_searches_for_merge_requests_spec.rb
+++ b/spec/features/search/user_searches_for_merge_requests_spec.rb
@@ -5,8 +5,14 @@ require 'spec_helper'
RSpec.describe 'User searches for merge requests', :js do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
- let!(:merge_request1) { create(:merge_request, title: 'Foo', source_project: project, target_project: project) }
- let!(:merge_request2) { create(:merge_request, :simple, title: 'Bar', source_project: project, target_project: project) }
+ let!(:merge_request1) { create(:merge_request, title: 'Merge Request Foo', source_project: project, target_project: project, created_at: 1.hour.ago) }
+ let!(:merge_request2) { create(:merge_request, :simple, title: 'Merge Request Bar', source_project: project, target_project: project) }
+
+ def search_for_mr(search)
+ fill_in('dashboard_search', with: search)
+ find('.btn-search').click
+ select_search_scope('Merge requests')
+ end
before do
project.add_maintainer(user)
@@ -18,9 +24,7 @@ RSpec.describe 'User searches for merge requests', :js do
include_examples 'top right search form'
it 'finds a merge request' do
- fill_in('dashboard_search', with: merge_request1.title)
- find('.btn-search').click
- select_search_scope('Merge requests')
+ search_for_mr(merge_request1.title)
page.within('.results') do
expect(page).to have_link(merge_request1.title)
@@ -28,6 +32,22 @@ RSpec.describe 'User searches for merge requests', :js do
end
end
+ it 'sorts by created date' do
+ search_for_mr('Merge Request')
+
+ page.within('.results') do
+ expect(page.all('.search-result-row').first).to have_link(merge_request2.title)
+ expect(page.all('.search-result-row').last).to have_link(merge_request1.title)
+ end
+
+ find('[data-testid="sort-highest-icon"]').click
+
+ page.within('.results') do
+ expect(page.all('.search-result-row').first).to have_link(merge_request1.title)
+ expect(page.all('.search-result-row').last).to have_link(merge_request2.title)
+ end
+ end
+
context 'when on a project page' do
it 'finds a merge request' do
find('[data-testid="project-filter"]').click
@@ -38,9 +58,7 @@ RSpec.describe 'User searches for merge requests', :js do
click_on(project.full_name)
end
- fill_in('dashboard_search', with: merge_request1.title)
- find('.btn-search').click
- select_search_scope('Merge requests')
+ search_for_mr(merge_request1.title)
page.within('.results') do
expect(page).to have_link(merge_request1.title)
diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb
index b64909dd42f..e34ae031679 100644
--- a/spec/features/search/user_searches_for_projects_spec.rb
+++ b/spec/features/search/user_searches_for_projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for projects' do
+RSpec.describe 'User searches for projects', :js do
let!(:project) { create(:project, :public, name: 'Shop') }
context 'when signed out' do
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 0f8daaf8e15..e17521e1d02 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -69,7 +69,13 @@ RSpec.describe 'Task Lists', :js do
wait_for_requests
expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox")
- expect(page).to have_selector('.btn-close')
+ end
+
+ it_behaves_like 'page with comment and close button', 'Close issue' do
+ def setup
+ visit_issue(project, issue)
+ wait_for_requests
+ end
end
it 'is only editable by author' do
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 5762a54a717..eed67e3ac78 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -39,6 +39,11 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
end
it 'allows the same device to be registered for multiple users' do
+ # U2f specs will be removed after WebAuthn migration completed
+ pending('FakeU2fDevice has static key handle, '\
+ 'leading to duplicate credential_xid for WebAuthn during migration, '\
+ 'resulting in unique constraint violation')
+
# First user
visit profile_account_path
manage_two_factor_authentication
@@ -148,6 +153,11 @@ RSpec.describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :j
describe "and also the current user" do
it "allows logging in with that particular device" do
+ # U2f specs will be removed after WebAuthn migration completed
+ pending('FakeU2fDevice has static key handle, '\
+ 'leading to duplicate credential_xid for WebAuthn during migration, '\
+ 'resulting in unique constraint violation')
+
# Register current user with the same U2F device
current_user = gitlab_sign_in(:user)
current_user.update_attribute(:otp_required_for_login, true)
diff --git a/spec/features/user_sees_revert_modal_spec.rb b/spec/features/user_sees_revert_modal_spec.rb
index 331f51dad95..5edf8358244 100644
--- a/spec/features/user_sees_revert_modal_spec.rb
+++ b/spec/features/user_sees_revert_modal_spec.rb
@@ -7,26 +7,33 @@ RSpec.describe 'Merge request > User sees revert modal', :js, :sidekiq_might_not
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
+ shared_examples 'showing the revert modal' do
+ it 'shows the revert modal' do
+ click_button('Revert')
+
+ page.within('[data-testid="modal-commit"]') do
+ expect(page).to have_content 'Revert this merge request'
+ end
+ end
+ end
+
before do
sign_in(user)
visit(project_merge_request_path(project, merge_request))
click_button('Merge')
wait_for_requests
-
- visit(merge_request_path(merge_request))
- click_link('Revert')
end
- it 'shows the revert modal' do
- page.within('.modal-header') do
- expect(page).to have_content 'Revert this merge request'
- end
+ context 'without page reload after merge validates js correctly loaded' do
+ it_behaves_like 'showing the revert modal'
end
- it 'closes the revert modal with escape keypress' do
- find('#modal-revert-commit').send_keys(:escape)
+ context 'with page reload validates js correctly loaded' do
+ before do
+ visit(merge_request_path(merge_request))
+ end
- expect(page).not_to have_selector('#modal-revert-commit', visible: true)
+ it_behaves_like 'showing the revert modal'
end
end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 5a537c1d4df..1d1120709b5 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -765,7 +765,7 @@ RSpec.describe 'Login' do
click_link 'Proceed'
expect(current_path).to eq(profile_account_path)
- expect(page).to have_content('Congratulations! You have enabled Two-factor Authentication!')
+ expect(page).to have_content('You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can use that key to generate additional recovery codes.')
end
end
diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb
index 67216b04504..902079b7b93 100644
--- a/spec/features/users/overview_spec.rb
+++ b/spec/features/users/overview_spec.rb
@@ -151,6 +151,132 @@ RSpec.describe 'Overview tab on a user profile', :js do
end
end
+ describe 'followers section' do
+ describe 'user has no followers' do
+ before do
+ visit user.username
+ page.find('.js-followers-tab a').click
+ wait_for_requests
+ end
+
+ it 'shows an empty followers list with an info message' do
+ page.within('#followers') do
+ expect(page).to have_content('You do not have any followers')
+ expect(page).not_to have_selector('.gl-card.gl-mb-5')
+ expect(page).not_to have_selector('.gl-pagination')
+ end
+ end
+ end
+
+ describe 'user has less then 20 followers' do
+ let(:follower) { create(:user) }
+
+ before do
+ follower.follow(user)
+ visit user.username
+ page.find('.js-followers-tab a').click
+ wait_for_requests
+ end
+
+ it 'shows followers' do
+ page.within('#followers') do
+ expect(page).to have_content(follower.name)
+ expect(page).to have_selector('.gl-card.gl-mb-5')
+ expect(page).not_to have_selector('.gl-pagination')
+ end
+ end
+ end
+
+ describe 'user has more then 20 followers' do
+ let(:other_users) { create_list(:user, 21) }
+
+ before do
+ other_users.each do |follower|
+ follower.follow(user)
+ end
+
+ visit user.username
+ page.find('.js-followers-tab a').click
+ wait_for_requests
+ end
+ it 'shows paginated followers' do
+ page.within('#followers') do
+ other_users.each_with_index do |follower, i|
+ break if i == 20
+
+ expect(page).to have_content(follower.name)
+ end
+ expect(page).to have_selector('.gl-card.gl-mb-5')
+ expect(page).to have_selector('.gl-pagination')
+ expect(page).to have_selector('.gl-pagination .js-pagination-page', count: 2)
+ end
+ end
+ end
+ end
+
+ describe 'following section' do
+ describe 'user is not following others' do
+ before do
+ visit user.username
+ page.find('.js-following-tab a').click
+ wait_for_requests
+ end
+
+ it 'shows an empty following list with an info message' do
+ page.within('#following') do
+ expect(page).to have_content('You are not following other users')
+ expect(page).not_to have_selector('.gl-card.gl-mb-5')
+ expect(page).not_to have_selector('.gl-pagination')
+ end
+ end
+ end
+
+ describe 'user is following less then 20 people' do
+ let(:followee) { create(:user) }
+
+ before do
+ user.follow(followee)
+ visit user.username
+ page.find('.js-following-tab a').click
+ wait_for_requests
+ end
+
+ it 'shows following user' do
+ page.within('#following') do
+ expect(page).to have_content(followee.name)
+ expect(page).to have_selector('.gl-card.gl-mb-5')
+ expect(page).not_to have_selector('.gl-pagination')
+ end
+ end
+ end
+
+ describe 'user is following more then 20 people' do
+ let(:other_users) { create_list(:user, 21) }
+
+ before do
+ other_users.each do |followee|
+ user.follow(followee)
+ end
+
+ visit user.username
+ page.find('.js-following-tab a').click
+ wait_for_requests
+ end
+ it 'shows paginated following' do
+ page.within('#following') do
+ other_users.each_with_index do |followee, i|
+ break if i == 20
+
+ expect(page).to have_content(followee.name)
+ end
+ expect(page).to have_selector('.gl-card.gl-mb-5')
+ expect(page).to have_selector('.gl-pagination')
+ expect(page).to have_selector('.gl-pagination .js-pagination-page', count: 2)
+ end
+ end
+ end
+ end
+
describe 'bot user' do
let(:bot_user) { create(:user, user_type: :security_bot) }
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index 6aeb3023db8..a8372800700 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -20,6 +20,8 @@ RSpec.describe 'User page' do
expect(page).to have_link('Contributed projects')
expect(page).to have_link('Personal projects')
expect(page).to have_link('Snippets')
+ expect(page).to have_link('Followers')
+ expect(page).to have_link('Following')
end
end
@@ -54,6 +56,50 @@ RSpec.describe 'User page' do
expect(page).to have_content('GitLab - work info test')
end
end
+
+ context 'follow/unfollow and followers/following' do
+ let_it_be(:followee) { create(:user) }
+ let_it_be(:follower) { create(:user) }
+
+ it 'does not show link to follow' do
+ subject
+
+ expect(page).not_to have_link(text: 'Follow', class: 'gl-button')
+ end
+
+ it 'shows 0 followers and 0 following' do
+ subject
+
+ expect(page).to have_content('0 followers')
+ expect(page).to have_content('0 following')
+ end
+
+ it 'shows 1 followers and 1 following' do
+ follower.follow(user)
+ user.follow(followee)
+
+ subject
+
+ expect(page).to have_content('1 follower')
+ expect(page).to have_content('1 following')
+ end
+
+ it 'does show link to follow' do
+ sign_in(user)
+ visit user_path(followee)
+
+ expect(page).to have_link(text: 'Follow', class: 'gl-button')
+ end
+
+ it 'does show link to unfollow' do
+ sign_in(user)
+ user.follow(followee)
+
+ visit user_path(followee)
+
+ expect(page).to have_link(text: 'Unfollow', class: 'gl-button')
+ end
+ end
end
context 'with private profile' do
@@ -83,6 +129,8 @@ RSpec.describe 'User page' do
expect(page).to have_link('Contributed projects')
expect(page).to have_link('Personal projects')
expect(page).to have_link('Snippets')
+ expect(page).to have_link('Followers')
+ expect(page).to have_link('Following')
end
end
end
@@ -242,6 +290,8 @@ RSpec.describe 'User page' do
expect(page).not_to have_link('Contributed projects')
expect(page).not_to have_link('Personal projects')
expect(page).not_to have_link('Snippets')
+ expect(page).not_to have_link('Followers')
+ expect(page).not_to have_link('Following')
end
end
end
@@ -261,6 +311,8 @@ RSpec.describe 'User page' do
expect(page).to have_link('Contributed projects')
expect(page).to have_link('Personal projects')
expect(page).to have_link('Snippets')
+ expect(page).to have_link('Followers')
+ expect(page).to have_link('Following')
end
end
end
diff --git a/spec/features/webauthn_spec.rb b/spec/features/webauthn_spec.rb
index 2ffb6bb3477..4eebc9d2c1e 100644
--- a/spec/features/webauthn_spec.rb
+++ b/spec/features/webauthn_spec.rb
@@ -129,6 +129,10 @@ RSpec.describe 'Using WebAuthn Devices for Authentication', :js do
end
it 'falls back to U2F' do
+ # WebAuthn registration is automatically created with the U2fRegistration because of the after_create callback
+ # so we need to delete it
+ WebauthnRegistration.delete_all
+
gitlab_sign_in(user)
u2f_device.respond_to_u2f_authentication
diff --git a/spec/features/whats_new_spec.rb b/spec/features/whats_new_spec.rb
new file mode 100644
index 00000000000..7c5625486f5
--- /dev/null
+++ b/spec/features/whats_new_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe "renders a `whats new` dropdown item", :js do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'shows notification dot and count and removes it once viewed' do
+ visit root_dashboard_path
+
+ page.within '.header-help' do
+ expect(page).to have_selector('.notification-dot', visible: true)
+
+ find('.header-help-dropdown-toggle').click
+
+ expect(page).to have_button(text: "What's new")
+ expect(page).to have_selector('.js-whats-new-notification-count')
+
+ find('button', text: "What's new").click
+ end
+
+ find('.whats-new-drawer .gl-drawer-close-button').click
+ find('.header-help-dropdown-toggle').click
+
+ page.within '.header-help' do
+ expect(page).not_to have_selector('.notification-dot', visible: true)
+ expect(page).to have_button(text: "What's new")
+ expect(page).not_to have_selector('.js-whats-new-notification-count')
+ end
+ end
+end